summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/controller/ArticleController.php169
-rw-r--r--src/controller/CalendarController.php79
-rw-r--r--src/controller/ContactFormController.php44
-rw-r--r--src/controller/EmailController.php91
-rw-r--r--src/controller/ImageUploadController.php184
-rw-r--r--src/controller/MenuAndPathsController.php189
-rw-r--r--src/controller/PageManagementController.php264
-rw-r--r--src/controller/ajax_admin.php636
-rw-r--r--src/controller/ajax_calendar_admin.php54
-rw-r--r--src/controller/ajax_calendar_visitor.php39
-rw-r--r--src/controller/ajax_email.php105
-rw-r--r--src/controller/password.php3
-rw-r--r--src/controller/post_functions_admin.php231
-rw-r--r--src/controller/post_router.php120
-rw-r--r--src/controller/request_router.php256
-rw-r--r--src/model/EventDTO.php2
16 files changed, 1279 insertions, 1187 deletions
diff --git a/src/controller/ArticleController.php b/src/controller/ArticleController.php
new file mode 100644
index 0000000..e3e4edb
--- /dev/null
+++ b/src/controller/ArticleController.php
@@ -0,0 +1,169 @@
1<?php
2// src/controller/ArticleController.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7use App\Entity\Article;
8use Doctrine\ORM\EntityManager;
9
10class ArticleController
11{
12 static public function editorSubmit(EntityManager $entityManager, array $json): void
13 {
14 if(json_last_error() === JSON_ERROR_NONE)
15 {
16 $id = $json['id'];
17 $director = new Director($entityManager);
18
19 // cas d'une nouvelle "news"
20 if(is_array($json['content'])){
21 foreach($json['content'] as $one_input){
22 $one_input = Security::secureString($one_input);
23 }
24 $content = $json['content'];
25 }
26 else{
27 $content = Security::secureString($json['content']);
28 }
29
30 // nouvel article
31 if($id[0] === 'n')
32 {
33 $section_id = (int)substr($id, 1); // id du bloc <section>
34 $director->findNodeById($section_id);
35 $director->makeSectionNode();
36 $node = $director->getNode(); // = <section>
37
38 if(is_array($content)){
39 $date = new \DateTime($content['d']);
40 $article = new Article($content['i'], $date, $content['t'], $content['p']);
41 $article_node = new Node('new', 'i' . (string)$date->getTimestamp(), [], count($node->getChildren()) + 1, $node, $node->getPage(), $article);
42
43 // id_node tout juste généré
44 //$article_node->getId();
45 }
46 else{
47 $timestamp = time();
48 $date = new \DateTime;
49 $date->setTimestamp($timestamp);
50
51 $article = new Article($content, $date); // le "current" timestamp est obtenu par la BDD
52 $article_node = new Node('article', 'i' . (string)$timestamp, [], count($node->getChildren()) + 1, $node, $node->getPage(), $article);
53 }
54
55 $entityManager->persist($article_node);
56 $entityManager->flush();
57
58 echo json_encode(['success' => true, 'article_id' => $article_node->getArticleTimestamp()]);
59 die;
60 }
61 // modification article
62 else{
63 $id[0] = 'i'; // id de l'article node
64 }
65
66 if($director->makeArticleNode($id)) // une entrée est trouvée
67 {
68 $node = $director->getArticleNode(); // article
69 switch($json['id'][0]){
70 case 'i':
71 $node->getArticle()->setContent($content);
72 break;
73 case 'p':
74 $node->getArticle()->setPreview($content); // html de l'éditeur
75 break;
76 case 't':
77 $node->getArticle()->setTitle($content); // html de l'éditeur
78 break;
79 case 'd':
80 echo json_encode(['success' => false, 'message' => 'l\'action editor_submit ne supporte pas les dates, utiliser date_submit.']);
81 die;
82 default:
83 echo json_encode(['success' => false, 'message' => 'identifiant non utilisable']);
84 die;
85 }
86 $entityManager->flush();
87 echo json_encode(['success' => true]);
88 }
89 else
90 {
91 echo json_encode(['success' => false, 'message' => 'article non identifié']);
92 }
93 }
94 else{
95 echo json_encode(['success' => false, 'message' => 'Erreur de décodage JSON']);
96 }
97 die;
98 }
99
100 static public function deleteArticle(EntityManager $entityManager, array $json): void
101 {
102 $director = new Director($entityManager);
103 $director->makeArticleNode($json['id'], true);
104 $article = $director->getArticleNode();
105 $section = $director->getNode();
106
107 $entityManager->remove($article);
108 $section->removeChild($article);
109 $section->sortChildren(true); // régénère les positions
110 $entityManager->flush();
111
112 // test avec une nouvelle requête qui ne devrait rien trouver
113 if(!$director->makeArticleNode($json['id']))
114 {
115 echo json_encode(['success' => true]);
116
117 // on pourrait afficher une notification "toast"
118 }
119 else{
120 http_response_code(500);
121 echo json_encode(['success' => false, 'message' => 'Erreur lors de la suppression de l\'article.']);
122 }
123 die;
124 }
125
126 static public function switchPositions(EntityManager $entityManager, array $json): void
127 {
128 $director = new Director($entityManager);
129 $director->makeArticleNode($json['id1'], true);
130 $article1 = $director->getArticleNode();
131 $section = $director->getNode();
132
133 $section->sortChildren(true); // régénère les positions avant inversion
134
135 $article2 = null;
136 foreach($section->getChildren() as $child){
137 if($child->getArticleTimestamp() === $json['id2']) // type string
138 {
139 $article2 = $child;
140 break;
141 }
142 }
143
144 // inversion
145 $tmp = $article1->getPosition();
146 $article1->setPosition($article2->getPosition());
147 $article2->setPosition($tmp);
148 $entityManager->flush();
149
150 echo json_encode(['success' => true]);
151 die;
152 }
153
154 static public function dateSubmit(EntityManager $entityManager, array $json): void
155 {
156 $id = $json['id'];
157 $id[0] = 'i';
158 $date = new DateTime($json['date']);
159
160 $director = new Director($entityManager);
161 $director->makeArticleNode($id);
162 $node = $director->getArticleNode();
163 $node->getArticle()->setDateTime($date);
164 $entityManager->flush();
165
166 echo json_encode(['success' => true]);
167 die;
168 }
169} \ No newline at end of file
diff --git a/src/controller/CalendarController.php b/src/controller/CalendarController.php
new file mode 100644
index 0000000..cc37d0f
--- /dev/null
+++ b/src/controller/CalendarController.php
@@ -0,0 +1,79 @@
1<?php
2// /src/controller/CalendarController.php
3
4declare(strict_types=1);
5
6use Doctrine\ORM\EntityManager;
7use App\Entity\Event;
8
9class CalendarController
10{
11 static public function getData(EntityManager $entityManager): void
12 {
13 // bornes début et fin du calendrier affiché à l'heure locale
14 // noter que la vue "planning" est similaire à la vue "semaine"
15 $start = new DateTime($_GET['start']);
16 $end = new DateTime($_GET['end']);
17 $start->setTimezone(new DateTimeZone('UTC'));
18 $end->setTimezone(new DateTimeZone('UTC'));
19
20 // affichage format ISO à l'heure UTC
21 //$date->format('Y-m-d\TH:i:s\Z');
22
23 // on prend les évènements se finissant après le début ou commençant avant la fin de la fourchette
24 $dql = 'SELECT e FROM App\Entity\Event e WHERE e.end >= :start AND e.start <= :end';
25 $bulk_data = $entityManager->createQuery($dql)
26 ->setParameter('start', $start)
27 ->setParameter('end', $end)
28 ->getResult();
29
30 $events = [];
31 foreach($bulk_data as $one_entry){
32 $event = new EventDTO($one_entry);
33 $events[] = $event->toArray();
34 }
35
36 header('Content-Type: application/json');
37 echo json_encode($events);
38 die;
39 }
40
41 static public function newEvent(array $json, EntityManager $entityManager):void
42 {
43 try{
44 $event = new Event($json);
45 }
46 catch(InvalidArgumentException $e){
47 echo json_encode(['success' => false, 'error' => $e->getMessage()]);
48 http_response_code(400);
49 die;
50 }
51 $entityManager->persist($event);
52 $entityManager->flush();
53
54 echo json_encode(['success' => true, 'id' => $event->getId()]);
55 }
56 static public function updateEvent(array $json, EntityManager $entityManager):void
57 {
58 $event = $entityManager->find('App\Entity\Event', (int)$json['id']);
59 try{
60 $event->securedUpdateFromJSON($json);
61 }
62 catch(InvalidArgumentException $e){
63 echo json_encode(['success' => false, 'error' => $e->getMessage()]);
64 http_response_code(400);
65 die;
66 }
67 $entityManager->flush();
68
69 echo json_encode(['success' => true]);
70 }
71 static public function removeEvent(array $json, EntityManager $entityManager):void
72 {
73 $event = $entityManager->find('App\Entity\Event', (int)$json['id']);
74 $entityManager->remove($event);
75 $entityManager->flush();
76
77 echo json_encode(['success' => true]);
78 }
79} \ No newline at end of file
diff --git a/src/controller/ContactFormController.php b/src/controller/ContactFormController.php
new file mode 100644
index 0000000..9d62a77
--- /dev/null
+++ b/src/controller/ContactFormController.php
@@ -0,0 +1,44 @@
1<?php
2// src/controller/ContactFormController.php
3
4declare(strict_types=1);
5
6use Doctrine\ORM\EntityManager;
7
8class ContactFormController
9{
10 static public function updateRecipient(EntityManager $entityManager, array $json): void
11 {
12 $email = htmlspecialchars(trim($json['email']));
13
14 if((filter_var($email, FILTER_VALIDATE_EMAIL) // nouvel e-mail
15 || ($json['email'] === '' && !empty(Config::$email_dest))) // e-mail par défaut
16 && isset($json['hidden']) && empty($json['hidden']))
17 {
18 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
19 $form_data->updateData('email', $email);
20 $entityManager->persist($form_data);
21 $entityManager->flush();
22
23 echo json_encode(['success' => true]);
24 }
25 else{
26 echo json_encode(['success' => false]);
27 }
28 die;
29 }
30 static public function sendTestEmail(EntityManager $entityManager, array $json): void
31 {
32 // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur
33 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
34 $recipient = $form_data->getData()['email'] ?? Config::$email_dest;
35
36 if(EmailController::send($recipient, false, 'nom du visiteur', 'adresse@du_visiteur.fr', "TEST d'un envoi d'e-mail depuis le site web")){
37 echo json_encode(['success' => true]);
38 }
39 else{
40 echo json_encode(['success' => false]);
41 }
42 die;
43 }
44} \ No newline at end of file
diff --git a/src/controller/EmailController.php b/src/controller/EmailController.php
new file mode 100644
index 0000000..1eea257
--- /dev/null
+++ b/src/controller/EmailController.php
@@ -0,0 +1,91 @@
1<?php
2// src/controller/EmailController.php
3
4declare(strict_types=1);
5
6use PHPMailer\PHPMailer\PHPMailer;
7use PHPMailer\PHPMailer\Exception;
8use App\Entity\Email;
9use Doctrine\ORM\EntityManager;
10
11class EmailController
12{
13 static public function send(string $recipient, bool $true_email, string $name = '', string $email = '', string $message = ''): bool
14 {
15 $mail = new PHPMailer(true); // true => exceptions
16 $mail->CharSet = 'UTF-8';
17
18 try{
19 // Paramètres du serveur
20 $mail->isSMTP();
21 $mail->Host = Config::$smtp_host;
22 $mail->SMTPAuth = true;
23 $mail->Port = 25;
24
25 if($mail->SMTPAuth){
26 $mail->Username = Config::$smtp_username; // e-mail
27 $mail->Password = Config::$smtp_password;
28 $mail->SMTPSecure = Config::$smtp_secure; // tls (starttls) ou ssl (smtps)
29 if($mail->SMTPSecure === 'tls'){
30 $mail->Port = 587;
31 }
32 elseif($mail->SMTPSecure === 'ssl'){
33 $mail->Port = 465;
34 }
35 }
36 //var_dump($mail->smtpConnect());die; // test de connexion
37
38 // Expéditeur et destinataire
39 $mail->setFrom(strtolower(Config::$email_from), Config::$email_from_name); // expéditeur
40 $mail->addAddress(strtolower($recipient), Config::$email_dest_name); // destinataire
41
42 // Contenu
43 $mail->isHTML(true);
44 if($true_email){
45 $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web';
46
47 }
48 else{
49 $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web";
50 }
51 $mail->Body = $message;
52 $mail->AltBody = $message;
53
54 $mail->send();
55 return true;
56 }
57 catch(Exception $e){
58 return false;
59 //echo "Le message n'a pas pu être envoyé. Erreur : {$mail->ErrorInfo}";
60 }
61 }
62
63 static public function submit(array $json, EntityManager $entityManager): void
64 {
65 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0;
66 $captcha_try = isset($json['captcha']) ? Captcha::controlInput($json['captcha']) : 0;
67
68 // contrôles des entrées
69 $name = htmlspecialchars(trim($json['name']));
70 $email = strtolower(htmlspecialchars(trim($json['email'])));
71 $message = htmlspecialchars(trim($json['message']));
72
73 // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur
74 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
75 $recipient = $form_data->getData()['email'] ?? Config::$email_dest;
76
77 if($captcha_try != 0 && $captcha_solution != 0 && ($captcha_try === $captcha_solution)
78 && filter_var($email, FILTER_VALIDATE_EMAIL) && isset($json['hidden']) && empty($json['hidden'])
79 && self::send($recipient, true, $name, $email, $message))
80 {
81 $db_email = new Email($email, Config::$email_dest, $message);
82 $entityManager->persist($db_email);
83 $entityManager->flush();
84 echo json_encode(['success' => true]);
85 }
86 else{
87 echo json_encode(['success' => false]);
88 }
89 die;
90 }
91} \ No newline at end of file
diff --git a/src/controller/ImageUploadController.php b/src/controller/ImageUploadController.php
new file mode 100644
index 0000000..29b8059
--- /dev/null
+++ b/src/controller/ImageUploadController.php
@@ -0,0 +1,184 @@
1<?php
2// src/controller/ImageUploadController.php
3
4declare(strict_types=1);
5
6class ImageUploadController
7{
8 static public function imagickCleanImage(string $image_data, string $local_path, string $format = 'jpeg'): bool // "string" parce que file_get_contents...
9 {
10 try{
11 $imagick = new Imagick();
12 $imagick->readImageBlob($image_data);
13 $imagick->stripImage(); // nettoyage métadonnées
14 $imagick->setImageFormat($format);
15 if($format === 'jpeg'){
16 $imagick->setImageCompression(Imagick::COMPRESSION_JPEG);
17 $imagick->setImageCompressionQuality(85); // optionnel
18 }
19 $imagick->writeImage($local_path); // enregistrement
20 $imagick->clear();
21 $imagick->destroy();
22 return true;
23 }
24 catch(Exception $e){
25 return false;
26 }
27 }
28 static public function curlDownloadImage(string $url, $maxRetries = 3, $timeout = 10): string|false
29 {
30 $attempt = 0;
31 $imageData = false;
32
33 while($attempt < $maxRetries){
34 $ch = curl_init($url); // instance de CurlHandle
35 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
36 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
37 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
38 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
39 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
40 curl_setopt($ch, CURLOPT_USERAGENT, 'TinyMCE-Image-Downloader');
41
42 $imageData = curl_exec($ch);
43 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
44 //$curlError = curl_error($ch);
45
46 curl_close($ch);
47
48 if($imageData !== false && $httpCode >= 200 && $httpCode < 300){
49 return $imageData;
50 }
51
52 $attempt++;
53 sleep(1);
54 }
55
56 return false; // échec après trois tentatives
57 }
58
59 // téléchargement par le plugin (bouton "insérer une image")
60 static public function imageUploadTinyMce(): void
61 {
62 if(isset($_FILES['file'])){
63 $file = $_FILES['file'];
64 $dest = 'images/';
65 $dest_mini = 'images-mini/';
66
67 // Vérifier si les répertoires existent, sinon les créer
68 if(!is_dir($dest)) {
69 mkdir($dest, 0700, true);
70 }
71 if(!is_dir($dest_mini)) {
72 mkdir($dest_mini, 0700, true);
73 }
74
75 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif'];
76 $name = Security::secureFileName(pathinfo($file['name'], PATHINFO_FILENAME));
77 $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
78 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
79 $extension = 'jpeg';
80 }
81 $file_path = $dest . $name . '_' . uniqid() . '.' . $extension;
82
83 // créer une miniature de l'image
84 //
85
86 if(self::imagickCleanImage(file_get_contents($file['tmp_name']), $file_path, $extension)){ // recréer l’image pour la nettoyer
87 echo json_encode(['location' => $file_path]); // renvoyer l'URL de l'image téléchargée
88 }
89 else{
90 http_response_code(500);
91 echo json_encode(['message' => 'Erreur image non valide']);
92 }
93 }
94 else{
95 http_response_code(400);
96 echo json_encode(['message' => 'Erreur 400: Bad Request']);
97 }
98 die;
99 }
100
101 // collage de HTML => recherche de balises <img>, téléchargement côté serveur et renvoi de l'adresse sur le serveur
102 static public function uploadImageHtml(): void
103 {
104 $json = json_decode(file_get_contents('php://input'), true);
105
106 if(isset($json['image_url'])){
107 $image_data = self::curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents
108 $dest = 'images/';
109
110 if(!is_dir($dest)) { // Vérifier si le répertoire existe, sinon le créer
111 mkdir($dest, 0777, true);
112 }
113
114 if($image_data === false){
115 http_response_code(400);
116 echo json_encode(['message' => "Erreur, le serveur n'a pas réussi à télécharger l'image."]);
117 die;
118 }
119
120 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif'];
121 $url_path = parse_url($json['image_url'], PHP_URL_PATH);
122 $name = Security::secureFileName(pathinfo($url_path, PATHINFO_FILENAME));
123 $extension = strtolower(pathinfo($url_path, PATHINFO_EXTENSION));
124 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
125 $extension = 'jpeg';
126 }
127 $local_path = $dest . $name . '_' . uniqid() . '.' . $extension;
128
129 if(self::imagickCleanImage($image_data, $local_path, $extension)){ // recréer l’image pour la nettoyer
130 echo json_encode(['location' => $local_path]); // nouvelle adresse
131 }
132 else{
133 http_response_code(500);
134 echo json_encode(['message' => 'Erreur image non valide']);
135 }
136 }
137 else{
138 echo json_encode(['message' => 'Erreur 400: Bad Request']);
139 }
140 die;
141 }
142
143 // collage simple d'une image (base64 dans le presse-papier) non encapsulée dans du HTML
144 static public function uploadImageBase64(): void
145 {
146 $json = json_decode(file_get_contents('php://input'), true);
147 $dest = 'images/';
148
149 if(!is_dir('images')){
150 mkdir('images', 0777, true);
151 }
152
153 // détection de data:image/ et de ;base64, et capture du format dans $type
154 if(!isset($json['image_base64']) || !preg_match('/^data:image\/(\w+);base64,/', $json['image_base64'], $type)){
155 http_response_code(400);
156 echo json_encode(['message' => 'Données image base64 manquantes ou invalides']);
157 die;
158 }
159
160 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif'];
161 $extension = strtolower($type[1]);
162 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
163 $extension = 'jpeg';
164 }
165
166 $image_data = base64_decode(substr($json['image_base64'], strpos($json['image_base64'], ',') + 1)); // découpe la chaine à la virgule puis convertit en binaire
167 if($image_data === false){
168 http_response_code(400);
169 echo json_encode(['message' => 'Décodage base64 invalide']);
170 die;
171 }
172
173 $local_path = $dest . 'pasted_image_' . uniqid() . '.' . $extension;
174
175 if(self::imagickCleanImage($image_data, $local_path)){
176 echo json_encode(['location' => $local_path]);
177 }
178 else{
179 http_response_code(500);
180 echo json_encode(['message' => 'Erreur image non valide']);
181 }
182 die;
183 }
184} \ No newline at end of file
diff --git a/src/controller/MenuAndPathsController.php b/src/controller/MenuAndPathsController.php
new file mode 100644
index 0000000..d429287
--- /dev/null
+++ b/src/controller/MenuAndPathsController.php
@@ -0,0 +1,189 @@
1<?php
2// src/controller/MenuAndPathsController.php
3
4declare(strict_types=1);
5
6use App\Entity\Page;
7use Doctrine\ORM\EntityManager;
8
9class MenuAndPathsController
10{
11 static public function newUrlMenuEntry(EntityManager $entityManager): void
12 {
13 Director::$menu_data = new Menu($entityManager);
14 $previous_page = Director::$menu_data->findPageById((int)$_POST["location"]); // (int) à cause de declare(strict_types=1);
15 $parent = $previous_page->getParent();
16
17 $page = new Page(
18 trim(htmlspecialchars($_POST["label_input"])),
19 filter_var($_POST["url_input"], FILTER_VALIDATE_URL),
20 true, true, false,
21 $previous_page->getPosition(),
22 $parent); // peut et DOIT être null si on est au 1er niveau
23
24 // 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
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
27 if($parent == null){
28 $parent = Director::$menu_data;
29 }
30 $parent->addChild($page); // true pour réindexer les positions en BDD
31 $parent->reindexPositions();
32
33 $entityManager->persist($page);
34 $entityManager->flush();
35 header("Location: " . new URL(['page' => $_GET['from']]));
36 die;
37 }
38
39 static public function deleteUrlMenuEntry(EntityManager $entityManager): void
40 {
41 Director::$menu_data = new Menu($entityManager);
42 $page = Director::$menu_data->findPageById((int)$_POST["delete"]);
43 $parent = $page->getParent();
44 if($parent == null){
45 $parent = Director::$menu_data;
46 }
47
48 $parent->removeChild($page); // suppression de $children avant de trier
49 $parent->reindexPositions();
50
51 $entityManager->remove($page); // suppression en BDD
52 $entityManager->flush();
53 header("Location: " . new URL(['page' => $_GET['from']]));
54 die;
55 }
56
57 static public function MoveOneLevelUp(EntityManager $entityManager, array $json): void
58 {
59 $id = $json['id'];
60 $page = Director::$menu_data->findPageById((int)$id);
61
62 $parent = $page->getParent(); // peut être null
63 if($parent === null){
64 // 1er niveau: ne rien faire
65 echo json_encode(['success' => false]);
66 die;
67 }
68 // BDD
69 else{
70 $page->setPosition($parent->getPosition() + 1); // nouvelle position
71
72 // 2ème niveau: le parent devient $menu_data, puis null après tri
73 if($parent->getParent() === null){
74 // connexion dans les deux sens
75 $page->setParent(Director::$menu_data); // => pour la persistance
76
77 //Director::$menu_data->addChild($page); // => pour sortChildren
78 $page->getParent()->addChild($page); // => pour sortChildren
79 //Director::$menu_data->sortChildren(true); // positions décaléees des nouveaux petits frères
80 $page->getParent()->sortChildren(true); // positions décaléees des nouveaux petits frères
81 $page->setParent(null);
82
83 // affichage
84 $page->setPagePath($page->getEndOfPath());
85 $page->fillChildrenPagePath();
86 }
87 // 3ème niveau et plus
88 else{
89 $page->setParent($parent->getParent()); // nouveau parent
90 $page->getParent()->addChild($page); // => pour sortChildren
91 $page->getParent()->sortChildren(true); // positions décaléees des nouveaux petits frères
92 $page->fillChildrenPagePath($page->getParent()->getPagePath());
93 }
94 //$parent->sortChildren(true); // positions des enfants restants, inutile si la fonction est récursive?
95 $entityManager->flush();
96
97 // affichage
98 $parent->removeChild($page);
99 $nav_builder = new NavBuilder();
100 $menu_builder = new MenuBuilder(null, false);
101 echo json_encode(['success' => true, 'nav' => $nav_builder->render(), 'menu_buttons' => $menu_builder->render()]);
102 die;
103 }
104 }
105
106 static public function MoveOneLevelDown(EntityManager $entityManager, array $json): void
107 {
108 $id = $json['id'];
109 $page = Director::$menu_data->findPageById((int)$id);
110
111 $parent = $page->getParent(); // peut être null
112 if($parent == null){
113 $parent = Director::$menu_data;
114 }
115
116 // BDD
117 $parent->sortChildren(true); // trie et réindexe par sécurité: 1, 2, 3...
118 if($page->getPosition() > 1){
119 foreach($parent->getChildren() as $child){
120 if($child->getPosition() === $page->getPosition() - 1){
121 $page->setParent($child);
122 break;
123 }
124 }
125 $page->setPosition(count($page->getParent()->getChildren()) + 1);
126 }
127 $entityManager->flush();
128
129 // affichage
130 $parent->removeChild($page);
131 $page->getParent()->addChild($page);
132 $page->fillChildrenPagePath($page->getParent()->getPagePath()); // variable non mappée $page_path
133 $nav_builder = new NavBuilder();
134 $menu_builder = new MenuBuilder(null, false);
135
136 echo json_encode(['success' => true, 'nav' => $nav_builder->render(), 'menu_buttons' => $menu_builder->render()]);
137 die;
138 }
139
140 static public function SwitchPositions(EntityManager $entityManager, array $json): void
141 {
142 $id1 = $json['id1'];
143 $id2 = $json['id2'];
144
145 // vérifier qu'ils ont le même parent
146 $page1 = Director::$menu_data->findPageById((int)$id1);
147 $page2 = Director::$menu_data->findPageById((int)$id2);
148
149 // double le contrôle fait en JS
150 if($page1->getParent() === $page2->getParent()) // comparaison stricte d'objet (même instance du parent?)
151 {
152 // inversion
153 $tmp = $page1->getPosition();
154 $page1->setPosition($page2->getPosition());
155 $page2->setPosition($tmp);
156 Director::$menu_data->sortChildren(true); // modifie tableau children
157 $entityManager->flush();
158
159 // nouveau menu
160 $nav_builder = new NavBuilder();
161 echo json_encode(['success' => true, 'nav' => $nav_builder->render()]);
162 }
163 else{
164 echo json_encode(['success' => false]);
165 }
166
167 die;
168 }
169
170 static public function displayInMenu(EntityManager $entityManager, array $json): void
171 {
172 $id = $json['id'];
173 $checked = $json['checked'];
174
175 $page = Director::$menu_data->findPageById((int)$id);
176 if($page->isHidden() === $checked){
177 $page->setHidden(!$checked);
178 $entityManager->flush();
179
180 // nouveau menu
181 $nav_builder = new NavBuilder();
182 echo json_encode(['success' => true, 'nav' => $nav_builder->render()]);
183 }
184 else{
185 echo json_encode(['success' => false]);
186 }
187 die;
188 }
189} \ No newline at end of file
diff --git a/src/controller/PageManagementController.php b/src/controller/PageManagementController.php
new file mode 100644
index 0000000..f84c528
--- /dev/null
+++ b/src/controller/PageManagementController.php
@@ -0,0 +1,264 @@
1<?php
2// src/controller/PageManagementController.php
3
4declare(strict_types=1);
5
6use App\Entity\Page;
7use App\Entity\Node;
8use App\Entity\NodeData;
9//use App\Entity\Image;
10use Doctrine\Common\Collections\ArrayCollection;
11use Doctrine\ORM\EntityManager;
12
13class PageManagementController
14{
15 /* -- partie page -- */
16 static public function setPageTitle(EntityManager $entityManager, array $json): void
17 {
18 $page = $entityManager->find('App\Entity\Page', $json['page_id']);
19 $page->setPageName(htmlspecialchars($json['title']));
20 $entityManager->flush();
21 echo json_encode(['success' => true, 'title' => $page->getPageName()]);
22 die;
23 }
24
25 static public function updatePageMenuPath(EntityManager $entityManager): void
26 {
27 Director::$menu_data = new Menu($entityManager);
28 Director::$page_path = new Path();
29 $page = Director::$page_path->getLast();
30 $path = htmlspecialchars($_POST['page_menu_path']);
31
32 // mise en snake_case: filtre caractères non-alphanumériques, minuscule, doublons d'underscore, trim des underscores
33 $path = trim(preg_replace('/_+/', '_', strtolower(preg_replace('/[^a-zA-Z0-9]/', '_', $path))), '_');
34 $page->setEndOfPath($path);
35 foreach(Director::$menu_data->getChildren() as $child){
36 if($child->getEndOfPath() === Director::$page_path->getArray()[0]->getEndOfPath()){
37 $child->fillChildrenPagePath(); // MAJ de $page_path
38 }
39 }
40 $entityManager->flush();
41 header("Location: " . new URL(['page' => $page->getPagePath(), 'action' => 'modif_page']));
42 die;
43 }
44
45 static public function setPageDescription(EntityManager $entityManager, array $json): void
46 {
47 $node_data = $entityManager->find('App\Entity\NodeData', $json['node_data_id']);
48 $node_data->updateData('description', htmlspecialchars($json['description']));
49 $entityManager->flush();
50 echo json_encode(['success' => true, 'description' => $node_data->getData()['description']]);
51 die;
52 }
53
54 static public function newPage(EntityManager $entityManager): void
55 {
56 // titre et chemin
57 $director = new Director($entityManager, true);
58 //Director::$menu_data = new Menu($entityManager);
59 $previous_page = Director::$menu_data->findPageById((int)$_POST["page_location"]); // (int) à cause de declare(strict_types=1);
60 $parent = $previous_page->getParent();
61
62 $page = new Page(
63 trim(htmlspecialchars($_POST["page_name"])),
64 trim(htmlspecialchars($_POST["page_name_path"])),
65 true, true, false,
66 $previous_page->getPosition(),
67 $parent); // peut et DOIT être null si on est au 1er niveau
68
69 // on a donné à la nouvelle entrée la même position qu'à la précédente,
70 // addChild l'ajoute à la fin du tableau "children" puis on trie
71 // 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
72 if($parent == null){
73 $parent = Director::$menu_data;
74 }
75 $parent->addChild($page);
76 $parent->reindexPositions();
77
78 $page->setPagePath(ltrim($parent->getPagePath() . '/' . $page->getEndOfPath(), '/'));
79
80 // noeud "head"
81 $node = new Node(
82 'head',
83 null, [],
84 1, // position d'un head = 1
85 null, // pas de parent
86 $page);
87 $node->useDefaultAttributes(); // fichiers CSS et JS
88
89 $data = new NodeData([
90 // pas de titre, il est dans $page
91 'description' => trim(htmlspecialchars($_POST["page_description"]))],
92 $node);
93
94 $bulk_data = $entityManager
95 ->createQuery('SELECT n FROM App\Entity\Image n WHERE n.file_name LIKE :name')
96 ->setParameter('name', '%favicon%')
97 ->getResult();
98 $data->setImages(new ArrayCollection($bulk_data));
99
100 $entityManager->persist($page);
101 $entityManager->persist($node);
102 $entityManager->persist($data);
103 $entityManager->flush();
104
105 // page créée, direction la page en mode modification pour ajouter des blocs
106 header("Location: " . new URL(['page' => $page->getPagePath(), 'action' => 'modif_page']));
107 die;
108 }
109
110 static public function deletePage(EntityManager $entityManager): void
111 {
112 $page = $entityManager->find('App\Entity\Page', (int)$_POST['page_id']);
113 $nodes = $entityManager->getRepository('App\Entity\Node')->findBy(['page' => $page]);
114 $data = [];
115 foreach($nodes as $node){
116 $data[] = $entityManager->getRepository('App\Entity\NodeData')->findOneBy(['node' => $node]);
117 $entityManager->remove($node);
118 }
119 foreach($data as $one_data){
120 $entityManager->remove($one_data);
121 }
122 $entityManager->remove($page); // suppression en BDD
123
124 $entityManager->flush();
125 header("Location: " . new URL);
126 die;
127 }
128
129 /* partie "blocs" */
130 static public function addBloc(EntityManager $entityManager): void
131 {
132 $director = new Director($entityManager, true); // on a besoin de page_path qui dépend de menu_data
133 $page = Director::$page_path->getLast();
134 $director->findUniqueNodeByName('main');
135 $director->findItsChildren();
136 $main = $director->getNode();
137 $position = count($main->getChildren()) + 1; // position dans la fraterie
138
139 $blocks = ['blog', 'grid', 'calendar', 'galery', 'form']; // même liste dans FormBuilder.php
140 if(!in_array($_POST["bloc_select"], $blocks, true)) // 3è param: contrôle du type
141 {
142 header("Location: " . new URL(['page' => $_GET['page'], 'error' => 'bad_bloc_type']));
143 die;
144 }
145
146 if($_POST["bloc_select"] === 'calendar' || $_POST["bloc_select"] === 'form'){
147 $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page AND n.name_node = :name'; // noeud 'head' de la page
148 $bulk_data = $entityManager
149 ->createQuery($dql)
150 ->setParameter('page', $page)
151 ->setParameter('name', 'head')
152 ->getResult();
153
154 if(count($bulk_data) != 1){ // 1 head par page
155 header("Location: " . new URL(['page' => $_GET['page'], 'error' => 'head_node_not_found']));
156 die;
157 }
158
159 $bulk_data[0]->addAttribute('css_array', $_POST["bloc_select"]);
160 if($_POST["bloc_select"] === 'form'){
161 $bulk_data[0]->addAttribute('js_array', $_POST["bloc_select"]);
162 }
163 $entityManager->persist($bulk_data[0]);
164 }
165
166 $bloc = new Node(
167 $_POST["bloc_select"],
168 null, [],
169 $position,
170 $main,
171 $page);
172 $data = new NodeData(
173 ['title' => trim(htmlspecialchars($_POST["bloc_title"]))],
174 $bloc);
175
176 $entityManager->persist($bloc);
177 $entityManager->persist($data);
178 $entityManager->flush();
179 header("Location: " . new URL(['page' => $_GET['page'], 'action' => 'modif_page']));
180 die;
181 }
182
183 static public function deleteBloc(EntityManager $entityManager): void
184 {
185 $director = new Director($entityManager, true);
186 $director->findUniqueNodeByName('main');
187 $director->findItsChildren();
188 //$director->findNodeById((int)$_POST['delete_bloc_id']);
189 $main = $director->getNode();
190 $bloc = null;
191 foreach($main->getChildren() as $child){
192 if($child->getId() === (int)$_POST['delete_bloc_id']){
193 $bloc = $child;
194 break;
195 }
196 }
197 if(!empty($bloc)){ // si $bloc est null c'est que le HTML a été modifié volontairement
198 $main->removeChild($bloc); // réindex le tableau $children au passage
199 $main->reindexPositions();
200 $entityManager->remove($bloc); // suppression en BDD
201 $entityManager->flush();
202 }
203 header("Location: " . new URL(['page' => $_GET['page'], 'action' => 'modif_page']));
204 die;
205 }
206
207 static public function renameBloc(EntityManager $entityManager, array $json): void
208 {
209 if(isset($json['bloc_title']) && $json['bloc_title'] !== null && isset($json['bloc_id']) && is_int($json['bloc_id'])){
210 $director = new Director($entityManager);
211 $director->findNodeById($json['bloc_id']);
212
213 // le titre (du JSON en BDD) est récupéré sous forme de tableau, modifié et renvoyé
214 $data = $director->getNode()->getNodeData()->getData();
215 $data['title'] = htmlspecialchars($json['bloc_title']);
216 $director->getNode()->getNodeData()->updateData('title', htmlspecialchars($json['bloc_title']));
217
218 $entityManager->flush();
219 echo json_encode(['success' => true, 'title' => $data['title']]);
220 }
221 else{
222 echo json_encode(['success' => false]);
223 }
224 die;
225 }
226
227 static public function SwitchBlocsPositions(EntityManager $entityManager, array $json): void
228 {
229 if(isset($json['id1']) && is_int($json['id1']) && isset($json['id2']) && is_int($json['id2']) && isset($_GET['page'])){
230 $director = new Director($entityManager, true);
231 $director->findUniqueNodeByName('main');
232 $director->findItsChildren();
233 $main = $director->getNode();
234 $main->sortChildren(true); // régénère les positions avant inversion
235
236 $bloc1 = null;
237 $bloc2 = null;
238 foreach($main->getChildren() as $child){
239 if($child->getId() === $json['id1']){
240 $bloc1 = $child;
241 break;
242 }
243 }
244 foreach($main->getChildren() as $child){
245 if($child->getId() === $json['id2']){
246 $bloc2 = $child;
247 break;
248 }
249 }
250
251 // inversion
252 $tmp = $bloc1->getPosition();
253 $bloc1->setPosition($bloc2->getPosition());
254 $bloc2->setPosition($tmp);
255
256 $entityManager->flush();
257 echo json_encode(['success' => true]);
258 }
259 else{
260 echo json_encode(['success' => false]);
261 }
262 die;
263 }
264} \ No newline at end of file
diff --git a/src/controller/ajax_admin.php b/src/controller/ajax_admin.php
deleted file mode 100644
index b69be77..0000000
--- a/src/controller/ajax_admin.php
+++ /dev/null
@@ -1,636 +0,0 @@
1<?php
2// src/controller/ajax_admin.php
3
4declare(strict_types=1);
5
6use App\Entity\Page;
7use App\Entity\Node;
8use App\Entity\Article;
9
10// mettre ça ailleurs
11function imagickCleanImage(string $image_data, string $local_path, string $format = 'jpeg'): bool // "string" parce que file_get_contents...
12{
13 try{
14 $imagick = new Imagick();
15 $imagick->readImageBlob($image_data);
16 $imagick->stripImage(); // nettoyage métadonnées
17 $imagick->setImageFormat($format);
18 if($format === 'jpeg'){
19 $imagick->setImageCompression(Imagick::COMPRESSION_JPEG);
20 $imagick->setImageCompressionQuality(85); // optionnel
21 }
22 $imagick->writeImage($local_path); // enregistrement
23 $imagick->clear();
24 $imagick->destroy();
25 return true;
26 }
27 catch(Exception $e){
28 return false;
29 }
30}
31function curlDownloadImage(string $url, $maxRetries = 3, $timeout = 10): string|false
32{
33 $attempt = 0;
34 $imageData = false;
35
36 while($attempt < $maxRetries){
37 $ch = curl_init($url); // instance de CurlHandle
38 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
39 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
40 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
41 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
42 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
43 curl_setopt($ch, CURLOPT_USERAGENT, 'TinyMCE-Image-Downloader');
44
45 $imageData = curl_exec($ch);
46 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
47 //$curlError = curl_error($ch);
48
49 curl_close($ch);
50
51 if($imageData !== false && $httpCode >= 200 && $httpCode < 300){
52 return $imageData;
53 }
54
55 $attempt++;
56 sleep(1);
57 }
58
59 return false; // échec après trois tentatives
60}
61
62function imageUploadTinyMce(): void
63{
64 if(isset($_FILES['file'])){
65 $file = $_FILES['file'];
66 $dest = 'images/';
67 $dest_mini = 'images-mini/';
68
69 // Vérifier si les répertoires existent, sinon les créer
70 if(!is_dir($dest)) {
71 mkdir($dest, 0700, true);
72 }
73 if(!is_dir($dest_mini)) {
74 mkdir($dest_mini, 0700, true);
75 }
76
77 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif'];
78 $name = Security::secureFileName(pathinfo($file['name'], PATHINFO_FILENAME));
79 $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
80 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
81 $extension = 'jpeg';
82 }
83 $file_path = $dest . $name . '_' . uniqid() . '.' . $extension;
84
85 // créer une miniature de l'image
86 //
87
88 if(imagickCleanImage(file_get_contents($file['tmp_name']), $file_path, $extension)){ // recréer l’image pour la nettoyer
89 echo json_encode(['location' => $file_path]); // renvoyer l'URL de l'image téléchargée
90 }
91 else{
92 http_response_code(500);
93 echo json_encode(['message' => 'Erreur image non valide']);
94 }
95 }
96 else{
97 http_response_code(400);
98 echo json_encode(['message' => 'Erreur 400: Bad Request']);
99 }
100 die;
101}
102
103// détection des requêtes d'upload d'image de tinymce
104if(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false && isset($_GET['action']) && $_GET['action'] === 'upload_image')
105{
106 imageUploadTinyMce();
107}
108// cas du collage d'un contenu HTML, réception d'une URL, téléchargement par le serveur et renvoie de l'adresse sur le serveur
109elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url')
110{
111 $json = json_decode(file_get_contents('php://input'), true);
112
113 if(isset($json['image_url'])){
114 $image_data = curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents
115 $dest = 'images/';
116
117 if(!is_dir($dest)) { // Vérifier si le répertoire existe, sinon le créer
118 mkdir($dest, 0777, true);
119 }
120
121 if($image_data === false){
122 http_response_code(400);
123 echo json_encode(['message' => "Erreur, le serveur n'a pas réussi à télécharger l'image."]);
124 die;
125 }
126
127 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif'];
128 $url_path = parse_url($json['image_url'], PHP_URL_PATH);
129 $name = Security::secureFileName(pathinfo($url_path, PATHINFO_FILENAME));
130 $extension = strtolower(pathinfo($url_path, PATHINFO_EXTENSION));
131 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
132 $extension = 'jpeg';
133 }
134 $local_path = $dest . $name . '_' . uniqid() . '.' . $extension;
135
136 if(imagickCleanImage($image_data, $local_path, $extension)){ // recréer l’image pour la nettoyer
137 echo json_encode(['location' => $local_path]); // nouvelle adresse
138 }
139 else{
140 http_response_code(500);
141 echo json_encode(['message' => 'Erreur image non valide']);
142 }
143 }
144 else{
145 echo json_encode(['message' => 'Erreur 400: Bad Request']);
146 }
147 die;
148}
149// cas du collage d'une image (code base64) non encapsulée dans du HTML
150elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_base64')
151{
152 $json = json_decode(file_get_contents('php://input'), true);
153 $dest = 'images/';
154
155 if(!is_dir('images')){
156 mkdir('images', 0777, true);
157 }
158
159 // détection de data:image/ et de ;base64, et capture du format dans $type
160 if(!isset($json['image_base64']) || !preg_match('/^data:image\/(\w+);base64,/', $json['image_base64'], $type)){
161 http_response_code(400);
162 echo json_encode(['message' => 'Données image base64 manquantes ou invalides']);
163 die;
164 }
165
166 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif'];
167 $extension = strtolower($type[1]);
168 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
169 $extension = 'jpeg';
170 }
171
172 $image_data = base64_decode(substr($json['image_base64'], strpos($json['image_base64'], ',') + 1)); // découpe la chaine à la virgule puis convertit en binaire
173 if($image_data === false){
174 http_response_code(400);
175 echo json_encode(['message' => 'Décodage base64 invalide']);
176 die;
177 }
178
179 $local_path = $dest . 'pasted_image_' . uniqid() . '.' . $extension;
180
181 if(imagickCleanImage($image_data, $local_path)){
182 echo json_encode(['location' => $local_path]);
183 }
184 else{
185 http_response_code(500);
186 echo json_encode(['message' => 'Erreur image non valide']);
187 }
188 die;
189}
190
191// détection des requêtes de type XHR, y en a pas à priori
192/*elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){
193 echo "requête XHR reçue par le serveur";
194 die;
195}*/
196
197
198// détection des requêtes envoyées avec fetch (application/json) et récupération du JSON
199if($_SERVER['CONTENT_TYPE'] === 'application/json')
200{
201 $data = file_get_contents('php://input');
202 $json = json_decode($data, true);
203
204 if(isset($_GET['action']))
205 {
206 if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content']))
207 {
208 if(json_last_error() === JSON_ERROR_NONE)
209 {
210 $id = $json['id'];
211 $director = new Director($entityManager);
212
213 // cas d'une nouvelle "news"
214 if(is_array($json['content'])){
215 foreach($json['content'] as $one_input){
216 $one_input = Security::secureString($one_input);
217 }
218 $content = $json['content'];
219 }
220 else{
221 $content = Security::secureString($json['content']);
222 }
223
224 // nouvel article
225 if($id[0] === 'n')
226 {
227 $section_id = (int)substr($id, 1); // id du bloc <section>
228 $director->findNodeById($section_id);
229 $director->makeSectionNode();
230 $node = $director->getNode(); // = <section>
231
232 if(is_array($content)){
233 $date = new \DateTime($content['d']);
234 $article = new Article($content['i'], $date, $content['t'], $content['p']);
235 $article_node = new Node('new', 'i' . (string)$date->getTimestamp(), [], count($node->getChildren()) + 1, $node, $node->getPage(), $article);
236
237 // id_node tout juste généré
238 //$article_node->getId();
239 }
240 else{
241 $timestamp = time();
242 $date = new \DateTime;
243 $date->setTimestamp($timestamp);
244
245 $article = new Article($content, $date); // le "current" timestamp est obtenu par la BDD
246 $article_node = new Node('article', 'i' . (string)$timestamp, [], count($node->getChildren()) + 1, $node, $node->getPage(), $article);
247 }
248
249 $entityManager->persist($article_node);
250 $entityManager->flush();
251
252 echo json_encode(['success' => true, 'article_id' => $article_node->getArticleTimestamp()]);
253 die;
254 }
255 // modification article
256 else{
257 $id[0] = 'i'; // id de l'article node
258 }
259
260 if($director->makeArticleNode($id)) // une entrée est trouvée
261 {
262 $node = $director->getArticleNode(); // article
263 switch($json['id'][0]){
264 case 'i':
265 $node->getArticle()->setContent($content);
266 break;
267 case 'p':
268 $node->getArticle()->setPreview($content); // html de l'éditeur
269 break;
270 case 't':
271 $node->getArticle()->setTitle($content); // html de l'éditeur
272 break;
273 case 'd':
274 echo json_encode(['success' => false, 'message' => 'l\'action editor_submit ne supporte pas les dates, utiliser date_submit.']);
275 die;
276 default:
277 echo json_encode(['success' => false, 'message' => 'identifiant non utilisable']);
278 die;
279 }
280 $entityManager->flush();
281 echo json_encode(['success' => true]);
282 }
283 else
284 {
285 echo json_encode(['success' => false, 'message' => 'article non identifié']);
286 }
287 }
288 else{
289 echo json_encode(['success' => false, 'message' => 'Erreur de décodage JSON']);
290 }
291 die;
292 }
293 elseif($_GET['action'] === 'delete_article' && isset($json['id']))
294 {
295 $director = new Director($entityManager);
296 $director->makeArticleNode($json['id'], true);
297 $article = $director->getArticleNode();
298 $section = $director->getNode();
299
300 $entityManager->remove($article);
301 $section->removeChild($article);
302 $section->sortChildren(true); // régénère les positions
303 $entityManager->flush();
304
305 // test avec une nouvelle requête qui ne devrait rien trouver
306 if(!$director->makeArticleNode($json['id']))
307 {
308 echo json_encode(['success' => true]);
309
310 // on pourrait afficher une notification "toast"
311 }
312 else{
313 http_response_code(500);
314 echo json_encode(['success' => false, 'message' => 'Erreur lors de la suppression de l\'article.']);
315 }
316 die;
317 }
318 // inversion de la position de deux noeuds
319 elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2']))
320 {
321 $director = new Director($entityManager);
322 $director->makeArticleNode($json['id1'], true);
323 $article1 = $director->getArticleNode();
324 $section = $director->getNode();
325
326 $section->sortChildren(true); // régénère les positions avant inversion
327
328 $article2;
329 foreach($section->getChildren() as $child){
330 if($child->getArticleTimestamp() === $json['id2']) // type string
331 {
332 $article2 = $child;
333 break;
334 }
335 }
336
337 // inversion
338 $tmp = $article1->getPosition();
339 $article1->setPosition($article2->getPosition());
340 $article2->setPosition($tmp);
341 $entityManager->flush();
342
343 echo json_encode(['success' => true]);
344 die;
345 }
346 elseif($_GET['action'] === 'date_submit' && isset($json['id']) && isset($json['date']))
347 {
348 $id = $json['id'];
349 $id[0] = 'i';
350 $date = new DateTime($json['date']);
351
352 $director = new Director($entityManager);
353 $director->makeArticleNode($id);
354 $node = $director->getArticleNode();
355 $node->getArticle()->setDateTime($date);
356 $entityManager->flush();
357
358 echo json_encode(['success' => true]);
359 die;
360 }
361
362
363 /* -- bloc Formulaire -- */
364 elseif($_GET['action'] === 'recipient_email'){
365 $email = htmlspecialchars(trim($json['email']));
366
367 if((filter_var($email, FILTER_VALIDATE_EMAIL) // nouvel e-mail
368 || ($json['email'] === '' && !empty(Config::$email_dest))) // e-mail par défaut
369 && isset($json['hidden']) && empty($json['hidden']))
370 {
371 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
372 $form_data->updateData('email', $email);
373 $entityManager->persist($form_data);
374 $entityManager->flush();
375
376 echo json_encode(['success' => true]);
377 }
378 else{
379 echo json_encode(['success' => false]);
380 }
381 die;
382 }
383 elseif($_GET['action'] === 'test_email'){
384 // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur
385 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
386 $recipient = $form_data->getData()['email'] ?? Config::$email_dest;
387
388 if(sendEmail($recipient, false, 'nom du visiteur', 'adresse@du_visiteur.fr', "TEST d'un envoi d'e-mail depuis le site web")){
389 echo json_encode(['success' => true]);
390 }
391 else{
392 echo json_encode(['success' => false]);
393 }
394 die;
395 }
396 }
397
398
399 /* -- page Menu et chemins -- */
400 elseif(isset($_GET['menu_edit']))
401 {
402 // récupération des données
403 $data = file_get_contents('php://input');
404 $json = json_decode($data, true);
405 Director::$menu_data = new Menu($entityManager);
406
407 // flèche gauche <=: position = position du parent + 1, parent = grand-parent, recalculer les positions
408 if($_GET['menu_edit'] === 'move_one_level_up' && isset($json['id'])){
409 $id = $json['id'];
410 $page = Director::$menu_data->findPageById((int)$id);
411
412 $parent = $page->getParent(); // peut être null
413 if($parent === null){
414 // 1er niveau: ne rien faire
415 echo json_encode(['success' => false]);
416 die;
417 }
418 // BDD
419 else{
420 $page->setPosition($parent->getPosition() + 1); // nouvelle position
421
422 // 2ème niveau: le parent devient $menu_data, puis null après tri
423 if($parent->getParent() === null){
424 // connexion dans les deux sens
425 $page->setParent(Director::$menu_data); // => pour la persistance
426
427 //Director::$menu_data->addChild($page); // => pour sortChildren
428 $page->getParent()->addChild($page); // => pour sortChildren
429 //Director::$menu_data->sortChildren(true); // positions décaléees des nouveaux petits frères
430 $page->getParent()->sortChildren(true); // positions décaléees des nouveaux petits frères
431 $page->setParent(null);
432
433 // affichage
434 $page->setPagePath($page->getEndOfPath());
435 $page->fillChildrenPagePath();
436 }
437 // 3ème niveau et plus
438 else{
439 $page->setParent($parent->getParent()); // nouveau parent
440 $page->getParent()->addChild($page); // => pour sortChildren
441 $page->getParent()->sortChildren(true); // positions décaléees des nouveaux petits frères
442 $page->fillChildrenPagePath($page->getParent()->getPagePath());
443 }
444 //$parent->sortChildren(true); // positions des enfants restants, inutile si la fonction est récursive?
445 $entityManager->flush();
446
447 // affichage
448 $parent->removeChild($page);
449 $nav_builder = new NavBuilder();
450 $menu_builder = new MenuBuilder(null, false);
451 echo json_encode(['success' => true, 'nav' => $nav_builder->render(), 'menu_buttons' => $menu_builder->render()]);
452 die;
453 }
454 }
455
456 // flèche droite =>: position = nombre d'éléments de la fraterie + 1, l'élément précédent devient le parent
457 if($_GET['menu_edit'] === 'move_one_level_down' && isset($json['id'])){
458 $id = $json['id'];
459 $page = Director::$menu_data->findPageById((int)$id);
460
461 $parent = $page->getParent(); // peut être null
462 if($parent == null){
463 $parent = Director::$menu_data;
464 }
465
466 // BDD
467 $parent->sortChildren(true); // trie et réindexe par sécurité: 1, 2, 3...
468 if($page->getPosition() > 1){
469 foreach($parent->getChildren() as $child){
470 if($child->getPosition() === $page->getPosition() - 1){
471 $page->setParent($child);
472 break;
473 }
474 }
475 $page->setPosition(count($page->getParent()->getChildren()) + 1);
476 }
477 $entityManager->flush();
478
479 // affichage
480 $parent->removeChild($page);
481 $page->getParent()->addChild($page);
482 $page->fillChildrenPagePath($page->getParent()->getPagePath()); // variable non mappée $page_path
483 $nav_builder = new NavBuilder();
484 $menu_builder = new MenuBuilder(null, false);
485
486 echo json_encode(['success' => true, 'nav' => $nav_builder->render(), 'menu_buttons' => $menu_builder->render()]);
487 die;
488 }
489
490 if($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2']))
491 {
492 $id1 = $json['id1'];
493 $id2 = $json['id2'];
494
495 // vérifier qu'ils ont le même parent
496 $page1 = Director::$menu_data->findPageById((int)$id1);
497 $page2 = Director::$menu_data->findPageById((int)$id2);
498
499 // double le contrôle fait en JS
500 if($page1->getParent() === $page2->getParent()) // comparaison stricte d'objet (même instance du parent?)
501 {
502 // inversion
503 $tmp = $page1->getPosition();
504 $page1->setPosition($page2->getPosition());
505 $page2->setPosition($tmp);
506 Director::$menu_data->sortChildren(true); // modifie tableau children
507 $entityManager->flush();
508
509 // nouveau menu
510 $nav_builder = new NavBuilder();
511 echo json_encode(['success' => true, 'nav' => $nav_builder->render()]);
512 }
513 else{
514 echo json_encode(['success' => false]);
515 }
516
517 die;
518 }
519
520 if($_GET['menu_edit'] === 'displayInMenu' && isset($json['id']) && isset($json['checked']))
521 {
522 $id = $json['id'];
523 $checked = $json['checked'];
524
525 $page = Director::$menu_data->findPageById((int)$id);
526 if($page->isHidden() === $checked){
527 $page->setHidden(!$checked);
528 $entityManager->flush();
529
530 // nouveau menu
531 $nav_builder = new NavBuilder();
532 echo json_encode(['success' => true, 'nav' => $nav_builder->render()]);
533 }
534 else{
535 echo json_encode(['success' => false]);
536 }
537 die;
538 }
539 }
540
541
542 /* -- mode Modification d'une page -- */
543
544 // partie "page"
545 elseif(isset($_GET['page_edit']))
546 {
547 // récupération des données
548 $data = file_get_contents('php://input');
549 $json = json_decode($data, true);
550
551 // titre de la page
552 if($_GET['page_edit'] === 'page_title'){
553 $page = $entityManager->find('App\Entity\Page', $json['page_id']);
554 $page->setPageName(htmlspecialchars($json['title']));
555 $entityManager->flush();
556 echo json_encode(['success' => true, 'title' => $page->getPageName()]);
557 }
558 // titre en snake_case pour le menu
559 /*elseif($_GET['page_edit'] === 'page_menu_path'){
560 $page = $entityManager->find('App\Entity\Page', $json['page_id']);
561 $page->setEndOfPath(htmlspecialchars($json['page_menu_path']));
562 $entityManager->flush();
563 echo json_encode(['success' => true, 'page_name_path' => $page->getEndOfPath()]);
564 }*/
565 // description dans les métadonnées
566 elseif($_GET['page_edit'] === 'page_description'){
567 $node_data = $entityManager->find('App\Entity\NodeData', $json['node_data_id']);
568 $node_data->updateData('description', htmlspecialchars($json['description']));
569 $entityManager->flush();
570 echo json_encode(['success' => true, 'description' => $node_data->getData()['description']]);
571 }
572 die;
573 }
574
575 // partie "blocs"
576 elseif(isset($_GET['bloc_edit']))
577 {
578 // renommage d'un bloc
579 if($_GET['bloc_edit'] === 'rename_page_bloc')
580 {
581 if(isset($json['bloc_title']) && $json['bloc_title'] !== null && isset($json['bloc_id']) && is_int($json['bloc_id'])){
582 $director = new Director($entityManager);
583 $director->findNodeById($json['bloc_id']);
584
585 // le titre (du JSON en BDD) est récupéré sous forme de tableau, modifié et renvoyé
586 $data = $director->getNode()->getNodeData()->getData();
587 $data['title'] = htmlspecialchars($json['bloc_title']);
588 $director->getNode()->getNodeData()->updateData('title', htmlspecialchars($json['bloc_title']));
589
590 $entityManager->flush();
591 echo json_encode(['success' => true, 'title' => $data['title']]);
592 }
593 else{
594 echo json_encode(['success' => false]);
595 }
596 die;
597 }
598 // inversion des positions de deux blocs
599 elseif($_GET['bloc_edit'] === 'switch_blocs_positions')
600 {
601 if(isset($json['id1']) && is_int($json['id1']) && isset($json['id2']) && is_int($json['id2']) && isset($_GET['page'])){
602 $director = new Director($entityManager, true);
603 $director->findUniqueNodeByName('main');
604 $director->findItsChildren();
605 $main = $director->getNode();
606 $main->sortChildren(true); // régénère les positions avant inversion
607
608 $bloc1; $bloc2;
609 foreach($main->getChildren() as $child){
610 if($child->getId() === $json['id1']){
611 $bloc1 = $child;
612 break;
613 }
614 }
615 foreach($main->getChildren() as $child){
616 if($child->getId() === $json['id2']){
617 $bloc2 = $child;
618 break;
619 }
620 }
621
622 // inversion
623 $tmp = $bloc1->getPosition();
624 $bloc1->setPosition($bloc2->getPosition());
625 $bloc2->setPosition($tmp);
626
627 $entityManager->flush();
628 echo json_encode(['success' => true]);
629 }
630 else{
631 echo json_encode(['success' => false]);
632 }
633 die;
634 }
635 }
636} \ No newline at end of file
diff --git a/src/controller/ajax_calendar_admin.php b/src/controller/ajax_calendar_admin.php
deleted file mode 100644
index 0baf73e..0000000
--- a/src/controller/ajax_calendar_admin.php
+++ /dev/null
@@ -1,54 +0,0 @@
1<?php
2// src/controller/ajax_calendar_admin.php
3
4declare(strict_types=1);
5
6use App\Entity\Event;
7
8// actions sur le calendrier
9if(isset($_SESSION['admin']) && $_SESSION['admin'] === true
10 && $_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/json')
11{
12 $data = file_get_contents('php://input');
13 $json = json_decode($data, true);
14
15 if($_GET['action'] === 'new_event'){
16 try{
17 $event = new Event($json);
18 }
19 catch(InvalidArgumentException $e){
20 echo json_encode(['success' => false, 'error' => $e->getMessage()]);
21 http_response_code(400);
22 die;
23 }
24 $entityManager->persist($event);
25 $entityManager->flush();
26
27 echo json_encode(['success' => true, 'id' => $event->getId()]);
28 }
29 elseif($_GET['action'] === 'update_event'){
30 $event = $entityManager->find('App\Entity\Event', (int)$json['id']);
31 try{
32 $event->securedUpdateFromJSON($json);
33 }
34 catch(InvalidArgumentException $e){
35 echo json_encode(['success' => false, 'error' => $e->getMessage()]);
36 http_response_code(400);
37 die;
38 }
39 $entityManager->flush();
40
41 echo json_encode(['success' => true]);
42 }
43 elseif($_GET['action'] === 'remove_event'){
44 $event = $entityManager->find('App\Entity\Event', (int)$json['id']);
45 $entityManager->remove($event);
46 $entityManager->flush();
47
48 echo json_encode(['success' => true]);
49 }
50 else{
51 echo json_encode(['success' => false]);
52 }
53 die;
54} \ No newline at end of file
diff --git a/src/controller/ajax_calendar_visitor.php b/src/controller/ajax_calendar_visitor.php
deleted file mode 100644
index dcdbebd..0000000
--- a/src/controller/ajax_calendar_visitor.php
+++ /dev/null
@@ -1,39 +0,0 @@
1<?php
2// src/controller/ajax_calendar_visitor.php
3
4declare(strict_types=1);
5
6use App\Entity\Event;
7
8// chargement des évènements à la création du calendrier
9// et au changement de dates affichées (boutons flèches mais pas changement de vue)
10if($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'get_events'
11 && isset($_GET['start']) && isset($_GET['end']) && empty($_POST))
12{
13 // bornes début et fin du calendrier affiché à l'heure locale
14 // noter que la vue "planning" est similaire à la vue "semaine"
15 $start = new DateTime($_GET['start']);
16 $end = new DateTime($_GET['end']);
17 $start->setTimezone(new DateTimeZone('UTC'));
18 $end->setTimezone(new DateTimeZone('UTC'));
19
20 // affichage format ISO à l'heure UTC
21 //$date->format('Y-m-d\TH:i:s\Z');
22
23 // on prend les évènements se finissant après le début ou commençant avant la fin de la fourchette
24 $dql = 'SELECT e FROM App\Entity\Event e WHERE e.end >= :start AND e.start <= :end';
25 $bulk_data = $entityManager->createQuery($dql)
26 ->setParameter('start', $start)
27 ->setParameter('end', $end)
28 ->getResult();
29
30 $events = [];
31 foreach($bulk_data as $one_entry){
32 $event = new EventDTO($one_entry);
33 $events[] = $event->toArray();
34 }
35
36 header('Content-Type: application/json');
37 echo json_encode($events);
38 die;
39} \ No newline at end of file
diff --git a/src/controller/ajax_email.php b/src/controller/ajax_email.php
deleted file mode 100644
index 1138e04..0000000
--- a/src/controller/ajax_email.php
+++ /dev/null
@@ -1,105 +0,0 @@
1<?php
2// src/controller/ajax.php
3
4declare(strict_types=1);
5
6use PHPMailer\PHPMailer\PHPMailer;
7use PHPMailer\PHPMailer\Exception;
8use App\Entity\Email;
9use Doctrine\ORM\EntityManager;
10
11// mettre ça ailleurs?
12function sendEmail(string $recipient, bool $true_email, string $name = '', string $email = '', string $message = ''): bool
13{
14 $mail = new PHPMailer(true); // true => exceptions
15 $mail->CharSet = 'UTF-8';
16
17 try{
18 // Paramètres du serveur
19 $mail->isSMTP();
20 $mail->Host = Config::$smtp_host;
21 $mail->SMTPAuth = true;
22 $mail->Port = 25;
23
24 if($mail->SMTPAuth){
25 $mail->Username = Config::$smtp_username; // e-mail
26 $mail->Password = Config::$smtp_password;
27 $mail->SMTPSecure = Config::$smtp_secure; // tls (starttls) ou ssl (smtps)
28 if($mail->SMTPSecure === 'tls'){
29 $mail->Port = 587;
30 }
31 elseif($mail->SMTPSecure === 'ssl'){
32 $mail->Port = 465;
33 }
34 }
35 //var_dump($mail->smtpConnect());die; // test de connexion
36
37 // Expéditeur et destinataire
38 $mail->setFrom(strtolower(Config::$email_from), Config::$email_from_name); // expéditeur
39 $mail->addAddress(strtolower($recipient), Config::$email_dest_name); // destinataire
40
41 // Contenu
42 $mail->isHTML(true);
43 if($true_email){
44 $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web';
45
46 }
47 else{
48 $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web";
49 }
50 $mail->Body = $message;
51 $mail->AltBody = $message;
52
53 $mail->send();
54 return true;
55 }
56 catch(Exception $e){
57 return false;
58 //echo "Le message n'a pas pu être envoyé. Erreur : {$mail->ErrorInfo}";
59 }
60}
61
62function submitEmail(array $json, EntityManager $entityManager): void
63{
64 $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0;
65 $captcha_try = isset($json['captcha']) ? Captcha::controlInput($json['captcha']) : 0;
66
67 // contrôles des entrées
68 $name = htmlspecialchars(trim($json['name']));
69 $email = strtolower(htmlspecialchars(trim($json['email'])));
70 $message = htmlspecialchars(trim($json['message']));
71
72 // destinataire = e-mail par défaut dans config.ini OU choisi par l'utilisateur
73 $form_data = $entityManager->find('App\Entity\NodeData', $json['id']);
74 $recipient = $form_data->getData()['email'] ?? Config::$email_dest;
75
76 if($captcha_try != 0 && $captcha_solution != 0 && ($captcha_try === $captcha_solution)
77 && filter_var($email, FILTER_VALIDATE_EMAIL) && isset($json['hidden']) && empty($json['hidden'])
78 && sendEmail($recipient, true, $name, $email, $message))
79 {
80 $db_email = new Email($email, Config::$email_dest, $message);
81 $entityManager->persist($db_email);
82 $entityManager->flush();
83 echo json_encode(['success' => true]);
84 }
85 else{
86 echo json_encode(['success' => false]);
87 }
88 die;
89}
90
91
92// détection des requêtes envoyées avec fetch (application/json) et récupération du JSON
93if($_SERVER['CONTENT_TYPE'] === 'application/json')
94{
95 $data = file_get_contents('php://input');
96 $json = json_decode($data, true);
97
98 if(isset($_GET['action']))
99 {
100 // formulaire de contact
101 if($_GET['action'] === 'send_email'){
102 submitEmail($json, $entityManager);
103 }
104 }
105} \ No newline at end of file
diff --git a/src/controller/password.php b/src/controller/password.php
index e91cc16..3d003da 100644
--- a/src/controller/password.php
+++ b/src/controller/password.php
@@ -226,7 +226,8 @@ function changePassword(EntityManager $entityManager): void
226 } 226 }
227 elseif(!isset($_POST['login']) || empty($_POST['login']) 227 elseif(!isset($_POST['login']) || empty($_POST['login'])
228 || !isset($_POST['old_password']) || empty($_POST['old_password']) 228 || !isset($_POST['old_password']) || empty($_POST['old_password'])
229 || !isset($_POST['new_password']) || empty($_POST['new_password'])) 229 || !isset($_POST['new_password']) || empty($_POST['new_password'])
230 || !isset($_POST['modify_password_hidden']) || !empty($_POST['modify_password_hidden']))
230 { 231 {
231 $error = 'bad_login_or_password'; 232 $error = 'bad_login_or_password';
232 } 233 }
diff --git a/src/controller/post_functions_admin.php b/src/controller/post_functions_admin.php
deleted file mode 100644
index b47850a..0000000
--- a/src/controller/post_functions_admin.php
+++ /dev/null
@@ -1,231 +0,0 @@
1<?php
2// src/controller/post_functions_admin.php
3//
4// sera remplacé pas une ou plusieurs classes pour le routeur de symfony
5
6declare(strict_types=1);
7
8use App\Entity\Node;
9use App\Entity\NodeData;
10use App\Entity\Page;
11//use App\Entity\Image;
12use Doctrine\Common\Collections\ArrayCollection;
13use Doctrine\ORM\EntityManager;
14
15
16function newPage(EntityManager $entityManager): void
17{
18 // titre et chemin
19 $director = new Director($entityManager, true);
20 //Director::$menu_data = new Menu($entityManager);
21 $previous_page = Director::$menu_data->findPageById((int)$_POST["page_location"]); // (int) à cause de declare(strict_types=1);
22 $parent = $previous_page->getParent();
23
24 $page = new Page(
25 trim(htmlspecialchars($_POST["page_name"])),
26 trim(htmlspecialchars($_POST["page_name_path"])),
27 true, true, false,
28 $previous_page->getPosition(),
29 $parent); // peut et DOIT être null si on est au 1er niveau
30
31 // on a donné à la nouvelle entrée la même position qu'à la précédente,
32 // addChild l'ajoute à la fin du tableau "children" puis on trie
33 // 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
34 if($parent == null){
35 $parent = Director::$menu_data;
36 }
37 $parent->addChild($page);
38 $parent->reindexPositions();
39
40 $page->setPagePath(ltrim($parent->getPagePath() . '/' . $page->getEndOfPath(), '/'));
41
42 // noeud "head"
43 $node = new Node(
44 'head',
45 null, [],
46 1, // position d'un head = 1
47 null, // pas de parent
48 $page);
49 $node->useDefaultAttributes(); // fichiers CSS et JS
50
51 $data = new NodeData([
52 // pas de titre, il est dans $page
53 'description' => trim(htmlspecialchars($_POST["page_description"]))],
54 $node);
55
56 $bulk_data = $entityManager
57 ->createQuery('SELECT n FROM App\Entity\Image n WHERE n.file_name LIKE :name')
58 ->setParameter('name', '%favicon%')
59 ->getResult();
60 $data->setImages(new ArrayCollection($bulk_data));
61
62 $entityManager->persist($page);
63 $entityManager->persist($node);
64 $entityManager->persist($data);
65 $entityManager->flush();
66
67 // page créée, direction la page en mode modification pour ajouter des blocs
68 header("Location: " . new URL(['page' => $page->getPagePath(), 'action' => 'modif_page']));
69 die;
70}
71
72function deletePage(EntityManager $entityManager): void
73{
74 $page = $entityManager->find('App\Entity\Page', (int)$_POST['page_id']);
75 $nodes = $entityManager->getRepository('App\Entity\Node')->findBy(['page' => $page]);
76 $data = [];
77 foreach($nodes as $node){
78 $data[] = $entityManager->getRepository('App\Entity\NodeData')->findOneBy(['node' => $node]);
79 $entityManager->remove($node);
80 }
81 foreach($data as $one_data){
82 $entityManager->remove($one_data);
83 }
84 $entityManager->remove($page); // suppression en BDD
85
86 $entityManager->flush();
87 header("Location: " . new URL);
88 die;
89}
90
91function pageMenuPathUpdate(EntityManager $entityManager): void
92{
93 $director = new Director($entityManager, true);
94 $page = Director::$page_path->getLast();
95 $path = htmlspecialchars($_POST['page_menu_path']);
96
97 // mise en snake_case: filtre caractères non-alphanumériques, minuscule, doublons d'underscore, trim des underscores
98 $path = trim(preg_replace('/_+/', '_', strtolower(preg_replace('/[^a-zA-Z0-9]/', '_', $path))), '_');
99 $page->setEndOfPath($path);
100 foreach(Director::$menu_data->getChildren() as $child){
101 if($child->getEndOfPath() === Director::$page_path->getArray()[0]->getEndOfPath()){
102 $child->fillChildrenPagePath(); // MAJ de $page_path
103 }
104 }
105 $entityManager->flush();
106 header("Location: " . new URL(['page' => $page->getPagePath(), 'action' => 'modif_page']));
107 die;
108}
109
110function addBloc(EntityManager $entityManager): void
111{
112 $director = new Director($entityManager, true); // on a besoin de page_path qui dépend de menu_data
113 $page = Director::$page_path->getLast();
114 $director->findUniqueNodeByName('main');
115 $director->findItsChildren();
116 $main = $director->getNode();
117 $position = count($main->getChildren()) + 1; // position dans la fraterie
118
119 $blocks = ['blog', 'grid', 'calendar', 'galery', 'form']; // même liste dans FormBuilder.php
120 if(!in_array($_POST["bloc_select"], $blocks, true)) // 3è param: contrôle du type
121 {
122 header("Location: " . new URL(['page' => $_GET['page'], 'error' => 'bad_bloc_type']));
123 die;
124 }
125
126 if($_POST["bloc_select"] === 'calendar' || $_POST["bloc_select"] === 'form'){
127 $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page AND n.name_node = :name'; // noeud 'head' de la page
128 $bulk_data = $entityManager
129 ->createQuery($dql)
130 ->setParameter('page', $page)
131 ->setParameter('name', 'head')
132 ->getResult();
133
134 if(count($bulk_data) != 1){ // 1 head par page
135 header("Location: " . new URL(['page' => $_GET['page'], 'error' => 'head_node_not_found']));
136 die;
137 }
138
139 $bulk_data[0]->addAttribute('css_array', $_POST["bloc_select"]);
140 if($_POST["bloc_select"] === 'form'){
141 $bulk_data[0]->addAttribute('js_array', $_POST["bloc_select"]);
142 }
143 $entityManager->persist($bulk_data[0]);
144 }
145
146 $bloc = new Node(
147 $_POST["bloc_select"],
148 null, [],
149 $position,
150 $main,
151 $page);
152 $data = new NodeData(
153 ['title' => trim(htmlspecialchars($_POST["bloc_title"]))],
154 $bloc);
155
156 $entityManager->persist($bloc);
157 $entityManager->persist($data);
158 $entityManager->flush();
159 header("Location: " . new URL(['page' => $_GET['page'], 'action' => 'modif_page']));
160 die;
161}
162
163function deleteBloc(EntityManager $entityManager): void
164{
165 $director = new Director($entityManager, true);
166 $director->findUniqueNodeByName('main');
167 $director->findItsChildren();
168 //$director->findNodeById((int)$_POST['delete_bloc_id']);
169 $main = $director->getNode();
170 $bloc = null;
171 foreach($main->getChildren() as $child){
172 if($child->getId() === (int)$_POST['delete_bloc_id']){
173 $bloc = $child;
174 break;
175 }
176 }
177 if(!empty($bloc)){ // si $bloc est null c'est que le HTML a été modifié volontairement
178 $main->removeChild($bloc); // réindex le tableau $children au passage
179 $main->reindexPositions();
180 $entityManager->remove($bloc); // suppression en BDD
181 $entityManager->flush();
182 }
183 header("Location: " . new URL(['page' => $_GET['page'], 'action' => 'modif_page']));
184 die;
185}
186
187function newUrlMenuEntry(EntityManager $entityManager): void
188{
189 Director::$menu_data = new Menu($entityManager);
190 $previous_page = Director::$menu_data->findPageById((int)$_POST["location"]); // (int) à cause de declare(strict_types=1);
191 $parent = $previous_page->getParent();
192
193 $page = new Page(
194 trim(htmlspecialchars($_POST["label_input"])),
195 filter_var($_POST["url_input"], FILTER_VALIDATE_URL),
196 true, true, false,
197 $previous_page->getPosition(),
198 $parent); // peut et DOIT être null si on est au 1er niveau
199
200 // on a donné à la nouvelle entrée la même position qu'à la précédente,
201 // addChild l'ajoute à la fin du tableau "children" puis on trie
202 // 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
203 if($parent == null){
204 $parent = Director::$menu_data;
205 }
206 $parent->addChild($page); // true pour réindexer les positions en BDD
207 $parent->reindexPositions();
208
209 $entityManager->persist($page);
210 $entityManager->flush();
211 header("Location: " . new URL(['page' => $_GET['from']]));
212 die;
213}
214
215function deleteUrlMenuEntry(EntityManager $entityManager): void
216{
217 Director::$menu_data = new Menu($entityManager);
218 $page = Director::$menu_data->findPageById((int)$_POST["delete"]);
219 $parent = $page->getParent();
220 if($parent == null){
221 $parent = Director::$menu_data;
222 }
223
224 $parent->removeChild($page); // suppression de $children avant de trier
225 $parent->reindexPositions();
226
227 $entityManager->remove($page); // suppression en BDD
228 $entityManager->flush();
229 header("Location: " . new URL(['page' => $_GET['from']]));
230 die;
231} \ No newline at end of file
diff --git a/src/controller/post_router.php b/src/controller/post_router.php
deleted file mode 100644
index e71852a..0000000
--- a/src/controller/post_router.php
+++ /dev/null
@@ -1,120 +0,0 @@
1<?php
2// src/controller/post_router.php
3//
4// routage des requêtes des formulaires et AJAX
5// n'utilisent que des POST à l'exception d'un GET par fullcalendar
6// les contrôleurs des formulaires sont appelés ici,
7// ceux des requêtes AJAX sont derrière d'autres routeurs
8
9declare(strict_types=1);
10
11
12/* appel des contrôleurs dans password.php */
13if(isset($_GET['action']) && $_GET['action'] === 'deconnexion')
14{
15 disconnect($entityManager);
16}
17elseif(isset($_GET['action']) && $_GET['action'] === 'modif_mdp')
18{
19 changePassword($entityManager);
20}
21
22
23if($_SERVER['REQUEST_METHOD'] === 'POST'){
24 /* -- contrôleurs appellables par tout le monde -- */
25 // POST "ajax"
26 require '../src/controller/ajax_email.php';
27
28 // POST "form"
29 // ...
30
31
32 if($_SESSION['admin'] === true)
33 {
34 /* -- requêtes "form" -- */
35 if($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded') // moyen approximatif de distinguer les requêtes de formulaires et AJAX
36 {
37 require '../src/controller/post_functions_admin.php';
38
39 /* -- nouvelle page -- */
40 if(isset($_POST['page_name']) && $_POST['page_name'] !== null
41 && isset($_POST['page_name_path']) && $_POST['page_name_path'] !== null
42 && isset($_POST['page_location']) && $_POST['page_location'] !== null
43 && isset($_POST['page_description']) && $_POST['page_description'] !== null
44 && isset($_POST['new_page_hidden']) && $_POST['new_page_hidden'] === '')
45 {
46 newPage($entityManager);
47 }
48
49 /* -- suppression d'une page -- */
50 elseif(isset($_POST['page_id']) && $_POST['page_id'] !== null
51 && isset($_POST['submit_hidden']) && $_POST['submit_hidden'] === '')
52 {
53 deletePage($entityManager);
54 }
55
56
57 /* -- mode Modification d'une page -- */
58
59 // modification du chemins en snake_case
60 elseif(isset($_POST['page_menu_path']) && $_POST['page_menu_path'] !== null
61 && isset($_POST['page_id']) && $_POST['page_id'] !== null
62 && isset($_POST['page_name_path_hidden']) && $_POST['page_name_path_hidden'] === '')
63 {
64 pageMenuPathUpdate($entityManager);
65 }
66 // ajout d'un bloc dans une page
67 elseif(isset($_POST['bloc_title']) && $_POST['bloc_title'] !== null
68 && isset($_POST['bloc_select']) && $_POST['bloc_select'] !== null
69 && isset($_POST['bloc_title_hidden']) && $_POST['bloc_title_hidden'] === '') // contrôle anti-robot avec input hidden
70 {
71 addBloc($entityManager);
72 }
73 // suppression d'un bloc de page
74 elseif(isset($_POST['delete_bloc_id']) && $_POST['delete_bloc_id'] !== null
75 && isset($_POST['delete_bloc_hidden']) && $_POST['delete_bloc_hidden'] === '') // contrôle anti-robot avec input hidden
76 {
77 deleteBloc($entityManager);
78 }
79
80
81 /* -- page Menu et chemins -- */
82
83 // création d'une entrée de menu avec une URL
84 elseif(isset($_POST["label_input"]) && isset($_POST["url_input"]) && isset($_POST["location"])){
85 newUrlMenuEntry($entityManager);
86 }
87 // suppression d'une entrée de menu avec une URL
88 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
89 deleteUrlMenuEntry($entityManager);
90 }
91
92 // modification du mot de passe
93 elseif(isset($_GET['action']) && $_GET['action'] === 'modif_mdp'
94 && isset($_POST['login']) && isset($_POST['old_password']) && isset($_POST['new_password'])
95 && isset($_POST['modify_password_hidden']) && empty($_POST['modify_password_hidden']))
96 {
97 changePassword($entityManager);
98 }
99 else{
100 header("Location: " . new URL(['error' => 'paramètres inconnus']));
101 die;
102 }
103 }
104
105 /* -- requêtes AJAX -- */
106 else{
107 require '../src/controller/ajax_admin.php';
108 require '../src/controller/ajax_calendar_admin.php';
109 }
110 }
111}
112// cas particulier d'un GET ajax non-admin par fullcalendar
113elseif($_SERVER['REQUEST_METHOD'] === 'GET'){
114 // non-admin
115 require '../src/controller/ajax_calendar_visitor.php';
116
117 if($_SESSION['admin'] === true){
118 // ...
119 }
120} \ No newline at end of file
diff --git a/src/controller/request_router.php b/src/controller/request_router.php
new file mode 100644
index 0000000..157bc80
--- /dev/null
+++ b/src/controller/request_router.php
@@ -0,0 +1,256 @@
1<?php
2// src/controller/request_router.php
3//
4// routage des requêtes des formulaires et AJAX
5// n'utilisent que des POST à l'exception d'un GET par fullcalendar
6// les contrôleurs des formulaires sont appelés ici,
7// ceux des requêtes AJAX sont derrière d'autres routeurs
8
9declare(strict_types=1);
10
11
12/* appel des contrôleurs dans password.php */
13if(isset($_GET['action']) && $_GET['action'] === 'deconnexion')
14{
15 disconnect($entityManager);
16}
17elseif(isset($_GET['action']) && $_GET['action'] === 'modif_mdp')
18{
19 changePassword($entityManager);
20}
21
22
23// presque tout est ici
24if($_SERVER['REQUEST_METHOD'] === 'POST'){
25 /* -- contrôleurs appellables par tout le monde -- */
26
27 // POST "ajax" avec fetch (application/json)
28 if($_SERVER['CONTENT_TYPE'] === 'application/json')
29 {
30 $data = file_get_contents('php://input');
31 $json = json_decode($data, true);
32
33 if(isset($_GET['action']))
34 {
35 // formulaire de contact
36 if($_GET['action'] === 'send_email'){
37 EmailController::submit($json, $entityManager);
38 }
39 }
40 }
41
42 // POST "form"
43 // ...
44
45
46 if($_SESSION['admin'] === true)
47 {
48 /* -- requêtes AJAX -- */
49 // requêtes JSON avec fetch()
50 if($_SERVER['CONTENT_TYPE'] === 'application/json')
51 {
52 $data = file_get_contents('php://input');
53 $json = json_decode($data, true);
54
55 if(isset($_GET['action']))
56 {
57 /* -- manipulation des articles -- */
58 if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content']))
59 {
60 ArticleController::editorSubmit($entityManager, $json);
61 }
62 elseif($_GET['action'] === 'delete_article' && isset($json['id']))
63 {
64 ArticleController::deleteArticle($entityManager, $json);
65 }
66 // inversion de la position de deux noeuds
67 elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2']))
68 {
69 ArticleController::switchPositions($entityManager, $json);
70 }
71 elseif($_GET['action'] === 'date_submit' && isset($json['id']) && isset($json['date']))
72 {
73 ArticleController::dateSubmit($entityManager, $json);
74 }
75
76 /* -- bloc Formulaire -- */
77 elseif($_GET['action'] === 'recipient_email'){
78 ContactFormController::updateRecipient($entityManager, $json);
79 }
80 elseif($_GET['action'] === 'test_email'){
81 ContactFormController::sendTestEmail($entityManager, $json);
82 }
83 }
84
85 /* -- page Menu et chemins -- */
86 elseif(isset($_GET['menu_edit']))
87 {
88 // récupération des données (serait peut-être mieux dans la classe)
89 Director::$menu_data = new Menu($entityManager);
90
91 // flèche gauche <=: position = position du parent + 1, parent = grand-parent, recalculer les positions
92 if($_GET['menu_edit'] === 'move_one_level_up' && isset($json['id'])){
93 MenuAndPathsController::MoveOneLevelUp($entityManager, $json);
94 }
95
96 // flèche droite =>: position = nombre d'éléments de la fraterie + 1, l'élément précédent devient le parent
97 if($_GET['menu_edit'] === 'move_one_level_down' && isset($json['id'])){
98 MenuAndPathsController::MoveOneLevelDown($entityManager, $json);
99 }
100
101 if($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){
102 MenuAndPathsController::switchPositions($entityManager, $json);
103 }
104
105 if($_GET['menu_edit'] === 'displayInMenu' && isset($json['id']) && isset($json['checked'])){
106 MenuAndPathsController::displayInMenu($entityManager, $json);
107 }
108 }
109
110 /* -- mode Modification d'une page -- */
111 // partie "page"
112 elseif(isset($_GET['page_edit']))
113 {
114 // titre de la page
115 if($_GET['page_edit'] === 'page_title'){
116 PageManagementController::setPageTitle($entityManager, $json);
117 }
118 // description dans les métadonnées
119 elseif($_GET['page_edit'] === 'page_description'){
120 PageManagementController::setPageDescription($entityManager, $json);
121 }
122 }
123
124 // partie "blocs"
125 elseif(isset($_GET['bloc_edit']))
126 {
127 // renommage d'un bloc
128 if($_GET['bloc_edit'] === 'rename_page_bloc')
129 {
130 PageManagementController::renameBloc($entityManager, $json);
131 }
132 // inversion des positions de deux blocs
133 elseif($_GET['bloc_edit'] === 'switch_blocs_positions')
134 {
135 PageManagementController::SwitchBlocsPositions($entityManager, $json);
136 }
137 }
138
139 /* -- upload d'image dans tinymce par copier-coller -- */
140 // collage de HTML contenant une ou plusieurs balises <img>
141 if(isset($_GET['action']) && $_GET['action'] == 'upload_image_html'){
142 ImageUploadController::uploadImageHtml();
143 }
144 // collage d'une image (code base64 dans le presse-papier) non encapsulée dans du HTML
145 elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_base64'){
146 ImageUploadController::uploadImageBase64();
147 }
148
149 /* -- requêtes spécifiques au calendrier -- */
150 if($_GET['action'] === 'new_event'){
151 CalendarController::newEvent($json, $entityManager);
152 }
153 elseif($_GET['action'] === 'update_event'){
154 CalendarController::updateEvent($json, $entityManager);
155 }
156 elseif($_GET['action'] === 'remove_event'){
157 CalendarController::removeEvent($json, $entityManager);
158 }
159 else{
160 echo json_encode(['success' => false]);
161 }
162 die;
163 }
164
165 // upload d'image dans tinymce avec le plugin (bouton "insérer une image" de l'éditeur)
166 elseif(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false && isset($_GET['action']) && $_GET['action'] === 'upload_image')
167 {
168 ImageUploadController::imageUploadTinyMce();
169 }
170 // requêtes XMLHttpRequest
171 elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
172 {
173 //echo "requête XMLHttpRequest reçue par le serveur";
174 echo json_encode(['success' => false]); // ça marche mais ça marche pas...
175 die;
176 }
177
178 /* -- envoi d'un formulaire HTML -- */
179 elseif($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded')
180 {
181 /* -- nouvelle page -- */
182 if(isset($_POST['page_name']) && $_POST['page_name'] !== null
183 && isset($_POST['page_name_path']) && $_POST['page_name_path'] !== null
184 && isset($_POST['page_location']) && $_POST['page_location'] !== null
185 && isset($_POST['page_description']) && $_POST['page_description'] !== null
186 && isset($_POST['new_page_hidden']) && $_POST['new_page_hidden'] === '')
187 {
188 PageManagementController::newPage($entityManager);
189 }
190
191 /* -- suppression d'une page -- */
192 elseif(isset($_POST['page_id']) && $_POST['page_id'] !== null
193 && isset($_POST['submit_hidden']) && $_POST['submit_hidden'] === '')
194 {
195 PageManagementController::deletePage($entityManager);
196 }
197
198 /* -- mode Modification d'une page -- */
199
200 // modification du chemins en snake_case
201 elseif(isset($_POST['page_menu_path']) && $_POST['page_menu_path'] !== null
202 && isset($_POST['page_id']) && $_POST['page_id'] !== null
203 && isset($_POST['page_name_path_hidden']) && $_POST['page_name_path_hidden'] === '')
204 {
205 PageManagementController::updatePageMenuPath($entityManager);
206 }
207 // ajout d'un bloc dans une page
208 elseif(isset($_POST['bloc_title']) && $_POST['bloc_title'] !== null
209 && isset($_POST['bloc_select']) && $_POST['bloc_select'] !== null
210 && isset($_POST['bloc_title_hidden']) && $_POST['bloc_title_hidden'] === '') // contrôle anti-robot avec input hidden
211 {
212 PageManagementController::addBloc($entityManager);
213 }
214 // suppression d'un bloc de page
215 elseif(isset($_POST['delete_bloc_id']) && $_POST['delete_bloc_id'] !== null
216 && isset($_POST['delete_bloc_hidden']) && $_POST['delete_bloc_hidden'] === '') // contrôle anti-robot avec input hidden
217 {
218 PageManagementController::deleteBloc($entityManager);
219 }
220
221
222 /* -- page Menu et chemins -- */
223
224 // création d'une entrée de menu avec une URL
225 elseif(isset($_POST["label_input"]) && isset($_POST["url_input"]) && isset($_POST["location"])){
226 MenuAndPathsController::newUrlMenuEntry($entityManager);
227 }
228 // suppression d'une entrée de menu avec une URL
229 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
230 MenuAndPathsController::deleteUrlMenuEntry($entityManager);
231 }
232
233 // redirection page d'accueil
234 else{
235 header("Location: " . new URL(['error' => 'paramètres inconnus']));
236 die;
237 }
238 }
239 }
240}
241
242// cas particulier d'un GET ajax non-admin par fullcalendar
243elseif($_SERVER['REQUEST_METHOD'] === 'GET'){
244 /* -- non-admin -- */
245 // chargement des évènements à la création du calendrier
246 // et au changement de dates affichées (boutons flèches mais pas changement de vue)
247 if($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'get_events'
248 && isset($_GET['start']) && isset($_GET['end']) && empty($_POST))
249 {
250 CalendarController::getData($entityManager);
251 }
252
253 if($_SESSION['admin'] === true){
254 // ...
255 }
256} \ No newline at end of file
diff --git a/src/model/EventDTO.php b/src/model/EventDTO.php
index 70013dd..6dd3722 100644
--- a/src/model/EventDTO.php
+++ b/src/model/EventDTO.php
@@ -2,7 +2,7 @@
2// src/model/EventDTO.php 2// src/model/EventDTO.php
3// 3//
4// classe de données JSONifiable compatible avec fullcalendar 4// classe de données JSONifiable compatible avec fullcalendar
5// servira aussi pour l'import/export de fichiers .ics 5// servira éventuellement pour l'import/export de fichiers .ics
6 6
7declare(strict_types=1); 7declare(strict_types=1);
8 8