From f1dd96bc67e497f6c07291d6e5f6f23ec243b091 Mon Sep 17 00:00:00 2001 From: git-pc-greta Date: Wed, 22 Jan 2025 01:23:58 +0100 Subject: classe CKEditor --- index.php | 51 +++++----- src/integration/ckeditor5/CKEditor.php | 153 +++++++++++++++++++++++++++++ src/integration/ckeditor5/clean_html.php | 40 -------- src/integration/ckeditor5/config.php | 22 ----- src/integration/ckeditor5/create.php | 18 ---- src/integration/ckeditor5/image_upload.php | 25 ++--- src/integration/ckeditor5/init.php | 23 ----- src/integration/ckeditor5/view.php | 17 ++-- src/view/templates/page.php | 6 +- 9 files changed, 195 insertions(+), 160 deletions(-) create mode 100644 src/integration/ckeditor5/CKEditor.php delete mode 100644 src/integration/ckeditor5/clean_html.php delete mode 100644 src/integration/ckeditor5/config.php delete mode 100644 src/integration/ckeditor5/create.php delete mode 100644 src/integration/ckeditor5/init.php diff --git a/index.php b/index.php index 96b3259..e02d3d1 100644 --- a/index.php +++ b/index.php @@ -1,54 +1,49 @@ checkAjaxReqest(); +} // submit normal if(isset($_GET['action']) && $_GET['action'] === 'submit') // HTML envoyé par l'éditeur { - require $ckeditor_integration . 'clean_html.php'; - $html_from_editor = getAndCleanEditorOutput(); // manipule $_POST['contenu']; - - // enregistrement des données - //var_dump($html_from_editor); - echo "ce programme n'enregistre rien, faîtes-le dans votre application et supprimer cette ligne dans index.php"; die; - - header('Location: ' . $previous_page); - die; + $ckeditor->checkSubmitPOST(); } +/*$articles = []; +foreach($articles as $article) +{}*/ + // ouvrir l'éditeur -elseif(isset($_GET['page']) && $_GET['page'] === 'editor') +if(isset($_GET['action']) && $_GET['action'] === 'editor') { - require $ckeditor_integration . 'create.php'; + $editeurHTML = $ckeditor->openEditor(); } -else // $previous_page, affichage sans l'article +else // affichage article { - // contrôleur - - // modèle - if($storage === 'database') - {} - elseif($storage === 'files') + if($ckeditor->storage === 'database') + { + // ton code + } + elseif($ckeditor->storage === 'files') { - $texte = file_get_contents('data/' . $page . '/html/' . $nom_article . '.html'); + $texte = file_get_contents($ckeditor->data_path . '/html/' . $ckeditor->nom_article . '.html'); //$texte = trim(addcslashes($texte, "'")); // échappe seulement les simples quotes $texte = trim($texte); } - // vue - $css_editeur = ''; - $contenu = '
' . $texte . '
-

ouvrir l\'éditeur

'; + $editeurHTML = '
' . $texte . '
+

ouvrir l\'éditeur

'; } -// vue require 'src/view/templates/page.php'; diff --git a/src/integration/ckeditor5/CKEditor.php b/src/integration/ckeditor5/CKEditor.php new file mode 100644 index 0000000..f3a60e0 --- /dev/null +++ b/src/integration/ckeditor5/CKEditor.php @@ -0,0 +1,153 @@ +makeTranslationSymLink(); + $this->from .= isset($_GET['from']) ? '?page=' . $_GET['from'] : ''; + $this->php_ini_max_size = $this->returnBytes(ini_get('upload_max_filesize')); // = 2M par défaut dans le php.ini + $this->css_outside_editor_tag = ''; + $this->server_root = $_SERVER['SERVER_NAME'] . '/ckeditor5/'; + } + + public function checkAjaxReqest() + { + // dans un fichier à part parce que trop de lignes + require self::INTEGRATION_PATH . 'image_upload.php'; + die; + } + + public function checkSubmitPOST() + { + $html_from_editor = $this->getAndCleanEditorOutput($_POST["contenu"]); // manipule $_POST['contenu']; + + // enregistrement des données + //var_dump($html_from_editor); + echo "ce programme n'enregistre rien, faîtes-le dans votre application et supprimer cette ligne dans index.php"; die; + + header('Location: ' . $this->from); + die; + } + + public function openEditor() + { + // version "minifiée" + $this->css_editor_tag = ''; + // version normale aérée et commentée" + $this->css_editor_tag = ''; + + if($this->storage === 'database') + {} + elseif($this->storage === 'files') + { + $texte = file_get_contents($this->data_path . '/html/' . $this->nom_article . '.html'); + + $texte = trim(addcslashes($texte, "'")); // échapper les simples quotes pour javascript + //$texte = trim(addslashes($texte)); // échappe ', ", \ et NULL, je sais pas si c'est bien + } + + require self::INTEGRATION_PATH . 'view.php'; // html + JS + return $editeurHTML; + } + + /* lien symbolique des traductions + l'éditeur recherche un dans module/ckeditor5/dist/browser/translations + un fichier se trouvant dans module/ckeditor5/dist/translations + c'est le meilleur moyen que j'ai trouvé de gérer ça (il y a surement mieux) */ + private function makeTranslationSymLink(): void + { + $target = '../translations'; + $link = 'node_modules/ckeditor5/dist/browser/translations'; + + if(!file_exists($link)) + { + if(PHP_OS_FAMILY === 'Linux') + { + symlink($target, $link); + } + elseif(PHP_OS_FAMILY === 'Windows') // note: PHP_OS = WINNT + { + // on peut créer une jointure sans droit d'admin + $target = 'node_modules\ckeditor5\dist\translations'; + exec('mklink /J ' . str_replace('/', '\\', $link) . ' ' . $target); + } + else + { + echo "erreur dans " . self::INTEGRATION_PATH . "CKEditor.php: système d'exploitation n'a pas été reconnu"; + } + // autres valeurs possibles pour PHP_OS_FAMILY: 'BSD', 'Darwin', 'Solaris', 'Unknown' + } + } + + // conversion des 2M du php.ini en 2000000 + // note: les kibi, mébi et gibi sont inutiles ici + private function returnBytes($size_str) // chaine du style '2M' + { + switch (substr ($size_str, -1)) + { + case 'Ki': case 'ki': return (int)$size_str * 1024; + case 'Mi': case 'mi': return (int)$size_str * 1048576; + case 'Gi': case 'gi': return (int)$size_str * 1073741824; + case 'K': case 'k': return (int)$size_str * 1000; + case 'M': case 'm': return (int)$size_str * 1000000; + case 'G': case 'g': return (int)$size_str * 1000000000; + default: return $size_str; + } + } + + private function getAndCleanEditorOutput(string $html): string + { + // bugs possibles sans trim() lorsqu'on insère le HTML dans l'éditeur + $html = trim($html); + + //checkContentInFile($html, 'avant'); + + // sécurisation du HTML (faille XSS) + require 'vendor/htmlawed/htmlawed/htmLawed.php'; + $configHtmLawed = array( + 'safe'=>1, // protection contre les élements et attributs dangereux + + // balises autorisées + 'elements'=>'h2, h3, h4, p, span, i, strong, u, s, mark, blockquote, li, ol, ul, a, figure, hr, img, figcaption, table, tbody, tr, td', + // note: change en + + // attributs interdits + 'deny_attribute'=>'id', // 'class' et 'style' sont conservés pour le ckeditor + ); + $specHtmLawed = ''; // optionnel: faire qu'un certain élément puisse n'avoir que certains attributs + $html = htmLawed($html, $configHtmLawed, $specHtmLawed); + + //checkContentInFile($html, 'après'); + + return $html; + } + + + private function checkContentInFile($html, $file_name): void + { + $page = 'page'; + $nom_fichier = $file_name . ".html"; + $fichier = fopen('data/' . $page . '/' . $nom_fichier, 'w'); // w peut créer un fichier, si il existe déjà, il est effacé par le nouveau contenu + fputs($fichier, $html); + fclose($fichier); + chmod('data/' . $page . '/' . $nom_fichier, 0666); + } +} \ No newline at end of file diff --git a/src/integration/ckeditor5/clean_html.php b/src/integration/ckeditor5/clean_html.php deleted file mode 100644 index e56f49c..0000000 --- a/src/integration/ckeditor5/clean_html.php +++ /dev/null @@ -1,40 +0,0 @@ -1, // protection contre les élements et attributs dangereux - - // balises autorisées - 'elements'=>'h2, h3, h4, p, span, i, strong, u, s, mark, blockquote, li, ol, ul, a, figure, hr, img, figcaption, table, tbody, tr, td', - // note: change en - - // attributs interdits - 'deny_attribute'=>'id', // 'class' et 'style' sont conservés pour le ckeditor - ); - $specHtmLawed = ''; // optionnel: faire qu'un certain élément puisse n'avoir que certains attributs - $html = htmLawed($html, $configHtmLawed, $specHtmLawed); - - //checkContentInFile($html, 'après'); - - return $html; -} - - -function checkContentInFile($html, $file_name) -{ - $page = 'page'; - $nom_fichier = $file_name . ".html"; - $fichier = fopen('data/' . $page . '/' . $nom_fichier, 'w'); // w peut créer un fichier, si il existe déjà, il est effacé par le nouveau contenu - fputs($fichier, $html); - fclose($fichier); - chmod('data/' . $page . '/' . $nom_fichier, 0666); -} \ No newline at end of file diff --git a/src/integration/ckeditor5/config.php b/src/integration/ckeditor5/config.php deleted file mode 100644 index e185e8e..0000000 --- a/src/integration/ckeditor5/config.php +++ /dev/null @@ -1,22 +0,0 @@ -'; - -$server_root = $_SERVER['SERVER_NAME'] . '/ckeditor5/'; -// pour l'importmap: j'ai modifié la version "installation avec CDN de la doc pour utiliser les fichiers locaux -// l'"importmap" permet d'utiliser "import" (ça ressemble pas mal au python) dans le navigateur comme n'importe quel langage de programmation normal - -$previous_page = 'index.php'; -$open_editor_link = 'index.php?page=editor'; -$form_action_file = 'index.php?action=submit'; -$upload_ajax_url = 'index.php?action=upload_image'; - -$toolbar_language = 'fr'; - -$storage = 'files'; // choisir 'files' ou 'database' -$page = 'page'; -$nom_article = "article"; - -$php_ini_max_size = ini_get('upload_max_filesize'); // = 2M par défaut dans le php.ini diff --git a/src/integration/ckeditor5/create.php b/src/integration/ckeditor5/create.php deleted file mode 100644 index cf7242c..0000000 --- a/src/integration/ckeditor5/create.php +++ /dev/null @@ -1,18 +0,0 @@ - -// pour voir cette réponse, les messages d'erreur ou tout affichage avec echo ou var_dump: -// outils de développement (F12) => réseau => trouver la requête (xhr) => cliquer dessus puis sur réponse - -// rappel: le téléchargement de fichier avec PHP nécessite un dossier temporaire et que le serveur y soit autorisé en écriture - - $erreur = ''; if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload']) - && strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false // simple upload adapter envoie "des form-data" - && isset($_GET['action']) && $_GET['action'] === 'upload_image') // image insérée dans l'éditeur => requête AJAX + && strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false) // le "simple upload adapter" envoie des "form-data" { if($_FILES['upload']['error'] == 0) // 0 signifie ok { - if($_FILES['upload']['size'] <= $php_ini_max_size ) + if($_FILES['upload']['size'] <= $this->php_ini_max_size) { $infos = pathinfo ($_FILES['upload']['name']); $extension = $infos['extension']; @@ -29,8 +19,8 @@ if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload']) // on prend la même liste que celle côté javascript, le SVG est bloqué pour raison de sécurité (javascript à l'intérieur) if(in_array($extension, $extautorisées)) { - move_uploaded_file ($_FILES['upload']['tmp_name'], 'data/' . $page . '/images/' . $_FILES['upload']['name']); - chmod('data/' . $page . '/images/' . $_FILES['upload']['name'], 0666); + move_uploaded_file ($_FILES['upload']['tmp_name'], $this->data_path . '/images/' . $_FILES['upload']['name']); + chmod($this->data_path . '/images/' . $_FILES['upload']['name'], 0666); } else { @@ -49,14 +39,17 @@ if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload']) if(empty($erreur)) { - $chemin = '{"url": "data/' . $page . '/images/' . $_FILES['upload']['name'] . '"}'; + $chemin = '{"url": "' . $this->data_path . '/images/' . $_FILES['upload']['name'] . '"}'; echo $chemin; } else { echo $erreur; } - die; +} +else +{ + echo "erreur: téléchargement non identifié"; } /* les erreurs retournées avec $_FILES['upload']['error']: diff --git a/src/integration/ckeditor5/init.php b/src/integration/ckeditor5/init.php deleted file mode 100644 index 30e5fb9..0000000 --- a/src/integration/ckeditor5/init.php +++ /dev/null @@ -1,23 +0,0 @@ -'; // version normale aérée et commentée -$css_editeur = ''; // version "minifiée" - ob_start(); ?>
-
+ - + @@ -29,7 +26,7 @@ ob_start(); Link, Table, TableColumnResize, TableToolbar, TableProperties, TableCellProperties, TextPartLanguage } from "ckeditor5"; - import coreTranslations from 'ckeditor5/translations/.js'; + import coreTranslations from 'ckeditor5/translations/toolbar_language ?>.js'; // n'utilise pas le bon chemin à cause d'un bug? solution = créer un lien symbolique à l'endroit attendu: // ln -s /srv/http/ckeditor5/node_modules/ckeditor5/dist/translations /srv/http/ckeditor5/node_modules/ckeditor5/dist/browser/ @@ -37,7 +34,7 @@ ob_start(); let editor; let html_existant = ''; // $texte doit avoir été sécurisé: simple quotes échappées au minimum - let upload_url = ''; + let upload_url = 'upload_ajax_url ?>'; // ATTENTION: si l'éditeur ne fonctionne pas, empêcher qu'on puisse cliquer sur Valider! // Il y a aussi des paramètres dans le fichier de config: ckeditor/webpack.config.js @@ -70,7 +67,7 @@ ob_start(); shouldNotGroupWhenFull: true }, - language: '', + language: 'toolbar_language ?>', translations: [coreTranslations], // barre d'outils dans une image diff --git a/src/view/templates/page.php b/src/view/templates/page.php index 3153ced..5051a5f 100644 --- a/src/view/templates/page.php +++ b/src/view/templates/page.php @@ -4,12 +4,12 @@ - - + css_outside_editor_tag ?> + css_editor_tag ?>
- +
-- cgit v1.2.3