aboutsummaryrefslogtreecommitdiff
path: root/src/view
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2026-10-23 00:47:32 +0200
committerpolo <ordipolo@gmx.fr>2026-10-23 00:47:32 +0200
commitf27cca08ff786ceff1870147f6d6bad14d71117f (patch)
treed36597a72025313d6faf4ed228755fa267ebe2f6 /src/view
parentc7309f900ef05bd5cb313110ec159b761af394ec (diff)
downloadcms-f27cca08ff786ceff1870147f6d6bad14d71117f.tar.gz
cms-f27cca08ff786ceff1870147f6d6bad14d71117f.tar.bz2
cms-f27cca08ff786ceff1870147f6d6bad14d71117f.zip
dossiers avec majuscule, namespace pour toutes les classes, autoload psr-4, UserDataService::createZip déplacé dans Backup
Diffstat (limited to 'src/view')
-rw-r--r--src/view/AbstractBuilder.php77
-rw-r--r--src/view/BreadcrumbBuilder.php50
-rw-r--r--src/view/CalendarBuilder.php29
-rw-r--r--src/view/FooterBuilder.php117
-rw-r--r--src/view/FormBuilder.php50
-rw-r--r--src/view/GaleryBuilder.php70
-rw-r--r--src/view/HeadBuilder.php81
-rw-r--r--src/view/HeaderBuilder.php129
-rw-r--r--src/view/LoginBuilder.php52
-rw-r--r--src/view/MainBuilder.php95
-rw-r--r--src/view/MaintenanceBuilder.php33
-rw-r--r--src/view/MenuBuilder.php97
-rw-r--r--src/view/NavBuilder.php73
-rw-r--r--src/view/NewBuilder.php152
-rw-r--r--src/view/NewPageBuilder.php49
-rw-r--r--src/view/NewsBlockBuilder.php73
-rw-r--r--src/view/PostBlockBuilder.php88
-rw-r--r--src/view/PostBuilder.php57
-rw-r--r--src/view/ShowEmailsBuilder.php69
-rw-r--r--src/view/UserEditBuilder.php62
-rw-r--r--src/view/ViewDirector.php28
-rw-r--r--src/view/templates/calendar.php11
-rw-r--r--src/view/templates/footer.php40
-rw-r--r--src/view/templates/form.php36
-rw-r--r--src/view/templates/form_admin.php72
-rw-r--r--src/view/templates/galery.php12
-rw-r--r--src/view/templates/head.php13
-rw-r--r--src/view/templates/header.php55
-rw-r--r--src/view/templates/login.php31
-rw-r--r--src/view/templates/maintenance.php62
-rw-r--r--src/view/templates/menu.php48
-rw-r--r--src/view/templates/modify_block.php58
-rw-r--r--src/view/templates/modify_page.php58
-rw-r--r--src/view/templates/new.php27
-rw-r--r--src/view/templates/new_page.php35
-rw-r--r--src/view/templates/news_block.php14
-rw-r--r--src/view/templates/post.php14
-rw-r--r--src/view/templates/post_block.php14
-rw-r--r--src/view/templates/show_emails.php30
-rw-r--r--src/view/templates/user_create.php53
-rw-r--r--src/view/templates/user_edit.php63
41 files changed, 0 insertions, 2277 deletions
diff --git a/src/view/AbstractBuilder.php b/src/view/AbstractBuilder.php
deleted file mode 100644
index b9a1d7f..0000000
--- a/src/view/AbstractBuilder.php
+++ /dev/null
@@ -1,77 +0,0 @@
1<?php
2// src/view/AbstractBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8abstract class AbstractBuilder
9{
10 const VIEWS_PATH = '../src/view/templates/';
11 protected string $html = '';
12 protected int $id_node;
13
14 protected function __construct(Node $node)
15 {
16 $this->id_node = $node->getId();
17 }
18
19 protected function useChildrenBuilder(Node $node): void
20 {
21 foreach($node->getChildren() as $child_node)
22 {
23 $builder_name = $this->snakeToPascalCase($child_node->getName()) . 'Builder';
24 $builder = new $builder_name($child_node);
25 $this->html .= $builder->render();
26
27 // pages spéciales où on n'assemble pas tout
28 if($builder_name === 'HeadBuilder' && in_array(Model::$page_path->getString(), ['connection', 'user_edit']))
29 {
30 foreach($node->getChildren() as $target_node){
31 if($target_node->getName() === 'main'){
32 $main_node = $target_node;
33 break;
34 }
35 }
36 // on construit <main> et on s'arrête! les autres noeuds sont ignorés
37 $builder_name = $this->snakeToPascalCase($main_node->getName()) . 'Builder';
38 $builder = new $builder_name($main_node);
39 $this->html .= "<body>\n";
40 $this->html .= $builder->render() . "\n";
41 $this->html .= "</body>\n</html>";
42 break;
43 }
44 }
45 }
46
47 private function snakeToPascalCase(string $input): string
48 {
49 return str_replace('_', '', ucwords($input, '_'));
50 }
51
52 public function render(): string // = getHTML(), nécéssite d'être public
53 {
54 return $this->html;
55 }
56 protected function addHTML(string $html): void
57 {
58 $this->html .= $html;
59 }
60
61 protected function insertSVG(string $path, array $attributes = []): string
62 {
63 $svg = file_get_contents($path);
64
65 // modification des attributs
66 if(!empty($attributes)){
67 $dom = new DOMDocument();
68 $dom->loadXML($svg);
69 $svg_elem = $dom->documentElement;
70 foreach($attributes as $key => $value){
71 $svg_elem->setAttribute($key, $value);
72 }
73 $svg = $dom->saveXML($svg_elem);
74 }
75 return $svg;
76 }
77} \ No newline at end of file
diff --git a/src/view/BreadcrumbBuilder.php b/src/view/BreadcrumbBuilder.php
deleted file mode 100644
index 2655551..0000000
--- a/src/view/BreadcrumbBuilder.php
+++ /dev/null
@@ -1,50 +0,0 @@
1<?php
2// src/view/BreadcrumbBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class BreadcrumbBuilder extends AbstractBuilder
9{
10 public function __construct(Node $node)
11 {
12 $this->html = $this->breadcrumbHTML();
13 }
14
15 private function breadcrumbHTML(): string
16 {
17 $asset = 'assets/home.svg'; // => BDD?
18 $breadcrumb_array = Model::$page_path->getArray(); // tableau de Page
19 $html = '';
20 $nb_of_entries = count($breadcrumb_array);
21
22 if($nb_of_entries > 1)
23 {
24 // petite maison et flèche
25 $html .= '<nav class="breadcrumb" aria-label="Breadcrumb">' . "\n";
26 $html .= '<a href="' . new URL . '"><img src="' . $asset . '"></a><span class="arrow"> →</span>' . "\n";
27
28 // partie intermédiaire (pas de lien sur le dernier élément)
29 for($i = 0; $i < ($nb_of_entries - 1); $i++)
30 {
31 // liens optionnels
32 if($breadcrumb_array[$i]->isReachable())
33 {
34 $html .= '<a href="' . new URL(['page' => $breadcrumb_array[$i]->getPagePath()]) . '">';
35 }
36 $html .= '<span>' . $breadcrumb_array[$i]->getPageName() . '</span>';
37 if($breadcrumb_array[$i]->isReachable())
38 {
39 $html .= '</a>';
40 }
41 $html .= '<span class="arrow"> →</span>' . "\n";
42 }
43
44 // fin du chemin (=> Thésée)
45 $html .= '<span id="thesee" aria-current="page">' . $breadcrumb_array[$nb_of_entries - 1]->getPageName() . "</span>\n";
46 $html .= "</nav>\n";
47 }
48 return $html;
49 }
50} \ No newline at end of file
diff --git a/src/view/CalendarBuilder.php b/src/view/CalendarBuilder.php
deleted file mode 100644
index 0c0006d..0000000
--- a/src/view/CalendarBuilder.php
+++ /dev/null
@@ -1,29 +0,0 @@
1<?php
2// src/view/CalendarBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class CalendarBuilder extends AbstractBuilder
9{
10 public function __construct(Node $node)
11 {
12 parent::__construct($node);
13 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
14
15 $calendar_js_file = IS_ADMIN ? 'calendar_admin' : 'calendar';
16
17 if(file_exists($viewFile))
18 {
19 if(!empty($node->getNodeData()->getData()))
20 {
21 extract($node->getNodeData()->getData());
22 }
23
24 ob_start();
25 require $viewFile;
26 $this->html = ob_get_clean(); // pas de concaténation ici, on écrase
27 }
28 }
29} \ No newline at end of file
diff --git a/src/view/FooterBuilder.php b/src/view/FooterBuilder.php
deleted file mode 100644
index bb99c13..0000000
--- a/src/view/FooterBuilder.php
+++ /dev/null
@@ -1,117 +0,0 @@
1<?php
2// src/view/FooterBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7use App\Entity\Asset;
8
9class FooterBuilder extends AbstractBuilder
10{
11 public function __construct(Node $node)
12 {
13 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
14
15 if(file_exists($viewFile))
16 {
17 $node_data = $node->getNodeData();
18 // nom du contact, adresse et e-mail
19 if(!empty($node_data->getData()))
20 {
21 extract($node_data->getData());
22 }
23
24 $footer_logo = Asset::USER_PATH . $node_data->getAssetByRole('footer_logo')?->getFileName() ?? '';
25
26 $this->useChildrenBuilder($node);
27 $breadcrumb = $this->html;
28
29 $empty_admin_zone = '';
30 if(IS_ADMIN)
31 {
32 // données du footer
33 $admin_footer_name = '<input type="text" id="footer_name_input" class="hidden" value="' . htmlspecialchars($name ?? '') . '" placeholder="raison sociale" size="30">
34 <img id="footer_name_open" class="action_icon" src="assets/edit.svg" onclick="footer_name.open()">
35 <img id="footer_name_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_name.submit()">
36 <img id="footer_name_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_name.cancel()">';
37 $admin_footer_address = '<input type="text" id="footer_address_input" class="hidden" value="' . htmlspecialchars($address ?? '') . '" placeholder="adresse" size="30">
38 <img id="footer_address_open" class="action_icon" src="assets/edit.svg" onclick="footer_address.open()">
39 <img id="footer_address_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_address.submit()">
40 <img id="footer_address_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_address.cancel()">';
41 $admin_footer_email = '<input type="text" id="footer_email_input" class="hidden" value="' . htmlspecialchars($email ?? '') . '" placeholder="e-mail" size="30">
42 <img id="footer_email_open" class="action_icon" src="assets/edit.svg" onclick="footer_email.open()">
43 <img id="footer_email_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_email.submit()">
44 <img id="footer_email_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_email.cancel()">';
45
46 $admin_footer_logo = '<input type="file" id="footer_logo_input" class="hidden" accept="image/png, image/jpeg, image/gif, image/webp, image/tiff">
47 <img id="footer_logo_open" class="action_icon" src="assets/edit.svg" onclick="footer_logo.open()">
48 <img id="footer_logo_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_logo.submit()">
49 <img id="footer_logo_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_logo.cancel()">';
50
51 // zone admin
52 $empty_admin_zone = 'empty_admin_zone';
53 if(MainBuilder::$modif_mode){
54 $mode = 'modification de page';
55 $div_admin = 'logged_in modif_mode';
56 }
57 else{
58 $mode = 'administrateur';
59 $div_admin = 'logged_in';
60 }
61
62 $link_change_password = new URL(['page' => 'user_edit', 'from' => CURRENT_PAGE]);
63 isset($_GET['id']) ? $link_change_password->addParams(['id' => $_GET['id']]) : '';
64
65 $link_logout = new URL(['action' => 'deconnection', 'from' => CURRENT_PAGE]);
66 isset($_GET['id']) ? $link_logout->addParams(['id' => $_GET['id']]) : '';
67
68 $zone_admin = '<div class="admin_buttons_zone">
69 <p>Vous êtes en mode ' . $mode . ".</p>\n" .
70 '<div><a href="' . new URL(['page' => 'new_page']) . '"><button>Nouvelle page</button></a></div>' . "\n" .
71 $this->makePageModifModeButton() . "\n" .
72 '<div><a href="' . new URL(['page' => 'menu_paths']) . '"><button>Menu et chemins</button></a></div>' . "\n" .
73 '<div><a href="' . $link_change_password . '"><button>Mon compte</button></a></div>' . "\n" .
74 '<div><a href="' . new URL(['page' => 'maintenance']) . '"><button>Maintenance</button></a></div>' . "\n" .
75 '<div><a href="' . $link_logout . '"><button>Déconnexion</button></a></div>' . "\n" .
76 '</div>' . "\n";
77 }
78 else
79 {
80 $div_admin = 'logged_out';
81 $url = new URL(['page' => 'connection', 'from' => CURRENT_PAGE]);
82 if(Model::$page_path->getLast()->getEndOfPath() === 'article' && isset($_GET['id'])){
83 $url->addParams(['id' => $_GET['id']]);
84 }
85 $zone_admin = '<button><a href="' . $url . '">Mode admin</a></button>';
86
87 $admin_footer_name = '';
88 $admin_footer_address = '';
89 $admin_footer_email = '';
90 $admin_footer_logo = '';
91 }
92
93 ob_start();
94 require $viewFile;
95 $this->html = ob_get_clean();
96 }
97 }
98
99 private function makePageModifModeButton(): string
100 {
101 $link_edit_page = new URL(['page' => CURRENT_PAGE]);
102 if(!in_array(CURRENT_PAGE, ViewController::SPECIAL_PAGES)) // cas impossibles: 'user_edit' et 'connection' qui n'ont pas de footer
103 {
104 if(MainBuilder::$modif_mode){
105 $link_edit_label = 'Sortir du mode modification';
106 }
107 else{
108 $link_edit_page->addParams(['mode' => 'page_modif']);
109 $link_edit_label = 'Modifier la page';
110 }
111 return '<div><a href="' . $link_edit_page . '"><button>' . $link_edit_label . '</button></a></div>';
112 }
113 else{
114 return '';
115 }
116 }
117} \ No newline at end of file
diff --git a/src/view/FormBuilder.php b/src/view/FormBuilder.php
deleted file mode 100644
index 9a900ce..0000000
--- a/src/view/FormBuilder.php
+++ /dev/null
@@ -1,50 +0,0 @@
1<?php
2// src/view/FormBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class FormBuilder extends AbstractBuilder
9{
10 static private ?Captcha $captcha = null;
11
12 public function __construct(Node $node){
13 parent::__construct($node);
14
15 if(!empty($node->getNodeData()->getData())){
16 extract($node->getNodeData()->getData());
17 }
18
19 // un seul captcha à la fois!
20 if(!self::$captcha){
21 self::$captcha = new Captcha;
22 $_SESSION['captcha'] = self::$captcha->getSolution();
23 }
24
25 // données stockées en vrac dans du JSON et récupérées avec extract => changer ça un jour
26 $smtp_host = $smtp_host ?? Config::$smtp_host;
27 $smtp_secure = $smtp_secure ?? Config::$smtp_secure;
28 $smtp_username = $smtp_username ?? Config::$smtp_username;
29 $email_dest = $email_dest ?? Config::$email_dest;
30 $keep_emails = (bool)($keep_emails ?? false); // (bool) est inutile mais plus clair
31 $retention_period = $this->getRetentionPeriod($retention_period ?? null, App\Entity\Email::DEFAULT_RETENTION_PERIOD);
32 $retention_period_sensible = $this->getRetentionPeriod($retention_period_sensible ?? null, App\Entity\Email::DEFAULT_RETENTION_PERIOD_SENSITIVE);
33
34 $admin_content = '';
35 if(IS_ADMIN){
36 ob_start();
37 require self::VIEWS_PATH . 'form_admin.php';
38 $admin_content = ob_get_clean();
39 }
40
41 ob_start();
42 require self::VIEWS_PATH . $node->getName() . '.php';
43 $this->html = ob_get_clean(); // pas de concaténation ici, on écrase
44 }
45
46 private function getRetentionPeriod(mixed $period, int $default_period): int
47 {
48 return ($period === null || (int)$period <= 0) ? $default_period : (int)$period; // (int) est nécessaire à cause du stockage JSON
49 }
50} \ No newline at end of file
diff --git a/src/view/GaleryBuilder.php b/src/view/GaleryBuilder.php
deleted file mode 100644
index 019a2c7..0000000
--- a/src/view/GaleryBuilder.php
+++ /dev/null
@@ -1,70 +0,0 @@
1<?php
2// src/view/GaleryBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class GaleryBuilder extends AbstractBuilder
9{
10 public function __construct(Node $node)
11 {
12 parent::__construct($node);
13 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
14
15 if(file_exists($viewFile))
16 {
17 if(!empty($node->getNodeData()->getData()))
18 {
19 extract($node->getNodeData()->getData());
20 }
21
22 // ajouter un article
23 $new_article = '';
24 if(IS_ADMIN)
25 {
26 $id = 'n' . $this->id_node;
27 $js = 'onclick="openEditor(\'' . $id . '\')"';
28
29 $share_button = '<p class="share hidden"><img class="action_icon" src="assets/share.svg"></p>';
30 $html = '';
31
32 $new_button = '<p id="new-' . $id . '">' . "\n" .
33 '<button ' . $js . '><img class="action_icon" src="assets/edit.svg">Nouvel article</button></p>';
34
35 $modify_js = 'onclick="openEditor(\'' . $id . '\')"';
36 $modify_article = '<p id="edit-' . $id . '" class="hidden"><img class="action_icon" src="assets/edit.svg" ' . $modify_js . '></p>' . "\n";
37
38 $up_js = 'onclick="switchPositions(\'' . $id . '\', \'up\')"';
39 $up_button = '<p id="position_up-' . $id . '" class="hidden"><img class="action_icon" src="assets/arrow-up.svg" ' . $up_js . '></p>' . "\n";
40
41 $down_js = 'onclick="switchPositions(\'' . $id . '\', \'down\')"';
42 $down_button = '<p id="position_down-' . $id . '" class="hidden"><img class="action_icon" src="assets/arrow-down.svg" ' . $down_js . '></p>' . "\n";
43
44 $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"';
45 $delete_article = '<p id="delete-' . $id . '" class="hidden"><img class="action_icon" src="assets/delete-bin.svg" ' . $delete_js . '></p>' . "\n";
46
47 $close_js = 'onclick="closeEditor(\'' . $id . '\')"';
48 $close_editor = '<p id="cancel-' . $id . '" class="hidden"><button ' . $close_js . '>Annuler</button></p>';
49
50 $submit_js = 'onclick="submitArticle(\'' . $id . '\', \'\', clone' . $this->id_node . ')"';
51 $submit_article = '<p id="submit-' . $id . '" class="hidden"><button ' . $submit_js . '>Valider</button></p>';
52
53 $admin_buttons = $new_button . $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article;
54
55 // squelette d'un nouvel article
56 ob_start();
57 require self::VIEWS_PATH . 'post.php';
58 $new_article = ob_get_clean();
59 }
60
61 // articles existants
62 $this->useChildrenBuilder($node);
63 $content = $this->html;
64
65 ob_start();
66 require $viewFile;
67 $this->html = ob_get_clean(); // pas de concaténation ici, on écrase
68 }
69 }
70} \ No newline at end of file
diff --git a/src/view/HeadBuilder.php b/src/view/HeadBuilder.php
deleted file mode 100644
index 9cb0650..0000000
--- a/src/view/HeadBuilder.php
+++ /dev/null
@@ -1,81 +0,0 @@
1<?php
2// src/view/HeadBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Asset;
7use App\Entity\Node;
8
9class HeadBuilder extends AbstractBuilder
10{
11 public function __construct(Node $node)
12 {
13 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
14
15 if(file_exists($viewFile)){
16 // css et js
17 $page = Model::$page_path->getLast();
18
19 $css = '';
20 foreach($page->getCSS() as $name){
21 $css .= self::insertCSS($name);
22 }
23
24 $js = '';
25 foreach($page->getJS() as $name){
26 $js .= self::insertJS($name);
27 }
28
29 if(MainBuilder::$modif_mode){
30 $css .= self::insertCSS('modif_page');
31 $js .= self::insertJS('modif_page');
32 }
33
34 if(IS_ADMIN){
35 // édition éléments sur toutes les pages (header, footer et favicon)
36 $js .= self::insertJS('Input');
37
38 // sert partout?
39 $js .= self::insertJS('Fetcher');
40
41 // tinymce, nécéssite un script post-install et post-update dans composer.json
42 $css .= self::insertCSS('tinymce');
43 $js .= self::insertJS('tinymce/tinymce.min');
44 $js .= self::insertJS('tinymce');
45 }
46
47 $title = Model::$page_path->getLast()->getPageName();
48 $description = Model::$page_path->getLast()->getDescription();
49
50 // favicon
51 // ?-> est l'opérateur de navigation sécurisée => LOVE!
52 $favicon_name = ($favicon_object = $node->getNodeData()->getAssetByRole('head_favicon'))?->getFileName();
53 $favicon = $favicon_name ? Asset::USER_PATH . $favicon_name : '';
54 $favicon_type = $favicon_object?->getMimeType() ?? '';
55
56 ob_start();
57 require $viewFile;
58 $this->html .= ob_get_clean();
59 }
60 }
61
62 static function insertCSS(string $name): string
63 {
64 return '<link rel="stylesheet" href="' . self::versionedFileURL('css', $name) . '">' . "\n";
65 }
66 static function insertJS(string $name, bool $module = false): string
67 {
68 return '<script ' . ($module ? 'type="module"' : '') . ' src="' . self::versionedFileURL('js', $name) . '"></script>' . "\n";
69 }
70
71 static function versionedFileURL(string $type, string $filename): string
72 {
73 $path = $type . '/' . $filename . '.' . $type;
74
75 if(file_exists($path)){
76 $version = substr(md5_file($path), 0, 8);
77 return $path . '?v=' . $version;
78 }
79 return $path; // sécurité fichier absent
80 }
81}
diff --git a/src/view/HeaderBuilder.php b/src/view/HeaderBuilder.php
deleted file mode 100644
index bfd5963..0000000
--- a/src/view/HeaderBuilder.php
+++ /dev/null
@@ -1,129 +0,0 @@
1<?php
2// src/view/HeaderBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7use App\Entity\NodeData;
8use App\Entity\Asset;
9
10class HeaderBuilder extends AbstractBuilder
11{
12 private ?Node $nav = null;
13 private ?Node $breadcrumb = null;
14 const ICON_PATH = 'icons/';
15
16 public function __construct(Node $node)
17 {
18 // pas de useChildrenBuilder, il faudrait peut-être
19 $children = $node->getChildren();
20 foreach($children as $child)
21 {
22 if($child->getName() === 'nav'){
23 $this->nav = $child;
24 // actuellement le noeud nav ne contient aucune info utile et l'envoyer à NavBuilder est inutile
25 $nav_builder = new NavBuilder($this->nav);
26
27 $nav = $nav_builder->render();
28 }
29 elseif($child->getName() === 'breadcrumb'){
30 $this->breadcrumb = $child;
31 $breadcrumb_builder = new BreadcrumbBuilder($this->breadcrumb);
32 $breadcrumb = $breadcrumb_builder->render();
33 }
34 }
35
36 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
37
38 if(file_exists($viewFile))
39 {
40 $node_data = $node->getNodeData();
41 // titre et description
42 if(!empty($node_data->getData()))
43 {
44 extract($node_data->getData());
45 }
46
47 // réseaux sociaux + logo dans l'entête
48 // ?-> est l'opérateur de chainage optionnel
49 $header_logo = Asset::USER_PATH . $node_data->getAssetByRole('header_logo')?->getFileName() ?? '';
50 $header_background_name = $node_data->getAssetByRole('header_background')?->getFileName();
51 $header_background = $header_background_name ? Asset::USER_PATH . $header_background_name : '';
52 $social_networks = '';
53
54 // boutons mode admin
55 if(IS_ADMIN){
56 // assets dans classe header_additional_inputs
57 $admin_head_favicon = '<input type="file" id="head_favicon_input" class="hidden" accept="image/png, image/jpeg, image/gif, image/webp, image/tiff, image/x-icon, image/bmp">
58 <button id="head_favicon_open" onclick="head_favicon.open()"><img id="head_favicon_content" class="action_icon"> Favicon</button>
59 <img id="head_favicon_submit" class="action_icon hidden" src="assets/save.svg" onclick="head_favicon.submit()">
60 <img id="head_favicon_cancel" class="action_icon hidden" src="assets/close.svg" onclick="head_favicon.cancel()">';
61 $admin_header_background = '<input type="file" id="header_background_input" class="hidden" accept="image/png, image/jpeg, image/gif, image/webp, image/tiff">
62 <button id="header_background_open" onclick="header_background.open()"><img id="header_background_content" class="background_button" src="' . $header_background . '"> Image de fond</button>
63 <img id="header_background_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_background.submit()">
64 <img id="header_background_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_background.cancel()">';
65
66 // asset dans classe header_content
67 $admin_header_logo = '<input type="file" id="header_logo_input" class="hidden" accept="image/png, image/jpeg, image/gif, image/webp, image/tiff">
68 <img id="header_logo_open" class="action_icon" src="assets/edit.svg" onclick="header_logo.open()">
69 <img id="header_logo_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_logo.submit()">
70 <img id="header_logo_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_logo.cancel()">';
71
72 // texte dans classe header_content
73 $admin_header_title = '<input type="text" id="header_title_input" class="hidden" value="' . htmlspecialchars($title ?? '') . '" placeholder="nom du site web" size="30">
74 <img id="header_title_open" class="action_icon" src="assets/edit.svg" onclick="header_title.open()">
75 <img id="header_title_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_title.submit()">
76 <img id="header_title_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_title.cancel()">';
77 $admin_header_description = '<input type="text" id="header_description_input" class="hidden" value="' . htmlspecialchars($description ?? '') . '" placeholder="sous-titre ou description" size="30">
78 <img id="header_description_open" class="action_icon" src="assets/edit.svg" onclick="header_description.open()">
79 <img id="header_description_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_description.submit()">
80 <img id="header_description_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_description.cancel()">';
81
82 // icônes réseaux sociaux
83 // boucle sur la liste complète de réseaux sociaux
84 foreach(NodeData::$social_networks as $network){
85 $checked = (isset($social_show[$network]) && $social_show[$network]) ? 'checked' : '';
86 $href = (isset($social[$network]) && $social[$network] !== '') ? 'href="' . $social[$network] . '"' : '';
87
88 $social_networks .= '<div id="header_' . $network . '">
89 <input type="checkbox" onclick="checkSocialNetwork(\'header_' . $network . '\')" ' . $checked . '>
90 <a ' . $href . ' target="_blank" rel="noopener noreferrer">'
91 . $this->insertSVG(self::ICON_PATH . $network . '.svg', ['id' => 'header_' . $network . '_content', 'class' => ($checked ? 'svg_fill_red' : '')])
92 . '</a>
93 <input type="url" id="header_' . $network . '_input" class="hidden" value="' . ($social[$network] ?? "") . '" placeholder="https://..." size="30" onchange="controlURL(this)">
94 <img id="header_' . $network . '_open" class="action_icon" src="assets/edit.svg" onclick="header_' . $network . '.open()">
95 <img id="header_' . $network . '_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_' . $network . '.submit()">
96 <img id="header_' . $network . '_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_' . $network . '.cancel()">
97 <script>let header_' . $network . ' = new InputTextSocialNetwork(\'header_' . $network . '\', {\'has_content\': false});</script>
98 </div>';
99 // {'has_content': false} => InputToggle ne gèrera pas cette balise
100 }
101 }
102 else{
103 $admin_head_favicon = '';
104 $admin_header_background = '';
105 $admin_header_logo = '';
106 $admin_header_title = '';
107 $admin_header_description = '';
108
109 if(isset($social_show)){
110 // boucle sur les réseaux sociaux "activés"
111 foreach(array_keys($social_show) as $network){
112 if($social_show[$network]){
113 $href = (isset($social[$network]) && $social[$network] !== '') ? 'href="' . $social[$network] . '"' : '';
114 $social_networks .= '<div id="header_' . $network . '">
115 <a ' . $href . ' target="_blank" rel="noopener noreferrer">'
116 . $this->insertSVG(self::ICON_PATH . $network . '.svg', ['id' => 'header_' . $network . '_content','class' => 'svg_fill_red'])
117 . '</a>
118 </div>';
119 }
120 }
121 }
122 }
123
124 ob_start();
125 require $viewFile;
126 $this->html .= ob_get_clean();
127 }
128 }
129} \ No newline at end of file
diff --git a/src/view/LoginBuilder.php b/src/view/LoginBuilder.php
deleted file mode 100644
index 479398c..0000000
--- a/src/view/LoginBuilder.php
+++ /dev/null
@@ -1,52 +0,0 @@
1<?php
2// src/view/LoginBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class LoginBuilder extends AbstractBuilder
9{
10 public function __construct(Node $node)
11 {
12 // déjà connecté?
13 if(IS_ADMIN)
14 {
15 header('Location: ' . new URL);
16 die;
17 }
18
19 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
20
21 $captcha = new Captcha;
22 $_SESSION['captcha'] = $captcha->getSolution(); // enregistrement de la réponse du captcha pour vérification
23
24 //$this->html .= $header;
25
26 $error_messages = [
27 'error_non_valid_captcha' => 'Erreur au test anti-robot, veuillez saisir un nombre entier.',
28 'captcha_server_error' => 'captcha_server_error',
29 'bad_solution_captcha' => 'Erreur au test anti-robot, veuillez réessayer.',
30 'bad_login_or_password' => 'Mauvais identifiant ou mot de passe, veuillez réessayer.', // ne pas indiquer où est l'erreur
31 'forbidden_characters' => 'Caractères interdits: espaces, tabulations, sauts CR/LF.'
32 ];
33
34 $link_form = new URL(['action' => 'connection']);
35 isset($_GET['from']) ? $link_form->addParams(['from' => $_GET['from']]) : '';
36 isset($_GET['id']) ? $link_form->addParams(['id' => $_GET['id']]) : '';
37
38 $link_exit = new URL;
39 isset($_GET['from']) ? $link_exit->addParams(['page' => $_GET['from'] ]) : '';
40 isset($_GET['id']) ? $link_exit->addParams(['id' => $_GET['id']]) : '';
41
42 $error = isset($_GET['error']) ? $error_messages[$_GET['error']] : '';
43
44 ob_start();
45 require $viewFile;
46 $this->html = ob_get_clean(); // nouveau contenu
47
48 //$this->html .= <p style='color: red;'>Ce site utilise un cookie « obligatoire » lorsque vous êtes connecté ainsi que sur cette page.<br>Il sera supprimé à votre déconnexion ou dès que vous aurez quitté le site.</p>;
49
50 //$this->html .= $footer;
51 }
52}
diff --git a/src/view/MainBuilder.php b/src/view/MainBuilder.php
deleted file mode 100644
index 1df5846..0000000
--- a/src/view/MainBuilder.php
+++ /dev/null
@@ -1,95 +0,0 @@
1<?php
2// src/view/MainBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Article;
7use App\Entity\Node;
8use App\Entity\Presentation;
9
10class MainBuilder extends AbstractBuilder
11{
12 static public bool $modif_mode = false;
13
14 public function __construct(Node $node)
15 {
16 $this->html .= "<main>\n";
17
18 // page article: cas particulier où l'article est greffé sur main
19 if(Model::$page_path->getLast()->getEndOfPath() === 'article'){
20 // pas censé arriver
21 if(!isset($_GET['id'])){
22 header('Location: ' . new URL);
23 die;
24 }
25
26 // nouvel article
27 if($node->getAdoptedChild() == null){
28 $date = new \DateTime;
29 $article = new Article('', $date);
30 $new = new Node('new', 0, null, null, $article);
31 }
32 // modification
33 else{
34 $new = $node->getAdoptedChild();
35 }
36 //$builder_name = $this->snakeToPascalCase($new->getName()) . 'Builder';
37 $builder_name = 'NewBuilder';
38 $builder = new $builder_name($new);
39 $this->html .= $builder->render();
40 }
41 else{
42 // si action = "modif_page", affiche des commandes supplémentaires
43 if(IS_ADMIN && self::$modif_mode){
44 // ajouter un contrôle du champ in_menu
45 $this->viewEditBlocks($node);
46 }
47
48 // dans tous les cas
49 $this->useChildrenBuilder($node);
50 }
51
52 $this->html .= "</main>\n";
53 }
54
55 // mode modification de page uniquement
56 private function viewEditBlocks(Node $node): void
57 {
58 $options = '';
59 foreach(Presentation::BLOCKS as $key => $value){
60 $options .= '<option value= "' . $key . '">' . $value . "</option>\n";
61 }
62
63 // ceci pourrait être déplacé au début des blocs
64 $bloc_edit = '';
65 foreach($node->getChildren() as $child_node){
66 // ordre des articles 'news'
67 if($child_node->getName() === 'news_block'){
68 $order = $child_node->getNodeData()->getChronoOrder() ? 'chrono' : 'antichrono';
69 }
70
71 // présentation par défaut
72 if(Presentation::hasPresentation($child_node->getName()) && $child_node->getNodeData()->getPresentation() === null){
73 $child_node->getNodeData()->setPresentation('full_width'); // pas de persistence ici
74 }
75
76 ob_start();
77 require self::VIEWS_PATH . 'modify_block.php';
78 $bloc_edit .= ob_get_clean();
79 }
80
81 ob_start();
82 require self::VIEWS_PATH . 'modify_page.php';
83 $this->html .= ob_get_clean();
84 }
85
86 // utilisée dans modify_block.php
87 private function makePresentationOptions(string $presentation): string
88 {
89 $options = '';
90 foreach(Presentation::PRESENTATIONS as $key => $value){
91 $options .= '<option value="' . $key . '" ' . ($presentation === $key ? 'selected' : '') . '>' . $value . '</option>';
92 }
93 return $options;
94 }
95} \ No newline at end of file
diff --git a/src/view/MaintenanceBuilder.php b/src/view/MaintenanceBuilder.php
deleted file mode 100644
index 5ed1ce2..0000000
--- a/src/view/MaintenanceBuilder.php
+++ /dev/null
@@ -1,33 +0,0 @@
1<?php
2// src/view/MaintenanceBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class MaintenanceBuilder extends AbstractBuilder
9{
10 public function __construct(Node $node){
11 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
12
13 // noter qu'un backup a été créé dans UserController::connect()
14 try{
15 $backup_array = Backup::getBackupList();
16 }
17 catch(RuntimeException $e){
18 $backup_array = [];
19 $_SESSION['flash_message'] = $e->getMessage(); // peut écraser une flash error déjà dans $_SESSION['flash_message']
20 }
21
22 $backup_options = '';
23 for($i = count($backup_array) - 1; $i >= 0; $i--){
24 $backup_options .= '<option value="' . $backup_array[$i] . '">' . $backup_array[$i] . '</option>';
25 }
26
27 if(file_exists($viewFile)){
28 ob_start();
29 require $viewFile;
30 $this->html = ob_get_clean();
31 }
32 }
33} \ No newline at end of file
diff --git a/src/view/MenuBuilder.php b/src/view/MenuBuilder.php
deleted file mode 100644
index 0395dfa..0000000
--- a/src/view/MenuBuilder.php
+++ /dev/null
@@ -1,97 +0,0 @@
1<?php
2// src/view/MenuBuilder.php
3//
4// page Menu et chemins en mode admin, fonctionne avec menu.js
5
6declare(strict_types=1);
7
8use App\Entity\Node;
9use App\Entity\Page;
10
11class MenuBuilder extends AbstractBuilder
12{
13 //private int $margin_left_multiplier = 29;
14 private string $options = '';
15
16 public function __construct(?Node $node, bool $template = true)
17 {
18 // $node peut $etre null parce qu'on ne construit pas de page dans MenuAndPathsController, on ne lit pas la table "node", etc
19 $viewFile = $node === null ? self::VIEWS_PATH . 'menu.php' : self::VIEWS_PATH . $node->getName() . '.php';
20
21 if(file_exists($viewFile))
22 {
23 if(IS_ADMIN){
24 $this->unfoldMenu(Model::$menu);
25
26 if($template){
27 $this->unfoldOptions(Model::$menu);
28 }
29 }
30 else{
31 header('Location: ' . new URL);
32 die;
33 }
34
35 // si faux, n'utilise pas le template
36 if($template){
37 ob_start();
38 require $viewFile; // insertion de $this->html généré par unfoldMenu
39 $this->html = ob_get_clean(); // pas de concaténation .= cette fois on écrase
40 }
41 }
42 }
43
44 private function unfoldMenu(Page $page): void
45 {
46 $this->html .= '<div class="level">' . "\n";
47
48 foreach($page->getChildren() as $entry)
49 {
50 $checked = $entry->isHidden() ? '' : 'checked';
51 $this->html .= '<div id="' . $entry->getId() . '" class="menu_edit_entry">
52 <img class="move_entry_icon" onclick="moveOneLevelUp(' . $entry->getId() . ')" src="assets/arrow-left.svg">
53 <img class="move_entry_icon" onclick="moveOneLevelDown(' . $entry->getId() . ')" src="assets/arrow-right.svg">
54 <img class="move_entry_icon" onclick="switchMenuPositions(' . $entry->getId() . ', \'up\')" src="assets/arrow-up.svg">
55 <img class="move_entry_icon" onclick="switchMenuPositions(' . $entry->getId() . ', \'down\')" src="assets/arrow-down.svg">
56 <span class="menu_entry_checkbox">
57 <input type="checkbox" ' . $checked . ' onclick="checkMenuEntry(' . $entry->getId() . ')">
58 </span>';
59
60 // entrées URL: bouton poubelle + nom du lien + adresse
61 if(str_starts_with($entry->getEndOfPath(), 'http')){
62 $this->html .= '<form style="display: inline;" id="delete-i' . $entry->getId() . '" method="post" action="' . new URL(['from' => 'menu_paths']) . '">
63 <input type="hidden" name="delete" value="' . $entry->getId() . '">
64 <input type="image" class="move_entry_icon" src="assets/delete-bin.svg" alt="delete link button" onclick="return confirm(\'Voulez-vous vraiment supprimer cette entrée?\');">
65 </form>
66 <span class="url_name">
67 <button class="url_name"><input type="text" value="' . htmlspecialchars($entry->getPageName()) . '"></button>
68 <img class="move_entry_icon" src="assets/save.svg" onclick="editUrl(' . $entry->getId() . ', \'url_name\')">
69 </span>
70 <span class="url_content">
71 <input type="url" value="' . htmlspecialchars($entry->getEndOfPath()) . '" placeholder="https://..." onchange="controlURL(this)">
72 <img class="move_entry_icon" src="assets/save.svg" onclick="editUrl(' . $entry->getId() . ', \'url_content\')">
73 </span>';
74 }
75 else{
76 $this->html .= '<button>' . $entry->getPageName() . '</button>
77 <i class="path">' . $entry->getPagePath() . '</i>';
78 }
79
80 if(count($entry->getChildren()) > 0){
81 $this->unfoldMenu($entry);
82 }
83 $this->html .= '</div>' . "\n";
84 }
85 $this->html .= "</div>\n";
86 }
87
88 private function unfoldOptions(Page $page): void
89 {
90 foreach($page->getChildren() as $entry){
91 $this->options .= '<option value="' . $entry->getId() . '">' . $entry->getPageName() . "</option>\n";
92 if(count($entry->getChildren()) > 0){
93 $this->unfoldOptions($entry);
94 }
95 }
96 }
97} \ No newline at end of file
diff --git a/src/view/NavBuilder.php b/src/view/NavBuilder.php
deleted file mode 100644
index 6caefbc..0000000
--- a/src/view/NavBuilder.php
+++ /dev/null
@@ -1,73 +0,0 @@
1<?php
2// src/view/NavBuilder.php
3//
4// menu principal
5
6declare(strict_types=1);
7
8use App\Entity\Node;
9use App\Entity\Page;
10
11class NavBuilder extends AbstractBuilder
12{
13 public function __construct(Node $node = null)
14 {
15 $this->html .= '<nav class="nav_main"><ul>';
16 if(count(Model::$menu->getChildren()) > 1 || !Model::$menu->getChildren()[0]->getChildren()->isEmpty()){
17 $this->html .= $this->navMainHTML(
18 Model::$menu, // Menu étend Page
19 Model::$page_path != null ? Model::$page_path->getArray() : []); // param nullable, ça retire une dépendance stricte entre NavBuilder et Model
20 }
21 $this->html .= '</ul></nav>';
22 }
23
24 private function navMainHTML(Page $nav_data, array $current): string
25 {
26 $nav_html = '';
27 static $level = 0;
28
29 foreach($nav_data->getChildren() as $data)
30 {
31 if(!$data->isHidden()){
32 $li_class = '';
33 if(isset($current[$level]) && $data->getEndOfPath() === $current[$level]->getEndOfPath()){
34 $li_class = 'current ';
35 }
36
37 $link = '';
38 if($data->isReachable()) // titre de catégorie du menu non clicable
39 {
40 if(str_starts_with($data->getEndOfPath(), 'http')) // lien vers autre site
41 {
42 $link .= '<a href="' . htmlspecialchars($data->getEndOfPath()) . '" target="_blank">';
43 }
44 elseif($data->getEndOfPath() != '') // lien relatif
45 {
46 $link .= '<a href="' . new URL(['page' => $data->getPagePath()]) . '">';
47 }
48 }
49 else{
50 $link .= '<a>';
51 }
52
53 if(count($data->getChildren()) > 0) // titre de catégorie
54 {
55 $li_class .= $data->getParent() == null ? 'drop-down' : 'drop-right';
56
57 $nav_html .= '<li class="'. $li_class . '">' . $link . '<p id="m_' . $data->getId() . '">' . $data->getPageName() . '</p></a>
58 <button class="sub-menu-toggle" aria-label="Ouvrir le sous-menu">▼</button>
59 <ul class="sub-menu">' . "\n";
60 $level++;
61 $nav_html .= $this->navMainHTML($data, $current);
62 $level--;
63 $nav_html .= '</ul></li>' . "\n";
64 }
65 else
66 {
67 $nav_html .= '<li class="'. $li_class . '">' . $link . '<p id="m_' . $data->getId() . '">' . $data->getPageName() . '</p></a></li>' . "\n";
68 }
69 }
70 }
71 return $nav_html;
72 }
73} \ No newline at end of file
diff --git a/src/view/NewBuilder.php b/src/view/NewBuilder.php
deleted file mode 100644
index 7459cc8..0000000
--- a/src/view/NewBuilder.php
+++ /dev/null
@@ -1,152 +0,0 @@
1<?php
2// src/view/NewBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class NewBuilder extends AbstractBuilder
9{
10 static public bool $new_article_mode = false;
11
12 public function __construct(Node $node)
13 {
14 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
15
16 if(file_exists($viewFile))
17 {
18 if(self::$new_article_mode){
19 $id = $_GET['id']; // ici l'id est le nom du block news_block parent
20 $title = '';
21 $preview = '';
22
23 // lettre au début de l'id: t = title, p = preview, i = article, d = date
24 $id_title = $id;
25 $id_title[0] = 't';
26 $id_preview = $id;
27 $id_preview[0] = 'p';
28 $id_content = 'i' . $id;
29 $id_content[0] = 'i';
30 $id_date = $id;
31 $id_date[0] = 'd';
32 }
33 else{
34 $id = (string)$node->getId();
35
36 // html, date
37 $title = $node->getArticle()->getTitle();
38 $preview = $node->getArticle()->getPreview();
39
40 // lettre au début de l'id: t = title, p = preview, i = article, d = date
41 $id_title = 't' . $id;
42 $id_preview = 'p' . $id;
43 $id_content = 'i' . $id;
44 $id_date = 'd' . $id;
45 }
46
47 $content = '';
48 if(CURRENT_PAGE === 'article'){
49 $content = $node->getArticle()->getContent();
50 $from_to_button = '<p><a class="link_to_article" href="' . new URL(isset($_GET['from']) ? ['page' => $_GET['from']] : []) . '"><button>Retour</button></a></p>';
51 $overflow = '';
52 }
53 else{
54 $from_to_button = '<p><a class="link_to_article" href="' . new URL(['page' => 'article', 'id' => $id, 'from' => CURRENT_PAGE]) . '"><button><img class="action_icon" src="assets/book-open.svg">Lire la suite</button></a></p>';
55 $overflow = ' overflow_hidden';
56 }
57
58 $date = $node->getArticle()->getDateTime()->format('Y-m-d\TH:i:s.v\Z'); // format: 2025-07-17T13:54:00.000Z
59 // format(\DateTime::ATOM) produit le format: 2025-10-10T12:17:00+00:00, c'est aussi de la norme ISO, mais à éviter pour être compatible avec date.toISOString en JS
60
61 // partage
62 $share_link = new URL(['page' => 'article', 'id' => $id]);
63 $share_js = 'onclick="copyInClipBoard(\'' . $share_link . '\')"';
64 if(isset($_GET['id']) && $_GET['id'][0] === 'n'){
65 $class = 'class="share hidden"';
66 }
67 else{
68 $class = 'class="share"';
69 }
70 $share_button = '<p ' . $class . ' ' . $share_js . '><img class="action_icon" src="assets/share.svg"></p>' . "\n";
71
72 // modifier un article
73 $title_buttons = '';
74 $preview_buttons = '';
75 $article_buttons = '';
76 $date_buttons = '';
77 $admin_buttons = '';
78 if(IS_ADMIN){
79 if(CURRENT_PAGE === 'article'){
80 $title_js = 'onclick="openEditor(\'' . $id_title . '\')"';
81 $modify_title = '<p id="edit-' . $id_title . '"><button ' . $title_js . '><img class="action_icon" src="assets/edit.svg">Titre</button></p>' . "\n";
82 $close_js_title = 'onclick="closeEditor(\'' . $id_title . '\')"';
83 $close_editor_title = '<p id="cancel-' . $id_title . '" class="hidden"><button ' . $close_js_title . '>Annuler</button></p>';
84 $submit_js_title = 'onclick="submitArticle(\'' . $id_title . '\')"';
85 $submit_title = '<p id="submit-' . $id_title . '" class="hidden"><button ' . $submit_js_title . '>Valider</button></p>';
86 $title_buttons = '<div class="button_zone">' . $modify_title . $close_editor_title . $submit_title . '</div>';
87
88 $preview_js = 'onclick="openEditor(\'' . $id_preview . '\')"';
89 $modify_preview = '<p id="edit-' . $id_preview . '"><button ' . $preview_js . '><img class="action_icon" src="assets/edit.svg">Aperçu</button></a></p>' . "\n";
90 $close_js_preview = 'onclick="closeEditor(\'' . $id_preview . '\')"';
91 $close_editor_preview = '<p id="cancel-' . $id_preview . '" class="hidden"><button ' . $close_js_preview . '>Annuler</button></p>';
92 $submit_js_preview = 'onclick="submitArticle(\'' . $id_preview . '\')"';
93 $submit_preview = '<p id="submit-' . $id_preview . '" class="hidden"><button ' . $submit_js_preview . '>Valider</button></p>';
94 $preview_buttons = '<div class="button_zone">' . $modify_preview . $close_editor_preview . $submit_preview . '</div>';
95
96 $article_js = 'onclick="openEditor(\'' . $id_content . '\')"';
97 $modify_article = '<p id="edit-' . $id_content . '"><button ' . $article_js . '><img class="action_icon" src="assets/edit.svg">Article</button></p>' . "\n";
98 $close_js_article = 'onclick="closeEditor(\'' . $id_content . '\')"';
99 $close_editor_article = '<p id="cancel-' . $id_content . '" class="hidden"><button ' . $close_js_article . '>Annuler</button></p>';
100 $submit_js_article = 'onclick="submitArticle(\'' . $id_content . '\')"';
101 $submit_article = '<p id="submit-' . $id_content . '" class="hidden"><button ' . $submit_js_article . '>Valider</button></p>';
102 $article_buttons = '<div class="button_zone">' . $modify_article . $close_editor_article . $submit_article . '</div>';
103
104 $date_js = 'onclick="openDatetimeLocalInput(\'' . $id_date . '\', \'article\');';
105 $modify_date = '<p id="edit-' . $id_date . '"><button ' . $date_js . '"><img class="action_icon" src="assets/edit.svg">Date</button></p>' . "\n";
106 $close_js_date = 'onclick="closeInput(\'' . $id_date . '\')"';
107 $close_editor_date = '<p id="cancel-' . $id_date . '" class="hidden"><button ' . $close_js_date . '>Annuler</button></p>';
108 $submit_js_date = 'onclick="submitDate(\'' . $id_date . '\')"';
109 $submit_date = '<p id="submit-' . $id_date . '" class="hidden"><button ' . $submit_js_date . '>Valider</button></p>';
110 $date_buttons = '<div class="button_zone">' . $modify_date . $close_editor_date . $submit_date . '</div>';
111
112 // mode nouvel article
113 if(self::$new_article_mode){
114 $delete_article = '';
115 // valider la création d'un nouvel article
116 $submit_js = 'onclick="submitArticle(\'' . $_GET['id'] . '\')"';
117 $submit_article = '<p id="save-' . $id . '"><img class="action_icon delete_button" src="assets/save.svg" ' . $submit_js . '></p>' . "\n";
118 }
119 // mode article existant
120 else{
121 $url = new URL(['action' => 'delete_article', 'id' => $_GET['id'], 'from' => $_GET['from'] ?? '']);
122 $delete_article = '<form id="delete-' . $id . '" method="post" action="' . $url . '">
123 <p>
124 <img src="assets/delete-bin.svg" alt="Supprimer l\'article" class="action_icon" style="cursor: pointer;" onclick="if(confirm(\'Voulez-vous vraiment supprimer cet article ?\')) { this.closest(\'form\').submit(); }"
125 </p>
126 </form>' . "\n"; // this.closest('form').submit() = submit du formulaire avec javascript
127 $submit_article = '';
128 }
129
130 $admin_buttons = $share_button . $delete_article . $submit_article . $from_to_button;
131 }
132 // autre page
133 else{
134 $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"';
135 $delete_article = '<p id="delete-' . $id . '"><img class="action_icon" src="assets/delete-bin.svg" ' . $delete_js . '></p>' . "\n";
136
137 $close_editor = '<p id="cancel-' . $id . '" class="hidden"></p>';
138 $submit_article = '<p id="submit-' . $id . '" class="hidden"></p>';
139
140 $admin_buttons = $from_to_button . $share_button . $delete_article . $close_editor . $submit_article;
141 }
142 }
143 else{
144 $admin_buttons = $share_button . $from_to_button;
145 }
146
147 ob_start();
148 require($viewFile);
149 $this->html .= ob_get_clean();
150 }
151 }
152}
diff --git a/src/view/NewPageBuilder.php b/src/view/NewPageBuilder.php
deleted file mode 100644
index 0a3a137..0000000
--- a/src/view/NewPageBuilder.php
+++ /dev/null
@@ -1,49 +0,0 @@
1<?php
2// src/view/NewPageBuilder.php
3//
4// page Nouvelle page en mode admin, fonctionne avec new_page.js
5
6declare(strict_types=1);
7
8use App\Entity\Node;
9use App\Entity\Page;
10
11class NewPageBuilder extends AbstractBuilder
12{
13 //private int $margin_left_multiplier = 29;
14 private string $options = '';
15
16 public function __construct(Node $node = null)
17 {
18 //parent::__construct($node);
19 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
20
21 if(IS_ADMIN && file_exists($viewFile))
22 {
23 /*if(!empty($node->getNodeData()->getData()))
24 {
25 extract($node->getNodeData()->getData());
26 }*/
27
28 $this->unfoldOptions(Model::$menu);
29
30 ob_start();
31 require $viewFile; // insertion de $this->html généré par unfoldMenu
32 $this->html = ob_get_clean(); // pas de concaténation .= cette fois on écrase
33 }
34 else{
35 header('Location: ' . new URL);
36 die;
37 }
38 }
39
40 private function unfoldOptions(Page $page): void
41 {
42 foreach($page->getChildren() as $entry){
43 $this->options .= '<option value="' . $entry->getId() . '">' . $entry->getPageName() . "</option>\n";
44 if(count($entry->getChildren()) > 0){
45 $this->unfoldOptions($entry);
46 }
47 }
48 }
49} \ No newline at end of file
diff --git a/src/view/NewsBlockBuilder.php b/src/view/NewsBlockBuilder.php
deleted file mode 100644
index f1fe12b..0000000
--- a/src/view/NewsBlockBuilder.php
+++ /dev/null
@@ -1,73 +0,0 @@
1<?php
2// src/view/NewsBlockBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class NewsBlockBuilder extends AbstractBuilder
9{
10 public function __construct(Node $node)
11 {
12 parent::__construct($node);
13
14 // à remplacer par list.php/grid.php (une vue par stratégie) le jour ou ou a besoin de les différencier
15 $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; // = news_block.php, actuellement identique à post_block.php
16
17 if(file_exists($viewFile))
18 {
19 if(!empty($node->getNodeData()->getData()))
20 {
21 extract($node->getNodeData()->getData());
22 }
23
24 // stratégie d'affichage du contenu (utilisation de méthodes ou de classe List, GridPresentation, etc)
25 $section_class = $node->getNodeData()->getPresentation(); // = list, grid , mosaic ou carousel
26 $cols_min_width = '';
27 if($section_class === 'grid'){
28 $min_width = $node->getNodeData()->getColsMinWidth();
29 $cols_min_width = 'grid-template-columns: repeat(auto-fit, minmax(' . (string)$min_width . 'px, 1fr));';
30 }
31 $fetch_button_hidden = $node->getNodeData()->getNumberOfPages() > 1 ? '' : ' hidden';
32
33 // ajouter un article
34 $new_article = '';
35 if(IS_ADMIN)
36 {
37 $id = 'n' . $this->id_node;
38
39 $share_button = '<p class="share hidden"><img class="action_icon" src="assets/share.svg"></p>';
40
41 $new_button = '<p><a class="link_to_article" href="' . new URL(['page' => 'article', 'id' => $id, 'from' => CURRENT_PAGE]) . '"><button><img class="action_icon" src="assets/edit.svg">Nouvel article</button></a></p>';
42
43 $modify_js = 'onclick="openEditor(\'' . $id . '\')"';
44 $modify_article = '<p id="edit-' . $id . '" class="hidden"><img class="action_icon" src="assets/edit.svg" ' . $modify_js . '></p>' . "\n";
45
46 $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"';
47 $delete_article = '<p id="delete-' . $id . '" class="hidden"><img class="action_icon" src="assets/delete-bin.svg" ' . $delete_js . '></p>' . "\n";
48
49 $close_js = 'onclick="closeEditor(\'' . $id . '\')"';
50 $close_editor = '<p id="cancel-' . $id . '" class="hidden"><button ' . $close_js . '>Annuler</button></p>';
51
52 $submit_js = 'onclick="submitArticle(\'' . $id . '\', clone' . $this->id_node . ')"';
53 $submit_article = '<p id="submit-' . $id . '" class="hidden"><button ' . $submit_js . '>Valider</button></p>';
54
55 $html = '';
56 $admin_buttons = $new_button . $modify_article . $delete_article . $close_editor . $submit_article;
57
58 // insérer post.php pour le bouton "Nouvel article", new.php pour les vrais articles
59 ob_start();
60 require self::VIEWS_PATH . 'post.php'; // nécéssite $admin_buttons et $html
61 $new_article = ob_get_clean();
62 }
63
64 // articles existants
65 $this->useChildrenBuilder($node);
66 $content = $this->html;
67
68 ob_start();
69 require $viewFile; // utilise $content
70 $this->html = ob_get_clean(); // nouveau contenu
71 }
72 }
73} \ No newline at end of file
diff --git a/src/view/PostBlockBuilder.php b/src/view/PostBlockBuilder.php
deleted file mode 100644
index c6094e8..0000000
--- a/src/view/PostBlockBuilder.php
+++ /dev/null
@@ -1,88 +0,0 @@
1<?php
2// src/view/PostBlockBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class PostBlockBuilder extends AbstractBuilder
9{
10 public function __construct(Node $node)
11 {
12 parent::__construct($node);
13
14 // à remplacer par list.php/grid.php (une vue par stratégie) le jour ou ou a besoin de les différencier
15 $viewFile = self::VIEWS_PATH . $node->getName() . '.php'; // = post_block.php, actuellement identique à news_block.php
16
17 if(file_exists($viewFile))
18 {
19 if(!empty($node->getNodeData()->getData()))
20 {
21 extract($node->getNodeData()->getData());
22 }
23
24 // stratégie d'affichage du contenu (utilisation de méthodes ou de classe List, GridPresentation, etc)
25 $section_class = $node->getNodeData()->getPresentation(); // = list, grid , mosaic ou carousel
26 $cols_min_width = '';
27 if($section_class === 'grid'){
28 $min_width = $node->getNodeData()->getColsMinWidth();
29 $cols_min_width = 'grid-template-columns: repeat(auto-fit, minmax(' . (string)$min_width . 'px, 1fr));';
30 }
31 $fetch_button_hidden = $node->getNodeData()->getNumberOfPages() > 1 ? '' : ' hidden';
32
33 // ajouter un article
34 // => fait un peu double emploi avec PostBuilder
35 $new_article = '';
36 if(IS_ADMIN)
37 {
38 $id = 'n' . $this->id_node;
39
40 $share_button = '<p class="share hidden"><img class="action_icon" src="assets/share.svg"></p>';
41
42 $new_button = '<p id="new-' . $id . '">' . "\n" . '<button onclick="openEditor(\'' . $id . '\')"><img class="action_icon" src="assets/edit.svg">Nouvel article</button></p>';
43
44 $modify_js = 'onclick="openEditor(\'' . $id . '\')"';
45 $modify_article = '<p id="edit-' . $id . '" class="hidden"><img class="action_icon" src="assets/edit.svg" ' . $modify_js . '></p>' . "\n";
46
47 $up_js = 'onclick="switchPositions(\'' . $id . '\', \'up\')"';
48 $up_button = '<p id="position_up-' . $id . '" class="hidden"><img class="action_icon" src="assets/arrow-up.svg" ' . $up_js . '></p>' . "\n";
49
50 $down_js = 'onclick="switchPositions(\'' . $id . '\', \'down\')"';
51 $down_button = '<p id="position_down-' . $id . '" class="hidden"><img class="action_icon" src="assets/arrow-down.svg" ' . $down_js . '></p>' . "\n";
52
53 $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"';
54 $delete_article = '<p id="delete-' . $id . '" class="hidden"><img class="action_icon" src="assets/delete-bin.svg" ' . $delete_js . '></p>' . "\n";
55
56 $close_js = 'onclick="closeEditor(\'' . $id . '\')"';
57 $close_editor = '<p id="cancel-' . $id . '" class="hidden"><button ' . $close_js . '>Annuler</button></p>';
58
59 $position = '<div id="radio-' . $id . '" class="hidden" style="margin: 5px; font-size: 90%;">
60 Placement:<br>
61 <input type="radio" id="radio_first-' . $id . '" name="article_placement-' . $id . '" value="first" onchange="setArticlePlacement(\'' . $id . '\')">
62 <label for="radio_start">en premier</label><br>
63 <input type="radio" id="radio_last-' . $id . '" name="article_placement-' . $id . '" value="last" onchange="setArticlePlacement(\'' . $id . '\')">
64 <label for="radio_end">en dernier</label>
65 </div>';
66
67 $submit_js = 'onclick="submitArticle(\'' . $id . '\', clone' . $this->id_node . ')"';
68 $submit_article = '<p id="submit-' . $id . '" class="hidden"><button ' . $submit_js . '>Valider</button></p>';
69
70 $html = '';
71 $admin_buttons = $new_button . $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article . $position;
72
73 // squelette d'un nouvel article
74 ob_start();
75 require self::VIEWS_PATH . 'post.php';
76 $new_article = ob_get_clean();
77 }
78
79 // articles existants
80 $this->useChildrenBuilder($node);
81 $content = $this->html;
82
83 ob_start();
84 require $viewFile;
85 $this->html = ob_get_clean(); // pas de concaténation ici, on écrase
86 }
87 }
88} \ No newline at end of file
diff --git a/src/view/PostBuilder.php b/src/view/PostBuilder.php
deleted file mode 100644
index e0bf985..0000000
--- a/src/view/PostBuilder.php
+++ /dev/null
@@ -1,57 +0,0 @@
1<?php
2// src/view/PostBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7
8class PostBuilder extends AbstractBuilder
9{
10 public function __construct(Node $node)
11 {
12 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
13
14 if(file_exists($viewFile))
15 {
16 // html
17 $title = $node->getArticle()->getTitle();
18 $html = $node->getArticle()->getContent();
19 $id = (string)$node->getId();
20
21 // partage
22 $share_link = new URL(['page' => CURRENT_PAGE], $id);
23 $share_js = 'onclick="copyInClipBoard(\'' . $share_link . '\')"';
24 $share_button = '<p class="share" ' . $share_js . '><img class="action_icon" src="assets/share.svg"></p>' . "\n";
25
26 // modifier un article
27 // => fait un peu double emploi avec PostBlockBuilder
28 $admin_buttons = '';
29 if(IS_ADMIN)
30 {
31 $modify_js = 'onclick="openEditor(\'' . $id . '\')"';
32 $modify_article = '<p id="edit-' . $id . '"><img class="action_icon" src="assets/edit.svg" ' . $modify_js . '></p>' . "\n";
33
34 $up_js = 'onclick="switchPositions(\'' . $id . '\', \'up\')"';
35 $up_button = '<p id="position_up-' . $id . '"><img class="action_icon" src="assets/arrow-up.svg" ' . $up_js . '></p>' . "\n";
36
37 $down_js = 'onclick="switchPositions(\'' . $id . '\', \'down\')"';
38 $down_button = '<p id="position_down-' . $id . '"><img class="action_icon" src="assets/arrow-down.svg" ' . $down_js . '></p>' . "\n";
39
40 $delete_js = 'onclick="deleteArticle(\'' . $id . '\')"';
41 $delete_article = '<p id="delete-' . $id . '"><img class="action_icon" src="assets/delete-bin.svg" ' . $delete_js . '></p>' . "\n";
42
43 $close_js = 'onclick="closeEditor(\'' . $id . '\')"';
44 $close_editor = '<p id="cancel-' . $id . '" class="hidden"><button ' . $close_js . '>Annuler</button></p>';
45
46 $submit_js = 'onclick="submitArticle(\'' . $id . '\')"';
47 $submit_article = '<p id="submit-' . $id . '" class="hidden"><button ' . $submit_js . '>Valider</button></p>';
48
49 $admin_buttons = $modify_article . $up_button . $down_button . $delete_article . $close_editor . $submit_article;
50 }
51
52 ob_start();
53 require($viewFile);
54 $this->html .= ob_get_clean();
55 }
56 }
57}
diff --git a/src/view/ShowEmailsBuilder.php b/src/view/ShowEmailsBuilder.php
deleted file mode 100644
index e88b349..0000000
--- a/src/view/ShowEmailsBuilder.php
+++ /dev/null
@@ -1,69 +0,0 @@
1<?php
2// src/view/ShowEmailsBuilder.php
3
4declare(strict_types=1);
5
6use App\Entity\Node;
7use App\Entity\Page;
8
9class ShowEmailsBuilder extends AbstractBuilder
10{
11 public function __construct(?Node $node)
12 {
13 //parent::__construct($node);
14 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
15 if(file_exists($viewFile))
16 {
17 // objets Email groupés par destinataire
18 $emails_by_recipient = [];
19 foreach($node->getNodeData()->getEmails() as $email){
20 $recipient = $email->getRecipient();
21 $emails_by_recipient[$recipient][] = $email;
22 }
23
24 // affiche une table par destinataire
25 $emails = '';
26 foreach($emails_by_recipient as $recipient => $emails_list){
27 $html = '<h4>Destinataire: ' . $recipient . '</h4>
28 <table>
29 <thead>
30 <tr>
31 <th>Expéditeur</th>
32 <th>Adresse</th>
33 <th>Contenu</th>
34 <th>Date</th>
35 <th>Effacement prévu le</th>
36 <th>Sensible</th>
37 <th class="email_delete_button"></th>
38 </tr>
39 </thead>
40 <tbody>';
41
42 // insère les données
43 foreach($emails_list as $email){
44 $html .= '<tr id="' . $email->getId() . '">
45 <td>' . htmlspecialchars($email->getSenderName()) . '</td>
46 <td>' . htmlspecialchars($email->getSenderAddress()) . '</td>
47 <td>' . htmlspecialchars($email->getContent()) . '</td>
48 <td>' . $email->getDateTime()->format('d/m/Y') . '</td>
49 <td class="deletion_date">' . $email->getDeletionDate()->format('d/m/Y') . '</td>
50 <td><input class="make_checkbox_sensitive" type="checkbox" ' . ($email->isSensitive() ? 'checked' : '') . ' onclick="toggleSensitiveEmail(' . $email->getId() . ')"></td>
51 <td class="email_delete_button"><img class="action_icon" src="assets/delete-bin.svg" onclick="deleteEmail(' . $email->getId() . ')"></td>
52 </tr>';
53 }
54
55 $html .= '</tbody>
56 </table>';
57 $emails .= $html;
58 }
59
60 ob_start();
61 require $viewFile; // insertion de $this->html généré par unfoldMenu
62 $this->html = ob_get_clean(); // pas de concaténation .= cette fois on écrase
63 }
64 else{
65 header('Location: ' . new URL(['error' => 'show_emails_view_not_found']));
66 die;
67 }
68 }
69} \ No newline at end of file
diff --git a/src/view/UserEditBuilder.php b/src/view/UserEditBuilder.php
deleted file mode 100644
index 0a347d4..0000000
--- a/src/view/UserEditBuilder.php
+++ /dev/null
@@ -1,62 +0,0 @@
1<?php
2// src/view/UserEditBuilder.php
3//
4// fonctionne avec UserController
5
6declare(strict_types=1);
7
8use App\Entity\Node;
9
10class UserEditBuilder extends AbstractBuilder
11{
12 public function __construct(Node $node)
13 {
14 // pour éviter les arnaques
15 if(!IS_ADMIN)
16 {
17 header('Location: ' . new URL);
18 die;
19 }
20
21 $viewFile = self::VIEWS_PATH . $node->getName() . '.php';
22
23 $error_messages = [
24 'error_non_valid_captcha' => 'Erreur au test anti-robot, veuillez saisir un nombre entier.',
25 'captcha_server_error' => 'captcha_server_error',
26
27 'bad_login_or_password' => 'Mauvais identifiant ou mot de passe, veuillez réessayer.', // ne pas indiquer où est l'erreur
28 'bad_solution_captcha' => 'Erreur au test anti-robot, veuillez réessayer.',
29 'forbidden_characters' => 'Caractères interdits: espaces, tabulations, sauts CR/LF.',
30 'same_username_as_before' => 'Nouveau nom identique au précédent.',
31 'same_password_as_before' => 'Nouveau mot de passe identique au précédent.'
32 ];
33
34 $error_username = isset($_GET['error_username']) ? $error_messages[$_GET['error_username']] : '';
35 $success_username = (isset($_GET['success_username']) && $_GET['success_username']) ? 'Identifiant modifié avec succès.' : '';
36 $error_password = isset($_GET['error_password']) ? $error_messages[$_GET['error_password']] : '';
37 $success_password = (isset($_GET['success_password']) && $_GET['success_password']) ? 'Mot de passe modifié avec succès.' : '';
38
39 $captcha = new Captcha;
40 $_SESSION['captcha'] = $captcha->getSolution(); // enregistrement de la réponse du captcha pour vérification
41
42 $link_user_form = new URL(['action' => 'update_username']);
43 isset($_GET['from']) ? $link_user_form->addParams(['from' => $_GET['from']]) : '';
44 isset($_GET['id']) ? $link_user_form->addParams(['id' => $_GET['id']]) : '';
45
46 $link_password_form = new URL(['action' => 'update_password']);
47 isset($_GET['from']) ? $link_password_form->addParams(['from' => $_GET['from']]) : '';
48 isset($_GET['id']) ? $link_password_form->addParams(['id' => $_GET['id']]) : '';
49
50 $link_exit = new URL;
51 isset($_GET['from']) ? $link_exit->addParams(['page' => $_GET['from'] ]) : '';
52 isset($_GET['id']) ? $link_exit->addParams(['id' => $_GET['id']]) : '';
53
54 $link_logout = new URL(['action' => 'deconnection']);
55 isset($_GET['from']) ? $link_logout->addParams(['from' => $_GET['from'] ]) : '';
56 isset($_GET['id']) ? $link_logout->addParams(['id' => $_GET['id']]) : '';
57
58 ob_start();
59 require $viewFile;
60 $this->html = ob_get_clean(); // nouveau contenu
61 }
62} \ No newline at end of file
diff --git a/src/view/ViewDirector.php b/src/view/ViewDirector.php
deleted file mode 100644
index c1a38b8..0000000
--- a/src/view/ViewDirector.php
+++ /dev/null
@@ -1,28 +0,0 @@
1<?php
2// src/view/ViewDirector.php
3//
4// génère le HTML avec des Builder
5
6declare(strict_types=1);
7
8use App\Entity\Node;
9
10class ViewDirector extends AbstractBuilder // ViewDirector est le premier Builder
11{
12 public Node $root_node;
13
14 public function __construct(){} // surcharge celui de AbstractBuilder
15
16 public function buildHTML(Node $root_node): string
17 {
18 $this->root_node = $root_node;
19 $this->useChildrenBuilder($this->root_node);
20
21 if(isset($_SESSION['flash_message'])){
22 $this->html .= '<script>window.flash_message = "' . $_SESSION['flash_message'] . '";</script>';
23 unset($_SESSION['flash_message']);
24 }
25
26 return $this->html;
27 }
28} \ No newline at end of file
diff --git a/src/view/templates/calendar.php b/src/view/templates/calendar.php
deleted file mode 100644
index 18f68f2..0000000
--- a/src/view/templates/calendar.php
+++ /dev/null
@@ -1,11 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="calendar" id="<?= $this->id_node ?>">
3 <?= HeadBuilder::insertJS('fullcalendar/index.global.min') ?>
4 <?= HeadBuilder::insertJS('fullcalendar/fr.global.min') ?>
5 <?= HeadBuilder::insertJS($calendar_js_file) ?>
6 <h3><?= $title ?></h3>
7 <div id="calendar_zone">
8 <div id="calendar"></div>
9 <aside id="event_modal" class="modal"></aside>
10 </div>
11</section> \ No newline at end of file
diff --git a/src/view/templates/footer.php b/src/view/templates/footer.php
deleted file mode 100644
index 6450e21..0000000
--- a/src/view/templates/footer.php
+++ /dev/null
@@ -1,40 +0,0 @@
1<?php declare(strict_types=1); ?>
2 <footer>
3<?= $breadcrumb ?>
4 <div class="data">
5 <div class="contact">
6 <div id="footer_name">
7 <span id="footer_name_content"><?= htmlspecialchars($name ?? '') ?></span>
8 <?= $admin_footer_name ?>
9 </div>
10 <div id="footer_address">
11 <span id="footer_address_content"><?= htmlspecialchars($address ?? '') ?></span>
12 <?= $admin_footer_address ?>
13 </div>
14 <div id="footer_email">
15 <a id="footer_email_content" href="mailto:<?= htmlspecialchars($email ?? '') ?>"><?= htmlspecialchars($email ?? '') ?></a>
16 <?= $admin_footer_email ?>
17 </div>
18 </div>
19 <div id="footer_logo">
20 <a href="<?= new URL ?>"><img id="footer_logo_content" src="<?= $footer_logo ?? '' ?>" alt=""></a>
21 <?= $admin_footer_logo ?>
22 </div>
23<?php if(IS_ADMIN){ ?>
24 <script>
25 let footer_name = new InputText('footer_name');
26 let footer_address = new InputText('footer_address');
27 let footer_email = new InputText('footer_email');
28 let footer_logo = new InputFile('footer_logo');
29 </script>
30<?php } ?>
31 </div>
32 <div class="<?= $empty_admin_zone ?>"></div>
33 <div class="<?= $div_admin ?>">
34<?= $zone_admin ?>
35 </div>
36 <div id="toast"></div>
37 </footer>
38 </div>
39 </body>
40</html> \ No newline at end of file
diff --git a/src/view/templates/form.php b/src/view/templates/form.php
deleted file mode 100644
index 384bde9..0000000
--- a/src/view/templates/form.php
+++ /dev/null
@@ -1,36 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="form" id="<?= $this->id_node ?>">
3 <h3><?= $title ?></h3>
4 <?= $admin_content ?>
5 <div class="form_inputs">
6 <label for="email_name_<?= $node->getNodeData()->getId() ?>">Votre nom</label>
7 <input id="email_name_<?= $node->getNodeData()->getId() ?>" type="text" name="email_name" value="">
8
9 <label for="email_address_<?= $node->getNodeData()->getId() ?>">Votre e-mail</label>
10 <input id="email_address_<?= $node->getNodeData()->getId() ?>" type="email" name="email_address" placeholder="mon-adresse@email.fr" value="" onchange="checkCase(<?= $node->getNodeData()->getId() ?>)">
11
12 <label for="email_message_<?= $node->getNodeData()->getId() ?>">Votre message</label>
13 <textarea id="email_message_<?= $node->getNodeData()->getId() ?>" type="text" name="email_message" rows="4"></textarea>
14
15 <div class="full_width_column">
16 <label for="email_captcha_<?= $node->getNodeData()->getId() ?>" >Montrez que vous n'êtes pas un robot</label>
17 </div>
18
19 <label for="email_captcha_<?= $node->getNodeData()->getId() ?>" >Combien font <?= self::$captcha->getA() ?> fois <?= self::$captcha->getB() ?>?</label>
20 <div>
21 <input id="email_captcha_<?= $node->getNodeData()->getId() ?>" type="text" name="email_captcha" size="1" autocomplete="off">
22 </div>
23
24 <input id="form_id_hidden" type="hidden" name="form_id_hidden" value="">
25 <input id="email_hidden_<?= $node->getNodeData()->getId() ?>" type="hidden" name="email_hidden">
26
27 <div class="full_width_column">
28 <input type="submit" value="Envoyez votre message" onclick="sendVisitorEmail(<?= $node->getNodeData()->getId() ?>)">
29 </div>
30
31 <p class="send_email_success_<?= $node->getNodeData()->getId() ?> full_width_column"></p>
32 </div>
33 <p id="form_warning_<?= $node->getNodeData()->getId() ?>" class="form_warning <?= ($keep_emails ?? false) ? '' : 'hidden' ?>"><i>
34 Une copie de votre e-mail (nom, adresse et message) sera conservée dans notre base de données dans le but de pouvoir mieux répondre à votre demande et éventuellement dans d'autres buts (prospection). Ces données seront traitées automatiquement par notre serveur et conservées pendant au maximum <?= $retention_period ?> mois à compter de votre dernier message. Ce traitement repose sur votre consentement. Vous pouvez consulter, modifier ou supprimer vos données sur simple demande.
35 </i></p>
36</section> \ No newline at end of file
diff --git a/src/view/templates/form_admin.php b/src/view/templates/form_admin.php
deleted file mode 100644
index d745656..0000000
--- a/src/view/templates/form_admin.php
+++ /dev/null
@@ -1,72 +0,0 @@
1<?php
2// src/view/templates/form_params.php
3declare(strict_types=1);
4// note: l'id ici n'est pas celui du noeud bloc mais celui de l'entrée dans node_data correspondante
5?>
6<div class="admin_form">
7 <p>
8 <label for="keep_emails_<?= $node->getNodeData()->getId() ?>">Conserver les e-mails en base de données</label>
9 <input type="checkbox" id="keep_emails_<?= $node->getNodeData()->getId() ?>" <?= $keep_emails ? 'checked' : '' ?> onclick="keepEmails(<?= $node->getNodeData()->getId() ?>)">
10 </p>
11 <p><i>Notez que ces enregistrements sont des données personnelles et sont concernés par le RGPD.</i></p>
12 <p><a href="<?= new URL(['page' => 'emails']) ?>"><button>Consulter les e-mails enregistrés</button></a></p>
13 <p>
14 <label for="retention_period_<?= $node->getNodeData()->getId() ?>">Durée de conservation (en mois)</label>
15 <input type="number" id="retention_period_<?= $node->getNodeData()->getId() ?>" min="0" value="<?= $retention_period ?>" size="2" onchange="setEmailsRetentionPeriod(<?= $node->getNodeData()->getId() ?>, 'retention_period')">
16 </p>
17 <p>
18 <label for="retention_period_sensible_<?= $node->getNodeData()->getId() ?>">Durée de conservation des emails sensibles(en mois)</label>
19 <input type="number" id="retention_period_sensible_<?= $node->getNodeData()->getId() ?>" min="0" value="<?= $retention_period_sensible ?>" size="2" onchange="setEmailsRetentionPeriod(<?= $node->getNodeData()->getId() ?>, 'retention_period_sensible')">
20 </p>
21
22</div>
23<div class="admin_form">
24 <h3>Paramètres d'envoi</h3>
25 <p>
26 <label for="smtp_host_<?= $node->getNodeData()->getId() ?>">Adresse serveur SMTP</label>
27 <input id="smtp_host_<?= $node->getNodeData()->getId() ?>" type="text" name="smtp_host" placeholder="smtp.messagerie.fr" value="<?= htmlspecialchars($smtp_host) ?>">
28 <input type="hidden" id="smtp_host_hidden_<?= $node->getNodeData()->getId() ?>" value="">
29 <button onclick="setEmailParam('smtp_host', <?= $node->getNodeData()->getId() ?>)">Valider</button>
30 </p>
31 <p>
32 <label for="smtp_secure_<?= $node->getNodeData()->getId() ?>">Chiffrement</label>
33 <select id="smtp_secure_<?= $node->getNodeData()->getId() ?>" name="smtp_secure" onchange="setEmailParam('smtp_secure', <?= $node->getNodeData()->getId() ?>)">
34 <option value="plain_text" >Aucun (port 25)</option>
35 <option value="tls" <?= htmlspecialchars($smtp_secure) === 'tls' ? 'selected' : '' ?>>StartTLS (port 587)</option>
36 <option value="ssl" <?= htmlspecialchars($smtp_secure) === 'ssl' ? 'selected' : '' ?>>SSL (port 465)</option>
37 </select>
38 <input type="hidden" id="smtp_secure_hidden_<?= $node->getNodeData()->getId() ?>" value="">
39 </p>
40 <p>
41 <label for="smtp_username_<?= $node->getNodeData()->getId() ?>">Identifiant (adresse e-mail)</label>
42 <input id="smtp_username_<?= $node->getNodeData()->getId() ?>" type="email" name="smtp_username" autocomplete="new-password" placeholder="mon-adresse@email.fr" value="<?= htmlspecialchars($smtp_username) ?>">
43 <input type="hidden" id="smtp_username_hidden_<?= $node->getNodeData()->getId() ?>" value="">
44 <button onclick="setEmailParam('smtp_username', <?= $node->getNodeData()->getId() ?>)">Valider</button>
45 </p>
46 <p>
47 <label for="smtp_password_<?= $node->getNodeData()->getId() ?>">Mot de passe</label>
48 <input id="smtp_password_<?= $node->getNodeData()->getId() ?>" type="password" name="smtp_password" autocomplete="new-password">
49 <input type="hidden" id="smtp_password_hidden_<?= $node->getNodeData()->getId() ?>" value="">
50 <button onclick="setEmailParam('smtp_password', <?= $node->getNodeData()->getId() ?>)">Valider</button>
51 </p>
52 <p><i>Il s'agit du service qui acheminera les messages envoyés par ce formulaire.<br>
53 Site web => serveur d'envoi => serveur de réception => votre application "mail"</i></p>
54 <p><i>
55 Ce formulaire utilise les paramètres dans le fichier config.ini sur le serveur à mois que vous n'en renseignez d'autres ci-dessus. Les adresses d'envoi et de réception peuvent être identiques.
56 </i></p>
57 <p><i>Le site web peut ne pas réussir à se connecter à certains fournisseurs.</i></p>
58</div>
59<div class="admin_form">
60 <h3>Paramètres de réception</h3>
61 <p>
62 <label for="email_dest_<?= $node->getNodeData()->getId() ?>">Adresse e-mail</label>
63 <input id="email_dest_<?= $node->getNodeData()->getId() ?>" type="email" name="email_dest" placeholder="mon-adresse@email.fr" value="<?= htmlspecialchars($email_dest) ?>">
64 <input type="hidden" id="email_dest_hidden_<?= $node->getNodeData()->getId() ?>" value="">
65 <button onclick="setEmailParam('email_dest', <?= $node->getNodeData()->getId() ?>)">Valider</button>
66 </p>
67</div>
68<div class="admin_form">
69 <p><button onclick="sendTestEmail(<?= $node->getNodeData()->getId() ?>)">Envoi d'un e-mail de test</button></p>
70 <p><i>Vérifie la connexion au serveur d'envoi. Pour tester la réception, consultez vos e-mails à l'adresse de réception.</i></p>
71 <p class="test_email_success_<?= $node->getNodeData()->getId() ?> full_width_column"></p>
72</div> \ No newline at end of file
diff --git a/src/view/templates/galery.php b/src/view/templates/galery.php
deleted file mode 100644
index 97f8cd8..0000000
--- a/src/view/templates/galery.php
+++ /dev/null
@@ -1,12 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="galery" id="<?= $this->id_node ?>">
3 <h3><?= $title ?></h3>
4<?= $new_article ?>
5 <script>
6 var clone<?= $this->id_node ?> = document.currentScript.previousElementSibling.cloneNode(true);
7 </script>
8 <div class="galery_photos">
9<?= $content ?>
10 </div>
11 <script>enableGaleryScroller();</script>
12</section> \ No newline at end of file
diff --git a/src/view/templates/head.php b/src/view/templates/head.php
deleted file mode 100644
index bce0a19..0000000
--- a/src/view/templates/head.php
+++ /dev/null
@@ -1,13 +0,0 @@
1<?php declare(strict_types=1); ?>
2<!DOCTYPE html>
3<html lang="fr">
4 <head>
5 <script>window.Config = {page: "<?= CURRENT_PAGE ?>", favicon: "<?= $favicon ?>"};</script>
6 <meta charset="utf-8">
7 <title><?= $title ?></title>
8 <link rel="icon" type="<?= $favicon_type ?>" href="<?= $favicon ?>">
9 <meta name="description" content="<?= $description ?>">
10 <meta name="viewport" content="width=device-width">
11 <?= $css ?>
12 <?= $js ?>
13 </head> \ No newline at end of file
diff --git a/src/view/templates/header.php b/src/view/templates/header.php
deleted file mode 100644
index 400c7c0..0000000
--- a/src/view/templates/header.php
+++ /dev/null
@@ -1,55 +0,0 @@
1<?php declare(strict_types=1); ?>
2<body>
3 <div>
4 <header style="background-image: url('<?= $header_background ?? '' ?>');">
5 <div id="nav_zone">
6 <div class="nav_button">
7 <button onclick="toggleTouchMenu()">MENU</button>
8 </div>
9 <?= $nav ?>
10 </div>
11 <div class="header_above">
12 <div id="head_favicon">
13 <?= $admin_head_favicon ?>
14 </div>
15 <div id="header_background">
16 <?= $admin_header_background ?>
17 </div>
18 </div>
19 <div class="header_content">
20 <div class="header_left_col">
21 <div id="header_logo">
22 <a href="<?= new URL ?>"><img id="header_logo_content" src="<?= $header_logo ?? '' ?>" alt=""></a>
23 <?= $admin_header_logo ?>
24 </div>
25 </div>
26 <div class="header_center_col">
27 <h1 id="header_title">
28 <a id="header_title_content" href="<?= new URL ?>"><?= htmlspecialchars($title ?? '') ?></a>
29 <?= $admin_header_title ?>
30 </h1>
31 <h2 id="header_description">
32 <span id="header_description_content"><?= htmlspecialchars($description ?? '') ?></span>
33 <?= $admin_header_description ?>
34 </h2>
35 </div>
36 <div class="header_right_col">
37 <div id="header_social">
38 <div id="header_social_content">
39 <?= $social_networks ?>
40 </div>
41 </div>
42 <?= $breadcrumb ?? '' ?>
43 </div>
44 </div>
45<?php if(IS_ADMIN){ ?>
46 <script>
47 document.getElementById("head_favicon_content").src = window.Config.favicon;
48 let head_favicon = new InputFileFavicon('head_favicon');
49 let header_background = new InputFileHeaderBackground('header_background');
50 let header_logo = new InputFile('header_logo');
51 let header_title = new InputText('header_title');
52 let header_description = new InputText('header_description');
53 </script>
54<?php } ?>
55 </header> \ No newline at end of file
diff --git a/src/view/templates/login.php b/src/view/templates/login.php
deleted file mode 100644
index 3257690..0000000
--- a/src/view/templates/login.php
+++ /dev/null
@@ -1,31 +0,0 @@
1<?php declare(strict_types=1); ?>
2 <section>
3 <h3>Connexion à l'espace d'administration</h3>
4 <div class="basic_div">
5 <p>Veuillez saisir votre identifiant et votre mot de passe.</p>
6 <p style="color: red; font-style: italic;"><?= $error ?></p>
7 <form method="post" action="<?= $link_form ?>" >
8 <p><label for="login" >Identifiant:</label>
9 <input id="login" type="text" name="login" autofocus required></p>
10 <p><label for="password" >Mot de passe:</label>
11 <input id="password" type="password" name="password" required></p>
12 <input type="hidden" name="connection_hidden">
13
14 <p>Montrez que vous n'êtes pas un robot.<br>
15 <label for="captcha" >Combien font <?= $captcha->getA() ?> fois <?= $captcha->getB() ?>?</label>
16 <input required type="text" id="captcha" name="captcha" autocomplete="off" size="1">
17 </p>
18
19 <input type="submit" value="Valider">
20 </form>
21 <p>Au fait? Vous n'utilisez pas votre propre ordinateur ou téléphone?<br/>
22 Utilisez la navigation privée.</p>
23 </div>
24 </section>
25 <section>
26 <div class="basic_div">
27 <a href="<?= $link_exit ?>" >
28 <button>Retour au site</button>
29 </a>
30 </div>
31 </section> \ No newline at end of file
diff --git a/src/view/templates/maintenance.php b/src/view/templates/maintenance.php
deleted file mode 100644
index 61d5146..0000000
--- a/src/view/templates/maintenance.php
+++ /dev/null
@@ -1,62 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="maintenance">
3 <h3>Maintenance du site</h3>
4 <div class="basic_div">
5 <p>
6 <button onclick="displayLogs()">Consulter les journaux de connexion</button><br>
7 <i>Qui a essayé de se connecter, quand et a-t'il réussi?</i>
8 </p>
9 <div id="log_table"></div>
10 <p><button onclick="cleanLogs()">Effacer les journaux de connexion</button></p>
11
12 </div>
13 <div class="basic_div">
14 <p>
15 <a href="<?= new URL(['page' => 'emails']) ?>"><button>Consulter les emails</button></a>
16 <i>reçus depuis tous les formulaires de contact</i>
17 </p>
18 </div>
19 <div class="basic_div">
20 <p>
21 <a href="<?= new URL(['action' => 'get_mysqldump']) ?>">
22 <button id="get_mysqldump" onclick="preventClickSpam(this)">Télécharger une sauvegarde de la base de données</button>
23 </a><br>
24 <i>Obtenir un fichier SQL à conserver. Une sauvegarde (désignée par "auto") est réalisée à chaque connexion au mode admin.</i>
25 </p>
26 </div>
27 <div class="basic_div">
28 <p>Restaurer la base de données à partir d'un fichier SQL.<br>
29 <i>Attention, l'actuelle BDD sera écrasée! (à l'exception de la table <?= TABLE_PREFIX ?>user)</i>
30 </p>
31 <form action="<?= new URL(['from' => 'maintenance', 'action' => 'restore_database']) ?>" method="post">
32 <label for="selected_sql">Utiliser une sauvegarde conservée sur le serveur:</label><br>
33 <select id="selected_sql" name="selected_sql">
34 <?= $backup_options ?>
35 </select>
36 <input type="hidden" name="hidden" value="">
37 <input type="submit" value="Valider" onclick="return confirm('Voulez-vous vraiment restaurer la base de données? Toutes les données seront supprimées et remplacées par les nouvelles.')">
38 </form>
39 <form enctype="multipart/form-data" action="<?= new URL(['from' => 'maintenance', 'action' => 'restore_database']) ?>" method="post">
40 <label for="uploaded_sql">Utiliser un fichier sur votre ordinateur:</label><br>
41 <input id="uploaded_sql" type="file" accept=".sql" name="uploaded_sql">
42 <input type="hidden" name="hidden" value="">
43 <input type="submit" value="Valider" onclick="return confirm('Voulez-vous vraiment restaurer la base de données? Toutes les données seront supprimées et remplacées par les nouvelles.')">
44 </form>
45 </div>
46 <div class="basic_div">
47 <p>
48 <a href="<?= new URL(['from' => 'maintenance', 'action' => 'get_all_media']) ?>">
49 <button id="get_all_media" onclick="preventClickSpam(this)">Récupérer l'ensemble des fichiers mutimedia</button>
50 </a><br>
51 <i>Toutes vos photos et vos documents dans un .zip</i>
52 </p>
53 <p>
54 <button onclick="openExplorer()" style="color: grey">Explorateur de fichiers</button><br>
55 <i>Gérer les fichiers multimedia, fonction actuellement indisponible</i>
56 </p>
57 </div>
58
59 <div class="basic_div">
60 <a href="http://nageurs.localhost/index.php"><button>Retour au site</button></a>
61 </div>
62</section> \ No newline at end of file
diff --git a/src/view/templates/menu.php b/src/view/templates/menu.php
deleted file mode 100644
index ccfb518..0000000
--- a/src/view/templates/menu.php
+++ /dev/null
@@ -1,48 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="menu">
3 <h3>Menu et chemins</h3>
4 <div class="new_page_button">
5 <p>Créer une <a href="<?= new URL(['page' => 'new_page']) ?>"><button style="color: #ff1d04;">Nouvelle page</button></a>.</p>
6 </div>
7 <div class="url_form_zone">
8 <p>Créer une entrée dans le menu avec une adresse vers un site quelconque:</p>
9 <form method="post" action="<?= new URL(['from' => 'menu_paths']) ?>">
10 <p>
11 <label for="label_input">Nom dans le menu:</label>
12 <input id="label_input" type="text" name="label_input">
13 </p>
14 <p>
15 <label for="url_input">Adresse (collez votre lien):</label>
16 <input id="url_input" type="url" name="url_input" placeholder="https://..." onchange="controlURL(this)">
17 </p>
18 <p>
19 <label>Placer le lien juste après cette entrée:</label>
20 <select id="location" name="location">
21 <?= $this->options ?>
22 </select>
23 </p>
24 <input type="submit" onclick="createUrlEntry()" value="Valider">
25 </form>
26 </div>
27 <aside>
28 <p>Modifier le menu:</p>
29 <div class="controls_explanations">
30 <p><img src="assets/arrow-left.svg"> remonter dans l'arbre</p>
31 <p><img src="assets/arrow-right.svg"> devenir une branche de l'élément précédent</p>
32 <p><img src="assets/arrow-up.svg"><img src="assets/arrow-down.svg"> déplacer la branche parmi celles de même niveau</p>
33 <p><input type="checkbox" checked> afficher/cacher</p>
34 <p><img src="assets/save.svg"> enregistrer une modification</p>
35 <p><img src="assets/delete-bin.svg"> supprimer un lien</p>
36 </div>
37 </aside>
38 <div id="menu_edit_buttons">
39<?= $this->html ?>
40 </div>
41</section>
42<section>
43 <div class="basic_div">
44 <a href="<?= new URL ?>">
45 <button>Retour au site</button>
46 </a>
47 </div>
48</section> \ No newline at end of file
diff --git a/src/view/templates/modify_block.php b/src/view/templates/modify_block.php
deleted file mode 100644
index e966ee3..0000000
--- a/src/view/templates/modify_block.php
+++ /dev/null
@@ -1,58 +0,0 @@
1<?php declare(strict_types=1); ?>
2<div class="modify_one_block" id="bloc_edit_<?= $child_node->getId() ?>">
3 <div class="block_options">
4 <label for="bloc_rename_<?= $child_node->getId() ?>">Type <b><?= App\Entity\Presentation::BLOCKS[$child_node->getName()] ?? '<i>erreur base de données</i>' ?></b>
5 </label>
6 <p>
7 <input type="text" id="bloc_rename_<?= $child_node->getId() ?>" name="bloc_rename_title" value="<?= $child_node->getNodeData()->getdata()['title'] ?>" required>
8 <button onclick="renamePageBloc(<?= $child_node->getId() ?>)">Renommer</button>
9 </p>
10 <div>
11 <p>
12 <img class="action_icon" onclick="switchBlocsPositions(<?= $child_node->getId() ?>, 'up')" src="assets/arrow-up.svg">
13 <img class="action_icon" onclick="switchBlocsPositions(<?= $child_node->getId() ?>, 'down')" src="assets/arrow-down.svg">
14 </p>
15 <form method="post" action="<?= new URL(['page' => CURRENT_PAGE]) ?>">
16 <input type="hidden" name="delete_bloc_id" value="<?= $child_node->getId() ?>">
17 <input type="hidden" name="delete_bloc_hidden">
18 <input type="submit" value="Supprimer" onclick="return confirm('Voulez-vous vraiment supprimer ce bloc?');">
19 </form>
20 </div>
21 </div>
22<?php
23if($child_node->getName() === 'news_block'){
24?>
25 <div class="news_order">
26 <label>Ordre des articles</label>
27 <select id="articles_order_select_<?= $child_node->getId() ?>" onchange="articlesOrderSelect(<?= $child_node->getId() ?>)">
28 <option value="antichrono" <?= $order === 'antichrono' ? 'selected' : '' ?>>Antichronologique</option>
29 <option value="chrono" <?= $order === 'chrono' ? 'selected' : '' ?>>Chronologique</option>
30 </select>
31 </div>
32<?php
33}
34if(App\Entity\Presentation::hasPresentation($child_node->getName())){
35?>
36 <div class="grid_options">
37 <div>
38 <label for="presentation_select_<?= $child_node->getId() ?>">Présentation</label>
39 <select id="presentation_select_<?= $child_node->getId() ?>" onchange="changePresentation(<?= $child_node->getId() ?>)">
40 <?= $this->makePresentationOptions($child_node->getNodeData()->getPresentation()) ?>
41 </select>
42 </div>
43 <div id="cols_min_width_edit_<?= $child_node->getId() ?>" class="<?= ($child_node->getNodeData()->getPresentation() === 'grid' ? '' : 'hidden') ?>">
44 <label for="cols_min_width_select_<?= $child_node->getId() ?>">Largeur minimum </label>
45 <input type="number" id="cols_min_width_select_<?= $child_node->getId() ?>" onchange="changeColsMinWidth(<?= $child_node->getId() ?>)" min="150" max="400" value="<?= $child_node->getNodeData()->getColsMinWidth() ?>"> pixels
46 </div>
47 </div>
48 <div class="pagination_limit">
49 <label for="pagination_limit_<?= $child_node->getId() ?>">
50 Nombre max d'articles affichés
51 <input type="number" id="pagination_limit_<?= $child_node->getId() ?>" name="pagination_limit" onchange="changePaginationLimit(<?= $child_node->getId() ?>)" min="0" max="30" value="<?= $child_node->getNodeData()->getPaginationLimit() ?>">
52 <i>(mettre 0 désactive la pagination)</i>
53 </label>
54 </div>
55<?php
56}
57?>
58</div> \ No newline at end of file
diff --git a/src/view/templates/modify_page.php b/src/view/templates/modify_page.php
deleted file mode 100644
index d32179c..0000000
--- a/src/view/templates/modify_page.php
+++ /dev/null
@@ -1,58 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="page_modification">
3 <h3>Modification de la page</h3>
4 <div class="edit_page_zone">
5 <div class="edit_page_title_zone">
6 <p id="edit_page_title">
7 <label for="page_name">Titre de la page</label>
8 <input type="text" id="page_name" name="edit_page_title" value="<?= Model::$page_path->getLast()->getPageName() ?>" onchange="makePageNamePath()" required>
9 <button onclick="changePageTitle('<?= Model::$page_path->getLast()->getId() ?>')">Renommer</button>
10 </p>
11 <form id="edit_page_menu_path" method="post" action="<?= new URL(['page' => CURRENT_PAGE]) ?>">
12 <label for="page_name_path">Chemin en "snake_case"</label>
13 <input type="text" id="page_name_path" name="page_menu_path" value="<?= Model::$page_path->getLast()->getEndOfPath() ?>" placeholder="ex: nouvelle_page" required>
14 <input type="hidden" name="page_id" value="<?= Model::$page_path->getLast()->getId() ?>"><input type="hidden" name="page_name_path_hidden">
15 <input type="submit" value="Modifier">
16 </form>
17 </div>
18 <div id="edit_description">
19 <label for="description_textarea">Description sous le titre dans les moteurs de recherche</label>
20 <div>
21 <textarea id="description_textarea" name="edit_description" cols="40" rows="3" placeholder="ex: nous faisons ceci et cela, etc" required><?= Model::$page_path->getLast()->getDescription(); ?></textarea>
22 <button onclick="changeDescription('<?= Model::$page_path->getLast()->getId() ?>')">Modifier</button>
23 </div>
24 </div>
25 </div>
26 <div class="delete_page_zone">
27 <form method="post" action="<?= new URL(['page' => CURRENT_PAGE]) ?>">
28 <label>Supprimer cette page</label>
29 <input type="hidden" name="page_id" value="<?= Model::$page_path->getLast()->getId() ?>">
30 <input type="hidden" name="submit_hidden">
31 <input type="submit" value="Supprimer" onclick="return confirm('Voulez-vous vraiment supprimer cette page?');">
32 </form>
33 </div>
34 <div class="edit_block_zone">
35 <div class="new_block">
36 <p>Ajouter un bloc de page</p>
37 <form method="post" action="<?= new URL(['page' => CURRENT_PAGE]) ?>">
38 <p><label for="bloc_title">Titre</label>
39 <input type="text" id="bloc_title" name="bloc_title" required></p>
40 <p><label for="bloc_select">Type</label>
41 <select id="bloc_select" name="bloc_select" required>
42 <?= $options ?>
43 </select>
44 <input type="hidden" name="bloc_title_hidden">
45 <input type="submit" value="Valider"></p>
46 </form>
47 <div class="explanations">
48 <p><b>Articles libres</b>: textes riches générés par l'éditeur et librement positionnables</p>
49 <p><b>Actualités</b>: contenus structurés avec titre, aperçu, date et possédant une page dédiée</p>
50 <!-- <p><b>Galerie</b>: photos parcourables en mode plein écran (cette fonctionalité n'est pas encore disponible)</p> -->
51 </div>
52 </div>
53 <div class="modify_block">
54 <p>Modifier un bloc</p>
55 <?= $bloc_edit ?>
56 </div>
57 </div>
58</section> \ No newline at end of file
diff --git a/src/view/templates/new.php b/src/view/templates/new.php
deleted file mode 100644
index 30b244f..0000000
--- a/src/view/templates/new.php
+++ /dev/null
@@ -1,27 +0,0 @@
1<?php declare(strict_types=1); ?>
2<article>
3 <div class="new_content <?= $overflow ?>" id="<?= $id ?>">
4 <div class="data" id="<?= $id_title ?>">
5 <?= $title ?>
6 </div>
7 <?= $title_buttons ?>
8 <div class="data new_content_text" id="<?= $id_preview ?>">
9 <?= $preview ?>
10 </div>
11 <?= $preview_buttons ?>
12 <div class="data article_content_text" id="<?= $id_content ?>">
13 <?= $content ?>
14 </div>
15 <?= $article_buttons ?>
16 <div class="under_an_article">
17 <p>
18 <img src="assets/calendar.svg">
19 <span class="data local_date" id="<?= $id_date ?>" date-utc="<?= $date ?>">Chargement...</span>
20 </p>
21 </div>
22 <?= $date_buttons ?>
23 <div class="article_admin_zone">
24 <?= $admin_buttons ?>
25 </div>
26 </div>
27</article> \ No newline at end of file
diff --git a/src/view/templates/new_page.php b/src/view/templates/new_page.php
deleted file mode 100644
index 7bf4c01..0000000
--- a/src/view/templates/new_page.php
+++ /dev/null
@@ -1,35 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="new_page">
3 <h3>Création d'une nouvelle page</h3>
4 <div class="form_zone">
5 <form method="post" action="<?= new URL ?>">
6 <p>
7 <label for="page_name">Nom de la page</label>
8 <input id="page_name" type="text" name="page_name" onchange="makePageNamePath()" required>
9 </p>
10 <p>
11 <label for="page_name_path">Chemin de l'URL</label>
12 <input id="page_name_path" type="text" name="page_name_path" placeholder="ex: nouvelle_page" required>
13 </p>
14 <p>
15 <label for="page_location">Placer la page juste après cette entrée</label>
16 <select id="page_location" name="page_location">
17 <?= $this->options ?>
18 </select>
19 </p>
20 <input type="hidden" name="new_page_hidden">
21 <p>
22 <label class="label_textarea" for="page_description">Description qui apparaît sous le titre dans les moteurs de recherche</label>
23 <textarea id="page_description" name="page_description" cols="35" rows="3" placeholder="ex: nous faisons ceci et cela, etc"></textarea>
24 </p>
25 <input type="submit" value="Créer la page">
26 </form>
27 </div>
28</section>
29<section>
30 <div class="basic_div">
31 <a href="<?= new URL ?>">
32 <button>Retour au site</button>
33 </a>
34 </div>
35</section> \ No newline at end of file
diff --git a/src/view/templates/news_block.php b/src/view/templates/news_block.php
deleted file mode 100644
index 43dbe01..0000000
--- a/src/view/templates/news_block.php
+++ /dev/null
@@ -1,14 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="<?= $section_class ?>" block-type="<?= $node->getName() ?>" id="<?= $this->id_node ?>">
3 <h3><?= $title ?></h3>
4<?= $new_article ?>
5 <script>
6 var clone<?= $this->id_node ?> = document.currentScript.previousElementSibling.cloneNode(true);
7 </script>
8 <div class="section_child" style="<?= $cols_min_width ?>">
9<?= $content ?>
10 </div>
11 <div class="fetch_articles">
12 <button class="<?= $fetch_button_hidden ?>" onclick="fetchArticles(<?= $this->id_node ?>)">Articles suivants</button>
13 </div>
14</section> \ No newline at end of file
diff --git a/src/view/templates/post.php b/src/view/templates/post.php
deleted file mode 100644
index d0d01b2..0000000
--- a/src/view/templates/post.php
+++ /dev/null
@@ -1,14 +0,0 @@
1<?php declare(strict_types=1); ?>
2<article>
3 <div class="post_content">
4 <div class="article_title_zone">
5 <?= $share_button ?>
6 </div>
7 <div id="<?= $id ?>">
8 <?= $html ?>
9 </div>
10 <div class="article_admin_zone">
11 <?= $admin_buttons ?>
12 </div>
13 </div>
14</article> \ No newline at end of file
diff --git a/src/view/templates/post_block.php b/src/view/templates/post_block.php
deleted file mode 100644
index 43dbe01..0000000
--- a/src/view/templates/post_block.php
+++ /dev/null
@@ -1,14 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="<?= $section_class ?>" block-type="<?= $node->getName() ?>" id="<?= $this->id_node ?>">
3 <h3><?= $title ?></h3>
4<?= $new_article ?>
5 <script>
6 var clone<?= $this->id_node ?> = document.currentScript.previousElementSibling.cloneNode(true);
7 </script>
8 <div class="section_child" style="<?= $cols_min_width ?>">
9<?= $content ?>
10 </div>
11 <div class="fetch_articles">
12 <button class="<?= $fetch_button_hidden ?>" onclick="fetchArticles(<?= $this->id_node ?>)">Articles suivants</button>
13 </div>
14</section> \ No newline at end of file
diff --git a/src/view/templates/show_emails.php b/src/view/templates/show_emails.php
deleted file mode 100644
index f561dc7..0000000
--- a/src/view/templates/show_emails.php
+++ /dev/null
@@ -1,30 +0,0 @@
1<?php declare(strict_types=1); ?>
2<section class="show_emails">
3 <h3>Table "<?= TABLE_PREFIX ?>email" de la base de données</h3>
4 <p><i>
5 Les e-mails ci-dessous sont des copies de ceux arrivés dans votre boite de messagerie depuis tous les formulaires existant sur le site. Ils sont conservés dans un but pratique (historique, prospection) ou dans tout autre but justifiant leur conservation. Cette conservation est optionnelle et règlementée par la loi européenne RGPD.<br>
6 <b>Notez qu'utiliser cette application ne vous garantit pas d'être en conformité avec la loi, c'est un outil vous permettant de faire les choses comme vous l'entendez. Si tout ceci vous gêne, désactivez le stockage des e-mails et supprimez ceux déjà enregistrés (ci-dessous).</b>
7 </i></p>
8 <h4>Durées de conservation</h4>
9 <p><i>
10 Ce sont des durées maximales, les données peuvent être supprimées plus tôt ou même immédiatement. Le faire est d'ailleurs une obligation dans le cas où les personnes concernées le demandent.<br>
11 </i></p>
12 <p><i>
13 Les e-mails ordinaires d'un même expéditeur (même adresse e-mail) sont tous supprimés simultanément lorsque le plus récent d'entre eux atteint les 3 ans (utilisateur est considéré inactif).<br>
14 Les e-mails sensibles quand à eux sont supprimés 5 ans après être devenus sensibles (durée juridique d'une preuve).
15 </i></p>
16 <p><i>
17 Un nettoyeur supprimant les messages dépassant ces durées est exécuté au moment de votre connexion au mode administrateur.<br>
18 Si vos connexions sont rares, il est possible d'automatiser ce nettoyage à l'aide d'une tâche CRON. Pour cela, vous devez configurer le serveur pour qu'il exécute periodiquement la commande "php /chemin/du/site/bin/cron.php".
19 </i></p>
20 <h4>Données sensibles</h4>
21 <p><i>
22 Un e-mail peut-être considéré comme "sensible". Vous pouvez rendre un e-mail sensible lorsqu'il possède une valeur de preuve dans le cas d'un litige.<br>
23 Lorsqu'une personne demande la suppression de ses données personnelles du serveur, les e-mails sensibles peuvent être conservés, vous aurez noté que la durée de conservation est calculée différement.
24 </i></p>
25 <p><i>
26 Les spams ne sont pas sensibles, c'est juste de la pollution, supprimez-les!
27 </i></p>
28
29 <?= $emails ?>
30</section> \ No newline at end of file
diff --git a/src/view/templates/user_create.php b/src/view/templates/user_create.php
deleted file mode 100644
index 870b904..0000000
--- a/src/view/templates/user_create.php
+++ /dev/null
@@ -1,53 +0,0 @@
1<?php
2declare(strict_types=1);
3
4$error_messages = [
5 'error_non_valid_captcha' => 'Erreur au test anti-robot, veuillez saisir un nombre entier.',
6 'captcha_server_error' => 'captcha_server_error',
7 'bad_solution_captcha' => 'Erreur au test anti-robot, veuillez réessayer.',
8 'bad_login_or_password' => 'Mauvais identifiant ou mot de passe, veuillez réessayer.',
9 'different_passwords' => 'Les deux mots de passe saisis sont différents',
10 'forbidden_characters' => 'Caractères interdits: espaces, tabulations, sauts CR/LF.'
11];
12$error = isset($_GET['error']) ? $error_messages[$_GET['error']] : '';
13
14$captcha = new Captcha;
15$_SESSION['captcha'] = $captcha->getSolution(); // enregistrement de la réponse du captcha pour vérification
16?>
17<!DOCTYPE html>
18<html lang="fr">
19 <head>
20 <meta charset="utf-8">
21 <title>Bienvenue</title>
22 <link rel="icon" type="image/png" href="assets/favicon48x48.png">
23 <link rel="stylesheet" href="css/body.css">
24 <meta name="viewport" content="width=device-width, initial-scale=1.0">
25 </head>
26 <body>
27 <main>
28 <section>
29 <h3>Bienvenue.</h3>
30 <p style="text-align: center;">Pour commencer, veuillez créer un compte administrateur.</p>
31 <div class="basic_div">
32 <p style="color: red; font-style: italic;"><?= $error ?></p>
33 <form method="post" action="index.php?action=create_user" >
34 <p><label for="login" >Identifiant:</label>
35 <input id="login" type="text" name="login" autofocus required></p>
36 <p><label for="password" >Mot de passe:</label>
37 <input id="password" type="password" name="password" required></p>
38 <p><label for="password_confirmation" >Confirmer le mot de passe:</label>
39 <input id="password_confirmation" type="password" name="password_confirmation" required></p>
40 <input type="hidden" name="create_user_hidden">
41
42 <p>Montrez que vous n'êtes pas un robot.<br>
43 <label for="captcha" >Combien font <?= $captcha->getA() ?> fois <?= $captcha->getB() ?>?</label>
44 <input required type="text" id="captcha" name="captcha" autocomplete="off" size="1">
45 </p>
46
47 <input type="submit" value="Valider">
48 </form>
49 </div>
50 </section>
51 </main>
52 </body>
53</html> \ No newline at end of file
diff --git a/src/view/templates/user_edit.php b/src/view/templates/user_edit.php
deleted file mode 100644
index 9191e7d..0000000
--- a/src/view/templates/user_edit.php
+++ /dev/null
@@ -1,63 +0,0 @@
1<?php declare(strict_types=1); ?>
2 <section>
3 <div class="user_edit_header">
4 <div class="empty_column"></div>
5 <h3>Mon compte</h3>
6 <div>
7 <img class="user_icon" src="assets/user_hollow.svg">
8 <div><?= $_SESSION['user']['username'] ?></div>
9 </div>
10 </div>
11 <div class="user_edit_flex">
12 <div class="basic_div">
13 <p>Modifier mon <b>nom d'utilisateur</b>.</p>
14 <p style="color: red; font-style: italic;"><?= $error_username ?></p>
15 <p style="color: green; font-style: italic;"><?= $success_username ?></p>
16 <form method="post" action="<?= $link_user_form ?>" >
17 <p><label for="login" >Ancien nom:</label>
18 <input id="login" type="text" name="login" required></p>
19 <p><label for="password" >Mot de passe:</label>
20 <input id="password" type="password" name="password" required ></p>
21 <p><label for="new_login" >Nouveau nom:</label>
22 <input id="new_login" type="text" name="new_login" required></p>
23 <input type="hidden" name="modify_username_hidden">
24
25 <p>Montrez que vous n'êtes pas un robot.<br>
26 <label for="captcha" >Combien font <?= $captcha->getA() ?> fois <?= $captcha->getB() ?>?</label>
27 <input required type="text" id="captcha" name="captcha" autocomplete="off" size="1">
28 </p>
29
30 <input type="submit" value="Valider">
31 </form>
32 </div>
33 <div class="basic_div">
34 <p>Modifier mon <b>mot de passe</b>.</p>
35 <p style="color: red; font-style: italic;"><?= $error_password ?></p>
36 <p style="color: green; font-style: italic;"><?= $success_password ?></p>
37 <form method="post" action="<?= $link_password_form ?>" >
38 <p><label for="login" >Nom:</label>
39 <input id="login" type="text" name="login" required></p>
40 <p><label for="password" >Ancien mot de passe:</label>
41 <input id="password" type="password" name="password" required ></p>
42 <p><label for="new_password" >Nouveau mot de passe:</label>
43 <input id="new_password" type="password" name="new_password" required autocomplete="off"></p>
44 <input type="hidden" name="modify_password_hidden">
45
46 <p>Montrez que vous n'êtes pas un robot.<br>
47 <label for="captcha" >Combien font <?= $captcha->getA() ?> fois <?= $captcha->getB() ?>?</label>
48 <input required type="text" id="captcha" name="captcha" autocomplete="off" size="1">
49 </p>
50
51 <input type="submit" value="Valider">
52 </form>
53 </div>
54 </div>
55 </section>
56 <section>
57 <div class="basic_div">
58 <a href="<?= $link_exit ?>">
59 <button>Retour au site</button></a>
60 <a href="<?= $link_logout ?>">
61 <button>Déconnexion</button></a>
62 </div>
63 </section> \ No newline at end of file