diff options
Diffstat (limited to 'public')
| -rw-r--r-- | public/index.php | 258 |
1 files changed, 166 insertions, 92 deletions
diff --git a/public/index.php b/public/index.php index 2cd0c78..f46b384 100644 --- a/public/index.php +++ b/public/index.php | |||
| @@ -72,7 +72,8 @@ function curlDownloadImage(string $url, int $maxRetries = 3, int $timeout = 10): | |||
| 72 | return false; // échec après trois tentatives | 72 | return false; // échec après trois tentatives |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | function sanitizeFileName(string $filename): string { | 75 | function sanitizeFileName(string $filename): string |
| 76 | { | ||
| 76 | $filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $filename); // ne garder que les lettres, chiffres, tirets et underscores | 77 | $filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $filename); // ne garder que les lettres, chiffres, tirets et underscores |
| 77 | $filename = preg_replace('/_+/', '_', $filename); // doublons d'underscores | 78 | $filename = preg_replace('/_+/', '_', $filename); // doublons d'underscores |
| 78 | return trim($filename, '_'); | 79 | return trim($filename, '_'); |
| @@ -80,8 +81,8 @@ function sanitizeFileName(string $filename): string { | |||
| 80 | 81 | ||
| 81 | function checkFileDownload(array $file): bool | 82 | function checkFileDownload(array $file): bool |
| 82 | { | 83 | { |
| 83 | $extensions_white_list = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'odt', 'ods', 'odp']; | 84 | $extensions_white_list = ['pdf', 'rtf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'odt', 'ods', 'odp']; // = extensions_white_list côté javascript |
| 84 | $mime_type_white_list = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation']; | 85 | $mime_type_white_list = ['application/pdf', 'application/rtf', 'text/rtf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation']; |
| 85 | 86 | ||
| 86 | // 1/ extension | 87 | // 1/ extension |
| 87 | $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); | 88 | $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); |
| @@ -93,18 +94,15 @@ function checkFileDownload(array $file): bool | |||
| 93 | if(!is_uploaded_file($file['tmp_name'])){ | 94 | if(!is_uploaded_file($file['tmp_name'])){ |
| 94 | return false; | 95 | return false; |
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | $finfo = finfo_open(FILEINFO_MIME_TYPE); | ||
| 98 | |||
| 99 | // 3/ objet $finfo valide (dépend du paramètre FILEINFO_MIME_TYPE) | 98 | // 3/ objet $finfo valide (dépend du paramètre FILEINFO_MIME_TYPE) |
| 99 | $finfo = new finfo(FILEINFO_MIME_TYPE); | ||
| 100 | if($finfo === false){ | 100 | if($finfo === false){ |
| 101 | return false; | 101 | return false; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | $real_type = finfo_file($finfo, $file['tmp_name']); | ||
| 105 | finfo_close($finfo); | ||
| 106 | |||
| 107 | // 4/ contrôle du "vrai" type mime (finfo_file lit les 1ers octets des fichiers pour y trouver des "signatures", très fiable sauf avec les conteneurs: doc, zip...) | 104 | // 4/ contrôle du "vrai" type mime (finfo_file lit les 1ers octets des fichiers pour y trouver des "signatures", très fiable sauf avec les conteneurs: doc, zip...) |
| 105 | $real_type = finfo_file($finfo, $file['tmp_name']); | ||
| 108 | return in_array($real_type, $mime_type_white_list, true); | 106 | return in_array($real_type, $mime_type_white_list, true); |
| 109 | } | 107 | } |
| 110 | 108 | ||
| @@ -113,9 +111,9 @@ if(isset($_GET['action']) && $_GET['action'] == 'editor_submit'){ | |||
| 113 | $data = file_get_contents('php://input'); | 111 | $data = file_get_contents('php://input'); |
| 114 | $json = json_decode($data, true); | 112 | $json = json_decode($data, true); |
| 115 | 113 | ||
| 116 | if(json_last_error() === JSON_ERROR_NONE) { | 114 | if(json_last_error() === JSON_ERROR_NONE){ |
| 117 | // Traitement côté serveur | 115 | // Traitement côté serveur |
| 118 | $articleId = $json['id']; | 116 | $article_id = $json['id']; |
| 119 | $content = $json['content']; | 117 | $content = $json['content']; |
| 120 | 118 | ||
| 121 | // retour au client | 119 | // retour au client |
| @@ -135,7 +133,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'delete_article'){ | |||
| 135 | $success = true; | 133 | $success = true; |
| 136 | 134 | ||
| 137 | // retour au client | 135 | // retour au client |
| 138 | if($success) { | 136 | if($success){ |
| 139 | echo json_encode(['success' => true]); | 137 | echo json_encode(['success' => true]); |
| 140 | } | 138 | } |
| 141 | else { | 139 | else { |
| @@ -145,8 +143,6 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'delete_article'){ | |||
| 145 | } | 143 | } |
| 146 | die; | 144 | die; |
| 147 | } | 145 | } |
| 148 | |||
| 149 | |||
| 150 | elseif(isset($_GET['action']) && in_array($_GET['action'], ['upload_image', 'upload_image_url', 'upload_image_base64'])){ | 146 | elseif(isset($_GET['action']) && in_array($_GET['action'], ['upload_image', 'upload_image_url', 'upload_image_base64'])){ |
| 151 | $dest = 'images/'; | 147 | $dest = 'images/'; |
| 152 | if(!is_dir($dest)){ | 148 | if(!is_dir($dest)){ |
| @@ -222,8 +218,6 @@ elseif(isset($_GET['action']) && in_array($_GET['action'], ['upload_image', 'upl | |||
| 222 | } | 218 | } |
| 223 | die; | 219 | die; |
| 224 | } | 220 | } |
| 225 | |||
| 226 | |||
| 227 | elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | 221 | elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ |
| 228 | if(isset($_FILES['file'])){ | 222 | if(isset($_FILES['file'])){ |
| 229 | $dest = 'media/'; | 223 | $dest = 'media/'; |
| @@ -275,15 +269,15 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 275 | <script> | 269 | <script> |
| 276 | let editors = {}; | 270 | let editors = {}; |
| 277 | 271 | ||
| 278 | function openEditor(articleId) { | 272 | function openEditor(article_id){ |
| 279 | // Récupérer et sauvegarder le contenu d'origine de l'article | 273 | // Récupérer et sauvegarder le contenu d'origine de l'article |
| 280 | const articleContent = document.getElementById(articleId).innerHTML; | 274 | const articleContent = document.getElementById(article_id).innerHTML; |
| 281 | document.getElementById(articleId).setAttribute('data-original-content', articleContent); | 275 | document.getElementById(article_id).setAttribute('data-original-content', articleContent); |
| 282 | 276 | ||
| 283 | tinymce.init({ | 277 | tinymce.init({ |
| 284 | selector: `#${articleId}`, | 278 | selector: `#${article_id}`, |
| 285 | language: 'fr_FR', // télécharger des paquets de langue ici: https://www.tiny.cloud/get-tiny/language-packages/ | 279 | language: 'fr_FR', // télécharger des paquets de langue ici: https://www.tiny.cloud/get-tiny/language-packages/ |
| 286 | language_url: '../vendor/tweeb/tinymce-i18n/langs/fr_FR.js', // ou installer tweeb/tinymce-i18n avec composer | 280 | language_url: '../vendor/mklkj/tinymce-i18n/langs/fr_FR.js', // paquet mklkj/tinymce-i18n avec composer |
| 287 | license_key: 'gpl', | 281 | license_key: 'gpl', |
| 288 | branding: false, | 282 | branding: false, |
| 289 | plugins: 'lists link autolink table image media autoresize help', | 283 | plugins: 'lists link autolink table image media autoresize help', |
| @@ -298,16 +292,17 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 298 | attrs.rel += 'noreferrer'; // cacher la page d'où on vient | 292 | attrs.rel += 'noreferrer'; // cacher la page d'où on vient |
| 299 | } | 293 | } |
| 300 | },*/ | 294 | },*/ |
| 301 | //paste_data_images: true, | 295 | setup: function (editor){ |
| 302 | setup: function (editor) { | 296 | const extensions_white_list = ['pdf', 'rtf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'odt', 'ods', 'odp']; // = $extensions_white_list côté PHP |
| 303 | editor.on('init', function () { | 297 | |
| 304 | editors[articleId] = editor; | 298 | editor.on('init', function (){ |
| 299 | editors[article_id] = editor; | ||
| 305 | 300 | ||
| 306 | // Masquer le bouton "Modifier" et afficher les boutons "Annuler" et "Soumettre" | 301 | // Masquer le bouton "Modifier" et afficher les boutons "Annuler" et "Soumettre" |
| 307 | document.querySelector(`#edit-${articleId}`).classList.add('hidden'); | 302 | document.querySelector(`#edit-${article_id}`).classList.add('hidden'); |
| 308 | document.querySelector(`#delete-${articleId}`).classList.add('hidden'); | 303 | document.querySelector(`#delete-${article_id}`).classList.add('hidden'); |
| 309 | document.querySelector(`#cancel-${articleId}`).classList.remove('hidden'); | 304 | document.querySelector(`#cancel-${article_id}`).classList.remove('hidden'); |
| 310 | document.querySelector(`#submit-${articleId}`).classList.remove('hidden'); | 305 | document.querySelector(`#submit-${article_id}`).classList.remove('hidden'); |
| 311 | }); | 306 | }); |
| 312 | let skipPastePreProcess = false; | 307 | let skipPastePreProcess = false; |
| 313 | editor.on('Paste', function (e){ // déclenchement AVANT PastePreProcess et quelque que soit le contenu collé | 308 | editor.on('Paste', function (e){ // déclenchement AVANT PastePreProcess et quelque que soit le contenu collé |
| @@ -315,41 +310,39 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 315 | if(!clipboardData){ | 310 | if(!clipboardData){ |
| 316 | return; | 311 | return; |
| 317 | } | 312 | } |
| 318 | const items = clipboardData.items; | 313 | const items = clipboardData.items; // base64 |
| 319 | let foundImage = false; | 314 | const files = clipboardData.files; // explorateur de fichiers |
| 320 | 315 | let found_file = false; | |
| 321 | for(let i = 0; i < items.length; i++){ | 316 | |
| 322 | let item = items[i]; | 317 | // données dans files |
| 323 | 318 | if(files && files.length > 0){ // noter que files peut être vide, alors que items non | |
| 324 | if(item.type.indexOf('image') !== -1){ // test type MIME contenant image | 319 | for(let i = 0; i < files.length; i++){ |
| 325 | foundImage = true; | 320 | let file = files[i]; |
| 326 | 321 | ||
| 327 | const file = item.getAsFile(); // presse-papier => fichier lisible | 322 | if(extensions_white_list.includes(file.name.split('.').pop().toLowerCase())){ |
| 328 | const reader = new FileReader(); | 323 | found_file = true; |
| 329 | 324 | uploadDocument(file, editor); | |
| 330 | reader.onload = function (event){ // fonction exécutée lorsque reader.readAsDataURL(file) est terminée | 325 | } |
| 331 | const base64Data = event.target.result; // données de l'image | 326 | else if(file.type.indexOf('image') !== -1){ |
| 332 | 327 | found_file = true; | |
| 333 | fetch('index.php?action=upload_image_base64', { | 328 | uploadImageBase64(file, editor); |
| 334 | method: 'POST', | 329 | } |
| 335 | headers: { 'Content-Type': 'application/json' }, | 330 | } |
| 336 | body: JSON.stringify({ image_base64: base64Data }) | 331 | } |
| 337 | }) | 332 | // données dans items |
| 338 | .then(response => response.json()) | 333 | else{ // les images collées depuis l'explorateur sont aussi dans items, or elles sont déjà gérées plus haut |
| 339 | .then(data => { | 334 | for(let i = 0; i < items.length; i++){ |
| 340 | if(data.location){ | 335 | let item = items[i]; |
| 341 | editor.insertContent('<img src="' + data.location + '">'); | 336 | |
| 342 | } | 337 | if(item.type.indexOf('image') !== -1){ // test type MIME contenant image |
| 343 | }) | 338 | found_file = true; |
| 344 | .catch(error => { | 339 | const file = item.getAsFile(); // presse-papier => fichier lisible |
| 345 | console.error('Erreur lors de l’upload de l’image base64 :', error); | 340 | uploadImageBase64(file, editor); |
| 346 | }); | 341 | } |
| 347 | }; | ||
| 348 | reader.readAsDataURL(file); // lecture asynchrone du fichier | ||
| 349 | } | 342 | } |
| 350 | } | 343 | } |
| 351 | 344 | ||
| 352 | if(foundImage){ | 345 | if(found_file){ |
| 353 | e.preventDefault(); // supprime le collage automatiue | 346 | e.preventDefault(); // supprime le collage automatiue |
| 354 | skipPastePreProcess = true; // désactiver le PastePreProcess pour ce collage | 347 | skipPastePreProcess = true; // désactiver le PastePreProcess pour ce collage |
| 355 | } | 348 | } |
| @@ -363,7 +356,6 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 363 | const parser = new DOMParser(); | 356 | const parser = new DOMParser(); |
| 364 | const doc = parser.parseFromString(e.content, 'text/html'); | 357 | const doc = parser.parseFromString(e.content, 'text/html'); |
| 365 | const images = doc.querySelectorAll('img'); | 358 | const images = doc.querySelectorAll('img'); |
| 366 | |||
| 367 | let downloads_in_progress = []; | 359 | let downloads_in_progress = []; |
| 368 | 360 | ||
| 369 | images.forEach(img => { | 361 | images.forEach(img => { |
| @@ -395,7 +387,6 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 395 | // une image web ou plus: différer l'insertion dans l'éditeur le temps que le serveur télécharge les images | 387 | // une image web ou plus: différer l'insertion dans l'éditeur le temps que le serveur télécharge les images |
| 396 | if(downloads_in_progress.length > 0){ | 388 | if(downloads_in_progress.length > 0){ |
| 397 | e.preventDefault(); | 389 | e.preventDefault(); |
| 398 | |||
| 399 | Promise.all(downloads_in_progress).then(() => { | 390 | Promise.all(downloads_in_progress).then(() => { |
| 400 | e.content = doc.body.innerHTML; // remplacement du HTML dans l'éditeur par la copie modifiée (doc) | 391 | e.content = doc.body.innerHTML; // remplacement du HTML dans l'éditeur par la copie modifiée (doc) |
| 401 | editor.insertContent(e.content); | 392 | editor.insertContent(e.content); |
| @@ -405,13 +396,47 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 405 | e.content = doc.body.innerHTML; // remplacement du HTML dans l'éditeur par la copie modifiée (doc) | 396 | e.content = doc.body.innerHTML; // remplacement du HTML dans l'éditeur par la copie modifiée (doc) |
| 406 | } | 397 | } |
| 407 | }); | 398 | }); |
| 399 | // glisser-déposer de fichiers (sauf images qui sont déjà gérées nativement) | ||
| 400 | editor.on('drop', function(e){ | ||
| 401 | const data = e.dataTransfer; | ||
| 402 | if(!data || !data.files || data.files.length === 0){ | ||
| 403 | return; // Laisser TinyMCE gérer (texte, images déjà supportées, etc.) | ||
| 404 | } | ||
| 405 | const files = data.files; | ||
| 406 | |||
| 407 | let has_documents = false; | ||
| 408 | for(let i = 0; i < files.length; i++){ | ||
| 409 | if(extensions_white_list.includes(files[i].name.split('.').pop().toLowerCase())){ | ||
| 410 | has_documents = true; | ||
| 411 | break; | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | if(has_documents){ | ||
| 416 | e.preventDefault(); | ||
| 417 | e.stopPropagation(); | ||
| 418 | |||
| 419 | for(let i = 0; i < files.length; i++){ | ||
| 420 | let file = files[i]; | ||
| 421 | |||
| 422 | if(extensions_white_list.includes(file.name.split('.').pop().toLowerCase())){ | ||
| 423 | uploadDocument(file, editor); | ||
| 424 | } | ||
| 425 | else if(file.type.indexOf('image') !== -1){ | ||
| 426 | uploadImageBase64(file, editor); | ||
| 427 | } | ||
| 428 | } | ||
| 429 | } | ||
| 430 | // autres cas: tinymce gère tout seul | ||
| 431 | }); | ||
| 408 | }, | 432 | }, |
| 409 | // upload d'image natif de tinymce avec le bouton "Insérer une image" | 433 | // upload d'image avec le bouton "Insérer une image" |
| 410 | images_upload_handler: images_upload_handler, // = fonction fléchée | 434 | images_upload_handler: images_upload_handler, // = fonction fléchée |
| 411 | // upload de documents (bouton "insérer un lien") | 435 | // upload de documents avec le bouton "insérer un lien" |
| 412 | files_upload_handler: files_upload_handler, // = fonction fléchée | 436 | files_upload_handler: files_upload_handler, // = fonction fléchée |
| 413 | documents_file_types: [ // files_upload_handler a besoin qu'on lui donne tous les types mime | 437 | documents_file_types: [ // files_upload_handler a besoin qu'on lui donne tous les types mime |
| 414 | { mimeType: 'application/pdf', extensions: [ 'pdf' ] }, | 438 | { mimeType: 'application/pdf', extensions: [ 'pdf' ] }, |
| 439 | { mimeType: 'application/rtf', extensions: [ 'rtf' ] }, | ||
| 415 | { mimeType: 'application/msword', extensions: [ 'doc' ] }, | 440 | { mimeType: 'application/msword', extensions: [ 'doc' ] }, |
| 416 | { mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', extensions: [ 'docx' ] }, | 441 | { mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', extensions: [ 'docx' ] }, |
| 417 | { mimeType: 'application/vnd.ms-excel', extensions: [ 'xls' ] }, | 442 | { mimeType: 'application/vnd.ms-excel', extensions: [ 'xls' ] }, |
| @@ -426,7 +451,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 426 | }); | 451 | }); |
| 427 | 452 | ||
| 428 | // Remplacer le contenu de l'article par l'éditeur | 453 | // Remplacer le contenu de l'article par l'éditeur |
| 429 | document.getElementById(articleId).innerHTML = articleContent; | 454 | document.getElementById(article_id).innerHTML = articleContent; |
| 430 | } | 455 | } |
| 431 | 456 | ||
| 432 | const images_upload_handler = (blobInfo, progress) => new Promise((resolve, reject) => { | 457 | const images_upload_handler = (blobInfo, progress) => new Promise((resolve, reject) => { |
| @@ -439,10 +464,10 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 439 | }) | 464 | }) |
| 440 | .then(response => response.json()) | 465 | .then(response => response.json()) |
| 441 | .then(data => { | 466 | .then(data => { |
| 442 | if(data.location) { | 467 | if(data.location){ |
| 443 | resolve({url: data.location}); | 468 | resolve({url: data.location}); |
| 444 | } | 469 | } |
| 445 | else { | 470 | else{ |
| 446 | reject("Erreur: Chemin d'image invalide"); | 471 | reject("Erreur: Chemin d'image invalide"); |
| 447 | } | 472 | } |
| 448 | }) | 473 | }) |
| @@ -451,7 +476,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 451 | }); | 476 | }); |
| 452 | }); | 477 | }); |
| 453 | 478 | ||
| 454 | const files_upload_handler = (blobInfo, progress) => new Promise((resolve, reject) => { | 479 | const files_upload_handler = (blobInfo, progress) => new Promise((resolve, reject) => { // utilisation = bouton "link" (OU drag & drop, et oui) |
| 455 | const formData = new FormData(); | 480 | const formData = new FormData(); |
| 456 | formData.append("file", blobInfo.blob()); | 481 | formData.append("file", blobInfo.blob()); |
| 457 | 482 | ||
| @@ -461,7 +486,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 461 | }) | 486 | }) |
| 462 | .then(response => response.json()) | 487 | .then(response => response.json()) |
| 463 | .then(data => { | 488 | .then(data => { |
| 464 | if(data.location) { | 489 | if(data.location){ |
| 465 | console.log(blobInfo.filename()); | 490 | console.log(blobInfo.filename()); |
| 466 | console.log(data.location); | 491 | console.log(data.location); |
| 467 | 492 | ||
| @@ -471,7 +496,7 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 471 | fileName: blobInfo.filename(), | 496 | fileName: blobInfo.filename(), |
| 472 | }); | 497 | }); |
| 473 | } | 498 | } |
| 474 | else { | 499 | else{ |
| 475 | reject("Erreur: Chemin du fichier invalide"); | 500 | reject("Erreur: Chemin du fichier invalide"); |
| 476 | } | 501 | } |
| 477 | }) | 502 | }) |
| @@ -480,21 +505,21 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 480 | }); | 505 | }); |
| 481 | }); | 506 | }); |
| 482 | 507 | ||
| 483 | function deleteArticle(articleId) { | 508 | function deleteArticle(article_id) { |
| 484 | if (confirm('Voulez-vous vraiment supprimer cet article ?')) { | 509 | if(confirm('Voulez-vous vraiment supprimer cet article ?')){ |
| 485 | // Envoyer une requête au serveur pour supprimer l'article | 510 | // Envoyer une requête au serveur pour supprimer l'article |
| 486 | fetch('index.php?action=delete_article', { | 511 | fetch('index.php?action=delete_article', { |
| 487 | method: 'POST', | 512 | method: 'POST', |
| 488 | headers: { | 513 | headers: { |
| 489 | 'Content-Type': 'application/json' | 514 | 'Content-Type': 'application/json' |
| 490 | }, | 515 | }, |
| 491 | body: JSON.stringify({ id: articleId }) | 516 | body: JSON.stringify({ id: article_id }) |
| 492 | }) | 517 | }) |
| 493 | .then(response => response.json()) | 518 | .then(response => response.json()) |
| 494 | .then(data => { | 519 | .then(data => { |
| 495 | if (data.success) { | 520 | if (data.success) { |
| 496 | // Supprimer l'article du DOM | 521 | // Supprimer l'article du DOM |
| 497 | const articleElement = document.getElementById(articleId); | 522 | const articleElement = document.getElementById(article_id); |
| 498 | articleElement.parentElement.remove(); | 523 | articleElement.parentElement.remove(); |
| 499 | } | 524 | } |
| 500 | else { | 525 | else { |
| @@ -507,29 +532,29 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 507 | } | 532 | } |
| 508 | } | 533 | } |
| 509 | 534 | ||
| 510 | function closeEditor(articleId){ | 535 | function closeEditor(article_id){ |
| 511 | // Récupérer le contenu d'origine de l'article | 536 | // Récupérer le contenu d'origine de l'article |
| 512 | const originalContent = document.getElementById(articleId).getAttribute('data-original-content'); | 537 | const originalContent = document.getElementById(article_id).getAttribute('data-original-content'); |
| 513 | 538 | ||
| 514 | // Fermer l'éditeur | 539 | // Fermer l'éditeur |
| 515 | tinymce.remove(`#${articleId}`); | 540 | tinymce.remove(`#${article_id}`); |
| 516 | delete editors[articleId]; | 541 | delete editors[article_id]; |
| 517 | 542 | ||
| 518 | // Restaurer le contenu d'origine de l'article | 543 | // Restaurer le contenu d'origine de l'article |
| 519 | document.getElementById(articleId).innerHTML = originalContent; | 544 | document.getElementById(article_id).innerHTML = originalContent; |
| 520 | 545 | ||
| 521 | // Afficher le bouton "Modifier" et masquer les boutons "Annuler" et "Soumettre" | 546 | // Afficher le bouton "Modifier" et masquer les boutons "Annuler" et "Soumettre" |
| 522 | document.querySelector(`#edit-${articleId}`).classList.remove('hidden'); | 547 | document.querySelector(`#edit-${article_id}`).classList.remove('hidden'); |
| 523 | document.querySelector(`#delete-${articleId}`).classList.remove('hidden'); | 548 | document.querySelector(`#delete-${article_id}`).classList.remove('hidden'); |
| 524 | document.querySelector(`#cancel-${articleId}`).classList.add('hidden'); | 549 | document.querySelector(`#cancel-${article_id}`).classList.add('hidden'); |
| 525 | document.querySelector(`#submit-${articleId}`).classList.add('hidden'); | 550 | document.querySelector(`#submit-${article_id}`).classList.add('hidden'); |
| 526 | } | 551 | } |
| 527 | 552 | ||
| 528 | function submitArticle(articleId) { | 553 | function submitArticle(article_id) { |
| 529 | // Récupérer l'éditeur correspondant à l'article | 554 | // Récupérer l'éditeur correspondant à l'article |
| 530 | const editor = editors[articleId]; | 555 | const editor = editors[article_id]; |
| 531 | if(!editor){ | 556 | if(!editor){ |
| 532 | console.error('Éditeur non trouvé pour l\'article:', articleId); | 557 | console.error('Éditeur non trouvé pour l\'article:', article_id); |
| 533 | return; | 558 | return; |
| 534 | } | 559 | } |
| 535 | 560 | ||
| @@ -542,18 +567,18 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 542 | headers: { | 567 | headers: { |
| 543 | 'Content-Type': 'application/json' | 568 | 'Content-Type': 'application/json' |
| 544 | }, | 569 | }, |
| 545 | body: JSON.stringify({ id: articleId, content: newContent }) | 570 | body: JSON.stringify({ id: article_id, content: newContent }) |
| 546 | }) | 571 | }) |
| 547 | .then(response => response.json()) | 572 | .then(response => response.json()) |
| 548 | .then(data => { | 573 | .then(data => { |
| 549 | // Fermer l'éditeur et mettre à jour le contenu de l'article | 574 | // Fermer l'éditeur et mettre à jour le contenu de l'article |
| 550 | closeEditor(articleId); | 575 | closeEditor(article_id); |
| 551 | document.getElementById(articleId).innerHTML = newContent; | 576 | document.getElementById(article_id).innerHTML = newContent; |
| 552 | //console.log(newContent); | 577 | //console.log(newContent); |
| 553 | if(data.success){ | 578 | if(data.success){ |
| 554 | // Fermer l'éditeur et mettre à jour le contenu de l'article | 579 | // Fermer l'éditeur et mettre à jour le contenu de l'article |
| 555 | tinymce.remove(`#${articleId}`); | 580 | tinymce.remove(`#${article_id}`); |
| 556 | document.getElementById(articleId).innerHTML = newContent; | 581 | document.getElementById(article_id).innerHTML = newContent; |
| 557 | } | 582 | } |
| 558 | else{ | 583 | else{ |
| 559 | alert('Erreur lors de la sauvegarde de l\'article.'); | 584 | alert('Erreur lors de la sauvegarde de l\'article.'); |
| @@ -563,6 +588,55 @@ elseif(isset($_GET['action']) && $_GET['action'] == 'upload_file'){ | |||
| 563 | console.error('Erreur:', error); | 588 | console.error('Erreur:', error); |
| 564 | }); | 589 | }); |
| 565 | } | 590 | } |
| 591 | |||
| 592 | function uploadImageBase64(file, editor){ | ||
| 593 | const reader = new FileReader(); | ||
| 594 | |||
| 595 | reader.onload = function (event){ // fonction exécutée lorsque reader.readAsDataURL(file) est terminée | ||
| 596 | const base64 = event.target.result; // données de l'image | ||
| 597 | |||
| 598 | fetch('index.php?action=upload_image_base64', { | ||
| 599 | method: 'POST', | ||
| 600 | headers: { 'Content-Type': 'application/json' }, | ||
| 601 | body: JSON.stringify({ image_base64: base64 }) | ||
| 602 | }) | ||
| 603 | .then(response => response.json()) | ||
| 604 | .then(data => { | ||
| 605 | if(data.location){ | ||
| 606 | editor.insertContent('<img src="' + data.location + '">'); | ||
| 607 | } | ||
| 608 | }) | ||
| 609 | .catch(error => { | ||
| 610 | console.error('Erreur lors de l’upload de l’image base64 :', error); | ||
| 611 | }); | ||
| 612 | }; | ||
| 613 | reader.readAsDataURL(file); // lecture asynchrone du fichier | ||
| 614 | } | ||
| 615 | function uploadDocument(file, editor){ // utilisation = copier-coller de l'explorateur de fichiers | ||
| 616 | const formData = new FormData(); | ||
| 617 | formData.append("file", file); | ||
| 618 | |||
| 619 | fetch('index.php?action=upload_file', { | ||
| 620 | method: 'POST', | ||
| 621 | body: formData | ||
| 622 | }) | ||
| 623 | .then(response => response.json()) | ||
| 624 | .then(data => { | ||
| 625 | if(data.location){ | ||
| 626 | // créer le lien <a> | ||
| 627 | const file_name = file.name; | ||
| 628 | const extension = file_name.split('.').pop().toLowerCase(); | ||
| 629 | const target = extension === 'pdf' ? 'target="_blank"' : ''; // PDF = page | ||
| 630 | editor.insertContent(`<a href="${data.location}" ${target} title="${file_name}">[${extension}] ${file_name}</a>`); | ||
| 631 | } | ||
| 632 | else { | ||
| 633 | console.error("Erreur: Chemin du fichier invalide"); | ||
| 634 | } | ||
| 635 | }) | ||
| 636 | .catch(error => { | ||
| 637 | console.error("Erreur lors de l'upload du document :", error); | ||
| 638 | }); | ||
| 639 | } | ||
| 566 | </script> | 640 | </script> |
| 567 | </head> | 641 | </head> |
| 568 | <body> | 642 | <body> |
