summaryrefslogtreecommitdiff
path: root/view/template-formulaires.php
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2022-02-17 18:13:00 +0100
committerpolo <ordipolo@gmx.fr>2022-02-17 18:13:00 +0100
commit787d03e48471ba62cd830379428f04d996f0b74b (patch)
treee9f98c7b9288c4530b50985688dd82622106ba2d /view/template-formulaires.php
parent29df6f1362745eabf4fbcaedf309eb63795152fa (diff)
downloadmelaine-787d03e48471ba62cd830379428f04d996f0b74b.zip
model update
Diffstat (limited to 'view/template-formulaires.php')
-rw-r--r--view/template-formulaires.php375
1 files changed, 177 insertions, 198 deletions
diff --git a/view/template-formulaires.php b/view/template-formulaires.php
index e864719..a3dc9cd 100644
--- a/view/template-formulaires.php
+++ b/view/template-formulaires.php
@@ -1,199 +1,178 @@
1<?php 1<?php
2// view/template-formulaire.php 2// view/template-formulaire.php
3 3
4// voici la partie HTML et javascript du ckeditor, 4// voici la partie HTML et javascript du ckeditor:
5// elle permette d'insérer l'éditeur dans la page avec un formulaire qui comporte 4 éléments: 5// on crée un formulaire <form> avec zones de saisie et boutons
6// barre d'outils, zone de saisie, formulaire caché, bouton "valider" 6// l'éditeur est inséré par lib/ckeditor5/build/ckeditor.js
7 7// en remplaçant les balises <textarea>
8// le formulaire est presque classique, en effet on utilise le ckeditor 5 dans sa version "Document" (ou DecoupledEditor): 8
9// - avantage: plus de possibilité dans la barre d'outils (se rapproche d'un traitement de texte) 9// il est en version "Classic" customizée pour pouvoir ressembler à la version "DecoupledEditor", on peut le créer ici:
10// - inconvénient: on ne peut utiliser la balise <textarea> qu'on remplace par une <div> 10// https://ckeditor.com/ckeditor-5/online-builder/
11 11// la version DecoupledEditor ne peut remplacer une <textarea>, il faut une <div>, ce qui interdit la méthode 1) ci-dessous
12// pour pouvoir envoyer le contenu dans le POST, on a deux possibilité: 12
13// - savoir coder en javascript ! et donc faire de l'AJAX, dans ce cas on n'a même plus besoin de formulaire, le contenu est envoyé au serveur au fur et à mesure! ce sera l'objet d'une future amélioration 13// pour envoyer les données au serveur, on a trois méthodes:
14// - utiliser l'astuce du formulaire caché ! 14// 1) facile: le JS de la bibliothèque remplace la <textarea> par l'éditeur, noter que c'est impossible avec la version DecoupledEditor du ckeditor5, d'ou le choix la "custom"
15 15// 2) moins facile; technique du formulaire caché (l'éditeur remplace une <div>), à la validation, du JS (appelé avec onload="") place les données dans l'<input> caché duquel part le POST
16// le javascript place les données dans le formulaire caché 16// 3) méthode AJAX (compliquée): le formulaire ni le bouton "valider" ne sont plus nécessaires et l'enregistrement des données est immédiat
17// avec la fonction envoiDonnees(), les données se retrouvent dans une <input> et sont donc envoyées avec par la POST (haha) 17
18 18
19 19// falsification de requête inter-site - CSRF
20// variable $inputsAlbum (page discographie uniquement) 20// à faire plus tard
21// note: on ne peut préremplir le champ input texte 21
22// solution: on affiche le nom du fichier, si l'utilisateur en télécharge un autre, on le remplace 22
23ob_start(); 23// variable $inputsAlbum (page discographie uniquement)
24?> 24// attention: on ne peut préremplir le champ input file
25 <p><i>Infos qui seront affichées sur cette page:</i></p> 25// solution: on affiche le nom du fichier, si l'utilisateur en télécharge un autre, on le remplace;
26 <label for="titre" >Titre</label> 26// il devient donc impossible de n'avoir aucun fichier
27 <input type="text" name="titre" value="<?= $vignette[0] ?>" required ><br /> 27ob_start();
28 <label for="annee" >Année</label> 28?>
29 <input type="text" name="annee" value="<?= $vignette[1] ?>" required ><br /> 29 <img class="imageFormulaire" src="<?= $imageFormulaire ?>" >
30 <label for="pochette" >Pochette du disque (jpg ou png) : <?= $vignette[2] ?></label><br/> 30 <br/>
31 <input type="file" name="upload" > 31 <p><i>Infos qui seront affichées sur cette page:</i></p>
32 <br /><br /> 32 <label for="titre" >Titre</label>
33<?php 33 <input type="text" name="titre" value="<?= $vignette[0] ?>" required ><br />
34$inputsAlbum = ob_get_clean(); 34 <label for="annee" >Année</label>
35 35 <input type="text" name="annee" value="<?= $vignette[1] ?>" required ><br />
36 36 <label for="pochette" >Pochette du disque (jpg ou png) : <?= $vignette[2] ?></label><br/>
37// variable $editeurHTML 37 <input type="file" name="upload" ><br /><br /><br />
38ob_start(); 38<?php
39?> 39$inputsAlbum = ob_get_clean();
40 40
41 <div class="conteneur_article" > 41
42 <form action="index.php?page=<?= $page_actuelle ?>&article=<?= $album_code ?>&action=submit" method="post" enctype="multipart/form-data" > 42// variable $editeurHTML
43<?php 43ob_start();
44if($page_actuelle == 'discographie') 44?>
45{ 45
46 echo($inputsAlbum . "<p><i>Infos qui seront affichées dans la page spécifique à cet album:</i></p>"); 46 <div class="conteneur_article" >
47} 47 <form action="index.php?page=<?= $page_actuelle ?><?= $fileCodeArgument ?>&action=submit" method="post" enctype="multipart/form-data" >
48?> 48<?php
49 49if($page_actuelle == 'discographie')
50 <textarea id="editor" name="contenu" ></textarea> 50{
51<?php 51 echo($inputsAlbum . "<p><i>Infos qui seront affichées dans la page spécifique à cet album:</i></p>");
52// falsification de requête inter-site 52}
53// on ajoute un formulaire caché avec une valeur aléatoire cryptée utilisable une seule fois (=jeton) 53?>
54 54
55// "Vous pouvez rendre chaque jeton utilisable une seule fois et ainsi éviter de rejouer plusieurs fois la même requête. 55 <textarea id="editor" name="contenu" ></textarea>
56// Les jetons sont stockés dans le back-office. 56 <input class="boutonSubmitEditeur" type="submit" value="Valider" />
57// Une rotation des jetons est effectuée quand le nombre maximum a été atteint, les plus vieux en premier. 57 <a class="boutonAnnuler" href="index.php?page=<?= $page_actuelle ?>" >
58// Chaque jeton peut être lié à une URL spécifique. 58 <input type="button" value="Annuler" />
59// Si un jeton est intercepté, il ne peut pas être utilisé dans un autre contexte. 59 </a>
60// Si besoin, les jetons peuvent être attachés à une adresse IP spécifique. 60
61// Depuis la version v2.1, les jetons peuvent être réutilisés (par exemple pour les requêtes AJAX). 61 <script>
62// Si vous n’utilisez pas un framework qui gère la protection CSRF pour vous, jetez un oeil à Anti-CSRF." 62 let editor; // variable utilisable depuis une fonction
63 63
64// une bibli qui fait ça: https://github.com/paragonie/anti-csrf 64 // ATTENTION: si l'éditeur ne fonctionne pas, empêcher qu'on puisse cliquer sur Valider!
65?> 65 // Il y a aussi des paramètres dans le fichier de config: ckeditor/webpack.config.js
66 66 ClassicEditor
67 <input class="boutonSubmitEditeur" type="submit" value="Valider" onclick="envoiDonnees();" /> 67 .create( document.querySelector( '#editor' ),{
68 68 language: 'fr',
69 <script> 69
70 let editor; // variable utilisable depuis une fonction 70 // barre d'outils à adapter au "custom build"
71 71 toolbar: {
72 // code exécuté à la validation du formulaire 72 items: [ 'undo', 'redo', 'selectAll', '|', 'heading', '|', 'alignment', 'bulletedList', 'numberedList', 'todoList', 'blockQuote', 'horizontalLine', '-', 'bold', 'italic', 'underline',
73 function envoiDonnees() 73 //'strikethrough',
74 { 74 '|',
75 // supprimer le positionnement absolu de l'iframe 75 //'fontFamily',
76 //~ let balisesIframe = document.getElementsByTagName("iframe"); 76 'fontColor', 'fontSize', 'highlight', '|', 'imageInsert', 'link',
77 //~ for(var i = 0; i < balisesIframe.length; i++) 77 //'htmlEmbed',
78 //~ { 78 //'mediaEmbed',
79 //~ alert(balisesIframe[i].getAttribute("style")); // affiche le CSS 79 'insertTable'
80 //~ balisesIframe[i].removeAttribute("style"); 80 //'|', 'textPartLanguage'
81 //~ alert(balisesIframe[i].getAttribute("style")); // affiche null 81 ],
82 //~ } 82 // multiligne automatique (le '-' dans la liste permet de choisir l'endroit où couper)
83 } 83 shouldNotGroupWhenFull: true
84 84
85 // ATTENTION: si l'éditeur ne fonctionne pas, empêcher qu'on puisse cliquer sur Valider! 85 // noter que 'fontFamily' ne comporte que des polices microsoft => problèmes de droits?
86 // Il y a aussi des paramètres dans le fichier de config: ckeditor/webpack.config.js 86 // de plus, il faut que le navigateur connaisse toutes les polices
87 ClassicEditor 87 },
88 .create( document.querySelector( '#editor' ),{ 88
89 language: 'fr', 89 // barre d'outils dans une image
90 90 image: {
91 // barre d'outils à adapter au build 91 toolbar: [
92 toolbar: { 92 'imageTextAlternative',
93 items: [ 'undo', 'redo', 'selectAll', '|', 'heading', '|', 'alignment', 'bulletedList', 'numberedList', 'todoList', 'blockQuote', 'horizontalLine', '-', 'bold', 'italic', 'underline', 93 'imageStyle:full',
94 //'strikethrough', 94 'imageStyle:side',
95 '|', 95 'linkImage'
96 //'fontFamily', 96 ]
97 'fontColor', 'fontSize', 'highlight', '|', 'imageInsert', 'link', 97 },
98 //'htmlEmbed', 98
99 //'mediaEmbed', 99 // barre d'outils dans un tableau
100 'insertTable' 100 table: {
101 //'|', 'textPartLanguage' 101 contentToolbar: [
102 ], 102 'tableColumn',
103 // multiligne automatique (le '-' dans la liste permet de choisir l'endroit où couper) 103 'tableRow',
104 shouldNotGroupWhenFull: true 104 'mergeTableCells',
105 105 //'tableCellProperties',
106 // noter que 'fontFamily' ne comporte que des polices microsoft => problèmes de droits? 106 'tableProperties'
107 // de plus, il faut que le navigateur connaisse toutes les polices 107 ]
108 }, 108 },
109 109
110 // barre d'outils dans une image 110 // plugin code HTML
111 image: { 111 // pour coller le code HTML "embed" proposé par certains sites
112 toolbar: [ 112
113 'imageTextAlternative', 113 // media embarqué (audio, vidéo, carte)
114 'imageStyle:full', 114 //mediaEmbed: {
115 'imageStyle:side', 115 //previewsInData: true,
116 'linkImage' 116 // vaut "false" par defaut, on crée la balise non standard <oembed url="" > // https://oembed.com/
117 ] 117 // qui nécessite un traitement (en JS ou côté serveur) en utilisant le lien à l'intérieur
118 }, 118 // avec "true", on crée la balise <iframe> qui sert à insérer une page HTML dans une autre,
119 119 // notre "embarquement de média" devrit donc réussir quelque soit le site
120 // barre d'outils dans un tableau 120 // c'est plus simple, il ne reste plus qu'à ajuster le contenu avec du CSS (important)
121 table: { 121 // 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
122 contentToolbar: [ 122
123 'tableColumn', 123 // en outre, le plugin mediaEmbed (dans l'éditeur), tout comme la balise "iframe" (hors éditeur),
124 'tableRow', 124 // permettent d'afficher un aperçu (preview): une image avec un bouton lecture dessus
125 'mergeTableCells', 125 // cette image est envoyée imédiatement par le serveur, et le contenu après un clic dessus
126 //'tableCellProperties', 126 //
127 'tableProperties' 127 //},
128 ] 128
129 }, 129 // images
130 130 // https://ckeditor.com/docs/ckeditor5/latest/features/image-upload/simple-upload-adapter.html
131 // plugin code HTML 131 // le plugin "image upload" s'occupe est la partie graphique, il crée un objet "createLoader",
132 // pour coller le code HTML "embed" proposé par certains sites 132 // le plugin "simple upload adapter" est la classe qui communique avec le serveur au moyen de requêtes AJAX
133 133 // il attend du serveur une réponse au format .json contenant l'adresse où est stockée l'image:
134 // media embarqué (audio, vidéo, carte) 134 //{
135 //mediaEmbed: { 135 // "url": "https://example.com/images/foo.jpg"
136 //previewsInData: true, 136 //}
137 // vaut "false" par defaut, on crée la balise non standard <oembed url="" > // https://oembed.com/ 137 // cette url sert à deux choses:
138 // qui nécessite un traitement (en JS ou côté serveur) en utilisant le lien à l'intérieur 138 // - à télécharger immédiatement l'image envoyée pour la placer dans l'éditeur
139 // avec "true", on crée la balise <iframe> qui sert à insérer une page HTML dans une autre, 139 // - à inclure l'adresse de l'image dans le HTML produit par l'éditeur
140 // notre "embarquement de média" devrit donc réussir quelque soit le site 140 simpleUpload: {
141 // c'est plus simple, il ne reste plus qu'à ajuster le contenu avec du CSS (important) 141 uploadUrl: 'index.php?action=upload_image&page=<?= $page_actuelle ?>',
142 // 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 142 // noter qu'il est possible (parce que souhaitable je ne pense pas) d'envoyer une requête AJAX
143 143 // en indiquant une adresse "statique" du type: fichier.txt ou .xml, jpg, png, etc,
144 // en outre, le plugin mediaEmbed (dans l'éditeur), tout comme la balise "iframe" (hors éditeur), 144
145 // permettent d'afficher un aperçu (preview): une image avec un bouton lecture dessus 145 // Headers supplémentaires envoyés avec la requête
146 // cette image est envoyée imédiatement par le serveur, et le contenu après un clic dessus 146 // c'est ici qu'on installe les mécanismes de sécurités comme l'authentification et la protection au CSRF
147 // 147 headers: {
148 //}, 148 'X-CSRF-TOKEN': 'CSRF-Token',
149 149 Authorization: 'Bearer <JSON Web Token>'
150 // images 150 }
151 // https://ckeditor.com/docs/ckeditor5/latest/features/image-upload/simple-upload-adapter.html 151 },
152 // le plugin "image upload" s'occupe est la partie graphique, il crée un objet "createLoader", 152 // formats acceptés par défaut: jpeg, png, gif, bmp, webp, tiff
153 // le plugin "simple upload adapter" est la classe qui communique avec le serveur au moyen de requêtes AJAX 153 // 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
154 // il attend du serveur une réponse au format .json contenant l'adresse où est stockée l'image: 154 // ce plugin est simple (JS pur) et n'oblige pas le serveur à disposer de l'extension GD
155 //{ 155 // niveau perfs, on garde le choix d'utiler GD ou imagemagick ou un système d'onglets
156 // "url": "https://example.com/images/foo.jpg" 156
157 //} 157 // plugin autosave
158 // cette url sert à deux choses: 158 } )
159 // - à télécharger immédiatement l'image envoyée pour la placer dans l'éditeur 159
160 // - à inclure l'adresse de l'image dans le HTML produit par l'éditeur 160 .then( newEditor => {
161 simpleUpload: { 161 editor = newEditor;
162 uploadUrl: 'index.php?action=upload_image&page=<?= $page_actuelle ?>', 162
163 // noter qu'il est possible (parce que souhaitable je ne pense pas) d'envoyer une requête AJAX 163 // obtenir la liste des éléments disponibles pour la barre d'outils
164 // en indiquant une adresse "statique" du type: fichier.txt ou .xml, jpg, png, etc, 164 //alert(Array.from( editor.ui.componentFactory.names() ));
165 165 // obtenir la liste des plugins disponibles:
166 // Headers supplémentaires envoyés avec la requête 166 //alert(ClassicEditor.builtinPlugins.map( plugin => plugin.pluginName ));
167 // c'est ici qu'on installe les mécanismes de sécurités comme l'authentification et la protection au CSRF 167
168 headers: { 168 var initial = '<?= $texte ?>';
169 'X-CSRF-TOKEN': 'CSRF-Token', 169 editor.setData(initial);
170 Authorization: 'Bearer <JSON Web Token>' 170 } )
171 } 171 .catch( error => {
172 }, 172 console.error( error );
173 // formats acceptés par défaut: jpeg, png, gif, bmp, webp, tiff 173 } );
174 // 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 174 </script>
175 // ce plugin est simple (JS pur) et n'oblige pas le serveur à disposer de l'extension GD 175 </form>
176 // niveau perfs, on garde le choix d'utiler GD ou imagemagick ou un système d'onglets 176 </div>
177 177<?php
178 // plugin autosave
179 } )
180
181 .then( newEditor => {
182 editor = newEditor;
183
184 // obtenir la liste des éléments disponibles pour la barre d'outils
185 //alert(Array.from( editor.ui.componentFactory.names() ));
186 // obtenir la liste des plugins disponibles:
187 //alert(ClassicEditor.builtinPlugins.map( plugin => plugin.pluginName ));
188
189 var initial = '<?= $texte ?>';
190 editor.setData(initial);
191 } )
192 .catch( error => {
193 console.error( error );
194 } );
195 </script>
196 </form>
197 </div>
198<?php
199$editeurHTML = ob_get_clean(); \ No newline at end of file 178$editeurHTML = ob_get_clean(); \ No newline at end of file