From 15cbf5d56b4644151d59fee512f5f8fbe496caa3 Mon Sep 17 00:00:00 2001 From: polo Date: Thu, 9 Oct 2025 01:35:52 +0200 Subject: pagination partie 2, nouvelles fonctions + renommage dans Director, ArticleController::fetch, et en JS: fetchArticles, insertLocalDates, modifs dans changePaginationLimit, dans les vues --- public/css/body.css | 8 ++ public/js/main.js | 57 ++++++++++-- public/js/modif_page.js | 78 +++++++++++++--- public/js/tinymce.js | 28 +++--- src/controller/ArticleController.php | 41 +++++++++ src/controller/Director.php | 132 ++++++++++++++++------------ src/controller/PageManagementController.php | 17 ++++ src/model/entities/NodeData.php | 17 +++- src/router.php | 31 ++++--- src/view/AbstractBuilder.php | 4 +- src/view/NewBuilder.php | 6 +- src/view/NewsBlockBuilder.php | 3 +- src/view/PostBlockBuilder.php | 1 + src/view/templates/modify_block.php | 7 ++ src/view/templates/news_block.php | 5 +- src/view/templates/post_block.php | 5 +- 16 files changed, 332 insertions(+), 108 deletions(-) diff --git a/public/css/body.css b/public/css/body.css index 140655b..b728d05 100644 --- a/public/css/body.css +++ b/public/css/body.css @@ -49,12 +49,20 @@ main { display: none; } +section +{ + margin: 10px 0; +} section > h3 { padding: 15px; margin: 0; text-align: center; } +section .fetch_articles +{ + margin-left: 15px; +} .login_form { background-color: white; diff --git a/public/js/main.js b/public/js/main.js index d2f8876..59a9331 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -36,15 +36,10 @@ function toastNotify(message){ setTimeout(function(){ toast.className = toast.className.replace('show', ''); }, 5000); } - // exécuté à la fin du chargement de la page document.addEventListener('DOMContentLoaded', () => { - // détection des dates et conversion à l'heure locale - document.querySelectorAll('.local_date').forEach(function(element){ - const utc_date = element.getAttribute('date-utc'); // forme: 2025-10-10T12:17:00+00:00 - element.innerText = toFormatedLocalDate(utc_date); - }); + insertLocalDates(); // ouvrir/fermer les sous-menus avec écran tactile document.querySelectorAll('.sub-menu-toggle').forEach(button => { @@ -85,6 +80,48 @@ document.addEventListener('DOMContentLoaded', () => { }); +function fetchArticles(bloc_id){ + const parent = document.getElementById(bloc_id); + + const block_type = parent.getAttribute('block-type'); + let last_article = ''; + if(block_type === 'post_block'){ + // pas parfait, suppose que les positions sont correctes + last_article = parent.querySelectorAll('article').length - 1; + } + else if(block_type === 'news_block'){ + // date_time du dernier article affiché (heure UTC), date vide si bloc vide + const news_elements = parent.querySelector('.section_child').querySelectorAll('article'); + last_article = news_elements.length !== 0 ? news_elements[news_elements.length - 1].querySelector('.local_date').getAttribute('date-utc') : ''; + } + else{ + console.log("Erreur, le type de bloc n'est pas reconnu"); + return; + } + + fetch('index.php?fetch=next_articles&id=' + bloc_id + '&last_article=' + last_article) // méthode GET par défaut + .then(response => response.json()) + .then(data => { + if(data.success){ + // insérer les articles + parent.querySelector('.section_child').innerHTML += data.html; + insertLocalDates(); + + // cacher le bouton + parent.querySelector('.fetch_articles').querySelector('button').className = data.truncated ? '' : 'hidden'; + + console.log("Articles insérés dans le bloc"); + } + else{ + console.log("Erreur côté serveur à la récupération d'articles"); + } + }) + .catch(error => { + console.error('Erreur:', error); + }); +} + + // complète les fonctions dans tinymce.js function switchPositions(article_id, direction) { @@ -239,6 +276,14 @@ function submitDate(id_date) } } +function insertLocalDates(){ + // détection des dates et conversion à l'heure locale + document.querySelectorAll('.local_date').forEach(function(element){ + const utc_date = element.getAttribute('date-utc'); // forme: 2025-10-10T12:17:00+00:00 + element.innerText = toFormatedLocalDate(utc_date); + }); +} + function toFormatedLocalDate(utc_string_date){ // forme: 2025-07-17T13:54:00.000Z ou 2025-02-04T00:24 const date = new Date(utc_string_date); diff --git a/public/js/modif_page.js b/public/js/modif_page.js index bf269ee..15f3598 100644 --- a/public/js/modif_page.js +++ b/public/js/modif_page.js @@ -1,5 +1,8 @@ /* -- mode modification d'une page -- */ +// beaucoup de fonctions similaires +// à factoriser avec le pattern stratégie? + // même fonction que dans new_page.js function makePageNamePath(){ document.getElementById("page_name_path").value = document.getElementById("page_name").value @@ -92,7 +95,7 @@ function changeDescription(node_data_id){ toastNotify("la nouvelle description de la page est: " + data.description); } else{ - console.error('Erreur à la modification de la description de la page.'); + console.error('Erreur côté serveur à la modification de la description de la page.'); } }) .catch(error => { @@ -119,7 +122,7 @@ function renamePageBloc(bloc_id){ toastNotify('Le bloc a été renommé: ' + data.title); } else{ - console.error('Erreur au renommage du titre.'); + console.error('Erreur côté serveur au renommage du titre.'); } }) .catch(error => { @@ -168,7 +171,7 @@ function switchBlocsPositions(bloc_id, direction) { } else { - console.error('Échec de l\'inversion'); + console.error("Échec de l'inversion côté serveur"); } }) .catch(error => { @@ -187,17 +190,21 @@ function articlesOrderSelect(bloc_id){ .then(response => response.json()) .then(data => { if(data.success){ - // inverser l'ordre des articles!! - const parent = document.getElementById(bloc_id).querySelector(".section_child"); + // inversion des articles + /*const parent = document.getElementById(bloc_id).querySelector(".section_child"); const articles = Array.from(parent.querySelectorAll("article")); articles.reverse().forEach(article => { parent.appendChild(article); // déplace dans le DOM, ne copie pas - }); + });*/ + + // à cause de la pagination, au lieu d'inverser, on remplace les articles par les 1er dans le nouveau sens + document.getElementById(bloc_id).querySelector('.section_child').innerHTML = ''; + fetchArticles(bloc_id); console.log('ordre ' + articles_order_select); } else{ - console.log("Erreur au changement de l'ordre d'affichage côté serveur"); + console.log("Erreur côté serveur au changement de l'ordre d'affichage"); } }) .catch(error => { @@ -219,10 +226,10 @@ function changePresentation(bloc_id){ document.getElementById(bloc_id).className = presentation; document.getElementById(bloc_id).querySelector(".section_child").style.gridTemplateColumns = presentation === 'grid' ? 'repeat(auto-fit, minmax(' + data.cols_min_width + 'px, 1fr))' : ''; document.getElementById('cols_min_width_edit_' + bloc_id).className = presentation === 'grid' ? '' : 'hidden'; - console.log('changement de présentation'); + console.log('Changement de présentation'); } else{ - console.log('Erreur au changement de présentation côté serveur'); + console.log('Erreur côté serveur au changement de présentation'); } }) .catch(error => { @@ -230,6 +237,7 @@ function changePresentation(bloc_id){ }); } +// ressemble à changePaginationLimit function changeColsMinWidth(bloc_id){ const cols_min_width_input = document.getElementById('cols_min_width_select_' + bloc_id); @@ -250,10 +258,58 @@ function changeColsMinWidth(bloc_id){ if(data.success){ document.getElementById(bloc_id).className = 'grid'; document.getElementById(bloc_id).querySelector(".section_child").style.gridTemplateColumns = 'repeat(auto-fit, minmax(' + data.cols_min_width + 'px, 1fr))'; - console.log('changement de la largeur minimum en mode grille'); + console.log('Changement de la largeur minimum en mode grille'); + } + else{ + console.log('Erreur côté serveur au changement du nb de colonnes en mode grille'); + } + }) + .catch(error => { + console.error('Erreur:', error); + }); +} + +// ressemble à changeColsMinWidth +function changePaginationLimit(bloc_id){ + const pagination_limit_input = document.getElementById('pagination_limit_' + bloc_id); + + if(pagination_limit_input.value > 30){ + pagination_limit_input.value = 30; + } + else if(pagination_limit_input.value < 0){ + pagination_limit_input.value = 0; // fait joli dans la BDD, les valeurs négatives ont le même effet que 0 + } + + fetch('index.php?bloc_edit=change_pagination_limit', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ id: bloc_id, pagination_limit: pagination_limit_input.value }) + }) + .then(response => response.json()) + .then(data => { + if(data.success){ + const parent = document.getElementById(bloc_id).querySelector('.section_child'); + const articles_list = parent.querySelectorAll('article'); + + if(data.new_limit > data.old_limit || data.new_limit <= 0){ // si 0, fetchArticles va TOUT chercher! + parent.innerHTML = ''; // pas opti, mais améliorer ça serait très compliqué + fetchArticles(bloc_id); + } + else if(data.new_limit < articles_list.length){ + // retirer les articles + const articles_array = Array.from(articles_list).slice(0, data.new_limit); + parent.innerHTML = ''; + for(let i = 0; i < articles_array.length; i++){ + parent.appendChild(articles_array[i]); + } + // remettre le bouton "Articles suivants" + document.getElementById(bloc_id).querySelector('.fetch_articles').querySelector('button').className = ''; + } + + console.log("Changement du nombre d'articles affichés simultanément dans ce bloc"); } else{ - console.log('Erreur au changement du nb de colonnes en mode grille côté serveur'); + console.log("Erreur côté serveur au changement du nb d'éléments affichés par la pagination"); } }) .catch(error => { diff --git a/public/js/tinymce.js b/public/js/tinymce.js index 97ecad8..d2f9c46 100644 --- a/public/js/tinymce.js +++ b/public/js/tinymce.js @@ -1,5 +1,5 @@ // code à réorganiser -// seule certaines fonctions ont leur place dans Editor, d'autres servent à manipuler les articles d'une autre manière (déplacer, supprimer...) +// seules certaines fonctions ont leur place dans Editor, d'autres servent à manipuler les articles d'une autre manière (déplacer, supprimer...) // => encapsuler Editor dans une classe Article (comme la balise) qui existe même quand l'éditeur est fermé @@ -292,7 +292,7 @@ class Editor } submit(clone = null){ - var content; + let content; const params = new URL(document.location).searchParams; // "search" = ? et paramètres, searchParams = objet avec des getters // à comparer avec: new URLSearchParams(window.location.search); // c'est pareil ou pas? @@ -302,7 +302,7 @@ class Editor const prefixes = ['t', 'p', 'i', 'd']; const allElemsWithId = document.querySelectorAll('.data'); content = {}; - var id_from_builder; + let id_from_builder; allElemsWithId.forEach(element => { const first_letter = element.id.charAt(0).toLowerCase(); @@ -394,14 +394,14 @@ class Editor // restera ici jusqu'à ce que la gestion des balises soient faite ailleurs function makeNewArticleButtons(id, article_id, clone, placement = 'last') { - var share_btn = document.querySelector(`.share.hidden`); // combinaison de deux classes - var new_btn = document.getElementById(`new-${id}`); - var edit_btn = document.getElementById(`edit-${id}`); - var pos_up_btn = document.getElementById(`position_up-${id}`); - var pos_down_btn = document.getElementById(`position_down-${id}`); - var delete_btn = document.getElementById(`delete-${id}`); - var cancel_btn = document.getElementById(`cancel-${id}`); - var submit_btn = document.getElementById(`submit-${id}`); + let share_btn = document.querySelector(`.share.hidden`); // combinaison de deux classes + let new_btn = document.getElementById(`new-${id}`); + let edit_btn = document.getElementById(`edit-${id}`); + let pos_up_btn = document.getElementById(`position_up-${id}`); + let pos_down_btn = document.getElementById(`position_down-${id}`); + let delete_btn = document.getElementById(`delete-${id}`); + let cancel_btn = document.getElementById(`cancel-${id}`); + let submit_btn = document.getElementById(`submit-${id}`); share_btn.classList.remove('hidden'); new_btn.classList.add('hidden'); @@ -412,8 +412,8 @@ function makeNewArticleButtons(id, article_id, clone, placement = 'last') //cancel_btn.classList.add('hidden'); //submit_btn.classList.add('hidden'); - var article = document.getElementById(id); - var article_elem_parent = findParentByTagName(article, 'article'); + let article = document.getElementById(id); + let article_elem_parent = findParentByTagName(article, 'article'); share_btn.setAttribute('onclick', "copyInClipBoard('" + window.location.href + article_id + "')"); // # de l'ancre ajouté au clic sur le lien ouvrant l'éditeur article.id = article_id; @@ -430,7 +430,7 @@ function makeNewArticleButtons(id, article_id, clone, placement = 'last') submit_btn.id = 'submit-' + article_id; submit_btn.querySelector('button').setAttribute('onclick', "submitArticle('" + article_id + "')"); - var section_child = article_elem_parent.parentNode.querySelector('.section_child'); // renommer section_child + let section_child = article_elem_parent.parentNode.querySelector('.section_child'); // renommer section_child // parentNode vise la balise section article_elem_parent.parentNode.replaceChild(clone.cloneNode(true), article_elem_parent); // clone du squelette pour le garder intact diff --git a/src/controller/ArticleController.php b/src/controller/ArticleController.php index 411c1dc..8bbef19 100644 --- a/src/controller/ArticleController.php +++ b/src/controller/ArticleController.php @@ -6,10 +6,51 @@ declare(strict_types=1); use App\Entity\Node; use App\Entity\Article; use Doctrine\ORM\EntityManager; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; class ArticleController { + static public function fetch(EntityManager $entityManager, Request $request): void + { + if($request->query->has('id') && !empty($request->query->get('id')) && $request->query->has('last_article')){ + //var_dump($request->query->get('last_article')); + $id = (int)$request->get('id'); // type et nettoie + $director = new Director($entityManager); + $director->findNodeById($id); + $parent_block = $director->getNode(); + + if(Blocks::hasPresentation($parent_block->getName())){ + $get_articles_return = $director->getNextArticles($parent_block, $request); + $bulk_data = $get_articles_return[0]; + + if($parent_block->getName() === 'post_block'){ + $builder_name = 'PostBuilder'; + } + elseif($parent_block->getName() === 'news_block'){ + $builder_name = 'NewBuilder'; + } + + $html = ''; + foreach($bulk_data as $article){ + $builder = new $builder_name($article); + $html .= $builder->render(); + } + + echo json_encode(['success' => true, 'html' => $html, 'truncated' => $get_articles_return[1]]); + die; + } + else{ + echo json_encode(['success' => false, 'error' => 'mauvais type de bloc']); + die; + } + } + else{ + echo json_encode(['success' => false, 'error' => 'la requête ne comporte pas les paramètres attendus']); + die; + } + } + static public function editorSubmit(EntityManager $entityManager, array $json): void { if(json_last_error() === JSON_ERROR_NONE) diff --git a/src/controller/Director.php b/src/controller/Director.php index 8f038fa..9c1c6e3 100644 --- a/src/controller/Director.php +++ b/src/controller/Director.php @@ -1,12 +1,14 @@ src/model/Model.php serait mieux + declare(strict_types=1); use Doctrine\ORM\EntityManager; use App\Entity\Page; use App\Entity\Node; -//use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\QueryBuilder; use Symfony\Component\HttpFoundation\Request; class Director @@ -24,7 +26,9 @@ class Director $this->node = new Node; // instance mère "vide" ne possédant rien d'autre que des enfants } - public function makeMenuAndPaths(): void + // à déplacer dans Path ou un truc comme ça? + // couper Director en deux classes NodeModel et PageModel? + public function makeMenuAndPaths(): void // lit la table "page" { self::$menu_data = new Menu($this->entityManager); self::$page_path = new Path(); @@ -41,13 +45,12 @@ class Director } // affichage d'une page ordinaire - public function getWholePageData(Request $request): void + public function getWholePageData(Request $request): void // lit la table "node" + jointures { $id = CURRENT_PAGE === 'article' ? htmlspecialchars($request->query->get('id')) : ''; - if($id === '') // page "normale" - { - // tous les noeuds sauf les articles, tri par page + if($id === ''){ // page "normale" + // récupérer tous les noeuds sauf les articles $dql = "SELECT n FROM App\Entity\Node n WHERE n.name_node != 'new' AND n.name_node != 'post' AND (n.page = :page OR n.page IS null)"; $bulk_data = $this->entityManager ->createQuery($dql) @@ -57,45 +60,11 @@ class Director // groupes d'articles triés par bloc, permet de paginer par bloc foreach($bulk_data as $parent_block){ if(Blocks::hasPresentation($parent_block->getName())){ // = post_block ou news_block - $qb = $this->entityManager->createQueryBuilder(); - $qb->select('n') - ->from('App\Entity\Node', 'n') - ->where('n.parent = :parent') - ->setParameter('parent', $parent_block); - - if($parent_block->getName() === 'post_block'){ - $qb->orderBy('n.position'); - } - elseif($parent_block->getName() === 'news_block'){ - $qb->join('n.article', 'a'); - if($parent_block->getNodeData()->getChronoOrder() ?? false){ // ordre antichrono par défaut - $qb->orderBy('a.date_time', 'ASC'); - } - else{ - $qb->orderBy('a.date_time', 'DESC'); - } - } - - // pagination - $limit = $parent_block->getNodeData()->getPaginationLimit() ?? 0; // 0 par défaut = pas de pagination, sinon 12 rend bien avec des grilles de 2, 3 ou 4 colonnes - if($limit > 0){ - //$this->paginateWithCursor($qb, $request->query->get('last_position') ?? 0, $limit); - $qb->andWhere('n.position > :last_position') - ->setParameter('last_position', $request->query->get('last_position') ?? 0) - ->setMaxResults($limit); - - $nb_pages = $this->getNumberOfPages($parent_block, $limit); // nombres de "pages" d'articles - if($nb_pages > 1){ - //$parent_block->setNumberOfPages($nb_pages); // => navigation en HTML - } - } - - $bulk_data = array_merge($bulk_data, $qb->getQuery()->getResult()); + $bulk_data = array_merge($bulk_data, $this->getNextArticles($parent_block, $request)[0]); } } } - else // page "article" - { + else{ // page "article" $dql = 'SELECT n FROM App\Entity\Node n WHERE n.page = :page OR n.page IS null OR n.id_node = :id'; $bulk_data = $this->entityManager ->createQuery($dql) @@ -103,18 +72,74 @@ class Director ->setParameter('id', $id) ->getResult(); } + $this->makeNodeTree($bulk_data); } - /*private function paginateWithCursor(QueryBuilder $qb, int $last_position = 0, int $limit = 0): void + // récupération d'articles + public function getNextArticles(Node $parent_block, Request $request): array + { + $qb = $this->entityManager->createQueryBuilder(); + $qb->select('n') + ->from('App\Entity\Node', 'n') + ->where('n.parent = :parent') + ->setParameter('parent', $parent_block); + + if($parent_block->getName() === 'post_block'){ + $qb->orderBy('n.position'); + } + elseif($parent_block->getName() === 'news_block'){ + $qb->join('n.article', 'a'); + if($parent_block->getNodeData()->getChronoOrder() ?? false){ // ordre antichrono par défaut + $qb->orderBy('a.date_time', 'ASC'); + } + else{ + $qb->orderBy('a.date_time', 'DESC'); + } + } + + // pagination + $limit = $parent_block->getNodeData()->getPaginationLimit(); // = 12 par défaut si = null en BDD + $this->paginateWithCursor($qb, $parent_block, $request->query->get('last_article')); + $result = $qb->getQuery()->getResult(); + + // il reste des articles à récupérer SI on vient d'en récupérer trop + // ET on gère le cas particulier de $limit <= 0 + $truncated = false; + if(count($result) > $limit && $limit > 0){ // si nb résultat > limit > 0 + $truncated = true; + array_pop($result); // compenser le $limit + 1 dans paginateWithCursor + } + + return [$result, $truncated]; // besoin exceptionnel de retourner deux valeurs + } + + private function paginateWithCursor(QueryBuilder $qb, Node $parent_block, ?string $last_article): void { - $qb->andWhere('n.position > :last_position') - ->setParameter('last_position', $last_position) - ->setMaxResults($limit); - }*/ + //var_dump($last_article); + $limit = $parent_block->getNodeData()->getPaginationLimit(); // = 12 par défaut si = null en BDD + + if($limit > 0){ // si 0 ou moins pas de pagination + // nombres de "pages" d'articles + $nb_pages = $this->getNumberOfPages($parent_block, $limit); + $parent_block->getNodeData()->setNumberOfPages($nb_pages > 1 ? $nb_pages : 1); + + // adaptation de la requête + if($parent_block->getName() === 'post_block'){ + $qb->andWhere('n.position > :last_position') + ->setParameter('last_position', empty($last_article) ? 0 : $last_article) + ->setMaxResults($limit + 1); + } + elseif($parent_block->getName() === 'news_block'){ + $cursor_start = $parent_block->getNodeData()->getChronoOrder() ? '1970-01-01' : '9999-12-31'; + $qb->andWhere($parent_block->getNodeData()->getChronoOrder() ? 'a.date_time > :date_time' : 'a.date_time < :date_time') + ->setParameter('date_time', empty($last_article) ? $cursor_start : $last_article) + ->setMaxResults($limit + 1); + } + } + } - // requête à part n'alimentant pas $bulk_data - // fonctionnalité offerte par le Paginator de doctrine si on décidait de s'en servir + // le Paginator de doctrine le fait aussi si on décidait de s'en servir private function getNumberOfPages(Node $parent_block, int $limit): int { $dql = 'SELECT COUNT(n.id_node) FROM App\Entity\Node n WHERE n.parent = :parent'; @@ -129,11 +154,9 @@ class Director { // 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) - { + foreach($bulk_data as $node){ // premier niveau - if($node->getParent() == null) - { + if($node->getParent() == null){ $this->node->addChild($node); // spécifique page article @@ -142,8 +165,7 @@ class Director } } // autres niveaux - else - { + else{ $node->getParent()->addChild($node); // spécifique page article diff --git a/src/controller/PageManagementController.php b/src/controller/PageManagementController.php index 2cc88a5..8efcb79 100644 --- a/src/controller/PageManagementController.php +++ b/src/controller/PageManagementController.php @@ -334,4 +334,21 @@ class PageManagementController } die; } + static public function changePaginationLimit(EntityManager $entityManager, array $json): void + { + if(isset($json['id']) && isset($json['pagination_limit'])){ + $director = new Director($entityManager); + $director->findNodeById($json['id']); + $old_limit = $director->getNode()->getNodeData()->getPaginationLimit() ?? 12; + $director->getNode()->getNodeData()->setPaginationLimit((int)$json['pagination_limit']); // attention conversion? + + $entityManager->flush(); + + echo json_encode(['success' => true, 'old_limit' => $old_limit, 'new_limit' => $json['pagination_limit']]); + } + else{ + echo json_encode(['success' => false]); + } + die; + } } \ No newline at end of file diff --git a/src/model/entities/NodeData.php b/src/model/entities/NodeData.php index 1d7db4c..d8281c0 100644 --- a/src/model/entities/NodeData.php +++ b/src/model/entities/NodeData.php @@ -48,6 +48,8 @@ class NodeData )] private Collection $images; + private int $nb_pages = 1; + public function __construct(array $data, Node $node, Collection $images = new ArrayCollection, ?string $presentation = null, ?bool $chrono_order = null) { $this->data = $data; @@ -111,7 +113,20 @@ class NodeData public function getPaginationLimit(): ?int { - return $this->pagination_limit ?? null; + $default = 12; // si 0 pas de pagination, 12 rend bien avec des grilles de 2, 3 ou 4 colonnes + return $this->pagination_limit === null ? $default : $this->pagination_limit; + } + public function setPaginationLimit(int $pagination_limit): void + { + $this->pagination_limit = $pagination_limit; + } + public function getNumberOfPages(): int + { + return $this->nb_pages; + } + public function setNumberOfPages(int $nb_pages): void + { + $this->nb_pages = $nb_pages; } /*public function setNode(Node $node): void diff --git a/src/router.php b/src/router.php index 773a25c..8d19e49 100644 --- a/src/router.php +++ b/src/router.php @@ -1,7 +1,7 @@ 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 @@ -13,22 +13,27 @@ $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' requête AJAX xhs, non uti declare(strict_types=1); -if($_SERVER['REQUEST_METHOD'] === 'GET'){ +if($request->getMethod() === 'GET'){ // table "user" vide if(!UserController::existUsers($entityManager)){ - require '../src/view/templates/user_create.php'; + require AbstractBuilder::VIEWS_PATH . 'user_create.php'; die; } - // bouton déconnexion + // 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($_SERVER['REQUEST_METHOD'] === 'GET' && $request->query->has('action') && $request->query->get('action') === 'get_events' - && $request->query->has('start') && $request->query->has('end') && empty($_POST)) + 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); } @@ -43,7 +48,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET'){ } -elseif($_SERVER['REQUEST_METHOD'] === 'POST'){ +elseif($request->getMethod() === 'POST'){ /* -- contrôleurs appellables par tout le monde -- */ // table "user" vide @@ -54,8 +59,7 @@ elseif($_SERVER['REQUEST_METHOD'] === 'POST'){ // requêtes JSON avec fetch() if($_SERVER['CONTENT_TYPE'] === 'application/json') { - $data = file_get_contents('php://input'); - $json = json_decode($data, true); + $json = json_decode($request->getContent(), true); // = json_decode(file_get_contents('php://input'), true); if(isset($_GET['action'])) { @@ -195,6 +199,9 @@ elseif($_SERVER['REQUEST_METHOD'] === 'POST'){ 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); + } } } @@ -316,7 +323,7 @@ if(isset($response)){ header('Location: ' . new URL(['page' => !empty($_GET['from']) ? $_GET['from'] : 'accueil'])); } // redirection après traitement de formulaires HTTP - elseif($_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ + elseif($request->getMethod() === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ $response_data = json_decode(($response)->getContent(), true); $url = new URL(['page' => !empty($_GET['from']) ? $_GET['from'] : 'accueil']); $url->addParams(['success' => $response_data['success'], 'message' => $response_data['message']]); @@ -329,7 +336,7 @@ if(isset($response)){ } // pas utilisation de RESPONSE (cas destiné à disparaître) else{ - if($_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ + if($request->getMethod() === 'POST' && $_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded'){ header("Location: " . new URL(['error' => 'erreur côté serveur'])); } else{ @@ -337,4 +344,4 @@ else{ echo "erreur côté serveur"; } } -//die; // inutile normalement \ No newline at end of file +//die; // inutile \ No newline at end of file diff --git a/src/view/AbstractBuilder.php b/src/view/AbstractBuilder.php index ab5e389..0748df0 100644 --- a/src/view/AbstractBuilder.php +++ b/src/view/AbstractBuilder.php @@ -7,7 +7,7 @@ use App\Entity\Node; abstract class AbstractBuilder { - protected const string VIEWS_PATH = '../src/view/templates/'; + public const string VIEWS_PATH = '../src/view/templates/'; protected string $html = ''; protected int $id_node; @@ -44,7 +44,7 @@ abstract class AbstractBuilder } } - protected function snakeToPascalCase(string $input): string + private function snakeToPascalCase(string $input): string { return str_replace('_', '', ucwords($input, '_')); } diff --git a/src/view/NewBuilder.php b/src/view/NewBuilder.php index 83dd728..ec48541 100644 --- a/src/view/NewBuilder.php +++ b/src/view/NewBuilder.php @@ -51,9 +51,7 @@ class NewBuilder extends AbstractBuilder } $content = ''; - - // page article unique - if(Director::$page_path->getLast()->getEndOfPath() === 'article'){ + if(CURRENT_PAGE === 'article'){ $content = $node->getArticle()->getContent(); $from_to_button = '

'; $overflow = ''; @@ -84,7 +82,7 @@ class NewBuilder extends AbstractBuilder $date_buttons = ''; $admin_buttons = ''; if($_SESSION['admin']){ - if(Director::$page_path->getLast()->getEndOfPath() === 'article'){ + if(CURRENT_PAGE === 'article'){ $title_js = 'onclick="openEditor(\'' . $id_title . '\')"'; $modify_title = '

' . "\n"; $close_js_title = 'onclick="closeEditor(\'' . $id_title . '\')"'; diff --git a/src/view/NewsBlockBuilder.php b/src/view/NewsBlockBuilder.php index 963afe6..4c7c4ec 100644 --- a/src/view/NewsBlockBuilder.php +++ b/src/view/NewsBlockBuilder.php @@ -28,6 +28,7 @@ class NewsBlockBuilder extends AbstractBuilder $min_width = $node->getNodeData()->getColsMinWidth(); $cols_min_width = 'grid-template-columns: repeat(auto-fit, minmax(' . (string)$min_width . 'px, 1fr));'; } + $fetch_button_hidden = $node->getNodeData()->getNumberOfPages() > 1 ? '' : ' hidden'; // ajouter un article $new_article = ''; @@ -54,7 +55,7 @@ class NewsBlockBuilder extends AbstractBuilder $html = ''; $admin_buttons = $new_button . $modify_article . $delete_article . $close_editor . $submit_article; - // post vide mis là pour le bouton "Nouvel article" => déplace vers page "article" + // insérer post.php pour le bouton "Nouvel article", new.php pour les vrais articles ob_start(); require self::VIEWS_PATH . 'post.php'; // nécéssite $admin_buttons et $html $new_article = ob_get_clean(); diff --git a/src/view/PostBlockBuilder.php b/src/view/PostBlockBuilder.php index ba54901..ba4de12 100644 --- a/src/view/PostBlockBuilder.php +++ b/src/view/PostBlockBuilder.php @@ -28,6 +28,7 @@ class PostBlockBuilder extends AbstractBuilder $min_width = $node->getNodeData()->getColsMinWidth(); $cols_min_width = 'grid-template-columns: repeat(auto-fit, minmax(' . (string)$min_width . 'px, 1fr));'; } + $fetch_button_hidden = $node->getNodeData()->getNumberOfPages() > 1 ? '' : ' hidden'; // ajouter un article // => fait un peu double emploi avec PostBuilder diff --git a/src/view/templates/modify_block.php b/src/view/templates/modify_block.php index 1d097e0..3620100 100644 --- a/src/view/templates/modify_block.php +++ b/src/view/templates/modify_block.php @@ -47,4 +47,11 @@ if(Blocks::hasPresentation($child_node->getName())){ +
+ +
\ No newline at end of file diff --git a/src/view/templates/news_block.php b/src/view/templates/news_block.php index 9d01513..43dbe01 100644 --- a/src/view/templates/news_block.php +++ b/src/view/templates/news_block.php @@ -1,5 +1,5 @@ -
+