aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpolo <ordipolo@gmx.fr>2025-10-23 14:15:01 +0200
committerpolo <ordipolo@gmx.fr>2025-10-23 14:15:01 +0200
commitf9a9bf1ae4fac9317fd71f0bcb207544b000e6f1 (patch)
treea0ca5cdc0374529b80951d7d126f1c49442bae68
parentdb5521f7b194190013dd0ad606a018ab68464562 (diff)
downloadcms-f9a9bf1ae4fac9317fd71f0bcb207544b000e6f1.zip
téléchargement d'assets dans head, header et footer
-rw-r--r--public/css/body.css5
-rw-r--r--public/css/foot.css15
-rw-r--r--public/css/head.css17
-rw-r--r--public/js/InputFile.js51
-rw-r--r--public/js/InputText.js15
-rw-r--r--public/user_data/assets/favicon48x48.png (renamed from public/assets/favicon48x48.png)bin3067 -> 3067 bytes
-rw-r--r--public/user_data/assets/fond-piscine.jpg (renamed from public/assets/fond-piscine.jpg)bin24757 -> 24757 bytes
-rw-r--r--public/user_data/assets/logo-120x75.jpg (renamed from public/assets/logo-120x75.jpg)bin12240 -> 12240 bytes
-rw-r--r--public/user_data/assets/logo-150x94.jpg (renamed from public/assets/logo-150x94.jpg)bin13506 -> 13506 bytes
-rw-r--r--public/user_data/assets/logo-nb-et-ffn.png (renamed from public/assets/logo-nb-et-ffn.png)bin61236 -> 61236 bytes
-rw-r--r--public/user_data/assets/logo2.jpg (renamed from public/assets/logo2.jpg)bin36899 -> 36899 bytes
-rw-r--r--src/controller/HeadFootController.php60
-rw-r--r--src/controller/ImageUploadController.php22
-rw-r--r--src/model/entities/Article.php5
-rw-r--r--src/model/entities/Asset.php3
-rw-r--r--src/model/entities/NodeData.php21
-rw-r--r--src/router.php15
-rw-r--r--src/view/AbstractBuilder.php2
-rw-r--r--src/view/FooterBuilder.php23
-rw-r--r--src/view/HeadBuilder.php4
-rw-r--r--src/view/HeaderBuilder.php42
-rw-r--r--src/view/templates/footer.php17
-rw-r--r--src/view/templates/header.php34
23 files changed, 252 insertions, 99 deletions
diff --git a/public/css/body.css b/public/css/body.css
index 8ad3a52..cadf955 100644
--- a/public/css/body.css
+++ b/public/css/body.css
@@ -43,7 +43,7 @@ main
43 margin: auto; 43 margin: auto;
44 /*max-width: 1200px;*/ 44 /*max-width: 1200px;*/
45 background-color: #E3F3FF; 45 background-color: #E3F3FF;
46 padding: 15px 0; 46 padding: 15px;
47} 47}
48.hidden 48.hidden
49{ 49{
@@ -51,8 +51,7 @@ main
51} 51}
52section 52section
53{ 53{
54 margin: 10px 0; 54 margin-top: 20px;
55 padding: 15px;
56} 55}
57section > h3 56section > h3
58{ 57{
diff --git a/public/css/foot.css b/public/css/foot.css
index 8d0a94d..8a0d08d 100644
--- a/public/css/foot.css
+++ b/public/css/foot.css
@@ -14,11 +14,12 @@ footer > div
14 max-width: 1200px; 14 max-width: 1200px;
15 display: flex; 15 display: flex;
16 justify-content: space-around; 16 justify-content: space-around;
17 flex-wrap: wrap;
17} 18}
18 19
19.contact 20footer .data > div
20{ 21{
21 margin: 16px 0; /* alignement avec la <p> autour du logo */ 22 margin: 20px 10px;
22} 23}
23.contact a 24.contact a
24{ 25{
@@ -73,7 +74,7 @@ footer > div
73 margin: 0 3px; 74 margin: 0 3px;
74}*/ 75}*/
75 76
76.footer_logo img 77#footer_logo_img
77{ 78{
78 max-width: 288px; 79 max-width: 288px;
79 min-width: 150px; 80 min-width: 150px;
@@ -85,14 +86,6 @@ footer > div
85 width: 70px; 86 width: 70px;
86 } 87 }
87} 88}
88@media screen and (max-width: 800px)
89{
90 footer > div
91 {
92 flex-direction: column;
93 align-items: center;
94 }
95}
96 89
97.breadcrumb a 90.breadcrumb a
98{ 91{
diff --git a/public/css/head.css b/public/css/head.css
index b173077..4206044 100644
--- a/public/css/head.css
+++ b/public/css/head.css
@@ -17,7 +17,12 @@ header
17 display: none; 17 display: none;
18} 18}
19 19
20.header-content 20.background_button
21{
22 max-height: 24px;
23}
24
25.header_content
21{ 26{
22 padding: 20px 0; 27 padding: 20px 0;
23 display: grid; 28 display: grid;
@@ -27,12 +32,12 @@ header
27/*.header_left_col 32/*.header_left_col
28{}*/ 33{}*/
29@media screen and (max-width: 1000px){ 34@media screen and (max-width: 1000px){
30 .header-content{ 35 .header_content{
31 padding: 18px 0; 36 padding: 18px 0;
32 } 37 }
33} 38}
34@media screen and (max-width: 450px){ 39@media screen and (max-width: 450px){
35 .header-content 40 .header_content
36 { 41 {
37 /*grid-template-columns: 1fr 2fr 1fr;*/ 42 /*grid-template-columns: 1fr 2fr 1fr;*/
38 display: block; 43 display: block;
@@ -69,16 +74,16 @@ header img
69{ 74{
70 vertical-align: bottom; /* supprime espace sous l'image */ 75 vertical-align: bottom; /* supprime espace sous l'image */
71 max-width: 150px; 76 max-width: 150px;
72 max-height: 75px; 77 max-height: 100px;
73} 78}
74header a 79header a
75{ 80{
76 color: unset; /* ne plus hériter */ 81 color: unset; /* ne plus hériter */
77 text-decoration: none; 82 text-decoration: none;
78} 83}
79#edit_favicon_zone 84.editing_zone > div
80{ 85{
81 margin-bottom: 10px; 86 /*display: inline;*/ /* à l'ancienne */
82} 87}
83/*.header_right_col 88/*.header_right_col
84{}*/ 89{}*/
diff --git a/public/js/InputFile.js b/public/js/InputFile.js
new file mode 100644
index 0000000..f5e450c
--- /dev/null
+++ b/public/js/InputFile.js
@@ -0,0 +1,51 @@
1// étendre une classe parente avec InputFile?
2class InputFile{
3 constructor(name){
4 this.name = name;
5 this.parent = document.getElementById(name);
6 }
7 open(){
8 this.parent.querySelector('#' + this.name + '_img').classList.add('hidden');
9 this.parent.querySelector('#' + this.name + '_input').classList.remove('hidden');
10 this.parent.querySelector('#' + this.name + '_open').classList.add('hidden');
11 this.parent.querySelector('#' + this.name + '_submit').classList.remove('hidden');
12 this.parent.querySelector('#' + this.name + '_cancel').classList.remove('hidden');
13 }
14 close(){
15 this.parent.querySelector('#' + this.name + '_img').classList.remove('hidden');
16 this.parent.querySelector('#' + this.name + '_input').classList.add('hidden');
17 this.parent.querySelector('#' + this.name + '_open').classList.remove('hidden');
18 this.parent.querySelector('#' + this.name + '_submit').classList.add('hidden');
19 this.parent.querySelector('#' + this.name + '_cancel').classList.add('hidden');
20 }
21 submit(){
22 const file = this.parent.querySelector('#' + this.name + '_input').files[0];
23 if(!file){
24 console.error("Erreur: aucun fichier sélectionné.");
25 return;
26 }
27 const form_data = new FormData();
28 form_data.append('file', file);
29
30 fetch('index.php?head_foot_image=' + this.name, {
31 method: 'POST', // apparemment il faudrait utiliser PUT
32 body: form_data
33 })
34 .then(response => response.json())
35 .then(data => {
36 if(data.success){
37 this.parent.querySelector('#' + this.name + '_img').src = data.location;
38 this.close(this.name);
39 }
40 else{
41 console.error("Erreur: le serveur n'a pas enregistré l'image'.");
42 }
43 })
44 .catch(error => {
45 console.error('Erreur:', error);
46 });
47 }
48 cancel(){
49 this.close(this.name);
50 }
51} \ No newline at end of file
diff --git a/public/js/InputText.js b/public/js/InputText.js
index ba7e8e4..33dcf8d 100644
--- a/public/js/InputText.js
+++ b/public/js/InputText.js
@@ -1,27 +1,28 @@
1// s'en servir dans menu et chemin 1// s'en servir dans menu et chemin
2// étendre un classe parente avec InputText?
2class InputText{ 3class InputText{
3 constructor(name){ 4 constructor(name){
4 this.name = name; 5 this.name = name;
5 this.parent = document.getElementById(name); 6 this.parent = document.getElementById(name);
6 } 7 }
7 openTextInput(){ 8 open(){
8 this.parent.querySelector('#' + this.name + '_span').classList.add('hidden'); 9 this.parent.querySelector('#' + this.name + '_span').classList.add('hidden');
9 this.parent.querySelector('#' + this.name + '_input').classList.remove('hidden'); 10 this.parent.querySelector('#' + this.name + '_input').classList.remove('hidden');
10 this.parent.querySelector('#' + this.name + '_open').classList.add('hidden'); 11 this.parent.querySelector('#' + this.name + '_open').classList.add('hidden');
11 this.parent.querySelector('#' + this.name + '_submit').classList.remove('hidden'); 12 this.parent.querySelector('#' + this.name + '_submit').classList.remove('hidden');
12 this.parent.querySelector('#' + this.name + '_cancel').classList.remove('hidden'); 13 this.parent.querySelector('#' + this.name + '_cancel').classList.remove('hidden');
13 } 14 }
14 closeTextInput(){ 15 close(){
15 this.parent.querySelector('#' + this.name + '_span').classList.remove('hidden'); 16 this.parent.querySelector('#' + this.name + '_span').classList.remove('hidden');
16 this.parent.querySelector('#' + this.name + '_input').classList.add('hidden'); 17 this.parent.querySelector('#' + this.name + '_input').classList.add('hidden');
17 this.parent.querySelector('#' + this.name + '_open').classList.remove('hidden'); 18 this.parent.querySelector('#' + this.name + '_open').classList.remove('hidden');
18 this.parent.querySelector('#' + this.name + '_submit').classList.add('hidden'); 19 this.parent.querySelector('#' + this.name + '_submit').classList.add('hidden');
19 this.parent.querySelector('#' + this.name + '_cancel').classList.add('hidden'); 20 this.parent.querySelector('#' + this.name + '_cancel').classList.add('hidden');
20 } 21 }
21 submitTextInput(){ 22 submit(){
22 const new_text = this.parent.querySelector('#' + this.name + '_input').value; 23 const new_text = this.parent.querySelector('#' + this.name + '_input').value;
23 24
24 fetch('index.php?entire_site_edit=' + this.name, { 25 fetch('index.php?head_foot_text=' + this.name, {
25 method: 'POST', 26 method: 'POST',
26 headers: { 'Content-Type': 'application/json' }, 27 headers: { 'Content-Type': 'application/json' },
27 body: JSON.stringify({new_text: new_text}) 28 body: JSON.stringify({new_text: new_text})
@@ -30,7 +31,7 @@ class InputText{
30 .then(data => { 31 .then(data => {
31 if(data.success){ 32 if(data.success){
32 this.parent.querySelector('#' + this.name + '_span').innerHTML = new_text; 33 this.parent.querySelector('#' + this.name + '_span').innerHTML = new_text;
33 this.closeTextInput(this.name); 34 this.close(this.name);
34 } 35 }
35 else{ 36 else{
36 console.error("Erreur: le serveur n'a pas enregistré le nouveau texte."); 37 console.error("Erreur: le serveur n'a pas enregistré le nouveau texte.");
@@ -40,8 +41,8 @@ class InputText{
40 console.error('Erreur:', error); 41 console.error('Erreur:', error);
41 }); 42 });
42 } 43 }
43 cancelTextInput(){ 44 cancel(){
44 this.parent.querySelector('#' + this.name + '_input').value = this.parent.querySelector('#' + this.name + '_span').innerHTML; 45 this.parent.querySelector('#' + this.name + '_input').value = this.parent.querySelector('#' + this.name + '_span').innerHTML;
45 this.closeTextInput(this.name); 46 this.close(this.name);
46 } 47 }
47} \ No newline at end of file 48} \ No newline at end of file
diff --git a/public/assets/favicon48x48.png b/public/user_data/assets/favicon48x48.png
index 9825db1..9825db1 100644
--- a/public/assets/favicon48x48.png
+++ b/public/user_data/assets/favicon48x48.png
Binary files differ
diff --git a/public/assets/fond-piscine.jpg b/public/user_data/assets/fond-piscine.jpg
index 239d95d..239d95d 100644
--- a/public/assets/fond-piscine.jpg
+++ b/public/user_data/assets/fond-piscine.jpg
Binary files differ
diff --git a/public/assets/logo-120x75.jpg b/public/user_data/assets/logo-120x75.jpg
index b58a7a6..b58a7a6 100644
--- a/public/assets/logo-120x75.jpg
+++ b/public/user_data/assets/logo-120x75.jpg
Binary files differ
diff --git a/public/assets/logo-150x94.jpg b/public/user_data/assets/logo-150x94.jpg
index 67ec6cc..67ec6cc 100644
--- a/public/assets/logo-150x94.jpg
+++ b/public/user_data/assets/logo-150x94.jpg
Binary files differ
diff --git a/public/assets/logo-nb-et-ffn.png b/public/user_data/assets/logo-nb-et-ffn.png
index f51ac9c..f51ac9c 100644
--- a/public/assets/logo-nb-et-ffn.png
+++ b/public/user_data/assets/logo-nb-et-ffn.png
Binary files differ
diff --git a/public/assets/logo2.jpg b/public/user_data/assets/logo2.jpg
index 39c03bd..39c03bd 100644
--- a/public/assets/logo2.jpg
+++ b/public/user_data/assets/logo2.jpg
Binary files differ
diff --git a/src/controller/HeadFootController.php b/src/controller/HeadFootController.php
index 8d59d10..0429aac 100644
--- a/src/controller/HeadFootController.php
+++ b/src/controller/HeadFootController.php
@@ -5,23 +5,24 @@ declare(strict_types=1);
5 5
6//use App\Entity\Node; 6//use App\Entity\Node;
7//use App\Entity\NodeData; 7//use App\Entity\NodeData;
8//use App\Entity\Image; 8use App\Entity\Asset;
9//use Doctrine\Common\Collections\ArrayCollection; 9use Doctrine\Common\Collections\ArrayCollection;
10use Doctrine\ORM\EntityManager; 10use Doctrine\ORM\EntityManager;
11 11
12class HeadFootController 12class HeadFootController
13{ 13{
14 static public function setTextData(EntityManager $entityManager, array $request_params, array $json): void 14 static public function setTextData(EntityManager $entityManager, string $request_params, array $json): void
15 { 15 {
16 if(count($request_params) !== 2){ 16 $params_array = explode('_', $request_params); // header_title, header_description, footer_name, footer_address, footer_email
17 if(count($params_array) !== 2){
17 echo json_encode(['success' => false]); 18 echo json_encode(['success' => false]);
18 die; 19 die;
19 } 20 }
20 21
21 $model = new Model($entityManager); 22 $model = new Model($entityManager);
22 if($model->findWhateverNode('name_node', $request_params[0])){ 23 if($model->findWhateverNode('name_node', $params_array[0])){
23 $node_data = $model->getNode()->getNodeData(); 24 $node_data = $model->getNode()->getNodeData();
24 $node_data->updateData($request_params[1], $json['new_text']); // $request_params[1] n'est pas contrôlé 25 $node_data->updateData($params_array[1], $json['new_text']); // $params_array[1] n'est pas contrôlé
25 $entityManager->flush(); 26 $entityManager->flush();
26 echo json_encode(['success' => true]); 27 echo json_encode(['success' => true]);
27 } 28 }
@@ -30,4 +31,51 @@ class HeadFootController
30 } 31 }
31 die; 32 die;
32 } 33 }
34 static public function uploadAsset(EntityManager $entityManager, string $request_params): void
35 {
36 if(empty($_FILES)){
37 http_response_code(400);
38 echo json_encode(['success' => false]);
39 }
40 else{
41 $file = $_FILES['file'];
42
43 if(!is_dir(Asset::USER_PATH)){
44 mkdir(Asset::USER_PATH, 0700, true);
45 }
46
47 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif', 'ico', 'bmp']; // pas de SVG
48 $name = Security::secureFileName(pathinfo($file['name'], PATHINFO_FILENAME));
49 $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
50 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
51 $extension = 'jpeg';
52 }
53 $file_path = uniqid($name . '_') . '.' . $extension;
54
55 if(ImageUploadController::imagickCleanImage(file_get_contents($file['tmp_name']), Asset::USER_PATH . $file_path, $extension)){ // recréer l’image pour la nettoyer
56 $params_array = explode('_', $request_params); // favicon, header_logo, header_background, footer_logo
57
58 $model = new Model($entityManager);
59 if($model->findWhateverNode('name_node', $params_array[0])){
60 $node_data = $model->getNode()->getNodeData();
61 $image = new Asset($name, $file_path, mime_content_type($file['tmp_name']), $request_params);
62 $node_data->addAsset($image);
63
64 $entityManager->persist($image);
65 $entityManager->flush();
66 echo json_encode(['success' => true, 'location' => Asset::USER_PATH . $file_path]);
67 }
68 else{
69 echo json_encode(['success' => false, 'message' => 'Erreur noeud non trouvé.']);
70 }
71 }
72 else{
73 http_response_code(500);
74 echo json_encode(['success' => false, 'message' => 'Erreur image non valide.']);
75 }
76 }
77 die;
78 }
79
80 //static public function uploadImage(EntityManager $entityManager, array $request_params): void
33} \ No newline at end of file 81} \ No newline at end of file
diff --git a/src/controller/ImageUploadController.php b/src/controller/ImageUploadController.php
index 29b8059..5e80ba5 100644
--- a/src/controller/ImageUploadController.php
+++ b/src/controller/ImageUploadController.php
@@ -61,14 +61,14 @@ class ImageUploadController
61 { 61 {
62 if(isset($_FILES['file'])){ 62 if(isset($_FILES['file'])){
63 $file = $_FILES['file']; 63 $file = $_FILES['file'];
64 $dest = 'images/'; 64 $dest = 'user_data/images/';
65 $dest_mini = 'images-mini/'; 65 $dest_mini = 'user_data/images-mini/';
66 66
67 // Vérifier si les répertoires existent, sinon les créer 67 // Vérifier si les répertoires existent, sinon les créer
68 if(!is_dir($dest)) { 68 if(!is_dir($dest)){
69 mkdir($dest, 0700, true); 69 mkdir($dest, 0700, true);
70 } 70 }
71 if(!is_dir($dest_mini)) { 71 if(!is_dir($dest_mini)){
72 mkdir($dest_mini, 0700, true); 72 mkdir($dest_mini, 0700, true);
73 } 73 }
74 74
@@ -78,7 +78,7 @@ class ImageUploadController
78 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ 78 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
79 $extension = 'jpeg'; 79 $extension = 'jpeg';
80 } 80 }
81 $file_path = $dest . $name . '_' . uniqid() . '.' . $extension; 81 $file_path = uniqid($dest . $name . '_') . '.' . $extension;
82 82
83 // créer une miniature de l'image 83 // créer une miniature de l'image
84 // 84 //
@@ -105,7 +105,7 @@ class ImageUploadController
105 105
106 if(isset($json['image_url'])){ 106 if(isset($json['image_url'])){
107 $image_data = self::curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents 107 $image_data = self::curlDownloadImage($json['image_url']); // téléchargement de l’image par le serveur avec cURL au lieu de file_get_contents
108 $dest = 'images/'; 108 $dest = 'user_data/images/';
109 109
110 if(!is_dir($dest)) { // Vérifier si le répertoire existe, sinon le créer 110 if(!is_dir($dest)) { // Vérifier si le répertoire existe, sinon le créer
111 mkdir($dest, 0777, true); 111 mkdir($dest, 0777, true);
@@ -124,7 +124,7 @@ class ImageUploadController
124 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){ 124 if(!in_array($extension, $allowed_extensions) || $extension === 'jpg'){
125 $extension = 'jpeg'; 125 $extension = 'jpeg';
126 } 126 }
127 $local_path = $dest . $name . '_' . uniqid() . '.' . $extension; 127 $local_path = uniqid($dest . $name . '_') . '.' . $extension;
128 128
129 if(self::imagickCleanImage($image_data, $local_path, $extension)){ // recréer l’image pour la nettoyer 129 if(self::imagickCleanImage($image_data, $local_path, $extension)){ // recréer l’image pour la nettoyer
130 echo json_encode(['location' => $local_path]); // nouvelle adresse 130 echo json_encode(['location' => $local_path]); // nouvelle adresse
@@ -144,10 +144,10 @@ class ImageUploadController
144 static public function uploadImageBase64(): void 144 static public function uploadImageBase64(): void
145 { 145 {
146 $json = json_decode(file_get_contents('php://input'), true); 146 $json = json_decode(file_get_contents('php://input'), true);
147 $dest = 'images/'; 147 $dest = 'user_data/images/';
148 148
149 if(!is_dir('images')){ 149 if(!is_dir($dest)){
150 mkdir('images', 0777, true); 150 mkdir($dest, 0777, true);
151 } 151 }
152 152
153 // détection de data:image/ et de ;base64, et capture du format dans $type 153 // détection de data:image/ et de ;base64, et capture du format dans $type
@@ -170,7 +170,7 @@ class ImageUploadController
170 die; 170 die;
171 } 171 }
172 172
173 $local_path = $dest . 'pasted_image_' . uniqid() . '.' . $extension; 173 $local_path = uniqid($dest . 'pasted_image_') . '.' . $extension;
174 174
175 if(self::imagickCleanImage($image_data, $local_path)){ 175 if(self::imagickCleanImage($image_data, $local_path)){
176 echo json_encode(['location' => $local_path]); 176 echo json_encode(['location' => $local_path]);
diff --git a/src/model/entities/Article.php b/src/model/entities/Article.php
index 5412497..b9cb4bb 100644
--- a/src/model/entities/Article.php
+++ b/src/model/entities/Article.php
@@ -33,11 +33,10 @@ class Article
33 #[ORM\Column(type: "text")] 33 #[ORM\Column(type: "text")]
34 private string $content; // de l'éditeur html 34 private string $content; // de l'éditeur html
35 35
36 // liaison avec table intermédiaire 36 #[ORM\ManyToMany(targetEntity: Image::class, inversedBy: "article")] // cascade: ['remove'] = très dangereux!
37 #[ORM\ManyToMany(targetEntity: Image::class, inversedBy: "article")]
38 #[ORM\JoinTable( 37 #[ORM\JoinTable(
39 name: TABLE_PREFIX . "article_image", 38 name: TABLE_PREFIX . "article_image",
40 joinColumns: [new ORM\JoinColumn(name: "article_id", referencedColumnName: "id_article", onDelete: "CASCADE")], 39 joinColumns: [new ORM\JoinColumn(name: "article_id", referencedColumnName: "id_article", onDelete: "CASCADE")], // onDelete: "CASCADE": très utile
41 inverseJoinColumns: [new ORM\JoinColumn(name: "image_id", referencedColumnName: "id_image", onDelete: "CASCADE")] 40 inverseJoinColumns: [new ORM\JoinColumn(name: "image_id", referencedColumnName: "id_image", onDelete: "CASCADE")]
42 )] 41 )]
43 private Collection $images; 42 private Collection $images;
diff --git a/src/model/entities/Asset.php b/src/model/entities/Asset.php
index e1071b4..e359e21 100644
--- a/src/model/entities/Asset.php
+++ b/src/model/entities/Asset.php
@@ -11,6 +11,9 @@ use Doctrine\ORM\Mapping as ORM;
11#[ORM\Table(name: TABLE_PREFIX . "asset")] 11#[ORM\Table(name: TABLE_PREFIX . "asset")]
12class Asset 12class Asset
13{ 13{
14 const PATH = 'assets/';
15 const USER_PATH = 'user_data/assets/';
16
14 #[ORM\Id] 17 #[ORM\Id]
15 #[ORM\GeneratedValue] 18 #[ORM\GeneratedValue]
16 #[ORM\Column(type: "integer")] 19 #[ORM\Column(type: "integer")]
diff --git a/src/model/entities/NodeData.php b/src/model/entities/NodeData.php
index 5938eca..9db866e 100644
--- a/src/model/entities/NodeData.php
+++ b/src/model/entities/NodeData.php
@@ -39,11 +39,10 @@ class NodeData
39 #[ORM\Column(type: "integer", nullable: true)] 39 #[ORM\Column(type: "integer", nullable: true)]
40 private ?int $pagination_limit = null; // pour les post_block et news_block 40 private ?int $pagination_limit = null; // pour les post_block et news_block
41 41
42 // liaison avec table intermédiaire 42 #[ORM\ManyToMany(targetEntity: Asset::class, inversedBy: "node_data")] // cascade: ['remove'] = très dangereux!
43 #[ORM\ManyToMany(targetEntity: Asset::class, inversedBy: "node_data")]
44 #[ORM\JoinTable( 43 #[ORM\JoinTable(
45 name: TABLE_PREFIX . "node_asset", 44 name: TABLE_PREFIX . "nodedata_asset",
46 joinColumns: [new ORM\JoinColumn(name: "node_data_id", referencedColumnName: "id_node_data", onDelete: "CASCADE")], 45 joinColumns: [new ORM\JoinColumn(name: "node_data_id", referencedColumnName: "id_node_data", onDelete: "CASCADE")], // onDelete: "CASCADE": très utile
47 inverseJoinColumns: [new ORM\JoinColumn(name: "asset_id", referencedColumnName: "id_asset", onDelete: "CASCADE")] 46 inverseJoinColumns: [new ORM\JoinColumn(name: "asset_id", referencedColumnName: "id_asset", onDelete: "CASCADE")]
48 )] 47 )]
49 private Collection $assets; 48 private Collection $assets;
@@ -137,8 +136,18 @@ class NodeData
137 { 136 {
138 return $this->assets; 137 return $this->assets;
139 } 138 }
140 public function setAssets(Collection $assets): void 139 public function addAsset(Asset $asset): void
141 { 140 {
142 $this->assets = $assets; 141 if(!$this->assets->contains($asset)){
142 $this->assets->add($asset);
143 //$asset->addNodeData($this); // autre sens
144 }
145 }
146 public function removeAsset(Asset $asset): void
147 {
148 $this->assets->removeElement($asset);
149 /*if($this->assets->removeElement($asset)){ // autre sens
150 $asset->removeNodeData($this);
151 }*/
143 } 152 }
144} 153}
diff --git a/src/router.php b/src/router.php
index 1127c81..cfb1dec 100644
--- a/src/router.php
+++ b/src/router.php
@@ -142,9 +142,8 @@ elseif($request->getMethod() === 'POST'){
142 } 142 }
143 143
144 /* -- site entier (header, footer, favicon) -- */ 144 /* -- site entier (header, footer, favicon) -- */
145 elseif($request->query->has('entire_site_edit')){ 145 elseif($request->query->has('head_foot_text')){
146 $request_params = explode('_', $request->query->get('entire_site_edit')); // header_title, header_description, footer_text, etc 146 HeadFootController::setTextData($entityManager, $request->query->get('head_foot_text'), $json);
147 HeadFootController::setTextData($entityManager, $request_params, $json);
148 } 147 }
149 148
150 /* -- page Menu et chemins -- */ 149 /* -- page Menu et chemins -- */
@@ -211,10 +210,16 @@ elseif($request->getMethod() === 'POST'){
211 } 210 }
212 211
213 // upload d'image dans tinymce avec le plugin (bouton "insérer une image" de l'éditeur) 212 // upload d'image dans tinymce avec le plugin (bouton "insérer une image" de l'éditeur)
214 elseif(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false && $request->query->has('action') && $request->query->get('action') === 'upload_image_tinymce') 213 elseif(strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false)
215 { 214 {
216 ImageUploadController::imageUploadTinyMce(); 215 if($request->query->has('action') && $request->query->get('action') === 'upload_image_tinymce'){
216 ImageUploadController::imageUploadTinyMce();
217 }
218 elseif($request->query->has('head_foot_image')){
219 HeadFootController::uploadAsset($entityManager, $request->query->get('head_foot_image'));
220 }
217 } 221 }
222
218 // requêtes XMLHttpRequest 223 // requêtes XMLHttpRequest
219 elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') 224 elseif(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
220 { 225 {
diff --git a/src/view/AbstractBuilder.php b/src/view/AbstractBuilder.php
index 34d1462..3d315b1 100644
--- a/src/view/AbstractBuilder.php
+++ b/src/view/AbstractBuilder.php
@@ -7,7 +7,7 @@ use App\Entity\Node;
7 7
8abstract class AbstractBuilder 8abstract class AbstractBuilder
9{ 9{
10 public const VIEWS_PATH = '../src/view/templates/'; 10 const VIEWS_PATH = '../src/view/templates/';
11 protected string $html = ''; 11 protected string $html = '';
12 protected int $id_node; 12 protected int $id_node;
13 13
diff --git a/src/view/FooterBuilder.php b/src/view/FooterBuilder.php
index dc0d31d..35df010 100644
--- a/src/view/FooterBuilder.php
+++ b/src/view/FooterBuilder.php
@@ -26,15 +26,19 @@ class FooterBuilder extends AbstractBuilder
26 if($_SESSION['admin']) 26 if($_SESSION['admin'])
27 { 27 {
28 // données du footer 28 // données du footer
29 $buttons_footer_name = '<img id="footer_name_open" class="action_icon" src="assets/edit.svg" onclick="footer_name.openTextInput()"> 29 $buttons_footer_name = '<img id="footer_name_open" class="action_icon" src="assets/edit.svg" onclick="footer_name.open()">
30 <img id="footer_name_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_name.submitTextInput()"> 30 <img id="footer_name_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_name.submit()">
31 <img id="footer_name_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_name.cancelTextInput()">'; 31 <img id="footer_name_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_name.cancel()">';
32 $buttons_footer_address = '<img id="footer_address_open" class="action_icon" src="assets/edit.svg" onclick="footer_address.openTextInput()"> 32 $buttons_footer_address = '<img id="footer_address_open" class="action_icon" src="assets/edit.svg" onclick="footer_address.open()">
33 <img id="footer_address_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_address.submitTextInput()"> 33 <img id="footer_address_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_address.submit()">
34 <img id="footer_address_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_address.cancelTextInput()">'; 34 <img id="footer_address_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_address.cancel()">';
35 $buttons_footer_email = '<img id="footer_email_open" class="action_icon" src="assets/edit.svg" onclick="footer_email.openTextInput()"> 35 $buttons_footer_email = '<img id="footer_email_open" class="action_icon" src="assets/edit.svg" onclick="footer_email.open()">
36 <img id="footer_email_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_email.submitTextInput()"> 36 <img id="footer_email_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_email.submit()">
37 <img id="footer_email_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_email.cancelTextInput()">'; 37 <img id="footer_email_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_email.cancel()">';
38
39 $buttons_footer_logo = '<img id="footer_logo_open" class="action_icon" src="assets/edit.svg" onclick="footer_logo.open()">
40 <img id="footer_logo_submit" class="action_icon hidden" src="assets/save.svg" onclick="footer_logo.submit()">
41 <img id="footer_logo_cancel" class="action_icon hidden" src="assets/close.svg" onclick="footer_logo.cancel()">';
38 42
39 // zone admin 43 // zone admin
40 $empty_admin_zone = 'empty_admin_zone'; 44 $empty_admin_zone = 'empty_admin_zone';
@@ -76,6 +80,7 @@ class FooterBuilder extends AbstractBuilder
76 $buttons_footer_name = ''; 80 $buttons_footer_name = '';
77 $buttons_footer_address = ''; 81 $buttons_footer_address = '';
78 $buttons_footer_email = ''; 82 $buttons_footer_email = '';
83 $buttons_footer_logo = '';
79 } 84 }
80 85
81 ob_start(); 86 ob_start();
diff --git a/src/view/HeadBuilder.php b/src/view/HeadBuilder.php
index 978d9ed..b3d78aa 100644
--- a/src/view/HeadBuilder.php
+++ b/src/view/HeadBuilder.php
@@ -3,6 +3,7 @@
3 3
4declare(strict_types=1); 4declare(strict_types=1);
5 5
6use App\Entity\Asset;
6use App\Entity\Node; 7use App\Entity\Node;
7 8
8class HeadBuilder extends AbstractBuilder 9class HeadBuilder extends AbstractBuilder
@@ -36,6 +37,7 @@ class HeadBuilder extends AbstractBuilder
36 if($_SESSION['admin']){ 37 if($_SESSION['admin']){
37 // édition éléments sur toutes les pages (header, footer et favicon) 38 // édition éléments sur toutes les pages (header, footer et favicon)
38 $js .= '<script src="' . self::versionedFileURL('js', 'InputText') . '"></script>' . "\n"; 39 $js .= '<script src="' . self::versionedFileURL('js', 'InputText') . '"></script>' . "\n";
40 $js .= '<script src="' . self::versionedFileURL('js', 'InputFile') . '"></script>' . "\n";
39 41
40 // tinymce, nécéssite un script de copie dans composer.json 42 // tinymce, nécéssite un script de copie dans composer.json
41 $css .= '<link rel="stylesheet" href="' . self::versionedFileURL('css', 'tinymce') . '">' . "\n"; 43 $css .= '<link rel="stylesheet" href="' . self::versionedFileURL('css', 'tinymce') . '">' . "\n";
@@ -57,7 +59,7 @@ class HeadBuilder extends AbstractBuilder
57 }*/ 59 }*/
58 60
59 // en dur temporairement 61 // en dur temporairement
60 $favicon = 'assets/favicon48x48.png'; 62 $favicon = Asset::USER_PATH . 'favicon48x48.png';
61 $alt = 'favicon'; 63 $alt = 'favicon';
62 64
63 ob_start(); 65 ob_start();
diff --git a/src/view/HeaderBuilder.php b/src/view/HeaderBuilder.php
index 5492340..3b45a11 100644
--- a/src/view/HeaderBuilder.php
+++ b/src/view/HeaderBuilder.php
@@ -3,6 +3,7 @@
3 3
4declare(strict_types=1); 4declare(strict_types=1);
5 5
6use App\Entity\Asset;
6use App\Entity\Node; 7use App\Entity\Node;
7 8
8class HeaderBuilder extends AbstractBuilder 9class HeaderBuilder extends AbstractBuilder
@@ -79,26 +80,35 @@ class HeaderBuilder extends AbstractBuilder
79 80
80 // boutons mode admin 81 // boutons mode admin
81 if($_SESSION['admin']){ 82 if($_SESSION['admin']){
82 $edit_favicon_hidden = 'hidden'; 83 $editing_zone_margin = '5px';
83 $button_favicon = ''; 84 $favicon = Asset::USER_PATH . 'favicon48x48.png'; // double le code dans HeadBuilder
84 $button_header_logo = ''; 85 $buttons_favicon = '<button id="head_favicon_open" onclick="head_favicon.open()"><img id="head_favicon_img" class="action_icon" src="' . $favicon . '"> Favicon</button>
85 //$edit_favicon_hidden = ''; 86 <img id="head_favicon_submit" class="action_icon hidden" src="assets/save.svg" onclick="head_favicon.submit()">
86 //$favicon = 'assets/favicon48x48.png'; // double le code dans HeadBuilder 87 <img id="head_favicon_cancel" class="action_icon hidden" src="assets/close.svg" onclick="head_favicon.cancel()">';
87 //$button_favicon = '<button onclick="editFavicon()"><img class="action_icon" src="' . $favicon . '"> Favicon</button>'; 88 $background = Asset::USER_PATH . 'fond-piscine.jpg';
88 //$button_header_logo = '<img class="action_icon" src="assets/edit.svg" onclick="editHeaderLogo()">'; 89 $buttons_background = '<button id="header_background_open" onclick="header_background.open()"><img id="header_background_img" class="background_button" src="' . $background . '"> Image de fond</button>
89 $buttons_header_title = '<img id="header_title_open" class="action_icon" src="assets/edit.svg" onclick="header_title.openTextInput()"> 90 <img id="header_background_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_background.submit()">
90 <img id="header_title_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_title.submitTextInput()"> 91 <img id="header_background_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_background.cancel()">';
91 <img id="header_title_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_title.cancelTextInput()">'; 92
92 $buttons_header_description = '<img id="header_description_open" class="action_icon" src="assets/edit.svg" onclick="header_description.openTextInput()"> 93 $buttons_header_logo = '<img id="header_logo_open" class="action_icon" src="assets/edit.svg" onclick="header_logo.open()">
93 <img id="header_description_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_description.submitTextInput()"> 94 <img id="header_logo_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_logo.submit()">
94 <img id="header_description_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_description.cancelTextInput()">'; 95 <img id="header_logo_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_logo.cancel()">';
96
97 $buttons_header_title = '<img id="header_title_open" class="action_icon" src="assets/edit.svg" onclick="header_title.open()">
98 <img id="header_title_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_title.submit()">
99 <img id="header_title_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_title.cancel()">';
100 $buttons_header_description = '<img id="header_description_open" class="action_icon" src="assets/edit.svg" onclick="header_description.open()">
101 <img id="header_description_submit" class="action_icon hidden" src="assets/save.svg" onclick="header_description.submit()">
102 <img id="header_description_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_description.cancel()">';
103
95 //$buttons_social_networks = '<img class="action_icon" src="assets/edit.svg" onclick="editSocialNetworks()">'; 104 //$buttons_social_networks = '<img class="action_icon" src="assets/edit.svg" onclick="editSocialNetworks()">';
96 $buttons_social_networks = ''; 105 $buttons_social_networks = '';
97 } 106 }
98 else{ 107 else{
99 $edit_favicon_hidden = 'hidden'; 108 $editing_zone_margin = '0';
100 $button_favicon = ''; 109 $buttons_favicon = '';
101 $button_header_logo = ''; 110 $buttons_background = '';
111 $buttons_header_logo = '';
102 $buttons_header_title = ''; 112 $buttons_header_title = '';
103 $buttons_header_description = ''; 113 $buttons_header_description = '';
104 $buttons_social_networks = ''; 114 $buttons_social_networks = '';
diff --git a/src/view/templates/footer.php b/src/view/templates/footer.php
index 33647a6..2bb5a9e 100644
--- a/src/view/templates/footer.php
+++ b/src/view/templates/footer.php
@@ -4,25 +4,34 @@
4 <div class="data"> 4 <div class="data">
5 <div class="contact"> 5 <div class="contact">
6 <div id="footer_name"> 6 <div id="footer_name">
7 <script>let footer_name = new InputText('footer_name');</script>
8 <span id="footer_name_span"><?= htmlspecialchars($name ?? '') ?></span> 7 <span id="footer_name_span"><?= htmlspecialchars($name ?? '') ?></span>
9 <input type="text" id="footer_name_input" class="hidden" value="<?= htmlspecialchars($name ?? '') ?>" size="30"> 8 <input type="text" id="footer_name_input" class="hidden" value="<?= htmlspecialchars($name ?? '') ?>" size="30">
10 <?= $buttons_footer_name ?> 9 <?= $buttons_footer_name ?>
11 </div> 10 </div>
12 <div id="footer_address"> 11 <div id="footer_address">
13 <script>let footer_address = new InputText('footer_address');</script>
14 <span id="footer_address_span"><?= htmlspecialchars($address ?? '') ?></span> 12 <span id="footer_address_span"><?= htmlspecialchars($address ?? '') ?></span>
15 <input type="text" id="footer_address_input" class="hidden" value="<?= htmlspecialchars($address ?? '') ?>" size="30"> 13 <input type="text" id="footer_address_input" class="hidden" value="<?= htmlspecialchars($address ?? '') ?>" size="30">
16 <?= $buttons_footer_address ?> 14 <?= $buttons_footer_address ?>
17 </div> 15 </div>
18 <div id="footer_email"> 16 <div id="footer_email">
19 <script>let footer_email = new InputText('footer_email');</script>
20 <a href="mailto:<?= $email ?>"><span id="footer_email_span"><?= htmlspecialchars($email ?? '') ?></span></a> 17 <a href="mailto:<?= $email ?>"><span id="footer_email_span"><?= htmlspecialchars($email ?? '') ?></span></a>
21 <input type="text" id="footer_email_input" class="hidden" value="<?= htmlspecialchars($email ?? '') ?>" size="30"> 18 <input type="text" id="footer_email_input" class="hidden" value="<?= htmlspecialchars($email ?? '') ?>" size="30">
22 <?= $buttons_footer_email ?> 19 <?= $buttons_footer_email ?>
23 </div> 20 </div>
24 </div> 21 </div>
25 <p class="footer_logo"><img src="<?= $footer_logo ?>" alt="logo"></p> 22 <div id="footer_logo">
23 <a href="<?= new URL ?>"><img id="footer_logo_img" src="<?= $footer_logo ?>" alt="logo_alt"></a>
24 <input type="file" id="footer_logo_input" class="hidden" accept="image/png, image/jpeg, image/gif, image/webp, image/tiff">
25 <?= $buttons_footer_logo ?>
26 </div>
27<?php if($_SESSION['admin']){ ?>
28 <script>
29 let footer_name = new InputText('footer_name');
30 let footer_address = new InputText('footer_address');
31 let footer_email = new InputText('footer_email');
32 let footer_logo = new InputFile('footer_logo');
33 </script>
34<?php } ?>
26 </div> 35 </div>
27 <div class="<?= $empty_admin_zone ?>"></div> 36 <div class="<?= $empty_admin_zone ?>"></div>
28 <div class="<?= $div_admin ?>"> 37 <div class="<?= $div_admin ?>">
diff --git a/src/view/templates/header.php b/src/view/templates/header.php
index 7977ef3..b63aa84 100644
--- a/src/view/templates/header.php
+++ b/src/view/templates/header.php
@@ -5,15 +5,22 @@
5 <div id="nav_zone"> 5 <div id="nav_zone">
6 <?= $nav ?> 6 <?= $nav ?>
7 </div> 7 </div>
8 8 <div class="editing_zone">
9 <div class="header-content"> 9 <div id="head_favicon" style="margin: <?= $editing_zone_margin ?>;">
10 <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">
11 <?= $buttons_favicon ?>
12 </div>
13 <div id="header_background">
14 <input type="file" id="header_background_input" class="hidden" accept="image/png, image/jpeg, image/gif, image/webp, image/tiff">
15 <?= $buttons_background ?>
16 </div>
17 </div>
18 <div class="header_content">
10 <div class="header_left_col"> 19 <div class="header_left_col">
11 <div id="edit_favicon_zone" class="<?= $edit_favicon_hidden ?>"> 20 <div id="header_logo">
12 <?= $button_favicon ?> 21 <a href="<?= new URL ?>"><img id="header_logo_img" src="<?= $header_logo ?>" alt="logo_alt"></a>
13 </div> 22 <input type="file" id="header_logo_input" class="hidden" accept="image/png, image/jpeg, image/gif, image/webp, image/tiff">
14 <div> 23 <?= $buttons_header_logo ?>
15 <a href="<?= new URL ?>"><img id="header_logo" src="<?= $header_logo ?>" alt="logo_alt"></a>
16 <?= $button_header_logo ?>
17 </div> 24 </div>
18 </div> 25 </div>
19 <div class="nav_button"> 26 <div class="nav_button">
@@ -21,13 +28,11 @@
21 </div> 28 </div>
22 <div class="site_title"> 29 <div class="site_title">
23 <h1 id="header_title"> 30 <h1 id="header_title">
24 <script>let header_title = new InputText('header_title');</script>
25 <a href="<?= new URL ?>"><span id="header_title_span"><?= htmlspecialchars($title ?? '') ?></span></a> 31 <a href="<?= new URL ?>"><span id="header_title_span"><?= htmlspecialchars($title ?? '') ?></span></a>
26 <input type="text" id="header_title_input" class="hidden" value="<?= htmlspecialchars($title ?? '') ?>" size="30"> 32 <input type="text" id="header_title_input" class="hidden" value="<?= htmlspecialchars($title ?? '') ?>" size="30">
27 <?= $buttons_header_title ?> 33 <?= $buttons_header_title ?>
28 </h1> 34 </h1>
29 <h2 id="header_description"> 35 <h2 id="header_description">
30 <script>let header_description = new InputText('header_description');</script>
31 <span id="header_description_span"><?= htmlspecialchars($description ?? '') ?></span> 36 <span id="header_description_span"><?= htmlspecialchars($description ?? '') ?></span>
32 <input type="text" id="header_description_input" class="hidden" value="<?= htmlspecialchars($description ?? '') ?>" size="30"> 37 <input type="text" id="header_description_input" class="hidden" value="<?= htmlspecialchars($description ?? '') ?>" size="30">
33 <?= $buttons_header_description ?> 38 <?= $buttons_header_description ?>
@@ -41,4 +46,13 @@
41 <?= $breadcrumb ?? '' ?> 46 <?= $breadcrumb ?? '' ?>
42 </div> 47 </div>
43 </div> 48 </div>
49<?php if($_SESSION['admin']){ ?>
50 <script>
51 let head_favicon = new InputFile('head_favicon');
52 let header_background = new InputFile('header_background');
53 let header_logo = new InputFile('header_logo');
54 let header_title = new InputText('header_title');
55 let header_description = new InputText('header_description');
56 </script>
57<?php } ?>
44 </header> \ No newline at end of file 58 </header> \ No newline at end of file