From bce9fda51d334a547ec5a48f0b7699ed3b5d7944 Mon Sep 17 00:00:00 2001 From: polo Date: Wed, 29 Oct 2025 21:42:39 +0100 Subject: =?UTF-8?q?classe=20Fetcher,=20gestion=20r=C3=A9seaux=20sociaux=20?= =?UTF-8?q?pr=C3=A9sents/absents,=20partie=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/head.css | 2 +- public/js/Fetcher.js | 51 +++++++++++++++++ public/js/Input.js | 105 +++++++++++++++------------------- public/js/main.js | 21 +++++++ src/controller/HeadFootController.php | 30 +++++++++- src/router.php | 3 + src/view/HeadBuilder.php | 3 + src/view/HeaderBuilder.php | 13 +++-- 8 files changed, 161 insertions(+), 67 deletions(-) create mode 100644 public/js/Fetcher.js diff --git a/public/css/head.css b/public/css/head.css index b5f1bd1..1844f93 100644 --- a/public/css/head.css +++ b/public/css/head.css @@ -87,7 +87,7 @@ header a gap: 4px; flex-wrap: wrap; } -#header_social_content img +#header_social_content a img { width: 28px; background-color: #ffffffb3; diff --git a/public/js/Fetcher.js b/public/js/Fetcher.js new file mode 100644 index 0000000..0f56628 --- /dev/null +++ b/public/js/Fetcher.js @@ -0,0 +1,51 @@ +class Fetcher{ + constructor(options = {}) { + this.endpoint = options.endpoint || 'index.php'; + this.method = options.method || 'POST'; // normalement c'est GET par défaut + + // Callbacks optionnels + this.onSuccess = options.onSuccess || null; + this.onFailure = options.onFailure || null; + //this.onError = options.onError || null; // Pour les erreurs réseau/parsing + } + + send(body){ + const options = { method: this.method }; + + if(this.method !== 'GET'){ // si GET, ni body ni headers + if(body instanceof FormData){ // pas de headers + options.body = body; + } + else if(body !== null && typeof body === 'object'){ // objet => json + options.headers = { 'Content-Type': 'application/json' }; + options.body = JSON.stringify(body); + } + else{ // blob? + options.body = body; + } + } + + return fetch(this.endpoint, options) + .then(response => response.json()) + .then(data => this.onResponse(data)) + .catch(error => { + console.error('Erreur:', error); + }); + } + + onResponse(data){ + if(data.success){ + if(this.onSuccess){ + this.onSuccess(data); + } + return{ success: true, data }; + } + else{ + if(this.onFailure){ + this.onFailure(data); + } + console.error(data.message || "Erreur serveur"); + return { success: false, data }; + } + } +} \ No newline at end of file diff --git a/public/js/Input.js b/public/js/Input.js index 54872d1..0ebbbbb 100644 --- a/public/js/Input.js +++ b/public/js/Input.js @@ -1,9 +1,10 @@ +// mère class InputToggler{ constructor(name, options = {}){ this.name = name; this.parent = document.getElementById(name); - // ids alternatifs optionnels + // des ids alternatifs sont possibles this.content_elem = this.parent.querySelector(options.content_selector || `#${name}_content`); this.input_elem = this.parent.querySelector(options.input_selector || `#${name}_input`); this.open_elem = this.parent.querySelector(options.open_selector || `#${name}_open`); @@ -39,34 +40,26 @@ class InputToggler{ } } + +// enfants class InputText extends InputToggler{ constructor(name, options = {}){ super(name, options); - this.fetch_endpoint = options.endpoint || 'index.php'; - this.fetch_key = options.fetch_key || 'head_foot_text'; + this.fetcher = new Fetcher({ + endpoint: (options.endpoint || 'index.php?') + (options.fetch_key || 'head_foot_text=') + this.name, + method: 'POST', + onSuccess: (data) => this.onSuccess(data) + }); } submit(){ - fetch(this.fetch_endpoint + '?' + this.fetch_key + '=' + this.name, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({new_text: this.input_elem.value}) - }) - .then(response => response.json()) - .then(data => { - if(data.success){ - this.onSuccess(data); - this.close(); - } - else{ - console.error("Erreur: le serveur n'a pas enregistré le nouveau texte."); - } - }) - .catch(error => { - console.error('Erreur:', error); - }); + this.fetcher.send({new_text: this.input_elem.value}) + .then(result => { + toastNotify(result.success ? 'texte modifié avec succès' : "erreur: la modification des données côté serveur a échoué"); + }); } onSuccess(data){ this.content_elem.innerHTML = this.input_elem.value; + this.close(); } cancel(){ this.input_elem.value = this.content_elem.innerHTML; @@ -74,29 +67,15 @@ class InputText extends InputToggler{ } } -class InputTextSocialNetwork extends InputText{ - open(){ - const elem_parent = this.content_elem.parentNode; - if(elem_parent.tagName.toLowerCase() === 'a'){ - this.input_elem.value = elem_parent.href; - } - super.open(); - } - onSuccess(data){ - if(this.input_elem.value){ - this.content_elem.parentNode.href = this.input_elem.value; - } - else{ - this.content_elem.parentNode.removeAttribute('href'); - } - } -} - class InputFile extends InputToggler{ constructor(name, options = {}){ super(name, options); - this.fetch_endpoint = options.endpoint || 'index.php'; - this.fetch_key = options.fetch_key || 'head_foot_image'; + this.fetcher = new Fetcher({ + endpoint: (options.endpoint || 'index.php?') + (options.fetch_key || 'head_foot_image=') + this.name, + method: 'POST', + onSuccess: (data) => this.onSuccess(data), + onFailure: (data) => this.onFailure(data) + }); } submit(){ const file = this.input_elem.files[0]; @@ -108,27 +87,14 @@ class InputFile extends InputToggler{ const form_data = new FormData(); form_data.append('file', file); - fetch(this.fetch_endpoint + '?' + this.fetch_key + '=' + this.name, { - method: 'POST', // apparemment il faudrait utiliser PUT - body: form_data - }) - .then(response => response.json()) - .then(data => { - if(data.success){ - this.onSuccess(data); - this.close(); - } - else{ - this.onFailure(data); - console.error(data.message); - } - }) - .catch(error => { - console.error('Erreur:', error); - }); + this.fetcher.send(form_data) + .then(result => { + toastNotify(result.success ? 'fichier téléchargé avec succès' : "erreur: la modification des données côté serveur a échoué"); + }); } onSuccess(data){ this.content_elem.src = data.location; + this.close(); } onFailure(data){ if(data.format === 'ico'){ @@ -137,6 +103,8 @@ class InputFile extends InputToggler{ } } + +// petits enfants class InputFileFavicon extends InputFile{ onSuccess(data){ const link = document.querySelector('link[rel="icon"]'); @@ -150,4 +118,23 @@ class InputFileHeaderBackground extends InputFile{ document.querySelector('header').style.backgroundImage = `url('${data.location}')`; super.onSuccess(data); } +} + +class InputTextSocialNetwork extends InputText{ + open(){ + const elem_parent = this.content_elem.parentNode; + if(elem_parent.tagName.toLowerCase() === 'a'){ + this.input_elem.value = elem_parent.href; + } + super.open(); + } + onSuccess(data){ + if(this.input_elem.value){ + this.content_elem.parentNode.href = this.input_elem.value; + } + else{ + this.content_elem.parentNode.removeAttribute('href'); + } + this.close(); // vu qu'on n'appelle pas super.onSuccess + } } \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js index 59a9331..3cc144a 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -199,6 +199,27 @@ function findParentByTagName(element, tag_name){ return null; } +function checkSocialNetwork(id){ + const checkbox = document.getElementById(id).querySelector('input[type="checkbox"]'); + + new Fetcher({ + endpoint: 'index.php?head_foot_social_check=' + id, + method: 'POST', + onSuccess: (data) => { + console.log(data); + + checkbox.checked = data.checked; + + /*const color = checkbox.checked ? "#ff1d04" : "grey"; + clicked_menu_entry.querySelector("button").style.color = color;*/ + }, + onFailure: (data) => { + console.log(data); + } + }) + .send({checked: checkbox.checked}); +} + /* -- fonctions spécifiques à la gestion des dates -- */ diff --git a/src/controller/HeadFootController.php b/src/controller/HeadFootController.php index 7597683..a4727e2 100644 --- a/src/controller/HeadFootController.php +++ b/src/controller/HeadFootController.php @@ -22,15 +22,17 @@ class HeadFootController if($model->findWhateverNode('name_node', $params_array[0])){ $node_data = $model->getNode()->getNodeData(); + // liens réseaux sociaux if(in_array($params_array[1], NodeData::$social_networks)){ - $social = $node_data->getData()['social']; + $social = $node_data->getData()['social'] ?? []; $social[$params_array[1]] = $json['new_text']; $node_data->updateData('social', $social); } + // autres textes else{ $node_data->updateData($params_array[1], $json['new_text']); // $params_array[1] n'est pas contrôlé } - + $entityManager->flush(); echo json_encode(['success' => true]); } @@ -120,5 +122,29 @@ class HeadFootController die; } + static public function displaySocialNetwork(EntityManager $entityManager, string $request_params, array $json): void + { + $params_array = explode('_', $request_params); + if(count($params_array) !== 2){ + echo json_encode(['success' => false]); + die; + } + + $model = new Model($entityManager); + if(in_array($params_array[1], NodeData::$social_networks) && $model->findWhateverNode('name_node', $params_array[0])){ + $node_data = $model->getNode()->getNodeData(); + $social_show = $node_data->getData()['social_show'] ?? []; + $social_show[$params_array[1]] = $json['checked']; + $node_data->updateData('social_show', $social_show); + + $entityManager->flush(); + echo json_encode(['success' => true, 'checked' => $json['checked']]); + } + else{ + echo json_encode(['success' => false]); + } + die; + } + //static public function uploadImage(EntityManager $entityManager, array $request_params): void } \ No newline at end of file diff --git a/src/router.php b/src/router.php index ebe645f..2ba3b88 100644 --- a/src/router.php +++ b/src/router.php @@ -145,6 +145,9 @@ elseif($request->getMethod() === 'POST'){ elseif($request->query->has('head_foot_text')){ HeadFootController::setTextData($entityManager, $request->query->get('head_foot_text'), $json); } + elseif($request->query->has('head_foot_social_check')){ + HeadFootController::displaySocialNetwork($entityManager, $request->query->get('head_foot_social_check'), $json); + } /* -- page Menu et chemins -- */ elseif(isset($_GET['menu_edit'])) diff --git a/src/view/HeadBuilder.php b/src/view/HeadBuilder.php index 88e69fb..e3d620c 100644 --- a/src/view/HeadBuilder.php +++ b/src/view/HeadBuilder.php @@ -38,6 +38,9 @@ class HeadBuilder extends AbstractBuilder // édition éléments sur toutes les pages (header, footer et favicon) $js .= '' . "\n"; + // sert partout? + $js .= '' . "\n"; + // tinymce, nécéssite un script de copie dans composer.json $css .= '' . "\n"; $js .= '' . "\n"; // pour js/tinymce/tinymce.min.js diff --git a/src/view/HeaderBuilder.php b/src/view/HeaderBuilder.php index 1cc4fc3..f988156 100644 --- a/src/view/HeaderBuilder.php +++ b/src/view/HeaderBuilder.php @@ -84,10 +84,10 @@ class HeaderBuilder extends AbstractBuilder // boucle sur la liste complète de réseaux sociaux foreach(NodeData::$social_networks as $network){ $checked = (isset($social_show[$network]) && $social_show[$network]) ? 'checked' : ''; - $href = isset($social[$network]) ? 'href="' . $social[$network] . '"' : ''; + $href = (isset($social[$network]) && $social[$network] !== '') ? 'href="' . $social[$network] . '"' : ''; $social_networks .= '
- + '. $network . '_alt @@ -110,9 +110,12 @@ class HeaderBuilder extends AbstractBuilder if(isset($social_show)){ // boucle sur les réseaux sociaux "activés" foreach(array_keys($social_show) as $network){ - $social_networks .= '
- '. $network . '_alt -
'; + if($social_show[$network]){ + $href = (isset($social[$network]) && $social[$network] !== '') ? 'href="' . $social[$network] . '"' : ''; + $social_networks .= '
+ '. $network . '_alt +
'; + } } } } -- cgit v1.2.3