aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--public/css/foot.css26
-rw-r--r--public/css/head.css70
-rw-r--r--public/js/Input.js132
-rw-r--r--src/controller/HeadFootController.php3
-rw-r--r--src/controller/ImageUploadController.php4
-rw-r--r--src/view/HeaderBuilder.php17
-rw-r--r--src/view/templates/footer.php2
-rw-r--r--src/view/templates/header.php18
8 files changed, 174 insertions, 98 deletions
diff --git a/public/css/foot.css b/public/css/foot.css
index 38284e3..ed65666 100644
--- a/public/css/foot.css
+++ b/public/css/foot.css
@@ -79,16 +79,16 @@ footer .data > div
79 margin: 0 3px; 79 margin: 0 3px;
80}*/ 80}*/
81 81
82#footer_logo_img 82#footer_logo_content
83{ 83{
84 max-width: 288px; 84 max-width: 288px;
85 min-width: 150px; 85 min-width: 150px;
86} 86}
87@media screen and (max-width: 1000px) 87@media screen and (max-width: 800px)
88{ 88{
89 .footer .contact 89 footer .contact
90 { 90 {
91 width: 70px; 91 font-size: smaller;
92 } 92 }
93} 93}
94 94
@@ -119,10 +119,6 @@ footer .data > div
119 color: black; 119 color: black;
120 text-decoration: none; 120 text-decoration: none;
121} 121}
122.empty_admin_zone
123{
124 height: 70px;
125}
126.logged_in 122.logged_in
127{ 123{
128 /*height: 70px;*/ 124 /*height: 70px;*/
@@ -149,6 +145,20 @@ footer .data > div
149 background-color: yellow; 145 background-color: yellow;
150 border-radius: 4px; 146 border-radius: 4px;
151} 147}
148@media screen and (max-width: 550px){
149 .logged_in, .logged_in button
150 {
151 font-size: 96%;
152 }
153 .admin_buttons_zone
154 {
155 margin: 3px;
156 }
157}
158.empty_admin_zone
159{
160 height: 93px; /* 40 => 1 ligne, 70/64 => 2 lignes, 93/83 => 3 lignes */
161}
152.modif_mode 162.modif_mode
153{ 163{
154 background-color: #ffae1a; /* orange clair soupe de poisson */ 164 background-color: #ffae1a; /* orange clair soupe de poisson */
diff --git a/public/css/head.css b/public/css/head.css
index dab81a8..c178d3a 100644
--- a/public/css/head.css
+++ b/public/css/head.css
@@ -21,33 +21,24 @@ header
21{ 21{
22 max-height: 24px; 22 max-height: 24px;
23} 23}
24 24.header_additional_inputs
25{
26 display: flex;
27 flex-wrap: wrap;
28 justify-content: space-evenly;
29 align-items: center;
30 margin-top: 10px;
31}
25.header_content 32.header_content
26{ 33{
27 padding: 20px 0; 34 margin-top: 10px;
35 padding-bottom: 20px;
28 display: grid; 36 display: grid;
29 grid-template-columns: 1fr 1fr 1fr; 37 grid-template-columns: 1fr 1fr 1fr;
30 align-items: end; 38 align-items: end;
31} 39}
32/*.header_left_col 40/*.header_left_col
33{}*/ 41{}*/
34@media screen and (max-width: 1000px){
35 .header_content{
36 padding: 18px 0;
37 }
38}
39@media screen and (max-width: 450px){
40 .header_content
41 {
42 /*grid-template-columns: 1fr 2fr 1fr;*/
43 display: block;
44 padding: 18px;
45 }
46 #header_logo
47 {
48 display: none;
49 }
50}
51 42
52.header_center_col 43.header_center_col
53{ 44{
@@ -73,7 +64,7 @@ header h2
73header img 64header img
74{ 65{
75 vertical-align: bottom; /* supprime espace sous l'image */ 66 vertical-align: bottom; /* supprime espace sous l'image */
76 max-width: 150px; 67 max-width: 180px;
77 max-height: 100px; 68 max-height: 100px;
78} 69}
79header a 70header a
@@ -83,13 +74,46 @@ header a
83} 74}
84/*.header_right_col 75/*.header_right_col
85{}*/ 76{}*/
86#header_social img 77#header_social
78{
79 display: flex;
80 justify-content: center;
81}
82#header_social_content img
87{ 83{
88 width: 28px; 84 width: 28px;
89 background-color: rgba(255, 255, 255, 0.7); 85 background-color: #ffffffb3;
90 border-radius: 50%; 86 border-radius: 50%;
91} 87}
92#header_social img:hover 88
89#header_social_content img:hover
93{ 90{
94 background-color: yellow; 91 background-color: yellow;
92}
93#header_social_input
94{
95 background-color: #ffffff7f;
96 border-radius: 10px;
97 padding: 5px;
98}
99
100@media screen and (max-width: 1000px){
101 .header_content{
102 padding: 15px 0;
103 }
104 header img
105 {
106 max-width: 160px;
107 }
108}
109@media screen and (max-width: 500px){
110 .header_content
111 {
112 display: block;
113 padding: 10px;
114 }
115 #header_logo
116 {
117 display: none;
118 }
95} \ No newline at end of file 119} \ No newline at end of file
diff --git a/public/js/Input.js b/public/js/Input.js
index 0b6912b..9a131c8 100644
--- a/public/js/Input.js
+++ b/public/js/Input.js
@@ -1,96 +1,126 @@
1class Input{ 1class InputToggler{
2 constructor(name){ 2 constructor(name, options = {}){
3 this.name = name; 3 this.name = name;
4 /*const name_array = name.split('_');
5 this.node = name_array[0];
6 this.what = name_array[1];*/
7 this.parent = document.getElementById(name); 4 this.parent = document.getElementById(name);
5
6 // ids alternatifs optionnels
7 this.content_elem = this.parent.querySelector(options.content_selector || `#${name}_content`);
8 this.input_elem = this.parent.querySelector(options.input_selector || `#${name}_input`);
9 this.open_elem = this.parent.querySelector(options.open_selector || `#${name}_open`);
10 this.submit_elem = this.parent.querySelector(options.submit_selector || `#${name}_submit`);
11 this.cancel_elem = this.parent.querySelector(options.cancel_selector || `#${name}_cancel`);
12
13 // balises à ne pas gérer (fonctionne mais inutilisé pour l'instant)
14 this.ignored_tags = {
15 has_content: options.has_content !== false, // => true sauf si le paramètre vaut false
16 has_input: options.has_input !== false,
17 has_open_button: options.has_open_button !== false,
18 has_submit_button: options.has_submit_button !== false,
19 has_cancel_button: options.has_cancel_button !== false
20 }
8 } 21 }
9 open(){ 22 open(){
10 this.parent.querySelector('#' + this.name + '_content').classList.add('hidden'); 23 this.toggleVisibility(true);
11 this.parent.querySelector('#' + this.name + '_input').classList.remove('hidden');
12 this.parent.querySelector('#' + this.name + '_open').classList.add('hidden');
13 this.parent.querySelector('#' + this.name + '_submit').classList.remove('hidden');
14 this.parent.querySelector('#' + this.name + '_cancel').classList.remove('hidden');
15 } 24 }
16 close(){ 25 close(){
17 this.parent.querySelector('#' + this.name + '_content').classList.remove('hidden'); 26 this.toggleVisibility(false);
18 this.parent.querySelector('#' + this.name + '_input').classList.add('hidden'); 27 }
19 this.parent.querySelector('#' + this.name + '_open').classList.remove('hidden'); 28 toggleVisibility(show_input = false){
20 this.parent.querySelector('#' + this.name + '_submit').classList.add('hidden'); 29 // avec && si la partie de gauche est "false", on traite la partie de droite
21 this.parent.querySelector('#' + this.name + '_cancel').classList.add('hidden'); 30 // ?. est l'opérateur de chainage optionnel
31 this.ignored_tags.has_content && this.content_elem.classList.toggle('hidden', show_input);
32 this.ignored_tags.has_input && this.input_elem.classList.toggle('hidden', !show_input);
33 this.ignored_tags.has_open_button && this.open_elem.classList.toggle('hidden', show_input);
34 this.ignored_tags.has_submit_button && this.submit_elem.classList.toggle('hidden', !show_input);
35 this.ignored_tags.has_cancel_button && this.cancel_elem.classList.toggle('hidden', !show_input);
22 } 36 }
23 cancel(){ 37 cancel(){
24 this.close(); 38 this.close();
25 } 39 }
26} 40}
27 41
28class InputFile extends Input{ 42class InputText extends InputToggler{
43 constructor(name, options = {}){
44 super(name, options);
45 this.fetch_endpoint = options.endpoint || 'index.php';
46 this.fetch_key = options.fetch_key || 'head_foot_text';
47 }
29 submit(){ 48 submit(){
30 const file = this.parent.querySelector('#' + this.name + '_input').files[0]; 49 fetch(this.fetch_endpoint + '?' + this.fetch_key + '=' + this.name, {
31 if(!file){ 50 method: 'POST',
32 console.error("Erreur: aucun fichier sélectionné."); 51 headers: { 'Content-Type': 'application/json' },
33 toastNotify("Erreur: aucun fichier sélectionné."); 52 body: JSON.stringify({new_text: this.input_elem.value})
34 return;
35 }
36 const form_data = new FormData();
37 form_data.append('file', file);
38
39 fetch('index.php?head_foot_image=' + this.name, {
40 method: 'POST', // apparemment il faudrait utiliser PUT
41 body: form_data
42 }) 53 })
43 .then(response => response.json()) 54 .then(response => response.json())
44 .then(data => { 55 .then(data => {
45 if(data.success){ 56 if(data.success){
46 // cas particuliers 57 this.content_elem.innerHTML = this.input_elem.value;
47 if(this.name === 'head_favicon'){
48 const link = document.querySelector('link[rel="icon"]');
49 link.type = data.mime_type;
50 link.href = data.location;
51 }
52 else if(this.name === 'header_background'){
53 document.querySelector('header').style.backgroundImage = "url('" + data.location + "')";
54 }
55
56 this.parent.querySelector('#' + this.name + '_content').src = data.location;
57 this.close(); 58 this.close();
58 } 59 }
59 else{ 60 else{
60 console.error("Erreur: le serveur n'a pas enregistré l'image'."); 61 console.error("Erreur: le serveur n'a pas enregistré le nouveau texte.");
61 } 62 }
62 }) 63 })
63 .catch(error => { 64 .catch(error => {
64 console.error('Erreur:', error); 65 console.error('Erreur:', error);
65 }); 66 });
66 } 67 }
68 cancel(){
69 this.input_elem.value = this.content_elem.innerHTML;
70 super.cancel();
71 }
67} 72}
68 73
69class InputText extends Input{ 74class InputFile extends InputToggler{
75 constructor(name, options = {}){
76 super(name, options);
77 this.fetch_endpoint = options.endpoint || 'index.php';
78 this.fetch_key = options.fetch_key || 'head_foot_image';
79 }
70 submit(){ 80 submit(){
71 const new_text = this.parent.querySelector('#' + this.name + '_input').value; 81 const file = this.input_elem.files[0];
82 if(!file){
83 console.error("Erreur: aucun fichier sélectionné.");
84 toastNotify("Erreur: aucun fichier sélectionné.");
85 return;
86 }
87 const form_data = new FormData();
88 form_data.append('file', file);
72 89
73 fetch('index.php?head_foot_text=' + this.name, { 90 fetch(this.fetch_endpoint + '?' + this.fetch_key + '=' + this.name, {
74 method: 'POST', 91 method: 'POST', // apparemment il faudrait utiliser PUT
75 headers: { 'Content-Type': 'application/json' }, 92 body: form_data
76 body: JSON.stringify({new_text: new_text})
77 }) 93 })
78 .then(response => response.json()) 94 .then(response => response.json())
79 .then(data => { 95 .then(data => {
80 if(data.success){ 96 if(data.success){
81 this.parent.querySelector('#' + this.name + '_content').innerHTML = new_text; 97 this.onSuccess(data);
82 this.close(); 98 this.close();
83 } 99 }
84 else{ 100 else{
85 console.error("Erreur: le serveur n'a pas enregistré le nouveau texte."); 101 console.error("Erreur: le serveur n'a pas enregistré l'image'.");
86 } 102 }
87 }) 103 })
88 .catch(error => { 104 .catch(error => {
89 console.error('Erreur:', error); 105 console.error('Erreur:', error);
90 }); 106 });
91 } 107 }
92 cancel(){ // surcharge 108 onSuccess(data){
93 this.parent.querySelector('#' + this.name + '_input').value = this.parent.querySelector('#' + this.name + '_content').innerHTML; 109 this.content_elem.src = data.location;
94 this.close();
95 } 110 }
111}
112
113class InputFileFavicon extends InputFile{
114 onSuccess(data){
115 const link = document.querySelector('link[rel="icon"]');
116 link.type = data.mime_type;
117 link.href = data.location;
118 super.onSuccess(data);
119 }
120}
121class InputFileHeaderBackground extends InputFile{
122 onSuccess(data){
123 document.querySelector('header').style.backgroundImage = `url('${data.location}')`;
124 super.onSuccess(data);
125 }
96} \ No newline at end of file 126} \ No newline at end of file
diff --git a/src/controller/HeadFootController.php b/src/controller/HeadFootController.php
index a739df8..5945c87 100644
--- a/src/controller/HeadFootController.php
+++ b/src/controller/HeadFootController.php
@@ -5,7 +5,6 @@ declare(strict_types=1);
5 5
6use App\Entity\NodeDataAsset; 6use App\Entity\NodeDataAsset;
7use App\Entity\Asset; 7use App\Entity\Asset;
8use Doctrine\Common\Collections\ArrayCollection;
9use Doctrine\ORM\EntityManager; 8use Doctrine\ORM\EntityManager;
10 9
11class HeadFootController 10class HeadFootController
@@ -38,7 +37,7 @@ class HeadFootController
38 } 37 }
39 else{ 38 else{
40 if(!is_dir(Asset::USER_PATH)){ 39 if(!is_dir(Asset::USER_PATH)){
41 mkdir(Asset::USER_PATH, 0755, true); 40 mkdir(Asset::USER_PATH, 0777, true);
42 } 41 }
43 42
44 /* -- téléchargement -- */ 43 /* -- téléchargement -- */
diff --git a/src/controller/ImageUploadController.php b/src/controller/ImageUploadController.php
index 5e80ba5..b085e11 100644
--- a/src/controller/ImageUploadController.php
+++ b/src/controller/ImageUploadController.php
@@ -66,10 +66,10 @@ class ImageUploadController
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, 0777, 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, 0777, true);
73 } 73 }
74 74
75 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif']; 75 $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'tif'];
diff --git a/src/view/HeaderBuilder.php b/src/view/HeaderBuilder.php
index ae9a888..7e3c363 100644
--- a/src/view/HeaderBuilder.php
+++ b/src/view/HeaderBuilder.php
@@ -43,6 +43,7 @@ class HeaderBuilder extends AbstractBuilder
43 } 43 }
44 44
45 // réseaux sociaux + logo dans l'entête 45 // réseaux sociaux + logo dans l'entête
46 // ?-> est l'opérateur de chainage optionnel
46 $header_logo = Asset::USER_PATH . $node_data->getAssetByRole('header_logo')?->getFileName() ?? ''; 47 $header_logo = Asset::USER_PATH . $node_data->getAssetByRole('header_logo')?->getFileName() ?? '';
47 $header_background = Asset::USER_PATH . $node_data->getAssetByRole('header_background')?->getFileName() ?? ''; 48 $header_background = Asset::USER_PATH . $node_data->getAssetByRole('header_background')?->getFileName() ?? '';
48 49
@@ -55,8 +56,7 @@ class HeaderBuilder extends AbstractBuilder
55 56
56 // boutons mode admin 57 // boutons mode admin
57 if($_SESSION['admin']){ 58 if($_SESSION['admin']){
58 // assets dans classe editing_zone 59 // assets dans classe header_additional_inputs
59 $editing_zone_margin = '5px';
60 $admin_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"> 60 $admin_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">
61 <button id="head_favicon_open" onclick="head_favicon.open()"><img id="head_favicon_content" class="action_icon"> Favicon</button> 61 <button id="head_favicon_open" onclick="head_favicon.open()"><img id="head_favicon_content" class="action_icon"> Favicon</button>
62 <script>document.getElementById(\'head_favicon_content\').src = window.Config.favicon;</script> 62 <script>document.getElementById(\'head_favicon_content\').src = window.Config.favicon;</script>
@@ -83,10 +83,21 @@ class HeaderBuilder extends AbstractBuilder
83 <img id="header_description_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_description.cancel()">'; 83 <img id="header_description_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_description.cancel()">';
84 84
85 // icônes réseaux sociaux 85 // icônes réseaux sociaux
86 $social_networks_inputs = '<div id="header_social_input" class="hidden">';
87 foreach($keys as $one_key){
88 $social_networks_inputs .= '<div>
89 <input type="text" placeholder="nom du réseau social">
90 <input type="text" placeholder="lien https://...">
91 <input type="file">
92 </div>';
93 }
94 $social_networks_inputs .= '</div>';
95 /*$admin_social_networks = $social_networks_inputs . '<img id="header_social_open" class="action_icon" src="assets/edit.svg" onclick="header_social.open()">
96 <div id="header_social_submit" class="hidden"></div>
97 <img id="header_social_cancel" class="action_icon hidden" src="assets/close.svg" onclick="header_social.cancel()">';*/
86 $admin_social_networks = ''; 98 $admin_social_networks = '';
87 } 99 }
88 else{ 100 else{
89 $editing_zone_margin = '0';
90 $admin_favicon = ''; 101 $admin_favicon = '';
91 $admin_background = ''; 102 $admin_background = '';
92 $admin_header_logo = ''; 103 $admin_header_logo = '';
diff --git a/src/view/templates/footer.php b/src/view/templates/footer.php
index dc1c6cb..0bc85f5 100644
--- a/src/view/templates/footer.php
+++ b/src/view/templates/footer.php
@@ -17,7 +17,7 @@
17 </div> 17 </div>
18 </div> 18 </div>
19 <div id="footer_logo"> 19 <div id="footer_logo">
20 <a id="footer_logo_content" href="<?= new URL ?>"><img src="<?= $footer_logo ?? '' ?>" alt="logo_alt"></a> 20 <a href="<?= new URL ?>"><img id="footer_logo_content" src="<?= $footer_logo ?? '' ?>" alt="logo_alt"></a>
21 <?= $admin_footer_logo ?> 21 <?= $admin_footer_logo ?>
22 </div> 22 </div>
23<?php if($_SESSION['admin']){ ?> 23<?php if($_SESSION['admin']){ ?>
diff --git a/src/view/templates/header.php b/src/view/templates/header.php
index 4d3b323..9c74f9b 100644
--- a/src/view/templates/header.php
+++ b/src/view/templates/header.php
@@ -5,8 +5,8 @@
5 <div id="nav_zone"> 5 <div id="nav_zone">
6 <?= $nav ?> 6 <?= $nav ?>
7 </div> 7 </div>
8 <div> 8 <div class="header_additional_inputs">
9 <div id="head_favicon" style="margin: <?= $editing_zone_margin ?>;"> 9 <div id="head_favicon">
10 <?= $admin_favicon ?> 10 <?= $admin_favicon ?>
11 </div> 11 </div>
12 <div id="header_background"> 12 <div id="header_background">
@@ -16,7 +16,7 @@
16 <div class="header_content"> 16 <div class="header_content">
17 <div class="header_left_col"> 17 <div class="header_left_col">
18 <div id="header_logo"> 18 <div id="header_logo">
19 <a id="header_logo_content" href="<?= new URL ?>"><img src="<?= $header_logo ?? '' ?>" alt="header_logo"></a> 19 <a href="<?= new URL ?>"><img id="header_logo_content" src="<?= $header_logo ?? '' ?>" alt="header_logo"></a>
20 <?= $admin_header_logo ?> 20 <?= $admin_header_logo ?>
21 </div> 21 </div>
22 </div> 22 </div>
@@ -25,7 +25,7 @@
25 </div> 25 </div>
26 <div class="header_center_col"> 26 <div class="header_center_col">
27 <h1 id="header_title"> 27 <h1 id="header_title">
28 <a id="header_title_content" href="<?= new URL ?>"><?= htmlspecialchars($title ?? '') ?></a></span> 28 <a id="header_title_content" href="<?= new URL ?>"><?= htmlspecialchars($title ?? '') ?></a>
29 <?= $admin_header_title ?> 29 <?= $admin_header_title ?>
30 </h1> 30 </h1>
31 <h2 id="header_description"> 31 <h2 id="header_description">
@@ -35,7 +35,9 @@
35 </div> 35 </div>
36 <div class="header_right_col"> 36 <div class="header_right_col">
37 <div id="header_social"> 37 <div id="header_social">
38 <?= $social_networks ?> 38 <div id="header_social_content">
39 <?= $social_networks ?>
40 </div>
39 <?= $admin_social_networks ?> 41 <?= $admin_social_networks ?>
40 </div> 42 </div>
41 <?= $breadcrumb ?? '' ?> 43 <?= $breadcrumb ?? '' ?>
@@ -43,12 +45,12 @@
43 </div> 45 </div>
44<?php if($_SESSION['admin']){ ?> 46<?php if($_SESSION['admin']){ ?>
45 <script> 47 <script>
46 let head_favicon = new InputFile('head_favicon'); 48 let head_favicon = new InputFileFavicon('head_favicon');
47 let header_background = new InputFile('header_background'); 49 let header_background = new InputFileHeaderBackground('header_background');
48 let header_logo = new InputFile('header_logo'); 50 let header_logo = new InputFile('header_logo');
49 let header_title = new InputText('header_title'); 51 let header_title = new InputText('header_title');
50 let header_description = new InputText('header_description'); 52 let header_description = new InputText('header_description');
51 let header_social = new InputFile('header_social'); 53 let header_social = new InputToggler('header_social');
52 </script> 54 </script>
53<?php } ?> 55<?php } ?>
54 </header> \ No newline at end of file 56 </header> \ No newline at end of file