aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2025-09-10 01:02:36 +0200
committerpolo <ordipolo@gmx.fr>2025-09-10 01:02:36 +0200
commitefd79d15adef2a27347c25ebb19754e9937f9715 (patch)
tree6114ec988ef4dfd7cdf2e2ca07cc9762f8fec4c3
parent5e41bea598ff38b3c520b69fd92ee3412e716df2 (diff)
downloadcms-efd79d15adef2a27347c25ebb19754e9937f9715.zip
modification d'une URL page Menu et chemin, htmlspecialchars sur les URL du menu à l'affichage
-rw-r--r--public/assets/save-nb.svg1
-rw-r--r--public/assets/save.svg37
-rw-r--r--public/css/menu.css11
-rw-r--r--public/js/menu.js94
-rw-r--r--src/controller/MenuAndPathsController.php30
-rw-r--r--src/router.php5
-rw-r--r--src/view/MenuBuilder.php30
-rw-r--r--src/view/NavBuilder.php2
-rw-r--r--src/view/templates/menu.php11
9 files changed, 188 insertions, 33 deletions
diff --git a/public/assets/save-nb.svg b/public/assets/save-nb.svg
new file mode 100644
index 0000000..f7fe755
--- /dev/null
+++ b/public/assets/save-nb.svg
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M7 19V13H17V19H19V7.82843L16.1716 5H5V19H7ZM4 3H17L21 7V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3ZM9 15V19H15V15H9Z"></path></svg> \ No newline at end of file
diff --git a/public/assets/save.svg b/public/assets/save.svg
new file mode 100644
index 0000000..f2405a5
--- /dev/null
+++ b/public/assets/save.svg
@@ -0,0 +1,37 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 viewBox="0 0 24 24"
4 fill="currentColor"
5 version="1.1"
6 id="svg1"
7 sodipodi:docname="save.svg"
8 inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
9 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns="http://www.w3.org/2000/svg"
12 xmlns:svg="http://www.w3.org/2000/svg">
13 <defs
14 id="defs1" />
15 <sodipodi:namedview
16 id="namedview1"
17 pagecolor="#ffffff"
18 bordercolor="#000000"
19 borderopacity="0.25"
20 inkscape:showpageshadow="2"
21 inkscape:pageopacity="0.0"
22 inkscape:pagecheckerboard="0"
23 inkscape:deskcolor="#d1d1d1"
24 inkscape:zoom="5.5"
25 inkscape:cx="16.090909"
26 inkscape:cy="19.545455"
27 inkscape:window-width="1330"
28 inkscape:window-height="1032"
29 inkscape:window-x="586"
30 inkscape:window-y="0"
31 inkscape:window-maximized="1"
32 inkscape:current-layer="svg1" />
33 <path
34 style="fill:#dd3333;stroke:#dd3333;stroke-width:0.687187"
35 d="M 3.7700827,20.814384 C 3.5436283,20.721841 3.2752699,20.427615 3.1737307,20.160547 3.0560503,19.851024 3.007568,16.786203 3.0400116,11.70747 3.0870587,4.3427109 3.1154561,3.7154214 3.415392,3.4154406 3.712924,3.1178639 4.2867777,3.0867003 10.324483,3.0402361 l 6.584608,-0.050673 2.052117,2.0601995 2.052117,2.0601994 -0.05212,6.575086 c -0.04777,6.027435 -0.07915,6.602112 -0.37665,6.899564 -0.300533,0.300481 -0.91908,0.327199 -8.363636,0.361255 C 7.6163,20.966927 4.0059136,20.910757 3.7700807,20.814384 Z M 7,16 v -3 h 5 5 v 3 3 h 1 1 V 13.322457 7.6449149 L 17.686369,6.3224575 16.372737,5 H 10.686369 5 v 7 7 h 1 1 z m 8,1 V 15 H 12 9 v 2 2 h 3 3 z"
36 id="path2" />
37</svg>
diff --git a/public/css/menu.css b/public/css/menu.css
index 62e6623..57d842a 100644
--- a/public/css/menu.css
+++ b/public/css/menu.css
@@ -70,7 +70,7 @@
70 vertical-align: middle; 70 vertical-align: middle;
71 border: 2px transparent solid; 71 border: 2px transparent solid;
72} 72}
73.menu #location:hover, .menu input[type=submit]:hover, .move_entry_icon:hover 73.menu #location:hover, .menu input[type=submit]:hover, .menu .move_entry_icon:hover
74{ 74{
75 background-color: yellow; 75 background-color: yellow;
76 border-radius: 4px; 76 border-radius: 4px;
@@ -106,8 +106,17 @@
106{ 106{
107 margin: 5px; 107 margin: 5px;
108 display: flex; 108 display: flex;
109 flex-wrap: wrap;
109 align-items: center; 110 align-items: center;
110} 111}
112.menu .url
113{
114 text-wrap: nowrap;
115}
116.menu .url input[type=url]
117{
118 width: 50%;
119}
111 120
112@media screen and (min-width: 80rem) { 121@media screen and (min-width: 80rem) {
113 i{} 122 i{}
diff --git a/public/js/menu.js b/public/js/menu.js
index 8f1f326..ef58c42 100644
--- a/public/js/menu.js
+++ b/public/js/menu.js
@@ -122,7 +122,7 @@ function checkMenuEntry(page_id){
122 const checkbox = clicked_menu_entry.querySelector("input"); 122 const checkbox = clicked_menu_entry.querySelector("input");
123 let color; 123 let color;
124 124
125 fetch('index.php?menu_edit=displayInMenu', { 125 fetch('index.php?menu_edit=display_in_menu', {
126 method: 'POST', 126 method: 'POST',
127 headers: { 127 headers: {
128 'Content-Type': 'application/json' 128 'Content-Type': 'application/json'
@@ -131,16 +131,14 @@ function checkMenuEntry(page_id){
131 }) 131 })
132 .then(response => response.json()) 132 .then(response => response.json())
133 .then(data => { 133 .then(data => {
134 if(data.success) 134 if(data.success){
135 {
136 color = checkbox.checked ? "#ff1d04" : "grey"; 135 color = checkbox.checked ? "#ff1d04" : "grey";
137 clicked_menu_entry.querySelector("button").style.color = color; 136 clicked_menu_entry.querySelector("button").style.color = color;
138 137
139 nav_zone.innerHTML = ''; 138 nav_zone.innerHTML = '';
140 nav_zone.insertAdjacentHTML('afterbegin', data.nav); 139 nav_zone.insertAdjacentHTML('afterbegin', data.nav);
141 } 140 }
142 else { 141 else{
143
144 console.error('Échec de l\'inversion'); 142 console.error('Échec de l\'inversion');
145 } 143 }
146 }) 144 })
@@ -149,13 +147,89 @@ function checkMenuEntry(page_id){
149 }); 147 });
150} 148}
151 149
152 150// seul la modification des URL est possible pour l'instant, les noms des entrées de menu attendront
153function editUrlEntry(page_id){ 151function editUrlEntry(page_id){
154 const selected_div = document.getElementById(page_id); 152 const parent_div = document.getElementById(page_id);
155 console.log(selected_div.id); 153 const url_input = parent_div.querySelector('.url').querySelector('input').value;
154
155 fetch('index.php?menu_edit=edit_url_entry', {
156 method: 'POST',
157 headers: {
158 'Content-Type': 'application/json'
159 },
160 body: JSON.stringify({ id: page_id, url_input: url_input })
161 })
162 .then(response => response.json())
163 .then(data => {
164 if(data.success){
165 findParentByTagName(document.getElementById('m_' + page_id), 'a').href = data.url_input; // MAJ menu
166 toastNotify("Nouvelle adresse enregistrée avec succès")
167 }
168 else{
169 toastNotify("Erreur rencontrée par le serveur, changements non pris en compte");
170 console.error("Erreur rencontrée par le serveur, changements non pris en compte");
171 }
172 })
173 .catch(error => {
174 console.error('Erreur:', error);
175 });
156} 176}
157 177
158function deleteUrlEntry(page_id){ 178
179
180// code à recycler pour pouvoir modifier le nom de l'entrée de menu correspondant aux liens
181/*function editUrlEntry(page_id){
182 const parent_div = document.getElementById(page_id);
183 parent_div.querySelector('i').classList.add('hidden');
184 parent_div.querySelector('.url').querySelector('input').classList.remove('hidden');
185 parent_div.querySelector('#edit-i' + page_id).classList.add('hidden');
186 parent_div.querySelector('#delete-i' + page_id).querySelector('input[type=image]').classList.add('hidden');
187 parent_div.querySelector('#cancel-i' + page_id).querySelector('button').classList.remove('hidden');
188 parent_div.querySelector('#submit-i' + page_id).querySelector('input[type=submit]').classList.remove('hidden');
189}
190function cancelUrlEntry(page_id){
191 const parent_div = document.getElementById(page_id);
192 parent_div.querySelector('.url').querySelector('input').value = parent_div.querySelector('i').textContent; // textContent (contrairement à innerHTML) ne transforme pas les & en entités HTML
193 closeUrlEntry(page_id, parent_div);
194}
195function submitUrlEntry(page_id){
196 const parent_div = document.getElementById(page_id);
197 const url_input = parent_div.querySelector('.url').querySelector('input').value;
198
199 fetch('index.php?menu_edit=edit_url_entry', {
200 method: 'POST',
201 headers: {
202 'Content-Type': 'application/json'
203 },
204 body: JSON.stringify({ id: page_id, url_input: url_input })
205 })
206 .then(response => response.json())
207 .then(data => {
208 if(data.success){
209 parent_div.querySelector('i').innerHTML = data.url_input; // MAJ <i>
210 findParentByTagName(document.getElementById('m_' + page_id), 'a').href = data.url_input; // MAJ menu
211 closeUrlEntry(page_id, parent_div);
212 }
213 else{
214 toastNotify("Erreur rencontrée par le serveur, changements non pris en compte");
215 console.error("Erreur rencontrée par le serveur, changements non pris en compte");
216 }
217 })
218 .catch(error => {
219 console.error('Erreur:', error);
220 });
221}
222function closeUrlEntry(page_id, parent_div){
223 parent_div.querySelector('i').classList.remove('hidden');
224 parent_div.querySelector('.url').querySelector('input').classList.add('hidden');
225 parent_div.querySelector('#edit-i' + page_id).classList.remove('hidden');
226 parent_div.querySelector('#delete-i' + page_id).querySelector('input[type=image]').classList.remove('hidden');
227 parent_div.querySelector('#cancel-i' + page_id).querySelector('button').classList.add('hidden');
228 parent_div.querySelector('#submit-i' + page_id).querySelector('input[type=submit]').classList.add('hidden');
229}*/
230
231/*function deleteUrlEntry(page_id){
159 const selected_div = document.getElementById(page_id); 232 const selected_div = document.getElementById(page_id);
160 console.log(selected_div.id); 233 console.log(selected_div.id);
161} \ No newline at end of file 234}*/
235
diff --git a/src/controller/MenuAndPathsController.php b/src/controller/MenuAndPathsController.php
index d429287..5779b39 100644
--- a/src/controller/MenuAndPathsController.php
+++ b/src/controller/MenuAndPathsController.php
@@ -14,9 +14,15 @@ class MenuAndPathsController
14 $previous_page = Director::$menu_data->findPageById((int)$_POST["location"]); // (int) à cause de declare(strict_types=1); 14 $previous_page = Director::$menu_data->findPageById((int)$_POST["location"]); // (int) à cause de declare(strict_types=1);
15 $parent = $previous_page->getParent(); 15 $parent = $previous_page->getParent();
16 16
17 $url_input = trim($_POST["url_input"]); // faire htmlspecialchars à l'affichage
18 if(!filter_var($url_input, FILTER_VALIDATE_URL) || !str_starts_with($url_input, 'http')){
19 header("Location: " . new URL(['page' => $_GET['from'], 'error' => 'invalide_url']));
20 die;
21 }
22
17 $page = new Page( 23 $page = new Page(
18 trim(htmlspecialchars($_POST["label_input"])), 24 trim(htmlspecialchars($_POST["label_input"])),
19 filter_var($_POST["url_input"], FILTER_VALIDATE_URL), 25 $url_input,
20 true, true, false, 26 true, true, false,
21 $previous_page->getPosition(), 27 $previous_page->getPosition(),
22 $parent); // peut et DOIT être null si on est au 1er niveau 28 $parent); // peut et DOIT être null si on est au 1er niveau
@@ -24,7 +30,7 @@ class MenuAndPathsController
24 // on a donné à la nouvelle entrée la même position qu'à la précédente, 30 // on a donné à la nouvelle entrée la même position qu'à la précédente,
25 // addChild l'ajoute à la fin du tableau "children" puis on trie 31 // addChild l'ajoute à la fin du tableau "children" puis on trie
26 // exemple avec 2 comme position demandée: 1 2 3 4 2 devient 1 2 3 4 5 et la nouvelle entrée sera en 3è position 32 // exemple avec 2 comme position demandée: 1 2 3 4 2 devient 1 2 3 4 5 et la nouvelle entrée sera en 3è position
27 if($parent == null){ 33 if(!$parent){
28 $parent = Director::$menu_data; 34 $parent = Director::$menu_data;
29 } 35 }
30 $parent->addChild($page); // true pour réindexer les positions en BDD 36 $parent->addChild($page); // true pour réindexer les positions en BDD
@@ -36,6 +42,25 @@ class MenuAndPathsController
36 die; 42 die;
37 } 43 }
38 44
45 static public function editUrlEntry(EntityManager $entityManager, array $json): void
46 {
47 $url_input = trim($json['url_input']); // faire htmlspecialchars à l'affichage
48 $page = $entityManager->find('App\Entity\Page', $json['id']);
49
50 if(!$page){
51 echo json_encode(['success' => false, 'message' => "id invalide"]);
52 }
53 elseif(!filter_var($url_input, FILTER_VALIDATE_URL) || !str_starts_with($url_input, 'http')){
54 echo json_encode(['success' => false, 'message' => "la chaîne envoyée n'est pas une URL valide"]);
55 }
56 else{
57 $page->setEndOfPath($url_input);
58 $entityManager->flush();
59 echo json_encode(['success' => true, 'url_input' => $url_input]);
60 }
61 die;
62 }
63
39 static public function deleteUrlMenuEntry(EntityManager $entityManager): void 64 static public function deleteUrlMenuEntry(EntityManager $entityManager): void
40 { 65 {
41 Director::$menu_data = new Menu($entityManager); 66 Director::$menu_data = new Menu($entityManager);
@@ -163,7 +188,6 @@ class MenuAndPathsController
163 else{ 188 else{
164 echo json_encode(['success' => false]); 189 echo json_encode(['success' => false]);
165 } 190 }
166
167 die; 191 die;
168 } 192 }
169 193
diff --git a/src/router.php b/src/router.php
index 04441a9..3c3c773 100644
--- a/src/router.php
+++ b/src/router.php
@@ -156,9 +156,12 @@ elseif($_SERVER['REQUEST_METHOD'] === 'POST'){
156 elseif($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){ 156 elseif($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){
157 MenuAndPathsController::switchPositions($entityManager, $json); 157 MenuAndPathsController::switchPositions($entityManager, $json);
158 } 158 }
159 elseif($_GET['menu_edit'] === 'displayInMenu' && isset($json['id']) && isset($json['checked'])){ 159 elseif($_GET['menu_edit'] === 'display_in_menu' && isset($json['id']) && isset($json['checked'])){
160 MenuAndPathsController::displayInMenu($entityManager, $json); 160 MenuAndPathsController::displayInMenu($entityManager, $json);
161 } 161 }
162 elseif($_GET['menu_edit'] === 'edit_url_entry' && isset($json['id']) && isset($json['url_input'])){
163 MenuAndPathsController::editUrlEntry($entityManager, $json);
164 }
162 } 165 }
163 166
164 /* -- mode Modification d'une page -- */ 167 /* -- mode Modification d'une page -- */
diff --git a/src/view/MenuBuilder.php b/src/view/MenuBuilder.php
index 9d4dda1..bc64e30 100644
--- a/src/view/MenuBuilder.php
+++ b/src/view/MenuBuilder.php
@@ -13,18 +13,13 @@ class MenuBuilder extends AbstractBuilder
13 //private int $margin_left_multiplier = 29; 13 //private int $margin_left_multiplier = 29;
14 private string $options = ''; 14 private string $options = '';
15 15
16 public function __construct(Node $node = null, bool $template = true) 16 public function __construct(Node $node, bool $template = true)
17 { 17 {
18 //parent::__construct($node); 18 // impossible de me rappeler pourquoi j'ai écrit ce test sur $node, pourquoi $node serait null?
19 $viewFile = $node === null ? self::VIEWS_PATH . 'menu.php' : self::VIEWS_PATH . $node->getName() . '.php'; 19 $viewFile = $node === null ? self::VIEWS_PATH . 'menu.php' : self::VIEWS_PATH . $node->getName() . '.php';
20 20
21 if(file_exists($viewFile)) 21 if(file_exists($viewFile))
22 { 22 {
23 /*if(!empty($node->getNodeData()->getData()))
24 {
25 extract($node->getNodeData()->getData());
26 }*/
27
28 if($_SESSION['admin']){ 23 if($_SESSION['admin']){
29 $this->unfoldMenu(Director::$menu_data); 24 $this->unfoldMenu(Director::$menu_data);
30 25
@@ -63,13 +58,26 @@ class MenuBuilder extends AbstractBuilder
63 </span> 58 </span>
64 <button>' . $entry->getPageName() . '</button>'; 59 <button>' . $entry->getPageName() . '</button>';
65 60
61 // seul la modification des URL est possible pour l'instant, les noms des entrées de menu attendront
66 if(str_starts_with($entry->getEndOfPath(), 'http')){ 62 if(str_starts_with($entry->getEndOfPath(), 'http')){
67 $this->html .= '<span id="edit-i' . $entry->getId() . '"><img class="move_entry_icon" src="assets/edit.svg" onclick="editUrlEntry(' . $entry->getId() . ')"></span> 63 $this->html .= '<form style="display: inline;" id="delete-i' . $entry->getId() . '" method="post" action="' . new URL(['from' => 'menu_chemins']) . '">
68 <i class="url">' . $entry->getEndOfPath() . '</i>
69 <form style="display: inline;" id="delete-i' . $entry->getId() . '" method="post" action="' . new URL(['from' => 'menu_chemins']) . '">
70 <input type="hidden" name="delete" value="' . $entry->getId() . '"> 64 <input type="hidden" name="delete" value="' . $entry->getId() . '">
71 <input type="image" class="move_entry_icon" src="assets/delete-bin.svg" alt="delete link button" onclick="return confirm(\'Voulez-vous vraiment supprimer cette entrée?\');"> 65 <input type="image" class="move_entry_icon" src="assets/delete-bin.svg" alt="delete link button" onclick="return confirm(\'Voulez-vous vraiment supprimer cette entrée?\');">
72 </form>'; 66 </form>
67 <span class="url">
68 <input type="url" value="' . htmlspecialchars($entry->getEndOfPath()) . '">
69 <img class="move_entry_icon" src="assets/save.svg" onclick="editUrlEntry(' . $entry->getId() . ')">
70 </span>';
71
72 // code à recycler pour pouvoir modifier le nom de l'entrée de menu correspondant aux liens
73 /*$this->html .= '<span id="cancel-i' . $entry->getId() . '">
74 <input type="hidden" name="cancel" value="' . $entry->getId() . '">
75 <button class="hidden" onclick="cancelUrlEntry(' . $entry->getId() . ')">Annuler</button>
76 </span>
77 <span id="submit-i' . $entry->getId() . '">
78 <input type="hidden" name="submit" value="' . $entry->getId() . '">
79 <input type="submit" class="hidden" onclick="submitUrlEntry(' . $entry->getId() . ')">
80 </span>';*/
73 } 81 }
74 else{ 82 else{
75 $this->html .= '<i class="path">' . $entry->getPagePath() . '</i>'; 83 $this->html .= '<i class="path">' . $entry->getPagePath() . '</i>';
diff --git a/src/view/NavBuilder.php b/src/view/NavBuilder.php
index a9cf49c..2cbdef9 100644
--- a/src/view/NavBuilder.php
+++ b/src/view/NavBuilder.php
@@ -38,7 +38,7 @@ class NavBuilder extends AbstractBuilder
38 { 38 {
39 if(str_starts_with($data->getEndOfPath(), 'http')) // lien vers autre site 39 if(str_starts_with($data->getEndOfPath(), 'http')) // lien vers autre site
40 { 40 {
41 $link .= '<a href="' . $data->getEndOfPath() . '" target="_blank">'; 41 $link .= '<a href="' . htmlspecialchars($data->getEndOfPath()) . '" target="_blank">';
42 } 42 }
43 elseif($data->getEndOfPath() != '') // lien relatif 43 elseif($data->getEndOfPath() != '') // lien relatif
44 { 44 {
diff --git a/src/view/templates/menu.php b/src/view/templates/menu.php
index 55c9ff9..d78c665 100644
--- a/src/view/templates/menu.php
+++ b/src/view/templates/menu.php
@@ -2,18 +2,18 @@
2<section class="menu"> 2<section class="menu">
3 <h3>Menu et chemins</h3> 3 <h3>Menu et chemins</h3>
4 <div class="new_page_button"> 4 <div class="new_page_button">
5 <p >Créer une <a href="<?= new URL(['page' => 'nouvelle_page']) ?>"><button style="color: #ff1d04;">Nouvelle page</button></a>.</p> 5 <p>Créer une <a href="<?= new URL(['page' => 'nouvelle_page']) ?>"><button style="color: #ff1d04;">Nouvelle page</button></a>.</p>
6 </div> 6 </div>
7 <div class="url_form_zone"> 7 <div class="url_form_zone">
8 <p>Ajouter au menu un lien vers un site web quelconque avec le formulaire ci-dessous:</p> 8 <p>Créer une entrée dans le menu avec une adresse vers un site quelconque:</p>
9 <form method="post" action="<?= new URL(['from' => 'menu_chemins']) ?>"> 9 <form method="post" action="<?= new URL(['from' => 'menu_chemins']) ?>">
10 <p> 10 <p>
11 <label for="label_input">Nom:</label> 11 <label for="label_input">Nom dans le menu:</label>
12 <input id="label_input" type="text" name="label_input"> 12 <input id="label_input" type="text" name="label_input">
13 </p> 13 </p>
14 <p> 14 <p>
15 <label for="url_input">Adresse URL:</label> 15 <label for="url_input">Adresse (collez votre lien):</label>
16 <input id="url_input" type="url" name="url_input"> 16 <input id="url_input" type="url" name="url_input" placeholder="http://">
17 </p> 17 </p>
18 <p> 18 <p>
19 <label>Placer le lien juste après cette entrée:</label> 19 <label>Placer le lien juste après cette entrée:</label>
@@ -38,5 +38,4 @@
38 <div id="menu_edit_buttons"> 38 <div id="menu_edit_buttons">
39<?= $this->html ?> 39<?= $this->html ?>
40 </div> 40 </div>
41
42</section> \ No newline at end of file 41</section> \ No newline at end of file