From 3b369122645b07b290f7fcc7bccb4787745cd5ea Mon Sep 17 00:00:00 2001 From: polo Date: Tue, 24 Mar 2026 22:39:29 +0100 Subject: =?UTF-8?q?mode=20maintenance,=20optimisation=20moins=20de=20contr?= =?UTF-8?q?=C3=B4les=20en=20mode=20run,=20dossier=20service=20et=20d=C3=A9?= =?UTF-8?q?placement=20fichiers,=20sessions=20et=20entit=C3=A9=20User=20pr?= =?UTF-8?q?=C3=A9par=C3=A9es=20=C3=A0=20l'impl=C3=A9mentation=20hypoth?= =?UTF-8?q?=C3=A9tique=20des=20r=C3=B4les,=20entit=C3=A9=20AppMetadata,=20?= =?UTF-8?q?meilleure=20s=C3=A9curit=C3=A9=20de=20fillStartingDatabase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Captcha.php | 55 ------ src/Config.php | 86 --------- src/EmailService.php | 102 ---------- src/FormValidation.php | 215 --------------------- src/Security.php | 110 ----------- src/URL.php | 88 --------- src/controller/UserController.php | 65 +++---- src/controller/ViewController.php | 6 +- src/installation.php | 161 ---------------- src/model/Menu.php | 4 - src/model/entities/AppMetadata.php | 46 +++++ src/model/entities/User.php | 19 +- src/router.php | 384 ------------------------------------- src/service/AppMode.php | 56 ++++++ src/service/Captcha.php | 55 ++++++ src/service/Config.php | 86 +++++++++ src/service/EmailService.php | 102 ++++++++++ src/service/FormValidation.php | 215 +++++++++++++++++++++ src/service/Installation.php | 209 ++++++++++++++++++++ src/service/Security.php | 110 +++++++++++ src/service/URL.php | 88 +++++++++ src/service/router.php | 384 +++++++++++++++++++++++++++++++++++++ src/service/session.php | 86 +++++++++ src/view/CalendarBuilder.php | 2 +- src/view/FooterBuilder.php | 2 +- src/view/FormBuilder.php | 9 +- src/view/GaleryBuilder.php | 2 +- src/view/HeadBuilder.php | 2 +- src/view/HeaderBuilder.php | 2 +- src/view/LoginBuilder.php | 2 +- src/view/MainBuilder.php | 2 +- src/view/MenuBuilder.php | 2 +- src/view/NewBuilder.php | 2 +- src/view/NewPageBuilder.php | 2 +- src/view/NewsBlockBuilder.php | 2 +- src/view/PostBlockBuilder.php | 2 +- src/view/PostBuilder.php | 2 +- src/view/UserEditBuilder.php | 2 +- src/view/templates/footer.php | 2 +- src/view/templates/header.php | 2 +- src/view/templates/maintenance.php | 27 +++ src/view/templates/user_create.php | 2 +- src/view/templates/user_edit.php | 2 +- 43 files changed, 1537 insertions(+), 1267 deletions(-) delete mode 100644 src/Captcha.php delete mode 100644 src/Config.php delete mode 100644 src/EmailService.php delete mode 100644 src/FormValidation.php delete mode 100644 src/Security.php delete mode 100644 src/URL.php delete mode 100644 src/installation.php create mode 100644 src/model/entities/AppMetadata.php delete mode 100644 src/router.php create mode 100644 src/service/AppMode.php create mode 100644 src/service/Captcha.php create mode 100644 src/service/Config.php create mode 100644 src/service/EmailService.php create mode 100644 src/service/FormValidation.php create mode 100644 src/service/Installation.php create mode 100644 src/service/Security.php create mode 100644 src/service/URL.php create mode 100644 src/service/router.php create mode 100644 src/service/session.php create mode 100644 src/view/templates/maintenance.php (limited to 'src') diff --git a/src/Captcha.php b/src/Captcha.php deleted file mode 100644 index a0c7a54..0000000 --- a/src/Captcha.php +++ /dev/null @@ -1,55 +0,0 @@ - on pourrait appliquer le pattern "singleton" (justification = le captcha devient une sorte de ressource partagée) - -declare(strict_types=1); - -class Captcha -{ - private int $a; - private int $b; - - public function __construct(){ - $this->a = rand(2, 9); - $this->b = rand(2, 9); - } - - public function getA(): string - { - return $this->toLettersFrench($this->a); - } - public function getB(): string - { - return $this->toLettersFrench($this->b); - } - public function getSolution(): int - { - return ($this->a * $this->b); - } - - private function toLettersFrench(int $number): string - { - return match($number){ - 2 => 'deux', - 3 => 'trois', - 4 => 'quatre', - 5 => 'cinq', - 6 => 'six', - 7 => 'sept', - 8 => 'huit', - 9 => 'neuf', - default => '', // erreur - }; - } - - // (à déplacer dans FormValidation?) - static public function controlInput(string $input = '0'): int - { - // un POST est une chaîne qu'on doit convertir en nombre dans deux conditions: - // test de format: $input est un nombre - // test d'intégrité: supprimer les décimales avec (int) ne change pas la valeur du nombre - return is_numeric($input) && $input == (int)$input ? (int)$input : 0; - } -} \ No newline at end of file diff --git a/src/Config.php b/src/Config.php deleted file mode 100644 index 76b34e2..0000000 --- a/src/Config.php +++ /dev/null @@ -1,86 +0,0 @@ -Le fichier config/config.ini n'existe pas ou n'est pas lisible.

"; - } - define('TABLE_PREFIX', self::$table_prefix); - } - - // renseigner les variables internes de Config - static private function hydrate(array $raw_data): void - { - foreach($raw_data as $field => $value) - { - if($value != '') // valeur par défaut - { - if(isset(self::$$field)) // le champ existe dans Config - { - // problème du slash à la fin du nom d'un dossier - $value = self::slashAtEndOfPath($field, $value); - self::$$field = $value; - } - else - { - echo "debug: le fichier config.ini comporte une erreur, le champ: " . $field . " est incorrect,\nl'information contenue sur cette ligne ne sera pas utilisée\n"; - } - } - /*else - { - echo "debug: le champ " . $field . " est vide, la valeur par défaut " . self::$$field . " sera utilisée.\n"; - }*/ - } - } - - - // pour que les chemins finissent toujours par un / - static private function slashAtEndOfPath(string $field, string $value): string - { - foreach(self::$path_vars as $item) - { - if($field === $item){ - return !str_ends_with($value, '/') ? $value . '/' : $value; - } - } - return $value; - } -} diff --git a/src/EmailService.php b/src/EmailService.php deleted file mode 100644 index c6d97b4..0000000 --- a/src/EmailService.php +++ /dev/null @@ -1,102 +0,0 @@ - exceptions - $mail->CharSet = 'UTF-8'; - - $smtp_host = $form_data->getData()['smtp_host'] ?? Config::$smtp_host; - $smtp_secure = $form_data->getData()['smtp_secure'] ?? Config::$smtp_secure; - $smtp_username = $form_data->getData()['smtp_username'] ?? Config::$smtp_username; - $smtp_password = $form_data->getData()['smtp_password'] ?? Config::$smtp_password; - $email_from = $form_data->getData()['email_from'] ?? Config::$email_from; // une adresse bidon est donnée à setFrom() - $email_from_name = $form_data->getData()['email_from_name'] ?? Config::$email_from_name; // = site web - $email_dest = $form_data->getData()['email_dest'] ?? Config::$email_dest; - $email_dest_name = $form_data->getData()['email_dest_name'] ?? Config::$email_dest_name; // = destinataire formulaire - - try{ - // Paramètres du serveur - $mail->isSMTP(); - $mail->Host = $smtp_host; - $mail->SMTPAuth = true; - $mail->Port = 25; - - if($mail->SMTPAuth){ - $mail->Username = $smtp_username; // e-mail - $mail->Password = $smtp_password; - $mail->SMTPSecure = $smtp_secure; // tls (starttls) ou ssl (smtps) - if($mail->SMTPSecure === 'tls'){ - $mail->Port = 587; - } - elseif($mail->SMTPSecure === 'ssl'){ - $mail->Port = 465; - } - } - //var_dump($mail->smtpConnect());die; // test de connexion - - // Expéditeur et destinataire - // $email_from, $email_from_name et $email_dest_name sont modifiables uniquement dans le config.ini pour l'instant - $mail->setFrom(strtolower($email_from), $email_from_name); - $mail->addAddress(strtolower($email_dest), $email_dest_name); - - // Contenu - $mail->isHTML(true); - if($test_email){ - $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web"; - } - else{ - $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web'; - } - $mail->Body = $message; - $mail->AltBody = $message; - - $mail->send(); - - // copie en BDD - if(!$test_email && ($form_data->getData()['keep_emails'] ?? self::KEEP_EMAILS_DEFAULT)){ - $db_email = new Email($name, $email, Config::$email_dest, $message, $form_data); - $entityManager->persist($db_email); - self::updateLastContactDate($entityManager, $email); - $entityManager->flush(); - } - - return true; - } - catch(Exception $e){ - echo "Le message n'a pas pu être envoyé. Erreur : {$e}
{$mail->ErrorInfo}"; - return false; - } - } - - static public function updateLastContactDate(EntityManager $entityManager, string $sender): void - { - foreach($entityManager->getRepository('App\Entity\Email')->findAll() as $email){ - $email->getSenderAddress() === $sender ? $email->updateLastContactDate() : null; - } - } - - // peut être appelée par bin/clean_emails_cron.php - static public function cleanEmails(EntityManager $entityManager): void - { - $emails = $entityManager->getRepository('App\Entity\Email')->findAll(); - foreach($emails as $email){ - if($email->getDeletionDate() < new \DateTime()){ - $entityManager->remove($email); - } - } - $entityManager->flush(); - } -} \ No newline at end of file diff --git a/src/FormValidation.php b/src/FormValidation.php deleted file mode 100644 index b3a3793..0000000 --- a/src/FormValidation.php +++ /dev/null @@ -1,215 +0,0 @@ -data = $data; - $this->validation_strategy = $validation_strategy; - } - - public function validate(): bool - { - $this->errors = []; - - // pattern stratégie en une seule classe - switch($this->validation_strategy){ - // bloc formulaire de contact - case 'email_send': - $this->emailStrategy(); - break; - case 'email_params': // paramètrage en mode admin - $this->emailParamsStrategy(); - break; - - // formulaires pages spéciales - case 'create_user': - $this->createUserStrategy(); - break; - case 'connection': - $this->connectionStrategy(); - break; - case 'username_update': - $this->usernameUpdateStrategy(); - break; - case 'password_update': - $this->passwordUpdateStrategy(); - break; - - default: - http_response_code(500); // c'est un peu comme jeter une exception - echo json_encode(['success' => false, 'error' => 'server_error']); - die; - } - - $this->validated = true; - return empty($this->errors); - } - - public function getErrors(): array - { - return $this->errors; - } - - public function getField(string $field): string - { - return $this->validated ? $this->data[$field] : ''; - } - - // méthodes de validation - private function captchaValidate(bool $clean_session = true): void - { - $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; - $captcha_try = isset($this->data['captcha']) ? Captcha::controlInput($this->data['captcha']) : 0; - if($clean_session){ - unset($_SESSION['captcha']); - } - - if($captcha_try == 0){ - $error = 'error_non_valid_captcha'; - } - elseif($captcha_solution == 0){ // ne peut pas arriver, si? - $error = 'captcha_server_error'; - } - elseif($captcha_try !== $captcha_solution){ - $this->errors[] = 'bad_solution_captcha'; - } - } - - // erreurs à la création des mots de passe - static private function removeSpacesTabsCRLF(string $chaine): string - { - $cibles = [' ', "\t", "\n", "\r"]; // doubles quotes !! - return(str_replace($cibles, '', $chaine)); - } - - - // stratégies - private function emailStrategy(): void - { - $this->captchaValidate(false); - - if(!isset($this->data['name']) || empty($this->data['name']) - || !isset($this->data['email']) || empty($this->data['email']) - || !isset($this->data['message']) || empty($this->data['message']) - || !isset($this->data['hidden']) || !empty($this->data['hidden'])){ - $this->errors[] = 'missing_fields'; - } - - elseif(!filter_var(trim($this->data['email']), FILTER_VALIDATE_EMAIL)){ - $this->errors[] = 'bad_email_address'; - } - - $this->data['name'] = htmlspecialchars(trim($this->data['name'])); - $this->data['email'] = htmlspecialchars(trim($this->data['email'])); - $this->data['message'] = htmlspecialchars($this->data['message']); - } - private function emailParamsStrategy(): void - { - if(!isset($this->data['id'], $this->data['what_param'], $this->data['value'], $this->data['hidden']) - || !empty($this->data['hidden'])){ - $this->errors[] = 'missing_fields'; - } - - elseif($this->data['value'] !== ''){ - if(!in_array($this->data['what_param'], ['smtp_host', 'smtp_secure', 'smtp_username', 'smtp_password', 'email_dest'])){ - $this->errors[] = 'unknown_parameter'; - } - elseif($this->data['what_param'] === 'smtp_username' || $this->data['what_param'] === 'email_dest'){ - if(!filter_var($this->data['value'], FILTER_VALIDATE_EMAIL)){ - $this->errors[] = 'invalide_email_address'; - } - } - } - - // htmlspecialchars exécutés à l'affichage dans FormBuilder - } - private function createUserStrategy(): void - { - $this->captchaValidate(); - - // test mauvais paramètres - if(!isset($this->data['login']) || empty($this->data['login']) - || !isset($this->data['password']) || empty($this->data['password']) - || !isset($this->data['password_confirmation']) || empty($this->data['password_confirmation']) - || !isset($this->data['create_user_hidden']) || !empty($this->data['create_user_hidden'])) - { - $this->errors[] = 'bad_login_or_password'; - } - - if($this->data['password'] !== $this->data['password_confirmation']){ - $this->errors[] = 'different_passwords'; - } - - if($this->data['login'] !== self::removeSpacesTabsCRLF(htmlspecialchars($this->data['login'])) - || $this->data['password'] !== self::removeSpacesTabsCRLF(htmlspecialchars($this->data['password']))){ - $this->errors[] = 'forbidden_characters'; - } - } - private function connectionStrategy(): void - { - $this->captchaValidate(); - - if(!isset($this->data['login']) || empty($this->data['login']) - || !isset($this->data['password']) || empty($this->data['password']) - || !isset($this->data['connection_hidden']) || !empty($this->data['connection_hidden'])) - { - $this->errors[] = 'bad_login_or_password'; - } - } - private function usernameUpdateStrategy(): void - { - $this->captchaValidate(); - - if(!isset($this->data['login']) || empty($this->data['login']) - || !isset($this->data['password']) || empty($this->data['password']) - || !isset($this->data['new_login']) || empty($this->data['new_login']) - || !isset($this->data['modify_username_hidden']) || !empty($this->data['modify_username_hidden'])) - { - $this->errors[] = 'bad_login_or_password'; - } - - $new_login = self::removeSpacesTabsCRLF(htmlspecialchars($this->data['new_login'])); - if($new_login !== $this->data['new_login']){ - $this->errors[] = 'forbidden_characters'; - } - - if($this->data['login'] !== $_SESSION['user']){ - $this->errors[] = 'bad_login_or_password'; - } - if($this->data['login'] === $new_login){ - $this->errors[] = 'same_username_as_before'; - } - } - private function passwordUpdateStrategy(): void - { - $this->captchaValidate(); - - if(!isset($this->data['login']) || empty($this->data['login']) - || !isset($this->data['password']) || empty($this->data['password']) - || !isset($this->data['new_password']) || empty($this->data['new_password']) - || !isset($this->data['modify_password_hidden']) || !empty($this->data['modify_password_hidden'])) - { - $this->errors[] = 'bad_login_or_password'; - } - - $new_password = self::removeSpacesTabsCRLF(htmlspecialchars($this->data['new_password'])); - if($new_password !== $this->data['new_password']){ - $this->errors[] = 'forbidden_characters'; - } - - if($this->data['login'] !== $_SESSION['user']){ - $this->errors[] = 'bad_login_or_password'; - } - if($this->data['password'] === $new_password){ - $this->errors[] = 'same_password_as_before'; - } - } -} \ No newline at end of file diff --git a/src/Security.php b/src/Security.php deleted file mode 100644 index c825994..0000000 --- a/src/Security.php +++ /dev/null @@ -1,110 +0,0 @@ -1, // protection contre les élements et attributs dangereux - - // liste blanche d'éléments HTML - 'elements'=> 'h1, h2, h3, h4, h5, h6, p, br, s, em, span, strong, a, ul, ol, li, sup, sub, code, blockquote, div, pre, table, caption, colgroup, col, tbody, tr, th, td, figure, img, figcaption, iframe, small', - - // liste noire d'attributs HTML - 'deny_attribute'=> 'id, class' // on garde 'style' - ); - // faire qu'un certain élément puisse n'avoir que certains attributs, regarder la doc - private static $specHtmLawed = ''; - - // obtenir du HTML non dangereur sans appliquer htmlspecialchars - public static function secureHTML(string $chaine): string - { - return trim(htmLawed($chaine, self::$configHtmLawed, self::$specHtmLawed)); - } - - public static function secureFileName(string $chaine): string - { - // sécuriser un nom avec chemin avec basename? - //$chaine = basename($chaine); - - /* - - caractères interdits sous windows / \ : * ? " < > | - - mac autorise les / - - mac interdit : - - linux autorise tout sauf les / - - imagemagick ne supporte pas les : - - - 'espace' fonctionne - - / remplacé par firefox en : - - \ retire ce qui est devant le \ - - * fonctionne - - ? permet le téléchargement mais pas l'affichage - - " ne fonctionne pas, remplacé par %22, filtrer %22 - - < > fonctionnent - - | fonctionne - - = fonctionne, mais je filtre parce qu'on en trouve dans une URL - - ' ` fonctionnent - - % fonctionne - - (){}[] fonctionnent - - ^ fonctionne - - # ne fonctionne pas - - ~ fonctionne - - & fonctionne - - ^ pas encore testé - */ - - // => on remplace tout par des _ - // filtrer / et \ semble inutile - - /*$cibles = [' ', '/', '\\', ':', '*', '?', '<', '>', '|', '=', "'", '`', '"', '%22', '#']; - $chaine = str_replace($cibles, '_', $chaine); // nécéssite l'extension mbstring - $chaine = mb_strtolower($chaine); - return($chaine);*/ - - $chaine = preg_replace('/[^a-zA-Z0-9_-]/', '_', $chaine); // ne garder que les lettres, chiffres, tirets et underscores - $chaine = preg_replace('/_+/', '_', $chaine); // doublons d'underscores - return trim($chaine, '_'); - - // les problèmes avec \ persistent !! - // => javascript - // malheureusement document.getElementById('upload').files[0].name = chaine; ne marche pas! interdit! - // javascript ne doit pas pouvoir accéder au système de fichiers - // solutions: - // - au lieu de fournir une chaine (le chemin du fichier), donner un objet à files[0].name - // - créer une copie du fichier et l'envoyer à la place - // - envoyer le fichier en AJAX - // - envoyer le nom du fichier à part puis renommer en PHP - } -} - -// lien sans http:// -function fixLinks($data) -{ - // 1/ - // si une adresse est de type "domaine.fr" sans le http:// devant, le comportement des navigateurs est de rechercher un fichier comme si mon adresse commençait par file:// - // tomber ainsi sur une page d'erreur est parfaitement déroutant - - // regex pour détecter les balises et ajouter http:// au début des liens si nécessaire - $pattern = '#(]+href=")((?!https?://)[^>]+>)#'; - //$data = preg_replace($pattern, '$1http://$2', $data); - - // 2/ - // cas où la regex fait mal son boulot: - // l'erreur 404 est gérée par le .htaccess - // et le visiteur est redirigé à la page "menu" - // (ça ne règle pas le problème mais c'est mieux) - - // 3/ - // quand l'éditeur est ouvert (avant de valider l'article), - // le lien qu'on vient de créer apparaît dans l'infobulle, - // cliquer dessus ouvre un onglet sur une erreur 404 - // solution partielle avec le .htaccess - // - // solution? fermer ce nouvel onglet avec echo ''; - // comment déclencher le JS? en faisant qu'une erreur 404 causée pour cette raison soit particulière? - - return($data); -} diff --git a/src/URL.php b/src/URL.php deleted file mode 100644 index a6d6379..0000000 --- a/src/URL.php +++ /dev/null @@ -1,88 +0,0 @@ -params = $gets; - if($anchor != ''){ - $this->setAnchor($anchor); - } - } - - // setters statiques - static public function setProtocol(string $protocol = 'http'): void - { - self::$protocol = $protocol === 'https' ? 'https://' : 'http://'; - } - static public function setPort(int|string $port = 80): void - { - if((int)$port === 443){ - self::$protocol = 'https://'; - self::$port = ''; - } - elseif((int)$port === 80){ - self::$protocol = 'http://'; - self::$port = ''; - } - else{ - self::$port = ':' . (string)$port; - } - } - static public function setHost(string $host): void - { - self::$host = $host; - } - static public function setPath(string $path): void - { - self::$path = '/' . ltrim($path, '/'); - } - - //setters normaux - public function addParams(array $gets): void - { - // array_merge est préféré à l'opérateur d'union +, si une clé existe déjà la valeur est écrasée - $this->params = array_merge($this->params, $gets); - } - public function setAnchor(string $anchor = ''): void - { - if($anchor != ''){ - $this->anchor = '#' . ltrim($anchor, '#'); - } - else{ - $this->anchor = ''; - } - } - - private function makeParams(): string - { - $output = ''; - $first = true; - - foreach($this->params as $key => $value) { - if($first){ - $output .= '?'; - $first = false; - } - else{ - $output .= '&'; - } - $output .= $key . '=' . $value; - } - return $output; - } - - public function __toString(): string - { - return self::$protocol . self::$host . self::$port . self::$path . $this->makeParams() . $this->anchor; - } -} \ No newline at end of file diff --git a/src/controller/UserController.php b/src/controller/UserController.php index f3c99e7..6928e86 100644 --- a/src/controller/UserController.php +++ b/src/controller/UserController.php @@ -22,14 +22,12 @@ class UserController // account static public function existUsers(EntityManager $entityManager): bool { - $nb_users = $entityManager - ->createQuery('SELECT COUNT(u.id_user) FROM App\Entity\User u') - ->getSingleScalarResult(); - - if($nb_users === 0) // table vide + if(!$entityManager // table vide + ->createQuery("SELECT u FROM App\Entity\User u") + ->setMaxResults(1) + ->getOneOrNullResult()) { - $_SESSION['user'] = ''; - $_SESSION['admin'] = false; + unset($_SESSION['user']); return false; } else{ @@ -38,7 +36,7 @@ class UserController } // account - static public function createUser(EntityManager $entityManager) + static public function createAdminUser(EntityManager $entityManager) { unset($_SESSION['user']); @@ -48,7 +46,7 @@ class UserController $error = ''; if($form->validate()){ $password = password_hash($_POST['password'], PASSWORD_DEFAULT); - $user = new App\Entity\User($_POST['login'], $password); + $user = new User($_POST['login'], 'admin', $password); $entityManager->persist($user); $entityManager->flush(); } @@ -64,31 +62,34 @@ class UserController die; } + // account + //static public function createUser(EntityManager $entityManager){} + // auth static public function connect(EntityManager $entityManager): void { - if($_SESSION['admin']) // déjà connecté? + if(IS_ADMIN) // déjà connecté? { header('Location: ' . new URL); die; } - $_SESSION['user'] = ''; - $_SESSION['admin'] = false; + unset($_SESSION['user']); $form = new FormValidation($_POST, 'connection'); $error = ''; if($form->validate()){ // à mettre dans une classe métier UserService, Authentication, AuthService? - $user = self::getUser($_POST['login'], $entityManager); + $user = self::getUserByName($_POST['login'], $entityManager); if(!empty($user) && $_POST['login'] === $user->getLogin() && password_verify($_POST['password'], $user->getPassword())) { $log = new Log(true); // protection fixation de session, si l'attaquant crée un cookie de session, il est remplacé session_regenerate_id(true); - $_SESSION['user'] = $_POST['login']; - $_SESSION['admin'] = true; + $_SESSION['user']['id'] = $user->getId(); + $_SESSION['user']['username'] = $user->getLogin(); + $_SESSION['user']['role'] = $user->getRole(); EmailService::cleanEmails($entityManager); @@ -123,7 +124,7 @@ class UserController static public function disconnect(): void { // nettoyage complet - $_SESSION = []; // mémoire vive + unset($_SESSION['user']); // mémoire vive session_destroy(); // fichier côté serveur setcookie('PHPSESSID', '', time() - 86400, '/'); // cookie de session @@ -138,7 +139,7 @@ class UserController // user static public function updateUsername(EntityManager $entityManager): void { - if(!$_SESSION['admin']){ // superflux, fait dans le routeur + if(!IS_ADMIN){ // superflux, fait dans le routeur self::disconnect(); } @@ -150,11 +151,11 @@ class UserController $error = ''; if($form->validate()){ // à mettre dans une classe métier UserService? - $user = self::getUser($_POST['login'], $entityManager); + $user = self::getUserByName($_POST['login'], $entityManager); if(password_verify($_POST['password'], $user->getPassword())){ $user->setLogin($_POST['new_login']); $entityManager->flush(); - $_SESSION['user'] = $_POST['new_login']; + $_SESSION['user']['username'] = $_POST['new_login']; $url->addParams(['success_username' => 'new_login']); $error = ''; @@ -178,7 +179,7 @@ class UserController // user static public function updatePassword(EntityManager $entityManager): void { - if(!$_SESSION['admin']){ // superflux, fait dans le routeur + if(!IS_ADMIN){ // superflux, fait dans le routeur self::disconnect(); } @@ -190,7 +191,7 @@ class UserController $error = ''; if($form->validate()){ // à mettre dans une classe métier UserService? - $user = self::getUser($_POST['login'], $entityManager); + $user = self::getUserByName($_POST['login'], $entityManager); if(password_verify($_POST['password'], $user->getPassword())){ $new_password = password_hash($_POST['new_password'], PASSWORD_DEFAULT); $user->setPassword($new_password); @@ -216,26 +217,22 @@ class UserController } // dans une classe mère ou un trait après découpage de UserController? - static private function getUser(string $login, EntityManager $entityManager): ?User + static private function getUserByName(string $login, EntityManager $entityManager): ?User { - $users = $entityManager->getRepository('App\Entity\User')->findBy(['login' => $login]); - - if(count($users) === 0) - { - $_SESSION['user'] = ''; - $_SESSION['admin'] = false; - } - - foreach($users as $user) - { - if($user->getLogin() === $login) - { + $users = $entityManager->getRepository(User::class)->findBy(['login' => $login]); + foreach($users as $user){ + if($user->getLogin() === $login){ return $user; } } return null; } + static public function getUserById(int $id, EntityManager $entityManager): ?User + { + return $entityManager->find(User::class, $id); + } + // dans une classe Form? // erreurs à la création des mots de passe static private function removeSpacesTabsCRLF(string $chaine): string diff --git a/src/controller/ViewController.php b/src/controller/ViewController.php index 8c95526..cf3477c 100644 --- a/src/controller/ViewController.php +++ b/src/controller/ViewController.php @@ -21,7 +21,7 @@ class ViewController extends AbstractBuilder // ViewController est aussi le prem /* 1/ 1er contrôle des paramètres */ // mode modification d'une page - if($_SESSION['admin'] + if(IS_ADMIN && $request->query->has('mode') && $request->query->get('mode') === 'page_modif' && !in_array(CURRENT_PAGE, ['article', 'new_page', 'menu_paths', 'user_edit', 'connection'])) { @@ -29,7 +29,7 @@ class ViewController extends AbstractBuilder // ViewController est aussi le prem } // page article: mode création et erreurs d'id if(CURRENT_PAGE === 'article'){ - if($_SESSION['admin']){ + if(IS_ADMIN){ if(!$request->query->has('id')){ return new Response($this->html, 302); } @@ -58,7 +58,7 @@ class ViewController extends AbstractBuilder // ViewController est aussi le prem /* 3/ 2ème contrôle des paramètres avec les données récupérées */ // article non trouvé en BDD - if(CURRENT_PAGE === 'article' && !$_SESSION['admin'] && self::$root_node->getNodeByName('main')->getAdoptedChild() === null){ + if(CURRENT_PAGE === 'article' && !IS_ADMIN && self::$root_node->getNodeByName('main')->getAdoptedChild() === null){ return new Response($this->html, 302); } diff --git a/src/installation.php b/src/installation.php deleted file mode 100644 index 2bef8e5..0000000 --- a/src/installation.php +++ /dev/null @@ -1,161 +0,0 @@ -l'extension " . $extension . ' est manquante

'); - $flag = true; - } - } - if(!extension_loaded('imagick') && !extension_loaded('gd')){ - echo("

il manque une de ces extensions au choix pour le traitement des images: imagick (de préférence) ou gd.

"); - $flag = true; - } - if($flag){ - echo '

Réalisez les actions nécéssaires sur le serveur ou contactez l\'administrateur du site.
- Quand le problème sera résolu, il vous suffira de
recharger la page.

'; - die; - } -} - -function installation(): void -{ - // -- droits des fichiers et dossiers -- - $droits_dossiers = 0700; - $droits_fichiers = 0600; - - if(!file_exists('user_data')){ - // créer le dossier user_data - mkdir('user_data/'); - chmod('user_data/', $droits_dossiers); - echo '

Le dossier public/user_data introuvable et le serveur n\'a pas la permission de le créer.
- Pour faire ça bien:
sudo -u "serveur web" mkdir /chemin/du/site/public/user_data

-

Aide: "serveur web" se nomme "www-data" sur debian et ubuntu, il s\'appelera "http" sur d\'autres distributions.

'; - die; - } - - if(!file_exists('../config/config.ini')){ - // aide à la création du config.ini - echo '

Ce fichier contient les codes de la base de données et quelques paramètres utilisés pour créer les liens internes.
- Un modèle est disponible, il s\'agit du fichier config/config-template.ini

-

Quand vous aurez terminé votre config.ini, donnez-lui si possible des droits 600.

'; - die; - } -} - -/* - // droits du config.ini - if(substr(sprintf('%o', fileperms('../config/config.ini')), -4) != 600){ - chmod('../config/config.ini', $droits_fichiers); - } - - // création de data et sous-dossiers - if(!file_exists('../data')){ - mkdir('../data/'); - chmod('../data/', $droits_dossiers); - } - if(!touch('../data')){ - echo("dossier data non autorisé en écriture"); - die; - } - $sous_dossiers = array('images', 'images-mini', 'videos'); - foreach ($sous_dossiers as $sous_dossier){ - if(!file_exists('../data/' . $sous_dossier)){ - mkdir('../data/' . $sous_dossier); - chmod('../data/' . $sous_dossier, $droits_dossiers); - } - if(!touch('../data/' . $sous_dossier)){ - echo("dossier data non autorisé en écriture"); - die; - } - } -*/ - -// création d'un site minimal avec une page d'accueil à la toute 1ère visite du site -// fonctiona appelée après la première requête envoyée en BDD, -// en l'occurence dans Menu parce que count($bulk_data) === 0 -function fillStartingDatabase(EntityManager $entityManager){ - /* -- table page -- */ - // paramètres: name_page, end_of_path, reachable, in_menu, hidden, position, parent - $accueil = new Page('Accueil', 'accueil', "Page d'accueil", true, true, false, 1, NULL); - $article = new Page('Article', 'article', "", true, false, false, NULL, NULL); - $connection = new Page('Connexion', 'connection', "Connexion", true, false, false, NULL, NULL); - $my_account = new Page('Mon compte', 'user_edit', "Mon compte", true, false, false, NULL, NULL); - $menu_paths = new Page("Menu et chemins", 'menu_paths', "Menu et chemins", true, false, false, NULL, NULL); - $menu_paths->addCSS('menu'); - $menu_paths->addJS('menu'); - $new_page = new Page('Nouvelle page', 'new_page', "Nouvelle page", true, false, false, NULL, NULL); - $new_page->addCSS('new_page'); - $new_page->addJS('new_page'); - $emails = new Page("Courriels", 'emails', "Consulter les courriels en base de données", true, false, false, NULL, NULL); - $emails->addCSS('show_emails'); - $emails->addJS('form'); - - /* -- table node -- */ - // paramètres: name_node, article_timestamp, attributes, position, parent, page, article - $head = new Node('head', 1, NULL, NULL, NULL); - $header = new Node('header', 2, NULL, NULL, NULL); - $nav = new Node('nav', 1, $header, NULL, NULL); - $main = new Node('main', 3, NULL, NULL, NULL); - $footer = new Node('footer', 4, NULL, NULL, NULL); - $breadcrumb = new Node('breadcrumb', 2, $header, NULL, NULL); - $login = new Node('login', 1, $main, $connection, NULL); - $user_edit = new Node('user_edit', 1, $main, $my_account, NULL); - $bloc_edit_menu = new Node('menu', 1, $main, $menu_paths, NULL); - $bloc_new_page = new Node('new_page', 1, $main, $new_page, NULL); - $bloc_emails = new Node('show_emails', 1, $main, $emails, NULL); - - /* -- table node_data -- */ - // paramètres: data, node, images - $head_data = new NodeData([], $head); - $header_data = new NodeData([], $header); - $footer_data = new NodeData([], $footer); - $emails_data = new NodeData([], $bloc_emails); - - /* -- table page -- */ - $entityManager->persist($accueil); - $entityManager->persist($article); - $entityManager->persist($connection); - $entityManager->persist($my_account); - $entityManager->persist($menu_paths); - $entityManager->persist($new_page); - $entityManager->persist($emails); - - /* -- table node -- */ - $entityManager->persist($head); - $entityManager->persist($header); - $entityManager->persist($nav); - $entityManager->persist($main); - $entityManager->persist($footer); - $entityManager->persist($breadcrumb); - $entityManager->persist($login); - $entityManager->persist($user_edit); - $entityManager->persist($bloc_edit_menu); - $entityManager->persist($bloc_new_page); - $entityManager->persist($bloc_emails); - - /* -- table node_data -- */ - $entityManager->persist($head_data); - $entityManager->persist($header_data); - $entityManager->persist($footer_data); - $entityManager->persist($emails_data); - - $entityManager->flush(); - header('Location: ' . new URL); - die; -} \ No newline at end of file diff --git a/src/model/Menu.php b/src/model/Menu.php index 3d07d3f..d277259 100644 --- a/src/model/Menu.php +++ b/src/model/Menu.php @@ -18,10 +18,6 @@ class Menu extends Page ->createQuery('SELECT n FROM App\Entity\Page n WHERE n.parent IS null') // :Doctrine\ORM\Query ->getResult(); // :array de Page - if(count($bulk_data) === 0){ - fillStartingDatabase($entityManager); // => installation.php - } - foreach($bulk_data as $first_level_entries){ // dans le menu if($first_level_entries->isInMenu()){ diff --git a/src/model/entities/AppMetadata.php b/src/model/entities/AppMetadata.php new file mode 100644 index 0000000..ae42ad9 --- /dev/null +++ b/src/model/entities/AppMetadata.php @@ -0,0 +1,46 @@ +key_name = $key; + $this->value = $value; + } + + public function getKey(): string + { + return $this->key_name; + } + public function getValue(): string + { + return $this->value; + } + + public function setKey(string $key): void + { + $this->key_name = $key; + } + public function setValue(string $value): void + { + $this->value = $value; + } +} \ No newline at end of file diff --git a/src/model/entities/User.php b/src/model/entities/User.php index 26802e2..36bc4db 100644 --- a/src/model/entities/User.php +++ b/src/model/entities/User.php @@ -22,15 +22,24 @@ class User #[ORM\Column(type: "string", length: 255, unique: true)] // risque de modifier son mot de passe sans s'apercevoir qu'il fonctionne encore sur un autre compte private string $login; + #[ORM\Column(type: "string", length: 50)] + private string $role; + #[ORM\Column(type: "string", length: 255)] private string $password; - public function __construct(string $login, string $password) + public function __construct(string $login, string $role, string $password) { $this->login = $login; + $this->role = $role; $this->password = $password; } + public function getId(): int + { + return $this->id_user; + } + public function getLogin(): string { return $this->login; @@ -39,6 +48,14 @@ class User { $this->login = $login; } + public function getRole(): string + { + return $this->role; + } + public function setRole(string $role): void + { + $this->role = $role; + } public function getPassword(): string { return $this->password; diff --git a/src/router.php b/src/router.php deleted file mode 100644 index ff219da..0000000 --- a/src/router.php +++ /dev/null @@ -1,384 +0,0 @@ - 1er test, méthode http: GET, POST ou autre chose -=> 2ème test, type de contenu (méthode POST uniquement): -"application/x-www-form-urlencoded" = formulaire -"application/json" = requête AJAX avec fetch() -"multipart/form-data" = upload d'image par tinymce -$_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' requête AJAX xhs, non utilisée -=> 3ème test, comme le 2ème test mais uniquement si $_SESSION['admin'] est vrai -*/ - -declare(strict_types=1); - -if($request->getMethod() === 'GET'){ - // table "user" vide - if(!UserController::existUsers($entityManager)){ - require AbstractBuilder::VIEWS_PATH . 'user_create.php'; - die; - } - - // bouton déconnexion (méthode GET parce que l'utilisateur ne modifie plus de données à partir de là) - if($request->query->has('action') && $request->query->get('action') === 'deconnection'){ - UserController::disconnect($entityManager); - } - - // articles suivants - if($request->query->has('fetch') && $request->query->get('fetch') === 'next_articles'){ - ArticleController::fetch($entityManager, $request); - } - - // données du calendrier - // création du calendrier et changement de dates affichées (boutons flèches mais pas changement de vue) - if($request->query->has('action') && $request->query->get('action') === 'get_events' - && $request->query->has('start') && $request->query->has('end') && empty($request->getPayload()->all())) // getPayload ne récupère pas que des POST - { - CalendarController::getData($entityManager); - } - - // pages interdites - if(!$_SESSION['admin'] && in_array(CURRENT_PAGE, ['menu_paths', 'new_page', 'user_edit', 'emails'])){ - header('Location: ' . new URL); - die; - } - - if($_SESSION['admin'] === true){ - // ... - } - - // construction d'une page - $response = (new ViewController)->buildView($entityManager, $request); // utilise Model - // parenthèses nécéssaires autour de l'instanciation pour PHP < 8.4 -} - - -elseif($request->getMethod() === 'POST'){ - /* -- contrôleurs appellables par tout le monde -- */ - - // table "user" vide - if(!UserController::existUsers($entityManager)){ - UserController::createUser($entityManager); - } - - // requêtes JSON avec fetch() - if($_SERVER['CONTENT_TYPE'] === 'application/json') - { - $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true); - - if(isset($_GET['action'])) - { - // formulaire de contact - if($_GET['action'] === 'send_email'){ - ContactFormController::sendVisitorEmail($entityManager, $json); - } - } - } - - // envoi formulaire HTML - elseif($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ - // tentative de connexion - if($request->query->has('action') && $request->query->get('action') === 'connection'){ - //$response = - UserController::connect($entityManager); - } - } - - - if($_SESSION['admin'] === true) - { - /* -- requêtes AJAX -- */ - - // requêtes JSON avec fetch() - if($_SERVER['CONTENT_TYPE'] === 'application/json') - { - $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true); - - if($request->query->has('action')) - { - /* -- manipulation des articles -- */ - if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content'])){ - ArticleController::editorSubmit($entityManager, $json); - } - elseif($_GET['action'] === 'delete_article' && isset($json['id'])){ - $response = ArticleController::deleteArticle($entityManager, $json); // version AJAX - } - elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){ - ArticleController::switchPositions($entityManager, $json); - } - elseif($_GET['action'] === 'date_submit' && isset($json['id']) && isset($json['date'])){ - ArticleController::dateSubmit($entityManager, $json); - } - - /* -- bloc Formulaire -- */ - elseif($_GET['action'] === 'keep_emails'){ - ContactFormController::keepEmails($entityManager, $json); - } - elseif($_GET['action'] === 'set_retention_period'){ - ContactFormController::setEmailsRetentionPeriod($entityManager, $json); - } - elseif($_GET['action'] === 'set_email_param'){ - ContactFormController::setEmailParam($entityManager, $json); - } - elseif($_GET['action'] === 'test_email'){ - ContactFormController::sendTestEmail($entityManager, $json); - } - - /* -- page emails -- */ - elseif($_GET['action'] === 'delete_email'){ - ContactFormController::deleteEmail($entityManager, $json); - } - elseif($_GET['action'] === 'toggle_sensitive_email'){ - ContactFormController::toggleSensitiveEmail($entityManager, $json); - } - - /* -- upload d'image dans tinymce par copier-coller -- */ - // collage de HTML contenant une ou plusieurs balises - elseif($request->query->get('action') === 'upload_image_url'){ - ImageUploadController::uploadImageHtml(); - } - // collage d'une image (code base64 dans le presse-papier) non encapsulée dans du HTML - elseif($request->query->get('action') === 'upload_image_base64'){ - ImageUploadController::uploadImageBase64(); - } - - - /* -- requêtes spécifiques au calendrier -- */ - elseif($request->query->get('action') === 'new_event'){ - CalendarController::newEvent($json, $entityManager); - } - elseif($request->query->get('action') === 'update_event'){ - CalendarController::updateEvent($json, $entityManager); - } - elseif($request->query->get('action') === 'remove_event'){ - CalendarController::removeEvent($json, $entityManager); - } - else{ - echo json_encode(['success' => false]); - die; - } - } - - /* -- site entier (header, footer, favicon) -- */ - elseif($request->query->has('head_foot_text')){ - HeadFootController::setTextData($entityManager, $request->query->get('head_foot_text'), $json); - } - elseif($request->query->has('head_foot_social_check')){ - HeadFootController::displaySocialNetwork($entityManager, $request->query->get('head_foot_social_check'), $json); - } - - /* -- page Menu et chemins -- */ - elseif(isset($_GET['menu_edit'])) - { - // ne suit pas la règle, faire ça dans un contrôleur? - Model::$menu = new Menu($entityManager); // récupération des données - - // 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'])){ - MenuAndPathsController::MoveOneLevelUp($entityManager, $json); - } - // flèche droite =>: position = nombre d'éléments de la fraterie + 1, l'élément précédent devient le parent - elseif($_GET['menu_edit'] === 'move_one_level_down' && isset($json['id'])){ - MenuAndPathsController::MoveOneLevelDown($entityManager, $json); - } - elseif($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){ - MenuAndPathsController::switchPositions($entityManager, $json); - } - elseif($_GET['menu_edit'] === 'display_in_menu' && isset($json['id']) && isset($json['checked'])){ - MenuAndPathsController::displayInMenu($entityManager, $json); - } - elseif($_GET['menu_edit'] === 'url_edit' && isset($json['id']) && isset($json['field']) && isset($json['input_data'])){ - MenuAndPathsController::editUrl($entityManager, $json); - } - } - - /* -- mode Modification d'une page -- */ - // partie "page" - elseif(isset($_GET['page_edit'])) - { - // titre de la page - if($_GET['page_edit'] === 'page_title'){ - PageManagementController::setPageTitle($entityManager, $json); - } - // description dans les métadonnées - elseif($_GET['page_edit'] === 'page_description'){ - PageManagementController::setPageDescription($entityManager, $json); - } - } - - // partie "blocs" - elseif($request->query->has('bloc_edit')) - { - if($request->query->get('bloc_edit') === 'rename_page_bloc'){ - PageManagementController::renameBloc($entityManager, $json); - } - elseif($request->query->get('bloc_edit') === 'switch_blocs_positions'){ - PageManagementController::SwitchBlocsPositions($entityManager, $json); - } - elseif($request->query->get('bloc_edit') === 'change_articles_order'){ - PageManagementController::changeArticlesOrder($entityManager, $json); - } - elseif($request->query->get('bloc_edit') === 'change_presentation'){ - PageManagementController::changePresentation($entityManager, $json); - } - elseif($request->query->get('bloc_edit') === 'change_cols_min_width'){ - PageManagementController::changeColsMinWidth($entityManager, $json); - } - elseif($request->query->get('bloc_edit') === 'change_pagination_limit'){ - PageManagementController::changePaginationLimit($entityManager, $json); - } - } - } - - // upload avec FormData - elseif(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false) - { - // dans tinymce avec le plugin (bouton "insérer une image" de l'éditeur ou glisser-déposer) - if($request->query->has('action') && $request->query->get('action') === 'upload_image_tinymce'){ - ImageUploadController::imageUploadTinyMce(); - } - // dans tinymce, des quatre méthodes: bouton "link", drag & drop, html, base64 - elseif($request->query->has('action') && $request->query->get('action') === 'upload_file_tinymce'){ - FileUploadController::fileUploadTinyMce(); - } - elseif($request->query->has('head_foot_image')){ - HeadFootController::uploadAsset($entityManager, $request->query->get('head_foot_image')); - } - } - - // requêtes XMLHttpRequest - elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') - { - //echo "requête XMLHttpRequest reçue par le serveur"; - echo json_encode(['success' => false]); // noyer le poisson en laissant penser que le site gère les requêtes XHR - die; - } - - /* -- envoi formulaire HTML -- */ - elseif($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded') - { - if($request->query->has('action') && $request->query->get('action') === 'delete_article' && isset($_GET['id'])){ - $response = ArticleController::deleteArticle($entityManager, $_GET); // version formulaire - } - - /* -- nouvelle page -- */ - elseif(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'] === '') - { - PageManagementController::newPage($entityManager, $_POST); - } - - /* -- suppression d'une page -- */ - elseif(isset($_POST['page_id']) && $_POST['page_id'] !== null - && isset($_POST['submit_hidden']) && $_POST['submit_hidden'] === '') - { - PageManagementController::deletePage($entityManager); - } - - - /* -- mode Modification d'une page -- */ - - // modification du chemins en snake_case - 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'] === '') - { - PageManagementController::updatePageMenuPath($entityManager); - } - // 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 - { - PageManagementController::addBloc($entityManager); - } - // 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 - { - PageManagementController::deleteBloc($entityManager); - } - - - /* -- 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"])){ - MenuAndPathsController::newUrlMenuEntry($entityManager); - } - // 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 - MenuAndPathsController::deleteUrlMenuEntry($entityManager); - } - - - /* -- page Mon compte -- */ - elseif($request->query->has('action') && $request->query->get('action') === 'update_username') - { - UserController::updateUsername($entityManager); - } - elseif($request->query->has('action') && $request->query->get('action') === 'update_password') - { - UserController::updatePassword($entityManager); - } - - // redirection page d'accueil - else{ - header("Location: " . new URL(['error' => 'paramètres inconnus'])); - die; - } - } - // POST admin ne matchant pas - else{ - echo json_encode(['success' => false]); - die; - } - } - // POST non admin ne matchant pas - else{ - echo json_encode(['success' => false]); - die; - } -} - -// méthode inconnue -else{ - header("Location: " . new URL(['error' => 'tu fais quoi là mec?'])); - die; -} - - - -/* -- utilisation de la réponse -- */ -if(isset($response)){ - // cas gérés (d'autres sont à prévoir): mauvais id de la page article, accès page création d'article sans être admin - if($request->isMethod('GET') && $response->getStatusCode() == 302){ // 302 redirection temporaire - header('Location: ' . new URL(['page' => $_GET['from'] ?? ''])); - } - // redirection après traitement de formulaires HTTP - elseif($request->getMethod() === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ - $response_data = json_decode(($response)->getContent(), true); - $url = new URL(['page' => $_GET['from'] ?? '']); - $url->addParams(['success' => $response_data['success'], 'message' => $response_data['message']]); - header('Location: ' . $url); - } - // affichage d'une page OU requête AJAX - else{ - $response->send(); - } -} -// pas utilisation de RESPONSE (cas destiné à disparaître) -else{ - if($request->getMethod() === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ - header("Location: " . new URL(['error' => 'erreur côté serveur'])); - } - else{ - http_response_code(500); - echo "erreur côté serveur"; - } -} -//die; // inutile \ No newline at end of file diff --git a/src/service/AppMode.php b/src/service/AppMode.php new file mode 100644 index 0000000..60b58bd --- /dev/null +++ b/src/service/AppMode.php @@ -0,0 +1,56 @@ +getRepository(AppMetadata::class)->find('mode'); + if(!$metadata){ + self::$mode = 'maintenance'; + } + else{ + self::$mode = $metadata->getValue(); + } + } + + public static function is(string $mode): bool + { + return self::$mode === $mode; + } + + public static function get(): string + { + return self::$mode; + } + + public static function set(EntityManager $entityManager, string $mode): void + { + self::$mode = $mode; + + $metadata = $entityManager->find(AppMetadata::class, 'mode'); + if($metadata){ + $metadata->setValue($mode); + } + else{ + $metadata = new AppMetadata('mode', $mode); + $entityManager->persist($metadata); + } + $entityManager->flush(); + + /*self::$data = [ + 'mode' => $mode, + 'since' => (new DateTimeImmutable())->format('c'), + 'by' => $by, + ];*/ + } +} \ No newline at end of file diff --git a/src/service/Captcha.php b/src/service/Captcha.php new file mode 100644 index 0000000..d57f912 --- /dev/null +++ b/src/service/Captcha.php @@ -0,0 +1,55 @@ + on pourrait appliquer le pattern "singleton" (justification = le captcha devient une sorte de ressource partagée) + +declare(strict_types=1); + +class Captcha +{ + private int $a; + private int $b; + + public function __construct(){ + $this->a = rand(2, 9); + $this->b = rand(2, 9); + } + + public function getA(): string + { + return $this->toLettersFrench($this->a); + } + public function getB(): string + { + return $this->toLettersFrench($this->b); + } + public function getSolution(): int + { + return ($this->a * $this->b); + } + + private function toLettersFrench(int $number): string + { + return match($number){ + 2 => 'deux', + 3 => 'trois', + 4 => 'quatre', + 5 => 'cinq', + 6 => 'six', + 7 => 'sept', + 8 => 'huit', + 9 => 'neuf', + default => '', // erreur + }; + } + + // (à déplacer dans FormValidation?) + static public function controlInput(string $input = '0'): int + { + // un POST est une chaîne qu'on doit convertir en nombre dans deux conditions: + // test de format: $input est un nombre + // test d'intégrité: supprimer les décimales avec (int) ne change pas la valeur du nombre + return is_numeric($input) && $input == (int)$input ? (int)$input : 0; + } +} \ No newline at end of file diff --git a/src/service/Config.php b/src/service/Config.php new file mode 100644 index 0000000..e59f728 --- /dev/null +++ b/src/service/Config.php @@ -0,0 +1,86 @@ +Le fichier config/config.ini n'existe pas ou n'est pas lisible.

"; + } + define('TABLE_PREFIX', self::$table_prefix); + } + + // renseigner les variables internes de Config + static private function hydrate(array $raw_data): void + { + foreach($raw_data as $field => $value) + { + if($value != '') // valeur par défaut + { + if(isset(self::$$field)) // le champ existe dans Config + { + // problème du slash à la fin du nom d'un dossier + $value = self::slashAtEndOfPath($field, $value); + self::$$field = $value; + } + else + { + echo "debug: le fichier config.ini comporte une erreur, le champ: " . $field . " est incorrect,\nl'information contenue sur cette ligne ne sera pas utilisée\n"; + } + } + /*else + { + echo "debug: le champ " . $field . " est vide, la valeur par défaut " . self::$$field . " sera utilisée.\n"; + }*/ + } + } + + + // pour que les chemins finissent toujours par un / + static private function slashAtEndOfPath(string $field, string $value): string + { + foreach(self::$path_vars as $item) + { + if($field === $item){ + return !str_ends_with($value, '/') ? $value . '/' : $value; + } + } + return $value; + } +} diff --git a/src/service/EmailService.php b/src/service/EmailService.php new file mode 100644 index 0000000..6f4e93d --- /dev/null +++ b/src/service/EmailService.php @@ -0,0 +1,102 @@ + exceptions + $mail->CharSet = 'UTF-8'; + + $smtp_host = $form_data->getData()['smtp_host'] ?? Config::$smtp_host; + $smtp_secure = $form_data->getData()['smtp_secure'] ?? Config::$smtp_secure; + $smtp_username = $form_data->getData()['smtp_username'] ?? Config::$smtp_username; + $smtp_password = $form_data->getData()['smtp_password'] ?? Config::$smtp_password; + $email_from = $form_data->getData()['email_from'] ?? Config::$email_from; // une adresse bidon est donnée à setFrom() + $email_from_name = $form_data->getData()['email_from_name'] ?? Config::$email_from_name; // = site web + $email_dest = $form_data->getData()['email_dest'] ?? Config::$email_dest; + $email_dest_name = $form_data->getData()['email_dest_name'] ?? Config::$email_dest_name; // = destinataire formulaire + + try{ + // Paramètres du serveur + $mail->isSMTP(); + $mail->Host = $smtp_host; + $mail->SMTPAuth = true; + $mail->Port = 25; + + if($mail->SMTPAuth){ + $mail->Username = $smtp_username; // e-mail + $mail->Password = $smtp_password; + $mail->SMTPSecure = $smtp_secure; // tls (starttls) ou ssl (smtps) + if($mail->SMTPSecure === 'tls'){ + $mail->Port = 587; + } + elseif($mail->SMTPSecure === 'ssl'){ + $mail->Port = 465; + } + } + //var_dump($mail->smtpConnect());die; // test de connexion + + // Expéditeur et destinataire + // $email_from, $email_from_name et $email_dest_name sont modifiables uniquement dans le config.ini pour l'instant + $mail->setFrom(strtolower($email_from), $email_from_name); + $mail->addAddress(strtolower($email_dest), $email_dest_name); + + // Contenu + $mail->isHTML(true); + if($test_email){ + $mail->Subject = "TEST d'un envoi d'e-mail depuis le site web"; + } + else{ + $mail->Subject = 'Message envoyé par: ' . $name . ' (' . $email . ') depuis le site web'; + } + $mail->Body = $message; + $mail->AltBody = $message; + + $mail->send(); + + // copie en BDD + if(!$test_email && ($form_data->getData()['keep_emails'] ?? self::KEEP_EMAILS_DEFAULT)){ + $db_email = new Email($name, $email, Config::$email_dest, $message, $form_data); + $entityManager->persist($db_email); + self::updateLastContactDate($entityManager, $email); + $entityManager->flush(); + } + + return true; + } + catch(Exception $e){ + echo "Le message n'a pas pu être envoyé. Erreur : {$e}
{$mail->ErrorInfo}"; + return false; + } + } + + static public function updateLastContactDate(EntityManager $entityManager, string $sender): void + { + foreach($entityManager->getRepository('App\Entity\Email')->findAll() as $email){ + $email->getSenderAddress() === $sender ? $email->updateLastContactDate() : null; + } + } + + // peut être appelée par bin/clean_emails_cron.php + static public function cleanEmails(EntityManager $entityManager): void + { + $emails = $entityManager->getRepository('App\Entity\Email')->findAll(); + foreach($emails as $email){ + if($email->getDeletionDate() < new \DateTime()){ + $entityManager->remove($email); + } + } + $entityManager->flush(); + } +} \ No newline at end of file diff --git a/src/service/FormValidation.php b/src/service/FormValidation.php new file mode 100644 index 0000000..4677bef --- /dev/null +++ b/src/service/FormValidation.php @@ -0,0 +1,215 @@ +data = $data; + $this->validation_strategy = $validation_strategy; + } + + public function validate(): bool + { + $this->errors = []; + + // pattern stratégie en une seule classe + switch($this->validation_strategy){ + // bloc formulaire de contact + case 'email_send': + $this->emailStrategy(); + break; + case 'email_params': // paramètrage en mode admin + $this->emailParamsStrategy(); + break; + + // formulaires pages spéciales + case 'create_user': + $this->createUserStrategy(); + break; + case 'connection': + $this->connectionStrategy(); + break; + case 'username_update': + $this->usernameUpdateStrategy(); + break; + case 'password_update': + $this->passwordUpdateStrategy(); + break; + + default: + http_response_code(500); // c'est un peu comme jeter une exception + echo json_encode(['success' => false, 'error' => 'server_error']); + die; + } + + $this->validated = true; + return empty($this->errors); + } + + public function getErrors(): array + { + return $this->errors; + } + + public function getField(string $field): string + { + return $this->validated ? $this->data[$field] : ''; + } + + // méthodes de validation + private function captchaValidate(bool $clean_session = true): void + { + $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; + $captcha_try = isset($this->data['captcha']) ? Captcha::controlInput($this->data['captcha']) : 0; + if($clean_session){ + unset($_SESSION['captcha']); + } + + if($captcha_try == 0){ + $error = 'error_non_valid_captcha'; + } + elseif($captcha_solution == 0){ // ne peut pas arriver, si? + $error = 'captcha_server_error'; + } + elseif($captcha_try !== $captcha_solution){ + $this->errors[] = 'bad_solution_captcha'; + } + } + + // erreurs à la création des mots de passe + static private function removeSpacesTabsCRLF(string $chaine): string + { + $cibles = [' ', "\t", "\n", "\r"]; // doubles quotes !! + return(str_replace($cibles, '', $chaine)); + } + + + // stratégies + private function emailStrategy(): void + { + $this->captchaValidate(false); + + if(!isset($this->data['name']) || empty($this->data['name']) + || !isset($this->data['email']) || empty($this->data['email']) + || !isset($this->data['message']) || empty($this->data['message']) + || !isset($this->data['hidden']) || !empty($this->data['hidden'])){ + $this->errors[] = 'missing_fields'; + } + + elseif(!filter_var(trim($this->data['email']), FILTER_VALIDATE_EMAIL)){ + $this->errors[] = 'bad_email_address'; + } + + $this->data['name'] = htmlspecialchars(trim($this->data['name'])); + $this->data['email'] = htmlspecialchars(trim($this->data['email'])); + $this->data['message'] = htmlspecialchars($this->data['message']); + } + private function emailParamsStrategy(): void + { + if(!isset($this->data['id'], $this->data['what_param'], $this->data['value'], $this->data['hidden']) + || !empty($this->data['hidden'])){ + $this->errors[] = 'missing_fields'; + } + + elseif($this->data['value'] !== ''){ + if(!in_array($this->data['what_param'], ['smtp_host', 'smtp_secure', 'smtp_username', 'smtp_password', 'email_dest'])){ + $this->errors[] = 'unknown_parameter'; + } + elseif($this->data['what_param'] === 'smtp_username' || $this->data['what_param'] === 'email_dest'){ + if(!filter_var($this->data['value'], FILTER_VALIDATE_EMAIL)){ + $this->errors[] = 'invalide_email_address'; + } + } + } + + // htmlspecialchars exécutés à l'affichage dans FormBuilder + } + private function createUserStrategy(): void + { + $this->captchaValidate(); + + // test mauvais paramètres + if(!isset($this->data['login']) || empty($this->data['login']) + || !isset($this->data['password']) || empty($this->data['password']) + || !isset($this->data['password_confirmation']) || empty($this->data['password_confirmation']) + || !isset($this->data['create_user_hidden']) || !empty($this->data['create_user_hidden'])) + { + $this->errors[] = 'bad_login_or_password'; + } + + if($this->data['password'] !== $this->data['password_confirmation']){ + $this->errors[] = 'different_passwords'; + } + + if($this->data['login'] !== self::removeSpacesTabsCRLF(htmlspecialchars($this->data['login'])) + || $this->data['password'] !== self::removeSpacesTabsCRLF(htmlspecialchars($this->data['password']))){ + $this->errors[] = 'forbidden_characters'; + } + } + private function connectionStrategy(): void + { + $this->captchaValidate(); + + if(!isset($this->data['login']) || empty($this->data['login']) + || !isset($this->data['password']) || empty($this->data['password']) + || !isset($this->data['connection_hidden']) || !empty($this->data['connection_hidden'])) + { + $this->errors[] = 'bad_login_or_password'; + } + } + private function usernameUpdateStrategy(): void + { + $this->captchaValidate(); + + if(!isset($this->data['login']) || empty($this->data['login']) + || !isset($this->data['password']) || empty($this->data['password']) + || !isset($this->data['new_login']) || empty($this->data['new_login']) + || !isset($this->data['modify_username_hidden']) || !empty($this->data['modify_username_hidden'])) + { + $this->errors[] = 'bad_login_or_password'; + } + + $new_login = self::removeSpacesTabsCRLF(htmlspecialchars($this->data['new_login'])); + if($new_login !== $this->data['new_login']){ + $this->errors[] = 'forbidden_characters'; + } + + if($this->data['login'] !== $_SESSION['user']['username']){ + $this->errors[] = 'bad_login_or_password'; + } + if($this->data['login'] === $new_login){ + $this->errors[] = 'same_username_as_before'; + } + } + private function passwordUpdateStrategy(): void + { + $this->captchaValidate(); + + if(!isset($this->data['login']) || empty($this->data['login']) + || !isset($this->data['password']) || empty($this->data['password']) + || !isset($this->data['new_password']) || empty($this->data['new_password']) + || !isset($this->data['modify_password_hidden']) || !empty($this->data['modify_password_hidden'])) + { + $this->errors[] = 'bad_login_or_password'; + } + + $new_password = self::removeSpacesTabsCRLF(htmlspecialchars($this->data['new_password'])); + if($new_password !== $this->data['new_password']){ + $this->errors[] = 'forbidden_characters'; + } + + if($this->data['login'] !== $_SESSION['user']['username']){ + $this->errors[] = 'bad_login_or_password'; + } + if($this->data['password'] === $new_password){ + $this->errors[] = 'same_password_as_before'; + } + } +} \ No newline at end of file diff --git a/src/service/Installation.php b/src/service/Installation.php new file mode 100644 index 0000000..5c2a901 --- /dev/null +++ b/src/service/Installation.php @@ -0,0 +1,209 @@ +l'extension " . $extension . ' est manquante

'); + $flag = true; + } + } + if(!extension_loaded('imagick') && !extension_loaded('gd')){ + echo("

il manque une de ces extensions au choix pour le traitement des images: imagick (de préférence) ou gd.

"); + $flag = true; + } + if($flag){ + echo '

Réalisez les actions nécéssaires sur le serveur ou contactez l\'administrateur du site.
+ Quand le problème sera résolu, il vous suffira de
recharger la page.

'; + die; + } + } + + static public function checkFilesAndFoldersRights(): void + { + // -- droits des fichiers et dossiers -- + $droits_dossiers = 0700; + $droits_fichiers = 0600; + + if(!file_exists('user_data')){ + // créer le dossier user_data + mkdir('user_data/'); + chmod('user_data/', $droits_dossiers); + echo '

Le dossier public/user_data introuvable et le serveur n\'a pas la permission de le créer.
+ Pour faire ça bien:
sudo -u "serveur web" mkdir /chemin/du/site/public/user_data

+

Aide: "serveur web" se nomme "www-data" sur debian et ubuntu, il s\'appelera "http" sur d\'autres distributions.

'; + die; + } + + if(!file_exists('../config/config.ini')){ + // aide à la création du config.ini + echo '

Le fichier config/config.ini est introuvable.

'; + echo '

Il doit obligatoirement contenir les codes de la base de données, le protocole http ou https (et éventuellement le port) utilisé pour créer les liens internes.
+ Un modèle est disponible, il s\'agit du fichier config/config-template.ini

+

Quand vous aurez terminé votre config.ini, donnez-lui par sécurité des droits 600.

'; + die; + } + else{ + // droits du config.ini + /*if(substr(sprintf('%o', fileperms('../config/config.ini')), -4) != 600){ + chmod('../config/config.ini', $droits_fichiers); + }*/ + + // tester les liens internes + // + + // le test de connexion à la BDD est dans le doctrine bootstrap + } + } + + + /* création d'un site minimal avec une page d'accueil à la toute 1ère visite du site, ne doit surtout pas être exécutée une seconde fois */ + + // protection 1 utilisé à chaque requête + static private function isFirstRun(EntityManager $entityManager): bool + { + $metadata = $entityManager->getRepository(AppMetadata::class)->find('installed'); + return !$metadata || $metadata->getValue() !== '1'; + } + + // protection 2, qui vérifie vraiment que les tables concernées sont vides + static private function areTablesEmpty(EntityManager $entityManager): bool + { + $empty = true; + $entities = ['Page', 'Node', 'NodeData']; + foreach($entities as $entity){ + $entity = 'App\Entity\\' . $entity; // nécéssaire quand on insère le nom avec une variable + + if($entityManager + ->createQuery("SELECT e FROM $entity e") + ->setMaxResults(1) + ->getOneOrNullResult()){ + $empty = false; + } + } + + // cas anormal détecté, on remet en place la clé "installed" + if(!$empty){ + self::preventReinstallation($entityManager); + } + + return $empty; + } + + // met en place la protection + static private function preventReinstallation(EntityManager $entityManager): void + { + $metadata = $entityManager->getRepository(AppMetadata::class)->find('installed'); + if($metadata){ + $metadata->setValue('1'); + } + else{ + $metadata = new AppMetadata('installed', '1'); + $entityManager->persist($metadata); + } + $entityManager->flush(); + } + + static public function fillStartingDatabase(EntityManager $entityManager): void + { + if(!Installation::isFirstRun($entityManager)){ + return; + } + + // la BDD n'est pas vierge, on ne touche à rien + if(!self::areTablesEmpty($entityManager)){ + return; + } + + /* -- table page -- */ + // paramètres: name_page, end_of_path, reachable, in_menu, hidden, position, parent + $accueil = new Page('Accueil', 'accueil', "Page d'accueil", true, true, false, 1, NULL); + $article = new Page('Article', 'article', "", true, false, false, NULL, NULL); + $connection = new Page('Connexion', 'connection', "Connexion", true, false, false, NULL, NULL); + $my_account = new Page('Mon compte', 'user_edit', "Mon compte", true, false, false, NULL, NULL); + $menu_paths = new Page("Menu et chemins", 'menu_paths', "Menu et chemins", true, false, false, NULL, NULL); + $menu_paths->addCSS('menu'); + $menu_paths->addJS('menu'); + $new_page = new Page('Nouvelle page', 'new_page', "Nouvelle page", true, false, false, NULL, NULL); + $new_page->addCSS('new_page'); + $new_page->addJS('new_page'); + $emails = new Page("Courriels", 'emails', "Consulter les courriels en base de données", true, false, false, NULL, NULL); + $emails->addCSS('show_emails'); + $emails->addJS('form'); + + /* -- table node -- */ + // paramètres: name_node, article_timestamp, attributes, position, parent, page, article + $head = new Node('head', 1, NULL, NULL, NULL); + $header = new Node('header', 2, NULL, NULL, NULL); + $nav = new Node('nav', 1, $header, NULL, NULL); + $main = new Node('main', 3, NULL, NULL, NULL); + $footer = new Node('footer', 4, NULL, NULL, NULL); + $breadcrumb = new Node('breadcrumb', 2, $header, NULL, NULL); + $login = new Node('login', 1, $main, $connection, NULL); + $user_edit = new Node('user_edit', 1, $main, $my_account, NULL); + $bloc_edit_menu = new Node('menu', 1, $main, $menu_paths, NULL); + $bloc_new_page = new Node('new_page', 1, $main, $new_page, NULL); + $bloc_emails = new Node('show_emails', 1, $main, $emails, NULL); + + /* -- table node_data -- */ + // paramètres: data, node, images + $head_data = new NodeData([], $head); + $header_data = new NodeData([], $header); + $footer_data = new NodeData([], $footer); + $emails_data = new NodeData([], $bloc_emails); + + /* -- table page -- */ + $entityManager->persist($accueil); + $entityManager->persist($article); + $entityManager->persist($connection); + $entityManager->persist($my_account); + $entityManager->persist($menu_paths); + $entityManager->persist($new_page); + $entityManager->persist($emails); + + /* -- table node -- */ + $entityManager->persist($head); + $entityManager->persist($header); + $entityManager->persist($nav); + $entityManager->persist($main); + $entityManager->persist($footer); + $entityManager->persist($breadcrumb); + $entityManager->persist($login); + $entityManager->persist($user_edit); + $entityManager->persist($bloc_edit_menu); + $entityManager->persist($bloc_new_page); + $entityManager->persist($bloc_emails); + + /* -- table node_data -- */ + $entityManager->persist($head_data); + $entityManager->persist($header_data); + $entityManager->persist($footer_data); + $entityManager->persist($emails_data); + + $entityManager->flush(); + + // empêcher la réutilisation de cette fonction + self::preventReinstallation($entityManager); + + // fin de l'installation + AppMode::set($entityManager, 'run'); + + // recharger la page? + //header('Location: ' . new URL); + } +} \ No newline at end of file diff --git a/src/service/Security.php b/src/service/Security.php new file mode 100644 index 0000000..356f4f4 --- /dev/null +++ b/src/service/Security.php @@ -0,0 +1,110 @@ +1, // protection contre les élements et attributs dangereux + + // liste blanche d'éléments HTML + 'elements'=> 'h1, h2, h3, h4, h5, h6, p, br, s, em, span, strong, a, ul, ol, li, sup, sub, code, blockquote, div, pre, table, caption, colgroup, col, tbody, tr, th, td, figure, img, figcaption, iframe, small', + + // liste noire d'attributs HTML + 'deny_attribute'=> 'id, class' // on garde 'style' + ); + // faire qu'un certain élément puisse n'avoir que certains attributs, regarder la doc + private static $specHtmLawed = ''; + + // obtenir du HTML non dangereur sans appliquer htmlspecialchars + public static function secureHTML(string $chaine): string + { + return trim(htmLawed($chaine, self::$configHtmLawed, self::$specHtmLawed)); + } + + public static function secureFileName(string $chaine): string + { + // sécuriser un nom avec chemin avec basename? + //$chaine = basename($chaine); + + /* + - caractères interdits sous windows / \ : * ? " < > | + - mac autorise les / + - mac interdit : + - linux autorise tout sauf les / + - imagemagick ne supporte pas les : + + - 'espace' fonctionne + - / remplacé par firefox en : + - \ retire ce qui est devant le \ + - * fonctionne + - ? permet le téléchargement mais pas l'affichage + - " ne fonctionne pas, remplacé par %22, filtrer %22 + - < > fonctionnent + - | fonctionne + - = fonctionne, mais je filtre parce qu'on en trouve dans une URL + - ' ` fonctionnent + - % fonctionne + - (){}[] fonctionnent + - ^ fonctionne + - # ne fonctionne pas + - ~ fonctionne + - & fonctionne + - ^ pas encore testé + */ + + // => on remplace tout par des _ + // filtrer / et \ semble inutile + + /*$cibles = [' ', '/', '\\', ':', '*', '?', '<', '>', '|', '=', "'", '`', '"', '%22', '#']; + $chaine = str_replace($cibles, '_', $chaine); // nécéssite l'extension mbstring + $chaine = mb_strtolower($chaine); + return($chaine);*/ + + $chaine = preg_replace('/[^a-zA-Z0-9_-]/', '_', $chaine); // ne garder que les lettres, chiffres, tirets et underscores + $chaine = preg_replace('/_+/', '_', $chaine); // doublons d'underscores + return trim($chaine, '_'); + + // les problèmes avec \ persistent !! + // => javascript + // malheureusement document.getElementById('upload').files[0].name = chaine; ne marche pas! interdit! + // javascript ne doit pas pouvoir accéder au système de fichiers + // solutions: + // - au lieu de fournir une chaine (le chemin du fichier), donner un objet à files[0].name + // - créer une copie du fichier et l'envoyer à la place + // - envoyer le fichier en AJAX + // - envoyer le nom du fichier à part puis renommer en PHP + } +} + +// lien sans http:// +function fixLinks($data) +{ + // 1/ + // si une adresse est de type "domaine.fr" sans le http:// devant, le comportement des navigateurs est de rechercher un fichier comme si mon adresse commençait par file:// + // tomber ainsi sur une page d'erreur est parfaitement déroutant + + // regex pour détecter les balises
et ajouter http:// au début des liens si nécessaire + $pattern = '#(]+href=")((?!https?://)[^>]+>)#'; + //$data = preg_replace($pattern, '$1http://$2', $data); + + // 2/ + // cas où la regex fait mal son boulot: + // l'erreur 404 est gérée par le .htaccess + // et le visiteur est redirigé à la page "menu" + // (ça ne règle pas le problème mais c'est mieux) + + // 3/ + // quand l'éditeur est ouvert (avant de valider l'article), + // le lien qu'on vient de créer apparaît dans l'infobulle, + // cliquer dessus ouvre un onglet sur une erreur 404 + // solution partielle avec le .htaccess + // + // solution? fermer ce nouvel onglet avec echo ''; + // comment déclencher le JS? en faisant qu'une erreur 404 causée pour cette raison soit particulière? + + return($data); +} diff --git a/src/service/URL.php b/src/service/URL.php new file mode 100644 index 0000000..5bd2594 --- /dev/null +++ b/src/service/URL.php @@ -0,0 +1,88 @@ +params = $gets; + if($anchor != ''){ + $this->setAnchor($anchor); + } + } + + // setters statiques + static public function setProtocol(string $protocol = 'http'): void + { + self::$protocol = $protocol === 'https' ? 'https://' : 'http://'; + } + static public function setPort(int|string $port = 80): void + { + if((int)$port === 443){ + self::$protocol = 'https://'; + self::$port = ''; + } + elseif((int)$port === 80){ + self::$protocol = 'http://'; + self::$port = ''; + } + else{ + self::$port = ':' . (string)$port; + } + } + static public function setHost(string $host): void + { + self::$host = $host; + } + static public function setPath(string $path): void + { + self::$path = '/' . ltrim($path, '/'); + } + + //setters normaux + public function addParams(array $gets): void + { + // array_merge est préféré à l'opérateur d'union +, si une clé existe déjà la valeur est écrasée + $this->params = array_merge($this->params, $gets); + } + public function setAnchor(string $anchor = ''): void + { + if($anchor != ''){ + $this->anchor = '#' . ltrim($anchor, '#'); + } + else{ + $this->anchor = ''; + } + } + + private function makeParams(): string + { + $output = ''; + $first = true; + + foreach($this->params as $key => $value) { + if($first){ + $output .= '?'; + $first = false; + } + else{ + $output .= '&'; + } + $output .= $key . '=' . $value; + } + return $output; + } + + public function __toString(): string + { + return self::$protocol . self::$host . self::$port . self::$path . $this->makeParams() . $this->anchor; + } +} \ No newline at end of file diff --git a/src/service/router.php b/src/service/router.php new file mode 100644 index 0000000..fc6b028 --- /dev/null +++ b/src/service/router.php @@ -0,0 +1,384 @@ + 1er test, méthode http: GET, POST ou autre chose +=> 2ème test, type de contenu (méthode POST uniquement): +"application/x-www-form-urlencoded" = formulaire +"application/json" = requête AJAX avec fetch() +"multipart/form-data" = upload d'image par tinymce +$_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' requête AJAX xhs, non utilisée +=> 3ème test, comme le 2ème test mais uniquement si IS_ADMIN est vrai +*/ + +declare(strict_types=1); + +if($request->getMethod() === 'GET'){ + // table "user" vide + if(!UserController::existUsers($entityManager)){ + require AbstractBuilder::VIEWS_PATH . 'user_create.php'; + die; + } + + // bouton déconnexion (méthode GET parce que l'utilisateur ne modifie plus de données à partir de là) + if($request->query->has('action') && $request->query->get('action') === 'deconnection'){ + UserController::disconnect($entityManager); + } + + // articles suivants + if($request->query->has('fetch') && $request->query->get('fetch') === 'next_articles'){ + ArticleController::fetch($entityManager, $request); + } + + // données du calendrier + // création du calendrier et changement de dates affichées (boutons flèches mais pas changement de vue) + if($request->query->has('action') && $request->query->get('action') === 'get_events' + && $request->query->has('start') && $request->query->has('end') && empty($request->getPayload()->all())) // getPayload ne récupère pas que des POST + { + CalendarController::getData($entityManager); + } + + // pages interdites + if(!IS_ADMIN && in_array(CURRENT_PAGE, ['menu_paths', 'new_page', 'user_edit', 'emails'])){ + header('Location: ' . new URL); + die; + } + + if(IS_ADMIN === true){ + // ... + } + + // construction d'une page + $response = (new ViewController)->buildView($entityManager, $request); // utilise Model + // parenthèses nécéssaires autour de l'instanciation pour PHP < 8.4 +} + + +elseif($request->getMethod() === 'POST'){ + /* -- contrôleurs appellables par tout le monde -- */ + + // table "user" vide + if(!UserController::existUsers($entityManager)){ + UserController::createAdminUser($entityManager); + } + + // requêtes JSON avec fetch() + if($_SERVER['CONTENT_TYPE'] === 'application/json') + { + $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true); + + if(isset($_GET['action'])) + { + // formulaire de contact + if($_GET['action'] === 'send_email'){ + ContactFormController::sendVisitorEmail($entityManager, $json); + } + } + } + + // envoi formulaire HTML + elseif($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ + // tentative de connexion + if($request->query->has('action') && $request->query->get('action') === 'connection'){ + //$response = + UserController::connect($entityManager); + } + } + + + if(IS_ADMIN === true) + { + /* -- requêtes AJAX -- */ + + // requêtes JSON avec fetch() + if($_SERVER['CONTENT_TYPE'] === 'application/json') + { + $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true); + + if($request->query->has('action')) + { + /* -- manipulation des articles -- */ + if($_GET['action'] === 'editor_submit' && isset($json['id']) && isset($json['content'])){ + ArticleController::editorSubmit($entityManager, $json); + } + elseif($_GET['action'] === 'delete_article' && isset($json['id'])){ + $response = ArticleController::deleteArticle($entityManager, $json); // version AJAX + } + elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){ + ArticleController::switchPositions($entityManager, $json); + } + elseif($_GET['action'] === 'date_submit' && isset($json['id']) && isset($json['date'])){ + ArticleController::dateSubmit($entityManager, $json); + } + + /* -- bloc Formulaire -- */ + elseif($_GET['action'] === 'keep_emails'){ + ContactFormController::keepEmails($entityManager, $json); + } + elseif($_GET['action'] === 'set_retention_period'){ + ContactFormController::setEmailsRetentionPeriod($entityManager, $json); + } + elseif($_GET['action'] === 'set_email_param'){ + ContactFormController::setEmailParam($entityManager, $json); + } + elseif($_GET['action'] === 'test_email'){ + ContactFormController::sendTestEmail($entityManager, $json); + } + + /* -- page emails -- */ + elseif($_GET['action'] === 'delete_email'){ + ContactFormController::deleteEmail($entityManager, $json); + } + elseif($_GET['action'] === 'toggle_sensitive_email'){ + ContactFormController::toggleSensitiveEmail($entityManager, $json); + } + + /* -- upload d'image dans tinymce par copier-coller -- */ + // collage de HTML contenant une ou plusieurs balises + elseif($request->query->get('action') === 'upload_image_url'){ + ImageUploadController::uploadImageHtml(); + } + // collage d'une image (code base64 dans le presse-papier) non encapsulée dans du HTML + elseif($request->query->get('action') === 'upload_image_base64'){ + ImageUploadController::uploadImageBase64(); + } + + + /* -- requêtes spécifiques au calendrier -- */ + elseif($request->query->get('action') === 'new_event'){ + CalendarController::newEvent($json, $entityManager); + } + elseif($request->query->get('action') === 'update_event'){ + CalendarController::updateEvent($json, $entityManager); + } + elseif($request->query->get('action') === 'remove_event'){ + CalendarController::removeEvent($json, $entityManager); + } + else{ + echo json_encode(['success' => false]); + die; + } + } + + /* -- site entier (header, footer, favicon) -- */ + elseif($request->query->has('head_foot_text')){ + HeadFootController::setTextData($entityManager, $request->query->get('head_foot_text'), $json); + } + elseif($request->query->has('head_foot_social_check')){ + HeadFootController::displaySocialNetwork($entityManager, $request->query->get('head_foot_social_check'), $json); + } + + /* -- page Menu et chemins -- */ + elseif(isset($_GET['menu_edit'])) + { + // ne suit pas la règle, faire ça dans un contrôleur? + Model::$menu = new Menu($entityManager); // récupération des données + + // 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'])){ + MenuAndPathsController::MoveOneLevelUp($entityManager, $json); + } + // flèche droite =>: position = nombre d'éléments de la fraterie + 1, l'élément précédent devient le parent + elseif($_GET['menu_edit'] === 'move_one_level_down' && isset($json['id'])){ + MenuAndPathsController::MoveOneLevelDown($entityManager, $json); + } + elseif($_GET['menu_edit'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])){ + MenuAndPathsController::switchPositions($entityManager, $json); + } + elseif($_GET['menu_edit'] === 'display_in_menu' && isset($json['id']) && isset($json['checked'])){ + MenuAndPathsController::displayInMenu($entityManager, $json); + } + elseif($_GET['menu_edit'] === 'url_edit' && isset($json['id']) && isset($json['field']) && isset($json['input_data'])){ + MenuAndPathsController::editUrl($entityManager, $json); + } + } + + /* -- mode Modification d'une page -- */ + // partie "page" + elseif(isset($_GET['page_edit'])) + { + // titre de la page + if($_GET['page_edit'] === 'page_title'){ + PageManagementController::setPageTitle($entityManager, $json); + } + // description dans les métadonnées + elseif($_GET['page_edit'] === 'page_description'){ + PageManagementController::setPageDescription($entityManager, $json); + } + } + + // partie "blocs" + elseif($request->query->has('bloc_edit')) + { + if($request->query->get('bloc_edit') === 'rename_page_bloc'){ + PageManagementController::renameBloc($entityManager, $json); + } + elseif($request->query->get('bloc_edit') === 'switch_blocs_positions'){ + PageManagementController::SwitchBlocsPositions($entityManager, $json); + } + elseif($request->query->get('bloc_edit') === 'change_articles_order'){ + PageManagementController::changeArticlesOrder($entityManager, $json); + } + elseif($request->query->get('bloc_edit') === 'change_presentation'){ + PageManagementController::changePresentation($entityManager, $json); + } + elseif($request->query->get('bloc_edit') === 'change_cols_min_width'){ + PageManagementController::changeColsMinWidth($entityManager, $json); + } + elseif($request->query->get('bloc_edit') === 'change_pagination_limit'){ + PageManagementController::changePaginationLimit($entityManager, $json); + } + } + } + + // upload avec FormData + elseif(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false) + { + // dans tinymce avec le plugin (bouton "insérer une image" de l'éditeur ou glisser-déposer) + if($request->query->has('action') && $request->query->get('action') === 'upload_image_tinymce'){ + ImageUploadController::imageUploadTinyMce(); + } + // dans tinymce, des quatre méthodes: bouton "link", drag & drop, html, base64 + elseif($request->query->has('action') && $request->query->get('action') === 'upload_file_tinymce'){ + FileUploadController::fileUploadTinyMce(); + } + elseif($request->query->has('head_foot_image')){ + HeadFootController::uploadAsset($entityManager, $request->query->get('head_foot_image')); + } + } + + // requêtes XMLHttpRequest + elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') + { + //echo "requête XMLHttpRequest reçue par le serveur"; + echo json_encode(['success' => false]); // noyer le poisson en laissant penser que le site gère les requêtes XHR + die; + } + + /* -- envoi formulaire HTML -- */ + elseif($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded') + { + if($request->query->has('action') && $request->query->get('action') === 'delete_article' && isset($_GET['id'])){ + $response = ArticleController::deleteArticle($entityManager, $_GET); // version formulaire + } + + /* -- nouvelle page -- */ + elseif(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'] === '') + { + PageManagementController::newPage($entityManager, $_POST); + } + + /* -- suppression d'une page -- */ + elseif(isset($_POST['page_id']) && $_POST['page_id'] !== null + && isset($_POST['submit_hidden']) && $_POST['submit_hidden'] === '') + { + PageManagementController::deletePage($entityManager); + } + + + /* -- mode Modification d'une page -- */ + + // modification du chemins en snake_case + 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'] === '') + { + PageManagementController::updatePageMenuPath($entityManager); + } + // 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 + { + PageManagementController::addBloc($entityManager); + } + // 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 + { + PageManagementController::deleteBloc($entityManager); + } + + + /* -- 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"])){ + MenuAndPathsController::newUrlMenuEntry($entityManager); + } + // 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 + MenuAndPathsController::deleteUrlMenuEntry($entityManager); + } + + + /* -- page Mon compte -- */ + elseif($request->query->has('action') && $request->query->get('action') === 'update_username') + { + UserController::updateUsername($entityManager); + } + elseif($request->query->has('action') && $request->query->get('action') === 'update_password') + { + UserController::updatePassword($entityManager); + } + + // redirection page d'accueil + else{ + header("Location: " . new URL(['error' => 'paramètres inconnus'])); + die; + } + } + // POST admin ne matchant pas + else{ + echo json_encode(['success' => false]); + die; + } + } + // POST non admin ne matchant pas + else{ + echo json_encode(['success' => false]); + die; + } +} + +// méthode inconnue +else{ + header("Location: " . new URL(['error' => 'tu fais quoi là mec?'])); + die; +} + + + +/* -- utilisation de la réponse -- */ +if(isset($response)){ + // cas gérés (d'autres sont à prévoir): mauvais id de la page article, accès page création d'article sans être admin + if($request->isMethod('GET') && $response->getStatusCode() == 302){ // 302 redirection temporaire + header('Location: ' . new URL(['page' => $_GET['from'] ?? ''])); + } + // redirection après traitement de formulaires HTTP + elseif($request->getMethod() === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ + $response_data = json_decode(($response)->getContent(), true); + $url = new URL(['page' => $_GET['from'] ?? '']); + $url->addParams(['success' => $response_data['success'], 'message' => $response_data['message']]); + header('Location: ' . $url); + } + // affichage d'une page OU requête AJAX + else{ + $response->send(); + } +} +// pas utilisation de RESPONSE (cas destiné à disparaître) +else{ + if($request->getMethod() === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ + header("Location: " . new URL(['error' => 'erreur côté serveur'])); + } + else{ + http_response_code(500); + echo "erreur côté serveur"; + } +} +//die; // inutile \ No newline at end of file diff --git a/src/service/session.php b/src/service/session.php new file mode 100644 index 0000000..57f2143 --- /dev/null +++ b/src/service/session.php @@ -0,0 +1,86 @@ + 'session_invalide'])); + die; + } + + // MAJ de la session avec CERTAINES données + $_SESSION['user']['username'] = $user->getLogin(); + $_SESSION['user']['role'] = $user->getRole(); + + $is_admin = $user->getRole() === 'admin'; + } + + define('IS_ADMIN', $is_admin); + + // si on a un jour besoin d'une variable globale au lieu d'une constante + //$GLOBALS['is_admin'] = $is_admin; // version modifiable 1 + /*function isAdmin(): bool { // version modifiable 2 + return $_SESSION['user']['role'] ?? null === 'admin'; + }*/ + + + // => système de cache à ajouter pour ne pas lire la BDD à chaque fois + //remplacer ce qui est en haut + /*$user = $_SESSION['user'] ?? null; + if (!$user) { + // visiteur + } + // Vérification périodique (ex: toutes les 5 minutes) + if (time() - $user['last_check'] > 300) { + $user = UserController::getUserById($user['id'], $entityManager); + if (!$user) { + session_destroy(); + header('Location: /login.php'); + exit; + } + // cache pour ne pas avoir à lire la BDD à chaque page + $_SESSION['user'] = [ + 'id' => $user['id'], + 'role' => $user['role'], + 'username' => $user['username'], + 'last_check' => time() + ]; + $user = $_SESSION['user']; + } + $is_admin = ($user['role'] === 'admin');*/ + + + // améliorations possibles: ajouter expiration automatique + protection contre vol de session (IP / user-agent) sans casser ton app. +} + +// nettoyage complet +/*function cleanSession(){ + unset($_SESSION['user']); // mémoire vive + session_destroy(); // fichier côté serveur + setcookie('PHPSESSID', '', time() - 86400, '/'); // cookie de session +}*/ \ No newline at end of file diff --git a/src/view/CalendarBuilder.php b/src/view/CalendarBuilder.php index bdabcd2..0c0006d 100644 --- a/src/view/CalendarBuilder.php +++ b/src/view/CalendarBuilder.php @@ -12,7 +12,7 @@ class CalendarBuilder extends AbstractBuilder parent::__construct($node); $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; - $calendar_js_file = $_SESSION['admin'] ? 'calendar_admin' : 'calendar'; + $calendar_js_file = IS_ADMIN ? 'calendar_admin' : 'calendar'; if(file_exists($viewFile)) { diff --git a/src/view/FooterBuilder.php b/src/view/FooterBuilder.php index 8d24f25..f1623e7 100644 --- a/src/view/FooterBuilder.php +++ b/src/view/FooterBuilder.php @@ -27,7 +27,7 @@ class FooterBuilder extends AbstractBuilder $breadcrumb = $this->html; $empty_admin_zone = ''; - if($_SESSION['admin']) + if(IS_ADMIN) { // données du footer $admin_footer_name = ' diff --git a/src/view/FormBuilder.php b/src/view/FormBuilder.php index 6986dea..9a900ce 100644 --- a/src/view/FormBuilder.php +++ b/src/view/FormBuilder.php @@ -9,12 +9,10 @@ class FormBuilder extends AbstractBuilder { static private ?Captcha $captcha = null; - public function __construct(Node $node) - { + public function __construct(Node $node){ parent::__construct($node); - if(!empty($node->getNodeData()->getData())) - { + if(!empty($node->getNodeData()->getData())){ extract($node->getNodeData()->getData()); } @@ -34,8 +32,7 @@ class FormBuilder extends AbstractBuilder $retention_period_sensible = $this->getRetentionPeriod($retention_period_sensible ?? null, App\Entity\Email::DEFAULT_RETENTION_PERIOD_SENSITIVE); $admin_content = ''; - if($_SESSION['admin']) - { + if(IS_ADMIN){ ob_start(); require self::VIEWS_PATH . 'form_admin.php'; $admin_content = ob_get_clean(); diff --git a/src/view/GaleryBuilder.php b/src/view/GaleryBuilder.php index 749cb30..019a2c7 100644 --- a/src/view/GaleryBuilder.php +++ b/src/view/GaleryBuilder.php @@ -21,7 +21,7 @@ class GaleryBuilder extends AbstractBuilder // ajouter un article $new_article = ''; - if($_SESSION['admin']) + if(IS_ADMIN) { $id = 'n' . $this->id_node; $js = 'onclick="openEditor(\'' . $id . '\')"'; diff --git a/src/view/HeadBuilder.php b/src/view/HeadBuilder.php index 76d8d9d..fe57b55 100644 --- a/src/view/HeadBuilder.php +++ b/src/view/HeadBuilder.php @@ -32,7 +32,7 @@ class HeadBuilder extends AbstractBuilder $js .= self::insertJS('modif_page'); } - if($_SESSION['admin']){ + if(IS_ADMIN){ // édition éléments sur toutes les pages (header, footer et favicon) $js .= self::insertJS('Input'); diff --git a/src/view/HeaderBuilder.php b/src/view/HeaderBuilder.php index 6934e10..bfd5963 100644 --- a/src/view/HeaderBuilder.php +++ b/src/view/HeaderBuilder.php @@ -52,7 +52,7 @@ class HeaderBuilder extends AbstractBuilder $social_networks = ''; // boutons mode admin - if($_SESSION['admin']){ + if(IS_ADMIN){ // assets dans classe header_additional_inputs $admin_head_favicon = ' diff --git a/src/view/LoginBuilder.php b/src/view/LoginBuilder.php index 639f953..479398c 100644 --- a/src/view/LoginBuilder.php +++ b/src/view/LoginBuilder.php @@ -10,7 +10,7 @@ class LoginBuilder extends AbstractBuilder public function __construct(Node $node) { // déjà connecté? - if($_SESSION['admin']) + if(IS_ADMIN) { header('Location: ' . new URL); die; diff --git a/src/view/MainBuilder.php b/src/view/MainBuilder.php index 332efa5..b488703 100644 --- a/src/view/MainBuilder.php +++ b/src/view/MainBuilder.php @@ -40,7 +40,7 @@ class MainBuilder extends AbstractBuilder } else{ // si action = "modif_page", affiche des commandes supplémentaires - if($_SESSION['admin'] && self::$modif_mode){ + if(IS_ADMIN && self::$modif_mode){ // ajouter un contrôle du champ in_menu $this->viewEditBlocks($node); } diff --git a/src/view/MenuBuilder.php b/src/view/MenuBuilder.php index b8e9396..41ee189 100644 --- a/src/view/MenuBuilder.php +++ b/src/view/MenuBuilder.php @@ -20,7 +20,7 @@ class MenuBuilder extends AbstractBuilder if(file_exists($viewFile)) { - if($_SESSION['admin']){ + if(IS_ADMIN){ $this->unfoldMenu(Model::$menu); if($template){ diff --git a/src/view/NewBuilder.php b/src/view/NewBuilder.php index a31a1c4..7459cc8 100644 --- a/src/view/NewBuilder.php +++ b/src/view/NewBuilder.php @@ -75,7 +75,7 @@ class NewBuilder extends AbstractBuilder $article_buttons = ''; $date_buttons = ''; $admin_buttons = ''; - if($_SESSION['admin']){ + if(IS_ADMIN){ if(CURRENT_PAGE === 'article'){ $title_js = 'onclick="openEditor(\'' . $id_title . '\')"'; $modify_title = '

' . "\n"; diff --git a/src/view/NewPageBuilder.php b/src/view/NewPageBuilder.php index d519a22..0a3a137 100644 --- a/src/view/NewPageBuilder.php +++ b/src/view/NewPageBuilder.php @@ -18,7 +18,7 @@ class NewPageBuilder extends AbstractBuilder //parent::__construct($node); $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; - if(isset($_SESSION['admin']) && $_SESSION['admin'] && file_exists($viewFile)) + if(IS_ADMIN && file_exists($viewFile)) { /*if(!empty($node->getNodeData()->getData())) { diff --git a/src/view/NewsBlockBuilder.php b/src/view/NewsBlockBuilder.php index 4c7c4ec..f1fe12b 100644 --- a/src/view/NewsBlockBuilder.php +++ b/src/view/NewsBlockBuilder.php @@ -32,7 +32,7 @@ class NewsBlockBuilder extends AbstractBuilder // ajouter un article $new_article = ''; - if($_SESSION['admin']) + if(IS_ADMIN) { $id = 'n' . $this->id_node; diff --git a/src/view/PostBlockBuilder.php b/src/view/PostBlockBuilder.php index ba4de12..c6094e8 100644 --- a/src/view/PostBlockBuilder.php +++ b/src/view/PostBlockBuilder.php @@ -33,7 +33,7 @@ class PostBlockBuilder extends AbstractBuilder // ajouter un article // => fait un peu double emploi avec PostBuilder $new_article = ''; - if($_SESSION['admin']) + if(IS_ADMIN) { $id = 'n' . $this->id_node; diff --git a/src/view/PostBuilder.php b/src/view/PostBuilder.php index de7a818..e0bf985 100644 --- a/src/view/PostBuilder.php +++ b/src/view/PostBuilder.php @@ -26,7 +26,7 @@ class PostBuilder extends AbstractBuilder // modifier un article // => fait un peu double emploi avec PostBlockBuilder $admin_buttons = ''; - if($_SESSION['admin']) + if(IS_ADMIN) { $modify_js = 'onclick="openEditor(\'' . $id . '\')"'; $modify_article = '

' . "\n"; diff --git a/src/view/UserEditBuilder.php b/src/view/UserEditBuilder.php index 3604e91..0a347d4 100644 --- a/src/view/UserEditBuilder.php +++ b/src/view/UserEditBuilder.php @@ -12,7 +12,7 @@ class UserEditBuilder extends AbstractBuilder public function __construct(Node $node) { // pour éviter les arnaques - if(!$_SESSION['admin']) + if(!IS_ADMIN) { header('Location: ' . new URL); die; diff --git a/src/view/templates/footer.php b/src/view/templates/footer.php index 7809818..6450e21 100644 --- a/src/view/templates/footer.php +++ b/src/view/templates/footer.php @@ -20,7 +20,7 @@
- +