id_parent; } } # spip_log("Cherche: $nom_champ a partir de '$idb'"); $nom_champ = strtolower($nom_champ); // attention: entre la boucle nommee 0, "" et le tableau vide, // il y a incoherences qu'il vaut mieux eviter while (isset($boucles[$idb])) { list ($t, $c) = index_tables_en_pile($idb, $nom_champ, $boucles); if ($t) { if (!in_array($t, $boucles[$idb]->select)) { $boucles[$idb]->select[] = $t; } return '$Pile[$SP' . ($i ? "-$i" : "") . '][\'' . $c . '\']'; } # spip_log("On remonte vers $i"); // Sinon on remonte d'un cran $idb = $boucles[$idb]->id_parent; $i++; } # spip_log("Pas vu $nom_champ"); // esperons qu'il y sera return('@$Pile[0][\''. strtolower($nom_champ) . '\']'); } // http://doc.spip.org/@index_tables_en_pile function index_tables_en_pile($idb, $nom_champ, &$boucles) { global $exceptions_des_tables; $r = $boucles[$idb]->type_requete; if ($r == 'boucle') return array(); if (!$r) { # continuer pour chercher l'erreur suivante return array("'#" . $r . ':' . $nom_champ . "'",''); } $desc = $boucles[$idb]->show; $excep = isset($exceptions_des_tables[$r]) ? $exceptions_des_tables[$r] : ''; if ($excep) $excep = isset($excep[$nom_champ]) ? $excep[$nom_champ] : ''; if ($excep) { return index_exception($boucles[$idb], $desc, $nom_champ, $excep); } else { if (isset($desc['field'][$nom_champ])) { $t = $boucles[$idb]->id_table; return array("$t.$nom_champ", $nom_champ); } else { if ($boucles[$idb]->jointures_explicites) { $t = trouver_champ_exterieur($nom_champ, $boucles[$idb]->jointures, $boucles[$idb]); if ($t) return index_exception($boucles[$idb], $desc, $nom_champ, array($t[1]['id_table'], $nom_champ)); } return array('',''); } } } // Reference a une entite SPIP alias d'un champ SQL // Ca peut meme etre d'un champ dans une jointure // qu'il faut provoquer si ce n'est fait // http://doc.spip.org/@index_exception function index_exception(&$boucle, $desc, $nom_champ, $excep) { static $trouver_table; if (!$trouver_table) $trouver_table = charger_fonction('trouver_table', 'base'); if (is_array($excep)) { // permettre aux plugins de gerer eux meme des jointures derogatoire ingerables $t = NULL; if (count($excep)==3){ $index_exception_derogatoire = array_pop($excep); $t = $index_exception_derogatoire($boucle, $desc, $nom_champ, $excep); } if ($t == NULL) { list($e, $x) = $excep; #PHP4 affecte de gauche a droite $excep = $x; #PHP5 de droite a gauche ! $j = $trouver_table($e, $boucle->sql_serveur); if (!$j) return array('',''); $e = $j['table']; if (!$t = array_search($e, $boucle->from)) { $k = $j['key']['PRIMARY KEY']; if (strpos($k,',')) { $l = (preg_split('/\s*,\s*/', $k)); $k = $desc['key']['PRIMARY KEY']; if (!in_array($k, $l)) { spip_log("jointure impossible $e " . join(',', $l)); return array('',''); } } $k = array($boucle->id_table, array($e), $k); fabrique_jointures($boucle, array($k)); $t = array_search($e, $boucle->from); } } } else $t = $boucle->id_table; // demander a SQL de gerer le synonyme // ca permet que excep soit dynamique (Cedric, 2/3/06) if ($excep != $nom_champ) $excep .= ' AS '. $nom_champ; return array("$t.$excep", $nom_champ); } // cette fonction sert d'API pour demander le champ '$champ' dans la pile // http://doc.spip.org/@champ_sql function champ_sql($champ, $p) { return index_pile($p->id_boucle, $champ, $p->boucles, $p->nom_boucle); } // cette fonction sert d'API pour demander une balise Spip avec filtres // http://doc.spip.org/@calculer_champ function calculer_champ($p) { $p = calculer_balise($p->nom_champ, $p); return applique_filtres($p); } // Cette fonction sert d'API pour demander une balise SPIP sans filtres. // Pour une balise nommmee NOM, elle demande a charger_fonction de chercher // s'il existe une fonction balise_NOM ou balise_NOM_dist // eventuellement en chargeant le fichier balise/NOM.php. // Si la balise est de la forme PREFIXE_SUFFIXE (cf LOGO_* et URL_*) // elle fait de meme avec juste le PREFIXE. // Si pas de fonction, c'est une reference a une colonne de table SQL connue. // Les surcharges des colonnes SQL via charger_fonction sont donc possibles. // http://doc.spip.org/@calculer_balise function calculer_balise($nom, $p) { // S'agit-t-il d'une balise_XXXX[_dist]() ? if ($f = charger_fonction($nom, 'balise', true)) { $res = $f($p); if ($res !== NULL) return $res; } // Certaines des balises comportant un _ sont generiques if ($f = strpos($nom, '_') AND $f = charger_fonction(substr($nom,0,$f+1), 'balise', true)) { $res = $f($p); if ($res !== NULL) return $res; } $f = charger_fonction('DEFAUT', 'calculer_balise'); return $f($nom, $p); } function calculer_balise_DEFAUT_dist($nom, $p) { // ca pourrait etre un champ SQL homonyme, $p->code = index_pile($p->id_boucle, $nom, $p->boucles, $p->nom_boucle); // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour // il faut recracher {...} quand ce n'est finalement pas des args if ($p->fonctions AND (!$p->fonctions[0][0]) AND $p->fonctions[0][1]) { $code = addslashes($p->fonctions[0][1]); $p->code .= " . '$code'"; } // ne pas passer le filtre securite sur les id_xxx if (strpos($nom, 'ID_') === 0) $p->interdire_scripts = false; // Compatibilite ascendante avec les couleurs html (#FEFEFE) : // SI le champ SQL n'est pas trouve // ET si la balise a une forme de couleur // ET s'il n'y a ni filtre ni etoile // ALORS retourner la couleur. // Ca permet si l'on veut vraiment de recuperer [(#ACCEDE*)] if (preg_match("/^[A-F]{1,6}$/i", $nom) AND !$p->etoile AND !$p->fonctions) { $p->code = "'#$nom'"; $p->interdire_scripts = false; } return $p; } // // Traduction des balises dynamiques, notamment les "formulaire_*" // Inclusion du fichier associe a son nom, qui contient la fonction homonyme // donnant les arguments a chercher dans la pile, et qui sont donc compiles. // On leur adjoint les arguments explicites de la balise (cf #LOGIN{url}) // et d'eventuelles valeurs transmises d'autorite par la balise. // (cf http://trac.rezo.net/trac/spip/ticket/1728) // La fonction nommee ci-dessous recevra a l'execution la valeur de tout ca. define('CODE_EXECUTER_BALISE', "executer_balise_dynamique('%s', array(%s%s), array(%s%s))"); // http://doc.spip.org/@calculer_balise_dynamique function calculer_balise_dynamique($p, $nom, $l, $supp=array()) { if (!balise_distante_interdite($p)) { $p->code = "''"; return $p; } // compatibilite: depuis qu'on accepte #BALISE{ses_args} sans [(...)] autour // il faut recracher {...} quand ce n'est finalement pas des args if ($p->fonctions AND (!$p->fonctions[0][0]) AND $p->fonctions[0][1]) { $p->fonctions = null; } if ($p->param AND ($c = $p->param[0])) { // liste d'arguments commence toujours par la chaine vide array_shift($c); // construire la liste d'arguments comme pour un filtre $param = compose_filtres_args($p, $c, ','); } else $param = ""; $collecte = collecter_balise_dynamique($l, $p, $nom); $p->code = sprintf(CODE_EXECUTER_BALISE, $nom, join(',', $collecte), ($collecte ? $param : substr($param,1)), # virer la virgule memoriser_contexte_compil($p), (!$supp ? '' : (', ' . join(',', $supp)))); $p->interdire_scripts = false; return $p; } // Construction du tableau des arguments d'une balise dynamique. // Ces arguments peuvent etre eux-meme des balises (cf FORMULAIRE_SIGNATURE) // mais gare au bouclage (on peut s'aider de $nom pour le reperer au besoin) // En revanche ils n'ont pas de filtres, donc on appelle calculer_balise qui // ne s'occupe pas de ce qu'il y a dans $p (mais qui va y ecrire le code) // http://doc.spip.org/@collecter_balise_dynamique function collecter_balise_dynamique($l, &$p, $nom) { $args = array(); foreach($l as $c) { $x = calculer_balise($c, $p); $args[] = $x->code;} return $args; } // il faudrait savoir traiter les formulaires en local // tout en appelant le serveur SQL distant. // En attendant, cette fonction permet de refuser une authentification // sur qqch qui n'a rien a voir. // http://doc.spip.org/@balise_distante_interdite function balise_distante_interdite($p) { $nom = $p->id_boucle; if ($nom AND $p->boucles[$nom]->sql_serveur AND !in_array($p->boucles[$nom]->sql_serveur,$GLOBALS['exception_des_connect'])) { spip_log( $nom .':' . $p->nom_champ .' '._T('zbug_distant_interdit')); return false; } return true; } // // Traitements standard de divers champs // definis par $table_des_traitements, cf. ecrire/public/interfaces // // http://doc.spip.org/@champs_traitements function champs_traitements ($p) { global $table_des_traitements; if (!isset($table_des_traitements[$p->nom_champ])) return $p->code; $ps = $table_des_traitements[$p->nom_champ]; if (is_array($ps)) { // new style if ($p->nom_boucle) $type = $p->boucles[$p->nom_boucle]->type_requete; else $type = $p->type_requete; // le traitement peut n'etre defini que pour une table en particulier if (isset($ps[$type])) $ps = $ps[$type]; elseif(isset($ps[0])) $ps = $ps[0]; else $ps=false; } if (!$ps) return $p->code; // Si une boucle DOCUMENTS{doublons} est presente dans le squelette, // ou si in INCLURE contient {doublons} // on insere une fonction de remplissage du tableau des doublons // dans les filtres propre() ou typo() // (qui traitent les raccourcis referencant les docs) if (isset($p->descr['documents']) AND $p->descr['documents'] AND ( (strpos($ps,'propre') !== false) OR (strpos($ps,'typo') !== false) )) $ps = 'traiter_doublons_documents($doublons, '.$ps.')'; // Passer |safehtml sur les boucles "sensibles" // sauf sur les champs dont on est surs // ces exceptions doivent etre ventilees dans les plugins fonctionnels concernes // dans la globale table_des_traitements switch ($p->type_requete) { case 'signatures': case 'syndic_articles': $champs_surs = array( 'date', 'date_heure', 'statut', 'ip', 'url_article', 'maj', 'idx' ); if (!in_array(strtolower($p->nom_champ), $champs_surs) AND !preg_match(',^ID_,', $p->nom_champ)) $ps = 'safehtml('.$ps.')'; break; default: break; } // Remplacer enfin le placeholder %s par le vrai code de la balise return str_replace('%s', $p->code, $ps); } // // Appliquer les filtres a un champ [(#CHAMP|filtre1|filtre2)] // retourne un code php compile exprimant ce champ filtre et securise // - une etoile => pas de processeurs standards // - deux etoiles => pas de securite non plus ! // // http://doc.spip.org/@applique_filtres function applique_filtres($p) { // Traitements standards (cf. supra) if ($p->etoile == '') $code = champs_traitements($p); else $code = $p->code; // Appliquer les filtres perso if ($p->param) $code = compose_filtres($p, $code); // S'il y a un lien avec la session, ajouter un code qui levera // un drapeau dans la structure d'invalidation $Cache if (isset($p->descr['session'])) $code = "invalideur_session(\$Cache, $code)"; // Securite if ($p->interdire_scripts AND $p->etoile != '**') { if (!preg_match("/^sinon[(](.*),'([^']*)'[)]$/", $code, $r)) $code = "interdire_scripts($code)"; else { $code = interdire_scripts($r[2]); $code = "sinon(interdire_scripts($r[1]),'$code')"; } } return $code; } // Cf. function pipeline dans ecrire/inc_utils.php // http://doc.spip.org/@compose_filtres function compose_filtres(&$p, $code) { $image_miette = false; foreach($p->param as $filtre) { $fonc = array_shift($filtre); if (!$fonc) continue; // normalement qu'au premier tour. $is_filtre_image = ((substr($fonc,0,6)=='image_') AND $fonc!='image_graver'); if ($image_miette AND !$is_filtre_image){ // il faut graver maintenant car apres le filtre en cours // on est pas sur d'avoir encore le nom du fichier dans le pipe $code = "filtrer('image_graver', $code)"; $image_miette = false; } // recuperer les arguments du filtre, // a separer par "," ou ":" dans le cas du filtre "?{a,b}" if ($fonc !== '?') { $sep = ','; } else {$sep = ':'; // |?{a,b} *doit* avoir exactement 2 arguments ; on les force if (count($filtre) != 2) $filtre = array(isset($filtre[0])?$filtre[0]:"", isset($filtre[1])?$filtre[1]:""); } $arglist = compose_filtres_args($p, $filtre, $sep); $logique = filtre_logique($fonc, $code, substr($arglist,1)); if ($logique) $code = $logique; else { if (isset($GLOBALS['spip_matrice'][$fonc])) { $code = "filtrer('$fonc',$code$arglist)"; if ($is_filtre_image) $image_miette = true; } // le filtre est defini sous forme de fonction ou de methode // par ex. dans inc_texte, inc_filtres ou mes_fonctions elseif ($f = chercher_filtre($fonc)) { $code = "$f($code$arglist)"; } // le filtre n'existe pas, // on le notifie else erreur_squelette(array('zbug_erreur_filtre', array('filtre'=> texte_script($fonc))), $p); } } // ramasser les images intermediaires inutiles et graver l'image finale if ($image_miette) $code = "filtrer('image_graver',$code)"; return $code; } // Filtres et,ou,oui,non,sinon,xou,xor,and,or,not,yes // et comparateurs function filtre_logique($fonc, $code, $arg) { global $table_criteres_infixes; switch (true) { case in_array($fonc, $table_criteres_infixes): return "($code $fonc $arg)"; case ($fonc == 'and') OR ($fonc == 'et'): return "((($code) AND ($arg)) ?' ' :'')"; case ($fonc == 'or') OR ($fonc == 'ou'): return "((($code) OR ($arg)) ?' ' :'')"; case ($fonc == 'xor') OR ($fonc == 'xou'): return "((($code) XOR ($arg)) ?' ' :'')"; case ($fonc == 'sinon'): return "(((\$a = $code) OR (!is_array(\$a) AND strlen(\$a))) ? \$a : $arg)"; case ($fonc == 'not') OR ($fonc == 'non'): return "(($code) ?'' :' ')"; case ($fonc == 'yes') OR ($fonc == 'oui'): return "(($code) ?' ' :'')"; } return ''; } // http://doc.spip.org/@compose_filtres_args function compose_filtres_args($p, $args, $sep) { $arglist = ""; foreach ($args as $arg) { $arglist .= $sep . calculer_liste($arg, $p->descr, $p->boucles, $p->id_boucle); } return $arglist; } // // Reserve les champs necessaires a la comparaison avec le contexte donne par // la boucle parente ; attention en recursif il faut les reserver chez soi-meme // ET chez sa maman // // http://doc.spip.org/@calculer_argument_precedent function calculer_argument_precedent($idb, $nom_champ, &$boucles) { // si recursif, forcer l'extraction du champ SQL mais ignorer le code if ($boucles[$idb]->externe) { index_pile ($idb, $nom_champ, $boucles); $zero = '$SP'; } else $zero = '0'; // retourner $Pile[$SP] et pas $Pile[0] si recursion en 1ere boucle $prec = $boucles[$idb]->id_parent; return (($prec === '') ? ('$Pile[' . $zero . "]['$nom_champ']") : index_pile($prec, $nom_champ, $boucles)); } // // Rechercher dans la pile des boucles actives celle ayant un critere // comportant un certain $motif, et construire alors une reference // a l'environnement de cette boucle, qu'on indexe avec $champ. // Sert a referencer une cellule non declaree dans la table et pourtant la. // Par exemple pour la balise #POINTS on produit $Pile[$SP-n]['points'] // si la n-ieme boucle a un critere "recherche", car on sait qu'il a produit // "SELECT XXXX AS points" // // http://doc.spip.org/@rindex_pile function rindex_pile($p, $champ, $motif) { $n = 0; $b = $p->id_boucle; $p->code = ''; while ($b != '') { foreach($p->boucles[$b]->criteres as $critere) { if ($critere->op == $motif) { $p->code = '$Pile[$SP' . (($n==0) ? "" : "-$n") . "]['$champ']"; $b = ''; break 2; } } $n++; $b = $p->boucles[$b]->id_parent; } // si on est hors d'une boucle de {recherche}, cette balise est vide if (!$p->code) $p->code = "''"; $p->interdire_scripts = false; return $p; } ?>