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