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

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

Perf issue : suppression du fichier options qui ne sert a rien et deplacement des define dans inc/cvtupload qui est le seul a les utiliser

File size: 11.4 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
112        // On commence par nettoyer le dossier
113        cvtupload_nettoyer_repertoire($repertoire);
114        foreach (is_array($fichier['name'])?$fichier['name']:array($fichier['name']) as $cle => $nom) {
115                // On commence par transformer le nom du fichier pour éviter les conflits, on supprime notamment les accents
116                $nom = preg_replace(',\.\.+,', '.', $nom); // pas de .. dans le nom du doc
117                $nom = preg_replace("/[^.=\w-]+/", "_",
118                        translitteration(preg_replace("/<[^>]*>/", '', $nom)));
119                $nom = strtolower($nom);
120                if (// Si le fichier a bien un nom et qu'il n'y a pas d'erreur associé à ce fichier
121                        ($nom != null)
122                        and ($fichier['error'][$cle] == 0)
123                        // Et qu'on génère bien un nom de fichier aléatoire pour déplacer le fichier
124                        and $chemin_aleatoire = tempnam($repertoire, $form.'_')
125                ) {
126                        $extension = strtolower(pathinfo($fichier['name'][$cle], PATHINFO_EXTENSION));
127                        if (in_array($extension, array('png','jpg','gif'))) {
128                                $chemin_aleatoire .= ".$extension";
129                        }
130                        // Déplacement du fichier vers le dossier de réception temporaire + récupération d'infos
131                        if (deplacer_fichier_upload($fichier['tmp_name'][$cle], $chemin_aleatoire, $deplacer)) {
132                                $infos[$cle]['tmp_name'] = $chemin_aleatoire;
133                                $infos[$cle]['name'] = $nom;
134                                $infos[$cle]['extension'] = $extension;
135                                // si image on fait une copie avec l'extension pour pouvoir avoir l'image réduite en vignette
136                                if (in_array($extension, array('png','jpg','gif'))) {
137                                        $infos[$cle]['vignette'] = $chemin_aleatoire;
138                                } else {
139                                        $infos[$cle]['vignette'] = $vignette_par_defaut($infos[$cle]['extension'], false, true);
140                                }
141                                //On récupère le type MIME du fichier aussi
142                                $infos[$cle]['mime'] = cvt_upload_determiner_mime($fichier['type'][$cle], $infos[$cle]['extension']);
143                                $infos[$cle]['taille'] = $fichier['size'][$cle];
144                                // On stocke des infos sur le formulaire
145                                $infos[$cle]['form'] = $form;
146                                $infos[$cle]['infos_encodees'] = encoder_contexte_ajax($infos[$cle], $form);
147                        }
148                }
149        }
150        if (!is_array($fichier['name'])) {
151                $infos = reset($infos);
152        }
153
154        return $infos;
155}
156
157/**
158 * Modifier $_FILES pour que le nom et le chemin du fichier temporaire
159 * correspondent à ceux qu'on a défini dans cvtupload_deplacer_fichier().
160 * Cela permet aux traitements ultérieurs
161 * de ne pas avoir à se préoccuper de l'emploi ou non de cvtupload.
162 *
163 * @param $infos_fichiers
164 *  Information sur les fichiers tels que déplacés par cvtupload_deplacer_fichier()
165 * @return void
166**/
167function cvtupload_modifier_files($infos_fichiers) {
168        foreach ($infos_fichiers as $champ => $description) {
169                if (isset($description['tmp_name'])) {//Upload unique
170                         $_FILES[$champ] = array();//On surcharge tout la description $_FILES pour ce champ.  Dans tous les cas les infos ont été stockées dans $description
171                         $_FILES[$champ]['name'] = $description['name'];
172                         $_FILES[$champ]['tmp_name'] = $description['tmp_name'];
173                         $_FILES[$champ]['type'] = $description['mime'];
174                         $_FILES[$champ]['error'] = 0; //on fait comme s'il n'y avait pas d'erreur, ce qui n'est pas forcément vrai…
175                         $_FILES[$champ]['size'] = $description['taille'];
176                } else {//Upload multiple
177                        //On surcharge tout la description $_FILES pour ce champ. Dans tous les cas les infos ont été stockées dans $description
178                        if (isset($_FILES[$champ])) {
179                                $old_FILES_champ = $_FILES[$champ];
180                        } else {
181                                $old_FILES_champ = array();
182                        }
183                        $_FILES[$champ]['name'] = array();
184                        $_FILES[$champ]['tmp_name'] = array();
185                        $_FILES[$champ]['type'] = array();
186                        $_FILES[$champ]['error'] = array();
187                        $_FILES[$champ]['size'] = array();
188                        // Et on re-rempli à partir de $description
189                        foreach ($description as $fichier_individuel => $description_fichier_individuel) {
190                                $_FILES[$champ]['name'][$fichier_individuel] = $description_fichier_individuel['name'];
191                                $_FILES[$champ]['tmp_name'][$fichier_individuel] = $description_fichier_individuel['tmp_name'];
192                                $_FILES[$champ]['type'][$fichier_individuel] = $description_fichier_individuel['mime'];
193                                $_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…
194                                $_FILES[$champ]['size'][$fichier_individuel] = $description_fichier_individuel['taille'];
195                        }
196                        // Si on vient d'envoyer un ou plusieur $champ[] vide, on les rajoute dans notre nouveau $FILES
197                        if (isset($old_FILES_champ['error'])) {
198                                foreach ($old_FILES_champ['error'] as $id_fichier_individuel => $error_fichier_individuel){
199                                        if ($error_fichier_individuel!=0 and !isset($infos_fichiers[$champ][$id_fichier_individuel])){//Uniquement les erreurs
200                                                $_FILES[$champ]['name'][$id_fichier_individuel] = $old_FILES_champ['name'][$id_fichier_individuel];
201                                                $_FILES[$champ]['tmp_name'][$id_fichier_individuel] = $old_FILES_champ['tmp_name'][$id_fichier_individuel];
202                                                $_FILES[$champ]['type'][$id_fichier_individuel] = $old_FILES_champ['type'][$id_fichier_individuel];
203                                                $_FILES[$champ]['error'][$id_fichier_individuel] = $old_FILES_champ['error'][$id_fichier_individuel];
204                                                $_FILES[$champ]['size'][$id_fichier_individuel] = $old_FILES_champ['size'][$id_fichier_individuel];
205                                        }
206                                }
207                        }
208                        // 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
209                        foreach ($_FILES[$champ] as $propriete => $valeurs_propriete) {
210                                ksort($valeurs_propriete);
211                                $_FILES[$champ][$propriete] = $valeurs_propriete;
212                        }
213                }
214        }
215}
216
217/**
218 * Nettoyer $_FILES pour effacer les entrées dont on a vérifié qu'elle ne répondaient pas à certains critères
219 *
220 * @param string $champ
221 *      Le nom du champ concerné dans $_FILES
222 * @param string[]|string $erreurs
223 *      Si un upload multiple, un tableau des $erreurs avec comme clés les numéros des fichiers à supprimer dans $_FILES[$champ]
224 *      Si un upload unique, une chaîne, qui si non vide, indique qu'il faut effacer le $_FILE[$champ]
225 * @return void
226**/
227function cvtupload_nettoyer_files_selon_erreurs($champ, $erreurs) {
228        if (is_array($erreurs)) { // cas d'upload multiple
229                foreach ($erreurs as $cle => $erreur) {
230                        foreach ($_FILES[$champ] as $propriete => $valeur) {
231                                unset($_FILES[$champ][$propriete][$cle]);
232                        }
233                }
234        } elseif ($erreurs!='') { // cas d'upload unique avec erreur
235                unset($_FILES[$champ]);
236        }
237}
238
239/**
240 * Détermine un MIME lorsque les informations de PHP sont imprécises.
241 * Par exemple PHP considère qu'un fichier .tex est de MIME application/octet-stream
242 * Ce qui n'est absolument pas utilse
243 * @param string $mime_suppose
244 * @param string $extension
245 * @return string $mime_trouve
246**/
247function cvt_upload_determiner_mime($mime_suppose, $extension) {
248        if (!in_array($mime_suppose, array('text/plain', '', 'application/octet-stream'))) { // si on a un mime précis, on le renvoie, tout simplement
249                return $mime_suppose;
250        }
251        $mime_spip = sql_getfetsel('mime_type', 'spip_types_documents', 'extension='.sql_quote($extension));
252        if ($mime_spip) {
253                return $mime_spip;
254        } else {
255                return $mime_suppose;
256        }
257}
258
259/**
260 * Nettoyer un répertoire suivant l'age et le nombre de ses fichiers
261 *
262 * @param string $repertoire
263 *              Répertoire à nettoyer
264 * @param int $age_max
265 *              Age maxium des fichiers en seconde
266 * @param int $max_files
267 *              Nombre maximum de fichiers dans le dossier
268 * @return void
269 **/
270function cvtupload_nettoyer_repertoire($repertoire, $age_max = _CVTUPLOAD_AGE_MAX, $max_files = _CVTUPLOAD_MAX_FILES) {
271        include_spip('inc/flock');
272
273        // Si on entre bien dans le répertoire
274        if ($ressource_repertoire = opendir($repertoire)) {
275                $fichiers = array();
276
277                // On commence par supprimer les plus vieux
278                while ($fichier = readdir($ressource_repertoire)) {
279                        if (!in_array($fichier, array('.', '..', '.ok'))) {
280                                $chemin_fichier = $repertoire.$fichier;
281
282                                if (is_file($chemin_fichier) and !jeune_fichier($chemin_fichier, $age_max)) {
283                                        supprimer_fichier($chemin_fichier);
284                                } else {
285                                        $fichiers[@filemtime($chemin_fichier).'_'.rand()] = $chemin_fichier;
286                                }
287                        }
288                }
289
290                // On trie les fichiers par ordre de leur date
291                ksort($fichiers);
292
293                // Puis s'il reste trop de fichiers, on supprime le surplus
294                $nb_fichiers = count($fichiers);
295                if ($nb_fichiers > $max_files) {
296                        $nb_a_supprimer = $nb_fichiers - $max_files - 1;
297
298                        while ($nb_a_supprimer) {
299                                $fichier = array_shift($fichiers);
300                                supprimer_fichier($fichier);
301                                $nb_a_supprimer--;
302                        }
303                }
304        }
305}
Note: See TracBrowser for help on using the repository browser.