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