source: spip-zone/_core_/plugins/medias/action/ajouter_documents.php

Last change on this file was 116237, checked in by cedric@…, 3 months ago

derogation rubrique=0 comme pour site=0 : c'est le logo general des rubriques

File size: 18.7 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2019                                                *
7 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8 *                                                                         *
9 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11\***************************************************************************/
12
13/**
14 * Gestion de l'action ajouter_documents
15 *
16 * @package SPIP\Medias\Action
17 **/
18
19if (!defined('_ECRIRE_INC_VERSION')) {
20        return;
21}
22
23include_spip('inc/getdocument');
24include_spip('inc/documents');
25include_spip('inc/choisir_mode_document'); // compat core
26include_spip('inc/renseigner_document');
27
28/**
29 * Ajouter des documents
30 *
31 * @param int $id_document
32 *   Document à remplacer, ou pour une vignette, l'id_document de maman
33 *   0 ou 'new' pour une insertion
34 * @param array $files
35 *   Tableau de tableaux de propriétés pour chaque document à insérer
36 * @param string $objet
37 *   Objet auquel associer le document
38 * @param int $id_objet
39 *   id_objet
40 * @param string $mode
41 *   Mode par défaut si pas precisé pour le document
42 * @return array
43 *   Liste des id_documents inserés
44 */
45function action_ajouter_documents_dist($id_document, $files, $objet, $id_objet, $mode) {
46        $ajouter_un_document = charger_fonction('ajouter_un_document', 'action');
47        $ajoutes = array();
48
49        // on ne peut mettre qu'un seul document a la place d'un autre ou en vignette d'un autre
50        if (intval($id_document)) {
51                $ajoutes[] = $ajouter_un_document($id_document, reset($files), $objet, $id_objet, $mode);
52        } else {
53                foreach ($files as $file) {
54                        $ajoutes[] = $ajouter_un_document('new', $file, $objet, $id_objet, $mode);
55                }
56        }
57
58        return $ajoutes;
59}
60
61/**
62 * Ajouter un document (au format $_FILES)
63 *
64 * @param int $id_document
65 *   Document à remplacer, ou pour une vignette, l'id_document de maman
66 *   0 ou 'new' pour une insertion
67 * @param array $file
68 *   Propriétes au format $_FILE étendu :
69 *
70 *   - string tmp_name : source sur le serveur
71 *   - string name : nom du fichier envoye
72 *   - bool titrer : donner ou non un titre a partir du nom du fichier
73 *   - bool distant : pour utiliser une source distante sur internet
74 *   - string mode : vignette|image|documents|choix
75 * @param string $objet
76 *   Objet auquel associer le document
77 * @param int $id_objet
78 *   id_objet
79 * @param string $mode
80 *   Mode par défaut si pas precisé pour le document
81 * @return array|bool|int|mixed|string|unknown
82 *
83 *   - int : l'id_document ajouté (opération réussie)
84 *   - string : une erreur s'est produit, la chaine est le message d'erreur
85 *
86 */
87function action_ajouter_un_document_dist($id_document, $file, $objet, $id_objet, $mode) {
88
89        $source = $file['tmp_name'];
90        $nom_envoye = $file['name'];
91
92        // passer en minuscules le nom du fichier, pour eviter les collisions
93        // si le file system fait la difference entre les deux il ne detectera
94        // pas que Toto.pdf et toto.pdf
95        // et on aura une collision en cas de changement de file system
96        $file['name'] = strtolower(translitteration($file['name']));
97
98        // Pouvoir definir dans mes_options.php que l'on veut titrer tous les documents par d?faut
99        if (!defined('_TITRER_DOCUMENTS')) {
100                define('_TITRER_DOCUMENTS', false);
101        }
102
103        $titrer = isset($file['titrer']) ? $file['titrer'] : _TITRER_DOCUMENTS;
104        $mode = ((isset($file['mode']) and $file['mode']) ? $file['mode'] : $mode);
105
106        include_spip('inc/modifier');
107        if (isset($file['distant']) and $file['distant']
108                and !in_array($mode, array('choix', 'auto', 'image', 'document'))) {
109                spip_log("document distant $source accepte sans verification, mode=$mode", 'medias'._LOG_INFO_IMPORTANTE);
110                include_spip('inc/distant');
111                $file['tmp_name'] = _DIR_RACINE . copie_locale($source);
112                $source = $file['tmp_name'];
113                unset($file['distant']);
114        }
115
116        // Documents distants : pas trop de verifications bloquantes, mais un test
117        // via une requete HEAD pour savoir si la ressource existe (non 404), si le
118        // content-type est connu, et si possible recuperer la taille, voire plus.
119        if (isset($file['distant']) and $file['distant']) {
120                if (!tester_url_absolue($source)) {
121                        return _T('medias:erreur_chemin_distant', array('nom' => $source));
122                }
123                include_spip('inc/distant');
124                $source = str_replace(array("'",'"','<'),array("%27",'%22','%3C'), $source);
125                if (is_array($a = renseigner_source_distante($source))) {
126                        $champs = $a;
127                        # NB: dans les bonnes conditions (fichier autorise et pas trop gros)
128                        # $a['fichier'] est une copie locale du fichier
129
130                        $infos = renseigner_taille_dimension_image($champs['fichier'], $champs['extension'], true);
131                        // on ignore erreur eventuelle sur $infos car on est distant, ca ne marche pas forcement
132                        if (is_array($infos)) {
133                                $champs = array_merge($champs, $infos);
134                        }
135
136                        unset($champs['type_image']);
137                } // on ne doit plus arriver ici, car l'url distante a ete verifiee a la saisie !
138                else {
139                        spip_log("Echec du lien vers le document $source, abandon");
140
141                        return $a; // message d'erreur
142                }
143        } else { // pas distant
144
145                $champs = array(
146                        'distant' => 'non'
147                );
148
149                $champs['titre'] = '';
150                if ($titrer) {
151                        if ($titrer_document = charger_fonction('titrer_document', 'inc', true)) {
152                                $champs['titre'] = $titrer_document($nom_envoye);
153                        }
154                        else {
155                                $titre = substr($nom_envoye, 0, strrpos($nom_envoye, '.')); // Enlever l'extension du nom du fichier
156                                $titre = preg_replace(',[[:punct:][:space:]]+,u', ' ', $titre);
157                                $champs['titre'] = preg_replace(',\.([^.]+)$,', '', $titre);
158                        }
159                }
160
161                if (!is_array($fichier = fixer_fichier_upload($file, $mode))) {
162                        return is_string($fichier) ?
163                                $fichier : _T('medias:erreur_upload_type_interdit', array('nom' => $file['name']));
164                }
165
166                $champs['inclus'] = $fichier['inclus'];
167                $champs['extension'] = $fichier['extension'];
168                $champs['fichier'] = $fichier['fichier'];
169
170                /**
171                 * Récupère les informations du fichier
172                 * -* largeur
173                 * -* hauteur
174                 * -* type_image
175                 * -* taille
176                 * -* ses metadonnées si une fonction de metadatas/ est présente
177                 */
178                $infos = renseigner_taille_dimension_image($champs['fichier'], $champs['extension']);
179                if (is_string($infos)) {
180                        // c'est un message d'erreur !
181                        return $infos;
182                }
183
184                // lorsqu’une image arrive avec une mauvaise extension par rapport au mime type, adapter.
185                // Exemple : si extension .jpg mais le contenu est un png
186                if (!empty($infos['type_image']) and $infos['type_image'] !== $champs['extension']) {
187                        spip_log('Image `' . $file['name'] . '` mauvaise extension. Correcte : ' . $infos['type_image'], 'medias' . _LOG_INFO);
188                        $subdir = determiner_sous_dossier_document($infos['type_image'], $file['name'] . '.' . $infos['type_image'], $mode);
189                        $new = copier_document($infos['type_image'], $file['name'] . '.' . $infos['type_image'], $champs['fichier'], $subdir);
190                        if ($new) {
191                                supprimer_fichier($champs['fichier']);
192                                $champs['fichier'] = $new;
193                                $champs['extension'] = $infos['type_image'];
194                                $infos = renseigner_taille_dimension_image($champs['fichier'], $champs['extension']);
195                                if (is_string($infos)) {
196                                        // c'est un message d'erreur !
197                                        return $infos;
198                                }
199                                spip_log('> Image `' . $file['name'] . '` renommée en : ' . basename($champs['fichier']), 'medias' . _LOG_INFO);
200                        } else {
201                                spip_log('! Image  `' . $file['name'] . '` non renommée en extension : ' . $champs['extension'], 'medias' . _LOG_INFO_IMPORTANTE);
202                        }
203                }
204
205                $champs = array_merge($champs, $infos);
206
207                // Si mode == 'choix', fixer le mode image/document
208                if (in_array($mode, array('choix', 'auto'))) {
209                        $choisir_mode_document = charger_fonction('choisir_mode_document', 'inc');
210                        $mode = $choisir_mode_document($champs, $champs['inclus'] == 'image', $objet);
211                }
212                $champs['mode'] = $mode;
213
214                if (($test = verifier_taille_document_acceptable($champs)) !== true) {
215                        spip_unlink($champs['fichier']);
216
217                        return $test; // erreur sur les dimensions du fichier
218                }
219
220                unset($champs['type_image']);
221                unset($champs['inclus']);
222                $champs['fichier'] = set_spip_doc($champs['fichier']);
223        }
224
225        // si le media est pas renseigne, le faire, en fonction de l'extension
226        if (!isset($champs['media'])) {
227                $champs['media'] = sql_getfetsel(
228                        'media_defaut',
229                        'spip_types_documents',
230                        'extension=' . sql_quote($champs['extension'])
231                );
232        }
233
234        // lier le parent si necessaire
235        // attention au cas particulier du site 0 utilisé pour le logo du site
236        if ($objet and (($id_objet = intval($id_objet)) or in_array($objet, ['site', 'rubrique']))) {
237                $champs['parents'][] = "$objet|$id_objet";
238        }
239
240        // "mettre a jour un document" si on lui
241        // passe un id_document
242        if ($id_document = intval($id_document)) {
243                unset($champs['titre']); // garder le titre d'origine
244                unset($champs['date']); // garder la date d'origine
245                unset($champs['descriptif']); // garder la desc d'origine
246                // unset($a['distant']); # on peut remplacer un doc statique par un doc distant
247                // unset($a['mode']); # on peut remplacer une image par un document ?
248        }
249
250        include_spip('action/editer_document');
251        // Installer le document dans la base
252        if (!$id_document) {
253                if ($id_document = document_inserer()) {
254                        spip_log(
255                                'ajout du document ' . $file['tmp_name'] . ' ' . $file['name'] . "  (M '$mode' T '$objet' L '$id_objet' D '$id_document')",
256                                'medias'
257                        );
258                } else {
259                        spip_log(
260                                'Echec insert_document() du document ' . $file['tmp_name'] . ' ' . $file['name'] . "  (M '$mode' T '$objet' L '$id_objet' D '$id_document')",
261                                'medias' . _LOG_ERREUR
262                        );
263                }
264        }
265        if (!$id_document) {
266                return _T('medias:erreur_insertion_document_base', array('fichier' => '<em>' . $file['name'] . '</em>'));
267        }
268
269        document_modifier($id_document, $champs);
270
271        // permettre aux plugins de faire des modifs a l'ajout initial
272        // ex EXIF qui tourne les images si necessaire
273        // Ce plugin ferait quand même mieux de se placer dans metadata/jpg.php
274        pipeline(
275                'post_edition',
276                array(
277                        'args' => array(
278                                'table' => 'spip_documents', // compatibilite
279                                'table_objet' => 'documents',
280                                'spip_table_objet' => 'spip_documents',
281                                'type' => 'document',
282                                'id_objet' => $id_document,
283                                'champs' => array_keys($champs),
284                                'serveur' => '', // serveur par defaut, on ne sait pas faire mieux pour le moment
285                                'action' => 'ajouter_document',
286                                'operation' => 'ajouter_document', // compat <= v2.0
287                        ),
288                        'data' => $champs
289                )
290        );
291
292        return $id_document;
293}
294
295/**
296 * Sous-repertoire dans lequel on stocke le document
297 * en regle general $ext/ sauf pour les logo
298 * @param $ext
299 * @param $fichier
300 * @param $mode
301 * @return mixed
302 */
303function determiner_sous_dossier_document($ext, $fichier, $mode) {
304
305        // si mode un logoxx on met dans logo/
306        if (strncmp($mode, 'logo', 4) === 0) {
307                return "logo";
308        }
309
310        return $ext;
311}
312
313/**
314 * Corrige l'extension du fichier dans quelques cas particuliers
315 *
316 * @note
317 *     Une extension 'pdf ' passe dans la requête de contrôle
318 *     mysql> SELECT * FROM spip_types_documents WHERE extension="pdf ";
319 *
320 * @todo
321 *     À passer dans base/typedoc
322 *
323 * @param string $ext
324 * @return string
325 */
326function corriger_extension($ext) {
327        $ext = preg_replace(',[^a-z0-9],i', '', $ext);
328        switch ($ext) {
329                case 'htm':
330                        $ext = 'html';
331                        break;
332                case 'jpeg':
333                        $ext = 'jpg';
334                        break;
335                case 'tiff':
336                        $ext = 'tif';
337                        break;
338                case 'aif':
339                        $ext = 'aiff';
340                        break;
341                case 'mpeg':
342                        $ext = 'mpg';
343                        break;
344        }
345
346        return $ext;
347}
348
349/**
350 * Vérifie la possibilité d'uploader une extension
351 *
352 * Vérifie aussi si l'extension est autorisée pour le mode demandé
353 * si on connait le mode à ce moment là
354 *
355 * @param string $source
356 *     Nom du fichier
357 * @param string $mode
358 *     Mode d'inclusion du fichier, si connu
359 * @return array|bool|string
360 *
361 *     - array : extension acceptée (tableau descriptif).
362 *       Avec un index 'autozip' si il faut zipper
363 *     - false ou message d'erreur si l'extension est refusée
364 */
365function verifier_upload_autorise($source, $mode = '') {
366        $infos = array('fichier' => $source);
367        $res = false;
368        if (preg_match(',\.([a-z0-9]+)(\?.*)?$,i', $source, $match)
369                and $ext = $match[1]
370        ) {
371                $ext = corriger_extension(strtolower($ext));
372                if ($res = sql_fetsel(
373                        'extension,inclus,media_defaut as media',
374                        'spip_types_documents',
375                        'extension=' . sql_quote($ext) . " AND upload='oui'"
376                )) {
377                        $infos = array_merge($infos, $res);
378                }
379        }
380        if (!$res) {
381                if ($res = sql_fetsel(
382                        'extension,inclus,media_defaut as media',
383                        'spip_types_documents',
384                        "extension='zip' AND upload='oui'"
385                )) {
386                        $infos = array_merge($infos, $res);
387                        $res['autozip'] = true;
388                }
389        }
390        if ($mode and $res) {
391                // verifier en fonction du mode si une fonction est proposee
392                if ($verifier_document_mode = charger_fonction('verifier_document_mode_' . $mode, 'inc', true)) {
393                        $check = $verifier_document_mode($infos); // true ou message d'erreur sous forme de chaine
394                        if ($check !== true) {
395                                $res = $check;
396                        }
397                }
398        }
399
400        if (!$res or is_string($res)) {
401                spip_log("Upload $source interdit ($res)", _LOG_INFO_IMPORTANTE);
402        }
403
404        return $res;
405}
406
407
408/**
409 * Tester le type de document
410 *
411 * - le document existe et n'est pas de taille 0 ?
412 * - interdit a l'upload ?
413 * - quelle extension dans spip_types_documents ?
414 * - est-ce "inclus" comme une image ?
415 *
416 * Le zipper si necessaire
417 *
418 * @param array $file
419 *     Au format $_FILES
420 * @param string $mode
421 *     Mode d'inclusion du fichier, si connu
422 * @return array
423 */
424function fixer_fichier_upload($file, $mode = '') {
425        /**
426         * On vérifie que le fichier existe et qu'il contient quelque chose
427         */
428        if (is_array($row = verifier_upload_autorise($file['name'], $mode))) {
429                $subdir = determiner_sous_dossier_document($row['extension'], $file['name'], $mode);
430                if (!isset($row['autozip'])) {
431                        $row['fichier'] = copier_document($row['extension'], $file['name'], $file['tmp_name'], $subdir);
432                        /**
433                         * On vérifie que le fichier a une taille
434                         * si non, on le supprime et on affiche une erreur
435                         */
436                        if ($row['fichier'] && (!$taille = @intval(filesize(get_spip_doc($row['fichier']))))) {
437                                spip_log('Echec copie du fichier ' . $file['tmp_name'] . ' (taille de fichier indéfinie)');
438                                spip_unlink(get_spip_doc($row['fichier']));
439                                return _T('medias:erreur_copie_fichier', array('nom' => $file['tmp_name']));
440                        } else {
441                                return $row;
442                        }
443                } else {
444                        // creer un zip comme demande
445                        // pour encapsuler un fichier dont l'extension n'est pas supportee
446                        unset($row['autozip']);
447
448                        $ext = 'zip';
449                        if (!$tmp_dir = tempnam(_DIR_TMP, 'tmp_upload')) {
450                                return false;
451                        }
452
453                        spip_unlink($tmp_dir);
454                        @mkdir($tmp_dir);
455
456                        include_spip('inc/charsets');
457                        $tmp = $tmp_dir . '/' . translitteration($file['name']);
458
459                        // conserver l'extension dans le nom de fichier, par exemple toto.js => toto.js.zip
460                        $file['name'] .= '.' . $ext;
461
462                        // deplacer le fichier tmp_name dans le dossier tmp
463                        deplacer_fichier_upload($file['tmp_name'], $tmp, true);
464
465                        include_spip('inc/pclzip');
466                        $source = _DIR_TMP . basename($tmp_dir) . '.' . $ext;
467                        $archive = new PclZip($source);
468
469                        $v_list = $archive->create(
470                                $tmp,
471                                PCLZIP_OPT_REMOVE_PATH,
472                                $tmp_dir,
473                                PCLZIP_OPT_ADD_PATH,
474                                ''
475                        );
476
477                        effacer_repertoire_temporaire($tmp_dir);
478                        if (!$v_list) {
479                                spip_log('Echec creation du zip');
480                                return false;
481                        }
482
483                        $row['fichier'] = copier_document($row['extension'], $file['name'], $source, $subdir);
484                        spip_unlink($source);
485                        /**
486                         * On vérifie que le fichier a une taille
487                         * si non, on le supprime et on affiche une erreur
488                         */
489                        if ($row['fichier'] && (!$taille = @intval(filesize(get_spip_doc($row['fichier']))))) {
490                                spip_log('Echec copie du fichier ' . $file['tmp_name'] . ' (taille de fichier indéfinie)');
491                                spip_unlink(get_spip_doc($row['fichier']));
492
493                                return _T('medias:erreur_copie_fichier', array('nom' => $file['tmp_name']));
494                        } else {
495                                return $row;
496                        }
497                }
498        } else {
499                return $row;
500        } // retourner le message d'erreur
501}
502
503/**
504 * Verifier si le fichier respecte les contraintes de tailles
505 *
506 * @param  array $infos
507 * @return bool|mixed|string
508 */
509function verifier_taille_document_acceptable(&$infos) {
510
511        // si ce n'est pas une image
512        if (!$infos['type_image']) {
513                if (defined('_DOC_MAX_SIZE') and _DOC_MAX_SIZE > 0 and $infos['taille'] > _DOC_MAX_SIZE * 1024) {
514                        return _T(
515                                'medias:info_doc_max_poids',
516                                array(
517                                        'maxi' => taille_en_octets(_DOC_MAX_SIZE * 1024),
518                                        'actuel' => taille_en_octets($infos['taille'])
519                                )
520                        );
521                }
522        } // si c'est une image
523        else {
524                if ((defined('_IMG_MAX_WIDTH') and _IMG_MAX_WIDTH and $infos['largeur'] > _IMG_MAX_WIDTH)
525                        or (defined('_IMG_MAX_HEIGHT') and _IMG_MAX_HEIGHT and $infos['hauteur'] > _IMG_MAX_HEIGHT)
526                ) {
527                        $max_width = (defined('_IMG_MAX_WIDTH') and _IMG_MAX_WIDTH) ? _IMG_MAX_WIDTH : '*';
528                        $max_height = (defined('_IMG_MAX_HEIGHT') and _IMG_MAX_HEIGHT) ? _IMG_MAX_HEIGHT : '*';
529
530                        // pas la peine d'embeter le redacteur avec ca si on a active le calcul des miniatures
531                        // on met directement a la taille maxi a la volee
532                        if (isset($GLOBALS['meta']['creer_preview']) and $GLOBALS['meta']['creer_preview'] == 'oui') {
533                                include_spip('inc/filtres');
534                                $img = filtrer('image_reduire', $infos['fichier'], $max_width, $max_height);
535                                $img = extraire_attribut($img, 'src');
536                                $img = supprimer_timestamp($img);
537                                if (@file_exists($img) and $img !== $infos['fichier']) {
538                                        spip_unlink($infos['fichier']);
539                                        @rename($img, $infos['fichier']);
540                                        $size = @spip_getimagesize($infos['fichier']);
541                                        $infos['largeur'] = $size[0];
542                                        $infos['hauteur'] = $size[1];
543                                        $infos['taille'] = @filesize($infos['fichier']);
544                                }
545                        }
546
547                        if ((defined('_IMG_MAX_WIDTH') and _IMG_MAX_WIDTH and $infos['largeur'] > _IMG_MAX_WIDTH)
548                                or (defined('_IMG_MAX_HEIGHT') and _IMG_MAX_HEIGHT and $infos['hauteur'] > _IMG_MAX_HEIGHT)
549                        ) {
550                                return _T(
551                                        'medias:info_image_max_taille',
552                                        array(
553                                                'maxi' =>
554                                                        _T(
555                                                                'info_largeur_vignette',
556                                                                array(
557                                                                        'largeur_vignette' => $max_width,
558                                                                        'hauteur_vignette' => $max_height
559                                                                )
560                                                        ),
561                                                'actuel' =>
562                                                        _T(
563                                                                'info_largeur_vignette',
564                                                                array(
565                                                                        'largeur_vignette' => $infos['largeur'],
566                                                                        'hauteur_vignette' => $infos['hauteur']
567                                                                )
568                                                        )
569                                        )
570                                );
571                        }
572                }
573
574                if (defined('_IMG_MAX_SIZE') and _IMG_MAX_SIZE > 0 and $infos['taille'] > _IMG_MAX_SIZE * 1024) {
575                        return _T(
576                                'medias:info_image_max_poids',
577                                array(
578                                        'maxi' => taille_en_octets(_IMG_MAX_SIZE * 1024),
579                                        'actuel' => taille_en_octets($infos['taille'])
580                                )
581                        );
582                }
583        }
584
585        // verifier en fonction du mode si une fonction est proposee
586        if ($verifier_document_mode = charger_fonction('verifier_document_mode_' . $infos['mode'], 'inc', true)) {
587                return $verifier_document_mode($infos);
588        }
589
590        return true;
591}
Note: See TracBrowser for help on using the repository browser.