[SPIP] +2.1.12
[velocampus/web/www.git] / www / ecrire / public / references.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2011 *
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 // fonctions de recherche et de reservation
14 // dans l'arborescence des boucles
15
16 if (!defined('_ECRIRE_INC_VERSION')) return;
17
18 // index_pile retourne la position dans la pile du champ SQL $nom_champ
19 // en prenant la boucle la plus proche du sommet de pile (indique par $idb).
20 // Si on ne trouve rien, on considere que ca doit provenir du contexte
21 // (par l'URL ou l'include) qui a ete recopie dans Pile[0]
22 // (un essai d'affinage a debouche sur un bug vicieux)
23 // Si ca reference un champ SQL, on le memorise dans la structure $boucles
24 // afin de construire un requete SQL minimale (plutot qu'un brutal 'SELECT *')
25
26 // http://doc.spip.org/@index_pile
27 function index_pile($idb, $nom_champ, &$boucles, $explicite='') {
28
29 $i = 0;
30 if (strlen($explicite)) {
31 // Recherche d'un champ dans un etage superieur
32 while (($idb !== $explicite) && ($idb !=='')) {
33 # spip_log("Cherchexpl: $nom_champ '$explicite' '$idb' '$i'");
34 $i++;
35 $idb = $boucles[$idb]->id_parent;
36 }
37 }
38
39 # spip_log("Cherche: $nom_champ a partir de '$idb'");
40 $nom_champ = strtolower($nom_champ);
41 // attention: entre la boucle nommee 0, "" et le tableau vide,
42 // il y a incoherences qu'il vaut mieux eviter
43 while (isset($boucles[$idb])) {
44 list ($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles);
45
46 if ($t) {
47 if (!in_array($t, $boucles[$idb]->select)) {
48 $boucles[$idb]->select[] = $t;
49 }
50 return '$Pile[$SP' . ($i ? "-$i" : "") . '][\'' . $c . '\']';
51 }
52 # spip_log("On remonte vers $i");
53 // Sinon on remonte d'un cran
54 $idb = $boucles[$idb]->id_parent;
55 $i++;
56 }
57
58 # spip_log("Pas vu $nom_champ");
59 // esperons qu'il y sera
60 return('@$Pile[0][\''. strtolower($nom_champ) . '\']');
61 }
62
63 // http://doc.spip.org/@index_tables_en_pile
64 function index_tables_en_pile($idb, $nom_champ, &$boucles) {
65 global $exceptions_des_tables;
66
67 $r = $boucles[$idb]->type_requete;
68
69 if ($r == 'boucle') return array();
70 if (!$r) {
71 # continuer pour chercher l'erreur suivante
72 return array("'#" . $r . ':' . $nom_champ . "'",'');
73 }
74
75 $desc = $boucles[$idb]->show;
76 $excep = isset($exceptions_des_tables[$r]) ? $exceptions_des_tables[$r] : '';
77 if ($excep)
78 $excep = isset($excep[$nom_champ]) ? $excep[$nom_champ] : '';
79 if ($excep) {
80 return index_exception($boucles[$idb], $desc, $nom_champ, $excep);
81 } else {
82 if (isset($desc['field'][$nom_champ])) {
83 $t = $boucles[$idb]->id_table;
84 return array("$t.$nom_champ", $nom_champ);
85 } else {
86 if ($boucles[$idb]->jointures_explicites) {
87 $t = trouver_champ_exterieur($nom_champ,
88 $boucles[$idb]->jointures,
89 $boucles[$idb]);
90 if ($t)
91 return index_exception($boucles[$idb],
92 $desc,
93 $nom_champ,
94 array($t[1]['id_table'], $nom_champ));
95 }
96 return array('','');
97 }
98 }
99 }
100
101 // Reference a une entite SPIP alias d'un champ SQL
102 // Ca peut meme etre d'un champ dans une jointure
103 // qu'il faut provoquer si ce n'est fait
104
105 // http://doc.spip.org/@index_exception
106 function index_exception(&$boucle, $desc, $nom_champ, $excep)
107 {
108 static $trouver_table;
109 if (!$trouver_table)
110 $trouver_table = charger_fonction('trouver_table', 'base');
111
112 if (is_array($excep)) {
113 // permettre aux plugins de gerer eux meme des jointures derogatoire ingerables
114 $t = NULL;
115 if (count($excep)==3){
116 $index_exception_derogatoire = array_pop($excep);
117 $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep);
118 }
119 if ($t == NULL) {
120 list($e, $x) = $excep; #PHP4 affecte de gauche a droite
121 $excep = $x; #PHP5 de droite a gauche !
122 $j = $trouver_table($e, $boucle->sql_serveur);
123 if (!$j) return array('','');
124 $e = $j['table'];
125 if (!$t = array_search($e, $boucle->from)) {
126 $k = $j['key']['PRIMARY KEY'];
127 if (strpos($k,',')) {
128 $l = (preg_split('/\s*,\s*/', $k));
129 $k = $desc['key']['PRIMARY KEY'];
130 if (!in_array($k, $l)) {
131 spip_log("jointure impossible $e " . join(',', $l));
132 return array('','');
133 }
134 }
135 $k = array($boucle->id_table, array($e), $k);
136 fabrique_jointures($boucle, array($k));
137 $t = array_search($e, $boucle->from);
138 }
139 }
140 }
141 else $t = $boucle->id_table;
142 // demander a SQL de gerer le synonyme
143 // ca permet que excep soit dynamique (Cedric, 2/3/06)
144 if ($excep != $nom_champ) $excep .= ' AS '. $nom_champ;
145 return array("$t.$excep", $nom_champ);
146 }
147
148
149 // cette fonction sert d'API pour demander le champ '$champ' dans la pile
150 // http://doc.spip.org/@champ_sql
151 function champ_sql($champ, $p) {
152 return index_pile($p->id_boucle, $champ, $p->boucles, $p->nom_boucle);
153 }
154
155 // cette fonction sert d'API pour demander une balise Spip avec filtres
156
157 // http://doc.spip.org/@calculer_champ
158 function calculer_champ($p) {
159 $p = calculer_balise($p->nom_champ, $p);
160 return applique_filtres($p);
161 }
162
163 // Cette fonction sert d'API pour demander une balise SPIP sans filtres.
164 // Pour une balise nommmee NOM, elle demande a charger_fonction de chercher
165 // s'il existe une fonction balise_NOM ou balise_NOM_dist
166 // eventuellement en chargeant le fichier balise/NOM.php.
167 // Si la balise est de la forme PREFIXE_SUFFIXE (cf LOGO_* et URL_*)
168 // elle fait de meme avec juste le PREFIXE.
169 // Si pas de fonction, c'est une reference a une colonne de table SQL connue.
170 // Les surcharges des colonnes SQL via charger_fonction sont donc possibles.
171
172 // http://doc.spip.org/@calculer_balise
173 function calculer_balise($nom, $p) {
174
175 // S'agit-t-il d'une balise_XXXX[_dist]() ?
176 if ($f = charger_fonction($nom, 'balise', true)) {
177 $res = $f($p);
178 if ($res !== NULL)
179 return $res;
180 }
181
182 // Certaines des balises comportant un _ sont generiques
183 if ($f = strpos($nom, '_')
184 AND $f = charger_fonction(substr($nom,0,$f+1), 'balise', true)) {
185 $res = $f($p);
186 if ($res !== NULL)
187 return $res;
188 }
189
190 $f = charger_fonction('DEFAUT', 'calculer_balise');
191
192 return $f($nom, $p);
193 }
194
195 function calculer_balise_DEFAUT_dist($nom, $p) {
196
197 // ca pourrait etre un champ SQL homonyme,
198 $p->code = index_pile($p->id_boucle, $nom, $p->boucles, $p->nom_boucle);
199
200 // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
201 // il faut recracher {...} quand ce n'est finalement pas des args
202 if ($p->fonctions AND (!$p->fonctions[0][0]) AND $p->fonctions[0][1]) {
203 $code = addslashes($p->fonctions[0][1]);
204 $p->code .= " . '$code'";
205 }
206
207 // ne pas passer le filtre securite sur les id_xxx
208 if (strpos($nom, 'ID_') === 0)
209 $p->interdire_scripts = false;
210
211 // Compatibilite ascendante avec les couleurs html (#FEFEFE) :
212 // SI le champ SQL n'est pas trouve
213 // ET si la balise a une forme de couleur
214 // ET s'il n'y a ni filtre ni etoile
215 // ALORS retourner la couleur.
216 // Ca permet si l'on veut vraiment de recuperer [(#ACCEDE*)]
217 if (preg_match("/^[A-F]{1,6}$/i", $nom)
218 AND !$p->etoile
219 AND !$p->fonctions) {
220 $p->code = "'#$nom'";
221 $p->interdire_scripts = false;
222 }
223
224 return $p;
225 }
226
227
228 //
229 // Traduction des balises dynamiques, notamment les "formulaire_*"
230 // Inclusion du fichier associe a son nom, qui contient la fonction homonyme
231 // donnant les arguments a chercher dans la pile, et qui sont donc compiles.
232 // On leur adjoint les arguments explicites de la balise (cf #LOGIN{url})
233 // et d'eventuelles valeurs transmises d'autorite par la balise.
234 // (cf http://trac.rezo.net/trac/spip/ticket/1728)
235 // La fonction nommee ci-dessous recevra a l'execution la valeur de tout ca.
236
237 define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s',
238 array(%s%s),
239 array(%s%s))");
240
241 // http://doc.spip.org/@calculer_balise_dynamique
242 function calculer_balise_dynamique($p, $nom, $l, $supp=array()) {
243
244 if (!balise_distante_interdite($p)) {
245 $p->code = "''";
246 return $p;
247 }
248 // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
249 // il faut recracher {...} quand ce n'est finalement pas des args
250 if ($p->fonctions AND (!$p->fonctions[0][0]) AND $p->fonctions[0][1]) {
251 $p->fonctions = null;
252 }
253
254 if ($p->param AND ($c = $p->param[0])) {
255 // liste d'arguments commence toujours par la chaine vide
256 array_shift($c);
257 // construire la liste d'arguments comme pour un filtre
258 $param = compose_filtres_args($p, $c, ',');
259 } else $param = "";
260 $collecte = collecter_balise_dynamique($l, $p, $nom);
261
262 $p->code = sprintf(CODE_EXECUTER_BALISE, $nom,
263 join(',', $collecte),
264 ($collecte ? $param : substr($param,1)), # virer la virgule
265 memoriser_contexte_compil($p),
266 (!$supp ? '' : (', ' . join(',', $supp))));
267
268 $p->interdire_scripts = false;
269 return $p;
270 }
271
272 // Construction du tableau des arguments d'une balise dynamique.
273 // Ces arguments peuvent etre eux-meme des balises (cf FORMULAIRE_SIGNATURE)
274 // mais gare au bouclage (on peut s'aider de $nom pour le reperer au besoin)
275 // En revanche ils n'ont pas de filtres, donc on appelle calculer_balise qui
276 // ne s'occupe pas de ce qu'il y a dans $p (mais qui va y ecrire le code)
277
278 // http://doc.spip.org/@collecter_balise_dynamique
279 function collecter_balise_dynamique($l, &$p, $nom) {
280 $args = array();
281 foreach($l as $c) { $x = calculer_balise($c, $p); $args[] = $x->code;}
282 return $args;
283 }
284
285
286 // il faudrait savoir traiter les formulaires en local
287 // tout en appelant le serveur SQL distant.
288 // En attendant, cette fonction permet de refuser une authentification
289 // sur qqch qui n'a rien a voir.
290
291 // http://doc.spip.org/@balise_distante_interdite
292 function balise_distante_interdite($p) {
293 $nom = $p->id_boucle;
294
295 if ($nom
296 AND $p->boucles[$nom]->sql_serveur
297 AND !in_array($p->boucles[$nom]->sql_serveur,$GLOBALS['exception_des_connect'])) {
298 spip_log( $nom .':' . $p->nom_champ .' '._T('zbug_distant_interdit'));
299 return false;
300 }
301 return true;
302 }
303
304
305 //
306 // Traitements standard de divers champs
307 // definis par $table_des_traitements, cf. ecrire/public/interfaces
308 //
309 // http://doc.spip.org/@champs_traitements
310 function champs_traitements ($p) {
311 global $table_des_traitements;
312
313 if (!isset($table_des_traitements[$p->nom_champ]))
314 return $p->code;
315 $ps = $table_des_traitements[$p->nom_champ];
316 if (is_array($ps)) {
317 // new style
318
319 if ($p->nom_boucle)
320 $type = $p->boucles[$p->nom_boucle]->type_requete;
321 else
322 $type = $p->type_requete;
323 // le traitement peut n'etre defini que pour une table en particulier
324 if (isset($ps[$type]))
325 $ps = $ps[$type];
326 elseif(isset($ps[0]))
327 $ps = $ps[0];
328 else $ps=false;
329 }
330
331 if (!$ps) return $p->code;
332
333 // Si une boucle DOCUMENTS{doublons} est presente dans le squelette,
334 // ou si in INCLURE contient {doublons}
335 // on insere une fonction de remplissage du tableau des doublons
336 // dans les filtres propre() ou typo()
337 // (qui traitent les raccourcis <docXX> referencant les docs)
338
339 if (isset($p->descr['documents'])
340 AND
341 $p->descr['documents']
342 AND (
343 (strpos($ps,'propre') !== false)
344 OR
345 (strpos($ps,'typo') !== false)
346 ))
347 $ps = 'traiter_doublons_documents($doublons, '.$ps.')';
348
349 // Passer |safehtml sur les boucles "sensibles"
350 // sauf sur les champs dont on est surs
351 // ces exceptions doivent etre ventilees dans les plugins fonctionnels concernes
352 // dans la globale table_des_traitements
353 switch ($p->type_requete) {
354 case 'signatures':
355 case 'syndic_articles':
356 $champs_surs = array(
357 'date', 'date_heure', 'statut', 'ip', 'url_article', 'maj', 'idx'
358 );
359 if (!in_array(strtolower($p->nom_champ), $champs_surs)
360 AND !preg_match(',^ID_,', $p->nom_champ))
361 $ps = 'safehtml('.$ps.')';
362 break;
363 default:
364 break;
365 }
366
367 // Remplacer enfin le placeholder %s par le vrai code de la balise
368 return str_replace('%s', $p->code, $ps);
369 }
370
371
372 //
373 // Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)]
374 // retourne un code php compile exprimant ce champ filtre et securise
375 // - une etoile => pas de processeurs standards
376 // - deux etoiles => pas de securite non plus !
377 //
378 // http://doc.spip.org/@applique_filtres
379 function applique_filtres($p) {
380
381 // Traitements standards (cf. supra)
382 if ($p->etoile == '')
383 $code = champs_traitements($p);
384 else
385 $code = $p->code;
386
387 // Appliquer les filtres perso
388 if ($p->param)
389 $code = compose_filtres($p, $code);
390
391 // S'il y a un lien avec la session, ajouter un code qui levera
392 // un drapeau dans la structure d'invalidation $Cache
393 if (isset($p->descr['session']))
394 $code = "invalideur_session(\$Cache, $code)";
395
396 // Securite
397 if ($p->interdire_scripts
398 AND $p->etoile != '**') {
399 if (!preg_match("/^sinon[(](.*),'([^']*)'[)]$/", $code, $r))
400 $code = "interdire_scripts($code)";
401 else {
402 $code = interdire_scripts($r[2]);
403 $code = "sinon(interdire_scripts($r[1]),'$code')";
404 }
405 }
406 return $code;
407 }
408
409 // Cf. function pipeline dans ecrire/inc_utils.php
410 // http://doc.spip.org/@compose_filtres
411 function compose_filtres(&$p, $code) {
412
413 $image_miette = false;
414 foreach($p->param as $filtre) {
415 $fonc = array_shift($filtre);
416 if (!$fonc) continue; // normalement qu'au premier tour.
417 $is_filtre_image = ((substr($fonc,0,6)=='image_') AND $fonc!='image_graver');
418 if ($image_miette AND !$is_filtre_image){
419 // il faut graver maintenant car apres le filtre en cours
420 // on est pas sur d'avoir encore le nom du fichier dans le pipe
421 $code = "filtrer('image_graver', $code)";
422 $image_miette = false;
423 }
424 // recuperer les arguments du filtre,
425 // a separer par "," ou ":" dans le cas du filtre "?{a,b}"
426 if ($fonc !== '?') {
427 $sep = ',';
428 } else {$sep = ':';
429 // |?{a,b} *doit* avoir exactement 2 arguments ; on les force
430 if (count($filtre) != 2)
431 $filtre = array(isset($filtre[0])?$filtre[0]:"", isset($filtre[1])?$filtre[1]:"");
432 }
433 $arglist = compose_filtres_args($p, $filtre, $sep);
434 $logique = filtre_logique($fonc, $code, substr($arglist,1));
435 if ($logique)
436 $code = $logique;
437 else {
438 if (isset($GLOBALS['spip_matrice'][$fonc])) {
439 $code = "filtrer('$fonc',$code$arglist)";
440 if ($is_filtre_image) $image_miette = true;
441 }
442
443 // le filtre est defini sous forme de fonction ou de methode
444 // par ex. dans inc_texte, inc_filtres ou mes_fonctions
445 elseif ($f = chercher_filtre($fonc)) {
446 $code = "$f($code$arglist)";
447 }
448 // le filtre n'existe pas,
449 // on le notifie
450 else erreur_squelette(array('zbug_erreur_filtre', array('filtre'=> texte_script($fonc))), $p);
451 }
452 }
453 // ramasser les images intermediaires inutiles et graver l'image finale
454 if ($image_miette)
455 $code = "filtrer('image_graver',$code)";
456
457 return $code;
458 }
459
460 // Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes
461 // et comparateurs
462 function filtre_logique($fonc, $code, $arg)
463 {
464 global $table_criteres_infixes;
465 switch (true) {
466 case in_array($fonc, $table_criteres_infixes):
467 return "($code $fonc $arg)";
468 case ($fonc == 'and') OR ($fonc == 'et'):
469 return "((($code) AND ($arg)) ?' ' :'')";
470 case ($fonc == 'or') OR ($fonc == 'ou'):
471 return "((($code) OR ($arg)) ?' ' :'')";
472 case ($fonc == 'xor') OR ($fonc == 'xou'):
473 return "((($code) XOR ($arg)) ?' ' :'')";
474 case ($fonc == 'sinon'):
475 return "(((\$a = $code) OR (!is_array(\$a) AND strlen(\$a))) ? \$a : $arg)";
476 case ($fonc == 'not') OR ($fonc == 'non'):
477 return "(($code) ?'' :' ')";
478 case ($fonc == 'yes') OR ($fonc == 'oui'):
479 return "(($code) ?' ' :'')";
480 }
481 return '';
482 }
483
484 // http://doc.spip.org/@compose_filtres_args
485 function compose_filtres_args($p, $args, $sep)
486 {
487 $arglist = "";
488 foreach ($args as $arg) {
489 $arglist .= $sep .
490 calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle);
491 }
492 return $arglist;
493 }
494
495 //
496 // Reserve les champs necessaires a la comparaison avec le contexte donne par
497 // la boucle parente ; attention en recursif il faut les reserver chez soi-meme
498 // ET chez sa maman
499 //
500 // http://doc.spip.org/@calculer_argument_precedent
501 function calculer_argument_precedent($idb, $nom_champ, &$boucles) {
502
503 // si recursif, forcer l'extraction du champ SQL mais ignorer le code
504 if ($boucles[$idb]->externe) {
505 index_pile ($idb, $nom_champ, $boucles);
506 $zero = '$SP';
507 } else $zero = '0';
508 // retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle
509 $prec = $boucles[$idb]->id_parent;
510 return (($prec === '')
511 ? ('$Pile[' . $zero . "]['$nom_champ']")
512 : index_pile($prec, $nom_champ, $boucles));
513 }
514
515 //
516 // Rechercher dans la pile des boucles actives celle ayant un critere
517 // comportant un certain $motif, et construire alors une reference
518 // a l'environnement de cette boucle, qu'on indexe avec $champ.
519 // Sert a referencer une cellule non declaree dans la table et pourtant la.
520 // Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points']
521 // si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit
522 // "SELECT XXXX AS points"
523 //
524
525 // http://doc.spip.org/@rindex_pile
526 function rindex_pile($p, $champ, $motif)
527 {
528 $n = 0;
529 $b = $p->id_boucle;
530 $p->code = '';
531 while ($b != '') {
532 foreach($p->boucles[$b]->criteres as $critere) {
533 if ($critere->op == $motif) {
534 $p->code = '$Pile[$SP' . (($n==0) ? "" : "-$n") .
535 "]['$champ']";
536 $b = '';
537 break 2;
538 }
539 }
540 $n++;
541 $b = $p->boucles[$b]->id_parent;
542 }
543
544 // si on est hors d'une boucle de {recherche}, cette balise est vide
545 if (!$p->code)
546 $p->code = "''";
547
548 $p->interdire_scripts = false;
549 return $p;
550 }
551
552 ?>