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