From b97a68343ec5c4ff1fae25ff5dc41f1a2ce6a17f Mon Sep 17 00:00:00 2001 From: polo Date: Fri, 26 Nov 2021 04:29:10 +0100 Subject: =?UTF-8?q?disco=20modif/suppr=20d=C3=A9sordre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/Security.php | 23 ++++++ controller/admin.php | 26 ++++--- controller/ckeditor.php | 32 ++------ controller/visitor.php | 16 ++-- index.php | 26 +++++-- model/Album.php | 75 ++++++++++++++++++ model/Classes.php | 201 ------------------------------------------------ model/Image.php | 9 +-- model/Page.php | 97 +++++++++++++++++++++++ public/discographie.css | 5 ++ view/discographie.php | 103 +++++++++++++++---------- 11 files changed, 312 insertions(+), 301 deletions(-) create mode 100644 controller/Security.php create mode 100644 model/Album.php delete mode 100644 model/Classes.php create mode 100644 model/Page.php diff --git a/controller/Security.php b/controller/Security.php new file mode 100644 index 0000000..98d2e74 --- /dev/null +++ b/controller/Security.php @@ -0,0 +1,23 @@ +1, // protection contre les élements et attributs dangereux + 'elements'=>'h2, h3, h4, p, br, span, i, strong, u, mark, blockquote, li, ol, ul, a, figure, hr, img, figcaption, table, tbody, tr, td', // paramètre optionnel: les balises non indiquées sont supprimées + 'deny_attribute'=>'id', // gêner le JS hostile + // on garde 'class' et 'style' utilisés par le ckediteur + ); + private static $specHtmLawed = ''; // optionnel: faire qu'un certain élément puisse n'avoir que certains attributs + + public static function secureString($chaine) + { + $chaine = htmLawed($chaine, self::$configHtmLawed, self::$specHtmLawed); + $chaine = trim($chaine); // supprimer espaces, tabulations et sauts de ligne en début et fin de chaine (pour l'entrée de l'éditeur) + return $chaine; + } +} diff --git a/controller/admin.php b/controller/admin.php index ba6fd83..90924e0 100644 --- a/controller/admin.php +++ b/controller/admin.php @@ -9,9 +9,8 @@ function melaineEdit($numArticle, $suppression) $page_actuelle = "melaine"; // modèle - $Article = new OneArticle($page_actuelle); - $Article->makeFileList(); - + $Article = new Page($page_actuelle, 'html'); + // nouvel article if($numArticle == 0) { @@ -22,7 +21,7 @@ function melaineEdit($numArticle, $suppression) { // nom de l'article ciblé (objet et session) $Article->findFileName($numArticle); - $_SESSION['nomFichier'] = $Article->getFileName(); + $_SESSION['nomFichier'] = $Article->fileName; // suppression if($suppression) @@ -62,13 +61,13 @@ function melaineEdit($numArticle, $suppression) require('view/template.php'); } -function discoEdit($numArticle, $suppression) +function discoEdit($numArticle, $albumCode, $suppression) { $page_actuelle = "discographie"; $title = "Discographie"; // modèle - $Album = new OneArticle($page_actuelle); + $Album = new Album($page_actuelle); $Album->makeFileList(); // nouvel album @@ -81,7 +80,7 @@ function discoEdit($numArticle, $suppression) { // nom de l'article ciblé (objet et session) $Album->findFileName($numArticle); - $_SESSION['nomFichier'] = $Album->getFileName(); + $_SESSION['nomFichier'] = $Album->fileName; // suppression if($suppression) @@ -94,7 +93,8 @@ function discoEdit($numArticle, $suppression) else { $title = "Modifier un album"; - $texte = $Album->readOne(); // entrée de l'éditeur + $texte = $Album->readOneAlbum($albumCode); + $vignette = json_decode($Album->getVignette(), true); } } @@ -106,11 +106,10 @@ function discoEdit($numArticle, $suppression) $texte = preparationCKeditor($numArticle, $texte); } - // contenu de tous les fichiers JSON (= tableau de chaines) $albumsJSON = $Album->readAll(); // noms des fichiers JSON - $albumNamesJSON = $Album->getFileList(); + $albumNamesJSON = $Album->fileList; // changer les chaines JSON en tableaux: titre, année, pochette $i = 0; @@ -124,13 +123,15 @@ function discoEdit($numArticle, $suppression) $annees[$i] = $albumsJSON[$i][1]; $i++; } + print_r($albumsJSON); + //exit(); // tri d'un tableau multidimensionnel array_multisort($annees, $albumsJSON); // on passe maintenant au contenu HTML - $Album->setFormat('html'); + $Album->format = 'html'; $Album->makeFileList(); - $albumNamesHTML = $Album->getFileList(); + $albumNamesHTML = $Album->fileList; // lien vers le HTML ou ancre? // pour chaque album, détecter le fichier html @@ -149,6 +150,7 @@ function discoEdit($numArticle, $suppression) if(file_exists('data/discographie/html/' . $nomJSONsansExt . '.html')) { $lienAlbum[$i] = 'album&album_code=' . $nomJSONsansExt . '&album_name=' . $albumsJSON[$i][0]; + $lienBoutonModif[$i] = 'discographie&action=edition&album_code=' . $nomJSONsansExt; $avecLien[$i] = true; } else diff --git a/controller/ckeditor.php b/controller/ckeditor.php index d07f2a2..2b9f168 100644 --- a/controller/ckeditor.php +++ b/controller/ckeditor.php @@ -52,28 +52,15 @@ function submitCKeditor($nomFichier) exit(); } - // sécurité faille XSS avec htmLawed - $configHtmLawed = array( - 'safe'=>1, // protection contre les élements et attributs dangereux - 'elements'=>'h2, h3, h4, p, br, span, i, strong, u, mark, blockquote, li, ol, ul, a, figure, hr, img, figcaption, table, tbody, tr, td', // paramètre optionnel: les balises non indiquées sont supprimées - 'deny_attribute'=>'id', // gêner le JS hostile - // on garde 'class' et 'style' utilisés par le ckediteur - ); - $specHtmLawed = ''; // optionnel: faire qu'un certain élément puisse n'avoir que certains attributs - // de l'éditeur if(isset($_POST['contenu'])) // optionnel pour discographie { - $contenu = $_POST['contenu']; + $contenu = Security::secureString($_POST['contenu']); // récupérer les liens multimedia //require("media.php"); //$contenu = mediaSubmit($contenu); - $contenu = htmLawed($contenu, $configHtmLawed, $specHtmLawed); - // supprimer espaces, tabulations et sauts de ligne en début et fin de chaine (pour l'entrée de l'éditeur) - $contenu = trim($contenu); - // ne pas continuer si la variable est vide (javascript mal supporté ou utilisation de IE, bug?, erreur de l'utilisateur), risque perte de contenu !! if($page != 'discographie' && $contenu == '') { @@ -82,18 +69,11 @@ function submitCKeditor($nomFichier) } } - if($page == 'discographie') { - $titre = $_POST['titre']; - $annee = $_POST['annee']; + $titre = Security::secureString($_POST['titre']); + $annee = Security::secureString($_POST['annee']); $pochette = $_FILES['upload']['name']; - - // des formulaires simples - $titre = htmLawed($titre, $configHtmLawed, $specHtmLawed); - $titre = trim($titre); - $annee = htmLawed($annee, $configHtmLawed, $specHtmLawed); - $annee = trim($annee); // on instancie avec l'enfant de OneArticle $Album = new Album($page); @@ -103,7 +83,7 @@ function submitCKeditor($nomFichier) { // page disco $Album->createVignette($titre, $annee, $pochette); - + // page de l'album if(!empty($_POST['contenu'])) { @@ -125,7 +105,7 @@ function submitCKeditor($nomFichier) // autres pages else { - $Article = new OneArticle($page); + $Article = new Page($page, 'html'); if($_GET['article'] == 0) { @@ -133,7 +113,7 @@ function submitCKeditor($nomFichier) } else { - $Article->setFileName($nomFichier); + $Article->fileName = $nomFichier; $Article->update($contenu); } } diff --git a/controller/visitor.php b/controller/visitor.php index a5bf538..0e79ee2 100644 --- a/controller/visitor.php +++ b/controller/visitor.php @@ -33,8 +33,9 @@ function melaineVisitor() $title = "Mais qui est Melaine Favennec?"; // modèle - $Articles = new AllArticles($page_actuelle); - $Articles->makeFileList(); + $Articles = new Page($page_actuelle, 'html'); + + // récupération de toute la page dans $articles $articles = array_reverse($Articles->readAll()); // lourd // variables $css, $js, $header et $content @@ -53,9 +54,9 @@ function discoVisitor() $AllAlbums->makeFileList(); // contenu de tous les fichiers JSON (= tableau de chaines) - $albumsJSON = $AllAlbums->readAll(); + $albumsJSON = array_reverse($AllAlbums->readAll()); // noms des fichiers JSON - $albumNamesJSON = $AllAlbums->getFileList(); + $albumNamesJSON = $AllAlbums->fileList; // changer les chaines JSON en tableaux: titre, année, pochette $i = 0; @@ -74,9 +75,9 @@ function discoVisitor() array_multisort($annees, $albumsJSON); // on passe maintenant au contenu HTML - $AllAlbums->setFormat('html'); + $AllAlbums->format = 'html'; $AllAlbums->makeFileList(); - $albumNamesHTML = $AllAlbums->getFileList(); + $albumNamesHTML = $AllAlbums->fileList; // lien vers le HTML ou ancre? // pour chaque album, détecter le fichier html @@ -121,8 +122,7 @@ function album($albumCode, $albumName) $page_actuelle = 'discographie'; $title = $albumName; - //$Album = new OneArticle ($page_actuelle); - $album = OneArticle::readOneAlbum($albumCode); + $album = Album::readOneAlbum($albumCode); // simple, efficace // variables $css, $header et $content require('view/album.php'); diff --git a/index.php b/index.php index 7ffcad7..a534ddf 100644 --- a/index.php +++ b/index.php @@ -72,6 +72,7 @@ if(isset($_GET['action']) && isset($_GET['page']) && $_GET['action'] == 'upload_ exit; // arrêt ici !! } + // traitement des POST du ckeditor // la fonction submitCKeditor est "autonome", elle n'affiche rien puis redirige sans GET if(isset($_SESSION['admin']) && $_SESSION['admin'] == 1 @@ -81,8 +82,12 @@ if(isset($_SESSION['admin']) && $_SESSION['admin'] == 1 || (isset($_POST['titre']) && isset($_POST['annee'])))) { require('controller/ckeditor.php'); // traitement du POST - require('lib/htmlawed/htmLawed.php'); // failles XSS - require('model/Classes.php'); // modèle + require('controller/Security.php'); // sécurité des chaines + require('model/Page.php'); // modèle + if($_GET['page'] == 'discographie') + { + require('model/Album.php'); + } // modification if(isset($_SESSION['nomFichier'])) @@ -100,6 +105,7 @@ if(isset($_SESSION['admin']) && $_SESSION['admin'] == 1 unset($_GET['action']); unset($_POST['contenu']); header('Location: index.php?page=' . $_GET['page']); + exit(); } // déconnexion: nettoyer et recharger la page @@ -119,7 +125,11 @@ require('controller/visitor.php'); // utile pour presque toutes les pages if(isset($_GET['page']) && $_GET['page'] != 'menu') { - require('model/Classes.php'); + require('model/Page.php'); + if($_GET['page'] == 'discographie' || $_GET['page'] == 'album') + { + require('model/Album.php'); + } } // contrôleur des pages en mode admin @@ -194,7 +204,11 @@ if(isset($_GET['page'])) // modification if(isset($_GET['album']) && is_numeric($_GET['album']) && $_GET['album'] > 0) { - discoEdit($_GET['album'], 0); + if(!isset($_GET['album_code'])) + { + $_GET['album_code'] = ''; + } + discoEdit($_GET['album'], $_GET['album_code'], 0); } // nouvel article else @@ -204,13 +218,13 @@ if(isset($_GET['page'])) { unset($_SESSION['nomFichier']); } - discoEdit(0, 0); + discoEdit(0, '', 0); } } // suppression else if($_SESSION['admin'] == 1 && isset($_GET['action']) && $_GET['action'] == 'suppression') { - discoEdit($_GET['album'], 1); + discoEdit($_GET['album'], '', 1); } else { diff --git a/model/Album.php b/model/Album.php new file mode 100644 index 0000000..2254c10 --- /dev/null +++ b/model/Album.php @@ -0,0 +1,75 @@ +page = $page; + $this->format = 'json'; // vaut 'html' dans la classe mère + $this->time = time(); + } + + // GET + + // SET + + // fonctions CRUD + + // create + public function createVignette($titre, $annee, $pochette) + { + $this->format = 'json'; + + if($pochette != '') + { + // 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]); + + $nom_fichier = 'data/' . $this->page . '/' . $this->format . '/' . $this->time . '.' . $this->format; + + $fichier = fopen($nom_fichier, 'w'); // w pour créer ou écraser + fputs($fichier, $albumJSON); + fclose($fichier); + chmod($nom_fichier, 0666); + } + + // read + public function getVignette() + { + return(file_get_contents($this->fileName)); + } + + // page de l'album + public static function readOneAlbum($albumCode) + { + return(file_get_contents('data/discographie/html/' . $albumCode . '.html')); + } + + // pour afficher des dates + /*public function getDate($fileNumber) + { + // le 2è paramètre exclut le suffixe .html + $timestamp = basename($this->files[$fileNumber], '.html'); + return getdate($timestamp); + }*/ + + // update + public function updateVignette() + {} + + // delete + public function delete() + { + unlink($this->fileName); + } +} diff --git a/model/Classes.php b/model/Classes.php deleted file mode 100644 index 97a137a..0000000 --- a/model/Classes.php +++ /dev/null @@ -1,201 +0,0 @@ -page = $page; - $this->time = time(); - if($this->page == 'discographie') - { - $this->format = 'json'; - } - } - - // GET - public function getPage() - { - return($this->page); - } - - public function getNbArticles() - { - return($this->nbArticles); - } - public function getFileList() - { - return($this->files); - } - - // SET - public function setFormat($format) - { - $this->format = $format; - } - - // tableaux des noms des fichiers - public function makeFileList() - { - $this->files = glob('data/' . $this->page . '/' . $this->format . '/*.' . $this->format); - //$this->files = glob('*.' . $this->format); - } - /*public function makeFilePath() - {}*/ - - // fonctions CRUD (create - read - update - delete) - - // create - - // read - public function readAll() - { - $i = 0; - $articles = array(); - foreach ($this->files as $oneFile) - { - $articles[$i] = file_get_contents($oneFile); - $i++; - } - //print_r($articles); - return $articles; - } - - // update - - // delete -} - -// article créé ou ciblé pour modification/suppression -class OneArticle extends AllArticles -{ - private $fileName; // correspond à $_SESSION['nomFichier'] - - // GET - public function getFileName() - { - return($this->fileName); - } - - // SET - public function setFileName($nomFichier) // modification - { - $this->fileName = $nomFichier; - } - - public function findFileName($numArticle) // nouvel article - { - $this->fileName = $this->files[$numArticle - 1]; - } - - // fonctions CRUD (create - read - update - delete) - - // create - public function create($content) - { - $format = 'html'; - - // nommer les fichiers avec le timestamp pour: - // - les trier par ordre chronologique - // - rendre quasi impossible d'avoir deux fois le même nom (à la condition de gérer la "concurrence") - $nom_fichier = 'data/' . $this->page . '/' . $format . '/' . $this->time . '.' . $format; - - $fichier = fopen($nom_fichier, 'w'); // w pour créer ou écraser - fputs($fichier, $content); - fclose($fichier); - chmod($nom_fichier, 0666); - } - - // read - public function readOne() - { - return(file_get_contents($this->fileName)); - } - public static function readOneAlbum($albumCode) - { - return(file_get_contents('data/discographie/html/' . $albumCode . '.html')); - } - - // pour afficher des dates - /*public function getDate($fileNumber) - { - // le 2è paramètre exclut le suffixe .html - $timestamp = basename($this->files[$fileNumber], '.html'); - return getdate($timestamp); - }*/ - - // update - public function update($content) - { - $file = fopen($this->fileName, 'w'); // crée ou écrase - fputs($file, $content); - fclose($file); - //chown($this->fileName, 'http'); - chmod($this->fileName, 0666); - } - - // delete - public function delete() - { - unlink($this->fileName); - } -} - - -class Album extends OneArticle -{ - // variables - //private $fileNameJSON; // même nom en .json - - // GET - - // SET - - // fonctions CRUD - // create - public function createVignette($titre, $annee, $pochette) - { - $this->format = 'json'; - - if($pochette != '') - { - // 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]); - - $nom_fichier = 'data/' . $this->page . '/' . $this->format . '/' . $this->time . '.' . $this->format; - - $fichier = fopen($nom_fichier, 'w'); // w pour créer ou écraser - fputs($fichier, $albumJSON); - fclose($fichier); - chmod($nom_fichier, 0666); - } - - // read - public function read() - {} - - // update - public function updateVignette($titre, $annee, $pochette) - {} - - // delete - public function delete() - {} -} \ No newline at end of file diff --git a/model/Image.php b/model/Image.php index 0070b70..efd75cc 100644 --- a/model/Image.php +++ b/model/Image.php @@ -6,7 +6,7 @@ class Image private const MAX_WEIGHT = 2000000; // taille max des images (par défaut 2Mo dans php.ini) private $page; private $ajax = false; - private $erreur = ''; + public $erreur = ''; public function __construct($ajax) { @@ -16,14 +16,11 @@ class Image } // GET - public function getError() - { - return($this->erreur); - } + + // SET public function upload() { - // traitement et enregistrement de l'image if (isset($_FILES['upload']) AND $_FILES['upload']['error'] == 0) // 0 signifie ok { diff --git a/model/Page.php b/model/Page.php new file mode 100644 index 0000000..e03efe1 --- /dev/null +++ b/model/Page.php @@ -0,0 +1,97 @@ +page = $page; + $this->format = $format; + $this->time = time(); + $this->makeFileList(); + } + + // tableaux des noms des fichiers + public function makeFileList() + { + $this->fileList = glob('data/' . $this->page . '/' . $this->format . '/*.' . $this->format); + //$this->files = glob('*.' . $this->format); + } + /*public function makeFilePath() + {}*/ + + // nom d'un fichier + public function findFileName($numArticle) + { + if($numArticle > 0) + { + $this->fileName = $this->fileList[$numArticle - 1]; + } + } + + // GET + + // SET + + // fonctions CRUD (create - read - update - delete) + + // create + public function create($content) + { + $format = 'html'; + + // nommer les fichiers avec le timestamp pour: + // - les trier par ordre chronologique + // - rendre quasi impossible d'avoir deux fois le même nom (à la condition de gérer la "concurrence") + $nom_fichier = 'data/' . $this->page . '/' . $format . '/' . $this->time . '.' . $format; + + $fichier = fopen($nom_fichier, 'w'); // w pour créer ou écraser + fputs($fichier, $content); + fclose($fichier); + chmod($nom_fichier, 0666); + } + + // read + public function readAll() + { + $i = 0; + $articles = array(); + foreach ($this->fileList as $oneFile) + { + $articles[$i] = file_get_contents($oneFile); + $i++; + } + //print_r($articles); + return $articles; + } + public function readOne() + { + return(file_get_contents($this->fileName)); + } + + // update + public function update($content) + { + $file = fopen($this->fileName, 'w'); // crée ou écrase + fputs($file, $content); + fclose($file); + //chown($this->fileName, 'http'); + chmod($this->fileName, 0666); + } + + // delete + public function delete() + { + unlink($this->fileName); + } +} \ No newline at end of file diff --git a/public/discographie.css b/public/discographie.css index 8b050db..f2e9a5b 100644 --- a/public/discographie.css +++ b/public/discographie.css @@ -83,6 +83,10 @@ section { /*border: 1px red solid;*/ } +.flexArticles +{ + /*display: flex;*/ +} h3 { @@ -115,6 +119,7 @@ article { margin: 5px 0px; width: 45%; + /*min-width: 300px;*/ } .vignette diff --git a/view/discographie.php b/view/discographie.php index 19e982b..25b82e1 100644 --- a/view/discographie.php +++ b/view/discographie.php @@ -43,80 +43,99 @@ $header = ob_get_clean(); // variable $content ob_start(); ?> - +
-

Ajouter un album

+

Ajouter un album

-

- - Nouvel album - -

+

+ + Nouvel album + +

= 0; $i--) foreach($albumsJSON as $oneAlbum) { - // modification - if(false) - {} - else - { - // mettre une adresse lorsqu'un fichier html existe + //$i--; + // modification + if(false) + {} + else + { + // mettre une adresse lorsqu'un fichier html existe ?> -
+