aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/controller/MaintenanceController.php102
-rw-r--r--src/controller/ViewDirector.php5
-rw-r--r--src/service/Router.php444
-rw-r--r--src/service/router.php441
-rw-r--r--src/view/HeadBuilder.php3
-rw-r--r--src/view/MaintenanceBuilder.php6
6 files changed, 518 insertions, 483 deletions
diff --git a/src/controller/MaintenanceController.php b/src/controller/MaintenanceController.php
index 47e51ac..98f2e60 100644
--- a/src/controller/MaintenanceController.php
+++ b/src/controller/MaintenanceController.php
@@ -5,7 +5,10 @@ declare(strict_types=1);
5 5
6use Doctrine\ORM\EntityManager; 6use Doctrine\ORM\EntityManager;
7use App\Entity\log; 7use App\Entity\log;
8use Symfony\Component\HttpFoundation\File\UploadedFile; 8use Symfony\Component\HttpFoundation\Request;
9use Symfony\Component\HttpFoundation\BinaryFileResponse;
10use Symfony\Component\HttpFoundation\ResponseHeaderBag;
11use Symfony\Component\HttpFoundation\RedirectResponse;
9 12
10class MaintenanceController 13class MaintenanceController
11{ 14{
@@ -51,76 +54,101 @@ class MaintenanceController
51 54
52 static public function getLastDump(EntityManager $entityManager): void 55 static public function getLastDump(EntityManager $entityManager): void
53 { 56 {
54 $backup_list = Backup::getBackupList(); 57 try{
55 $nb = count($backup_list);
56
57 if($nb <= 0){ // se produit à la première connexion en mode admin pour une raison algorithimque
58 Backup::mySQLdump($entityManager, 'auto');
59 $backup_list = Backup::getBackupList(); 58 $backup_list = Backup::getBackupList();
60 $nb = count($backup_list); 59 $nb = count($backup_list);
61 if($nb <= 0){ // improbable, les dossiers devraient déjà avoir été créés 60
62 throw new RuntimeException("Le serveur a rencontré une erreur: aucun backup n'est disponible et ce n'est pas normal."); 61 if($nb <= 0){ // se produit à la première connexion en mode admin pour une raison algorithimque
62 Backup::mySQLdump($entityManager, 'auto');
63 $backup_list = Backup::getBackupList();
64 $nb = count($backup_list);
65 if($nb <= 0){ // improbable, les dossiers devraient déjà avoir été créés
66 throw new RuntimeException("Le serveur a rencontré une erreur: aucun backup n'est disponible et ce n'est pas normal.");
67 }
63 } 68 }
64 }
65 69
66 try{
67 $file_path = Backup::$backup_dir . '/' . $backup_list[$nb - 1]; 70 $file_path = Backup::$backup_dir . '/' . $backup_list[$nb - 1];
68 header('Content-Type: application/octet-stream'); // signifie fichier quelconque, du binaire quoi! 71 $response = new BinaryFileResponse($file_path);
69 header('Content-Disposition: attachment; filename="' . basename($file_path) . '"'); // pour provoquer un téléchargement et non pour afficher 72 $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT); // ne pas essayer de l'afficher dans le navigateur
70 header('Content-Length: ' . filesize($file_path)); // peut servir côté client (barre de progression...)
71 readfile($file_path);
72 die;
73 } 73 }
74 // exeptions lancées dans Backup::mySQLdump 74 catch(RuntimeException $e){
75 catch(RuntimeException $e){ // pas d'info $e pour le client7 75 $_SESSION['flash_message'] = $e->getMessage();
76 header('Location: ' . new URL(['page' => 'maintenance', 'get_last_dump' => $e->getMessage()])); 76 $response = new RedirectResponse((string) new URL(['page' => 'maintenance']));
77 } 77 }
78 $response->send();
78 die; 79 die;
79 } 80 }
80 static public function getAllMedia(): void 81 static public function getAllMedia(): void
81 { 82 {
82 try{ 83 try{
83 $file_path = '../var/' . UserDataService::createZip('all_media.zip', ['user_data/assets', 'user_data/images', 'user_data/media']); 84 $file_path = '../var/' . UserDataService::createZip('all_media.zip', ['user_data/assets', 'user_data/images', 'user_data/media']);
84 header('Content-Type: application/zip'); 85 $response = new BinaryFileResponse($file_path);
85 header('Content-Disposition: attachment; filename="' . basename($file_path) . '"'); // pour provoquer un téléchargement et non pour afficher 86 $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT); // ne pas essayer de l'afficher dans le navigateur
86 header('Content-Length: ' . filesize($file_path)); // peut servir côté client (barre de progression...)
87 readfile($file_path);
88 die;
89 } 87 }
90 // exeptions lancées dans Backup::mySQLdump 88 catch(RuntimeException $e){
91 catch(RuntimeException $e){ // pas d'info $e pour le client7 89 $_SESSION['flash_message'] = $e->getMessage();
92 header('Location: ' . new URL(['page' => 'maintenance', 'get_all_media' => $e->getMessage()])); 90 $response = new RedirectResponse((string) new URL(['page' => 'maintenance']));
93 } 91 }
92 $response->send();
94 die; 93 die;
95 } 94 }
96 95
97 // parce qu'il faut un contrôleur 96 // parce qu'il faut un contrôleur
98 static public function handleBackupSelection(EntityManager $entityManager, string $selected_file): void 97 static public function handleBackupSelection(EntityManager $entityManager, Request $request): void
99 { 98 {
100 if(pathinfo($selected_file)['extension'] !== 'sql'){ // pas censé se produire en fait 99 $selected_file = $request->request->get('selected_sql');
101 throw new Exception("charger un fichier au format SQL"); 100 $url = new URL;
101 if($request->query->has('from')){
102 $url->addParams(['page' => $request->query->get('from')]);
102 } 103 }
103 104
104 Backup::restoreDatabase($entityManager, $selected_file); 105 try{
106 if(pathinfo($selected_file)['extension'] !== 'sql'){ // pas censé se produire en fait
107 throw new Exception("charger un fichier au format SQL");
108 }
109 Backup::restoreDatabase($entityManager, $selected_file);
110
111 $_SESSION['flash_message'] = "La base de données a été restaurée avec succès !!";
112 }
113 catch(Exception $e){
114 $_SESSION['flash_message'] = "Une erreur s'est produite: " . $e->getMessage();
115 }
116
117 $response = new RedirectResponse((string)$url);
118 $response->send();
119 die;
105 } 120 }
106 121
107 static public function downloadSQL(EntityManager $entityManager, UploadedFile $uploaded_file): void 122 static public function downloadSQL(EntityManager $entityManager, Request $request): void
108 { 123 {
109 if(pathinfo($uploaded_file->getClientOriginalName())['extension'] !== 'sql'){ 124 $uploaded_file = $request->files->get('uploaded_sql');
110 throw new Exception("Charger un fichier au format SQL");
111 }
112 //echo $uploaded_file->getSize(); // à garder de côté au cas où
113
114 $date = new DateTime; 125 $date = new DateTime;
115 $server_place = Config::$database . '_' . $date->format('Y-m-d') . '_uploaded.sql'; 126 $server_place = Config::$database . '_' . $date->format('Y-m-d') . '_uploaded.sql';
127 $url = new URL;
128 if($request->query->has('from')){
129 $url->addParams(['page' => $request->query->get('from')]);
130 }
116 131
117 try{ 132 try{
133 if(pathinfo($uploaded_file->getClientOriginalName())['extension'] !== 'sql'){
134 throw new Exception("Charger un fichier au format SQL");
135 }
136 //echo $uploaded_file->getSize(); // à garder de côté au cas où
137
118 // enregistrer le fichier 138 // enregistrer le fichier
119 $uploaded_file->move(Backup::$backup_dir, $server_place); 139 $uploaded_file->move(Backup::$backup_dir, $server_place);
120 140
121 // s'en servir 141 // s'en servir
122 Backup::restoreDatabase($entityManager, $server_place); 142 Backup::restoreDatabase($entityManager, $server_place);
143
144 $_SESSION['flash_message'] = "La base de données a été restaurée avec succès !!";
123 } 145 }
124 finally{} 146 catch(Exception $e){
147 $_SESSION['flash_message'] = "Une erreur s'est produite: " . $e->getMessage();
148 }
149
150 $response = new RedirectResponse((string)$url);
151 $response->send();
152 die;
125 } 153 }
126} \ No newline at end of file 154} \ No newline at end of file
diff --git a/src/controller/ViewDirector.php b/src/controller/ViewDirector.php
index 59629c9..2d37598 100644
--- a/src/controller/ViewDirector.php
+++ b/src/controller/ViewDirector.php
@@ -66,6 +66,11 @@ class ViewDirector extends AbstractBuilder // ViewDirector est aussi le premier
66 /* 4/ construction de la page avec builders et vues */ 66 /* 4/ construction de la page avec builders et vues */
67 $this->useChildrenBuilder(self::$root_node); 67 $this->useChildrenBuilder(self::$root_node);
68 68
69 if(isset($_SESSION['flash_message'])){
70 $this->html .= '<script>window.flash_message = "' . $_SESSION['flash_message'] . '";</script>';
71 unset($_SESSION['flash_message']);
72 }
73
69 return new Response($this->html, 200); 74 return new Response($this->html, 200);
70 } 75 }
71} \ No newline at end of file 76} \ No newline at end of file
diff --git a/src/service/Router.php b/src/service/Router.php
new file mode 100644
index 0000000..ee6d25d
--- /dev/null
+++ b/src/service/Router.php
@@ -0,0 +1,444 @@
1<?php
2// src/service/router.php
3//
4/* fonctionnement:
5=> 1er test, méthode http GET? POST?
6=> 2ème test, type de contenu (méthode POST uniquement):
7"application/x-www-form-urlencoded" = formulaire
8"application/json" = requête AJAX avec fetch()
9"multipart/form-data" = upload d'image par tinymce
10$_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' requête AJAX xhs, non utilisée
11=> 3ème test, comme le 2ème test mais uniquement si IS_ADMIN est vrai
12*/
13
14declare(strict_types=1);
15
16use Symfony\Component\HttpFoundation\Request;
17use Doctrine\ORM\EntityManager;
18
19class Router{
20 // boulot = faire que TOUS les contrôleurs retournent une Response (et faire retourner une réponse par dispatch)
21
22 // exemple de méthode dispatch
23 /*public static function dispatch(Request $request): Response
24 {
25 if ($request->getMethod() === 'GET') {
26 return PageController::home($request);
27 }
28
29 if ($request->getMethod() === 'POST') {
30 return UserController::login($request);
31 }
32
33 return new RedirectResponse('/');
34 }*/
35
36 /* classes de réponses pour les contrôleurs
37 Response($html, Response::HTTP_OK) page html
38 JsonResponse(['success' => true, 'data' => $data]) ajax
39 RedirectResponse('index.php?page=login') redirection
40 BinaryFileResponse($filePath) téléchargement
41 StreamedResponse(function () {echo "ligne 1\n";echo "ligne 2\n";}) gros fichier
42 */
43
44 static public function dispatch(Request $request, EntityManager $entityManager){
45 if($request->getMethod() === 'GET'){
46 // table "user" vide
47 if(!UserController::existUsers($entityManager)){
48 require AbstractBuilder::VIEWS_PATH . 'user_create.php';
49 die;
50 }
51
52 // bouton déconnexion (méthode GET parce que l'utilisateur ne modifie plus de données à partir de là)
53 if($request->query->has('action') && $request->query->get('action') === 'deconnection'){
54 UserController::disconnect($entityManager);
55 }
56
57 // articles suivants
58 if($request->query->has('fetch') && $request->query->get('fetch') === 'next_articles'){
59 ArticleController::fetch($entityManager, $request);
60 }
61
62 // données du calendrier
63 // création du calendrier et changement de dates affichées (boutons flèches mais pas changement de vue)
64 if($request->query->has('action') && $request->query->get('action') === 'get_events'
65 && $request->query->has('start') && $request->query->has('end') && empty($request->getPayload()->all())) // getPayload ne récupère pas que des POST
66 {
67 CalendarController::getData($entityManager);
68 }
69
70 // pages interdites
71 if(!IS_ADMIN && in_array(CURRENT_PAGE, ['menu_paths', 'new_page', 'user_edit', 'emails', 'maintenance'])){
72 header('Location: ' . new URL);
73 die;
74 }
75
76 if(IS_ADMIN){
77 if($request->query->has('action') && $request->query->get('action') === 'get_mysqldump'){
78 MaintenanceController::getLastDump($entityManager);
79 die;
80 }
81 if($request->query->has('action') && $request->query->get('action') === 'get_all_media'){
82 MaintenanceController::getAllMedia();
83 die;
84 }
85 }
86
87 // construction d'une page
88 $response = (new ViewDirector)->buildView($entityManager, $request); // utilise Model
89 // parenthèses nécéssaires autour de l'instanciation pour PHP < 8.4
90 }
91
92
93 elseif($request->getMethod() === 'POST'){
94 /* -- contrôleurs appellables par tout le monde -- */
95
96 // table "user" vide
97 if(!UserController::existUsers($entityManager)){
98 UserController::createAdminUser($entityManager);
99 }
100
101 // requêtes JSON avec fetch()
102 if($request->headers->get('Content-Type') === 'application/json')
103 {
104 $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true);
105
106 if(isset($_GET['action']))
107 {
108 // formulaire de contact
109 if($_GET['action'] === 'send_email'){
110 ContactFormController::sendVisitorEmail($entityManager, $json);
111 }
112 }
113 }
114
115 // envoi formulaire HTML
116 elseif($request->headers->get('Content-Type') === 'application/x-www-form-urlencoded'){
117 // tentative de connexion
118 if($request->query->has('action') && $request->query->get('action') === 'connection'){
119 //$response =
120 UserController::connect($entityManager);
121 }
122 }
123
124 if(IS_ADMIN)
125 {
126 /* -- requêtes AJAX -- */
127
128 // requêtes JSON avec fetch()
129 if($request->headers->get('Content-Type') === 'application/json')
130 {
131 $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true);
132
133 if($request->query->has('action'))
134 {
135 /* -- manipulation des articles -- */
136 if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content'])){
137 ArticleController::editorSubmit($entityManager, $json);
138 }
139 elseif($_GET['action'] === 'delete_article' && isset($json['id'])){
140 $response = ArticleController::deleteArticle($entityManager, $json); // version AJAX
141 }
142 elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){
143 ArticleController::switchPositions($entityManager, $json);
144 }
145 elseif($_GET['action'] === 'date_submit' && isset($json['id']) && isset($json['date'])){
146 ArticleController::dateSubmit($entityManager, $json);
147 }
148
149 /* -- bloc Formulaire -- */
150 elseif($_GET['action'] === 'keep_emails'){
151 ContactFormController::keepEmails($entityManager, $json);
152 }
153 elseif($_GET['action'] === 'set_retention_period'){
154 ContactFormController::setEmailsRetentionPeriod($entityManager, $json);
155 }
156 elseif($_GET['action'] === 'set_email_param'){
157 ContactFormController::setEmailParam($entityManager, $json);
158 }
159 elseif($_GET['action'] === 'test_email'){
160 ContactFormController::sendTestEmail($entityManager, $json);
161 }
162
163 /* -- page emails -- */
164 elseif($_GET['action'] === 'delete_email'){
165 ContactFormController::deleteEmail($entityManager, $json);
166 }
167 elseif($_GET['action'] === 'toggle_sensitive_email'){
168 ContactFormController::toggleSensitiveEmail($entityManager, $json);
169 }
170
171 /* -- upload d'image dans tinymce par copier-coller -- */
172 // collage de HTML contenant une ou plusieurs balises <img>
173 elseif($request->query->get('action') === 'upload_image_url'){
174 ImageUploadController::uploadImageHtml();
175 }
176 // collage d'une image (code base64 dans le presse-papier) non encapsulée dans du HTML
177 elseif($request->query->get('action') === 'upload_image_base64'){
178 ImageUploadController::uploadImageBase64();
179 }
180
181
182 /* -- requêtes spécifiques au calendrier -- */
183 elseif($request->query->get('action') === 'new_event'){
184 CalendarController::newEvent($json, $entityManager);
185 }
186 elseif($request->query->get('action') === 'update_event'){
187 CalendarController::updateEvent($json, $entityManager);
188 }
189 elseif($request->query->get('action') === 'remove_event'){
190 CalendarController::removeEvent($json, $entityManager);
191 }
192
193 /* -- mode maintenance -- */
194 elseif($request->query->get('action') === 'get_logs'){
195 MaintenanceController::getLogs($entityManager);
196 die;
197 }
198 elseif($request->query->get('action') === 'erase_logs'){
199 MaintenanceController::eraseLogs($entityManager);
200 die;
201 }
202 else{
203 echo json_encode(['success' => false]);
204 die;
205 }
206 }
207
208 /* -- site entier (header, footer, favicon) -- */
209 elseif($request->query->has('head_foot_text')){
210 HeadFootController::setTextData($entityManager, $request->query->get('head_foot_text'), $json);
211 }
212 elseif($request->query->has('head_foot_social_check')){
213 HeadFootController::displaySocialNetwork($entityManager, $request->query->get('head_foot_social_check'), $json);
214 }
215
216 /* -- page Menu et chemins -- */
217 elseif(isset($_GET['menu_edit']))
218 {
219 // ne suit pas la règle, faire ça dans un contrôleur?
220 Model::$menu = new Menu($entityManager); // récupération des données
221
222 // flèche gauche <=: position = position du parent + 1, parent = grand-parent, recalculer les positions
223 if($_GET['menu_edit'] === 'move_one_level_up' && isset($json['id'])){
224 MenuAndPathsController::MoveOneLevelUp($entityManager, $json);
225 }
226 // flèche droite =>: position = nombre d'éléments de la fraterie + 1, l'élément précédent devient le parent
227 elseif($_GET['menu_edit'] === 'move_one_level_down' && isset($json['id'])){
228 MenuAndPathsController::MoveOneLevelDown($entityManager, $json);
229 }
230 elseif($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){
231 MenuAndPathsController::switchPositions($entityManager, $json);
232 }
233 elseif($_GET['menu_edit'] === 'display_in_menu' && isset($json['id']) && isset($json['checked'])){
234 MenuAndPathsController::displayInMenu($entityManager, $json);
235 }
236 elseif($_GET['menu_edit'] === 'url_edit' && isset($json['id']) && isset($json['field']) && isset($json['input_data'])){
237 MenuAndPathsController::editUrl($entityManager, $json);
238 }
239 }
240
241 /* -- mode Modification d'une page -- */
242 // partie "page"
243 elseif(isset($_GET['page_edit']))
244 {
245 // titre de la page
246 if($_GET['page_edit'] === 'page_title'){
247 PageManagementController::setPageTitle($entityManager, $json);
248 }
249 // description dans les métadonnées
250 elseif($_GET['page_edit'] === 'page_description'){
251 PageManagementController::setPageDescription($entityManager, $json);
252 }
253 }
254
255 // partie "blocs"
256 elseif($request->query->has('bloc_edit'))
257 {
258 if($request->query->get('bloc_edit') === 'rename_page_bloc'){
259 PageManagementController::renameBloc($entityManager, $json);
260 }
261 elseif($request->query->get('bloc_edit') === 'switch_blocs_positions'){
262 PageManagementController::SwitchBlocsPositions($entityManager, $json);
263 }
264 elseif($request->query->get('bloc_edit') === 'change_articles_order'){
265 PageManagementController::changeArticlesOrder($entityManager, $json);
266 }
267 elseif($request->query->get('bloc_edit') === 'change_presentation'){
268 PageManagementController::changePresentation($entityManager, $json);
269 }
270 elseif($request->query->get('bloc_edit') === 'change_cols_min_width'){
271 PageManagementController::changeColsMinWidth($entityManager, $json);
272 }
273 elseif($request->query->get('bloc_edit') === 'change_pagination_limit'){
274 PageManagementController::changePaginationLimit($entityManager, $json);
275 }
276 }
277 }
278
279 /* -- upload avec FormData OU formulaire HTML AVEC fichier -- */
280 elseif(str_starts_with($request->headers->get('Content-Type'), 'multipart/form-data')) // = $_SERVER['CONTENT_TYPE']
281 {
282 // dans tinymce avec le plugin (bouton "insérer une image" de l'éditeur ou glisser-déposer)
283 if($request->query->has('action') && $request->query->get('action') === 'upload_image_tinymce'){
284 ImageUploadController::imageUploadTinyMce();
285 }
286 // dans tinymce, des quatre méthodes: bouton "link", drag & drop, html, base64
287 elseif($request->query->has('action') && $request->query->get('action') === 'upload_file_tinymce'){
288 FileUploadController::fileUploadTinyMce();
289 }
290 elseif($request->query->has('head_foot_image')){
291 HeadFootController::uploadAsset($entityManager, $request->query->get('head_foot_image'));
292 }
293
294 /* -- page Maintenance -- */
295 elseif($request->query->has('action') && $request->query->get('action') === 'restore_database'
296 && $request->request->has('hidden') && $request->get('hidden') === ''
297 && $request->files->has('uploaded_sql'))
298 {
299 MaintenanceController::downloadSQL($entityManager, $request);
300 }
301 }
302
303 // requêtes XMLHttpRequest
304 elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
305 {
306 echo json_encode(['success' => false]); // noyer le poisson en laissant penser que le site gère les requêtes XHR
307 die;
308 }
309
310 /* -- formulaire HTML SANS fichier -- */
311 elseif($request->headers->get('Content-Type') === 'application/x-www-form-urlencoded')
312 {
313 if($request->query->has('action') && $request->query->get('action') === 'delete_article' && isset($_GET['id'])){
314 $response = ArticleController::deleteArticle($entityManager, $_GET); // version formulaire
315 }
316
317 /* -- nouvelle page -- */
318 elseif(isset($_POST['page_name']) && $_POST['page_name'] !== null
319 && isset($_POST['page_name_path']) && $_POST['page_name_path'] !== null
320 && isset($_POST['page_location']) && $_POST['page_location'] !== null
321 && isset($_POST['page_description']) && $_POST['page_description'] !== null
322 && isset($_POST['new_page_hidden']) && $_POST['new_page_hidden'] === '')
323 {
324 PageManagementController::newPage($entityManager, $_POST);
325 }
326
327 /* -- suppression d'une page -- */
328 elseif(isset($_POST['page_id']) && $_POST['page_id'] !== null
329 && isset($_POST['submit_hidden']) && $_POST['submit_hidden'] === '')
330 {
331 PageManagementController::deletePage($entityManager);
332 }
333
334
335 /* -- mode Modification d'une page -- */
336 // modification du chemins en snake_case
337 elseif(isset($_POST['page_menu_path']) && $_POST['page_menu_path'] !== null
338 && isset($_POST['page_id']) && $_POST['page_id'] !== null
339 && isset($_POST['page_name_path_hidden']) && $_POST['page_name_path_hidden'] === '')
340 {
341 PageManagementController::updatePageMenuPath($entityManager);
342 }
343 // ajout d'un bloc dans une page
344 elseif(isset($_POST['bloc_title']) && $_POST['bloc_title'] !== null
345 && isset($_POST['bloc_select']) && $_POST['bloc_select'] !== null
346 && isset($_POST['bloc_title_hidden']) && $_POST['bloc_title_hidden'] === '') // contrôle anti-robot avec input hidden
347 {
348 PageManagementController::addBloc($entityManager);
349 }
350 // suppression d'un bloc de page
351 elseif(isset($_POST['delete_bloc_id']) && $_POST['delete_bloc_id'] !== null
352 && isset($_POST['delete_bloc_hidden']) && $_POST['delete_bloc_hidden'] === '') // contrôle anti-robot avec input hidden
353 {
354 PageManagementController::deleteBloc($entityManager);
355 }
356
357
358 /* -- page Menu et chemins -- */
359 // création d'une entrée de menu avec une URL
360 elseif(isset($_POST["label_input"]) && isset($_POST["url_input"]) && isset($_POST["location"])){
361 MenuAndPathsController::newUrlMenuEntry($entityManager);
362 }
363 // suppression d'une entrée de menu avec une URL
364 elseif(isset($_POST['delete']) && isset($_POST['x']) && isset($_POST['y'])){ // 2 params x et y sont là parce qu'on a cliqué sur une image
365 MenuAndPathsController::deleteUrlMenuEntry($entityManager);
366 }
367
368
369 /* -- page Mon compte -- */
370 elseif($request->query->has('action') && $request->query->get('action') === 'update_username')
371 {
372 UserController::updateUsername($entityManager);
373 }
374 elseif($request->query->has('action') && $request->query->get('action') === 'update_password')
375 {
376 UserController::updatePassword($entityManager);
377 }
378
379 /* -- page Maintenance -- */
380 elseif($request->query->has('action') && $request->query->get('action') === 'restore_database'
381 && $request->request->has('hidden') && $request->get('hidden') === ''
382 && $request->request->has('selected_sql'))
383 {
384 MaintenanceController::handleBackupSelection($entityManager, $request);
385 }
386
387 // redirection page d'accueil
388 else{
389 header("Location: " . new URL(['error' => 'paramètres inconnus']));
390 die;
391 }
392 }
393
394 // POST admin ne matchant pas
395 else{
396 echo json_encode(['success' => false]);
397 die;
398 }
399 }
400 // POST non admin ne matchant pas
401 else{
402 echo json_encode(['success' => false]);
403 die;
404 }
405 }
406
407 // méthode inconnue
408 else{
409 header("Location: " . new URL(['error' => 'tu fais quoi là mec?']));
410 die;
411 }
412
413
414
415 /* -- utilisation de la réponse -- */
416 if(isset($response)){
417 // cas gérés (d'autres sont à prévoir): mauvais id de la page article, accès page création d'article sans être admin
418 if($request->isMethod('GET') && $response->getStatusCode() == 302){ // 302 redirection temporaire
419 header('Location: ' . new URL(['page' => $_GET['from'] ?? '']));
420 }
421 // redirection après traitement de formulaires HTTP
422 elseif($request->getMethod() === 'POST' && $request->headers->get('Content-Type') === 'application/x-www-form-urlencoded'){
423 $response_data = json_decode(($response)->getContent(), true);
424 $url = new URL(['page' => $_GET['from'] ?? '']);
425 $url->addParams(['success' => $response_data['success'], 'message' => $response_data['message']]);
426 header('Location: ' . $url);
427 }
428 // affichage d'une page OU requête AJAX
429 else{
430 $response->send();
431 }
432 }
433 // pas utilisation de RESPONSE (cas destiné à disparaître)
434 else{
435 if($request->getMethod() === 'POST' && $request->headers->get('Content-Type') === 'application/x-www-form-urlencoded'){
436 header("Location: " . new URL(['error' => 'erreur côté serveur']));
437 }
438 else{
439 http_response_code(500);
440 echo "erreur côté serveur";
441 }
442 }
443 }
444} \ No newline at end of file
diff --git a/src/service/router.php b/src/service/router.php
deleted file mode 100644
index b3ce16e..0000000
--- a/src/service/router.php
+++ /dev/null
@@ -1,441 +0,0 @@
1<?php
2// src/service/router.php
3//
4/* fonctionnement:
5=> 1er test, méthode http GET? POST?
6=> 2ème test, type de contenu (méthode POST uniquement):
7"application/x-www-form-urlencoded" = formulaire
8"application/json" = requête AJAX avec fetch()
9"multipart/form-data" = upload d'image par tinymce
10$_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' requête AJAX xhs, non utilisée
11=> 3ème test, comme le 2ème test mais uniquement si IS_ADMIN est vrai
12*/
13
14declare(strict_types=1);
15
16if($request->getMethod() === 'GET'){
17 // table "user" vide
18 if(!UserController::existUsers($entityManager)){
19 require AbstractBuilder::VIEWS_PATH . 'user_create.php';
20 die;
21 }
22
23 // bouton déconnexion (méthode GET parce que l'utilisateur ne modifie plus de données à partir de là)
24 if($request->query->has('action') && $request->query->get('action') === 'deconnection'){
25 UserController::disconnect($entityManager);
26 }
27
28 // articles suivants
29 if($request->query->has('fetch') && $request->query->get('fetch') === 'next_articles'){
30 ArticleController::fetch($entityManager, $request);
31 }
32
33 // données du calendrier
34 // création du calendrier et changement de dates affichées (boutons flèches mais pas changement de vue)
35 if($request->query->has('action') && $request->query->get('action') === 'get_events'
36 && $request->query->has('start') && $request->query->has('end') && empty($request->getPayload()->all())) // getPayload ne récupère pas que des POST
37 {
38 CalendarController::getData($entityManager);
39 }
40
41 // pages interdites
42 if(!IS_ADMIN && in_array(CURRENT_PAGE, ['menu_paths', 'new_page', 'user_edit', 'emails', 'maintenance'])){
43 header('Location: ' . new URL);
44 die;
45 }
46
47 if(IS_ADMIN){
48 if($request->query->has('action') && $request->query->get('action') === 'get_mysqldump'){
49 MaintenanceController::getLastDump($entityManager);
50 die;
51 }
52 if($request->query->has('action') && $request->query->get('action') === 'get_all_media'){
53 MaintenanceController::getAllMedia();
54 die;
55 }
56 }
57
58 // construction d'une page
59 $response = (new ViewDirector)->buildView($entityManager, $request); // utilise Model
60 // parenthèses nécéssaires autour de l'instanciation pour PHP < 8.4
61}
62
63
64elseif($request->getMethod() === 'POST'){
65 /* -- contrôleurs appellables par tout le monde -- */
66
67 // table "user" vide
68 if(!UserController::existUsers($entityManager)){
69 UserController::createAdminUser($entityManager);
70 }
71
72 // requêtes JSON avec fetch()
73 if($request->headers->get('Content-Type') === 'application/json')
74 {
75 $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true);
76
77 if(isset($_GET['action']))
78 {
79 // formulaire de contact
80 if($_GET['action'] === 'send_email'){
81 ContactFormController::sendVisitorEmail($entityManager, $json);
82 }
83 }
84 }
85
86 // envoi formulaire HTML
87 elseif($request->headers->get('Content-Type') === 'application/x-www-form-urlencoded'){
88 // tentative de connexion
89 if($request->query->has('action') && $request->query->get('action') === 'connection'){
90 //$response =
91 UserController::connect($entityManager);
92 }
93 }
94
95 if(IS_ADMIN)
96 {
97 /* -- requêtes AJAX -- */
98
99 // requêtes JSON avec fetch()
100 if($request->headers->get('Content-Type') === 'application/json')
101 {
102 $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true);
103
104 if($request->query->has('action'))
105 {
106 /* -- manipulation des articles -- */
107 if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content'])){
108 ArticleController::editorSubmit($entityManager, $json);
109 }
110 elseif($_GET['action'] === 'delete_article' && isset($json['id'])){
111 $response = ArticleController::deleteArticle($entityManager, $json); // version AJAX
112 }
113 elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){
114 ArticleController::switchPositions($entityManager, $json);
115 }
116 elseif($_GET['action'] === 'date_submit' && isset($json['id']) && isset($json['date'])){
117 ArticleController::dateSubmit($entityManager, $json);
118 }
119
120 /* -- bloc Formulaire -- */
121 elseif($_GET['action'] === 'keep_emails'){
122 ContactFormController::keepEmails($entityManager, $json);
123 }
124 elseif($_GET['action'] === 'set_retention_period'){
125 ContactFormController::setEmailsRetentionPeriod($entityManager, $json);
126 }
127 elseif($_GET['action'] === 'set_email_param'){
128 ContactFormController::setEmailParam($entityManager, $json);
129 }
130 elseif($_GET['action'] === 'test_email'){
131 ContactFormController::sendTestEmail($entityManager, $json);
132 }
133
134 /* -- page emails -- */
135 elseif($_GET['action'] === 'delete_email'){
136 ContactFormController::deleteEmail($entityManager, $json);
137 }
138 elseif($_GET['action'] === 'toggle_sensitive_email'){
139 ContactFormController::toggleSensitiveEmail($entityManager, $json);
140 }
141
142 /* -- upload d'image dans tinymce par copier-coller -- */
143 // collage de HTML contenant une ou plusieurs balises <img>
144 elseif($request->query->get('action') === 'upload_image_url'){
145 ImageUploadController::uploadImageHtml();
146 }
147 // collage d'une image (code base64 dans le presse-papier) non encapsulée dans du HTML
148 elseif($request->query->get('action') === 'upload_image_base64'){
149 ImageUploadController::uploadImageBase64();
150 }
151
152
153 /* -- requêtes spécifiques au calendrier -- */
154 elseif($request->query->get('action') === 'new_event'){
155 CalendarController::newEvent($json, $entityManager);
156 }
157 elseif($request->query->get('action') === 'update_event'){
158 CalendarController::updateEvent($json, $entityManager);
159 }
160 elseif($request->query->get('action') === 'remove_event'){
161 CalendarController::removeEvent($json, $entityManager);
162 }
163
164 /* -- mode maintenance -- */
165 elseif($request->query->get('action') === 'get_logs'){
166 MaintenanceController::getLogs($entityManager);
167 die;
168 }
169 elseif($request->query->get('action') === 'erase_logs'){
170 MaintenanceController::eraseLogs($entityManager);
171 die;
172 }
173 else{
174 echo json_encode(['success' => false]);
175 die;
176 }
177 }
178
179 /* -- site entier (header, footer, favicon) -- */
180 elseif($request->query->has('head_foot_text')){
181 HeadFootController::setTextData($entityManager, $request->query->get('head_foot_text'), $json);
182 }
183 elseif($request->query->has('head_foot_social_check')){
184 HeadFootController::displaySocialNetwork($entityManager, $request->query->get('head_foot_social_check'), $json);
185 }
186
187 /* -- page Menu et chemins -- */
188 elseif(isset($_GET['menu_edit']))
189 {
190 // ne suit pas la règle, faire ça dans un contrôleur?
191 Model::$menu = new Menu($entityManager); // récupération des données
192
193 // flèche gauche <=: position = position du parent + 1, parent = grand-parent, recalculer les positions
194 if($_GET['menu_edit'] === 'move_one_level_up' && isset($json['id'])){
195 MenuAndPathsController::MoveOneLevelUp($entityManager, $json);
196 }
197 // flèche droite =>: position = nombre d'éléments de la fraterie + 1, l'élément précédent devient le parent
198 elseif($_GET['menu_edit'] === 'move_one_level_down' && isset($json['id'])){
199 MenuAndPathsController::MoveOneLevelDown($entityManager, $json);
200 }
201 elseif($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){
202 MenuAndPathsController::switchPositions($entityManager, $json);
203 }
204 elseif($_GET['menu_edit'] === 'display_in_menu' && isset($json['id']) && isset($json['checked'])){
205 MenuAndPathsController::displayInMenu($entityManager, $json);
206 }
207 elseif($_GET['menu_edit'] === 'url_edit' && isset($json['id']) && isset($json['field']) && isset($json['input_data'])){
208 MenuAndPathsController::editUrl($entityManager, $json);
209 }
210 }
211
212 /* -- mode Modification d'une page -- */
213 // partie "page"
214 elseif(isset($_GET['page_edit']))
215 {
216 // titre de la page
217 if($_GET['page_edit'] === 'page_title'){
218 PageManagementController::setPageTitle($entityManager, $json);
219 }
220 // description dans les métadonnées
221 elseif($_GET['page_edit'] === 'page_description'){
222 PageManagementController::setPageDescription($entityManager, $json);
223 }
224 }
225
226 // partie "blocs"
227 elseif($request->query->has('bloc_edit'))
228 {
229 if($request->query->get('bloc_edit') === 'rename_page_bloc'){
230 PageManagementController::renameBloc($entityManager, $json);
231 }
232 elseif($request->query->get('bloc_edit') === 'switch_blocs_positions'){
233 PageManagementController::SwitchBlocsPositions($entityManager, $json);
234 }
235 elseif($request->query->get('bloc_edit') === 'change_articles_order'){
236 PageManagementController::changeArticlesOrder($entityManager, $json);
237 }
238 elseif($request->query->get('bloc_edit') === 'change_presentation'){
239 PageManagementController::changePresentation($entityManager, $json);
240 }
241 elseif($request->query->get('bloc_edit') === 'change_cols_min_width'){
242 PageManagementController::changeColsMinWidth($entityManager, $json);
243 }
244 elseif($request->query->get('bloc_edit') === 'change_pagination_limit'){
245 PageManagementController::changePaginationLimit($entityManager, $json);
246 }
247 }
248 }
249
250 /* -- upload avec FormData OU formulaire HTML AVEC fichier -- */
251 elseif(str_starts_with($request->headers->get('Content-Type'), 'multipart/form-data')) // = $_SERVER['CONTENT_TYPE']
252 {
253 // dans tinymce avec le plugin (bouton "insérer une image" de l'éditeur ou glisser-déposer)
254 if($request->query->has('action') && $request->query->get('action') === 'upload_image_tinymce'){
255 ImageUploadController::imageUploadTinyMce();
256 }
257 // dans tinymce, des quatre méthodes: bouton "link", drag & drop, html, base64
258 elseif($request->query->has('action') && $request->query->get('action') === 'upload_file_tinymce'){
259 FileUploadController::fileUploadTinyMce();
260 }
261 elseif($request->query->has('head_foot_image')){
262 HeadFootController::uploadAsset($entityManager, $request->query->get('head_foot_image'));
263 }
264
265 /* -- page Maintenance -- */
266 elseif($request->query->has('action') && $request->query->get('action') === 'restore_database'
267 && $request->request->has('hidden') && $request->get('hidden') === ''
268 && $request->files->has('uploaded_sql'))
269 {
270 $url = new URL;
271 if($request->query->has('from')){
272 $url->addParams(['page' => $request->query->get('from')]);
273 }
274
275 try{
276 MaintenanceController::downloadSQL($entityManager, $request->files->get('uploaded_sql'));
277 $url->addParams(['database_restauration' => 'successful']);
278 }
279 catch(Exception $e){
280 $url->addParams(['database_restauration' => $e->getMessage()]);
281 }
282
283 header('Location: ' . $url);
284 die;
285 }
286 }
287
288 // requêtes XMLHttpRequest
289 elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
290 {
291 echo json_encode(['success' => false]); // noyer le poisson en laissant penser que le site gère les requêtes XHR
292 die;
293 }
294
295 /* -- formulaire HTML SANS fichier -- */
296 elseif($request->headers->get('Content-Type') === 'application/x-www-form-urlencoded')
297 {
298 if($request->query->has('action') && $request->query->get('action') === 'delete_article' && isset($_GET['id'])){
299 $response = ArticleController::deleteArticle($entityManager, $_GET); // version formulaire
300 }
301
302 /* -- nouvelle page -- */
303 elseif(isset($_POST['page_name']) && $_POST['page_name'] !== null
304 && isset($_POST['page_name_path']) && $_POST['page_name_path'] !== null
305 && isset($_POST['page_location']) && $_POST['page_location'] !== null
306 && isset($_POST['page_description']) && $_POST['page_description'] !== null
307 && isset($_POST['new_page_hidden']) && $_POST['new_page_hidden'] === '')
308 {
309 PageManagementController::newPage($entityManager, $_POST);
310 }
311
312 /* -- suppression d'une page -- */
313 elseif(isset($_POST['page_id']) && $_POST['page_id'] !== null
314 && isset($_POST['submit_hidden']) && $_POST['submit_hidden'] === '')
315 {
316 PageManagementController::deletePage($entityManager);
317 }
318
319
320 /* -- mode Modification d'une page -- */
321 // modification du chemins en snake_case
322 elseif(isset($_POST['page_menu_path']) && $_POST['page_menu_path'] !== null
323 && isset($_POST['page_id']) && $_POST['page_id'] !== null
324 && isset($_POST['page_name_path_hidden']) && $_POST['page_name_path_hidden'] === '')
325 {
326 PageManagementController::updatePageMenuPath($entityManager);
327 }
328 // ajout d'un bloc dans une page
329 elseif(isset($_POST['bloc_title']) && $_POST['bloc_title'] !== null
330 && isset($_POST['bloc_select']) && $_POST['bloc_select'] !== null
331 && isset($_POST['bloc_title_hidden']) && $_POST['bloc_title_hidden'] === '') // contrôle anti-robot avec input hidden
332 {
333 PageManagementController::addBloc($entityManager);
334 }
335 // suppression d'un bloc de page
336 elseif(isset($_POST['delete_bloc_id']) && $_POST['delete_bloc_id'] !== null
337 && isset($_POST['delete_bloc_hidden']) && $_POST['delete_bloc_hidden'] === '') // contrôle anti-robot avec input hidden
338 {
339 PageManagementController::deleteBloc($entityManager);
340 }
341
342
343 /* -- page Menu et chemins -- */
344 // création d'une entrée de menu avec une URL
345 elseif(isset($_POST["label_input"]) && isset($_POST["url_input"]) && isset($_POST["location"])){
346 MenuAndPathsController::newUrlMenuEntry($entityManager);
347 }
348 // suppression d'une entrée de menu avec une URL
349 elseif(isset($_POST['delete']) && isset($_POST['x']) && isset($_POST['y'])){ // 2 params x et y sont là parce qu'on a cliqué sur une image
350 MenuAndPathsController::deleteUrlMenuEntry($entityManager);
351 }
352
353
354 /* -- page Mon compte -- */
355 elseif($request->query->has('action') && $request->query->get('action') === 'update_username')
356 {
357 UserController::updateUsername($entityManager);
358 }
359 elseif($request->query->has('action') && $request->query->get('action') === 'update_password')
360 {
361 UserController::updatePassword($entityManager);
362 }
363
364 /* -- page Maintenance -- */
365 elseif($request->query->has('action') && $request->query->get('action') === 'restore_database'
366 && $request->request->has('hidden') && $request->get('hidden') === ''
367 && $request->request->has('selected_sql'))
368 {
369 $url = new URL;
370 if($request->query->has('from')){
371 $url->addParams(['page' => $request->query->get('from')]);
372 }
373
374 try{
375 MaintenanceController::handleBackupSelection($entityManager, $request->request->get('selected_sql'));
376 $url->addParams(['database_restauration' => 'successful']);
377 }
378 catch(Exception $e){
379 $url->addParams(['database_restauration' => $e->getMessage()]);
380 }
381
382 header('Location: ' . $url);
383 die;
384 }
385
386 // redirection page d'accueil
387 else{
388 header("Location: " . new URL(['error' => 'paramètres inconnus']));
389 die;
390 }
391 }
392
393 // POST admin ne matchant pas
394 else{
395 echo json_encode(['success' => false]);
396 die;
397 }
398 }
399 // POST non admin ne matchant pas
400 else{
401 echo json_encode(['success' => false]);
402 die;
403 }
404}
405
406// méthode inconnue
407else{
408 header("Location: " . new URL(['error' => 'tu fais quoi là mec?']));
409 die;
410}
411
412
413
414/* -- utilisation de la réponse -- */
415if(isset($response)){
416 // cas gérés (d'autres sont à prévoir): mauvais id de la page article, accès page création d'article sans être admin
417 if($request->isMethod('GET') && $response->getStatusCode() == 302){ // 302 redirection temporaire
418 header('Location: ' . new URL(['page' => $_GET['from'] ?? '']));
419 }
420 // redirection après traitement de formulaires HTTP
421 elseif($request->getMethod() === 'POST' && $request->headers->get('Content-Type') === 'application/x-www-form-urlencoded'){
422 $response_data = json_decode(($response)->getContent(), true);
423 $url = new URL(['page' => $_GET['from'] ?? '']);
424 $url->addParams(['success' => $response_data['success'], 'message' => $response_data['message']]);
425 header('Location: ' . $url);
426 }
427 // affichage d'une page OU requête AJAX
428 else{
429 $response->send();
430 }
431}
432// pas utilisation de RESPONSE (cas destiné à disparaître)
433else{
434 if($request->getMethod() === 'POST' && $request->headers->get('Content-Type') === 'application/x-www-form-urlencoded'){
435 header("Location: " . new URL(['error' => 'erreur côté serveur']));
436 }
437 else{
438 http_response_code(500);
439 echo "erreur côté serveur";
440 }
441} \ No newline at end of file
diff --git a/src/view/HeadBuilder.php b/src/view/HeadBuilder.php
index fe57b55..9cb0650 100644
--- a/src/view/HeadBuilder.php
+++ b/src/view/HeadBuilder.php
@@ -12,8 +12,7 @@ class HeadBuilder extends AbstractBuilder
12 { 12 {
13 $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; 13 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
14 14
15 if(file_exists($viewFile)) 15 if(file_exists($viewFile)){
16 {
17 // css et js 16 // css et js
18 $page = Model::$page_path->getLast(); 17 $page = Model::$page_path->getLast();
19 18
diff --git a/src/view/MaintenanceBuilder.php b/src/view/MaintenanceBuilder.php
index 0633125..5ed1ce2 100644
--- a/src/view/MaintenanceBuilder.php
+++ b/src/view/MaintenanceBuilder.php
@@ -14,11 +14,11 @@ class MaintenanceBuilder extends AbstractBuilder
14 try{ 14 try{
15 $backup_array = Backup::getBackupList(); 15 $backup_array = Backup::getBackupList();
16 } 16 }
17 // exeptions lancées dans Backup::mySQLdump 17 catch(RuntimeException $e){
18 catch(RuntimeException $e){ // pas d'info $e pour le client
19 $backup_array = []; 18 $backup_array = [];
20 echo '<script>window.error_message = "' . $e->getMessage() . '";</script>'; 19 $_SESSION['flash_message'] = $e->getMessage(); // peut écraser une flash error déjà dans $_SESSION['flash_message']
21 } 20 }
21
22 $backup_options = ''; 22 $backup_options = '';
23 for($i = count($backup_array) - 1; $i >= 0; $i--){ 23 for($i = count($backup_array) - 1; $i >= 0; $i--){
24 $backup_options .= '<option value="' . $backup_array[$i] . '">' . $backup_array[$i] . '</option>'; 24 $backup_options .= '<option value="' . $backup_array[$i] . '">' . $backup_array[$i] . '</option>';