From 2d7bacce891eab0adb0263d598bfe44418788f42 Mon Sep 17 00:00:00 2001 From: polo Date: Fri, 4 Jul 2025 00:43:35 +0200 Subject: formulaire de contact 3 --- composer.json | 3 +- public/css/form.css | 8 +- public/js/form.js | 87 +++++- src/Config.php | 14 +- src/controller/ajax.php | 640 ++++-------------------------------------- src/controller/ajax_admin.php | 626 +++++++++++++++++++++++++++++++++++++++++ src/controller/post.php | 463 +++++++++++++++--------------- src/model/entities/Email.php | 41 +++ src/model/entities/Log.php | 2 - src/view/FormBuilder.php | 37 ++- src/view/templates/form.php | 31 +- 11 files changed, 1101 insertions(+), 851 deletions(-) create mode 100644 src/controller/ajax_admin.php create mode 100644 src/model/entities/Email.php diff --git a/composer.json b/composer.json index c4e089b..12c7202 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ "composer": "*", "htmlawed/htmlawed": "^1.2", "tinymce/tinymce": "^7.7", - "tweeb/tinymce-i18n": "^2.0" + "tweeb/tinymce-i18n": "^2.0", + "phpmailer/phpmailer": "^6.10" }, "scripts": { "post-install-cmd": [ diff --git a/public/css/form.css b/public/css/form.css index 473252c..d317360 100644 --- a/public/css/form.css +++ b/public/css/form.css @@ -1,4 +1,4 @@ -.form form{ +.form_inputs{ background-color: white; margin: auto; padding: 10px; @@ -14,7 +14,7 @@ .form .full_width_column{ grid-column: 1 / span 2; } -.form form input[type=submit] +.form_inputs input[type=submit] { color: #ff1d04; font-size: medium; @@ -22,7 +22,7 @@ background-color: white; border: lightgrey 2px outset; } -.form form input[type=submit]:hover +.form_inputs input[type=submit]:hover { background-color: #ffff00; border-radius: 4px; @@ -42,7 +42,7 @@ } @media screen and (max-width: 600px){ - .form form{ + .form_inputs{ grid-template-columns: 1fr; } .form label{ diff --git a/public/js/form.js b/public/js/form.js index 1752f55..0be2313 100644 --- a/public/js/form.js +++ b/public/js/form.js @@ -1,7 +1,9 @@ //function sendMessage(){} -function changeRecipient(id){ +// modif des paramètre d'envoi d'e-mail depuis l'espace admin +/*function changeRecipient(id){ const email = document.getElementById('recipient').value; + const hidden = document.getElementById('recipient_hidden').value; const warning = document.querySelector('.no_recipient_warning'); fetch('index.php?action=recipient_email', { @@ -9,7 +11,7 @@ function changeRecipient(id){ headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ id: id, email: email }) + body: JSON.stringify({ id: id, email: email, hidden: hidden }) }) .then(response => response.json()) .then(data => { @@ -18,10 +20,89 @@ function changeRecipient(id){ toastNotify('Adresse e-mail de destination modifiée'); } else{ - console.error('Erreur: echec de la modification de l\'adresse e-mail de destination'); + toastNotify('E-mail non valide'); } }) .catch(error => { console.error('Erreur:', error); }); +}*/ + +function sendTestEmail(){ + const admin_form = document.querySelector('.admin_form'); + const test_email_success = document.querySelector('.test_email_success'); + test_email_success.innerHTML = 'Envoi en cours, veuillez patienter'; + test_email_success.style.backgroundColor = '#f0f0f0'; + + fetch('index.php?action=test_email', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({}) + }) + .then(response => response.json()) + .then(data => { + let message; + let color; + if(data.success){ + message = 'E-mail de test envoyé avec succès'; + color = 'lawngreen'; + } + else{ + message = "Erreur à l'envoi de l'e-mail"; + color = "orangered" + } + test_email_success.innerHTML = message; + toastNotify(message); + test_email_success.style.backgroundColor = color; + }) + .catch(error => { + console.error('Erreur:', error); + }); +} + +function sendVisitorEmail(){ + const send_email_success = document.querySelector('.send_email_success'); + send_email_success.innerHTML = 'Envoi en cours, veuillez patienter'; + send_email_success.style.backgroundColor = 'initial'; + + const email_name = document.getElementById('email_name').value; + const email_address = document.getElementById('email_address').value; + const email_message = document.getElementById('email_message').value; + const email_captcha = document.getElementById('email_captcha').value; + const email_hidden = document.getElementById('email_hidden').value; + + fetch('index.php?action=send_email', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + name: email_name, + email: email_address, + message: email_message, + captcha: email_captcha, + hidden: email_hidden + }) + }) + .then(response => response.json()) + .then(data => { + let message; + let color; + if(data.success){ + message = 'Votre E-mail a été envoyé!'; + color = 'lawngreen'; + } + else{ + message = "Votre message n'a pas pu être envoyé, votre e-mail ou le captcha ne sont peut-être pas corrects"; + color = "orangered" + } + send_email_success.innerHTML = message; + toastNotify(message); + send_email_success.style.backgroundColor = color; + }) + .catch(error => { + console.error('Erreur:', error); + }); } \ No newline at end of file diff --git a/src/Config.php b/src/Config.php index 500d6ea..caa91d0 100644 --- a/src/Config.php +++ b/src/Config.php @@ -7,9 +7,9 @@ class Config { // BDD static public string $db_host = 'localhost'; - static public string $database = 'nageurs'; + static public string $database = ''; static public string $db_driver = 'pdo_mysql'; - static public string $user = 'root'; + static public string $user = ''; static public string $password = ''; static public string $table_prefix = ''; @@ -18,6 +18,16 @@ class Config static public string $index_path = ''; static public string $port = '80'; + // e-mails + static public string $smtp_host = ''; + static public string $smtp_username = ''; + static public string $smtp_password = ''; + static public string $smtp_secure = 'tls'; // tls (smarttls) ou ssl (smtps) + static public string $email_from = ''; + static public string $email_from_name = 'site web'; + static public string $email_dest = ''; + static public string $email_dest_name = 'destinataire formulaire'; + // copier dans ce tableau les variables contenant des chemins static private array $path_vars = []; diff --git a/src/controller/ajax.php b/src/controller/ajax.php index ae43b75..7529fe6 100644 --- a/src/controller/ajax.php +++ b/src/controller/ajax.php @@ -3,511 +3,89 @@ declare(strict_types=1); -use App\Entity\Page; -use App\Entity\Node; -use App\Entity\Article; +use PHPMailer\PHPMailer\PHPMailer; +use PHPMailer\PHPMailer\Exception; +use App\Entity\Email; - -// mettre ça ailleurs -function imagickCleanImage(string $image_data, string $local_path, string $format = 'jpeg'): bool // "string" parce que file_get_contents... -{ - try{ - $imagick = new Imagick(); - $imagick->readImageBlob($image_data); - $imagick->stripImage(); // nettoyage métadonnées - $imagick->setImageFormat($format); - if($format === 'jpeg'){ - $imagick->setImageCompression(Imagick::COMPRESSION_JPEG); - $imagick->setImageCompressionQuality(85); // optionnel - } - $imagick->writeImage($local_path); // enregistrement - $imagick->clear(); - $imagick->destroy(); - return true; - } - catch(Exception $e){ - return false; - } -} -function curlDownloadImage(string $url, $maxRetries = 3, $timeout = 10): string|false -{ - $attempt = 0; - $imageData = false; - - while($attempt < $maxRetries){ - $ch = curl_init($url); // instance de CurlHandle - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); - curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - curl_setopt($ch, CURLOPT_USERAGENT, 'TinyMCE-Image-Downloader'); - - $imageData = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - //$curlError = curl_error($ch); - - curl_close($ch); - - if($imageData !== false && $httpCode >= 200 && $httpCode < 300){ - return $imageData; - } - - $attempt++; - sleep(1); - } - - return false; // échec après trois tentatives -} - - -// détection des requêtes d'upload d'image de tinymce -if(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false && isset($_GET['action']) && $_GET['action'] === 'upload_image') +// mettre ça ailleurs? +function sendEmail(bool $true_email, string $name = '', string $email = '', string $message = ''): bool { - if(isset($_FILES['file'])){ - $file = $_FILES['file']; - $dest = 'images/'; - $dest_mini = 'images-mini/'; - - // Vérifier si les répertoires existent, sinon les créer - if(!is_dir($dest)) { - mkdir($dest, 0700, true); - } - if(!is_dir($dest_mini)) { - mkdir($dest_mini, 0700, true); - } - - $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; - $name = Security::secureFileName(pathinfo($file['name'], PATHINFO_FILENAME)); - $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); - if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ - $extension = 'jpeg'; - } - $file_path = $dest . $name . '_' . uniqid() . '.' . $extension; + $mail = new PHPMailer(true); // true => exceptions + $mail->CharSet = 'UTF-8'; - // créer une miniature de l'image - // - - if(imagickCleanImage(file_get_contents($file['tmp_name']), $file_path, $extension)){ // recréer l’image pour la nettoyer - echo json_encode(['location' => $file_path]); // renvoyer l'URL de l'image téléchargée - } - else{ - http_response_code(500); - echo json_encode(['message' => 'Erreur image non valide']); - } - } - else{ - http_response_code(400); - echo json_encode(['message' => 'Erreur 400: Bad Request']); - } - die; -} -// 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 -elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url'){ - $json = json_decode(file_get_contents('php://input'), true); - - if(isset($json['image_url'])){ - $image_data = curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents - $dest = 'images/'; - - if(!is_dir($dest)) { // Vérifier si le répertoire existe, sinon le créer - mkdir($dest, 0777, true); - } - - if($image_data === false){ - http_response_code(400); - echo json_encode(['message' => "Erreur, le serveur n'a pas réussi à télécharger l'image."]); - die; - } - - $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; - $url_path = parse_url($json['image_url'], PHP_URL_PATH); - $name = Security::secureFileName(pathinfo($url_path, PATHINFO_FILENAME)); - $extension = strtolower(pathinfo($url_path, PATHINFO_EXTENSION)); - if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ - $extension = 'jpeg'; - } - $local_path = $dest . $name . '_' . uniqid() . '.' . $extension; + try{ + // Paramètres du serveur + $mail->isSMTP(); + $mail->Host = Config::$smtp_host; + $mail->SMTPAuth = true; + $mail->Port = 25; - if(imagickCleanImage($image_data, $local_path, $extension)){ // recréer l’image pour la nettoyer - echo json_encode(['location' => $local_path]); // nouvelle adresse + if($mail->SMTPAuth){ + $mail->Username = Config::$smtp_username; // e-mail + $mail->Password = Config::$smtp_password; + $mail->SMTPSecure = Config::$smtp_secure; // tls (starttls) ou ssl (smtps) + if($mail->SMTPSecure === 'tls'){ + $mail->Port = 587; + } + elseif($mail->SMTPSecure === 'ssl'){ + $mail->Port = 465; + } } - else{ - http_response_code(500); - echo json_encode(['message' => 'Erreur image non valide']); - } - } - else{ - echo json_encode(['message' => 'Erreur 400: Bad Request']); - } - die; -} -// cas du collage d'une image (code base64) non encapsulée dans du HTML -elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_base64'){ - $json = json_decode(file_get_contents('php://input'), true); - $dest = 'images/'; + //var_dump($mail->smtpConnect());die; // test de connexion - if(!is_dir('images')){ - mkdir('images', 0777, true); - } - - // détection de data:image/ et de ;base64, et capture du format dans $type - if(!isset($json['image_base64']) || !preg_match('/^data:image\/(\w+);base64,/', $json['image_base64'], $type)){ - http_response_code(400); - echo json_encode(['message' => 'Données image base64 manquantes ou invalides']); - die; - } - - $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; - $extension = strtolower($type[1]); - if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ - $extension = 'jpeg'; - } + // Expéditeur et destinataire + $mail->setFrom(strtolower(Config::$email_from), Config::$email_from_name); // expéditeur + $mail->addAddress(strtolower(Config::$email_dest), Config::$email_dest_name); // destinataire - $image_data = base64_decode(substr($json['image_base64'], strpos($json['image_base64'], ',') + 1)); // découpe la chaine à la virgule puis convertit en binaire - if($image_data === false){ - http_response_code(400); - echo json_encode(['message' => 'Décodage base64 invalide']); - die; - } - - $local_path = $dest . 'pasted_image_' . uniqid() . '.' . $extension; + // Contenu + $mail->isHTML(true); + if($true_email){ + $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web'; + + } + else{ + $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web"; + } + $mail->Body = $message; + $mail->AltBody = $message; - if(imagickCleanImage($image_data, $local_path)){ - echo json_encode(['location' => $local_path]); + $mail->send(); + return true; } - else{ - http_response_code(500); - echo json_encode(['message' => 'Erreur image non valide']); + catch(Exception $e){ + return false; + //echo "Le message n'a pas pu être envoyé. Erreur : {$mail->ErrorInfo}"; } - die; } -// détection des requêtes de type XHR, y en a pas à priori -/*elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){ - echo "requête XHR reçue par le serveur"; - die; -}*/ - // détection des requêtes envoyées avec fetch (application/json) et récupération du JSON -if($_SERVER['CONTENT_TYPE'] === 'application/json'){ +if($_SERVER['CONTENT_TYPE'] === 'application/json') +{ $data = file_get_contents('php://input'); $json = json_decode($data, true); // requêtes de tinymce ou touchant aux articles if(isset($_GET['action'])) { - if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content'])) - { - if(json_last_error() === JSON_ERROR_NONE) - { - $id = $json['id']; - $director = new Director($entityManager); - - // cas d'une nouvelle "news" - if(is_array($json['content'])){ - foreach($json['content'] as $one_input){ - $one_input = Security::secureString($one_input); - } - $content = $json['content']; - } - else{ - $content = Security::secureString($json['content']); - } - - // nouvel article - if($id[0] === 'n') - { - $section_id = (int)substr($id, 1); // id du bloc
- $director->findNodeById($section_id); - $director->makeSectionNode(); - $node = $director->getNode(); // =
- - if(is_array($content)){ - $date = new \DateTime($content['d']); - $article = new Article($content['i'], $date, $content['t'], $content['p']); - $article_node = new Node('new', 'i' . (string)$date->getTimestamp(), [], count($node->getChildren()) + 1, $node, $node->getPage(), $article); - - // id_node tout juste généré - //$article_node->getId(); - } - else{ - $timestamp = time(); - $date = new \DateTime; - $date->setTimestamp($timestamp); - - $article = new Article($content, $date); // le "current" timestamp est obtenu par la BDD - $article_node = new Node('article', 'i' . (string)$timestamp, [], count($node->getChildren()) + 1, $node, $node->getPage(), $article); - } - - $entityManager->persist($article_node); - $entityManager->flush(); - - echo json_encode(['success' => true, 'article_id' => $article_node->getArticleTimestamp()]); - die; - } - // modification article - else{ - $id[0] = 'i'; // id de l'article node - } - - if($director->makeArticleNode($id)) // une entrée est trouvée - { - $node = $director->getArticleNode(); // article - switch($json['id'][0]){ - case 'i': - $node->getArticle()->setContent($content); - break; - case 'p': - $node->getArticle()->setPreview($content); // html de l'éditeur - break; - case 't': - $node->getArticle()->setTitle($content); // html de l'éditeur - break; - case 'd': - echo json_encode(['success' => false, 'message' => 'l\'action editor_submit ne supporte pas les dates, utiliser date_submit.']); - die; - default: - echo json_encode(['success' => false, 'message' => 'identifiant non utilisable']); - die; - } - $entityManager->flush(); - echo json_encode(['success' => true]); - } - else - { - echo json_encode(['success' => false, 'message' => 'article non identifié']); - } - } - else{ - echo json_encode(['success' => false, 'message' => 'Erreur de décodage JSON']); - } - die; - } - elseif($_GET['action'] === 'delete_article' && isset($json['id'])) - { - $director = new Director($entityManager); - $director->makeArticleNode($json['id'], true); - $article = $director->getArticleNode(); - $section = $director->getNode(); - - $entityManager->remove($article); - $section->removeChild($article); - $section->sortChildren(true); // régénère les positions - $entityManager->flush(); - - // test avec une nouvelle requête qui ne devrait rien trouver - if(!$director->makeArticleNode($json['id'])) - { - echo json_encode(['success' => true]); - - // on pourrait afficher une notification "toast" - } - else{ - http_response_code(500); - echo json_encode(['success' => false, 'message' => 'Erreur lors de la suppression de l\'article.']); - } - die; - } - // inversion de la position de deux noeuds - elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])) - { - $director = new Director($entityManager); - $director->makeArticleNode($json['id1'], true); - $article1 = $director->getArticleNode(); - $section = $director->getNode(); - - $section->sortChildren(true); // régénère les positions avant inversion - - $article2; - foreach($section->getChildren() as $child){ - if($child->getArticleTimestamp() === $json['id2']) // type string - { - $article2 = $child; - break; - } - } - - // inversion - $tmp = $article1->getPosition(); - $article1->setPosition($article2->getPosition()); - $article2->setPosition($tmp); - $entityManager->flush(); - - echo json_encode(['success' => true]); - die; - } - elseif($_GET['action'] === 'date_submit' && isset($json['id']) && isset($json['date'])) - { - $id = $json['id']; - $id[0] = 'i'; - $date = new DateTime($json['date']); - - $director = new Director($entityManager); - $director->makeArticleNode($id); - $node = $director->getArticleNode(); - $node->getArticle()->setDateTime($date); - $entityManager->flush(); + // e-mail envoyé par le formulaire de contact + if($_GET['action'] === 'send_email'){ + $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; + $captcha_try = isset($json['captcha']) ? Captcha::controlInput($json['captcha']) : 0; + + // contrôles des entrées + $name = htmlspecialchars(trim($json['name'])); + $email = strtolower(htmlspecialchars(trim($json['email']))); + $message = htmlspecialchars(trim($json['message'])); - echo json_encode(['success' => true]); - die; - } - - // config formulaire - elseif($_GET['action'] === 'recipient_email'){ - $form_data = $entityManager->find('App\Entity\NodeData', $json['id']); - $email = htmlspecialchars(trim($json['email'])); - - if(filter_var($email, FILTER_VALIDATE_EMAIL)){ - $form_data->updateData('email', $json['email']); - $entityManager->persist($form_data); - $entityManager->flush(); - echo json_encode(['success' => true]); - die; - } - else{ - echo json_encode(['success' => false]); - die; - } - } - } - - - /* -- page Menu et chemins -- */ - elseif(isset($_GET['menu_edit'])) - { - // récupération des données - $data = file_get_contents('php://input'); - $json = json_decode($data, true); - Director::$menu_data = new Menu($entityManager); - - // flèche gauche <=: position = position du parent + 1, parent = grand-parent, recalculer les positions - if($_GET['menu_edit'] === 'move_one_level_up' && isset($json['id'])){ - $id = $json['id']; - $page = Director::$menu_data->findPageById((int)$id); - - $parent = $page->getParent(); // peut être null - if($parent === null){ - // 1er niveau: ne rien faire - echo json_encode(['success' => false]); - die; - } - // BDD - else{ - $page->setPosition($parent->getPosition() + 1); // nouvelle position - - // 2ème niveau: le parent devient $menu_data, puis null après tri - if($parent->getParent() === null){ - // connexion dans les deux sens - $page->setParent(Director::$menu_data); // => pour la persistance - - //Director::$menu_data->addChild($page); // => pour sortChildren - $page->getParent()->addChild($page); // => pour sortChildren - //Director::$menu_data->sortChildren(true); // positions décaléees des nouveaux petits frères - $page->getParent()->sortChildren(true); // positions décaléees des nouveaux petits frères - $page->setParent(null); - - // affichage - $page->setPagePath($page->getEndOfPath()); - $page->fillChildrenPagePath(); - } - // 3ème niveau et plus - else{ - $page->setParent($parent->getParent()); // nouveau parent - $page->getParent()->addChild($page); // => pour sortChildren - $page->getParent()->sortChildren(true); // positions décaléees des nouveaux petits frères - $page->fillChildrenPagePath($page->getParent()->getPagePath()); - } - //$parent->sortChildren(true); // positions des enfants restants, inutile si la fonction est récursive? - $entityManager->flush(); - - // affichage - $parent->removeChild($page); - $nav_builder = new NavBuilder(); - $menu_builder = new MenuBuilder(null, false); - echo json_encode(['success' => true, 'nav' => $nav_builder->render(), 'menu_buttons' => $menu_builder->render()]); - die; - } - } - - // flèche droite =>: position = nombre d'éléments de la fraterie + 1, l'élément précédent devient le parent - if($_GET['menu_edit'] === 'move_one_level_down' && isset($json['id'])){ - $id = $json['id']; - $page = Director::$menu_data->findPageById((int)$id); - - $parent = $page->getParent(); // peut être null - if($parent == null){ - $parent = Director::$menu_data; - } - - // BDD - $parent->sortChildren(true); // trie et réindexe par sécurité: 1, 2, 3... - if($page->getPosition() > 1){ - foreach($parent->getChildren() as $child){ - if($child->getPosition() === $page->getPosition() - 1){ - $page->setParent($child); - break; - } - } - $page->setPosition(count($page->getParent()->getChildren()) + 1); - } - $entityManager->flush(); - - // affichage - $parent->removeChild($page); - $page->getParent()->addChild($page); - $page->fillChildrenPagePath($page->getParent()->getPagePath()); // variable non mappée $page_path - $nav_builder = new NavBuilder(); - $menu_builder = new MenuBuilder(null, false); - - echo json_encode(['success' => true, 'nav' => $nav_builder->render(), 'menu_buttons' => $menu_builder->render()]); - die; - } - - if($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])) - { - $id1 = $json['id1']; - $id2 = $json['id2']; - - // vérifier qu'ils ont le même parent - $page1 = Director::$menu_data->findPageById((int)$id1); - $page2 = Director::$menu_data->findPageById((int)$id2); - - // double le contrôle fait en JS - if($page1->getParent() === $page2->getParent()) // comparaison stricte d'objet (même instance du parent?) - { - // inversion - $tmp = $page1->getPosition(); - $page1->setPosition($page2->getPosition()); - $page2->setPosition($tmp); - Director::$menu_data->sortChildren(true); // modifie tableau children + if($captcha_try != 0 && $captcha_solution != 0 && ($captcha_try === $captcha_solution) + && filter_var($email, FILTER_VALIDATE_EMAIL) && isset($json['hidden']) && empty($json['hidden']) + && sendEmail(true, $name, $email, $message)) + { + $db_email = new Email(strtolower(Config::$email_from), strtolower(Config::$email_dest), $message); + $entityManager->persist($db_email); $entityManager->flush(); - - // nouveau menu - $nav_builder = new NavBuilder(); - echo json_encode(['success' => true, 'nav' => $nav_builder->render()]); - } - else{ - echo json_encode(['success' => false]); - } - - die; - } - - if($_GET['menu_edit'] === 'displayInMenu' && isset($json['id']) && isset($json['checked'])) - { - $id = $json['id']; - $checked = $json['checked']; - - $page = Director::$menu_data->findPageById((int)$id); - if($page->isHidden() === $checked){ - $page->setHidden(!$checked); - $entityManager->flush(); - - // nouveau menu - $nav_builder = new NavBuilder(); - echo json_encode(['success' => true, 'nav' => $nav_builder->render()]); + echo json_encode(['success' => true]); } else{ echo json_encode(['success' => false]); @@ -515,100 +93,4 @@ if($_SERVER['CONTENT_TYPE'] === 'application/json'){ die; } } - - - /* -- mode Modification d'une page -- */ - - // partie "page" - elseif(isset($_GET['page_edit'])) - { - // récupération des données - $data = file_get_contents('php://input'); - $json = json_decode($data, true); - - // titre de la page - if($_GET['page_edit'] === 'page_title'){ - $page = $entityManager->find('App\Entity\Page', $json['page_id']); - $page->setPageName(htmlspecialchars($json['title'])); - $entityManager->flush(); - echo json_encode(['success' => true, 'title' => $page->getPageName()]); - } - // titre en snake_case pour le menu - /*elseif($_GET['page_edit'] === 'page_menu_path'){ - $page = $entityManager->find('App\Entity\Page', $json['page_id']); - $page->setEndOfPath(htmlspecialchars($json['page_menu_path'])); - $entityManager->flush(); - echo json_encode(['success' => true, 'page_name_path' => $page->getEndOfPath()]); - }*/ - // description dans les métadonnées - elseif($_GET['page_edit'] === 'page_description'){ - $node_data = $entityManager->find('App\Entity\NodeData', $json['node_data_id']); - $node_data->updateData('description', htmlspecialchars($json['description'])); - $entityManager->flush(); - echo json_encode(['success' => true, 'description' => $node_data->getData()['description']]); - } - die; - } - - // partie "blocs" - elseif(isset($_GET['bloc_edit'])) - { - // renommage d'un bloc - if($_GET['bloc_edit'] === 'rename_page_bloc') - { - if(isset($json['bloc_title']) && $json['bloc_title'] !== null && isset($json['bloc_id']) && is_int($json['bloc_id'])){ - $director = new Director($entityManager); - $director->findNodeById($json['bloc_id']); - - // le titre (du JSON en BDD) est récupéré sous forme de tableau, modifié et renvoyé - $data = $director->getNode()->getNodeData()->getData(); - $data['title'] = htmlspecialchars($json['bloc_title']); - $director->getNode()->getNodeData()->updateData('title', htmlspecialchars($json['bloc_title'])); - - $entityManager->flush(); - echo json_encode(['success' => true, 'title' => $data['title']]); - } - else{ - echo json_encode(['success' => false]); - } - die; - } - // inversion des positions de deux blocs - elseif($_GET['bloc_edit'] === 'switch_blocs_positions') - { - if(isset($json['id1']) && is_int($json['id1']) && isset($json['id2']) && is_int($json['id2']) && isset($_GET['page'])){ - $director = new Director($entityManager, true); - $director->findUniqueNodeByName('main'); - $director->findItsChildren(); - $main = $director->getNode(); - $main->sortChildren(true); // régénère les positions avant inversion - - $bloc1; $bloc2; - foreach($main->getChildren() as $child){ - if($child->getId() === $json['id1']){ - $bloc1 = $child; - break; - } - } - foreach($main->getChildren() as $child){ - if($child->getId() === $json['id2']){ - $bloc2 = $child; - break; - } - } - - // inversion - $tmp = $bloc1->getPosition(); - $bloc1->setPosition($bloc2->getPosition()); - $bloc2->setPosition($tmp); - - $entityManager->flush(); - echo json_encode(['success' => true]); - } - else{ - echo json_encode(['success' => false]); - } - die; - } - } } \ No newline at end of file diff --git a/src/controller/ajax_admin.php b/src/controller/ajax_admin.php new file mode 100644 index 0000000..944e84b --- /dev/null +++ b/src/controller/ajax_admin.php @@ -0,0 +1,626 @@ +readImageBlob($image_data); + $imagick->stripImage(); // nettoyage métadonnées + $imagick->setImageFormat($format); + if($format === 'jpeg'){ + $imagick->setImageCompression(Imagick::COMPRESSION_JPEG); + $imagick->setImageCompressionQuality(85); // optionnel + } + $imagick->writeImage($local_path); // enregistrement + $imagick->clear(); + $imagick->destroy(); + return true; + } + catch(Exception $e){ + return false; + } +} +function curlDownloadImage(string $url, $maxRetries = 3, $timeout = 10): string|false +{ + $attempt = 0; + $imageData = false; + + while($attempt < $maxRetries){ + $ch = curl_init($url); // instance de CurlHandle + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_USERAGENT, 'TinyMCE-Image-Downloader'); + + $imageData = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + //$curlError = curl_error($ch); + + curl_close($ch); + + if($imageData !== false && $httpCode >= 200 && $httpCode < 300){ + return $imageData; + } + + $attempt++; + sleep(1); + } + + return false; // échec après trois tentatives +} + + +// détection des requêtes d'upload d'image de tinymce +if(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false && isset($_GET['action']) && $_GET['action'] === 'upload_image') +{ + if(isset($_FILES['file'])){ + $file = $_FILES['file']; + $dest = 'images/'; + $dest_mini = 'images-mini/'; + + // Vérifier si les répertoires existent, sinon les créer + if(!is_dir($dest)) { + mkdir($dest, 0700, true); + } + if(!is_dir($dest_mini)) { + mkdir($dest_mini, 0700, true); + } + + $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; + $name = Security::secureFileName(pathinfo($file['name'], PATHINFO_FILENAME)); + $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); + if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ + $extension = 'jpeg'; + } + $file_path = $dest . $name . '_' . uniqid() . '.' . $extension; + + // créer une miniature de l'image + // + + if(imagickCleanImage(file_get_contents($file['tmp_name']), $file_path, $extension)){ // recréer l’image pour la nettoyer + echo json_encode(['location' => $file_path]); // renvoyer l'URL de l'image téléchargée + } + else{ + http_response_code(500); + echo json_encode(['message' => 'Erreur image non valide']); + } + } + else{ + http_response_code(400); + echo json_encode(['message' => 'Erreur 400: Bad Request']); + } + die; +} +// 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 +elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_url') +{ + $json = json_decode(file_get_contents('php://input'), true); + + if(isset($json['image_url'])){ + $image_data = curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents + $dest = 'images/'; + + if(!is_dir($dest)) { // Vérifier si le répertoire existe, sinon le créer + mkdir($dest, 0777, true); + } + + if($image_data === false){ + http_response_code(400); + echo json_encode(['message' => "Erreur, le serveur n'a pas réussi à télécharger l'image."]); + die; + } + + $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; + $url_path = parse_url($json['image_url'], PHP_URL_PATH); + $name = Security::secureFileName(pathinfo($url_path, PATHINFO_FILENAME)); + $extension = strtolower(pathinfo($url_path, PATHINFO_EXTENSION)); + if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ + $extension = 'jpeg'; + } + $local_path = $dest . $name . '_' . uniqid() . '.' . $extension; + + if(imagickCleanImage($image_data, $local_path, $extension)){ // recréer l’image pour la nettoyer + echo json_encode(['location' => $local_path]); // nouvelle adresse + } + else{ + http_response_code(500); + echo json_encode(['message' => 'Erreur image non valide']); + } + } + else{ + echo json_encode(['message' => 'Erreur 400: Bad Request']); + } + die; +} +// cas du collage d'une image (code base64) non encapsulée dans du HTML +elseif(isset($_GET['action']) && $_GET['action'] == 'upload_image_base64') +{ + $json = json_decode(file_get_contents('php://input'), true); + $dest = 'images/'; + + if(!is_dir('images')){ + mkdir('images', 0777, true); + } + + // détection de data:image/ et de ;base64, et capture du format dans $type + if(!isset($json['image_base64']) || !preg_match('/^data:image\/(\w+);base64,/', $json['image_base64'], $type)){ + http_response_code(400); + echo json_encode(['message' => 'Données image base64 manquantes ou invalides']); + die; + } + + $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; + $extension = strtolower($type[1]); + if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ + $extension = 'jpeg'; + } + + $image_data = base64_decode(substr($json['image_base64'], strpos($json['image_base64'], ',') + 1)); // découpe la chaine à la virgule puis convertit en binaire + if($image_data === false){ + http_response_code(400); + echo json_encode(['message' => 'Décodage base64 invalide']); + die; + } + + $local_path = $dest . 'pasted_image_' . uniqid() . '.' . $extension; + + if(imagickCleanImage($image_data, $local_path)){ + echo json_encode(['location' => $local_path]); + } + else{ + http_response_code(500); + echo json_encode(['message' => 'Erreur image non valide']); + } + die; +} + +// détection des requêtes de type XHR, y en a pas à priori +/*elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){ + echo "requête XHR reçue par le serveur"; + die; +}*/ + + +// détection des requêtes envoyées avec fetch (application/json) et récupération du JSON +if($_SERVER['CONTENT_TYPE'] === 'application/json') +{ + $data = file_get_contents('php://input'); + $json = json_decode($data, true); + + // requêtes de tinymce ou touchant aux articles + if(isset($_GET['action'])) + { + if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content'])) + { + if(json_last_error() === JSON_ERROR_NONE) + { + $id = $json['id']; + $director = new Director($entityManager); + + // cas d'une nouvelle "news" + if(is_array($json['content'])){ + foreach($json['content'] as $one_input){ + $one_input = Security::secureString($one_input); + } + $content = $json['content']; + } + else{ + $content = Security::secureString($json['content']); + } + + // nouvel article + if($id[0] === 'n') + { + $section_id = (int)substr($id, 1); // id du bloc
+ $director->findNodeById($section_id); + $director->makeSectionNode(); + $node = $director->getNode(); // =
+ + if(is_array($content)){ + $date = new \DateTime($content['d']); + $article = new Article($content['i'], $date, $content['t'], $content['p']); + $article_node = new Node('new', 'i' . (string)$date->getTimestamp(), [], count($node->getChildren()) + 1, $node, $node->getPage(), $article); + + // id_node tout juste généré + //$article_node->getId(); + } + else{ + $timestamp = time(); + $date = new \DateTime; + $date->setTimestamp($timestamp); + + $article = new Article($content, $date); // le "current" timestamp est obtenu par la BDD + $article_node = new Node('article', 'i' . (string)$timestamp, [], count($node->getChildren()) + 1, $node, $node->getPage(), $article); + } + + $entityManager->persist($article_node); + $entityManager->flush(); + + echo json_encode(['success' => true, 'article_id' => $article_node->getArticleTimestamp()]); + die; + } + // modification article + else{ + $id[0] = 'i'; // id de l'article node + } + + if($director->makeArticleNode($id)) // une entrée est trouvée + { + $node = $director->getArticleNode(); // article + switch($json['id'][0]){ + case 'i': + $node->getArticle()->setContent($content); + break; + case 'p': + $node->getArticle()->setPreview($content); // html de l'éditeur + break; + case 't': + $node->getArticle()->setTitle($content); // html de l'éditeur + break; + case 'd': + echo json_encode(['success' => false, 'message' => 'l\'action editor_submit ne supporte pas les dates, utiliser date_submit.']); + die; + default: + echo json_encode(['success' => false, 'message' => 'identifiant non utilisable']); + die; + } + $entityManager->flush(); + echo json_encode(['success' => true]); + } + else + { + echo json_encode(['success' => false, 'message' => 'article non identifié']); + } + } + else{ + echo json_encode(['success' => false, 'message' => 'Erreur de décodage JSON']); + } + die; + } + elseif($_GET['action'] === 'delete_article' && isset($json['id'])) + { + $director = new Director($entityManager); + $director->makeArticleNode($json['id'], true); + $article = $director->getArticleNode(); + $section = $director->getNode(); + + $entityManager->remove($article); + $section->removeChild($article); + $section->sortChildren(true); // régénère les positions + $entityManager->flush(); + + // test avec une nouvelle requête qui ne devrait rien trouver + if(!$director->makeArticleNode($json['id'])) + { + echo json_encode(['success' => true]); + + // on pourrait afficher une notification "toast" + } + else{ + http_response_code(500); + echo json_encode(['success' => false, 'message' => 'Erreur lors de la suppression de l\'article.']); + } + die; + } + // inversion de la position de deux noeuds + elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])) + { + $director = new Director($entityManager); + $director->makeArticleNode($json['id1'], true); + $article1 = $director->getArticleNode(); + $section = $director->getNode(); + + $section->sortChildren(true); // régénère les positions avant inversion + + $article2; + foreach($section->getChildren() as $child){ + if($child->getArticleTimestamp() === $json['id2']) // type string + { + $article2 = $child; + break; + } + } + + // inversion + $tmp = $article1->getPosition(); + $article1->setPosition($article2->getPosition()); + $article2->setPosition($tmp); + $entityManager->flush(); + + echo json_encode(['success' => true]); + die; + } + elseif($_GET['action'] === 'date_submit' && isset($json['id']) && isset($json['date'])) + { + $id = $json['id']; + $id[0] = 'i'; + $date = new DateTime($json['date']); + + $director = new Director($entityManager); + $director->makeArticleNode($id); + $node = $director->getArticleNode(); + $node->getArticle()->setDateTime($date); + $entityManager->flush(); + + echo json_encode(['success' => true]); + die; + } + + // config formulaire + /*elseif($_GET['action'] === 'recipient_email'){ + $email = htmlspecialchars(trim($json['email'])); + + if(filter_var($email, FILTER_VALIDATE_EMAIL) && isset($json['hidden']) && empty($json['hidden'])){ + $form_data = $entityManager->find('App\Entity\NodeData', $json['id']); + $form_data->updateData('email', $json['email']); + $entityManager->persist($form_data); + $entityManager->flush(); + + echo json_encode(['success' => true]); + } + else{ + echo json_encode(['success' => false]); + } + die; + }*/ + // e-mail de test + elseif($_GET['action'] === 'test_email'){ + if(sendEmail(false, 'nom du visiteur', 'adresse@du_visiteur.fr', "TEST d'un envoi d'e-mail depuis le site web")){ + echo json_encode(['success' => true]); + } + else{ + echo json_encode(['success' => false]); + } + die; + } + } + + + /* -- page Menu et chemins -- */ + elseif(isset($_GET['menu_edit'])) + { + // récupération des données + $data = file_get_contents('php://input'); + $json = json_decode($data, true); + Director::$menu_data = new Menu($entityManager); + + // flèche gauche <=: position = position du parent + 1, parent = grand-parent, recalculer les positions + if($_GET['menu_edit'] === 'move_one_level_up' && isset($json['id'])){ + $id = $json['id']; + $page = Director::$menu_data->findPageById((int)$id); + + $parent = $page->getParent(); // peut être null + if($parent === null){ + // 1er niveau: ne rien faire + echo json_encode(['success' => false]); + die; + } + // BDD + else{ + $page->setPosition($parent->getPosition() + 1); // nouvelle position + + // 2ème niveau: le parent devient $menu_data, puis null après tri + if($parent->getParent() === null){ + // connexion dans les deux sens + $page->setParent(Director::$menu_data); // => pour la persistance + + //Director::$menu_data->addChild($page); // => pour sortChildren + $page->getParent()->addChild($page); // => pour sortChildren + //Director::$menu_data->sortChildren(true); // positions décaléees des nouveaux petits frères + $page->getParent()->sortChildren(true); // positions décaléees des nouveaux petits frères + $page->setParent(null); + + // affichage + $page->setPagePath($page->getEndOfPath()); + $page->fillChildrenPagePath(); + } + // 3ème niveau et plus + else{ + $page->setParent($parent->getParent()); // nouveau parent + $page->getParent()->addChild($page); // => pour sortChildren + $page->getParent()->sortChildren(true); // positions décaléees des nouveaux petits frères + $page->fillChildrenPagePath($page->getParent()->getPagePath()); + } + //$parent->sortChildren(true); // positions des enfants restants, inutile si la fonction est récursive? + $entityManager->flush(); + + // affichage + $parent->removeChild($page); + $nav_builder = new NavBuilder(); + $menu_builder = new MenuBuilder(null, false); + echo json_encode(['success' => true, 'nav' => $nav_builder->render(), 'menu_buttons' => $menu_builder->render()]); + die; + } + } + + // flèche droite =>: position = nombre d'éléments de la fraterie + 1, l'élément précédent devient le parent + if($_GET['menu_edit'] === 'move_one_level_down' && isset($json['id'])){ + $id = $json['id']; + $page = Director::$menu_data->findPageById((int)$id); + + $parent = $page->getParent(); // peut être null + if($parent == null){ + $parent = Director::$menu_data; + } + + // BDD + $parent->sortChildren(true); // trie et réindexe par sécurité: 1, 2, 3... + if($page->getPosition() > 1){ + foreach($parent->getChildren() as $child){ + if($child->getPosition() === $page->getPosition() - 1){ + $page->setParent($child); + break; + } + } + $page->setPosition(count($page->getParent()->getChildren()) + 1); + } + $entityManager->flush(); + + // affichage + $parent->removeChild($page); + $page->getParent()->addChild($page); + $page->fillChildrenPagePath($page->getParent()->getPagePath()); // variable non mappée $page_path + $nav_builder = new NavBuilder(); + $menu_builder = new MenuBuilder(null, false); + + echo json_encode(['success' => true, 'nav' => $nav_builder->render(), 'menu_buttons' => $menu_builder->render()]); + die; + } + + if($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])) + { + $id1 = $json['id1']; + $id2 = $json['id2']; + + // vérifier qu'ils ont le même parent + $page1 = Director::$menu_data->findPageById((int)$id1); + $page2 = Director::$menu_data->findPageById((int)$id2); + + // double le contrôle fait en JS + if($page1->getParent() === $page2->getParent()) // comparaison stricte d'objet (même instance du parent?) + { + // inversion + $tmp = $page1->getPosition(); + $page1->setPosition($page2->getPosition()); + $page2->setPosition($tmp); + Director::$menu_data->sortChildren(true); // modifie tableau children + $entityManager->flush(); + + // nouveau menu + $nav_builder = new NavBuilder(); + echo json_encode(['success' => true, 'nav' => $nav_builder->render()]); + } + else{ + echo json_encode(['success' => false]); + } + + die; + } + + if($_GET['menu_edit'] === 'displayInMenu' && isset($json['id']) && isset($json['checked'])) + { + $id = $json['id']; + $checked = $json['checked']; + + $page = Director::$menu_data->findPageById((int)$id); + if($page->isHidden() === $checked){ + $page->setHidden(!$checked); + $entityManager->flush(); + + // nouveau menu + $nav_builder = new NavBuilder(); + echo json_encode(['success' => true, 'nav' => $nav_builder->render()]); + } + else{ + echo json_encode(['success' => false]); + } + die; + } + } + + + /* -- mode Modification d'une page -- */ + + // partie "page" + elseif(isset($_GET['page_edit'])) + { + // récupération des données + $data = file_get_contents('php://input'); + $json = json_decode($data, true); + + // titre de la page + if($_GET['page_edit'] === 'page_title'){ + $page = $entityManager->find('App\Entity\Page', $json['page_id']); + $page->setPageName(htmlspecialchars($json['title'])); + $entityManager->flush(); + echo json_encode(['success' => true, 'title' => $page->getPageName()]); + } + // titre en snake_case pour le menu + /*elseif($_GET['page_edit'] === 'page_menu_path'){ + $page = $entityManager->find('App\Entity\Page', $json['page_id']); + $page->setEndOfPath(htmlspecialchars($json['page_menu_path'])); + $entityManager->flush(); + echo json_encode(['success' => true, 'page_name_path' => $page->getEndOfPath()]); + }*/ + // description dans les métadonnées + elseif($_GET['page_edit'] === 'page_description'){ + $node_data = $entityManager->find('App\Entity\NodeData', $json['node_data_id']); + $node_data->updateData('description', htmlspecialchars($json['description'])); + $entityManager->flush(); + echo json_encode(['success' => true, 'description' => $node_data->getData()['description']]); + } + die; + } + + // partie "blocs" + elseif(isset($_GET['bloc_edit'])) + { + // renommage d'un bloc + if($_GET['bloc_edit'] === 'rename_page_bloc') + { + if(isset($json['bloc_title']) && $json['bloc_title'] !== null && isset($json['bloc_id']) && is_int($json['bloc_id'])){ + $director = new Director($entityManager); + $director->findNodeById($json['bloc_id']); + + // le titre (du JSON en BDD) est récupéré sous forme de tableau, modifié et renvoyé + $data = $director->getNode()->getNodeData()->getData(); + $data['title'] = htmlspecialchars($json['bloc_title']); + $director->getNode()->getNodeData()->updateData('title', htmlspecialchars($json['bloc_title'])); + + $entityManager->flush(); + echo json_encode(['success' => true, 'title' => $data['title']]); + } + else{ + echo json_encode(['success' => false]); + } + die; + } + // inversion des positions de deux blocs + elseif($_GET['bloc_edit'] === 'switch_blocs_positions') + { + if(isset($json['id1']) && is_int($json['id1']) && isset($json['id2']) && is_int($json['id2']) && isset($_GET['page'])){ + $director = new Director($entityManager, true); + $director->findUniqueNodeByName('main'); + $director->findItsChildren(); + $main = $director->getNode(); + $main->sortChildren(true); // régénère les positions avant inversion + + $bloc1; $bloc2; + foreach($main->getChildren() as $child){ + if($child->getId() === $json['id1']){ + $bloc1 = $child; + break; + } + } + foreach($main->getChildren() as $child){ + if($child->getId() === $json['id2']){ + $bloc2 = $child; + break; + } + } + + // inversion + $tmp = $bloc1->getPosition(); + $bloc1->setPosition($bloc2->getPosition()); + $bloc2->setPosition($tmp); + + $entityManager->flush(); + echo json_encode(['success' => true]); + } + else{ + echo json_encode(['success' => false]); + } + die; + } + } +} \ No newline at end of file diff --git a/src/controller/post.php b/src/controller/post.php index acad1ce..bcafe6f 100644 --- a/src/controller/post.php +++ b/src/controller/post.php @@ -9,261 +9,272 @@ use App\Entity\Page; use App\Entity\Image; use Doctrine\Common\Collections\ArrayCollection; -if($_SERVER['REQUEST_METHOD'] === 'POST' && $_SESSION['admin'] === true) -{ - /* -- formulaires HTML classiques -- */ - if($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded') +if($_SERVER['REQUEST_METHOD'] === 'POST'){ + // POST ordinaires non admin + + // POST ajax non admin + require '../src/controller/ajax.php'; + + if($_SESSION['admin'] === true) { - /* -- nouvelle page -- */ - if(isset($_POST['page_name']) && $_POST['page_name'] !== null - && isset($_POST['page_name_path']) && $_POST['page_name_path'] !== null - && isset($_POST['page_location']) && $_POST['page_location'] !== null - && isset($_POST['page_description']) && $_POST['page_description'] !== null - && isset($_POST['new_page_hidden']) && $_POST['new_page_hidden'] === '') - { - // titre et chemin - $director = new Director($entityManager, true); - //Director::$menu_data = new Menu($entityManager); - $previous_page = Director::$menu_data->findPageById((int)$_POST["page_location"]); // (int) à cause de declare(strict_types=1); - $parent = $previous_page->getParent(); - - $page = new Page( - trim(htmlspecialchars($_POST["page_name"])), - trim(htmlspecialchars($_POST["page_name_path"])), - true, true, false, - $previous_page->getPosition(), - $parent); // peut et DOIT être null si on est au 1er niveau - - // on a donné à la nouvelle entrée la même position qu'à la précédente, - // addChild l'ajoute à la fin du tableau "children" puis on trie - // 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 - if($parent == null){ - $parent = Director::$menu_data; - } - $parent->addChild($page); - $parent->reindexPositions(); - - $page->setPagePath(ltrim($parent->getPagePath() . '/' . $page->getEndOfPath(), '/')); - - // noeud "head" - $node = new Node( - 'head', - null, [], - 1, // position d'un head = 1 - null, // pas de parent - $page); - $node->useDefaultAttributes(); // fichiers CSS et JS - - $data = new NodeData([ - // pas de titre, il est dans $page - 'description' => trim(htmlspecialchars($_POST["page_description"]))], - $node); - - $bulk_data = $entityManager - ->createQuery('SELECT n FROM App\Entity\Image n WHERE n.file_name LIKE :name') - ->setParameter('name', '%favicon%') - ->getResult(); - $data->setImages(new ArrayCollection($bulk_data)); - - $entityManager->persist($page); - $entityManager->persist($node); - $entityManager->persist($data); - $entityManager->flush(); - - // page créée, direction la page en mode modification pour ajouter des blocs - header("Location: " . new URL(['page' => $page->getPagePath(), 'action' => 'modif_page'])); - die; - } - - /* -- suppression d'une page -- */ - elseif(isset($_POST['page_id']) && $_POST['page_id'] !== null - && isset($_POST['submit_hidden']) && $_POST['submit_hidden'] === '') + /* -- formulaires HTML classiques -- */ + if($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded') { - $page = $entityManager->find('App\Entity\Page', (int)$_POST['page_id']); - $nodes = $entityManager->getRepository('App\Entity\Node')->findBy(['page' => $page]); - $data = []; - foreach($nodes as $node){ - $data[] = $entityManager->getRepository('App\Entity\NodeData')->findOneBy(['node' => $node]); - $entityManager->remove($node); - } - foreach($data as $one_data){ - $entityManager->remove($one_data); - } - $entityManager->remove($page); // suppression en BDD - - $entityManager->flush(); - header("Location: " . new URL); - die; - } + /* -- nouvelle page -- */ + if(isset($_POST['page_name']) && $_POST['page_name'] !== null + && isset($_POST['page_name_path']) && $_POST['page_name_path'] !== null + && isset($_POST['page_location']) && $_POST['page_location'] !== null + && isset($_POST['page_description']) && $_POST['page_description'] !== null + && isset($_POST['new_page_hidden']) && $_POST['new_page_hidden'] === '') + { + // titre et chemin + $director = new Director($entityManager, true); + //Director::$menu_data = new Menu($entityManager); + $previous_page = Director::$menu_data->findPageById((int)$_POST["page_location"]); // (int) à cause de declare(strict_types=1); + $parent = $previous_page->getParent(); + + $page = new Page( + trim(htmlspecialchars($_POST["page_name"])), + trim(htmlspecialchars($_POST["page_name_path"])), + true, true, false, + $previous_page->getPosition(), + $parent); // peut et DOIT être null si on est au 1er niveau + + // on a donné à la nouvelle entrée la même position qu'à la précédente, + // addChild l'ajoute à la fin du tableau "children" puis on trie + // 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 + if($parent == null){ + $parent = Director::$menu_data; + } + $parent->addChild($page); + $parent->reindexPositions(); + $page->setPagePath(ltrim($parent->getPagePath() . '/' . $page->getEndOfPath(), '/')); - /* -- mode Modification d'une page -- */ + // noeud "head" + $node = new Node( + 'head', + null, [], + 1, // position d'un head = 1 + null, // pas de parent + $page); + $node->useDefaultAttributes(); // fichiers CSS et JS - // modification des titres, chemins et descriptions - elseif(isset($_POST['page_menu_path']) && $_POST['page_menu_path'] !== null - && isset($_POST['page_id']) && $_POST['page_id'] !== null - && isset($_POST['page_name_path_hidden']) && $_POST['page_name_path_hidden'] === '') - { - $director = new Director($entityManager, true); - $page = Director::$page_path->getLast(); - $path = htmlspecialchars($_POST['page_menu_path']); - - // mise en snake_case: filtre caractères non-alphanumériques, minuscule, doublons d'underscore, trim des underscores - $path = trim(preg_replace('/_+/', '_', strtolower(preg_replace('/[^a-zA-Z0-9]/', '_', $path))), '_'); - $page->setEndOfPath($path); - foreach(Director::$menu_data->getChildren() as $child){ - if($child->getEndOfPath() === Director::$page_path->getArray()[0]->getEndOfPath()){ - $child->fillChildrenPagePath(); // MAJ de $page_path - } + $data = new NodeData([ + // pas de titre, il est dans $page + 'description' => trim(htmlspecialchars($_POST["page_description"]))], + $node); + + $bulk_data = $entityManager + ->createQuery('SELECT n FROM App\Entity\Image n WHERE n.file_name LIKE :name') + ->setParameter('name', '%favicon%') + ->getResult(); + $data->setImages(new ArrayCollection($bulk_data)); + + $entityManager->persist($page); + $entityManager->persist($node); + $entityManager->persist($data); + $entityManager->flush(); + + // page créée, direction la page en mode modification pour ajouter des blocs + header("Location: " . new URL(['page' => $page->getPagePath(), 'action' => 'modif_page'])); + die; } - $entityManager->flush(); - header("Location: " . new URL(['page' => $page->getPagePath(), 'action' => 'modif_page'])); - die; - } - // ajout d'un bloc dans une page - elseif(isset($_POST['bloc_title']) && $_POST['bloc_title'] !== null - && isset($_POST['bloc_select']) && $_POST['bloc_select'] !== null - && isset($_POST['bloc_title_hidden']) && $_POST['bloc_title_hidden'] === '') // contrôle anti-robot avec input hidden - { - $director = new Director($entityManager, true); // on a besoin de page_path qui dépend de menu_data - $page = Director::$page_path->getLast(); - $director->findUniqueNodeByName('main'); - $director->findItsChildren(); - $main = $director->getNode(); - $position = count($main->getChildren()) + 1; // position dans la fraterie - - $blocks = ['blog', 'grid', 'calendar', 'galery', 'form']; // même liste dans FormBuilder.php - if(!in_array($_POST["bloc_select"], $blocks, true)) // 3è param: contrôle du type + + /* -- suppression d'une page -- */ + elseif(isset($_POST['page_id']) && $_POST['page_id'] !== null + && isset($_POST['submit_hidden']) && $_POST['submit_hidden'] === '') { - header("Location: " . new URL(['page' => $_GET['page'], 'error' => 'bad_bloc_type'])); + $page = $entityManager->find('App\Entity\Page', (int)$_POST['page_id']); + $nodes = $entityManager->getRepository('App\Entity\Node')->findBy(['page' => $page]); + $data = []; + foreach($nodes as $node){ + $data[] = $entityManager->getRepository('App\Entity\NodeData')->findOneBy(['node' => $node]); + $entityManager->remove($node); + } + foreach($data as $one_data){ + $entityManager->remove($one_data); + } + $entityManager->remove($page); // suppression en BDD + + $entityManager->flush(); + header("Location: " . new URL); die; } - if($_POST["bloc_select"] === 'calendar' || $_POST["bloc_select"] === 'form'){ - $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page AND n.name_node = :name'; // noeud 'head' de la page - $bulk_data = $entityManager - ->createQuery($dql) - ->setParameter('page', $page) - ->setParameter('name', 'head') - ->getResult(); - if(count($bulk_data) != 1){ // 1 head par page - header("Location: " . new URL(['page' => $_GET['page'], 'error' => 'head_node_not_found'])); - die; - } + /* -- mode Modification d'une page -- */ - $bulk_data[0]->addAttribute('css_array', $_POST["bloc_select"]); - $entityManager->persist($bulk_data[0]); + // modification des titres, chemins et descriptions + elseif(isset($_POST['page_menu_path']) && $_POST['page_menu_path'] !== null + && isset($_POST['page_id']) && $_POST['page_id'] !== null + && isset($_POST['page_name_path_hidden']) && $_POST['page_name_path_hidden'] === '') + { + $director = new Director($entityManager, true); + $page = Director::$page_path->getLast(); + $path = htmlspecialchars($_POST['page_menu_path']); + + // mise en snake_case: filtre caractères non-alphanumériques, minuscule, doublons d'underscore, trim des underscores + $path = trim(preg_replace('/_+/', '_', strtolower(preg_replace('/[^a-zA-Z0-9]/', '_', $path))), '_'); + $page->setEndOfPath($path); + foreach(Director::$menu_data->getChildren() as $child){ + if($child->getEndOfPath() === Director::$page_path->getArray()[0]->getEndOfPath()){ + $child->fillChildrenPagePath(); // MAJ de $page_path + } + } + $entityManager->flush(); + header("Location: " . new URL(['page' => $page->getPagePath(), 'action' => 'modif_page'])); + die; } + // ajout d'un bloc dans une page + elseif(isset($_POST['bloc_title']) && $_POST['bloc_title'] !== null + && isset($_POST['bloc_select']) && $_POST['bloc_select'] !== null + && isset($_POST['bloc_title_hidden']) && $_POST['bloc_title_hidden'] === '') // contrôle anti-robot avec input hidden + { + $director = new Director($entityManager, true); // on a besoin de page_path qui dépend de menu_data + $page = Director::$page_path->getLast(); + $director->findUniqueNodeByName('main'); + $director->findItsChildren(); + $main = $director->getNode(); + $position = count($main->getChildren()) + 1; // position dans la fraterie + + $blocks = ['blog', 'grid', 'calendar', 'galery', 'form']; // même liste dans FormBuilder.php + if(!in_array($_POST["bloc_select"], $blocks, true)) // 3è param: contrôle du type + { + header("Location: " . new URL(['page' => $_GET['page'], 'error' => 'bad_bloc_type'])); + die; + } - $bloc = new Node( - $_POST["bloc_select"], - null, [], - $position, - $main, - $page); - $data = new NodeData( - ['title' => trim(htmlspecialchars($_POST["bloc_title"]))], - $bloc); - - $entityManager->persist($bloc); - $entityManager->persist($data); - $entityManager->flush(); - header("Location: " . new URL(['page' => $_GET['page'], 'action' => 'modif_page'])); - die; - } - // suppression d'un bloc de page - elseif(isset($_POST['delete_bloc_id']) && $_POST['delete_bloc_id'] !== null - && isset($_POST['delete_bloc_hidden']) && $_POST['delete_bloc_hidden'] === '') // contrôle anti-robot avec input hidden - { - $director = new Director($entityManager, true); - $director->findUniqueNodeByName('main'); - $director->findItsChildren(); - //$director->findNodeById((int)$_POST['delete_bloc_id']); - $main = $director->getNode(); - $bloc; - foreach($main->getChildren() as $child){ - if($child->getId() === (int)$_POST['delete_bloc_id']){ - $bloc = $child; - break; + if($_POST["bloc_select"] === 'calendar' || $_POST["bloc_select"] === 'form'){ + $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page AND n.name_node = :name'; // noeud 'head' de la page + $bulk_data = $entityManager + ->createQuery($dql) + ->setParameter('page', $page) + ->setParameter('name', 'head') + ->getResult(); + + if(count($bulk_data) != 1){ // 1 head par page + header("Location: " . new URL(['page' => $_GET['page'], 'error' => 'head_node_not_found'])); + die; + } + + $bulk_data[0]->addAttribute('css_array', $_POST["bloc_select"]); + if($_POST["bloc_select"] === 'form'){ + $bulk_data[0]->addAttribute('js_array', $_POST["bloc_select"]); + } + $entityManager->persist($bulk_data[0]); } + + $bloc = new Node( + $_POST["bloc_select"], + null, [], + $position, + $main, + $page); + $data = new NodeData( + ['title' => trim(htmlspecialchars($_POST["bloc_title"]))], + $bloc); + + $entityManager->persist($bloc); + $entityManager->persist($data); + $entityManager->flush(); + header("Location: " . new URL(['page' => $_GET['page'], 'action' => 'modif_page'])); + die; } - $main->removeChild($bloc); // réindex le tableau $children au passage - $main->reindexPositions(); + // suppression d'un bloc de page + elseif(isset($_POST['delete_bloc_id']) && $_POST['delete_bloc_id'] !== null + && isset($_POST['delete_bloc_hidden']) && $_POST['delete_bloc_hidden'] === '') // contrôle anti-robot avec input hidden + { + $director = new Director($entityManager, true); + $director->findUniqueNodeByName('main'); + $director->findItsChildren(); + //$director->findNodeById((int)$_POST['delete_bloc_id']); + $main = $director->getNode(); + $bloc; + foreach($main->getChildren() as $child){ + if($child->getId() === (int)$_POST['delete_bloc_id']){ + $bloc = $child; + break; + } + } + $main->removeChild($bloc); // réindex le tableau $children au passage + $main->reindexPositions(); - $entityManager->remove($bloc); // suppression en BDD - $entityManager->flush(); - header("Location: " . new URL(['page' => $_GET['page'], 'action' => 'modif_page'])); - die; - } + $entityManager->remove($bloc); // suppression en BDD + $entityManager->flush(); + header("Location: " . new URL(['page' => $_GET['page'], 'action' => 'modif_page'])); + die; + } - /* -- page Menu et chemins -- */ + /* -- page Menu et chemins -- */ - // création d'une entrée de menu avec une URL - elseif(isset($_POST["label_input"]) && isset($_POST["url_input"]) && isset($_POST["location"])){ - Director::$menu_data = new Menu($entityManager); - $previous_page = Director::$menu_data->findPageById((int)$_POST["location"]); // (int) à cause de declare(strict_types=1); - $parent = $previous_page->getParent(); + // création d'une entrée de menu avec une URL + elseif(isset($_POST["label_input"]) && isset($_POST["url_input"]) && isset($_POST["location"])){ + Director::$menu_data = new Menu($entityManager); + $previous_page = Director::$menu_data->findPageById((int)$_POST["location"]); // (int) à cause de declare(strict_types=1); + $parent = $previous_page->getParent(); - $page = new Page( - trim(htmlspecialchars($_POST["label_input"])), - filter_var($_POST["url_input"], FILTER_VALIDATE_URL), - true, true, false, - $previous_page->getPosition(), - $parent); // peut et DOIT être null si on est au 1er niveau + $page = new Page( + trim(htmlspecialchars($_POST["label_input"])), + filter_var($_POST["url_input"], FILTER_VALIDATE_URL), + true, true, false, + $previous_page->getPosition(), + $parent); // peut et DOIT être null si on est au 1er niveau - // on a donné à la nouvelle entrée la même position qu'à la précédente, - // addChild l'ajoute à la fin du tableau "children" puis on trie - // 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 - if($parent == null){ - $parent = Director::$menu_data; - } - $parent->addChild($page); // true pour réindexer les positions en BDD - $parent->reindexPositions(); + // on a donné à la nouvelle entrée la même position qu'à la précédente, + // addChild l'ajoute à la fin du tableau "children" puis on trie + // 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 + if($parent == null){ + $parent = Director::$menu_data; + } + $parent->addChild($page); // true pour réindexer les positions en BDD + $parent->reindexPositions(); - $entityManager->persist($page); - $entityManager->flush(); - header("Location: " . new URL(['page' => $_GET['from']])); - die; - } - // suppression d'une entrée de menu avec une URL - 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 - Director::$menu_data = new Menu($entityManager); - $page = Director::$menu_data->findPageById((int)$_POST["delete"]); - $parent = $page->getParent(); - if($parent == null){ - $parent = Director::$menu_data; + $entityManager->persist($page); + $entityManager->flush(); + header("Location: " . new URL(['page' => $_GET['from']])); + die; } + // suppression d'une entrée de menu avec une URL + 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 + Director::$menu_data = new Menu($entityManager); + $page = Director::$menu_data->findPageById((int)$_POST["delete"]); + $parent = $page->getParent(); + if($parent == null){ + $parent = Director::$menu_data; + } - $parent->removeChild($page); // suppression de $children avant de trier - $parent->reindexPositions(); + $parent->removeChild($page); // suppression de $children avant de trier + $parent->reindexPositions(); - $entityManager->remove($page); // suppression en BDD - $entityManager->flush(); - header("Location: " . new URL(['page' => $_GET['from']])); - die; - } - elseif(isset($_GET['action']) && $_GET['action'] === 'modif_mdp' - && isset($_POST['login']) && isset($_POST['old_password']) && isset($_POST['new_password']) - && isset($_POST['modify_password_hidden']) && empty($_POST['modify_password_hidden'])) - { - changePassword($entityManager); - header("Location: " . new URL(['page' => $_GET['from']])); - die; + $entityManager->remove($page); // suppression en BDD + $entityManager->flush(); + header("Location: " . new URL(['page' => $_GET['from']])); + die; + } + elseif(isset($_GET['action']) && $_GET['action'] === 'modif_mdp' + && isset($_POST['login']) && isset($_POST['old_password']) && isset($_POST['new_password']) + && isset($_POST['modify_password_hidden']) && empty($_POST['modify_password_hidden'])) + { + changePassword($entityManager); + header("Location: " . new URL(['page' => $_GET['from']])); + die; + } + else{ + header("Location: " . new URL(['error' => 'paramètres inconnus'])); + die; + } } + + /* -- requêtes AJAX -- */ else{ - header("Location: " . new URL(['error' => 'paramètres inconnus'])); - die; + require '../src/controller/ajax_admin.php'; } - } - /* -- requêtes AJAX -- */ - else{ - require '../src/controller/ajax.php'; + require '../src/controller/ajax_calendar_admin.php'; } - - require '../src/controller/ajax_calendar_admin.php'; } - -require '../src/controller/ajax_calendar_visitor.php'; +elseif($_SERVER['REQUEST_METHOD'] === 'GET'){ + require '../src/controller/ajax_calendar_visitor.php'; // fullcalendar utilise un GET pour récupérer les données +} \ No newline at end of file diff --git a/src/model/entities/Email.php b/src/model/entities/Email.php new file mode 100644 index 0000000..c6c2a29 --- /dev/null +++ b/src/model/entities/Email.php @@ -0,0 +1,41 @@ + 'CURRENT_TIMESTAMP'])] + private ?\DateTime $date_time ; + + public function __construct(string $sender, string $recipient, string $content){ + $this->sender = $sender; + $this->recipient = $recipient; + $this->content = $content; + $this->date_time = new \DateTime(); + } +} diff --git a/src/model/entities/Log.php b/src/model/entities/Log.php index 06a907e..39b4307 100644 --- a/src/model/entities/Log.php +++ b/src/model/entities/Log.php @@ -6,8 +6,6 @@ declare(strict_types=1); namespace App\Entity; use Doctrine\ORM\Mapping as ORM; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; #[ORM\Entity] #[ORM\Table(name: TABLE_PREFIX . "log")] diff --git a/src/view/FormBuilder.php b/src/view/FormBuilder.php index 5f8545c..508763b 100644 --- a/src/view/FormBuilder.php +++ b/src/view/FormBuilder.php @@ -19,32 +19,31 @@ class FormBuilder extends AbstractBuilder extract($node->getNodeData()->getData()); } - $action_url = new URL(['page' => CURRENT_PAGE]); $captcha = new Captcha; $_SESSION['captcha'] = $captcha->getSolution(); - $recipient_found = false; - if(isset($email)){ - $recipient_found = true; - } - else{ - $email = ''; - } - $admin_content = ''; if($_SESSION['admin']) { - $admin_content = ' -

Configuration du formulaire

-
- - - -
'; + $admin_content = '' + //. '

Configuration du formulaire

' . "\n" + . '
' . "\n" + /*. '

+ + + + +

+

+ + + + +

' . "\n"*/ + . '

' . "\n" + . '

' + . '
' . "\n"; } - - // vérifier qu'une adresse de destination est bien configurée - $no_recipient_warning = '

Aucune adresse de destination n\'a été configurée, envoi d\'e-mail impossible!

'; ob_start(); require $viewFile; diff --git a/src/view/templates/form.php b/src/view/templates/form.php index 0c44bf8..5f81f6f 100644 --- a/src/view/templates/form.php +++ b/src/view/templates/form.php @@ -2,31 +2,32 @@

- -
- - +
+ + + + + - - - - - + +
- +
- +
- - + +
- +
- + +

+
\ No newline at end of file -- cgit v1.2.3