source: spip-zone/_plugins_/crayons/action/crayons_store.php @ 100962

Last change on this file since 100962 was 100939, checked in by marcimat@…, 3 years ago

Introduction de 2 pipelines :

  • crayons_controleur : il permet de définir/modifier la fonction PHP qui servira de controleur.
  • crayons_verifier : il permet de retourner des erreurs de validation et/ou de modifier les contenus envoyés avant l'enregistrement (normaliser).

Un usage de ces pipelines est à venir dans le plugin Champs Extras Core.

File size: 17.6 KB
Line 
1<?php
2/**
3 * Crayons
4 * plugin for spip
5 * (c) Fil, toggg 2006-2013
6 * licence GPL
7 */
8
9if (!defined('_ECRIRE_INC_VERSION')) {
10        return;
11}
12
13function verif_secu($w, $secu) {
14        return (
15                $secu == md5($GLOBALS['meta']['alea_ephemere'].'='.$w)
16        or
17                $secu == md5($GLOBALS['meta']['alea_ephemere_ancien'].'='.$w)
18        );
19}
20
21function post_crayons() {
22        $results = array();
23
24        if (isset($_POST['crayons']) and is_array($_POST['crayons'])) {
25                foreach ($_POST['crayons'] as $crayon) {
26                        $name = $_POST['name_'.$crayon];
27                        $content = array();
28                        if ($_POST['fields_'.$crayon]) {
29                                foreach (explode(',', $_POST['fields_'.$crayon]) as $field) {
30                                        // cas particulier d'un envoi de fichier
31                                        if (isset($_FILES['content_'.$crayon.'_'.$field])) {
32                                                if ($_FILES['content_'.$crayon.'_'.$field]['size'] > 0) {
33                                                        $content[$field] = $_FILES['content_'.$crayon.'_'.$field];
34                                                } else {
35                                                        $content[$field] = false;
36                                                }
37                                                // cf. valeur passee dans crayon->md5() : false ou filemtime() du logo
38                                        } else {
39                                                /**
40                                                 * le changement de charset n'est plus necessaire
41                                                 * depuis jquery 1.5 (feature non documentee de jquery!)
42                                                 */
43                                                if (isset($_POST['content_'.$crayon.'_'.$field])) {
44                                                        $content[$field] = is_array($_POST['content_'.$crayon.'_'.$field])
45                                                                ?implode(',', $_POST['content_'.$crayon.'_'.$field])
46                                                                :$_POST['content_'.$crayon.'_'.$field];
47                                                } else {
48                                                        $content[$field] = null;
49                                                }
50                                        }
51                                }
52                        }
53
54                        // Si les donnees POSTees ne correspondent pas a leur md5,
55                        // il faut les traiter
56                        if (isset($name)
57                                and md5(serialize($content)) != $_POST['md5_'.$crayon]) {
58                                if (!isset($_POST['secu_'.$crayon])
59                                        or verif_secu($name, $_POST['secu_' . $crayon])) {
60                                        $results[] = array($name, $content, $_POST['md5_'.$crayon], $crayon);
61                                } else {
62                                        return false; // erreur secu
63                                }
64                        } else {
65                                // cas inchange
66                                $results[] = array($name, $content, false, $crayon);
67                        }
68                }
69        }
70        return $results;
71}
72
73
74function crayons_store($options = array()) {
75        // permettre de surcharger les fonctions de recuperation des valeurs
76        // et de sauvegardes de celles-ci
77        $options = array_merge(array(
78                        'f_get_valeur' => 'crayons_store_get_valeur',
79                        'f_set_modifs' => 'crayons_store_set_modifs',
80                ), $options);
81
82        include_spip('inc/crayons');
83        $wdgcfg = wdgcfg();
84
85        $return = array('$erreur'=>'');
86
87        $postees = post_crayons();
88
89        $invalides = $modifs = $updates = array();
90
91        if (!is_array($postees)) {
92                $return['$erreur'] = _U('crayons:donnees_mal_formatees');
93        } else {
94                foreach ($postees as $postee) {
95                        if ($postee[2] !== false) {
96                                $name = $postee[0];
97                                $content = $postee[1];
98
99                                if ($content && preg_match(_PREG_CRAYON, 'crayon '.$name, $regs)) {
100                                        list(,$crayon,$type,$modele,$id) = $regs;
101                                        $wid = $postee[3];
102
103                                        spip_log("autoriser('crayonner', $type, $id, null, array('modele' => $modele)", 'crayons_distant');
104                                        if (!autoriser('crayonner', $type, $id, null, array('modele' => $modele))) {
105                                                $return['$erreur'] =
106                                                        "$type $id: " . _U('crayons:non_autorise');
107                                        } else {
108                                                // recuperer l'existant pour calculer son md5 et verifier
109                                                // qu'il n'a pas ete modifie entre-temps
110                                                $get_valeur = $options['f_get_valeur'];
111                                                $data = $get_valeur($content, $regs);
112
113                                                $md5 = md5(serialize($data));
114
115                                                // est-ce que le champ a ete modifie dans la base entre-temps ?
116                                                if ($md5 != $postee[2]) {
117                                                        // si oui, la modif demandee correspond peut-etre
118                                                        // a la nouvelle valeur ? dans ce cas on procede
119                                                        // comme si "pas de modification", sinon erreur
120                                                        if ($md5 != md5(serialize($content))) {
121                                                                $return['$erreur'] = "$type $id $modele: " .
122                                                                        _U('crayons:modifie_par_ailleurs');
123                                                        }
124                                                }
125
126                                                // Aiguillage pour verification de la saisie
127                                                // Pour traitement ulterieur les fonctions de verifications doivent renvoyer $invalides :
128                                                // $invalides[wid_champ]['msg'] -> message de saisie invalide
129                                                // $invalides[wid_champ]['retour'] -> caracteres invalides
130                                                $f = 'verifier_'.$type.'_'.$modele;
131                                                if (function_exists($f)) {
132                                                        $_invalides = $f($modifs);
133                                                        if ($_invalides and is_array($invalides)) {
134                                                                $invalides = array_merge($invalides, $_invalides);
135                                                        }
136                                                }
137
138                                                // Fonctionnement plus simple, en retournant presque comme le pipeline formulaire_verifier
139                                                // On permet aussi de modifier la valeur soumise, si une valeur est retournée dans normaliser.
140
141                                                $data = pipeline(
142                                                        'crayons_verifier',
143                                                        array(
144                                                                'args' => array(
145                                                                        'type' => $type,
146                                                                        'modele' => $modele,
147                                                                        'id' => $id,
148                                                                        'content' => $content,
149                                                                        'wid' => $wid,
150                                                                ),
151                                                                'data' => array(
152                                                                        'erreurs' => array(), // couples : champ => texte d'erreur
153                                                                        'normaliser' => array(), // couples : champ => valeur à utiliser
154                                                                ),
155                                                        )
156                                                );
157
158                                                if (count($data['normaliser'])) {
159                                                        $content = $data['normaliser'] + $content;
160                                                }
161
162                                                if ($data['erreurs']) {
163                                                        foreach ($data['erreurs'] as $c => $e) {
164                                                                $invalides[$wid . '_' . $c]['msg'] = $e;
165                                                        }
166                                                }
167
168                                                $modifs[] = array($type, $modele, $id, $content, $wid);
169                                        }
170                                }
171                        }
172                }
173        }
174
175        if (!$modifs and !$return['$erreur']) {
176                $return['$erreur'] = $wdgcfg['msgNoChange'] ? _U('crayons:pas_de_modification') : ' ';
177                $return['$annuler'] = true;
178        }
179
180        // un champ invalide ... ou rien ==> on ne fait rien !
181        if (count($invalides)) {
182                $return['$invalides'] = $invalides;
183                return $return;
184        }
185
186        // une quelconque erreur ... ou rien ==> on ne fait rien !
187        if (isset($return['$erreur']) and $return['$erreur']) {
188                return $return;
189        }
190
191        // on traite toutes les modifications
192        // en appelant la fonction adequate de traitement
193        $set_modifs = $options['f_set_modifs'];
194        $return = $set_modifs($modifs, $return);
195
196        // une quelconque erreur ... ou rien ==> on ne fait rien !
197        if ($return['$erreur']) {
198                return $return;
199        }
200
201        // et maintenant refaire l'affichage des crayons modifies
202        include_spip('inc/texte');
203        foreach ($modifs as $m) {
204                list($type, $modele, $id, $content, $wid) = $m;
205                        $f = charger_fonction($type.'_'.$modele, 'vues', true)
206                                or $f = charger_fonction($modele, 'vues', true)
207                                or $f = charger_fonction($type, 'vues', true)
208                                or $f = 'vues_dist';
209                        $return[$wid] = $f($type, $modele, $id, $content, $wid);
210        }
211        return $return;
212}
213
214// recuperer une valeur en fonction des parametres recuperes
215// cette fonction cherche une valeur d'une colonne d'une table SQL
216function crayons_store_get_valeur($content, $regs) {
217        list(,$crayon,$type,$modele,$id) = $regs;
218        return valeur_colonne_table($type, array_keys($content), $id);
219}
220
221// stocke les valeurs envoyees dans des colonnes de table SQL
222function crayons_store_set_modifs($modifs, $return) {
223        // sinon on bosse : toutes les modifs ont ete acceptees
224        // verifier qu'on a tout ce qu'il faut pour mettre a jour la base
225        // et regrouper les mises a jour par type/id
226        foreach ($modifs as $modif) {
227                list($type, $modele, $id, $content, $wid) = $modif;
228
229                $fun = '';
230                // si le crayon est un MODELE avec une fonction xxx_revision associee
231                // cas ou une fonction xxx_revision existe
232                if (function_exists($f = $type.'_'. $modele . '_revision')
233                        or function_exists($f = $modele . '_revision')
234                        or function_exists($f = $type . '_revision')) {
235                        $fun = $f;
236                } elseif (function_exists('lister_tables_objets_sql')
237                        and $tables_objet = lister_tables_objets_sql()
238                        and isset($tables_objet[table_objet_sql($type)])) {
239                        // si on est en SPIP 3+ et qu'on edite un objet editorial bien declare
240                        // passer par l'API objet_modifier
241                        $fun = 'crayons_objet_modifier';
242                } else {
243                        // sinon spip < 3 (ou pas un objet edito)
244                        // on teste les objets connus et on route sur les fonctions correspondantes
245                        switch ($type) {
246                                case 'article':
247                                        $fun = 'crayons_update_article';
248                                        break;
249                                case 'breve':
250                                        include_spip('action/editer_breve');
251                                        $fun = 'revisions_breves';
252                                        break;
253                                case 'forum':
254                                        include_spip('inc/forum');
255                                        $fun = 'enregistre_et_modifie_forum';
256                                        break;
257                                case 'rubrique':
258                                        include_spip('action/editer_rubrique');
259                                        $fun = 'revisions_rubriques';
260                                        break;
261                                case 'syndic':
262                                case 'site':
263                                        include_spip('action/editer_site');
264                                        $fun = 'revisions_sites';
265                                        break;
266                                case 'document':
267                                        include_spip('plugins/installer');
268                                        include_spip('inc/plugin');
269                                        if (spip_version_compare($GLOBALS['spip_version_branche'], '3.0.0alpha', '>=')) {
270                                                include_spip('action/editer_document');
271                                                $fun = 'document_modifier';
272                                        } else {
273                                                include_spip('inc/modifier');
274                                                $fun = 'revision_document';
275                                        }
276                                        break;
277                                // cas geres de la maniere la plus standard
278                                case 'auteur':
279                                case 'mot':
280                                case 'signature':
281                                case 'petition':
282                                default:
283                                        include_spip('inc/modifier');
284                                        $fun = 'revision_'.$type;
285                                        break;
286                        }
287                }
288                // si on a pas reussi on passe par crayons_update() qui fera un update sql brutal
289                if (!$fun or !function_exists($fun)) {
290                        $fun = 'crayons_update';
291                        // $return['$erreur'] = "$type: " . _U('crayons:non_implemente');
292                        // break;
293                }
294
295                if (!isset($updates[$type][$fun])) {
296                        $updates[$type][$fun] = array();
297                }
298                if (!isset($updates[$type][$fun][$id])) {
299                        $updates[$type][$fun][$id] = array('wdg'=>array(), 'chval'=>array());
300                }
301                // pour reaffecter le retour d'erreur sql au cas ou
302                $updates[$type][$fun][$id]['wdg'][] = $wid;
303                foreach ($content as $champtable => $val) {
304                        $updates[$type][$fun][$id]['chval'][$champtable] = $val;
305                }
306        }
307
308        // il manque une fonction de mise a jour ==> on ne fait rien !
309        if ($return['$erreur']) {
310                return $return;
311        }
312
313        // hop ! mises a jour table par table et id par id
314        foreach ($updates as $type => $idschamps) {
315                foreach ($idschamps as $fun => $ids) {
316                        foreach ($ids as $id => $champsvaleurs) {
317                                /* cas particulier du logo dans un crayon complexe :
318                                   ce n'est pas un champ de la table */
319                                if (isset($champsvaleurs['chval']['logo'])) {
320                                        spip_log('revision logo', 'crayons');
321                                        logo_revision($id, $champsvaleurs['chval'], $type, $champsvaleurs['wdg']);
322                                        unset($champsvaleurs['chval']['logo']);
323                                }
324                                if (count($champsvaleurs['chval'])) {
325                                        // -- revisions_articles($id_article, $c) --
326                                        spip_log("$fun($id ...)", 'crayons');
327                                        $updok = $fun($id, $champsvaleurs['chval'], $type, $champsvaleurs['wdg']);
328                                        // Renvoyer erreur si update base distante echoue,
329                                        // on ne regarde pas les updates base local car ils ne renvoient rien
330                                        list($distant,$table) = distant_table($type);
331                                        if ($distant and !$updok) {
332                                                $return['$erreur'] = "$type: " . _U('crayons:update_impossible');
333                                        }
334                                }
335                        }
336                }
337        }
338        return $return;
339}
340
341//
342// VUE
343//
344function vues_dist($type, $modele, $id, $content, $wid) {
345        // pour ce qui a une {lang_select} par defaut dans la boucle,
346        // la regler histoire d'avoir la bonne typo dans le propre()
347        // NB: ceci n'a d'impact que sur le "par defaut" en bas
348        list($distant,$table) = distant_table($type);
349        if (colonne_table($type, 'lang')) {
350                $b = valeur_colonne_table($type, 'lang', $id);
351                lang_select($a = array_pop($b));
352        } else {
353                lang_select($a = $GLOBALS['meta']['langue_site']);
354        }
355
356        // chercher vues/article_toto.html
357        // sinon vues/toto.html
358        if (find_in_path(($fond = 'vues/' . $type . '_' . $modele) . '.html')
359                or find_in_path(($fond = 'vues/' . $modele) .'.html')
360                or find_in_path(($fond = 'vues/' . $type) .'.html')) {
361                $primary = (function_exists('id_table_objet')?id_table_objet($table):'id_' . $table);
362                $contexte = array(
363                        $primary => $id,
364                        'crayon_type' => $type,
365                        'crayon_modele' => $modele,
366                        'champ' => $modele,
367                        'class' => _request('class_'.$wid),
368                        'self' => _request('self'),
369                        'lang' => $GLOBALS['spip_lang']
370                );
371                $contexte = array_merge($contexte, $content);
372                include_spip('public/assembler');
373                return recuperer_fond($fond, $contexte);
374        } else {
375                // vue par defaut
376                // Par precaution on va rechercher la valeur
377                // dans la base de donnees (meme si a priori la valeur est
378                // ce qu'on vient d'envoyer, il y a nettoyage des caracteres et
379                // eventuellement d'autres filtres de saisie...)
380                $bdd = valeur_colonne_table($type, $modele, $id);
381                if (count($bdd)) {
382                        $valeur = array_pop($bdd);
383                } else {
384                        // les champs n'ont pas ete retrouves dans la base
385                        // ce qui signifie a priori que nous sommes en face d'une cle primaire compose
386                        // et qu'un crayon a modifie un element de cette cle (c'est pas malin !)
387                        // dans ce cas, on reaffiche a minima ce qu'on vient de publier
388                        // mais il sera impossible de le reediter dans la foulee avec le meme crayon
389                        // (car l'identifiant du crayon se base sur l'id).
390                        // Il faudra donc recharger la page pour pouvoir reediter.
391                        if (is_scalar($id)) {
392                                $valeur = $content[$modele];
393                        }
394                }
395
396                if ($valeur) {
397                        // seul spip core sait rendre les donnees
398                        if (function_exists('appliquer_traitement_champ')) {
399                                $valeur = appliquer_traitement_champ($valeur, $modele, table_objet($table));
400                        } else {
401                                if (in_array($modele, array('chapo', 'texte', 'descriptif', 'ps', 'bio'))) {
402                                        $valeur = propre($valeur);
403                                } else {
404                                        $valeur = typo($valeur);
405                                }
406                        }
407                }
408                return $valeur;
409        }
410}
411
412
413/**
414 * Fonction de mise a jour par API editer_objet
415 * @param $id
416 * @param $data
417 * @param $type
418 * @param $ref
419 * @return bool|mixed|string
420 */
421function crayons_objet_modifier($id, $data, $type, $ref) {
422        if (include_spip('action/editer_objet')
423                and function_exists('objet_modifier')) {
424                return objet_modifier(objet_type($type), $id, $data);
425        }
426        // fallback
427        return crayons_update($id, $data, $type);
428}
429
430//
431// Fonctions de mise a jour generique
432//
433function crayons_update($id, $colval = array(), $type = '') {
434        if (!$colval or !count($colval)) {
435                return false;
436        }
437        list($distant,$table) = distant_table($type);
438
439        if ($distant) {
440                list($nom_table, $where) = table_where($type, $id);
441                if (!$nom_table) {
442                        return false;
443                }
444
445                $update = $sep = '';
446                foreach ($colval as $col => $val) {
447                        $update .= $sep . '`' . $col . '`=' . _q($val);
448                        $sep = ', ';
449                }
450
451                $a = spip_query($q = 'UPDATE `' . $nom_table . '` SET ' . $update . ' WHERE ' . $where, $distant);
452
453                #spip_log($q);
454                include_spip('inc/invalideur');
455                suivre_invalideur($cond, $modif = true);
456        } else {
457                // cle primaire composee : 3-4-rubrique
458                // calculer un where approprie
459                // et modifier sans passer par la fonction destinee aux tables principales
460                // on limite a SPIP 2 mini car sql_updateq n'est pas mappe dans les crayons_compat
461                if (is_scalar($id) and ($GLOBALS['spip_version_code'] >= '1.93')) {
462                        list($nom_table, $where) = table_where($type, $id, true); // where sous forme de tableau
463                        $a = sql_updateq($nom_table, $colval, $where);
464                } else {
465                        // modification d'une table principale
466                        include_spip('inc/modifier');
467                        $a = modifier_contenu($type, $id, array(), $colval);
468                }
469        }
470        return $a;
471}
472
473//
474// Fonctions de mise a jour
475//
476function crayons_update_article($id_article, $c = false) {
477        include_spip('action/editer_article');
478
479        // Enregistrer les nouveaux contenus
480        revisions_articles($id_article, $c);
481
482        // En cas de statut ou de id_rubrique
483        // NB: instituer_article veut id_parent, et pas id_rubrique !
484        if (isset($c['id_rubrique'])) {
485                $c['id_parent'] = $c['id_rubrique'];
486                unset($c['id_rubrique']);
487        }
488        instituer_article($id_article, $c);
489}
490
491/**
492 * Enregistre les modifications sur une configuration
493 * suite à un crayon sur une meta
494 *
495 * La colonne est toujours 'valeur' pour ces données.
496 * La donnée à enregistrer peut-être une sous partie de configuration.
497 * Si c'est le cas, on gère l'enregistrement via ecrire_config.
498 *
499 * @param string $a
500 *   Nom ou clé de la meta (descriptif_site ou demo__truc pour demo/truc)
501 * @param bool|array $c
502 *   Liste des champs modifiés
503 *   Ici, 'valeur' normalement.
504 * @return void
505**/
506function revision_meta($a, $c = false) {
507        if (isset($c['valeur'])) {
508                // Certaines cles de configuration sont echapées ici (cf #EDIT_CONFIG{demo/truc})
509                $a = str_replace('__', '/', $a);
510                spip_log("meta '$a' = '$c[valeur]'", 'crayons');
511                // eviter de planter les vieux SPIP
512                if (false === strpos($a, '/')) {
513                        ecrire_meta($a, $c['valeur']);
514                // SPIP 3 ou Bonux 2 ou CFG
515                } else {
516                        include_spip('inc/config');
517                        ecrire_config($a, $c['valeur']);
518                }
519                include_spip('inc/invalideur');
520                suivre_invalideur('meta');
521        }
522}
523
524
525// TODO:
526// Ce modele est cense enregistrer les tags sous forme de ??
527// une ligne dans un champ spip_articles.tags, et/ou des mots-cles...
528function modeles_tags($id, $c) {
529        var_dump($id); #id_article
530        var_dump($c); # perturbant : ici on a array('id_article'=>'valeur envoyee')
531}
532
533function action_crayons_store_dist() {
534        return action_crayons_store_args();
535}
536
537// permettre de passer une autre fonction de stockage des informations
538function action_crayons_store_args($store = 'crayons_store') {
539        header('Content-Type: text/plain; charset='.$GLOBALS['meta']['charset']);
540        lang_select($GLOBALS['auteur_session']['lang']);
541
542        $r = $store();
543
544        // Si on a ete appeles par jQuery, on renvoie tout, c'est le client
545        // crayons.js qui va traiter l'affichage du resultat et status
546        # Attention le test $_SERVER["HTTP_X_REQUESTED_WITH"] === "XMLHttpRequest"
547        # n'est pas bon car le cas d'un fichier uploade via iframe n'est pas detecte
548
549        // S'il y a une adresse de redirection, on renvoie vers elle
550        // En cas d'erreur il faudrait ajouter &err=... dans l'url ?
551        if (_request('redirect')) {
552                if (!$r['$erreur']
553                        or $r['$annuler']) {
554                        include_spip('inc/headers');
555                        redirige_par_entete(_request('redirect'));
556                } else {
557                        echo "<h4 class='status'>".$r['$erreur']."</h4>\n";
558
559                        foreach ($r as $wid => $v) {
560                                if ($wid !== '$erreur') {
561                                        echo "<div id='$wid'>$v</div><hr />\n";
562                                }
563                        }
564                        echo "<a href='".quote_amp(_request('redirect'))."'>"
565                                .quote_amp(_request('redirect'))
566                                ."</a>\n";
567                }
568        } else {
569                // Cas normal : JSON
570                echo crayons_json_export($r);
571        }
572
573        exit;
574}
Note: See TracBrowser for help on using the repository browser.