3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2007 *
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 \***************************************************************************/
15 // fonctions de recherche et de reservation
16 // dans l'arborescence des boucles
18 if (!defined("_ECRIRE_INC_VERSION")) return;
20 // index_pile retourne la position dans la pile du champ SQL $nom_champ
21 // en prenant la boucle la plus proche du sommet de pile (indique par $idb).
22 // Si on ne trouve rien, on considere que ca doit provenir du contexte
23 // (par l'URL ou l'include) qui a ete recopie dans Pile[0]
24 // (un essai d'affinage a debouche sur un bug vicieux)
25 // Si ca reference un champ SQL, on le memorise dans la structure $boucles
26 // afin de construire un requete SQL minimale (plutot qu'un brutal 'SELECT *')
28 // http://doc.spip.org/@index_pile
29 function index_pile($idb, $nom_champ, &$boucles, $explicite='') {
30 global $exceptions_des_tables, $table_des_tables, $tables_des_serveurs_sql;
33 if (strlen($explicite)) {
34 // Recherche d'un champ dans un etage superieur
35 while (($idb != $explicite) && ($idb !='')) {
36 # spip_log("Cherchexpl: $nom_champ '$explicite' '$idb' '$i'");
38 $idb = $boucles[$idb]->id_parent
;
42 # spip_log("Cherche: $nom_champ a partir de '$idb'");
43 $nom_champ = strtolower($nom_champ);
44 // attention: entre la boucle nommee 0, "" et le tableau vide,
45 // il y a incoherences qu'il vaut mieux eviter
46 while (isset($boucles[$idb])) {
47 list ($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles);
49 if (!in_array($t, $boucles[$idb]->select
))
50 $boucles[$idb]->select
[] = $t;
51 return '$Pile[$SP' . ($i ?
"-$i" : "") . '][\'' . $c . '\']';
53 # spip_log("On remonte vers $i");
54 // Sinon on remonte d'un cran
55 $idb = $boucles[$idb]->id_parent
;
59 # spip_log("Pas vu $nom_champ");
60 // esperons qu'il y sera
61 return('$Pile[0][\''. strtolower($nom_champ) . '\']');
65 * retourne la description de la table associee a un type de boucle
66 * retourne un tableau avec les entrees field et key (comme dans serial.php)
67 * et type = type de boucle, serveur = serveur bdd associe et table = nom de
69 * retourne null si on ne trouve pas la table
71 // http://doc.spip.org/@description_type_requete
72 function description_type_requete($type, $serveur='') {
73 global $table_des_tables, $tables_des_serveurs_sql;
77 // indirection (pour les rares cas ou le nom de la table!=type)
78 $t = $table_des_tables[$type];
80 // pour les tables non Spip
82 $nom_table = $t = $type;
84 $nom_table = 'spip_' . $t;
87 $desc = $tables_des_serveurs_sql[$s][$nom_table];
88 if (!isset($desc['field'])) {
89 $desc = $table_des_tables[$type] ?
90 (($GLOBALS['table_prefix'] ?
$GLOBALS['table_prefix'] : 'spip')
91 . '_' . $t) : $nom_table;
93 $desc = spip_abstract_showtable($desc, $serveur);
94 if (!isset($desc['field']))
96 $tables_des_serveurs_sql[$s][$nom_table]= $desc;
100 $desc['table']= $nom_table;
105 // http://doc.spip.org/@index_tables_en_pile
106 function index_tables_en_pile($idb, $nom_champ, &$boucles) {
107 global $exceptions_des_tables;
109 $r = $boucles[$idb]->type_requete
;
110 $s = $boucles[$idb]->sql_serveur
;
112 $desc= description_type_requete($r, $s);
115 erreur_squelette(_T('zbug_table_inconnue', array('table' => $r)),
117 # continuer pour chercher l'erreur suivante
118 return array("'#" . $r . ':' . $nom_champ . "'",'');
122 $excep = isset($exceptions_des_tables[$r]) ?
$exceptions_des_tables[$r] : '';
124 $excep = isset($excep[$nom_champ]) ?
$excep[$nom_champ] : '';
127 return index_exception($boucles[$idb], $desc, $nom_champ, $excep);
129 if (isset($desc['field'][$nom_champ]))
130 return array("$t.$nom_champ", $nom_champ);
132 if ($boucles[$idb]->jointures_explicites
) {
133 $t = trouver_champ_exterieur($nom_champ,
134 $boucles[$idb]->jointures
,
137 return index_exception($boucles[$idb],
140 array($t[0], $nom_champ));
147 // Reference a une entite SPIP alias d'un champ SQL
148 // Ca peut meme etre d'un champ dans une jointure
149 // qu'il faut provoquer si ce n'est fait
151 // http://doc.spip.org/@index_exception
152 function index_exception(&$boucle, $desc, $nom_champ, $excep)
154 global $tables_des_serveurs_sql;
156 if (is_array($excep)) {
157 // permettre aux plugins de gerer eux meme des jointures derogatoire ingérables
159 if (count($excep)==3){
160 $index_exception_derogatoire = array_pop($excep);
161 $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep);
164 list($e, $x) = $excep; #PHP4 affecte de gauche a droite
165 $excep = $x; #PHP5 de droite a gauche !
166 if (!$t = array_search($e, $boucle->from
)) {
167 $t = 'J' . count($boucle->from
);
168 $boucle->from
[$t] = $e;
169 $j = $tables_des_serveurs_sql[$desc['serveur']][$e];
170 # essayer ca un jour: list($nom, $j) = trouver_def_table($e, $boucle);
171 $j = $j['key']['PRIMARY KEY'];
172 $boucle->where
[]= array("'='", "'$boucle->id_table." . "$j'", "'$t.$j'");
176 else $t = $desc['type'];
177 // demander a SQL de gerer le synonyme
178 // ca permet que excep soit dynamique (Cedric, 2/3/06)
179 if ($excep != $nom_champ) $excep .= ' AS '. $nom_champ;
180 return array("$t.$excep", $nom_champ);
184 // cette fonction sert d'API pour demander le champ '$champ' dans la pile
185 // http://doc.spip.org/@champ_sql
186 function champ_sql($champ, $p) {
187 return index_pile($p->id_boucle
, $champ, $p->boucles
, $p->nom_boucle
);
190 // cette fonction sert d'API pour demander une balise Spip avec filtres
192 // http://doc.spip.org/@calculer_champ
193 function calculer_champ($p) {
194 $p = calculer_balise($p->nom_champ
, $p);
195 return applique_filtres($p);
198 // Cette fonction sert d'API pour demander une balise SPIP sans filtres.
199 // Pour une balise nommmee NOM, elle demande a charger_fonction de chercher
200 // s'il existe une fonction balise_NOM ou balise_NOM_dist
201 // eventuellement en chargeant le fichier balise/NOM.php.
202 // Si ce n'est pas le cas, hormis le cas historique des balise LOGO_*,
203 // elle estime que c'est une reference a une colonne de table connue.
204 // Les surcharges via charger_fonction sont donc possibles.
206 // http://doc.spip.org/@calculer_balise
207 function calculer_balise($nom, $p) {
209 // S'agit-t-il d'une balise_XXXX[_dist]() ?
210 if ($f = charger_fonction($nom, 'balise', true
)) {
216 // S'agit-il d'un logo ? Une fonction speciale les traite tous
217 if (ereg('^LOGO_', $nom)) {
218 if (!function_exists($f = 'calculer_balise_logo')) $f .= '_dist';
224 // ca pourrait etre un champ SQL homonyme,
225 $p->code
= index_pile($p->id_boucle
, $nom, $p->boucles
, $p->nom_boucle
);
227 // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour
228 // il faut recracher {...} quand ce n'est finalement pas des args
229 if ($p->fonctions
AND (!$p->fonctions
[0][0]) AND $p->fonctions
[0][1]) {
230 $code = addslashes($p->fonctions
[0][1]);
231 $p->code
.= " . '$code'";
234 // ne pas passer le filtre securite sur les id_xxx
235 if (strpos($nom, 'ID_') === 0)
236 $p->interdire_scripts
= false
;
238 // Compatibilite ascendante avec les couleurs html (#FEFEFE) :
239 // SI le champ SQL n'est pas trouve
240 // ET si la balise a une forme de couleur
241 // ET s'il n'y a ni filtre ni etoile
242 // ALORS retourner la couleur.
243 // Ca permet si l'on veut vraiment de recuperer [(#ACCEDE*)]
244 if (preg_match("/^[A-F]{1,6}$/i", $nom)
246 AND !$p->fonctions
) {
247 $p->code
= "'#$nom'";
248 $p->interdire_scripts
= false
;
256 L'appel direct de #ARTICLE_TRADUCTIONS devient #MODELE{article_traductions}
258 // fonction speciale d'appel a un modele modeles/truc.html pour la balise #TRUC
259 // exemples : #TRADUCTIONS, #DOC, #IMG...
260 // http://doc.spip.org/@calculer_balise_modele_dist
261 function calculer_balise_modele_dist($p){
262 $nom = strtolower($p->nom_champ);
265 if (isset($p->param[0])){
266 while (count($p->param[0])>2){
267 $p->param[]=array($p->param[0][0],array_pop($p->param[0]));
271 $champ = phraser_arguments_inclure($p, true);
273 // si false, le compilo va bloquer sur des syntaxes avec un filtre sans argument qui suit la balise
274 // si true, les arguments simples (sans truc=chose) vont degager
275 $code_contexte = argumenter_inclure($champ, $p->descr, $p->boucles, $p->id_boucle, false);
277 // Si le champ existe dans la pile, on le met dans le contexte
278 // (a priori c'est du code mort ; il servait pour #LESAUTEURS dans
279 // le cas spip_syndic_articles)
280 #$code_contexte[] = "'$nom='.".champ_sql($nom, $p);
282 // Reserver la cle primaire de la boucle courante
283 if ($primary = $p->boucles[$p->id_boucle]->primary) {
284 $id = champ_sql($primary, $p);
285 $code_contexte[] = "'$primary='.".$id;
288 #print_r($code_contexte);
290 $p->code = "( ((\$recurs=(isset(\$Pile[0]['recurs'])?\$Pile[0]['recurs']:0))<5)?
291 recuperer_fond('modeles/".$nom."',
292 creer_contexte_de_modele(array(".join(',', $code_contexte).",'recurs='.++\$recurs, \$GLOBALS['spip_lang']))):'')";
293 $p->interdire_scripts = false; // securite assuree par le squelette
295 print $p->code."\n<hr/>\n";
302 // Traduction des balises dynamiques, notamment les "formulaire_*"
303 // Inclusion du fichier associe a son nom.
304 // Ca donne les arguments a chercher dans la pile,on compile leur localisation
305 // Ensuite on delegue a une fonction generale definie dans executer_squelette
306 // qui recevra a l'execution la valeur des arguments,
307 // ainsi que les pseudo filtres qui ne sont donc pas traites a la compil
308 // mais on traite le vrai parametre si present.
310 // http://doc.spip.org/@calculer_balise_dynamique
311 function calculer_balise_dynamique($p, $nom, $l) {
313 balise_distante_interdite($p);
315 if ($a = $p->param
) {
316 $c = array_shift($a);
317 if (!array_shift($c)) {
319 array_shift( $p->param
);
320 $param = compose_filtres_args($p, $c, ',');
323 $collecte = join(',',collecter_balise_dynamique($l, $p, $nom));
324 $p->code
= "executer_balise_dynamique('" . $nom . "',\n\tarray("
326 . ($collecte ?
$param : substr($param,1)) # virer la virgule
328 . argumenter_balise($p->param
, "', '")
329 . "), \$GLOBALS['spip_lang'],"
332 $p->interdire_scripts
= false
;
333 $p->fonctions
= array();
339 // Construction du tableau des arguments d'une balise dynamique.
340 // Ces arguments peuvent etre eux-meme des balises (cf FORMULAIRE_SIGNATURE)
341 // mais gare au bouclage (on peut s'aider de $nom pour le reperer au besoin)
343 // http://doc.spip.org/@collecter_balise_dynamique
344 function collecter_balise_dynamique($l, &$p, $nom) {
346 foreach($l as $c) { $x = calculer_balise($c, $p); $args[] = $x->code
;}
351 // il faudrait savoir traiter les formulaires en local
352 // tout en appelant le serveur SQL distant.
353 // En attendant, cette fonction permet de refuser une authentification
354 // sur qqch qui n'a rien a voir.
356 // http://doc.spip.org/@balise_distante_interdite
357 function balise_distante_interdite($p) {
358 $nom = $p->id_boucle
;
359 if ($nom AND $p->boucles
[$nom]->sql_serveur
) {
360 erreur_squelette($p->nom_champ
.' '._T('zbug_distant_interdit'), $nom);
366 // Traitements standard de divers champs
367 // definis par $table_des_traitements, cf. inc-compilo-api.php3
369 // http://doc.spip.org/@champs_traitements
370 function champs_traitements ($p) {
371 global $table_des_traitements;
373 if (!isset($table_des_traitements[$p->nom_champ
]))
375 $ps = $table_des_traitements[$p->nom_champ
];
380 $type = $p->boucles
[$p->nom_boucle
]->type_requete
;
382 $type = $p->type_requete
;
383 $ps = $ps[isset($ps[$type]) ?
$type : 0];
386 if (!$ps) return $p->code
;
388 // Si une boucle sous-jacente (?) traite les documents, on insere ici
389 // une fonction de remplissage du tableau des doublons -- mais seulement
390 // si on rencontre le filtre propre (qui traite les
391 // raccourcis <docXX> qui nous interessent)
392 if (isset($p->descr
['documents'])
393 AND preg_match(',propre,', $ps))
394 $ps = 'traiter_doublons_documents($doublons, '.$ps.')';
396 // De meme, en cas de sql_serveur, on supprime les < IMGnnn > tant
397 // qu'on ne rapatrie pas les documents distants joints..
398 // il faudrait aussi corriger les raccourcis d'URL locales
399 if ($p->id_boucle
AND $p->boucles
[$p->id_boucle
]->sql_serveur
)
400 $p->code
= 'supprime_img(' . $p->code
. ')';
403 // Passer |safehtml sur les boucles "sensibles"
404 // sauf sur les champs dont on est surs
405 switch ($p->type_requete
) {
408 case 'syndic_articles':
409 $champs_surs = array(
410 'date', 'date_heure', 'statut', 'ip', 'url_article', 'maj', 'idx',
412 if (!in_array(strtolower($p->nom_champ
), $champs_surs)
413 AND !preg_match(',^ID_,', $p->nom_champ
))
414 $ps = 'safehtml('.$ps.')';
420 // Remplacer enfin le placeholder %s par le vrai code de la balise
421 return str_replace('%s', $p->code
, $ps);
426 // Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)]
427 // retourne un code php compile exprimant ce champ filtre et securise
428 // - une etoile => pas de processeurs standards
429 // - deux etoiles => pas de securite non plus !
431 // http://doc.spip.org/@applique_filtres
432 function applique_filtres($p) {
434 // Traitements standards (cf. supra)
435 if ($p->etoile
== '')
436 $code = champs_traitements($p);
440 // Appliquer les filtres perso
442 $code = compose_filtres($p, $code);
445 if ($p->interdire_scripts
446 AND $p->etoile
!= '**')
447 $code = "interdire_scripts($code)";
452 // Cf. function pipeline dans ecrire/inc_utils.php
453 // http://doc.spip.org/@compose_filtres
454 function compose_filtres($p, $code) {
455 foreach($p->param
as $filtre) {
456 $fonc = array_shift($filtre);
458 // recuperer les arguments du filtre, les separer par des virgules
459 // *sauf* dans le cas du filtre "?" qui demande un ":"
461 // |?{a,b} *doit* avoir exactement 2 arguments ; on les force
462 if (count($filtre) != 2)
463 $filtre = array($filtre[0], $filtre[1]);
464 $arglist = compose_filtres_args($p, $filtre, ':');
466 $arglist = compose_filtres_args($p, $filtre, ',');
468 // le filtre est defini dans la matrice ? il faut alors l'appeler
469 // de maniere indirecte, pour charger au prealable sa definition
470 if (isset($GLOBALS['spip_matrice'][$fonc])) {
471 $code = "filtrer('$fonc',$code$arglist)";
473 // le filtre est defini sous forme de fonction ou de methode
474 // par ex. dans inc_texte, inc_filtres ou mes_fonctions
475 else if (function_exists($fonc)
476 OR (preg_match("/^(\w*)::(\w*)$/", $fonc, $regs)
477 AND is_callable(array($regs[1], $regs[2]))
479 $code = "$fonc($code$arglist)";
481 else if (strpos("x < > <= >= == === != !== <> ? ", " $fonc "))
482 $code = "($code $fonc " . substr($arglist,1) . ')';
484 $code = "erreur_squelette('"
485 .texte_script(_T('zbug_erreur_filtre', array('filtre'=>$fonc)))
486 ."','" . $p->id_boucle
. "')";
492 // http://doc.spip.org/@compose_filtres_args
493 function compose_filtres_args($p, $args, $sep)
496 foreach ($args as $arg) {
498 calculer_liste($arg, $p->descr
, $p->boucles
, $p->id_boucle
);
504 // Reserve les champs necessaires a la comparaison avec le contexte donne par
505 // la boucle parente ; attention en recursif il faut les reserver chez soi-meme
508 // http://doc.spip.org/@calculer_argument_precedent
509 function calculer_argument_precedent($idb, $nom_champ, &$boucles) {
511 // si recursif, forcer l'extraction du champ SQL mais ignorer le code
512 if ($boucles[$idb]->externe
)
513 index_pile ($idb, $nom_champ, $boucles);
514 // retourner $Pile[$SP] et pas $Pile[0] (bug recursion en 1ere boucle)
515 $prec = $boucles[$idb]->id_parent
;
516 return (($prec==="") ?
('$Pile[$SP][\''.$nom_champ.'\']') :
517 index_pile($prec, $nom_champ, $boucles));
521 // Rechercher dans la pile des boucles actives celle ayant un critere
522 // comportant un certain $motif, et construire alors une reference
523 // a l'environnement de cette boucle, qu'on indexe avec $champ.
524 // Sert a referencer une cellule non declaree dans la table et pourtant la.
525 // Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points']
526 // si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit
527 // "SELECT XXXX AS points"
530 // http://doc.spip.org/@rindex_pile
531 function rindex_pile($p, $champ, $motif)
537 if ($s = $p->boucles
[$b]->param
) {
539 if (strpos($v[1][0]->texte
,$motif) !== false
) {
540 $p->code
= '$Pile[$SP' . (($n==0) ?
"" : "-$n") .
548 $b = $p->boucles
[$b]->id_parent
;
551 erreur_squelette(_T('zbug_champ_hors_motif',
552 array('champ' => '#' . strtoupper($champ),
556 $p->interdire_scripts
= false
;