3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2014 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
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 \***************************************************************************/
13 // fonctions de recherche et de reservation
14 // dans l'arborescence des boucles
16 if (!defined('_ECRIRE_INC_VERSION')) return;
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 *')
26 // http://doc.spip.org/@index_pile
27 function index_pile($idb, $nom_champ, &$boucles, $explicite='') {
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'");
35 $idb = $boucles[$idb]->id_parent
;
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);
47 if (!in_array($t, $boucles[$idb]->select
)) {
48 $boucles[$idb]->select
[] = $t;
50 return '$Pile[$SP' . ($i ?
"-$i" : "") . '][\'' . $c . '\']';
52 # spip_log("On remonte vers $i");
53 // Sinon on remonte d'un cran
54 $idb = $boucles[$idb]->id_parent
;
58 # spip_log("Pas vu $nom_champ");
59 // esperons qu'il y sera
60 return('@$Pile[0][\''. strtolower($nom_champ) . '\']');
63 // http://doc.spip.org/@index_tables_en_pile
64 function index_tables_en_pile($idb, $nom_champ, &$boucles) {
65 global $exceptions_des_tables;
67 $r = $boucles[$idb]->type_requete
;
69 if ($r == 'boucle') return array();
71 # continuer pour chercher l'erreur suivante
72 return array("'#" . $r . ':' . $nom_champ . "'",'');
75 $desc = $boucles[$idb]->show
;
76 $excep = isset($exceptions_des_tables[$r]) ?
$exceptions_des_tables[$r] : '';
78 $excep = isset($excep[$nom_champ]) ?
$excep[$nom_champ] : '';
80 return index_exception($boucles[$idb], $desc, $nom_champ, $excep);
82 if (isset($desc['field'][$nom_champ])) {
83 $t = $boucles[$idb]->id_table
;
84 return array("$t.$nom_champ", $nom_champ);
86 if ($boucles[$idb]->jointures_explicites
) {
87 $t = trouver_champ_exterieur($nom_champ,
88 $boucles[$idb]->jointures
,
91 return index_exception($boucles[$idb],
94 array($t[1]['id_table'], $nom_champ));
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
105 // http://doc.spip.org/@index_exception
106 function index_exception(&$boucle, $desc, $nom_champ, $excep)
108 static $trouver_table;
110 $trouver_table = charger_fonction('trouver_table', 'base');
112 if (is_array($excep)) {
113 // permettre aux plugins de gerer eux meme des jointures derogatoire ingerables
115 if (count($excep)==3){
116 $index_exception_derogatoire = array_pop($excep);
117 $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep);
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('','');
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));
135 $k = array($boucle->id_table
, array($e), $k);
136 fabrique_jointures($boucle, array($k));
137 $t = array_search($e, $boucle->from
);
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);
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
);
155 // cette fonction sert d'API pour demander une balise Spip avec filtres
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);
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.
172 // http://doc.spip.org/@calculer_balise
173 function calculer_balise($nom, $p) {
175 // S'agit-t-il d'une balise_XXXX[_dist]() ?
176 if ($f = charger_fonction($nom, 'balise', true)) {
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)) {
190 $f = charger_fonction('DEFAUT', 'calculer_balise');
195 function calculer_balise_DEFAUT_dist($nom, $p) {
197 // ca pourrait etre un champ SQL homonyme,
198 $p->code
= index_pile($p->id_boucle
, $nom, $p->boucles
, $p->nom_boucle
);
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'";
207 // ne pas passer le filtre securite sur les id_xxx
208 if (strpos($nom, 'ID_') === 0)
209 $p->interdire_scripts
= false;
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)
219 AND !$p->fonctions
) {
220 $p->code
= "'#$nom'";
221 $p->interdire_scripts
= false;
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.
237 define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s',
241 // http://doc.spip.org/@calculer_balise_dynamique
242 function calculer_balise_dynamique($p, $nom, $l, $supp=array()) {
244 if (!balise_distante_interdite($p)) {
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;
254 if ($p->param
AND ($c = $p->param
[0])) {
255 // liste d'arguments commence toujours par la chaine vide
257 // construire la liste d'arguments comme pour un filtre
258 $param = compose_filtres_args($p, $c, ',');
260 $collecte = collecter_balise_dynamique($l, $p, $nom);
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))));
268 $p->interdire_scripts
= false;
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)
278 // http://doc.spip.org/@collecter_balise_dynamique
279 function collecter_balise_dynamique($l, &$p, $nom) {
281 foreach($l as $c) { $x = calculer_balise($c, $p); $args[] = $x->code
;}
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.
291 // http://doc.spip.org/@balise_distante_interdite
292 function balise_distante_interdite($p) {
293 $nom = $p->id_boucle
;
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'));
306 // Traitements standard de divers champs
307 // definis par $table_des_traitements, cf. ecrire/public/interfaces
309 // http://doc.spip.org/@champs_traitements
310 function champs_traitements ($p) {
311 global $table_des_traitements;
313 if (!isset($table_des_traitements[$p->nom_champ
]))
315 $ps = $table_des_traitements[$p->nom_champ
];
320 $type = $p->boucles
[$p->nom_boucle
]->type_requete
;
322 $type = $p->type_requete
;
323 // le traitement peut n'etre defini que pour une table en particulier
324 if (isset($ps[$type]))
326 elseif(isset($ps[0]))
331 if (!$ps) return $p->code
;
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)
339 if (isset($p->descr
['documents'])
341 $p->descr
['documents']
343 (strpos($ps,'propre') !== false)
345 (strpos($ps,'typo') !== false)
347 $ps = 'traiter_doublons_documents($doublons, '.$ps.')';
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
) {
355 case 'syndic_articles':
356 $champs_surs = array(
357 'date', 'date_heure', 'statut', 'ip', 'url_article', 'maj', 'idx'
359 if (!in_array(strtolower($p->nom_champ
), $champs_surs)
360 AND !preg_match(',^ID_,', $p->nom_champ
))
361 $ps = 'safehtml('.$ps.')';
367 // Remplacer enfin le placeholder %s par le vrai code de la balise
368 return str_replace('%s', $p->code
, $ps);
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 !
378 // http://doc.spip.org/@applique_filtres
379 function applique_filtres($p) {
381 // Traitements standards (cf. supra)
382 if ($p->etoile
== '')
383 $code = champs_traitements($p);
387 // Appliquer les filtres perso
389 $code = compose_filtres($p, $code);
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)";
397 if ($p->interdire_scripts
398 AND $p->etoile
!= '**') {
399 if (!preg_match("/^sinon[(](.*),'([^']*)'[)]$/", $code, $r))
400 $code = "interdire_scripts($code)";
402 $code = interdire_scripts($r[2]);
403 $code = "sinon(interdire_scripts($r[1]),'$code')";
409 // Cf. function pipeline dans ecrire/inc_utils.php
410 // http://doc.spip.org/@compose_filtres
411 function compose_filtres(&$p, $code) {
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;
424 // recuperer les arguments du filtre,
425 // a separer par "," ou ":" dans le cas du filtre "?{a,b}"
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]:"");
433 $arglist = compose_filtres_args($p, $filtre, $sep);
434 $logique = filtre_logique($fonc, $code, substr($arglist,1));
438 if (isset($GLOBALS['spip_matrice'][$fonc])) {
439 $code = "filtrer('$fonc',$code$arglist)";
440 if ($is_filtre_image) $image_miette = true;
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)";
448 // le filtre n'existe pas,
450 else erreur_squelette(array('zbug_erreur_filtre', array('filtre'=> texte_script($fonc))), $p);
453 // ramasser les images intermediaires inutiles et graver l'image finale
455 $code = "filtrer('image_graver',$code)";
460 // Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes
462 function filtre_logique($fonc, $code, $arg)
464 global $table_criteres_infixes;
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) ?' ' :'')";
484 // http://doc.spip.org/@compose_filtres_args
485 function compose_filtres_args($p, $args, $sep)
488 foreach ($args as $arg) {
490 calculer_liste($arg, $p->descr
, $p->boucles
, $p->id_boucle
);
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
500 // http://doc.spip.org/@calculer_argument_precedent
501 function calculer_argument_precedent($idb, $nom_champ, &$boucles) {
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);
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));
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"
525 // http://doc.spip.org/@rindex_pile
526 function rindex_pile($p, $champ, $motif)
532 foreach($p->boucles
[$b]->criteres
as $critere) {
533 if ($critere->op
== $motif) {
534 $p->code
= '$Pile[$SP' . (($n==0) ?
"" : "-$n") .
541 $b = $p->boucles
[$b]->id_parent
;
544 // si on est hors d'une boucle de {recherche}, cette balise est vide
548 $p->interdire_scripts
= false;