From df3612ed7e6691530503f79483d2fdbc032d01b8 Mon Sep 17 00:00:00 2001 From: polo-pc-greta Date: Thu, 27 Mar 2025 10:13:03 +0100 Subject: mise en ligne github --- src/Config.php | 76 +++++++++ src/controller/Director.php | 101 +++++++++++ src/controller/Security.php | 111 ++++++++++++ src/controller/URL.php | 88 ++++++++++ src/controller/ajax.php | 104 ++++++++++++ src/controller/installation.php | 144 ++++++++++++++++ src/controller/password.php | 357 +++++++++++++++++++++++++++++++++++++++ src/controller/post.php | 17 ++ src/model/Menu.php | 53 ++++++ src/model/Path.php | 84 +++++++++ src/model/doctrine-bootstrap.php | 31 ++++ src/model/entities/Article.php | 77 +++++++++ src/model/entities/Image.php | 91 ++++++++++ src/model/entities/Node.php | 168 ++++++++++++++++++ src/model/entities/NodeData.php | 62 +++++++ src/model/entities/Page.php | 97 +++++++++++ src/model/entities/User.php | 47 ++++++ src/view/AbstractBuilder.php | 52 ++++++ src/view/ArticleBuilder.php | 60 +++++++ src/view/BlogBuilder.php | 49 ++++++ src/view/BreadcrumbBuilder.php | 53 ++++++ src/view/FooterBuilder.php | 64 +++++++ src/view/GaleryBuilder.php | 49 ++++++ src/view/GridBuilder.php | 55 ++++++ src/view/HeadBuilder.php | 68 ++++++++ src/view/HeaderBuilder.php | 64 +++++++ src/view/LoginBuilder.php | 15 ++ src/view/MainBuilder.php | 30 ++++ src/view/NavBuilder.php | 61 +++++++ src/view/NewBuilder.php | 93 ++++++++++ src/view/ViewBuilder.php | 16 ++ src/view/password.php | 152 +++++++++++++++++ src/view/templates/article.php | 15 ++ src/view/templates/blog.php | 6 + src/view/templates/footer.php | 15 ++ src/view/templates/galery.php | 10 ++ src/view/templates/grid.php | 8 + src/view/templates/head.php | 11 ++ src/view/templates/header.php | 23 +++ src/view/templates/new.php | 21 +++ 40 files changed, 2698 insertions(+) create mode 100644 src/Config.php create mode 100644 src/controller/Director.php create mode 100644 src/controller/Security.php create mode 100644 src/controller/URL.php create mode 100644 src/controller/ajax.php create mode 100644 src/controller/installation.php create mode 100644 src/controller/password.php create mode 100644 src/controller/post.php create mode 100644 src/model/Menu.php create mode 100644 src/model/Path.php create mode 100644 src/model/doctrine-bootstrap.php create mode 100644 src/model/entities/Article.php create mode 100644 src/model/entities/Image.php create mode 100644 src/model/entities/Node.php create mode 100644 src/model/entities/NodeData.php create mode 100644 src/model/entities/Page.php create mode 100644 src/model/entities/User.php create mode 100644 src/view/AbstractBuilder.php create mode 100644 src/view/ArticleBuilder.php create mode 100644 src/view/BlogBuilder.php create mode 100644 src/view/BreadcrumbBuilder.php create mode 100644 src/view/FooterBuilder.php create mode 100644 src/view/GaleryBuilder.php create mode 100644 src/view/GridBuilder.php create mode 100644 src/view/HeadBuilder.php create mode 100644 src/view/HeaderBuilder.php create mode 100644 src/view/LoginBuilder.php create mode 100644 src/view/MainBuilder.php create mode 100644 src/view/NavBuilder.php create mode 100644 src/view/NewBuilder.php create mode 100644 src/view/ViewBuilder.php create mode 100644 src/view/password.php create mode 100644 src/view/templates/article.php create mode 100644 src/view/templates/blog.php create mode 100644 src/view/templates/footer.php create mode 100644 src/view/templates/galery.php create mode 100644 src/view/templates/grid.php create mode 100644 src/view/templates/head.php create mode 100644 src/view/templates/header.php create mode 100644 src/view/templates/new.php (limited to 'src') diff --git a/src/Config.php b/src/Config.php new file mode 100644 index 0000000..cfec876 --- /dev/null +++ b/src/Config.php @@ -0,0 +1,76 @@ + $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/controller/Director.php b/src/controller/Director.php new file mode 100644 index 0000000..896cde1 --- /dev/null +++ b/src/controller/Director.php @@ -0,0 +1,101 @@ +entityManager = $entityManager; + self::$menu_data = new Menu($entityManager); // Menu est un modèle mais pas une entité + self::$page_path = new Path(); + $this->page = self::$page_path->getLast(); + $this->root_node = new Node; // instance mère "vide" ne possédant rien d'autre que des enfants + } + + public function makeRootNode(string $id = ''): void + { + // on récupère toutes les entrées + $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page OR n.page IS null'; + if($id == '') + { + $bulk_data = $this->entityManager + ->createQuery($dql) + ->setParameter('page', $this->page) + ->getResult(); + } + else // avec $_GET['id'] dans l'URL + { + $dql .= ' OR n.article_timestamp = :id'; + $bulk_data = $this->entityManager + ->createQuery($dql) + ->setParameter('page', $this->page) + ->setParameter('id', $id) + ->getResult(); + } + $this->feedObjects($bulk_data); + } + + public function makeArticleNode(string $id = ''): bool + { + $bulk_data = $this->entityManager + ->createQuery('SELECT n FROM App\Entity\Node n WHERE n.article_timestamp = :id') + ->setParameter('id', $id) + ->getResult(); + + if(count($bulk_data) === 0){ + return false; + } + + $this->root_node = $bulk_data[0]; + return true; + } + + public function feedObjects(array $bulk_data): void // $bulk_data = tableau de Node + { + // puis on les range + // (attention, risque de disfonctionnement si les noeuds de 1er niveau ne sont pas récupérés en 1er dans la BDD) + foreach($bulk_data as $node) + { + // premier niveau + if($node->getParent() == null) + { + $this->root_node->addChild($node); + + // spécifique page article + if($node->getName() === 'main' && $this->page->getEndOfPath() == 'article'){ + $main = $node; + } + } + // autres niveaux + else + { + $node->getParent()->addChild($node); + + // spécifique page article + if($node->getName() === 'new' && $this->page->getEndOfPath() == 'article'){ + $new = $node; + } + } + } + if(isset($new)){ + $main->setTempChild($new); + } + } + + public function getRootNode(): Node + { + return $this->root_node; + } +} diff --git a/src/controller/Security.php b/src/controller/Security.php new file mode 100644 index 0000000..ab59d07 --- /dev/null +++ b/src/controller/Security.php @@ -0,0 +1,111 @@ +1, // protection contre les élements et attributs dangereux + + // liste blanche d'éléments HTML + 'elements'=> 'h1, h2, h3, h4, h5, h6, p, 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', + + // 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 = ''; + + public static function secureString(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); + + // 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 + } +} + +// erreurs à la création des mots de passe +function removeSpacesTabsCRLF(string $chaine): string +{ + $cibles = [' ', "\t", "\n", "\r"]; // doubles quotes !! + return(str_replace($cibles, '', $chaine)); +} + +// 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/controller/URL.php b/src/controller/URL.php new file mode 100644 index 0000000..956d85d --- /dev/null +++ b/src/controller/URL.php @@ -0,0 +1,88 @@ +params = $gets; + if($anchor != ''){ + $this->setAnchor($anchor); + } + } + + //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/ajax.php b/src/controller/ajax.php new file mode 100644 index 0000000..130c4c6 --- /dev/null +++ b/src/controller/ajax.php @@ -0,0 +1,104 @@ +makeArticleNode($articleId)) // une entrée est trouvée + { + $node = $director->getRootNode(); + $node->getArticle()->setContent($content); + $entityManager->flush(); + + echo json_encode(['success' => true]); + } + else{ + echo json_encode(['success' => false, 'message' => 'Aucune entrée trouvée en BDD']); + } + } + else{ + echo json_encode(['success' => false, 'message' => 'Erreur de décodage JSON']); + } + die; + } + elseif($_GET['action'] === 'delete_article' && isset($json['id'])) + { + $articleId = $json['id']; + + $director = new Director($entityManager); + $director->makeArticleNode($articleId); + $node = $director->getRootNode(); + $entityManager->remove($node); + $entityManager->flush(); + + // test avec une nouvelle requête qui ne devrait rien trouver + if(!$director->makeArticleNode($articleId)) + { + echo json_encode(['success' => true]); + + // on pourrait afficher une notification "toast" + } + else{ + http_response_code(500); + echo json_encode(['success' => false, 'message' => 'Erreur lors de la suppression de l\'article.']); + } + die; + } +} + +// détection des requêtes d'upload d'image de tinymce +if(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false && isset($_GET['action']) && $_GET['action'] === 'upload_image'){ + if (isset($_FILES['file'])) { + $file = $_FILES['file']; + $dest = 'images/'; + $dest_mini = 'images-mini/'; + + // Vérifier si les répertoires existent, sinon les créer + if(!is_dir($dest)) { + mkdir($dest, 0700, true); + } + if(!is_dir($dest_mini)) { + mkdir($dest_mini, 0700, true); + } + + $filePath = $dest . basename($file['name']); + + // créer une miniature de l'image + + if(move_uploaded_file($file['tmp_name'], $filePath)) { + $image_url = str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']); + echo json_encode(['location' => $image_url . $filePath]); // renvoyer l'URL de l'image téléchargée + } + else{ + http_response_code(500); + echo json_encode(['message' => 'Erreur 500: Internal Server Error']); + } + } + else{ + http_response_code(400); + echo json_encode(['message' => 'Erreur 400: Bad Request']); + } + die; +} + +// détection des requêtes de type XHR, pas d'utilité pour l'instant +/*elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){ + echo "requête XHR reçue par le serveur"; + die; +}*/ + + diff --git a/src/controller/installation.php b/src/controller/installation.php new file mode 100644 index 0000000..a692618 --- /dev/null +++ b/src/controller/installation.php @@ -0,0 +1,144 @@ +'); + } + } + if(!extension_loaded('imagick') && !extension_loaded('gd')){ + echo("il manque une de ces extensions au choix: imagick (de préférence) ou gd
"); + } + + /* -- droits des fichiers et dossiers -- */ + $droits_dossiers = 0700; + $droits_fichiers = 0600; + + // accès interdit en HTTP + if(!file_exists('../config/.htaccess')){ + $contenu = <<< HTACCESS + + Order Allow,Deny + Deny from all + +HTACCESS; + + $fichier = fopen('../config/.htaccess', 'w'); + fputs($fichier, $contenu); + fclose($fichier); + chmod('../config/.htaccess', $droits_fichiers); + //echo("danger
pas de .htaccess dans config
prévenez le respondable du site"); + //die; + } + + // accès limité en local (600) pour 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 de la page d'accueil à la toute 1ère visite du site +// les informations ici ne sont pas demandées à l'utilisateur pour l'instant (on verra ça plus tard) +function makeStartPage(EntityManager $entityManager){ + /* -- table page -- */ + // paramètres: name_page, end_of_path, reachable, in_menu, parent + $accueil = new Page('Accueil', 'accueil', true, true, NULL); + $connection = new Page('Connexion', 'connexion', true, false, NULL); + $article = new Page('Article', 'article', true, false, NULL); + $edit_page = new Page("Modification d'une page", 'modif_page', true, false, NULL); + $new_page = new Page('Nouvelle page', 'nouvelle_page', true, false, NULL); + $edit_paths = new Page("Menu et chemins", 'menu_chemins', true, false, NULL); + + /* -- table node -- */ + // paramètres: name_node, article_timestamp, attributes, position, parent, page, article + $head_accueil = new Node('head', NULL, ['css_array' => ['body', 'head', 'nav', 'main', 'foot'], 'js_array' => ['main']], 1, NULL, $accueil, NULL); + $header = new Node('header', NULL, [], 2, NULL, NULL, NULL); + $nav = new Node('nav', NULL, [], 1, $header, NULL, NULL); + $main = new Node('main', NULL, [], 3, NULL, NULL, NULL); + $footer = new Node('footer', NULL, [], 4, NULL, NULL, NULL); + $breadcrumb = new Node('breadcrumb', NULL, [], 1, $footer, NULL, NULL); + $head_login = new Node('head', NULL, ["stop" => true, 'css_array' => ['body', 'head', 'nav', 'main'], 'js_array' => ['main']], 1, NULL, $connection, NULL); + $login = new Node('login', NULL, [], 1, $main, $connection, NULL); + $head_article = new Node('head', NULL, ['css_array' => ['body', 'head', 'nav', 'main', 'foot'], 'js_array' => ['main']], 1, NULL, $article, NULL); + + /* -- table image -- */ + // paramètres: file_name, file_path, file_path_mini, mime_type, alt + $favicon = new Image("favicon48x48.png", NULL, "assets/favicon48x48.png", "image/png", "favicon"); + $logo = new Image("logo-120x75.jpg", NULL, "assets/logo-120x75.jpg", "image/png", "head_logo"); + $facebook = new Image("facebook.svg", NULL, "assets/facebook.svg", "image/svg+xml", "facebook"); + $instagram = new Image("instagram.svg", NULL, "assets/instagram.svg", "image/svg+xml", "instagram"); + $fond_piscine = new Image("fond-piscine.jpg", "assets/fond-piscine.jpg", NULL, "images/jpg", "fond-piscine"); + + /* -- table node_data -- */ + // paramètres: data, node + $head_accueil_data = new NodeData(["description" => "Club, École de natation et Perfectionnement", "title" => "Les Nageurs Bigoudens"], $head_accueil, new ArrayCollection([$favicon])); + $header_data = new NodeData(["description" => "Club, École de natation et Perfectionnement", "title" => "Les Nageurs Bigoudens", "facebook_link" => "https://www.facebook.com/nageursbigoudens29120", "instagram_link" => "https://www.instagram.com/nageursbigoudens/"], $header, new ArrayCollection([$logo, $facebook, $instagram, $fond_piscine])); + $footer_data = new NodeData(["adresse" => "17, rue Raymonde Folgoas Guillou, 29120 Pont-l’Abbé", "contact_nom" => "Les Nageurs Bigoudens", "e_mail" => "nb.secretariat@orange.fr"], $footer); + $head_login_data = new NodeData(["description" => "Connexion", "title" => "Connexion"], $head_login, new ArrayCollection([$favicon])); + $head_article_data = new NodeData(["description" => "", "title" => ""], $head_article, new ArrayCollection([$favicon])); + + $entityManager->persist($accueil); + $entityManager->persist($connection); + $entityManager->persist($article); + $entityManager->persist($edit_page); + $entityManager->persist($new_page); + $entityManager->persist($edit_paths); + $entityManager->persist($head_accueil); + $entityManager->persist($header); + $entityManager->persist($nav); + $entityManager->persist($main); + $entityManager->persist($footer); + $entityManager->persist($breadcrumb); + $entityManager->persist($head_login); + $entityManager->persist($login); + $entityManager->persist($head_article); + $entityManager->persist($favicon); + $entityManager->persist($logo); + $entityManager->persist($facebook); + $entityManager->persist($instagram); + $entityManager->persist($fond_piscine); + $entityManager->persist($head_accueil_data); + $entityManager->persist($header_data); + $entityManager->persist($footer_data); + $entityManager->persist($head_login_data); + $entityManager->persist($head_article_data); + $entityManager->flush(); + + header('Location: ' . new URL); + die; +} \ No newline at end of file diff --git a/src/controller/password.php b/src/controller/password.php new file mode 100644 index 0000000..d5e66ff --- /dev/null +++ b/src/controller/password.php @@ -0,0 +1,357 @@ +getRepository(User::class)->findAll(); + + // cas particulier table vide + if(count($users) === 0) + { + $_GET = []; + $_SESSION['user'] = ''; + $_SESSION['admin'] = false; + + // création d'un utilisateur, puis rechargement de la page + createPassword($entityManager); + } +} + + +function createPassword(EntityManager $entityManager) +{ + // fonction exécutée à priori deux fois d'affilée: affichage puis traitement de la saisie + + // II - traitement + unset($_SESSION['user']); + + $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; + $captcha = isset($_POST['captcha']) ? controlCaptchaInput($_POST['captcha']) : 0; + + $error = ''; + if(!isset($_POST['captcha'])) // page rechargée + { + //$error = ''; + } + elseif($captcha == 0) + { + $error = 'error_non_valid_captcha'; + } + elseif($captcha_solution == 0) + { + //$error = ''; + } + elseif($captcha != $captcha_solution) // le test! + { + $error = 'bad_solution_captcha'; + } + elseif(!isset($_POST['password']) || empty($_POST['password']) + || (!isset($_POST['login']) || empty($_POST['login']))) + { + $error = 'bad_login_or_password'; + } + else + { + // -> caractères HTML dangereux supprimés + $login = Security::secureString($_POST['login']); + $password = Security::secureString($_POST['password']); + + // -> prévenir la validation par erreur d'une chaine "vide" + $login = removeSpacesTabsCRLF($login); + $password = removeSpacesTabsCRLF($password); + + // conformité + if(isset($password) && isset($login) + && $password == $_POST['password'] && $login == $_POST['login']) + { + // enregistrement et redirection + $password = password_hash($password, PASSWORD_DEFAULT); + $user = new App\Entity\User($login, $password); + $entityManager->persist($user); + $entityManager->flush(); + + header('Location: ' . new URL); + exit(); + } + else + { + $error = 'bad_password'; + } + } + + // inséré dans $captchaHtml puis dans $formulaireNouveauMDP + $captcha = createCaptcha(); + // enregistrement de la réponse du captcha pour vérification + $_SESSION['captcha'] = $captcha[2]; // int + + + // I - affichage + $title = 'Bienvenue nageur bigouden'; + $subHeading = 'Veuillez choisir les codes que vous utiliserez pour gérer le site.'; + + // même vue que la fonction changerMotDePasse() + require('../src/view/password.php'); + + echo($header); + if($error != '') + { + sleep(1); + echo($error_messages[$error]); + } + echo($formulaireNouveauMDP); + echo($error_messages['forbidden_characters']); + echo($footer); + die; +} + + +function connect(LoginBuilder $builder, EntityManager $entityManager) +{ + // déjà connecté + if($_SESSION['admin']) + { + header('Location: ' . new URL); + die; + } + + // II - traitement + $_SESSION['user'] = ''; + $_SESSION['admin'] = false; + + $captcha_solution = (isset($_SESSION['captcha']) && is_int($_SESSION['captcha'])) ? $_SESSION['captcha'] : 0; + $captcha = isset($_POST['captcha']) ? controlCaptchaInput($_POST['captcha']) : 0; + + $error = ''; + if(!isset($_POST['captcha'])) // page rechargée + { + //$error = ''; + } + elseif($captcha == 0) + { + $error = 'error_non_valid_captcha'; + } + elseif($captcha_solution == 0) + { + //$error = ''; + } + elseif($captcha != $captcha_solution) // le test! + { + $error = 'bad_solution_captcha'; + } + elseif(!isset($_POST['login']) || empty($_POST['login']) + || !isset($_POST['password']) || empty($_POST['password'])) + { + $error = 'bad_password'; + } + else // c'est OK + { + $login = $_POST['login']; + $password = $_POST['password']; + $user = getUser($login, $entityManager); + + // enregistrement et redirection + if(password_verify($password, $user->getPassword())) + { + session_start(); + $_SESSION['user'] = $login; + $_SESSION['admin'] = true; + $link = new URL(isset($_GET['from']) ? ['page' => $_GET['from']] : []); + isset($_GET['id']) ? $link->addParams(['id' => $_GET['id']]) : ''; + header('Location: ' . $link); + die; + } + else + { + $error = 'bad_password'; + } + } + + // inséré dans $captchaHtml puis dans $formulaireNouveauMDP + $captcha = createCaptcha(); + // enregistrement de la réponse du captcha pour vérification + $_SESSION['captcha'] = $captcha[2]; // int + + // I - affichage + $title = "Connexion"; + $subHeading = "Veuillez saisir votre identifiant (e-mail) et votre mot de passe."; + + require('../src/view/password.php'); + + //$builder->addHTML($header); + if($error != '') + { + sleep(1); + $builder->addHTML($error_messages[$error]); + } + $builder->addHTML($formulaireConnexion); + //$builder->addHTML($warning_messages['message_cookie']); + $builder->addHTML($warning_messages['private_browsing']); + $builder->addHTML($footer); + + //die; +} + + +function changePassword(EntityManager $entityManager) +{ + // fonction exécutée à priori deux fois d'affilée: affichage puis traitement de la saisie + + // OUT !! + if(empty($_SESSION['user']) || !$_SESSION['admin']) + { + $_SESSION['user'] = ''; + $_SESSION['admin'] = false; + header('Location: index.php'); + die; + } + + // II - traitement + $error = ''; + $success = false; + if(empty($_POST)) // première fois ou page rechargée + { + // + } + elseif(!isset($_POST['login']) || empty($_POST['login']) + || !isset($_POST['old_password']) || empty($_POST['old_password']) + || !isset($_POST['new_password']) || empty($_POST['new_password'])) + { + $error = 'bad_login_or_password'; + } + else + { + // sécurisation de la saisie + $new_password = Security::secureString($_POST['new_password']); + $login = Security::secureString($_POST['login']); + $old_password = Security::secureString($_POST['old_password']); + + // éviter d'enregistrer une chaîne vide + $new_password = removeSpacesTabsCRLF($new_password); + + // tests de conformité + if($login != $_POST['login'] || $old_password != $_POST['old_password'] || $new_password != $_POST['new_password']) + { + $error = 'forbidden_characters'; + } + else + { + $user = getUser($login, $entityManager); + + if(password_verify($old_password, $user->getPassword())) + { + // enregistrement et redirection + $new_password = password_hash($new_password, PASSWORD_DEFAULT); + $user->setPassword($new_password); + $entityManager->flush(); + $success = true; + } + else + { + $error = 'bad_password'; + } + } + } + + + // I - affichage + $title = "Nouveau mot de passe"; + $subHeading = "Veuillez vous identifier à nouveau puis saisir votre nouveau mot de passe."; + + require('../src/view/password.php'); + + echo($header); + if($error != '') + { + sleep(1); // sécurité TRÈS insuffisante à la force brute + echo($error_messages[$error]); + } + elseif($success) + { + $success = false; + echo($alertJSNewPassword); + die; + } + echo($formulaireModifMDP); + echo($footer); + die; +} + + +function getUser(string $login, EntityManager $entityManager): User +{ + $users = $entityManager->getRepository('App\Entity\User')->findBy(['login' => $login]); + + // détection d'un abus + if(count($users) === 0) + { + $_SESSION['user'] = ''; + $_SESSION['admin'] = false; + + header('Location: index.php'); // page création d'un mot de passe à l'attérissage + die; + } + + foreach($users as $user) + { + if($user->getLogin() === $login) + { + return $user; + } + } + header('Location: ' . new URL); + die; +} + + +function disconnect(EntityManager $entityManager) +{ + // nettoyage complet + $_SESSION = []; // mémoire vive + session_destroy(); // fichier côté serveur + setcookie('PHPSESSID', '', time() - 4200, '/'); // cookie de session + $link = new URL(['page' => $_GET['page']]); + isset($_GET['id']) ? $link->addParams(['id' => $_GET['id']]) : ''; + header('Location: ' . $link); + die; +} + + +function createCaptcha(): array +{ + $a = rand(2, 9); + $b = rand(2, 9); + return array(toLettersFrench($a), toLettersFrench($b), $a * $b); +} + +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 + }; +} + +// on veut des chiffres +function controlCaptchaInput(string $captcha = '0'): int +{ + // $captcha est un POST donc une chaîne, '2.3' est acceptés + // (int) supprime les décimales + return (is_numeric($captcha) && $captcha == (int) $captcha) ? (int) $captcha : 0; +} \ No newline at end of file diff --git a/src/controller/post.php b/src/controller/post.php new file mode 100644 index 0000000..926a5ae --- /dev/null +++ b/src/controller/post.php @@ -0,0 +1,17 @@ +children = new ArrayCollection(); + + $bulk_data = $entityManager + ->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){ + makeStartPage($entityManager); + } + + foreach($bulk_data as $first_level_entries){ + // génération du menu + if($first_level_entries->getInMenu()){ + $this->addChild($first_level_entries); + } + // autres pages + else{ + // attention, seul le premier élément du chemin est pris en compte + $this->other_pages[] = $first_level_entries; + } + } + + foreach($this->getChildren() as $page){ + $page->fillChildrenPagePath(); + } + + /*for($i = 0; $i < count($this->getChildren()[1]->getChildren()); $i++){ + echo $this->getChildren()[1]->getChildren()[$i]->getEndOfPath() . ' - '; + echo $this->getChildren()[1]->getChildren()[$i]->getPageName() . '
'; + }*/ + //die; + } + + public function getOtherPages(): array + { + return $this->other_pages; + } +} \ No newline at end of file diff --git a/src/model/Path.php b/src/model/Path.php new file mode 100644 index 0000000..6faadfd --- /dev/null +++ b/src/model/Path.php @@ -0,0 +1,84 @@ +findPage(Director::$menu_data, $path_array); // remplit $this->current_page + } + catch(Exception $e){} + /*echo "nb d'autres pages: " . count(Director::$menu_data->getOtherPages()) . '
'; + echo 'longueur du chemin: ' . count($this->current_page) . '
'; + foreach($this->current_page as $current){ + echo $current->getEndOfPath() . ' '; + } + die;*/ + } + + // produit un tableau de Page en comparant le chemin demandé avec les données dans Menu + // succès => une exception est lancée pour sortir des fonctions imbriquées + // echec => redirection vers la page erreur 404 + private function findPage(Page|Menu $menu, array $path_array) + { + // recherche dans les autres pages + if($menu instanceof Menu){ + foreach($menu->getOtherPages() as $page) + { + if($path_array[0] === $page->getEndOfPath()) + { + $this->current_page[] = $page; + throw new Exception(); + } + } + } + // recherche dans le menu + foreach($menu->getChildren() as $page) + { + if($path_array[0] === $page->getEndOfPath()) + { + $this->current_page[] = $page; + if(count($path_array) > 1) + { + array_shift($path_array); // $this->path_array n'est pas modifié, un tableau PHP est passé à une fonction par copie + $this->findPage($page, $path_array); + } + else{ + throw new Exception(); // sortir de tous les findPage() en même temps + } + } + } + // rien trouvé + URL::setPath('erreur404.html'); + header('Location: '. new URL); + die; + } + + public function getString(): string + { + $path_string = ""; + foreach($this->current_page as $one_page){ + $path_string .= $one_page->getEndOfPath() . '/'; + } + return rtrim($path_string, '/'); + } + public function getArray(): array + { + return $this->current_page; + } + + // c'est là qu'on est quoi + public function getLast(): Page + { + return $this->current_page[count($this->current_page) - 1]; + } +} \ No newline at end of file diff --git a/src/model/doctrine-bootstrap.php b/src/model/doctrine-bootstrap.php new file mode 100644 index 0000000..139f410 --- /dev/null +++ b/src/model/doctrine-bootstrap.php @@ -0,0 +1,31 @@ + Config::$db_driver, + 'user' => Config::$user, + 'password' => Config::$password, + 'host' => Config::$db_host, + 'dbname' => Config::$database, +], $config); + +// obtaining the entity manager +$entityManager = new EntityManager($connection, $config); + +foreach($entityManager->getMetadataFactory()->getAllMetadata() as $class){} \ No newline at end of file diff --git a/src/model/entities/Article.php b/src/model/entities/Article.php new file mode 100644 index 0000000..3b846da --- /dev/null +++ b/src/model/entities/Article.php @@ -0,0 +1,77 @@ + 'CURRENT_TIMESTAMP'], unique: true)] + private \DateTime $date_time; // le type datetime de doctrine convertit en type \DateTime de PHP + + #[ORM\Column(type: "string")] + private string $title; + + #[ORM\Column(type: "text")] + private string $preview; // une simple textarea + + #[ORM\Column(type: "text")] + private string $content; // de l'éditeur html + + // liaison avec table intermédiaire + #[ORM\ManyToMany(targetEntity: Image::class, inversedBy: "article")] + #[ORM\JoinTable( + name: "nb_article_image", + joinColumns: [new ORM\JoinColumn(name: "article_id", referencedColumnName: "id_article", onDelete: "CASCADE")], + inverseJoinColumns: [new ORM\JoinColumn(name: "image_id", referencedColumnName: "id_image", onDelete: "CASCADE")] + )] + private Collection $images; + + public function __construct() + { + $this->images = new ArrayCollection(); // initialisation nécessaire + } + + public function getDateTime(): \DateTime + { + return $this->date_time; + } + public function getTimestamp(): int + { + return $this->date_time->getTimestamp(); + } + public function getTitle(): string + { + return $this->title; + } + public function getPreview(): string + { + return $this->preview; + } + public function getContent(): string + { + return $this->content; + } + public function setContent(string $data): void + { + $this->content = $data; + } + + public function getImages(): Collection + { + return $this->images; + } +} diff --git a/src/model/entities/Image.php b/src/model/entities/Image.php new file mode 100644 index 0000000..181c137 --- /dev/null +++ b/src/model/entities/Image.php @@ -0,0 +1,91 @@ + Validation du type de fichier : On vérifie que le fichier est bien une image en utilisant le type MIME. On peut aussi vérifier la taille du fichier. + => Création d'un répertoire structuré : On génère un chemin dynamique basé sur la date (uploads/2024/12/24/) pour organiser les images. + => Génération d'un nom de fichier unique : On utilise uniqid() pour générer un nom unique et éviter les conflits de nom. + => Déplacement du fichier sur le serveur : Le fichier est déplacé depuis son emplacement temporaire vers le répertoire uploads/. + => Enregistrement dans la base de données : On enregistre les informations de l'image dans la base de données. */ + + #[ORM\ManyToMany(targetEntity: NodeData::class, mappedBy: "images")] + private $node_data; + + #[ORM\ManyToMany(targetEntity: Article::class, mappedBy: "images")] + private $article; + + public function __construct(string $name, ?string $path, ?string $path_mini, string $mime_type, string $alt) + { + $this->file_name = $name; + $this->file_path = $path; + $this->file_path_mini = $path_mini; + $this->mime_type = $mime_type; + $this->alt = $alt; + } + + public function getFileName(): string + { + return $this->file_name; + } + public function getFilePath(): string + { + return $this->file_path; + } + public function getFilePathMini(): string + { + return $this->file_path_mini; + } + public function getAlt(): string + { + return $this->alt; + } + + + // pour ViewBuilder? + /*public function displayImage($imageId): void + { + //$imageId = 1; // Exemple d'ID d'image + $stmt = $pdo->prepare("SELECT file_path FROM images WHERE id = ?"); + $stmt->execute([$imageId]); + $image = $stmt->fetch(); + + if ($image) { + echo "Image"; + } else { + echo "Image non trouvée."; + } + }*/ +} diff --git a/src/model/entities/Node.php b/src/model/entities/Node.php new file mode 100644 index 0000000..49e16ba --- /dev/null +++ b/src/model/entities/Node.php @@ -0,0 +1,168 @@ +name_node = $name; + $this->article_timestamp = $article_timestamp; + $this->attributes = $attributes; + $this->position = $position; + $this->parent = $parent; + $this->page = $page; + $this->article = $article; + } + + public function addChild(self $child): void + { + $this->children[] = $child; + $this->sortChildren(); + } + + // utiliser $position pour afficher les éléments dans l'ordre + private function sortChildren(): void + { + $iteration = count($this->children); + while($iteration > 1) + { + for($i = 0; $i < $iteration - 1; $i++) + { + //echo '
' . $this->children[$i]->getPosition() . ' - ' . $this->children[$i + 1]->getPosition(); + if($this->children[$i]->getPosition() > $this->children[$i + 1]->getPosition()) + { + $tmp = $this->children[$i]; + $this->children[$i] = $this->children[$i + 1]; + $this->children[$i + 1] = $tmp; + } + } + $iteration--; + } + } + + // pfff... + public function getId(): int + { + return $this->id_node; + } + public function getName(): string + { + return $this->name_node; + } + /*public function setName(string $name): void + { + $this->name_node = $name; + }*/ + public function getArticleTimestamp(): string + { + return $this->article_timestamp; + } + public function getAttributes(): array + { + return $this->attributes; + } + /*public function setAttributes(array $attributes): void + { + $this->attributes = $attributes; + }*/ + public function getParent(): ?self + { + return $this->parent; + } + /*public function setParent(?self $parent): void + { + $this->parent = $parent; + }*/ + public function getPosition(): int + { + return $this->position; + } + /*public function setPosition(int $position): void + { + $this->position = $position; + }*/ + public function getPage(): Page + { + return $this->page; + } + /*public function setPage(Page $page): void + { + $this->page = $page; + }*/ + public function getArticle(): Article + { + return $this->article; + } + /*public function setArticle(Article $article): void + { + $this->article = $article; + }*/ + public function getNodeData(): ?NodeData + { + return $this->node_data; + } + public function getChildren(): array + { + return $this->children; + } + + public function getTempChild(): ?self // peut renvoyer null + { + return $this->temp_child; + } + public function setTempChild(self $child): void + { + $this->temp_child = $child; + } +} diff --git a/src/model/entities/NodeData.php b/src/model/entities/NodeData.php new file mode 100644 index 0000000..ddf6083 --- /dev/null +++ b/src/model/entities/NodeData.php @@ -0,0 +1,62 @@ +data = $data; + $this->node = $node; + $this->images = $images; + } + + public function getData(): array + { + return $this->data; + } + /*public function setData(array $data): void + { + $this->data = $data; + } + public function setNode(Node $node): void + { + $this->node = $node; + }*/ + public function getImages(): Collection + { + return $this->images; + } +} diff --git a/src/model/entities/Page.php b/src/model/entities/Page.php new file mode 100644 index 0000000..d7d8098 --- /dev/null +++ b/src/model/entities/Page.php @@ -0,0 +1,97 @@ +name_page = $name; + $this->end_of_path = $eop; + $this->reachable = $reachable; + $this->in_menu = $in_menu; + $this->parent = $parent; + $this->children = new ArrayCollection(); + } + + // getters + /*public function getId(): int + { + return $this->id_page; + }*/ + public function getPageName(): string + { + return $this->name_page; + } + public function getPagePath(): string + { + return $this->page_path; + } + public function getEndOfPath(): string + { + return $this->end_of_path; + } + public function getInMenu(): bool + { + return $this->in_menu; + } + public function getParent(): ?Page + { + return $this->parent; + } + public function getChildren(): Collection + { + return $this->children; + } + + public function fillChildrenPagePath(string $parent_path = ''): void + { + $this->page_path = $parent_path != '' ? $parent_path . '/' . $this->end_of_path : $this->end_of_path; + foreach($this->getChildren() as $page){ + $page->fillChildrenPagePath($this->page_path); + } + } + + public function addChild(self $child): void + { + $this->children[] = $child; + } +} diff --git a/src/model/entities/User.php b/src/model/entities/User.php new file mode 100644 index 0000000..4b1dcb8 --- /dev/null +++ b/src/model/entities/User.php @@ -0,0 +1,47 @@ +login = $login; + $this->password = $password; + } + + public function getLogin(): string + { + return $this->login; + } + public function getPassword(): string + { + return $this->password; + } + + public function setPassword(string $password): void + { + $this->password = $password; + } +} \ No newline at end of file diff --git a/src/view/AbstractBuilder.php b/src/view/AbstractBuilder.php new file mode 100644 index 0000000..cd2b361 --- /dev/null +++ b/src/view/AbstractBuilder.php @@ -0,0 +1,52 @@ +getChildren() as $child_node) + { + $builder_name = $this->snakeToPascalCase($child_node->getName()) . 'Builder'; + $builder = new $builder_name($child_node); + $this->html .= $builder->render(); + + // pages spéciales où on n'assemble pas tout + if($builder_name === 'HeadBuilder' && $builder->getStop()) + { + foreach($node->getChildren() as $target_node){ + if($target_node->getName() === 'main'){ + $main_node = $target_node; + break; + } + } + // on construit
et on s'arrête! les autres noeuds sont ignorés + $builder_name = $this->snakeToPascalCase($main_node->getName()) . 'Builder'; + $builder = new $builder_name($main_node); + $this->html .= "\n"; + $this->html .= $builder->render() . "\n"; + $this->html .= "\n"; + break; + } + } + } + + protected function snakeToPascalCase(string $input): string + { + return str_replace('_', '', ucwords($input, '_')); + } + + public function render(): string // = getHTML() + { + return $this->html; + } + public function addHTML(string $html): void + { + $this->html .= $html; + } +} \ No newline at end of file diff --git a/src/view/ArticleBuilder.php b/src/view/ArticleBuilder.php new file mode 100644 index 0000000..989da0d --- /dev/null +++ b/src/view/ArticleBuilder.php @@ -0,0 +1,60 @@ +getName() . '.php'; + + if(file_exists($viewFile)) + { + // id (timestamp) + if(!empty($node->getAttributes())) + { + extract($node->getAttributes()); + } + + // html + $title = $node->getArticle()->getTitle(); + $html = $node->getArticle()->getContent(); + $id = $node->getArticleTimestamp(); + + // partage + $share_link = new URL(['page' => CURRENT_PAGE], $id); + $share_js = 'onclick="copyInClipBoard(\'' . $share_link . '\')"'; + $share_button = '

' . "\n"; + + // modifier un article + $admin_buttons = ''; + if($_SESSION['admin']) + { + $modify_js = 'onclick="openEditor(\'' . $id . '\')"'; + $modify_article = '

' . "\n"; + + $up_link = new URL(['page' => CURRENT_PAGE, 'id' => $id, 'action' => 'position_up']); + $up_button = '

' . "\n"; + + $down_link = new URL(['page' => CURRENT_PAGE, 'id' => $id, 'action' => 'position_down']); + $down_button = '

' . "\n"; + + $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"'; + $delete_article = '

' . "\n"; + + $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; + $close_editor = ''; + + $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; + $submit_article = ''; + + $admin_buttons = $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article; + } + + ob_start(); + require($viewFile); + $this->html .= ob_get_clean(); + } + } +} diff --git a/src/view/BlogBuilder.php b/src/view/BlogBuilder.php new file mode 100644 index 0000000..8c2125f --- /dev/null +++ b/src/view/BlogBuilder.php @@ -0,0 +1,49 @@ +getName() . '.php'; + + if(file_exists($viewFile)) + { + if(!empty($node->getNodeData()->getData())) + { + extract($node->getNodeData()->getData()); + } + + // ajouter un article + $new_article = ''; + $new_article_admin_buttons = ''; + if($_SESSION['admin']) + { + $id = 'new'; + + //$link = new URL(['page' => CURRENT_PAGE, 'action' => 'open_editor']); + $js = 'onclick="openEditor(\'' . $id . '\')"'; + //$new_article = ''; + $new_article = '

' . "\n" . + '

'; + + $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; + $close_editor = '
'; + + $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; + $submit_article = '
'; + + $new_article_admin_buttons = $close_editor . $submit_article; + } + + $this->useChildrenBuilder($node); + $content = $this->html; + + ob_start(); + require $viewFile; + $this->html = ob_get_clean(); // pas de concaténation ici, on écrase + } + } +} \ No newline at end of file diff --git a/src/view/BreadcrumbBuilder.php b/src/view/BreadcrumbBuilder.php new file mode 100644 index 0000000..f1fdddf --- /dev/null +++ b/src/view/BreadcrumbBuilder.php @@ -0,0 +1,53 @@ +html = $this->breadcrumbHTML(false); + } + + private function breadcrumbHTML(bool $links = false): string + { + $asset = 'assets/home.svg'; // => BDD? + $breadcrumb_array = Director::$page_path->getArray(); // tableau de Page + $html = ''; + $nb_of_entries = count($breadcrumb_array); + + if($nb_of_entries > 1) + { + // petite maison et flèche + $html .= '\n"; + } + return $html; + } +} \ No newline at end of file diff --git a/src/view/FooterBuilder.php b/src/view/FooterBuilder.php new file mode 100644 index 0000000..49da71c --- /dev/null +++ b/src/view/FooterBuilder.php @@ -0,0 +1,64 @@ +getName() . '.php'; + + if(file_exists($viewFile)) + { + // $adresses postale et e-mail + if(!empty($node->getNodeData()->getData())) + { + extract($node->getNodeData()->getData()); + } + + $this->useChildrenBuilder($node); + $breadcrumb = $this->html; + + // zone admin + $empty_admin_zone = ''; + //$zone_admin = ''; + if($_SESSION['admin']) + { + $div_admin = 'logged_in'; + $empty_admin_zone = 'empty_admin_zone'; + $link_edit_page = new URL(['page' => CURRENT_PAGE, 'action' => 'modif_page']); + $link_new_page = new URL(['from' => CURRENT_PAGE, 'page' => 'nouvelle_page']); + $link_change_paths = new URL(['from' => CURRENT_PAGE, 'page' => 'menu_chemins']); + + $link_change_password = new URL(['from' => CURRENT_PAGE, 'action' => 'modif_mdp']); + isset($_GET['id']) ? $link_change_password->addParams(['id' => $_GET['id']]) : ''; + + $link_logout = new URL(['page' => CURRENT_PAGE, 'action' => 'deconnexion']); + isset($_GET['id']) ? $link_logout->addParams(['id' => $_GET['id']]) : ''; + + $zone_admin = '

Vous êtes en mode administrateur.' . "\n" . + '' . "\n" . + '' . "\n" . + '' . "\n" . + '' . "\n" . + '

' . "\n"; + } + else + { + $div_admin = 'logged_out'; + $zone_admin = ''; + if(Director::$page_path->getLast()->getEndOfPath() === 'article' && isset($_GET['id'])){ + $zone_admin = ''; + } + else{ + $zone_admin = ''; + } + } + + ob_start(); + require $viewFile; + $this->html = ob_get_clean(); + } + } +} \ No newline at end of file diff --git a/src/view/GaleryBuilder.php b/src/view/GaleryBuilder.php new file mode 100644 index 0000000..89be2b1 --- /dev/null +++ b/src/view/GaleryBuilder.php @@ -0,0 +1,49 @@ +getName() . '.php'; + + if(file_exists($viewFile)) + { + if(!empty($node->getNodeData()->getData())) + { + extract($node->getNodeData()->getData()); + } + + // ajouter un article + $new_article = ''; + $new_article_admin_buttons = ''; + if($_SESSION['admin']) + { + $id = 'new'; + + //$link = new URL(['page' => CURRENT_PAGE, 'action' => 'open_editor']); + $js = 'onclick="openEditor(\'' . $id . '\')"'; + //$new_article = ''; + $new_article = '

' . "\n" . + '

'; + + $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; + $close_editor = '
'; + + $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; + $submit_article = '
'; + + $new_article_admin_buttons = $close_editor . $submit_article; + } + + $this->useChildrenBuilder($node); + $content = $this->html; + + ob_start(); + require $viewFile; + $this->html = ob_get_clean(); // pas de concaténation ici, on écrase + } + } +} \ No newline at end of file diff --git a/src/view/GridBuilder.php b/src/view/GridBuilder.php new file mode 100644 index 0000000..6e16d46 --- /dev/null +++ b/src/view/GridBuilder.php @@ -0,0 +1,55 @@ +getName() . '.php'; + + if(file_exists($viewFile)) + { + if(!empty($node->getNodeData()->getData())) + { + extract($node->getNodeData()->getData()); + } + + // ajouter un article + $new_article = ''; + $new_article_admin_buttons = ''; + if($_SESSION['admin']) + { + $id = 'new'; + $js = 'onclick="openEditor(\'' . $id . '\')"'; + + if(Director::$page_path->getLast()->getEndOfPath() === 'accueil') + { + $new_article = '

' . "\n" . + '

'; + } + else + { + $new_article = '

' . "\n" . + '

'; + } + + $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; + $close_editor = '
'; + + $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; + $submit_article = '
'; + + $new_article_admin_buttons = $close_editor . $submit_article; + } + + $this->useChildrenBuilder($node); + $content = $this->html; + + ob_start(); + require $viewFile; + $this->html = ob_get_clean(); // pas de concaténation ici, on écrase + } + } +} \ No newline at end of file diff --git a/src/view/HeadBuilder.php b/src/view/HeadBuilder.php new file mode 100644 index 0000000..c31c930 --- /dev/null +++ b/src/view/HeadBuilder.php @@ -0,0 +1,68 @@ +getName() . '.php'; + + if(file_exists($viewFile)) + { + // css et js + if(!empty($node->getAttributes())) + { + extract($node->getAttributes()); + } + + // pages spéciales où on n'assemble pas tout + $this->stop = isset($stop) ? $stop : false; + $css = ''; + foreach($css_array as $name) + { + $css .= '' . "\n"; + } + $js = ''; + foreach($js_array as $name) + { + $js .= '' . "\n"; + } + + // tinymce, nécéssite un script de copie dans composer.json + if($_SESSION['admin']){ + $css .= '' . "\n"; + $js .= '' . "\n"; + $js .= '' . "\n"; + } + + // titre et description + if(!empty($node->getNodeData()->getData())) + { + extract($node->getNodeData()->getData()); + } + + // favicon + foreach($node->getNodeData()->getImages() as $image) + { + if(str_contains($image->getFileName(), 'favicon')) + { + $favicon = rtrim($image->getFilePathMini(), '/'); + $alt = $image->getAlt(); + } + } + + ob_start(); + require $viewFile; + $this->html .= ob_get_clean(); + } + } + + public function getStop(): bool + { + return $this->stop; + } +} diff --git a/src/view/HeaderBuilder.php b/src/view/HeaderBuilder.php new file mode 100644 index 0000000..252958a --- /dev/null +++ b/src/view/HeaderBuilder.php @@ -0,0 +1,64 @@ +getChildren(); + foreach($children as $child) + { + if($child->getName() === 'nav') + { + $this->nav = $child; + $nav_builder = new NavBuilder($this->nav); + $nav = $nav_builder->render(); + } + } + + $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; + + if(file_exists($viewFile)) + { + // titre et description + if(!empty($node->getNodeData()->getData())) + { + extract($node->getNodeData()->getData()); + } + + // attributs, aucun pour l'instant + if(!empty($node->getAttributes())) + { + extract($node->getAttributes()); + } + + // header logo + réseaux sociaux + $targets = ['logo', 'facebook', 'instagram', 'fond_piscine']; + $i = 0; + foreach($node->getNodeData()->getImages() as $image) + { + if(str_contains($image->getFileName(), $targets[$i])) + { + $var = $targets[$i]; + $$var = rtrim($image->getFilePathMini(), '/'); + $var .= '_alt'; // ex: logo_alt + $$var = $image->getAlt(); + } + $i++; + } + + // générer HTML réseaux sociaux + // + + ob_start(); + require $viewFile; + $this->html .= ob_get_clean(); + } + } +} \ No newline at end of file diff --git a/src/view/LoginBuilder.php b/src/view/LoginBuilder.php new file mode 100644 index 0000000..ac9910f --- /dev/null +++ b/src/view/LoginBuilder.php @@ -0,0 +1,15 @@ +html .= "
\n"; + + if(Director::$page_path->getLast()->getEndOfPath() === 'article'){ + if($node->getTempChild() == null){ + $new = new Node; + } + else{ + $new = $node->getTempChild(); + } + //$builder_name = $this->snakeToPascalCase($new->getName()) . 'Builder'; + $builder_name = 'NewBuilder'; + $builder = new $builder_name($new); + $this->html .= $builder->render(); + } + else{ + $this->useChildrenBuilder($node); + } + + $this->html .= "
\n"; + } +} diff --git a/src/view/NavBuilder.php b/src/view/NavBuilder.php new file mode 100644 index 0000000..e7254b1 --- /dev/null +++ b/src/view/NavBuilder.php @@ -0,0 +1,61 @@ +html .= ''; + } + + private function navMainHTML(Page $nav_data, array $current): string + { + $nav_html = ''; + static $level = 0; + + foreach($nav_data->getChildren() as $data) + { + $class = ''; + if(isset($current[$level]) && $data->getEndOfPath() === $current[$level]->getEndOfPath()){ + $class = ' current'; + } + + if(count($data->getChildren()) > 0) // titre de catégorie + { + $nav_html .= '' . "\n"; + } + else + { + $target = ''; + if(str_starts_with($data->getEndOfPath(), 'http')) // lien vers autre site + { + $link = $data->getEndOfPath(); // $link = chaine + $target = ' target="_blank"'; + } + elseif($data->getEndOfPath() != '') // lien relatif + { + $link = new URL(['page' => $data->getPagePath()]); // $link = objet + } + /*else + { + echo "else page d'accueil" . '
'; + $link = new URL; // page d'accueil + }*/ + + $nav_html .= '
  • ' . $data->getPageName() . '

  • ' . "\n"; + } + } + return $nav_html; + } +} \ No newline at end of file diff --git a/src/view/NewBuilder.php b/src/view/NewBuilder.php new file mode 100644 index 0000000..605c174 --- /dev/null +++ b/src/view/NewBuilder.php @@ -0,0 +1,93 @@ +getName() . '.php'; + + if(file_exists($viewFile)) + { + // id (timestamp) + if(!empty($node->getAttributes())) + { + extract($node->getAttributes()); + } + + // html, date + $title = $node->getArticle()->getTitle(); + $preview = $node->getArticle()->getPreview(); + $id = $node->getArticleTimestamp(); + $content = ''; + + // page article unique + if(Director::$page_path->getLast()->getEndOfPath() === 'article') + { + $content = $node->getArticle()->getContent(); + $from_to_button = '

    '; + } + // page d'accueil (avec des news) + else + { + $from_to_button = '

    '; + } + + + $date_object = $node->getArticle()->getDateTime(); // class DateTime + $date = 'le ' . str_replace(':', 'h', $date_object->format('d-m-Y à H:i')); + + // partage + $share_link = new URL(['page' => CURRENT_PAGE], $id); + isset($_GET['id']) ? $share_link->addParams(['id' => $_GET['id']]) : ''; + $share_js = 'onclick="copyInClipBoard(\'' . $share_link . '\')"'; + $share_button = '

    ' . "\n"; + + // modifier un article + $admin_buttons = ''; + if($_SESSION['admin']) + { + if(Director::$page_path->getLast()->getEndOfPath() === 'article'){ + $modify_js = 'onclick="openEditor(\'' . $id . '\')"'; + $modify_article = '

    ' . "\n"; + + $up_button = '

    ' . "\n"; + $down_button = '

    ' . "\n"; + + $delete_js = 'onclick="deleteArticle(\'' . $id . '\', \'' . CURRENT_PAGE . '\')"'; + $delete_article = '

    ' . "\n"; + + $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; + $close_editor = ''; + + $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; + $submit_article = ''; + } + else{ + $modify_article = '

    ' . "\n"; + + $up_link = new URL(['page' => CURRENT_PAGE, 'id' => $id, 'action' => 'position_up']); + $up_button = '

    ' . "\n"; + + $down_link = new URL(['page' => CURRENT_PAGE, 'id' => $id, 'action' => 'position_down']); + $down_button = '

    ' . "\n"; + + $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"'; + $delete_article = '

    ' . "\n"; + + $close_editor = ''; + $submit_article = ''; + + $submit_article = ''; + } + $admin_buttons = $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article; + } + + ob_start(); + require($viewFile); + $this->html .= ob_get_clean(); + } + } +} diff --git a/src/view/ViewBuilder.php b/src/view/ViewBuilder.php new file mode 100644 index 0000000..acac972 --- /dev/null +++ b/src/view/ViewBuilder.php @@ -0,0 +1,16 @@ +useChildrenBuilder($root_node); + } +} diff --git a/src/view/password.php b/src/view/password.php new file mode 100644 index 0000000..0ff717c --- /dev/null +++ b/src/view/password.php @@ -0,0 +1,152 @@ + +

    Montrez que vous n'êtes pas un robot.
    + + +

    + 'connexion']); +isset($_GET['from']) ? $link->addParams(['from' => $_GET['from']]) : ''; +isset($_GET['id']) ? $link->addParams(['id' => $_GET['id']]) : ''; +ob_start(); +?> +
    +

    +

    +

    +

    + + + + +
    + +
    +

    +

    +

    +

    + + + + +
    + 'modif_mdp']); +isset($_GET['from']) ? $link->addParams(['from' => $_GET['from']]) : ''; +isset($_GET['id']) ? $link->addParams(['id' => $_GET['id']]) : ''; +ob_start(); +?> +
    + +

    + +

    + + +

    + + +
    + + + + + + + <?= $title ?> + + + + + + + + +
    +

    +

    + '

    Erreur au test anti-robot, veuillez saisir un nombre entier.

    ', + 'bad_solution_captcha' => '

    Erreur au test anti-robot, veuillez réessayer.

    ', + 'bad_login_or_password' => '

    Saisir un Identifiant (e-mail) et un mot de passe.

    ', + 'bad_password' => '

    Mauvais mot de passe, veuillez réessayer.

    ', + 'forbidden_characters' => '

    Caractères interdits: espaces, tabulations, sauts CR/LF.

    ' +]; + +$warning_messages = [ + 'message_disconnect' => "

    N'oubliez de cliquer sur 'déconnexion' quand vous aurez fini.

    ", + //'message_cookie' => "

    Ce site utilise un cookie « obligatoire » lorsque vous êtes connecté ainsi que sur cette page.
    Il sera supprimé à votre déconnexion ou dès que vous aurez quitté le site.

    ", + 'private_browsing' =>"

    Au fait? Vous n'utilisez pas votre propre ordinateur ou téléphone?
    + Utilisez la navigation privée.

    " +]; + + +// confirmation modification du mot de passe +$page = isset($_GET['from']) ? $_GET['from'] : 'accueil'; +$id = isset($_GET['id']) ? ', \'' . $_GET['id'] . '\'' : ''; +$js = "newPassword('" . $page . "'" . $id . ");"; +ob_start(); +?> + + +addParams(['page' => $_GET['from'] ]) : ''; +isset($_GET['id']) ? $link->addParams(['id' => $_GET['id']]) : ''; +ob_start(); +if(isset($_GET['from'])) // exclue la "création du mot de passe" +{ +?> +

    + + + +

    + +
    +
    +

    + +
    +
    + +
    +
    + +
    +
    + +
    \ No newline at end of file diff --git a/src/view/templates/blog.php b/src/view/templates/blog.php new file mode 100644 index 0000000..35cac8b --- /dev/null +++ b/src/view/templates/blog.php @@ -0,0 +1,6 @@ +
    +

    + + + +
    \ No newline at end of file diff --git a/src/view/templates/footer.php b/src/view/templates/footer.php new file mode 100644 index 0000000..5af7924 --- /dev/null +++ b/src/view/templates/footer.php @@ -0,0 +1,15 @@ +
    +
    +


    +
    +

    + +
    +
    +
    + +
    +
    + + + \ No newline at end of file diff --git a/src/view/templates/galery.php b/src/view/templates/galery.php new file mode 100644 index 0000000..306526f --- /dev/null +++ b/src/view/templates/galery.php @@ -0,0 +1,10 @@ +
    +

    + + +
    +

    + +
    + +
    \ No newline at end of file diff --git a/src/view/templates/grid.php b/src/view/templates/grid.php new file mode 100644 index 0000000..a09ed40 --- /dev/null +++ b/src/view/templates/grid.php @@ -0,0 +1,8 @@ +
    +

    + + +
    + +
    +
    \ No newline at end of file diff --git a/src/view/templates/head.php b/src/view/templates/head.php new file mode 100644 index 0000000..1ebb17e --- /dev/null +++ b/src/view/templates/head.php @@ -0,0 +1,11 @@ + + + + + <?= $title ?> + + + + + + \ No newline at end of file diff --git a/src/view/templates/header.php b/src/view/templates/header.php new file mode 100644 index 0000000..fa55cea --- /dev/null +++ b/src/view/templates/header.php @@ -0,0 +1,23 @@ + +
    +
    +
    + +
    + +
    + +
    +

    +

    +
    + +
    +
    \ No newline at end of file diff --git a/src/view/templates/new.php b/src/view/templates/new.php new file mode 100644 index 0000000..9dd8969 --- /dev/null +++ b/src/view/templates/new.php @@ -0,0 +1,21 @@ +
    +
    +
    +

    + +
    +
    + +
    +
    + +
    +
    +

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