source: spip-zone/_plugins_/cvt-upload/trunk/inc/cvtupload.php @ 111802

Last change on this file since 111802 was 111802, checked in by maieul@…, 2 years ago

notice php

File size: 11.7 KB
Line 
1<?php
2
3// Sécurité
4if (!defined('_ECRIRE_INC_VERSION')) {
5        return;
6}
7
8
9/**
10 * Age maximum des fichiers dans le dossier temporaire
11 **/
12if (!defined('_CVTUPLOAD_AGE_MAX')) {
13        define('_CVTUPLOAD_AGE_MAX', 6*3600);
14}
15
16/**
17 * Nombre maximum de fichiers dans le dossier temporaire
18 **/
19if (!defined('_CVTUPLOAD_MAX_FILES')) {
20        define('_CVTUPLOAD_MAX_FILES', 200);
21}
22
23
24include_spip('base/abstract_sql');
25/**
26 * Chercher si des champs fichiers ont été déclarés dans le fichier formulaires/xxx.php
27 * Sert de condition preliminaire pour les pipelines formulaire_charger, formulaire_verifier et formulaire_fond du plugin
28 *
29 * @param string $form
30 *     le nom du formulaire
31 * @param array $args
32 *     - l'id de l'objet
33 *
34 * @return array
35 *     valeur(s) de l'attribut 'name' du ou des input de type file dans formulaires/xxx.html
36 */
37function cvtupload_chercher_fichiers($form, $args) {
38        $fichiers = array();
39
40        // S'il existe une fonction de fichiers dédiée à ce formulaire
41        if ($fonction_fichiers = charger_fonction('fichiers', 'formulaires/'.$form, true)) {
42                $fichiers = call_user_func_array($fonction_fichiers, $args);
43        }
44
45        // Dans tous les cas on applique le pipeline, si un plugin veut ajouter des choses
46        $fichiers = pipeline(
47                'formulaire_fichiers',
48                array('args'=>array('form'=>$form, 'args'=>$args), 'data'=>$fichiers)
49        );
50
51        return $fichiers;
52}
53
54/**
55 * Génére le HTML de chaque fichier déjà uploadé
56 *
57 * @param array $infos_fichiers
58 *              Tableau contenant les informations pour chaque champ de fichier
59 * @return array
60 *              Retourne un tableau avec pour chaque champ une clé contenant le HTML
61 **/
62function cvtupload_generer_html($infos_fichiers = null) {
63        static $html_fichiers = array();
64        // Si on a des infos de fichiers, on va re-générer du HTML
65        if ($infos_fichiers and is_array($infos_fichiers)) {
66                foreach ($infos_fichiers as $champ => $fichier) {
67                        // Si c'est un champ unique
68                        if (isset($fichier['name'])) {
69                                $html_fichiers[$champ] = recuperer_fond(
70                                        'formulaires/inc-cvtupload-fichier',
71                                        array_merge($fichier, array(
72                                                'crochets' => "[$champ]",
73                                                'champ'    => "$champ",
74                                        ))
75                                );
76                        } // Sinon c'est un champ multiple
77                        else {
78                                $html_fichiers[$champ] = array();
79                                foreach ($fichier as $cle => $infos) {
80                                        $html_fichiers[$champ][$cle] = recuperer_fond(
81                                                'formulaires/inc-cvtupload-fichier',
82                                                array_merge($infos, array(
83                                                        'crochets' => "[$champ][$cle]",
84                                                        'champ'    => $champ . "[$cle]",
85                                                ))
86                                        );
87                                }
88                        }
89                }
90        }
91
92        return $html_fichiers;
93}
94
95/**
96 * Déplace un fichier uploadé dans un endroit temporaire et retourne des informations dessus.
97 * @param array $fichier
98 *              Le morceau de $_FILES concernant le ou les fichiers
99 * @param string $repertoire
100 *              Chemin de destination des fichiers
101 * @param string $form
102 *              Formulaire d'où ça vient
103 * @param bool $deplacer
104 *              Mettre a False pour se contenter de copier
105 * @return array
106 *              Retourne un tableau d'informations sur le fichier ou un tableau de tableaux si plusieurs fichiers. Ce tableau est compatible avec l'action "ajouter_un_fichier" de SPIP.
107 **/
108function cvtupload_deplacer_fichier($fichier, $repertoire, $form, $deplacer = true) {
109        $vignette_par_defaut = charger_fonction('vignette', 'inc/');
110        $infos = array();
111        // On commence par nettoyer le dossier
112        cvtupload_nettoyer_repertoire($repertoire);
113
114        // Si on est sur un upload de type fichier unique, on reformate le tableau pour faire comme si on était en fichiers multiples
115        if (!is_array($fichier['name'])) {
116                $fichier_nouveau = array();
117                foreach ($fichier as $champ => $valeur) {
118                        $fichier_nouveau[$champ] = array($valeur);
119                }
120                $fichier = $fichier_nouveau;
121        }
122
123        foreach ($fichier['name'] as $cle => $nom) {
124                // On commence par transformer le nom du fichier pour éviter les conflits, on supprime notamment les accents
125                $nom = preg_replace(',\.\.+,', '.', $nom); // pas de .. dans le nom du doc
126                $nom = preg_replace("/[^.=\w-]+/", "_",
127                        translitteration(preg_replace("/<[^>]*>/", '', $nom)));
128                $nom = strtolower($nom);
129                if (// Si le fichier a bien un nom et qu'il n'y a pas d'erreur associé à ce fichier
130                        ($nom != null)
131                        and ($fichier['error'][$cle] == 0)
132                        // Et qu'on génère bien un nom de fichier aléatoire pour déplacer le fichier
133                        and $chemin_aleatoire = tempnam($repertoire, $form.'_')
134                ) {
135                        $extension = strtolower(pathinfo($fichier['name'][$cle], PATHINFO_EXTENSION));
136                        if (in_array($extension, array('png','jpg','gif'))) {
137                                $chemin_aleatoire .= ".$extension";
138                        }
139                        // Déplacement du fichier vers le dossier de réception temporaire + récupération d'infos
140                        if (deplacer_fichier_upload($fichier['tmp_name'][$cle], $chemin_aleatoire, $deplacer)) {
141                                $infos[$cle]['tmp_name'] = $chemin_aleatoire;
142                                $infos[$cle]['name'] = $nom;
143                                $infos[$cle]['extension'] = $extension;
144                                // si image on fait une copie avec l'extension pour pouvoir avoir l'image réduite en vignette
145                                if (in_array($extension, array('png','jpg','gif'))) {
146                                        $infos[$cle]['vignette'] = $chemin_aleatoire;
147                                } else {
148                                        $infos[$cle]['vignette'] = $vignette_par_defaut($infos[$cle]['extension'], false, true);
149                                }
150                                //On récupère le type MIME du fichier aussi
151                                $infos[$cle]['mime'] = cvt_upload_determiner_mime($fichier['type'][$cle], $infos[$cle]['extension']);
152                                $infos[$cle]['taille'] = $fichier['size'][$cle];
153                                // On stocke des infos sur le formulaire
154                                $infos[$cle]['form'] = $form;
155                                $infos[$cle]['infos_encodees'] = encoder_contexte_ajax($infos[$cle], $form);
156                        }
157                }
158        }
159        if (!is_array($fichier['name'])) {
160                $infos = reset($infos);
161        }
162
163        return $infos;
164}
165
166/**
167 * Modifier $_FILES pour que le nom et le chemin du fichier temporaire
168 * correspondent à ceux qu'on a défini dans cvtupload_deplacer_fichier().
169 * Cela permet aux traitements ultérieurs
170 * de ne pas avoir à se préoccuper de l'emploi ou non de cvtupload.
171 *
172 * @param $infos_fichiers
173 *  Information sur les fichiers tels que déplacés par cvtupload_deplacer_fichier()
174 * @return void
175**/
176function cvtupload_modifier_files($infos_fichiers) {
177        foreach ($infos_fichiers as $champ => $description) {
178                if (isset($description['tmp_name'])) {//Upload unique
179                         $_FILES[$champ] = array();//On surcharge tout la description $_FILES pour ce champ.  Dans tous les cas les infos ont été stockées dans $description
180                         $_FILES[$champ]['name'] = $description['name'];
181                         $_FILES[$champ]['tmp_name'] = $description['tmp_name'];
182                         $_FILES[$champ]['type'] = $description['mime'];
183                         $_FILES[$champ]['error'] = 0; //on fait comme s'il n'y avait pas d'erreur, ce qui n'est pas forcément vrai…
184                         $_FILES[$champ]['size'] = $description['taille'];
185                } else {//Upload multiple
186                        //On surcharge tout la description $_FILES pour ce champ. Dans tous les cas les infos ont été stockées dans $description
187                        if (isset($_FILES[$champ])) {
188                                $old_FILES_champ = $_FILES[$champ];
189                        } else {
190                                $old_FILES_champ = array();
191                        }
192                        $_FILES[$champ]['name'] = array();
193                        $_FILES[$champ]['tmp_name'] = array();
194                        $_FILES[$champ]['type'] = array();
195                        $_FILES[$champ]['error'] = array();
196                        $_FILES[$champ]['size'] = array();
197                        // Et on re-rempli à partir de $description
198                        foreach ($description as $fichier_individuel => $description_fichier_individuel) {
199                                $_FILES[$champ]['name'][$fichier_individuel] = $description_fichier_individuel['name'];
200                                $_FILES[$champ]['tmp_name'][$fichier_individuel] = $description_fichier_individuel['tmp_name'];
201                                $_FILES[$champ]['type'][$fichier_individuel] = $description_fichier_individuel['mime'];
202                                $_FILES[$champ]['error'][$fichier_individuel] = 0; //on fait comme s'il n'y avait pas d'erreur, ce qui n'est pas forcément vrai…
203                                $_FILES[$champ]['size'][$fichier_individuel] = $description_fichier_individuel['taille'];
204                        }
205                        // Si on vient d'envoyer un ou plusieur $champ[] vide, on les rajoute dans notre nouveau $FILES
206                        if (isset($old_FILES_champ['error']) and is_array($old_FILES_champ['error'])) {
207                                foreach ($old_FILES_champ['error'] as $id_fichier_individuel => $error_fichier_individuel){
208                                        if ($error_fichier_individuel!=0 and !isset($infos_fichiers[$champ][$id_fichier_individuel])){//Uniquement les erreurs
209                                                $_FILES[$champ]['name'][$id_fichier_individuel] = $old_FILES_champ['name'][$id_fichier_individuel];
210                                                $_FILES[$champ]['tmp_name'][$id_fichier_individuel] = $old_FILES_champ['tmp_name'][$id_fichier_individuel];
211                                                $_FILES[$champ]['type'][$id_fichier_individuel] = $old_FILES_champ['type'][$id_fichier_individuel];
212                                                $_FILES[$champ]['error'][$id_fichier_individuel] = $old_FILES_champ['error'][$id_fichier_individuel];
213                                                $_FILES[$champ]['size'][$id_fichier_individuel] = $old_FILES_champ['size'][$id_fichier_individuel];
214                                        }
215                                }
216                        }
217                        // On remet de l'ordre dans champ dans chaque tableau correspondant à une propriété de $_FILES, histoire d'avoir 0,1,2,3 et pas 3,1,0,2
218                        foreach ($_FILES[$champ] as $propriete => $valeurs_propriete) {
219                                ksort($valeurs_propriete);
220                                $_FILES[$champ][$propriete] = $valeurs_propriete;
221                        }
222                }
223        }
224}
225
226/**
227 * Nettoyer $_FILES pour effacer les entrées dont on a vérifié qu'elle ne répondaient pas à certains critères
228 *
229 * @param string $champ
230 *      Le nom du champ concerné dans $_FILES
231 * @param string[]|string $erreurs
232 *      Si un upload multiple, un tableau des $erreurs avec comme clés les numéros des fichiers à supprimer dans $_FILES[$champ]
233 *      Si un upload unique, une chaîne, qui si non vide, indique qu'il faut effacer le $_FILE[$champ]
234 * @return void
235**/
236function cvtupload_nettoyer_files_selon_erreurs($champ, $erreurs) {
237        if (is_array($erreurs)) { // cas d'upload multiple
238                foreach ($erreurs as $cle => $erreur) {
239                        foreach ($_FILES[$champ] as $propriete => $valeur) {
240                                unset($_FILES[$champ][$propriete][$cle]);
241                        }
242                }
243        } elseif ($erreurs!='') { // cas d'upload unique avec erreur
244                unset($_FILES[$champ]);
245        }
246}
247
248/**
249 * Détermine un MIME lorsque les informations de PHP sont imprécises.
250 * Par exemple PHP considère qu'un fichier .tex est de MIME application/octet-stream
251 * Ce qui n'est absolument pas utilse
252 * @param string $mime_suppose
253 * @param string $extension
254 * @return string $mime_trouve
255**/
256function cvt_upload_determiner_mime($mime_suppose, $extension) {
257        if (!in_array($mime_suppose, array('text/plain', '', 'application/octet-stream'))) { // si on a un mime précis, on le renvoie, tout simplement
258                return $mime_suppose;
259        }
260        $mime_spip = sql_getfetsel('mime_type', 'spip_types_documents', 'extension='.sql_quote($extension));
261        if ($mime_spip) {
262                return $mime_spip;
263        } else {
264                return $mime_suppose;
265        }
266}
267
268/**
269 * Nettoyer un répertoire suivant l'age et le nombre de ses fichiers
270 *
271 * @param string $repertoire
272 *              Répertoire à nettoyer
273 * @param int $age_max
274 *              Age maxium des fichiers en seconde
275 * @param int $max_files
276 *              Nombre maximum de fichiers dans le dossier
277 * @return void
278 **/
279function cvtupload_nettoyer_repertoire($repertoire, $age_max = _CVTUPLOAD_AGE_MAX, $max_files = _CVTUPLOAD_MAX_FILES) {
280        include_spip('inc/flock');
281
282        // Si on entre bien dans le répertoire
283        if ($ressource_repertoire = opendir($repertoire)) {
284                $fichiers = array();
285
286                // On commence par supprimer les plus vieux
287                while ($fichier = readdir($ressource_repertoire)) {
288                        if (!in_array($fichier, array('.', '..', '.ok'))) {
289                                $chemin_fichier = $repertoire.$fichier;
290
291                                if (is_file($chemin_fichier) and !jeune_fichier($chemin_fichier, $age_max)) {
292                                        supprimer_fichier($chemin_fichier);
293                                } else {
294                                        $fichiers[@filemtime($chemin_fichier).'_'.rand()] = $chemin_fichier;
295                                }
296                        }
297                }
298
299                // On trie les fichiers par ordre de leur date
300                ksort($fichiers);
301
302                // Puis s'il reste trop de fichiers, on supprime le surplus
303                $nb_fichiers = count($fichiers);
304                if ($nb_fichiers > $max_files) {
305                        $nb_a_supprimer = $nb_fichiers - $max_files - 1;
306
307                        while ($nb_a_supprimer) {
308                                $fichier = array_shift($fichiers);
309                                supprimer_fichier($fichier);
310                                $nb_a_supprimer--;
311                        }
312                }
313        }
314}
Note: See TracBrowser for help on using the repository browser.