From 68b6058e2a27fc251c117c4efeb141392a0c9736 Mon Sep 17 00:00:00 2001 From: polo Date: Sun, 6 Apr 2025 12:18:49 +0200 Subject: =?UTF-8?q?nouvel=20article,=20boutons=20dans=20les=20builders,=20?= =?UTF-8?q?makeArticleNode,=20JS=20MAJ=20page,=20tri=20quand=20d=C3=A9plac?= =?UTF-8?q?ement=20ou=20suppression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/Director.php | 93 ++++++++++++++++++++++++++++++------------ src/controller/Security.php | 2 +- src/controller/ajax.php | 88 ++++++++++++++++++++++++++++++--------- src/model/entities/Article.php | 8 +++- src/model/entities/Node.php | 71 ++++++++++++++++++++------------ src/view/AbstractBuilder.php | 6 +++ src/view/ArticleBuilder.php | 14 +++---- src/view/BlogBuilder.php | 41 ++++++++++++++----- src/view/GaleryBuilder.php | 41 ++++++++++++++----- src/view/GridBuilder.php | 47 ++++++++++++++------- src/view/templates/article.php | 1 - src/view/templates/blog.php | 8 +++- src/view/templates/galery.php | 7 ++-- src/view/templates/grid.php | 6 ++- 14 files changed, 306 insertions(+), 127 deletions(-) (limited to 'src') diff --git a/src/controller/Director.php b/src/controller/Director.php index 5ff8f47..db84661 100644 --- a/src/controller/Director.php +++ b/src/controller/Director.php @@ -13,17 +13,29 @@ class Director static public Menu $menu_data; // pour NavBuilder static public Path $page_path; // pour BreadcrumbBuilder private Page $page; - private Node $root_node; + private Node $node; + private Node $article; - public function __construct(EntityManager $entityManager) + public function __construct(EntityManager $entityManager, bool $for_display = false) { $this->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 + if($for_display){ + 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->node = new Node; // instance mère "vide" ne possédant rien d'autre que des enfants } + public function getNode(): Node + { + return $this->node; + } + public function getArticleNode(): Node + { + return $this->article; + } + public function makeRootNode(string $id = ''): void { // on récupère toutes les entrées @@ -44,25 +56,10 @@ class Director ->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; + $this->feedRootNodeObjects($bulk_data); } - private function feedObjects(array $bulk_data): void // $bulk_data = tableau de Node + private function feedRootNodeObjects(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) @@ -71,7 +68,7 @@ class Director // premier niveau if($node->getParent() == null) { - $this->root_node->addChild($node); + $this->node->addChild($node); // spécifique page article if($node->getName() === 'main' && $this->page->getEndOfPath() == 'article'){ @@ -94,8 +91,50 @@ class Director } } - public function getRootNode(): Node - { - return $this->root_node; + // récupération d'un article pour modification + public function makeArticleNode(string $id = '', bool $get_section = false): bool + { + if($get_section){ + $dql = 'SELECT n, p FROM App\Entity\Node n LEFT JOIN n.parent p WHERE n.article_timestamp = :id'; + } + else{ + $dql = 'SELECT n FROM App\Entity\Node n WHERE n.article_timestamp = :id'; + } + // n est l'article et p son $parent + $bulk_data = $this->entityManager + ->createQuery($dql) + ->setParameter('id', $id) + ->getResult(); + + if(count($bulk_data) === 0){ + return false; + } + + if($get_section){ + $this->article = $bulk_data[0]; + $this->makeSectionNode($bulk_data[0]->getParent()->getId()); + } + else{ + $this->article = $bulk_data[0]; + } + + return true; + } + + // récupération des articles d'un bloc
à la création d'un article + public function makeSectionNode(int $section_id): bool + { + $section = $this->entityManager->find('App\Entity\Node', (string)$section_id); + + $bulk_data = $this->entityManager + ->createQuery('SELECT n FROM App\Entity\Node n WHERE n.parent = :parent') + ->setParameter('parent', $section) + ->getResult(); + + foreach($bulk_data as $article){ + $section->addChild($article); // pas de flush, on ne va pas écrire dans la BDD à chaque nouvelle page + } + $this->node = $section; + return true; } } diff --git a/src/controller/Security.php b/src/controller/Security.php index 818a2bd..f9092e2 100644 --- a/src/controller/Security.php +++ b/src/controller/Security.php @@ -9,7 +9,7 @@ class Security 'safe'=>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, iframe, small', + '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' diff --git a/src/controller/ajax.php b/src/controller/ajax.php index 86acd39..b5c2e51 100644 --- a/src/controller/ajax.php +++ b/src/controller/ajax.php @@ -3,6 +3,9 @@ declare(strict_types=1); +use App\Entity\Article; +use App\Entity\Node; + // détection des requêtes de tinymce if($_SERVER['CONTENT_TYPE'] === 'application/json' && isset($_GET['action'])) { @@ -15,13 +18,44 @@ if($_SERVER['CONTENT_TYPE'] === 'application/json' && isset($_GET['action'])) if(json_last_error() === JSON_ERROR_NONE) { $id = $json['id']; - $id[0] = 'i'; $content = Security::secureString($json['content']); - $director = new Director($entityManager); + + // nouvel article + if($id[0] === 'n') + { + if($content === ''){ + echo json_encode(['success' => false, 'message' => 'pas de données à sauvegarder']); + die; + } + $section_id = (int)substr($id, 1); // id du bloc
+ $director->makeSectionNode($section_id); + $node = $director->getNode(); // =
+ + $timestamp = time(); + $date = new \DateTime; + $date->setTimestamp($timestamp); + + $article = new Article($content, $date); // le "current" timestamp est obtenu par la BDD + $article_node = new Node('article', 'i' . (string)$timestamp, [], count($node->getChildren()) + 1, $node, $node->getPage(), $article); + + $entityManager->persist($article_node); + $entityManager->flush(); + + // id_node tout juste généré + //$article_node->getId(); + + echo json_encode(['success' => true, 'article_id' => $article_node->getArticleTimestamp()]); + die; + } + // modification article + else{ + $id[0] = 'i'; // id de l'article node + } + if($director->makeArticleNode($id)) // une entrée est trouvée { - $node = $director->getRootNode(); + $node = $director->getArticleNode(); // article switch($json['id'][0]){ case 'i': $node->getArticle()->setContent($content); @@ -42,8 +76,9 @@ if($_SERVER['CONTENT_TYPE'] === 'application/json' && isset($_GET['action'])) $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' => 'article non identifié']); } } else{ @@ -53,16 +88,18 @@ if($_SERVER['CONTENT_TYPE'] === 'application/json' && isset($_GET['action'])) } elseif($_GET['action'] === 'delete_article' && isset($json['id'])) { - $id = $json['id']; - $director = new Director($entityManager); - $director->makeArticleNode($id); - $node = $director->getRootNode(); - $entityManager->remove($node); + $director->makeArticleNode($json['id'], true); + $article = $director->getArticleNode(); + $section = $director->getNode(); + + $entityManager->remove($article); + $section->removeChild($article); + $section->sortChildren(true); // régénère les positions $entityManager->flush(); // test avec une nouvelle requête qui ne devrait rien trouver - if(!$director->makeArticleNode($id)) + if(!$director->makeArticleNode($json['id'])) { echo json_encode(['success' => true]); @@ -78,14 +115,25 @@ if($_SERVER['CONTENT_TYPE'] === 'application/json' && isset($_GET['action'])) elseif($_GET['action'] === 'switch_positions' && isset($json['id1']) && isset($json['id2'])) { $director = new Director($entityManager); - $director->makeArticleNode($json['id1']); - $node1 = $director->getRootNode(); - $director->makeArticleNode($json['id2']); - $node2 = $director->getRootNode(); - - $tmp = $node1->getPosition(); - $node1->setPosition($node2->getPosition()); - $node2->setPosition($tmp); + $director->makeArticleNode($json['id1'], true); + $article1 = $director->getArticleNode(); + $section = $director->getNode(); + + $section->sortChildren(true); // régénère les positions avant inversion + + $article2; + foreach($section->getChildren() as $child){ + if($child->getArticleTimestamp() === $json['id2']) // type string + { + $article2 = $child; + break; + } + } + + // inversion + $tmp = $article1->getPosition(); + $article1->setPosition($article2->getPosition()); + $article2->setPosition($tmp); $entityManager->flush(); echo json_encode(['success' => true]); @@ -99,7 +147,7 @@ if($_SERVER['CONTENT_TYPE'] === 'application/json' && isset($_GET['action'])) $director = new Director($entityManager); $director->makeArticleNode($id); - $node = $director->getRootNode(); + $node = $director->getArticleNode(); $node->getArticle()->setDateTime($date); $entityManager->flush(); diff --git a/src/model/entities/Article.php b/src/model/entities/Article.php index 601e573..dc2d78b 100644 --- a/src/model/entities/Article.php +++ b/src/model/entities/Article.php @@ -20,7 +20,7 @@ class Article // datetime_immutable permet à la base de toujours gérer cette clé primaire correctement #[ORM\Column(type: 'datetime', options: ['default' => 'CURRENT_TIMESTAMP'], unique: true)] - private \DateTime $date_time; // le type datetime de doctrine convertit en type \DateTime de PHP + private ?\DateTime $date_time; // le type datetime de doctrine convertit en type \DateTime de PHP #[ORM\Column(type: "string")] private string $title; @@ -40,8 +40,12 @@ class Article )] private Collection $images; - public function __construct() + public function __construct(string $content, \DateTime $date_time = null, string $title = '', string $preview = '') { + $this->date_time = $date_time; + $this->title = $title; + $this->preview = $preview; + $this->content = $content; $this->images = new ArrayCollection(); // initialisation nécessaire } diff --git a/src/model/entities/Node.php b/src/model/entities/Node.php index 9240413..c9b310a 100644 --- a/src/model/entities/Node.php +++ b/src/model/entities/Node.php @@ -65,32 +65,6 @@ class Node $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 { @@ -156,6 +130,51 @@ class Node { return $this->children; } + public function addChild(self $child): void + { + $this->children[] = $child; + $this->sortChildren(false); + } + // utiliser $position pour afficher les éléments dans l'ordre + public function sortChildren(bool $reposition = false): void + { + // ordre du tableau des enfants + // inefficace quand des noeuds ont la même position + + // tri par insertion + for($i = 1; $i < count($this->children); $i++) + { + $tmp = $this->children[$i]; + $j = $i - 1; + + // Déplacez les éléments du tableau qui sont plus grands que la clé + // à une position devant leur position actuelle + while ($j >= 0 && $this->children[$j]->getPosition() > $tmp->getPosition()) { + $this->children[$j + 1] = $this->children[$j]; + $j = $j - 1; + } + $this->children[$j + 1] = $tmp; + } + + // nouvelles positions + if($reposition){ + $i = 1; + foreach($this->children as $child){ + $child->setPosition($i); + $i++; + } + } + } + public function removeChild(self $child): void + { + foreach($this->children as $key => $object){ + if($object->getId() === $child->getId()){ + unset($this->children[$key]); + } + break; + } + $this->children = array_values($this->children); // réindexer pour supprimer la case vide + } public function getTempChild(): ?self // peut renvoyer null { diff --git a/src/view/AbstractBuilder.php b/src/view/AbstractBuilder.php index cd2b361..285ebc3 100644 --- a/src/view/AbstractBuilder.php +++ b/src/view/AbstractBuilder.php @@ -7,6 +7,12 @@ abstract class AbstractBuilder { protected const VIEWS_PATH = '../src/view/templates/'; protected string $html = ''; + protected int $id_node; + + protected function __construct(Node $node) + { + $this->id_node = $node->getId(); + } protected function useChildrenBuilder(Node $node): void { diff --git a/src/view/ArticleBuilder.php b/src/view/ArticleBuilder.php index f86f9bd..1f5dbb8 100644 --- a/src/view/ArticleBuilder.php +++ b/src/view/ArticleBuilder.php @@ -25,29 +25,29 @@ class ArticleBuilder extends AbstractBuilder // partage $share_link = new URL(['page' => CURRENT_PAGE], $id); $share_js = 'onclick="copyInClipBoard(\'' . $share_link . '\')"'; - $share_button = '' . "\n"; + $share_button = '' . "\n"; // modifier un article $admin_buttons = ''; if($_SESSION['admin']) { $modify_js = 'onclick="openEditor(\'' . $id . '\')"'; - $modify_article = '

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

' . "\n"; $up_js = 'onclick="switchPositions(\'' . $id . '\', \'up\')"'; - $up_button = '

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

' . "\n"; $down_js = 'onclick="switchPositions(\'' . $id . '\', \'down\')"'; - $down_button = '

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

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

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

' . "\n"; $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; - $close_editor = ''; + $close_editor = ''; $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; - $submit_article = ''; + $submit_article = ''; $admin_buttons = $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article; } diff --git a/src/view/BlogBuilder.php b/src/view/BlogBuilder.php index 8c2125f..ca020b2 100644 --- a/src/view/BlogBuilder.php +++ b/src/view/BlogBuilder.php @@ -7,6 +7,7 @@ class BlogBuilder extends AbstractBuilder { public function __construct(Node $node) { + parent::__construct($node); $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; if(file_exists($viewFile)) @@ -18,26 +19,44 @@ class BlogBuilder extends AbstractBuilder // ajouter un article $new_article = ''; - $new_article_admin_buttons = ''; if($_SESSION['admin']) { - $id = 'new'; - - //$link = new URL(['page' => CURRENT_PAGE, 'action' => 'open_editor']); + $id = 'n' . $this->id_node; $js = 'onclick="openEditor(\'' . $id . '\')"'; - //$new_article = ''; - $new_article = '

' . "\n" . - '

'; + + $share_button = ''; + $html = ''; + + $new_button = '

' . "\n" . + '

'; + + $modify_js = 'onclick="openEditor(\'' . $id . '\')"'; + $modify_article = '' . "\n"; + + $up_js = 'onclick="switchPositions(\'' . $id . '\', \'up\')"'; + $up_button = '' . "\n"; + + $down_js = 'onclick="switchPositions(\'' . $id . '\', \'down\')"'; + $down_button = '' . "\n"; + + $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"'; + $delete_article = '' . "\n"; $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; - $close_editor = '
'; + $close_editor = ''; - $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; - $submit_article = '
'; + $submit_js = 'onclick="submitArticle(\'' . $id . '\', \'\', clone' . $this->id_node . ')"'; + $submit_article = ''; - $new_article_admin_buttons = $close_editor . $submit_article; + $admin_buttons = $new_button . $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article; + + // squelette d'un nouvel article + ob_start(); + require self::VIEWS_PATH . 'article.php'; + $new_article = ob_get_clean(); } + // articles existants $this->useChildrenBuilder($node); $content = $this->html; diff --git a/src/view/GaleryBuilder.php b/src/view/GaleryBuilder.php index 89be2b1..a895d70 100644 --- a/src/view/GaleryBuilder.php +++ b/src/view/GaleryBuilder.php @@ -7,6 +7,7 @@ class GaleryBuilder extends AbstractBuilder { public function __construct(Node $node) { + parent::__construct($node); $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; if(file_exists($viewFile)) @@ -18,26 +19,44 @@ class GaleryBuilder extends AbstractBuilder // ajouter un article $new_article = ''; - $new_article_admin_buttons = ''; if($_SESSION['admin']) { - $id = 'new'; - - //$link = new URL(['page' => CURRENT_PAGE, 'action' => 'open_editor']); + $id = 'n' . $this->id_node; $js = 'onclick="openEditor(\'' . $id . '\')"'; - //$new_article = ''; - $new_article = '

' . "\n" . - '

'; + + $share_button = ''; + $html = ''; + + $new_button = '

' . "\n" . + '

'; + + $modify_js = 'onclick="openEditor(\'' . $id . '\')"'; + $modify_article = '' . "\n"; + + $up_js = 'onclick="switchPositions(\'' . $id . '\', \'up\')"'; + $up_button = '' . "\n"; + + $down_js = 'onclick="switchPositions(\'' . $id . '\', \'down\')"'; + $down_button = '' . "\n"; + + $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"'; + $delete_article = '' . "\n"; $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; - $close_editor = '
'; + $close_editor = ''; - $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; - $submit_article = '
'; + $submit_js = 'onclick="submitArticle(\'' . $id . '\', \'\', clone' . $this->id_node . ')"'; + $submit_article = ''; - $new_article_admin_buttons = $close_editor . $submit_article; + $admin_buttons = $new_button . $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article; + + // squelette d'un nouvel article + ob_start(); + require self::VIEWS_PATH . 'article.php'; + $new_article = ob_get_clean(); } + // articles existants $this->useChildrenBuilder($node); $content = $this->html; diff --git a/src/view/GridBuilder.php b/src/view/GridBuilder.php index 6e16d46..e183e6f 100644 --- a/src/view/GridBuilder.php +++ b/src/view/GridBuilder.php @@ -7,6 +7,7 @@ class GridBuilder extends AbstractBuilder { public function __construct(Node $node) { + parent::__construct($node); $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; if(file_exists($viewFile)) @@ -18,32 +19,50 @@ class GridBuilder extends AbstractBuilder // ajouter un article $new_article = ''; - $new_article_admin_buttons = ''; if($_SESSION['admin']) { - $id = 'new'; + $id = 'n' . $this->id_node; $js = 'onclick="openEditor(\'' . $id . '\')"'; - if(Director::$page_path->getLast()->getEndOfPath() === 'accueil') - { - $new_article = '

' . "\n" . - '

'; + $share_button = ''; + $html = ''; + + if(CURRENT_PAGE === 'accueil'){ + $new_button = '

+

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

' . "\n" . - '

'; + else{ + $new_button = '

' . "\n" . + '

'; } + + $modify_js = 'onclick="openEditor(\'' . $id . '\')"'; + $modify_article = '' . "\n"; + + $up_js = 'onclick="switchPositions(\'' . $id . '\', \'up\')"'; + $up_button = '' . "\n"; + $down_js = 'onclick="switchPositions(\'' . $id . '\', \'down\')"'; + $down_button = '' . "\n"; + + $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"'; + $delete_article = '' . "\n"; + $close_js = 'onclick="closeEditor(\'' . $id . '\')"'; - $close_editor = '
'; + $close_editor = ''; - $submit_js = 'onclick="submitArticle(\'' . $id . '\')"'; - $submit_article = '
'; + $submit_js = 'onclick="submitArticle(\'' . $id . '\', \'\', clone' . $this->id_node . ')"'; + $submit_article = ''; - $new_article_admin_buttons = $close_editor . $submit_article; + $admin_buttons = $new_button . $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article; + + // squelette d'un nouvel article + ob_start(); + require self::VIEWS_PATH . 'article.php'; + $new_article = ob_get_clean(); } + // articles existants $this->useChildrenBuilder($node); $content = $this->html; diff --git a/src/view/templates/article.php b/src/view/templates/article.php index f3ab32f..c57c1cb 100644 --- a/src/view/templates/article.php +++ b/src/view/templates/article.php @@ -10,5 +10,4 @@ -
\ No newline at end of file diff --git a/src/view/templates/blog.php b/src/view/templates/blog.php index 35cac8b..e2066c8 100644 --- a/src/view/templates/blog.php +++ b/src/view/templates/blog.php @@ -1,6 +1,10 @@ -
+

- + +
+
\ No newline at end of file diff --git a/src/view/templates/galery.php b/src/view/templates/galery.php index 306526f..246c4dd 100644 --- a/src/view/templates/galery.php +++ b/src/view/templates/galery.php @@ -1,9 +1,10 @@ -
+

- +
-

diff --git a/src/view/templates/grid.php b/src/view/templates/grid.php index a09ed40..f9e441d 100644 --- a/src/view/templates/grid.php +++ b/src/view/templates/grid.php @@ -1,7 +1,9 @@ -
+

- +
-- cgit v1.2.3