From cdf1bda847edd3e22e9fe817a802219ad8e0f626 Mon Sep 17 00:00:00 2001 From: polo Date: Wed, 8 Sep 2021 02:09:34 +0200 Subject: vignette --- controller/admin.php | 51 +++++++++++--------- controller/ckeditor.php | 44 ++++++------------ controller/visitor.php | 54 ++++++++++++++-------- imageAJAX.php | 100 ---------------------------------------- index.php | 29 +++++++++--- model/Classes.php | 26 +++++------ model/Image.php | 59 ++++++++++++++++++++++++ public/discographie.css | 32 ++++++++++++- public/donnees_hors_editeur.css | 4 +- public/melaine.css | 1 - view/album.php | 2 +- view/discographie.php | 4 +- view/template-formulaires.php | 22 ++++----- 13 files changed, 214 insertions(+), 214 deletions(-) delete mode 100644 imageAJAX.php create mode 100644 model/Image.php diff --git a/controller/admin.php b/controller/admin.php index 3c2b613..ba6fd83 100644 --- a/controller/admin.php +++ b/controller/admin.php @@ -98,11 +98,6 @@ function discoEdit($numArticle, $suppression) } } - $albumsJSON = array_reverse($Album->readAll()); // lourd - - // $albums contient un tableau de chaines JSON, - // on extrait de chacune 3 variables: titre, année, pochette - // traitements PHP pour l'éditeur // sécurisation du contenu pré-existant inséré dans l'éditeur if($numArticle >= 0 && !$suppression) @@ -111,44 +106,58 @@ function discoEdit($numArticle, $suppression) $texte = preparationCKeditor($numArticle, $texte); } - // changer les chaines JSON en tableaux + + // contenu de tous les fichiers JSON (= tableau de chaines) + $albumsJSON = $Album->readAll(); + // noms des fichiers JSON + $albumNamesJSON = $Album->getFileList(); + + // changer les chaines JSON en tableaux: titre, année, pochette $i = 0; + $annees = array(); foreach($albumsJSON as $oneAlbum) { $albumsJSON[$i] = json_decode($oneAlbum, true); + + // ajout des noms des fichiers JSON + $albumsJSON[$i][3] = $albumNamesJSON[$i]; + $annees[$i] = $albumsJSON[$i][1]; $i++; } + // tri d'un tableau multidimensionnel + array_multisort($annees, $albumsJSON); // on passe maintenant au contenu HTML - $albumNamesJSON = array_reverse($Album->getFileNames()); $Album->setFormat('html'); $Album->makeFileList(); - $albumNamesHTML = array_reverse($Album->getFileNames()); + $albumNamesHTML = $Album->getFileList(); // lien vers le HTML ou ancre? // pour chaque album, détecter le fichier html // si non, ne fournir qu'un lien d'ancre pour la liste d'album $i = 0; $avecLien = []; - foreach($albumNamesJSON as $oneAlbum) + foreach($albumsJSON as $oneAlbum) { // nom sans extension - $chemin = pathinfo($oneAlbum); + $chemin = pathinfo($oneAlbum[3]); $nomJSONsansExt = $chemin['filename']; - $chemin = pathinfo($albumNamesHTML[0]); - // détection - if(file_exists($chemin['dirname'] . '/' . $nomJSONsansExt . '.html')) + // au cas où la discographie ne contient aucun html + if(!empty($albumNamesHTML)) { - $lienAlbum[$i] = 'album&album_code=' . $nomJSONsansExt . '&album_name=' . $albumsJSON[$i][0]; - $avecLien[$i] = true; + if(file_exists('data/discographie/html/' . $nomJSONsansExt . '.html')) + { + $lienAlbum[$i] = 'album&album_code=' . $nomJSONsansExt . '&album_name=' . $albumsJSON[$i][0]; + $avecLien[$i] = true; + } + else + { + $lienAlbum[$i] = 'discographie#' . $albumsJSON[$i][0]; + $avecLien[$i] = false; + } + $i++; } - else - { - $lienAlbum[$i] = 'discographie#' . $albumsJSON[$i][0]; - $avecLien[$i] = false; - } - $i++; } // morceaux en HTML à assembler diff --git a/controller/ckeditor.php b/controller/ckeditor.php index 2e72be2..d07f2a2 100644 --- a/controller/ckeditor.php +++ b/controller/ckeditor.php @@ -87,7 +87,7 @@ function submitCKeditor($nomFichier) { $titre = $_POST['titre']; $annee = $_POST['annee']; - $pochette = $_POST['pochette']; // une image + $pochette = $_FILES['upload']['name']; // des formulaires simples $titre = htmLawed($titre, $configHtmLawed, $specHtmLawed); @@ -95,36 +95,8 @@ function submitCKeditor($nomFichier) $annee = htmLawed($annee, $configHtmLawed, $specHtmLawed); $annee = trim($annee); - // pochette - // Album->imageUpload(); - // test formats jpg, jpeg, png, gif, tiff - // enregistrement du fichier - } - - // lien sans http:// - // un clic sur un lien dans l'éditeur affiche une infobulle montrant l'adresse cible du lien si celle-ci a déjà été précisée - // il est possible de cliquer sur ce lien, ce qui ouvre un onglet avec le site demandé - // toutefois si cette adresse est de type "domaine.fr" (sans http:// devant), le navigateur ne va pas rechercher un site mais un fichier comme si mon adresse était de type file:///fichier - // tomber ainsi sur une page d'erreur est déroutant: - // "ai-je perdu le texte que j'étais en train de taper?"" - // solution 1 (mauvaise): activer la redirection en cas d'erreur 404 dans le .htaccess - // solution 2 (façon pop-up): fermer ce nouvel onglet avec echo ''; - // pour faire passer par le .htaccess l'info comme quoi la page précédente comportait un éditeur ouvert... - - - // enregistrement - // var_dump($titre, $annee, $pochette, $contenu); - // die(); - - // modèle - if($page == 'discographie') - { // on instancie avec l'enfant de OneArticle $Album = new Album($page); - - //var_dump($_GET['article']); - //var_dump($_POST); - //exit(); // enregistrement if($_GET['article'] == 0) @@ -132,7 +104,7 @@ function submitCKeditor($nomFichier) // page disco $Album->createVignette($titre, $annee, $pochette); - // page détail de l'album + // page de l'album if(!empty($_POST['contenu'])) { $Album->create($contenu); @@ -143,7 +115,7 @@ function submitCKeditor($nomFichier) // page disco $Album->updateVignette($titre, $annee, $pochette); - // page détail de l'album + // page de l'album if(isset($_POST['contenu'])) { $Album->update($content); @@ -167,5 +139,15 @@ function submitCKeditor($nomFichier) } } +// lien sans http:// +// un clic sur un lien dans l'éditeur affiche une infobulle montrant l'adresse cible du lien si celle-ci a déjà été précisée +// il est possible de cliquer sur ce lien, ce qui ouvre un onglet avec le site demandé +// toutefois si cette adresse est de type "domaine.fr" (sans http:// devant), le navigateur ne va pas rechercher un site mais un fichier comme si mon adresse était de type file:///fichier +// tomber ainsi sur une page d'erreur est déroutant: +// "ai-je perdu le texte que j'étais en train de taper?"" +// solution 1 (mauvaise): activer la redirection en cas d'erreur 404 dans le .htaccess +// solution 2 (façon pop-up): fermer ce nouvel onglet avec echo ''; +// pour faire passer par le .htaccess l'info comme quoi la page précédente comportait un éditeur ouvert... + function cleanHTML($contenu) {} diff --git a/controller/visitor.php b/controller/visitor.php index 239c139..a5bf538 100644 --- a/controller/visitor.php +++ b/controller/visitor.php @@ -51,49 +51,62 @@ function discoVisitor() // modèle $AllAlbums = new Album($page_actuelle); $AllAlbums->makeFileList(); - $albumsJSON = array_reverse($AllAlbums->readAll()); // lourd - // $albums est un tableau de chaines JSON, - // chacune renferme 3 variables: titre, année, pochette + // contenu de tous les fichiers JSON (= tableau de chaines) + $albumsJSON = $AllAlbums->readAll(); + // noms des fichiers JSON + $albumNamesJSON = $AllAlbums->getFileList(); - // changer les chaines JSON en tableaux + // changer les chaines JSON en tableaux: titre, année, pochette $i = 0; + $annees = array(); foreach($albumsJSON as $oneAlbum) { $albumsJSON[$i] = json_decode($oneAlbum, true); + $annees[$i] = $albumsJSON[$i][1]; + + // ajout des noms des fichiers JSON + $albumsJSON[$i][3] = $albumNamesJSON[$i]; + $i++; } + // tri d'un tableau multidimensionnel + array_multisort($annees, $albumsJSON); // on passe maintenant au contenu HTML - $albumNamesJSON = array_reverse($AllAlbums->getFileNames()); $AllAlbums->setFormat('html'); $AllAlbums->makeFileList(); - $albumNamesHTML = array_reverse($AllAlbums->getFileNames()); - + $albumNamesHTML = $AllAlbums->getFileList(); + // lien vers le HTML ou ancre? // pour chaque album, détecter le fichier html // si non, ne fournir qu'un lien d'ancre pour la liste d'album $i = 0; $avecLien = []; - foreach($albumNamesJSON as $oneAlbum) + $linkDiscoChrono = []; + foreach($albumsJSON as $oneAlbum) { // nom sans extension - $chemin = pathinfo($oneAlbum); + $chemin = pathinfo($oneAlbum[3]); $nomJSONsansExt = $chemin['filename']; - $chemin = pathinfo($albumNamesHTML[0]); - // détection - if(file_exists($chemin['dirname'] . '/' . $nomJSONsansExt . '.html')) + // au cas où la discographie ne contient aucun html + if(!empty($albumNamesHTML)) { - $lienAlbum[$i] = 'album&album_code=' . $nomJSONsansExt . '&album_name=' . $albumsJSON[$i][0]; - $avecLien[$i] = true; + if(file_exists('data/discographie/html/' . $nomJSONsansExt . '.html')) + { + $lienAlbum[$i] = 'album&album_code=' . $nomJSONsansExt . '&album_name=' . $albumsJSON[$i][0]; + $avecLien[$i] = true; + $linkDiscoChrono[$i] = 'linkChrono'; // css + } + else + { + $lienAlbum[$i] = 'discographie#' . $albumsJSON[$i][0]; + $avecLien[$i] = false; + $linkDiscoChrono[$i] = 'noLinkChrono'; // css + } + $i++; } - else - { - $lienAlbum[$i] = 'discographie#' . $albumsJSON[$i][0]; - $avecLien[$i] = false; - } - $i++; } // variables $css, $js et $content @@ -102,6 +115,7 @@ function discoVisitor() require('view/template.php'); } +// page d'un album function album($albumCode, $albumName) { $page_actuelle = 'discographie'; diff --git a/imageAJAX.php b/imageAJAX.php deleted file mode 100644 index 0831986..0000000 --- a/imageAJAX.php +++ /dev/null @@ -1,100 +0,0 @@ - $_FILES['upload']['name'], - 'type' => $_FILES['upload']['type'], - 'tmp_name' => $_FILES['upload']['tmp_name'], - 'error' => $_FILES['upload']['error'], - 'size' => $_FILES['upload']['size'] -); -print_r($upload);*/ - -session_start(); -// et une backdoor de fermée! -if(!isset($_SESSION['admin']) || $_SESSION['admin'] != 1 || !isset($_FILES['upload']) || empty($_FILES['upload'])) -{ - header('Location: index.php?erreur=imageajax'); -} - -// get envoyé avec le javascript -$page = $_GET['page']; - -// déjà fait dans installation.php -if(!file_exists('data/' . $page . '/images')) -{ - mkdir('data/' . $page . '/images', 0777); - chmod('data/' . $page . '/images', 0777); -} - -// taille en Mo à adapter au serveur (2Mo est la valeur par défaut dans le php.ini) -$tailleMax = 2000000; -$erreur = ''; - -// traitement et enregistrement de l'image -if (isset($_FILES['upload']) AND $_FILES['upload']['error'] == 0) // 0 signifie ok -{ - if ($_FILES['upload']['size'] <= $tailleMax ) - { - $infos = pathinfo ($_FILES['upload']['name']); - $extension = $infos['extension']; - $extautorisées = array('jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff'); - // on prend la même liste que celle côté javascript, le SVG est bloqué pour raison de sécurité (javascript à l'intérieur) - if (in_array ($extension, $extautorisées)) - { - move_uploaded_file ($_FILES['upload']['tmp_name'], 'data/' . $page . '/images/' . $_FILES['upload']['name']); - chmod('data/' . $page . '/images/' . $_FILES['upload']['name'], 0666); - } - else{$erreur = 'mauvais format, veuillez utiliser une image comportant un de ces formats: jpg ou jpeg, png, gif, bmp, webp, tiff
le format svg n\'est pas supporté';} - } - else{$erreur = 'fichier trop lourd';} -} -else{$erreur = $_FILES['upload']['error'];} -/* les erreurs retournées avec $_FILES['upload']['error']: -0 pas d'erreur -1 taille du fichier supérieure à la valeur de upload_max_filesize dans le fichier php.ini (par défaut = 2 MO) -2 taille du fichier supérieure à la valeur de MAX_FILE_SIZE dans le formulaire HTML -3 fichier partiellement téléchargé -4 pas de fichier du tout -6 pas de dossier temporaire pour mettre le fichier -7 echec de l'écriture sur le DD -8 envoi arrêté par une extension de PHP mais on ne nous dit pas pourquoi => diagnostic avec la fonction phpinfo() */ - -// nouveau chemin à renvoyer en format json -$chemin = '{"url": "data/' . $page . '/images/' . $_FILES['upload']['name'] . '"}'; -//echo json_encode($chemin); -echo $chemin; diff --git a/index.php b/index.php index d8e0a58..7ffcad7 100644 --- a/index.php +++ b/index.php @@ -54,13 +54,31 @@ require('controller/password.php'); installation(); +// traitement des requêtes AJAX +if(isset($_GET['action']) && isset($_GET['page']) && $_GET['action'] == 'upload_image') +{ + // et une backdoor de fermée! + if(!isset($_SESSION['admin']) || $_SESSION['admin'] != 1 || !isset($_FILES['upload']) || empty($_FILES['upload'])) + { + header('Location: index.php?erreur=image_ajax'); + } + else + { + require('model/Image.php'); + // paramètre "true" parce qu'on reçoit une requête AJAX + $Image = new Image(true); + $Image->upload(); + } + exit; // arrêt ici !! +} + // traitement des POST du ckeditor -// la fonction submitCKeditor n'affiche rien (controller/admin.php n'est pas utilisé) puis redirige sans GET +// la fonction submitCKeditor est "autonome", elle n'affiche rien puis redirige sans GET if(isset($_SESSION['admin']) && $_SESSION['admin'] == 1 && isset($_GET['action']) && $_GET['action'] == 'submit' // trois possibilités: on a un contenu HTML ou JSON ou les deux && ((isset($_POST['contenu']) && $_POST['contenu'] != '') - || (isset($_POST['titre']) && isset($_POST['annee']) && isset($_POST['pochette'])))) + || (isset($_POST['titre']) && isset($_POST['annee'])))) { require('controller/ckeditor.php'); // traitement du POST require('lib/htmlawed/htmLawed.php'); // failles XSS @@ -94,11 +112,7 @@ if(isset($_GET['action']) && isset($_GET['page'])) } } -// le site comporte deux modes: -// le mode visiteur en "lecture seule" utilisant le contrôleur visitor.php -// le mode admin avec droits en "écriture" utilisant le contrôleur admin.php - -// contrôleur des pages en mode visiteur +// contrôleur des pages en mode visiteur (= lecture uniquement) // appelé tout le temps parce que certaines pages (accueil, menu) n'ont pas de version "admin" => à améliorer require('controller/visitor.php'); @@ -111,6 +125,7 @@ if(isset($_GET['page']) && $_GET['page'] != 'menu') // contrôleur des pages en mode admin if(isset($_SESSION['admin']) && $_SESSION['admin'] == 1) { + // contrôleur en mode admin (= lecture/écriture) require('controller/admin.php'); } else diff --git a/model/Classes.php b/model/Classes.php index 1846ce3..97a137a 100644 --- a/model/Classes.php +++ b/model/Classes.php @@ -31,7 +31,7 @@ class AllArticles { return($this->nbArticles); } - public function getFileNames() + public function getFileList() { return($this->files); } @@ -154,21 +154,27 @@ class Album extends OneArticle { // variables //private $fileNameJSON; // même nom en .json - //protected $format = 'json'; // GET // SET // fonctions CRUD - // create public function createVignette($titre, $annee, $pochette) { + $this->format = 'json'; + if($pochette != '') { - // enregistrer le fichier - // retourner une erreur en cas d'échec de l'upload + // télécharger la pochette + require('model/Image.php'); + $Image = new Image(false); + $Image->upload(); + + /*$erreur = $Image->getError(); + if(!empty($erreur)) + {}*/ } $albumJSON = json_encode([$titre, $annee, $pochette]); @@ -192,12 +198,4 @@ class Album extends OneArticle // delete public function delete() {} -} - - - -// note: les pros font de l'hydration -// le code du modèle est orienté objet et "refactorisé" d'une manière précise: -// on utilise un objet pour une chose (des articles, commentaires, etc), -// un objet article contiendrait ainsi tout le nécessaire pour lire, écrire ou modifier un article -// il y a pour ça un programme appelé "doctrine" (inclu dans symphony) qui est capable de lire une base de données et d'en écrire les objets PHP \ No newline at end of file +} \ No newline at end of file diff --git a/model/Image.php b/model/Image.php new file mode 100644 index 0000000..0070b70 --- /dev/null +++ b/model/Image.php @@ -0,0 +1,59 @@ +page = $_GET['page']; + $this->ajax = $ajax; + } + + // GET + public function getError() + { + return($this->erreur); + } + + public function upload() + { + + // traitement et enregistrement de l'image + if (isset($_FILES['upload']) AND $_FILES['upload']['error'] == 0) // 0 signifie ok + { + if ($_FILES['upload']['size'] <= self::MAX_WEIGHT) + { + $infos = pathinfo($_FILES['upload']['name']); + $extension = $infos['extension']; + $extautorisées = array('jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff'); + // on prend la même liste que celle côté javascript, le SVG est bloqué pour raison de sécurité (javascript à l'intérieur) + if (in_array($extension, $extautorisées)) + { + move_uploaded_file($_FILES['upload']['tmp_name'], 'data/' . $this->page . '/images/' . $_FILES['upload']['name']); + chmod('data/' . $this->page . '/images/' . $_FILES['upload']['name'], 0666); + } + else{$this->erreur = 'mauvais format, veuillez utiliser une image comportant un de ces formats: jpg ou jpeg, png, gif, bmp, webp, tiff
le format svg n\'est pas supporté';} + } + else{$this->erreur = 'fichier trop lourd';} + } + else + { + $this->erreur = $_FILES['upload']['error']; + } + + // retour des rêquetes AJAX + if($this->ajax) + { + // nouveau chemin à renvoyer en format json + $chemin = '{"url": "data/' . $this->page . '/images/' . $_FILES['upload']['name'] . '"}'; + //echo json_encode($chemin); + echo $chemin; + } + } +} \ No newline at end of file diff --git a/public/discographie.css b/public/discographie.css index adf5ac0..8b050db 100644 --- a/public/discographie.css +++ b/public/discographie.css @@ -39,7 +39,8 @@ aside div /*padding: 5px;*/ display: none; z-index: 1; /* placer le menu déroulant au dessus */ - background-color: #9fa8d0; + /*background-color: #9fa8d0;*/ + background-color: #a4afd4; } #chronologie p @@ -47,6 +48,29 @@ aside div margin: 8px; } +#chronologie p a:visited +{ + color: blue; +} +#chronologie p a:hover +{ + color: black; +} + +.linkChrono +{ + text-decoration: none; +} +.linkChrono:hover +{ + text-decoration: underline; +} +.noLinkChrono +{ + text-decoration: none; + color: black; +} + /* PC uniquement, pour les smartphones prévoir un clic => :checked et une checkbox => ou la balise select qui crée un menu déroulant */ @@ -129,11 +153,17 @@ a:hover figure figcaption text-decoration: underline; } + /* page dédiée à un album */ #albumHTML { width: 100%; } +.linkAlbumHTML +{ + color: blue; +} + @media screen and (min-width: 700px) { diff --git a/public/donnees_hors_editeur.css b/public/donnees_hors_editeur.css index 8619938..fdb12b5 100644 --- a/public/donnees_hors_editeur.css +++ b/public/donnees_hors_editeur.css @@ -26,13 +26,13 @@ input[type="checkbox"]:checked{border: none; background: #26ab33;} .table td{border: 1px grey solid; padding: 7px; min-width: 30px;} td p{margin: 0px;} -.image{margin: 0px 0px 0px 5px; text-align: center; display: inline-block;} +.image{margin: 0px; text-align: center; display: inline-block;} .image img{max-width: 630px;} .image-style-side{float: right;} .image-style-side img{max-width: 315px;} .image>figcaption{padding: 7px; text-align: center; font-size: small; background-color: #f0f0f0;} -.boutonArticle{clear: both;} +.boutonArticle{clear: both; padding: 10px 0px;} article:after{content: ""; display: block; clear: both;} iframe{min-width: 400px; min-height: 300px; max-width: 1200px; max-height: 900px;} \ No newline at end of file diff --git a/public/melaine.css b/public/melaine.css index 9736147..f7faf99 100644 --- a/public/melaine.css +++ b/public/melaine.css @@ -22,7 +22,6 @@ figure } .boutonArticle { - padding-bottom: 20px; border-bottom: 1px black solid; } .boutonArticle a diff --git a/view/album.php b/view/album.php index 39d180a..921efaf 100644 --- a/view/album.php +++ b/view/album.php @@ -30,6 +30,6 @@ ob_start(); -

Retour à la discographie

+

Retour à la discographie

-

:

+

- -

diff --git a/view/template-formulaires.php b/view/template-formulaires.php index 7eb1bb5..391f729 100644 --- a/view/template-formulaires.php +++ b/view/template-formulaires.php @@ -24,9 +24,9 @@ ob_start();
-
+
- +

-
+ ', + uploadUrl: 'index.php?action=upload_image&page=', // noter qu'il est possible (parce que souhaitable je ne pense pas) d'envoyer une requête AJAX - // en indiquant une adresse "statique" du type: fichier.txt ou .xml, jpg, png, etc + // en indiquant une adresse "statique" du type: fichier.txt ou .xml, jpg, png, etc, // Headers supplémentaires envoyés avec la requête // c'est ici qu'on installe les mécanismes de sécurités comme l'authentification et la protection au CSRF @@ -171,11 +170,8 @@ if($page_actuelle == 'discographie') }, // formats acceptés par défaut: jpeg, png, gif, bmp, webp, tiff // le svg n'est pas dans la liste, pour raison de sécurité apparemment, il parait qu'on peut mettre du javascript à l'intérieur - - // ce plugin a l'intérêt de ne pas imposer l'utilisation de l'extension GD de PHP, - // Reste qu'il sera quand même intéressant de l'ajouter pour le redimensionnement des images trop lourdes, - // quoi que le système d'onglets (limiter l'affichge à 5 ou 10 articles par page) règle aussi le problème d'une autre manière - + // ce plugin est simple (JS pur) et n'oblige pas le serveur à disposer de l'extension GD + // niveau perfs, on garde le choix d'utiler GD ou imagemagick ou un système d'onglets // plugin autosave } ) -- cgit v1.2.3