summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--controller/Security.php44
-rw-r--r--controller/ajax.php10
-rw-r--r--controller/ckeditor.php28
-rw-r--r--index.php2
-rw-r--r--model/Album.php31
-rw-r--r--model/Article.php2
-rw-r--r--model/Image.php37
-rw-r--r--public/css/discographie.css2
-rw-r--r--public/file_upload.js13
-rw-r--r--view/album.php1
-rw-r--r--view/articlesContent.php9
-rw-r--r--view/discographie.php10
-rw-r--r--view/template-formulaires.php2
13 files changed, 122 insertions, 69 deletions
diff --git a/controller/Security.php b/controller/Security.php
index 39205c1..3475a8d 100644
--- a/controller/Security.php
+++ b/controller/Security.php
@@ -20,6 +20,50 @@ class Security
20 $chaine = trim($chaine); // supprimer espaces, tabulations et sauts de ligne en début et fin de chaine (pour l'entrée de l'éditeur) 20 $chaine = trim($chaine); // supprimer espaces, tabulations et sauts de ligne en début et fin de chaine (pour l'entrée de l'éditeur)
21 return $chaine; 21 return $chaine;
22 } 22 }
23
24 public static function secureFileName(string $chaine): string
25 {
26 // sécuriser un nom avec chemin avec basename?
27 //$chaine = basename($chaine);
28
29 /*
30 - caractères interdits sous windows / \ : * ? " < > |
31 - mac autorise les /
32 - mac interdit :
33 - linux autorise tout sauf les /
34 - imagemagick ne supporte pas les :
35
36 - 'espace' fonctionne
37 - / remplacé par firefox en :
38 - \ retire ce qui est devant le \
39 - * fonctionne
40 - ? permet le téléchargement mais pas l'affichage
41 - " ne fonctionne pas, remplacé par %22, filtrer %22
42 - < > fonctionnent
43 - | fonctionne
44 - = fonctionne, mais je filtre parce qu'on en trouve dans une URL
45 - ' ` fonctionnent
46 - % fonctionne
47 - (){}[] fonctionnent
48 - ^ fonctionne
49 - # ne fonctionne pas
50 - ~ fonctionne
51 - & fonctionne
52 */
53
54 // => on remplace tout par des _
55 // filtrer / et \ semble inutile
56
57 $cibles = [' ', '/', '\\', ':', '*', '?', '<', '>', '|', '=', "'", '`', '"', '%22', '#'];
58 return(str_replace($cibles, '_', $chaine));
59
60 // les problèmes avec \ persistent !!
61 // => javascript
62 // malheureusement document.getElementById('upload').files[0].name = chaine; ne marche pas! interdit!
63 // solution?
64 // créer une copie du fichier et l'envoyer à la place
65 // envoyer le nom souhaité au serveur en AJAX puis renommer avec PHP
66 }
23} 67}
24 68
25// erreurs à la création des mots de passe 69// erreurs à la création des mots de passe
diff --git a/controller/ajax.php b/controller/ajax.php
index ed840cd..9272dea 100644
--- a/controller/ajax.php
+++ b/controller/ajax.php
@@ -17,7 +17,8 @@ if(isset($_GET['action']) && isset($_GET['page']) && $_GET['action'] == 'upload_
17 else 17 else
18 { 18 {
19 require('model/Image.php'); 19 require('model/Image.php');
20 // vaut "true" parce qu'on reçoit une requête AJAX 20 require('controller/Security.php');
21 // paramètre "true" parce qu'on reçoit une requête AJAX
21 $Image = new Image($_GET['page'], $_GET['page'], true); 22 $Image = new Image($_GET['page'], $_GET['page'], true);
22 // les données des albums sont dans le dossier discographie 23 // les données des albums sont dans le dossier discographie
23 if($_GET['page'] === 'album') 24 if($_GET['page'] === 'album')
@@ -25,12 +26,15 @@ if(isset($_GET['action']) && isset($_GET['page']) && $_GET['action'] == 'upload_
25 $Image->setFolder('discographie'); 26 $Image->setFolder('discographie');
26 } 27 }
27 28
28 $Image->upload(); 29 $nomFichier = Security::secureFileName($_FILES['upload']['name']);
30 $Image->setFileName($nomFichier);
31 $Image->setThumbnailWidth(630);
29 32
33 $Image->upload();
30 echo($Image->reponseAjax); // attendu par l'éditeur 34 echo($Image->reponseAjax); // attendu par l'éditeur
31 // attention ici aux var_dump() ou echo() qui trainent 35 // attention ici aux var_dump() ou echo() qui trainent
32 36
33 $Image->makeThumbnail(630); // => taille de la <div> 37 $Image->makeThumbnail(); // => taille de la <div>
34 // on pourra l'agrandir en cliquant dessus 38 // on pourra l'agrandir en cliquant dessus
35 // code exécuté alors que l'utilisateur récupère la main 39 // code exécuté alors que l'utilisateur récupère la main
36 } 40 }
diff --git a/controller/ckeditor.php b/controller/ckeditor.php
index 4d4639d..3765261 100644
--- a/controller/ckeditor.php
+++ b/controller/ckeditor.php
@@ -66,7 +66,7 @@ function submitCKeditor()
66 if($_GET['page'] === 'discographie' || $_GET['page'] === 'album') 66 if($_GET['page'] === 'discographie' || $_GET['page'] === 'album')
67 { 67 {
68 $Album = new Album($_GET['page'], 'discographie'); 68 $Album = new Album($_GET['page'], 'discographie');
69 69
70 // on retrouve les données si nécessaire 70 // on retrouve les données si nécessaire
71 if(isset($_SESSION['file_code']) && $_SESSION['file_code'] != '') 71 if(isset($_SESSION['file_code']) && $_SESSION['file_code'] != '')
72 { 72 {
@@ -76,10 +76,28 @@ function submitCKeditor()
76 76
77 $nouveauTitre = Security::secureString($_POST['titre']); 77 $nouveauTitre = Security::secureString($_POST['titre']);
78 $nouvelleAnnee = Security::secureString($_POST['annee']); 78 $nouvelleAnnee = Security::secureString($_POST['annee']);
79
80 // enregistrement de l'image
79 if(!empty($_FILES['upload']['name'])) 81 if(!empty($_FILES['upload']['name']))
80 { 82 {
81 $nouvellePochette = $_FILES['upload']['name']; 83 require('model/Image.php');
82 $nouvellePochetteMini = pathinfo($_FILES['upload']['name'], PATHINFO_FILENAME) . '-mini.' . pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION); 84 $Image = new Image($_GET['page'], 'discographie', false);
85 $nomFichier = Security::secureFileName($_FILES['upload']['name']);
86 $Image->setFileName($nomFichier);
87 $Image->setThumbnailWidth(201);
88
89 $nouvellePochette = $nomFichier;
90 /*$nouvellePochetteMini = pathinfo($_FILES['upload']['name'], PATHINFO_FILENAME) . '-mini.' . pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION);*/
91 $nouvellePochetteMini = pathinfo($nomFichier, PATHINFO_FILENAME) . '-mini.' . pathinfo($nomFichier, PATHINFO_EXTENSION);
92
93 $Image->upload();
94 $Image->makeThumbnail();
95
96 $erreur = $Image->erreur;
97 if(!empty($erreur))
98 {
99 // remettre pochette précédente si il y a
100 }
83 } 101 }
84 else 102 else
85 { 103 {
@@ -89,11 +107,11 @@ function submitCKeditor()
89 107
90 //var_dump($nouvellePochetteMini); die(); 108 //var_dump($nouvellePochetteMini); die();
91 109
92 // enregistrement nouvel album 110 // enregistrement du texte JSON
93 if(!isset($_SESSION['file_code']) || $_SESSION['file_code'] == '') 111 if(!isset($_SESSION['file_code']) || $_SESSION['file_code'] == '')
94 { 112 {
95 // page disco 113 // page disco
96 $Album->createVignette($nouveauTitre, $nouvelleAnnee, $nouvellePochette, $nouvellePochetteMini); 114 $Album->createVignetteJSON($nouveauTitre, $nouvelleAnnee, $nouvellePochette, $nouvellePochetteMini);
97 115
98 // page de l'album 116 // page de l'album
99 if(!empty($_POST['contenu'])) 117 if(!empty($_POST['contenu']))
diff --git a/index.php b/index.php
index 4e2de59..1888704 100644
--- a/index.php
+++ b/index.php
@@ -37,7 +37,7 @@ if(isset($_SESSION['admin']) && $_SESSION['admin'] == 1
37 || (isset($_POST['titre']) && isset($_POST['annee'])))) 37 || (isset($_POST['titre']) && isset($_POST['annee']))))
38{ 38{
39 require('controller/ckeditor.php'); // traitement du POST 39 require('controller/ckeditor.php'); // traitement du POST
40 require('controller/Security.php'); // sécurité des chaines 40 require('controller/Security.php'); // HTML + nom de fichier
41 require('model/Article.php'); 41 require('model/Article.php');
42 42
43 if($_GET['page'] === 'discographie' || $_GET['page'] === 'album') 43 if($_GET['page'] === 'discographie' || $_GET['page'] === 'album')
diff --git a/model/Album.php b/model/Album.php
index d2537f7..801afef 100644
--- a/model/Album.php
+++ b/model/Album.php
@@ -39,25 +39,10 @@ class Album extends Article
39 // fonctions CRUD 39 // fonctions CRUD
40 40
41 // create 41 // create
42 public function createVignette($titre, $annee, $pochette, $pochetteMini) 42 public function createVignetteJSON($titre, $annee, $pochette, $pochetteMini)
43 { 43 {
44 //$this->format = 'json'; 44 //$this->format = 'json';
45 45
46 if($pochette != '')
47 {
48 //echo($pochette); die(); // = nom sans chemin
49
50 // télécharger la pochette
51 require('model/Image.php');
52 $Image = new Image($this->page, $this->folder, false);
53 $Image->upload();
54 $Image->makeThumbnail(201);
55
56 $erreur = $Image->erreur;
57 if(!empty($erreur))
58 {}
59 }
60
61 $albumJSON = json_encode([$titre, $annee, $pochette, $pochetteMini]); 46 $albumJSON = json_encode([$titre, $annee, $pochette, $pochetteMini]);
62 //var_dump($albumJSON); die(); 47 //var_dump($albumJSON); die();
63 48
@@ -171,20 +156,6 @@ class Album extends Article
171 $pochette = $albumJSON[2]; 156 $pochette = $albumJSON[2];
172 $pochetteMini = $albumJSON[3]; 157 $pochetteMini = $albumJSON[3];
173 } 158 }
174 else
175 {
176 // télécharger la pochette
177 require('model/Image.php');
178 $Image = new Image($this->page, $this->folder, false);
179 $Image->upload();
180 $Image->makeThumbnail(201);
181 $erreur = $Image->erreur;
182 if(!empty($erreur))
183 {
184 // remettre pochette d'avant
185 //$pochette
186 }
187 }
188 159
189 $albumJSON = json_encode([$titre, $annee, $pochette, $pochetteMini]); 160 $albumJSON = json_encode([$titre, $annee, $pochette, $pochetteMini]);
190 161
diff --git a/model/Article.php b/model/Article.php
index 0e8db61..d94ba01 100644
--- a/model/Article.php
+++ b/model/Article.php
@@ -198,8 +198,6 @@ class Article
198 fputs($file, $content); 198 fputs($file, $content);
199 fclose($file); 199 fclose($file);
200 chmod($fileName, 0666); 200 chmod($fileName, 0666);
201
202
203 } 201 }
204 202
205 // read 203 // read
diff --git a/model/Image.php b/model/Image.php
index d71b96a..dbdad62 100644
--- a/model/Image.php
+++ b/model/Image.php
@@ -11,6 +11,8 @@ class Image
11 11
12 public $path; 12 public $path;
13 public $pathMini; 13 public $pathMini;
14 public $FileName; // après sécurisation
15 public $width;
14 public $pathInfos; 16 public $pathInfos;
15 public $erreur; 17 public $erreur;
16 18
@@ -33,20 +35,31 @@ class Image
33 $this->pathMini = 'data/' . $this->folder . '/images-mini/'; 35 $this->pathMini = 'data/' . $this->folder . '/images-mini/';
34 } 36 }
35 37
38 public function setFileName($fileName)
39 {
40 $this->FileName = $fileName;
41 }
42
43 public function setThumbnailWidth($width)
44 {
45 $this->width = $width;
46 }
36 47
37 public function upload() 48 public function upload()
38 { 49 {
39 // traitement et enregistrement de l'image 50 // traitement et enregistrement de l'image
40 if (isset($_FILES['upload']) AND $_FILES['upload']['error'] == 0) // 0 signifie ok 51 if (isset($_FILES['upload']) AND $_FILES['upload']['error'] == 0) // 0 signifie ok
41 { 52 {
42 $this->pathInfos = pathinfo($_FILES['upload']['name']); 53 //$this->pathInfos = pathinfo($_FILES['upload']['name']);
54 $this->pathInfos = pathinfo($this->FileName);
55
43 $extension = $this->pathInfos['extension']; 56 $extension = $this->pathInfos['extension'];
44 $extautorisées = array('jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff'); 57 $extautorisées = array('jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff');
45 // on prend la même liste que celle côté javascript, le SVG est bloqué pour raison de sécurité (javascript à l'intérieur) 58 // on prend la même liste que celle côté javascript, le SVG est bloqué pour raison de sécurité (javascript à l'intérieur)
46 if (in_array($extension, $extautorisées)) 59 if (in_array($extension, $extautorisées))
47 { 60 {
48 move_uploaded_file($_FILES['upload']['tmp_name'], $this->path . $_FILES['upload']['name']); 61 move_uploaded_file($_FILES['upload']['tmp_name'], $this->path . $this->FileName);
49 chmod($this->path . $_FILES['upload']['name'], 0666); 62 chmod($this->path . $this->FileName, 0666);
50 } 63 }
51 else 64 else
52 { 65 {
@@ -63,24 +76,24 @@ class Image
63 if($this->ajax && empty($Image->erreur)) 76 if($this->ajax && empty($Image->erreur))
64 { 77 {
65 // chemin en JSON attendu par l'éditeur 78 // chemin en JSON attendu par l'éditeur
66 $this->reponseAjax = '{"url": "data/' . $this->folder . '/images/' . $_FILES['upload']['name'] . '"}'; 79 $this->reponseAjax = '{"url": "data/' . $this->folder . '/images/' . $this->FileName . '"}';
67 } 80 }
68 } 81 }
69 82
70 // miniatures des photos 83 // miniatures des photos
71 public function makeThumbnail($largeur) 84 public function makeThumbnail()
72 { 85 {
73 global $imageLibrary; 86 global $imageLibrary;
74 87
75 if($imageLibrary == 'imagick') 88 if($imageLibrary == 'imagick')
76 { 89 {
77 $Image = new Imagick($this->path . $_FILES['upload']['name']); 90 $Image = new Imagick($this->path . $this->FileName);
78 91
79 $source = $Image->getImageGeometry(); 92 $source = $Image->getImageGeometry();
80 if($source['width'] > $largeur) 93 if($source['width'] > $this->width)
81 { 94 {
82 // 0 signifie qu'on conserve les proportions 95 // 0 signifie qu'on conserve les proportions
83 $Image->thumbnailImage($largeur, 0); 96 $Image->thumbnailImage($this->width, 0);
84 } 97 }
85 98
86 // écriture dans un fichier 99 // écriture dans un fichier
@@ -89,18 +102,18 @@ class Image
89 elseif($imageLibrary == 'gd') 102 elseif($imageLibrary == 'gd')
90 { 103 {
91 // cette fonction fonctionne pour tous les formats 104 // cette fonction fonctionne pour tous les formats
92 $source = imagecreatefromstring(file_get_contents($this->path . $_FILES['upload']['name'])); 105 $source = imagecreatefromstring(file_get_contents($this->path . $this->FileName));
93 $nomMiniImage = $this->pathMini . $this->pathInfos['filename'] . '-mini.jpg'; 106 $nomMiniImage = $this->pathMini . $this->pathInfos['filename'] . '-mini.jpg';
94 $forme = imagesy($source) / imagesx($source); 107 $forme = imagesy($source) / imagesx($source);
95 108
96 if(imagesx($source) > $largeur) 109 if(imagesx($source) > $this->width)
97 { 110 {
98 // créer un rectangle noir 111 // créer un rectangle noir
99 $destination = imagecreatetruecolor($largeur, $largeur * $forme); 112 $destination = imagecreatetruecolor($this->width, $this->width * $forme);
100 113
101 // sélectionne un rectangle dans l'image source 114 // sélectionne un rectangle dans l'image source
102 // et le place dans un rectangle dans la nouvelle 115 // et le place dans un rectangle dans la nouvelle
103 imagecopyresampled($destination, $source, 0, 0, 0, 0, $largeur, $largeur * $forme, imagesx($source), imagesy($source)); 116 imagecopyresampled($destination, $source, 0, 0, 0, 0, $this->width, $this->width * $forme, imagesx($source), imagesy($source));
104 117
105 // envoie l'image dans un fichier 118 // envoie l'image dans un fichier
106 imagejpeg($destination, $nomMiniImage); 119 imagejpeg($destination, $nomMiniImage);
diff --git a/public/css/discographie.css b/public/css/discographie.css
index 31df03e..38ca683 100644
--- a/public/css/discographie.css
+++ b/public/css/discographie.css
@@ -25,7 +25,7 @@ aside div
25 25
26#bouton_chronologie 26#bouton_chronologie
27{ 27{
28 width: 300px; 28 width: 250px;
29 text-align: center; 29 text-align: center;
30 font-size: large; 30 font-size: large;
31 font-weight: bold; 31 font-weight: bold;
diff --git a/public/file_upload.js b/public/file_upload.js
index 88a61a8..4102828 100644
--- a/public/file_upload.js
+++ b/public/file_upload.js
@@ -4,6 +4,19 @@
4// ce fichier est "caché", le serveur ne l'envoit qu'à 4// ce fichier est "caché", le serveur ne l'envoit qu'à
5// un utilisateur connecté et sur la page "restauration" 5// un utilisateur connecté et sur la page "restauration"
6 6
7// retirer antislash dans le nom à l'upload d'une image
8/*function filtrerAntislash()
9{
10 var fileInfos = getFileInfo('upload'); // = ID
11 var chaine = fileInfos.name.replace('\\', '_');
12 alert(document.getElementById('upload').files[0].name);
13
14 // ne marche pas, "name" est en lecture seule
15 document.getElementById('upload').files[0].name = chaine;
16 alert(document.getElementById('upload').files[0].name);
17 //return chaine;
18}*/
19
7// -> input file onchange 20// -> input file onchange
8function sendFileSize() 21function sendFileSize()
9{ 22{
diff --git a/view/album.php b/view/album.php
index f8879aa..fe5d8c2 100644
--- a/view/album.php
+++ b/view/album.php
@@ -14,6 +14,7 @@ $css = ob_get_clean();
14ob_start(); 14ob_start();
15?> 15?>
16 <script type="text/javascript" src="public/main.js" ></script> 16 <script type="text/javascript" src="public/main.js" ></script>
17 <!-- <script type="text/javascript" src="public/file_upload.js" ></script> -->
17<?php 18<?php
18if(isset($_GET['action']) && $_GET['action'] === 'edition') 19if(isset($_GET['action']) && $_GET['action'] === 'edition')
19{ 20{
diff --git a/view/articlesContent.php b/view/articlesContent.php
index a03b39d..33e50a4 100644
--- a/view/articlesContent.php
+++ b/view/articlesContent.php
@@ -15,7 +15,6 @@ if($_SESSION['admin'] == 1)
15<?php 15<?php
16 echo($editeurHTML); // injection de template-formulaires.php 16 echo($editeurHTML); // injection de template-formulaires.php
17 } 17 }
18
19 // bouton 18 // bouton
20 else 19 else
21 { 20 {
@@ -71,17 +70,11 @@ for($i = 0; $i < $Articles->fileListCount; $i++)
71 { 70 {
72 // on pourrait utiliser ici le timedate, ou encore le nom de l'article 71 // on pourrait utiliser ici le timedate, ou encore le nom de l'article
73?> 72?>
74 <h3>Modification d'un article</h3> 73 <h3>Modification d'un article</h3>
75<?php 74<?php
76 echo($editeurHTML); // injection de template-editor.php 75 echo($editeurHTML); // injection de template-editor.php
77?> 76?>
78 </div> 77 </div>
79 <p class="boutonArticle" >
80 <a class="infobulle" href="<?= $lien_partage ?>" >
81 <img src="public/icone_partager.png" onclick="copierDansPressePapier('<?= $host ?>/<?= $lien_partage ?>')" >
82 <button>Partager</button>
83 </a>
84 </p>
85<?php 78<?php
86 } 79 }
87 80
diff --git a/view/discographie.php b/view/discographie.php
index 8cefa18..8d82679 100644
--- a/view/discographie.php
+++ b/view/discographie.php
@@ -14,6 +14,7 @@ $css = ob_get_clean();
14ob_start(); 14ob_start();
15?> 15?>
16 <script type="text/javascript" src="public/main.js" ></script> 16 <script type="text/javascript" src="public/main.js" ></script>
17 <!-- <script type="text/javascript" src="public/file_upload.js" ></script> -->
17<?php 18<?php
18if(isset($_GET['action']) && $_GET['action'] === 'edition') 19if(isset($_GET['action']) && $_GET['action'] === 'edition')
19{ 20{
@@ -149,13 +150,8 @@ for($i = 0; $i < $Albums->fileListCount; $i++)
149 // boutons 150 // boutons
150 if($_SESSION['admin'] == 1) 151 if($_SESSION['admin'] == 1)
151 { 152 {
152 /*$lien_partage = 'index.php?page=' . $page . '#' . $Albums->fileList[$i]['fileCode']; 153?>
153 global $host;*/
154 ?>
155 <p> 154 <p>
156 <!-- <a class="infobulle" href="<?= $lien_partage ?>" >
157 <img src="public/icone_partager.png" onclick="copierDansPressePapier('<?= $host ?>/<?= $lien_partage ?>')" >
158 </a> -->
159 <a class="infobulle" href="index.php?<?= $lienBoutonModif[$i] ?>" > 155 <a class="infobulle" href="index.php?<?= $lienBoutonModif[$i] ?>" >
160 <img src="public/icone_modifier.png" > 156 <img src="public/icone_modifier.png" >
161 <button>Modifier</button> 157 <button>Modifier</button>
@@ -173,7 +169,7 @@ for($i = 0; $i < $Albums->fileListCount; $i++)
173 <button>Supprimer</button> 169 <button>Supprimer</button>
174 </a> 170 </a>
175 </p> 171 </p>
176 <?php 172<?php
177 } 173 }
178 } 174 }
179?> 175?>
diff --git a/view/template-formulaires.php b/view/template-formulaires.php
index 0ffbba3..fc73a27 100644
--- a/view/template-formulaires.php
+++ b/view/template-formulaires.php
@@ -33,6 +33,7 @@ ob_start();
33 <label for="annee" >Année</label> 33 <label for="annee" >Année</label>
34 <input type="text" name="annee" value="<?= $vignette[1] ?>" ><br /> 34 <input type="text" name="annee" value="<?= $vignette[1] ?>" ><br />
35 <label for="pochette" >Pochette du disque (jpg ou png) : <?= $vignette[2] ?></label><br/> 35 <label for="pochette" >Pochette du disque (jpg ou png) : <?= $vignette[2] ?></label><br/>
36 <!-- <input type="file" id="upload" name="upload" > -->
36 <input type="file" name="upload" ><br /><br /><br /> 37 <input type="file" name="upload" ><br /><br /><br />
37<?php 38<?php
38$inputsAlbum = ob_get_clean(); 39$inputsAlbum = ob_get_clean();
@@ -52,6 +53,7 @@ if($page === 'discographie' || $page === 'album')
52?> 53?>
53 54
54 <textarea id="editor" name="contenu" ></textarea> 55 <textarea id="editor" name="contenu" ></textarea>
56 <!-- <input class="boutonSubmitEditeur" type="submit" value="Valider" onclick="filtrerAntislash();"/> -->
55 <input class="boutonSubmitEditeur" type="submit" value="Valider" /> 57 <input class="boutonSubmitEditeur" type="submit" value="Valider" />
56 <a class="boutonAnnuler" href="index.php?page=<?= $lienBoutonAnnuler ?>" > 58 <a class="boutonAnnuler" href="index.php?page=<?= $lienBoutonAnnuler ?>" >
57 <input type="button" value="Annuler" /></a> 59 <input type="button" value="Annuler" /></a>