diff options
| author | polo <ordipolo@gmx.fr> | 2024-10-27 13:34:13 +0100 |
|---|---|---|
| committer | polo <ordipolo@gmx.fr> | 2024-10-27 13:34:13 +0100 |
| commit | 5b455dbf1474a3c7c839cd129fd470f1fdae6b0c (patch) | |
| tree | 85200f78a951a5ff8ac8f105bce5834c162ab717 | |
| download | ckeditor5-5b455dbf1474a3c7c839cd129fd470f1fdae6b0c.tar.gz ckeditor5-5b455dbf1474a3c7c839cd129fd470f1fdae6b0c.tar.bz2 ckeditor5-5b455dbf1474a3c7c839cd129fd470f1fdae6b0c.zip | |
v2 avec NPM et composer, customizable et plus facile à intégrer
| -rw-r--r-- | config.php | 19 | ||||
| -rw-r--r-- | data/page/html/article.html | 1 | ||||
| -rw-r--r-- | data/page/images/chirurgien jaune.jpg | bin | 0 -> 137915 bytes | |||
| -rw-r--r-- | index.php | 50 | ||||
| -rw-r--r-- | installation dans une application PHP.txt | 95 | ||||
| -rw-r--r-- | lib/ckeditor5/article_hors_editeur.css | 38 | ||||
| -rw-r--r-- | lib/ckeditor5/clean_html.php | 46 | ||||
| -rw-r--r-- | lib/ckeditor5/create.php | 16 | ||||
| -rw-r--r-- | lib/ckeditor5/image_upload.php | 64 | ||||
| -rw-r--r-- | lib/ckeditor5/view.php | 171 | ||||
| -rw-r--r-- | src/templates/page.php | 15 |
11 files changed, 515 insertions, 0 deletions
diff --git a/config.php b/config.php new file mode 100644 index 0000000..9cd5ff2 --- /dev/null +++ b/config.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | // config.php | ||
| 3 | |||
| 4 | $server_root = $_SERVER['SERVER_NAME'] . '/ckeditor5-new/'; | ||
| 5 | // pour l'importmap: j'ai modifié la version "installation avec CDN de la doc pour utiliser les fichiers locaux | ||
| 6 | // l'"importmap" permet d'utiliser "import" (ça ressemble pas mal au python) dans le navigateur comme n'importe quel langage de programmation normal | ||
| 7 | |||
| 8 | $previous_page = 'index.php'; | ||
| 9 | $open_editor_link = 'index.php?page=editor'; | ||
| 10 | $form_action_file = 'index.php?action=submit'; | ||
| 11 | $upload_ajax_url = 'index.php?action=upload_image'; | ||
| 12 | |||
| 13 | $toolbar_language = 'fr'; | ||
| 14 | |||
| 15 | $storage = 'files'; // choisir 'files' ou 'database' | ||
| 16 | $page = 'page'; | ||
| 17 | $nom_article = "article"; | ||
| 18 | |||
| 19 | $php_ini_max_size = ini_get('upload_max_filesize'); // = 2M par défaut dans le php.ini | ||
diff --git a/data/page/html/article.html b/data/page/html/article.html new file mode 100644 index 0000000..3f0058d --- /dev/null +++ b/data/page/html/article.html | |||
| @@ -0,0 +1 @@ | |||
| <figure class="image image-style-side"><img src="data/page/images/chirurgien jaune.jpg" alt="image" /><figcaption>image</figcaption></figure><h2>hello</h2><figure class="table"><table><tbody><tr><td>ok</td><td>ah oui</td><td>bon</td></tr><tr><td><p>c'est pas</p><p>grave</p></td><td><p>on verra</p><p>plus tard</p></td><td>salut</td></tr></tbody></table></figure><p><i><mark class="marker-green">goodbye</mark></i></p> \ No newline at end of file | |||
diff --git a/data/page/images/chirurgien jaune.jpg b/data/page/images/chirurgien jaune.jpg new file mode 100644 index 0000000..295bce2 --- /dev/null +++ b/data/page/images/chirurgien jaune.jpg | |||
| Binary files differ | |||
diff --git a/index.php b/index.php new file mode 100644 index 0000000..d3e2d1e --- /dev/null +++ b/index.php | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | <?php | ||
| 2 | // index.php | ||
| 3 | // | ||
| 4 | // routeur | ||
| 5 | |||
| 6 | require 'config.php'; | ||
| 7 | |||
| 8 | if(isset($_GET['action']) && $_GET['action'] === 'upload_image') // image insérée dans l'éditeur => requête AJAX | ||
| 9 | { | ||
| 10 | require 'lib/ckeditor5/image_upload.php'; | ||
| 11 | } | ||
| 12 | elseif(isset($_GET['action']) && $_GET['action'] === 'submit') // HTML envoyé par l'éditeur | ||
| 13 | { | ||
| 14 | require 'lib/ckeditor5/clean_html.php'; | ||
| 15 | $html_from_editor = getAndCleanEditorOutput(); // manipule $_POST['contenu']; | ||
| 16 | |||
| 17 | // enregistrement des données | ||
| 18 | //var_dump($html_from_editor); | ||
| 19 | echo "enregistrer les données et supprimer cette ligne dans index.php"; die; | ||
| 20 | |||
| 21 | header('Location: ' . $previous_page); | ||
| 22 | die; | ||
| 23 | } | ||
| 24 | elseif(isset($_GET['page']) && $_GET['page'] === 'editor') // ouvrir l'éditeur | ||
| 25 | { | ||
| 26 | require 'lib/ckeditor5/create.php'; | ||
| 27 | } | ||
| 28 | else // $previous_page, affichage sans l'article | ||
| 29 | { | ||
| 30 | // contrôleur | ||
| 31 | |||
| 32 | // modèle | ||
| 33 | if($storage === 'database') | ||
| 34 | {} | ||
| 35 | elseif($storage === 'files') | ||
| 36 | { | ||
| 37 | $texte = file_get_contents('data/' . $page . '/html/' . $nom_article . '.html'); | ||
| 38 | $texte = trim(addcslashes($texte, "'")); // échappe seulement les simples quotes | ||
| 39 | } | ||
| 40 | |||
| 41 | // vue | ||
| 42 | $css_editeur = ''; | ||
| 43 | $contenu = '<div class="conteneur_article" >' . $texte . '</div> | ||
| 44 | <p><a href="' . $open_editor_link . '">ouvrir l\'éditeur</a></p>'; | ||
| 45 | } | ||
| 46 | |||
| 47 | // vue | ||
| 48 | require 'src/templates/page.php'; | ||
| 49 | |||
| 50 | // fin du script | ||
diff --git a/installation dans une application PHP.txt b/installation dans une application PHP.txt new file mode 100644 index 0000000..210902c --- /dev/null +++ b/installation dans une application PHP.txt | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | 1/ installation | ||
| 2 | |||
| 3 | cd "mon projet" | ||
| 4 | git clone git@ordipolo.fr:ckeditor5 | ||
| 5 | copier tout son contenu (sauf le .git) dans le dossier racine, attention quand un index.php existe déjà | ||
| 6 | rm -r ckeditor5 --interactive=never | ||
| 7 | |||
| 8 | npm install ckeditor5 | ||
| 9 | composer require htmlawed/htmlawed | ||
| 10 | |||
| 11 | les 11 fichiers récupérés par le git clone: | ||
| 12 | lib/ckeditor5/create.php | ||
| 13 | lib/ckeditor5/clean_html.php | ||
| 14 | lib/ckeditor5/view.php | ||
| 15 | lib/ckeditor5/image_upload.php | ||
| 16 | lib/ckeditor5/articles_hors_editeur.css | ||
| 17 | config.php | ||
| 18 | installation dans une application PHP.txt | ||
| 19 | index.php (appel des contrôleurs) | ||
| 20 | src/templates/page.php (HTML principal de la page web) | ||
| 21 | data/page/html/articles.html | ||
| 22 | data/page/images/chirurgien jaune.jpg | ||
| 23 | |||
| 24 | |||
| 25 | 2/ à vérifier: | ||
| 26 | créer un lien symbolique comme ceci (commande pour debian): | ||
| 27 | ln -s /var/www/ckeditor5/node_modules/ckeditor5/dist/translations /var/www/ckeditor5/node_modules/ckeditor5/dist/browser/ | ||
| 28 | |||
| 29 | ça régle un problème de chemin rencontré à la ligne: import coreTranslations from 'ckeditor5/translations/fr.js'; dans lib/ckeditor5/template.php | ||
| 30 | gràce à un lien symbolique, le programme s'attend à trouver un dossier "translations" dans "browser" | ||
| 31 | |||
| 32 | |||
| 33 | 3/ essai | ||
| 34 | les paramètres dans config.php et les fichiers dans data permettent normalement | ||
| 35 | - d'ouvrir l'éditeur | ||
| 36 | - de lire des données dans des fichiers et de les insérer dans l'éditeur pour modification | ||
| 37 | |||
| 38 | la sortie au "submit" est nettoyée puis se retrouve dans $html_from_editor | ||
| 39 | |||
| 40 | |||
| 41 | 3/ intégration à un projet | ||
| 42 | |||
| 43 | a) index.php et src/templates/page.php sont à remplacer en fonction de notre application | ||
| 44 | regarder à l'intérieur et adapter son propre code | ||
| 45 | |||
| 46 | b) config.php est à personnaliser et/ou à copier ailleurs, | ||
| 47 | du moment que les variables à l'intérieur restent disponibles | ||
| 48 | |||
| 49 | c) le dossier data et ses sous-dossiers ont besoin de droits en écriture | ||
| 50 | |||
| 51 | a) adapter le routeur dans index.php | ||
| 52 | |||
| 53 | b) adapter ce qui suit "// modèle" dans index.php et lib/ckeditor5/create.php pour obtenir les données souhaitées (BDD, fichiers) | ||
| 54 | |||
| 55 | c) adapter le fichier config.php (vérifier les chemins) | ||
| 56 | |||
| 57 | d) ajouter <?= $css_editeur ?> dans le <head> | ||
| 58 | ajouter aussi <?= $contenu ?> dans le <body> pour afficher l'éditeur ou du HTML créé par l'éditeur | ||
| 59 | |||
| 60 | e) insérer dans chaque page affichant des données créées avec l'éditeur: | ||
| 61 | <link rel="stylesheet" href="lib/ckeditor5/article_hors_editeur.css" /> | ||
| 62 | l'éditeur ne génère pas de CSS mais seulement du HTML basique, ce CSS imite le rendu à l'intérieur de l'éditeur | ||
| 63 | |||
| 64 | normalement c'est bon fini, ce qui suit est de l'information utile si on souhaite partir de zéro avec NPM et la doc | ||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | |||
| 69 | |||
| 70 | 5/ explication et choix des plugins | ||
| 71 | tout ça est déjà fait, mais vous pouvez toujours personnaliser l'éditeur en choisissant des plugins différents | ||
| 72 | |||
| 73 | voici ce que j'ai fait dans: lib/ckeditor5/template.php | ||
| 74 | |||
| 75 | a) créer une importmap avec les chemins des fichiers | ||
| 76 | <script type="importmap"> | ||
| 77 | { | ||
| 78 | "imports": { | ||
| 79 | "ckeditor5": "http://<?= $server_root ?>node_modules/ckeditor5/dist/browser/ckeditor5.js", | ||
| 80 | "ckeditor5/": "http://<?= $server_root ?>node_modules/ckeditor5/dist/browser/" | ||
| 81 | } | ||
| 82 | } | ||
| 83 | </script> | ||
| 84 | |||
| 85 | b) charger une liste de plugins façon python | ||
| 86 | import { "liste de plugins" } from "ckeditor5"; | ||
| 87 | |||
| 88 | c) rechoisir les plugins dans ClassicEditor.create(): | ||
| 89 | plugins: [ "liste de plugins" ] ... | ||
| 90 | |||
| 91 | d) choisir les élément de la toolbar | ||
| 92 | toolbar; { items: [ "éléments" ], ... | ||
| 93 | |||
| 94 | e) autres éléments: | ||
| 95 | toolbar des images et des tables, sécurité des envois AJAX | ||
diff --git a/lib/ckeditor5/article_hors_editeur.css b/lib/ckeditor5/article_hors_editeur.css new file mode 100644 index 0000000..a38b384 --- /dev/null +++ b/lib/ckeditor5/article_hors_editeur.css | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | .conteneur_article{width: 630px;} | ||
| 2 | .conteneur_article:after{content: ""; display: block; clear: both;} | ||
| 3 | |||
| 4 | img{vertical-align: bottom;} | ||
| 5 | @media screen and (max-width: 1000px) | ||
| 6 | {img{max-width: 900px;}} | ||
| 7 | |||
| 8 | .text-tiny{font-size: x-small;} | ||
| 9 | .text-small{font-size: small;} | ||
| 10 | .text-big{font-size: large;} | ||
| 11 | .text-huge{font-size: x-large;} | ||
| 12 | |||
| 13 | blockquote{border-left: 5px #cccccc solid; margin: 14px 0px; padding: 2px 25px; font-style: italic;} | ||
| 14 | |||
| 15 | .marker-yellow{background-color: #fdfd77;} | ||
| 16 | .marker-green{background-color: #62f962;} | ||
| 17 | .marker-pink{background-color: #fc7899;} | ||
| 18 | .marker-blue{background-color: #72ccfd;} | ||
| 19 | .pen-red{background-color: white; color: red;} | ||
| 20 | .pen-green{background-color: white; color: green;} | ||
| 21 | |||
| 22 | ul{padding-left: 10px;} | ||
| 23 | .todo-list>li{list-style-type : none;} | ||
| 24 | input[type=checkbox]{-webkit-appearance: none;-moz-appearance: none;-ms-appearance: none; | ||
| 25 | height: 16px; width: 16px; border: 1px solid black; border-radius: 2px; position: relative; top: 5px; margin-right: 10px;} | ||
| 26 | input[type="checkbox"]:checked{border: none; background: #26ab33;} | ||
| 27 | |||
| 28 | .table>table{border-collapse: collapse;} | ||
| 29 | .table td{border: 1px grey solid; padding: 7px; min-width: 30px;} | ||
| 30 | td p{margin: 0px;} | ||
| 31 | |||
| 32 | .image{margin: 0px; text-align: center; display: inline-block;} | ||
| 33 | .image-style-side{float: right;} | ||
| 34 | .image img{max-width: 630px;} | ||
| 35 | .image-style-side img{max-width: 315px;} | ||
| 36 | .image>figcaption{margin: 0px 10px; padding: 7px; text-align: center; font-size: small; background-color: #f7f7f7;} | ||
| 37 | |||
| 38 | iframe{min-width: 400px; min-height: 300px; max-width: 1200px; max-height: 900px;} | ||
diff --git a/lib/ckeditor5/clean_html.php b/lib/ckeditor5/clean_html.php new file mode 100644 index 0000000..5d00532 --- /dev/null +++ b/lib/ckeditor5/clean_html.php | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | <?php | ||
| 2 | // lib/ckeditor5/clean_html.php | ||
| 3 | |||
| 4 | function getAndCleanEditorOutput(): string | ||
| 5 | { | ||
| 6 | // bugs possibles sans trim() lorsqu'on insère le HTML dans l'éditeur | ||
| 7 | $html = trim($_POST["contenu"]); | ||
| 8 | |||
| 9 | |||
| 10 | // pour debugguer ou tester des paramètres avec htmlawed | ||
| 11 | |||
| 12 | //~ $nom_fichier = "avant.html"; | ||
| 13 | //~ $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 | ||
| 14 | //~ fputs($fichier, $html); | ||
| 15 | //~ fclose($fichier); | ||
| 16 | //~ chmod('data/' . $page . '/' . $nom_fichier, 0666); | ||
| 17 | |||
| 18 | |||
| 19 | // sécurisation du HTML (faille XSS) | ||
| 20 | require 'vendor/htmlawed/htmlawed/htmLawed.php'; | ||
| 21 | $configHtmLawed = array( | ||
| 22 | 'safe'=>1, // protection contre les élements et attributs dangereux | ||
| 23 | |||
| 24 | // balises autorisées | ||
| 25 | 'elements'=>'h2, h3, h4, p, span, i, strong, u, s, mark, blockquote, li, ol, ul, a, figure, hr, img, figcaption, table, tbody, tr, td', | ||
| 26 | // note: change <s></s> en <span style="text-decoration: line-through;"></span> | ||
| 27 | |||
| 28 | // attributs interdits | ||
| 29 | 'deny_attribute'=>'id', // 'class' et 'style' sont conservés pour le ckeditor | ||
| 30 | ); | ||
| 31 | $specHtmLawed = ''; // optionnel: faire qu'un certain élément puisse n'avoir que certains attributs | ||
| 32 | $html = htmLawed($html, $configHtmLawed, $specHtmLawed); | ||
| 33 | |||
| 34 | |||
| 35 | //~ $nom_fichier = "après.html"; | ||
| 36 | //~ $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 | ||
| 37 | //~ fputs($fichier, $html); | ||
| 38 | //~ fclose($fichier); | ||
| 39 | //~ chmod('data/' . $page . '/' . $nom_fichier, 0666); | ||
| 40 | |||
| 41 | |||
| 42 | return $html; | ||
| 43 | } | ||
| 44 | |||
| 45 | |||
| 46 | |||
diff --git a/lib/ckeditor5/create.php b/lib/ckeditor5/create.php new file mode 100644 index 0000000..d62f893 --- /dev/null +++ b/lib/ckeditor5/create.php | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | <?php | ||
| 2 | // lib/ckeditor5/create.php | ||
| 3 | |||
| 4 | // modèle | ||
| 5 | if($storage === 'database') | ||
| 6 | {} | ||
| 7 | elseif($storage === 'files') | ||
| 8 | { | ||
| 9 | // modèle | ||
| 10 | $texte = file_get_contents('data/' . $page . '/html/' . $nom_article . '.html'); | ||
| 11 | $texte = trim(addcslashes($texte, "'")); // échappe seulement les simples quotes | ||
| 12 | } | ||
| 13 | |||
| 14 | // vue | ||
| 15 | require 'lib/ckeditor5/view.php'; // html + JS | ||
| 16 | $contenu = $editeurHTML; | ||
diff --git a/lib/ckeditor5/image_upload.php b/lib/ckeditor5/image_upload.php new file mode 100644 index 0000000..607be1b --- /dev/null +++ b/lib/ckeditor5/image_upload.php | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | <?php | ||
| 2 | // lib/ckeditor5/image_upload.php | ||
| 3 | |||
| 4 | // script récupérant les images téléchargée en AJAX par l'éditeur et c'est tout | ||
| 5 | // on récupère les données, on renvoie au navigateur la réponse qu'il attend et stop! | ||
| 6 | |||
| 7 | // le "simple upload adapter" envoie un POST appelé: $_FILES['upload'] | ||
| 8 | // en retour il attend impérativement des données au format JSON du genre: {"url": "data/page/images/monfichier.jpg"} | ||
| 9 | // cette adresse doit permettre à l'éditeur de télécharger l'image afficher de manière normale: <img scr="data/page/images/monfichier.jpg"> | ||
| 10 | |||
| 11 | // pour voir cette réponse, les messages d'erreur ou tout affichage avec echo ou var_dump: | ||
| 12 | // outils de développement (F12) => réseau => trouver la requête (xhr) => cliquer dessus puis sur réponse | ||
| 13 | |||
| 14 | // rappel: le téléchargement de fichier avec PHP nécessite un dossier temporaire et que le serveur y soit autorisé en écriture | ||
| 15 | |||
| 16 | |||
| 17 | $erreur = ''; | ||
| 18 | if(isset($_FILES['upload']) AND $_FILES['upload']['error'] == 0) // 0 signifie ok | ||
| 19 | { | ||
| 20 | if($_FILES['upload']['size'] <= $php_ini_max_size ) | ||
| 21 | { | ||
| 22 | $infos = pathinfo ($_FILES['upload']['name']); | ||
| 23 | $extension = $infos['extension']; | ||
| 24 | $extautorisées = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff']; | ||
| 25 | // on prend la même liste que celle côté javascript, le SVG est bloqué pour raison de sécurité (javascript à l'intérieur) | ||
| 26 | if(in_array($extension, $extautorisées)) | ||
| 27 | { | ||
| 28 | move_uploaded_file ($_FILES['upload']['tmp_name'], 'data/' . $page . '/images/' . $_FILES['upload']['name']); | ||
| 29 | chmod('data/' . $page . '/images/' . $_FILES['upload']['name'], 0666); | ||
| 30 | } | ||
| 31 | else | ||
| 32 | { | ||
| 33 | $erreur = 'mauvais format, veuillez utiliser une image comportant un de ces formats: jpg ou jpeg, png, gif, bmp, webp, tiff<br />le format svg n\'est pas supporté'; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | else | ||
| 37 | { | ||
| 38 | $erreur = 'fichier trop lourd'; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | else | ||
| 42 | { | ||
| 43 | $erreur = $_FILES['upload']['error']; | ||
| 44 | } | ||
| 45 | /* les erreurs retournées avec $_FILES['upload']['error']: | ||
| 46 | 0 pas d'erreur | ||
| 47 | 1 taille du fichier supérieure à la valeur de upload_max_filesize dans le fichier php.ini (par défaut = 2 MO) | ||
| 48 | 2 taille du fichier supérieure à la valeur de MAX_FILE_SIZE dans le formulaire HTML | ||
| 49 | 3 fichier partiellement téléchargé | ||
| 50 | 4 pas de fichier du tout | ||
| 51 | 6 pas de dossier temporaire pour mettre le fichier | ||
| 52 | 7 echec de l'écriture sur le DD | ||
| 53 | 8 envoi arrêté par une extension de PHP mais on ne nous dit pas pourquoi => diagnostic avec la fonction phpinfo() */ | ||
| 54 | |||
| 55 | if(empty($erreur)) | ||
| 56 | { | ||
| 57 | $chemin = '{"url": "data/' . $page . '/images/' . $_FILES['upload']['name'] . '"}'; | ||
| 58 | echo $chemin; | ||
| 59 | } | ||
| 60 | else | ||
| 61 | { | ||
| 62 | echo $erreur; | ||
| 63 | } | ||
| 64 | die; | ||
diff --git a/lib/ckeditor5/view.php b/lib/ckeditor5/view.php new file mode 100644 index 0000000..6d6c961 --- /dev/null +++ b/lib/ckeditor5/view.php | |||
| @@ -0,0 +1,171 @@ | |||
| 1 | <?php | ||
| 2 | // lib/ckeditor5/view.php | ||
| 3 | |||
| 4 | //$css_editeur = '<link rel="stylesheet" href="node_modules/ckeditor5/dist/ckeditor5.css" />'; // version normale aérée et commentée | ||
| 5 | $css_editeur = '<link rel="stylesheet" href="node_modules/ckeditor5/dist/browser/ckeditor5.css" />'; // version "minifiée" | ||
| 6 | |||
| 7 | ob_start(); | ||
| 8 | ?> | ||
| 9 | <div class="conteneur_article" > | ||
| 10 | <form action="<?= $form_action_file ?>" method="POST" enctype="multipart/form-data" > | ||
| 11 | <textarea id="editor" name="contenu" ></textarea> | ||
| 12 | <input class="boutonSubmitEditeur" type="submit" value="Valider"> | ||
| 13 | <a class="boutonAnnuler" href="<?= $previous_page ?>" > | ||
| 14 | <input type="button" value="Annuler"></a> | ||
| 15 | <script type="importmap"> | ||
| 16 | { | ||
| 17 | "imports": { | ||
| 18 | "ckeditor5": "http://<?= $server_root ?>node_modules/ckeditor5/dist/browser/ckeditor5.js", | ||
| 19 | "ckeditor5/": "http://<?= $server_root ?>node_modules/ckeditor5/dist/browser/" | ||
| 20 | } | ||
| 21 | } | ||
| 22 | </script> | ||
| 23 | <script type="module"> | ||
| 24 | import { // nécessite type="module" | ||
| 25 | ClassicEditor, Essentials, Heading, Paragraph, Alignment, List, | ||
| 26 | BlockQuote, HorizontalLine, Bold, Italic, Underline, Strikethrough, | ||
| 27 | Font, FontFamily, Highlight, FontBackgroundColor, SimpleUploadAdapter, | ||
| 28 | Image, ImageInsert, ImageToolbar, ImageStyle, ImageCaption, LinkImage, | ||
| 29 | Link, Table, TableColumnResize, TableToolbar, TableProperties, TableCellProperties, TextPartLanguage | ||
| 30 | } from "ckeditor5"; | ||
| 31 | |||
| 32 | import coreTranslations from 'ckeditor5/translations/<?= $toolbar_language ?>.js'; | ||
| 33 | // n'utilise pas le bon chemin à cause d'un bug? solution = créer un lien symbolique à l'endroit attendu: | ||
| 34 | // ln -s /srv/http/ckeditor5/node_modules/ckeditor5/dist/translations /srv/http/ckeditor5/node_modules/ckeditor5/dist/browser/ | ||
| 35 | |||
| 36 | //import 'ckeditor5/ckeditor5.css'; // marche pas chez moi | ||
| 37 | |||
| 38 | let editor; | ||
| 39 | let html_existant = '<?= $texte ?>'; // $texte doit avoir été sécurisé: simple quotes échappées au minimum | ||
| 40 | let upload_url = '<?= $upload_ajax_url ?>'; | ||
| 41 | |||
| 42 | // ATTENTION: si l'éditeur ne fonctionne pas, empêcher qu'on puisse cliquer sur Valider! | ||
| 43 | // Il y a aussi des paramètres dans le fichier de config: ckeditor/webpack.config.js | ||
| 44 | ClassicEditor | ||
| 45 | .create( document.querySelector( '#editor' ),{ | ||
| 46 | |||
| 47 | plugins: [ Essentials, Heading, Paragraph, Alignment, List, | ||
| 48 | BlockQuote, HorizontalLine, Underline, Strikethrough, | ||
| 49 | Bold, Italic, Font, FontFamily, Highlight, FontBackgroundColor, SimpleUploadAdapter, | ||
| 50 | Image, ImageInsert, ImageToolbar, ImageStyle, ImageCaption, LinkImage, | ||
| 51 | Link, Table, TableColumnResize, TableToolbar, TableProperties, TableCellProperties, TextPartLanguage], | ||
| 52 | |||
| 53 | toolbar: { | ||
| 54 | items: [ | ||
| 55 | 'undo', 'redo', 'selectAll', '|', 'heading', '|', 'alignment', 'bulletedList', 'numberedList', | ||
| 56 | //'todoList',// marche pas, ne crée pas de HTML | ||
| 57 | 'blockQuote', 'horizontalLine', '|', 'textPartLanguage', '-', 'bold', 'italic', 'underline', 'strikethrough', '|', | ||
| 58 | 'fontFamily', // polices microsoft uniquement | ||
| 59 | 'fontColor', 'fontSize', 'highlight', 'fontBackgroundColor', // un peu comme highlight | ||
| 60 | '|', 'imageInsert', 'link', | ||
| 61 | //'htmlEmbed', // marche pas, ne crée pas de HTML | ||
| 62 | //'mediaEmbed', | ||
| 63 | 'insertTable', | ||
| 64 | ], | ||
| 65 | // multiligne automatique (le '-' dans la liste permet de choisir l'endroit où couper) | ||
| 66 | // pour les plugins indiqués "marche pas", envoient-ils un requête AJAX quelque part? | ||
| 67 | |||
| 68 | shouldNotGroupWhenFull: true | ||
| 69 | }, | ||
| 70 | |||
| 71 | language: '<?= $toolbar_language ?>', | ||
| 72 | translations: [coreTranslations], | ||
| 73 | |||
| 74 | // barre d'outils dans une image | ||
| 75 | image: { | ||
| 76 | toolbar: [ | ||
| 77 | //'imageStyle:full', //inutile? | ||
| 78 | 'imageStyle:block', | ||
| 79 | 'imageStyle:inline', // complexe, on peut aussi placer l'image à la souris | ||
| 80 | 'imageStyle:side', | ||
| 81 | /*'imageStyle:align-left', | ||
| 82 | 'imageStyle:align-right',*/ | ||
| 83 | //'imageResize', // optionnel? on a les poignées dans les coins de l'image | ||
| 84 | 'linkImage', | ||
| 85 | 'toggleImageCaption', | ||
| 86 | 'imageTextAlternative' | ||
| 87 | ] | ||
| 88 | }, | ||
| 89 | |||
| 90 | // barre d'outils dans un tableau | ||
| 91 | table: { | ||
| 92 | contentToolbar: [ | ||
| 93 | 'tableColumn', // manipulation sur les colonnes et lignes | ||
| 94 | 'tableRow', | ||
| 95 | 'mergeTableCells', | ||
| 96 | 'tableProperties', // style sur la table | ||
| 97 | 'tableCellProperties' // style sur une cellule | ||
| 98 | ] | ||
| 99 | }, | ||
| 100 | |||
| 101 | // langues dispo pour TextPartLanguage | ||
| 102 | language: { | ||
| 103 | textPartLanguage: [ | ||
| 104 | { title: 'Arabic', languageCode: 'ar' }, | ||
| 105 | { title: 'English', languageCode: 'en' }, | ||
| 106 | { title: 'French', languageCode: 'fr' }, | ||
| 107 | { title: 'German', languageCode: 'de' }, | ||
| 108 | { title: 'Hebrew', languageCode: 'he' }, | ||
| 109 | { title: 'Spanish', languageCode: 'es' } | ||
| 110 | ] | ||
| 111 | }, | ||
| 112 | |||
| 113 | // plugin code HTML | ||
| 114 | // voir doc | ||
| 115 | |||
| 116 | // media embarqué (audio, vidéo, carte) | ||
| 117 | //mediaEmbed: { | ||
| 118 | //previewsInData: true, | ||
| 119 | // vaut "false" par defaut, on crée la balise non standard <oembed url="" > // https://oembed.com/ | ||
| 120 | // qui nécessite un traitement (en JS ou côté serveur) en utilisant le lien à l'intérieur | ||
| 121 | // avec "true", on crée la balise <iframe> qui sert à insérer une page HTML dans une autre, | ||
| 122 | // notre "embarquement de média" devrait donc réussir quelque soit le site | ||
| 123 | // c'est plus simple, il ne reste plus qu'à ajuster le contenu avec du CSS (important) | ||
| 124 | // on doit supprimer le positionnement absolu qui fait que l'iframe se place par dessus le reste et adapter la taille de l'élément | ||
| 125 | |||
| 126 | // en outre, le plugin mediaEmbed (dans l'éditeur), tout comme la balise "iframe" (hors éditeur), | ||
| 127 | // permettent d'afficher un aperçu (preview): une image avec un bouton lecture dessus | ||
| 128 | // cette image est envoyée imédiatement par le serveur, et le contenu après un clic dessus | ||
| 129 | // | ||
| 130 | //}, | ||
| 131 | |||
| 132 | // images | ||
| 133 | // le plugin "simple upload adapter" communique avec le serveur au moyen de requêtes AJAX | ||
| 134 | // il attend du serveur une réponse au format .json contenant l'adresse où est stockée l'image: | ||
| 135 | // {"url": "https://example.com/images/foo.jpg"} | ||
| 136 | simpleUpload: { | ||
| 137 | uploadUrl: upload_url, | ||
| 138 | |||
| 139 | // Headers supplémentaires envoyés avec la requête | ||
| 140 | // c'est ici qu'on installe les mécanismes de sécurités comme l'authentification et la protection au CSRF | ||
| 141 | headers: { | ||
| 142 | //'X-CSRF-TOKEN': 'CSRF-Token', | ||
| 143 | //Authorization: 'Bearer <JSON Web Token>' | ||
| 144 | } | ||
| 145 | }, | ||
| 146 | // formats acceptés par défaut: jpeg, png, gif, bmp, webp, tiff | ||
| 147 | // le svg n'est pas dans la liste, pour raison de sécurité apparemment, il parait qu'on peut mettre du javascript à l'intérieur | ||
| 148 | |||
| 149 | // plugin autosave | ||
| 150 | // voir doc | ||
| 151 | } ) | ||
| 152 | .then( newEditor => { | ||
| 153 | editor = newEditor; | ||
| 154 | // fin de ClassicEditor | ||
| 155 | |||
| 156 | // obtenir la liste des éléments disponibles pour la barre d'outils | ||
| 157 | //alert(Array.from( editor.ui.componentFactory.names() )); | ||
| 158 | // obtenir la liste des plugins disponibles: | ||
| 159 | //alert(ClassicEditor.builtinPlugins.map( plugin => plugin.pluginName )); | ||
| 160 | |||
| 161 | // insertion du HTML existant | ||
| 162 | editor.setData(html_existant); | ||
| 163 | } ) | ||
| 164 | .catch( error => { | ||
| 165 | console.error( error ); | ||
| 166 | } ); | ||
| 167 | </script> | ||
| 168 | </form> | ||
| 169 | </div> | ||
| 170 | <?php | ||
| 171 | $editeurHTML = ob_get_clean(); | ||
diff --git a/src/templates/page.php b/src/templates/page.php new file mode 100644 index 0000000..98e7979 --- /dev/null +++ b/src/templates/page.php | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | <!DOCTYPE html> | ||
| 2 | <html lang="fr"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <title></title> | ||
| 6 | <link rel="icon" type="image/png" href=""> | ||
| 7 | <link rel="stylesheet" href="lib/ckeditor5/article_hors_editeur.css" /> | ||
| 8 | <?= $css_editeur ?> | ||
| 9 | </head> | ||
| 10 | <body> | ||
| 11 | <div> | ||
| 12 | <?= $contenu ?> | ||
| 13 | </div> | ||
| 14 | </body> | ||
| 15 | </html> | ||
