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

Last change on this file since 111801 was 111801, checked in by maieul@…, 23 months ago

Si le champ est pour un envoi de fichier unique, il faut adapter le
traitement lors du déplacement du fichier dans le dossier temporaire.
Pour simplifier le code, on transforme la description du fichier envoyé
pour faire comme si c'était un tableau de fichier.

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'])) {
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.