+<?php
+/**
+ * Plugin Spip-Bonux
+ * Le plugin qui lave plus SPIP que SPIP
+ * (c) 2008 Mathieu Marcillaud, Cedric Morin, Romy Tetue
+ * Licence GPL
+ *
+ */
+
+if (!defined("_ECRIRE_INC_VERSION")) return;
+
+// recuperer le nom du serveur,
+// mais pas si c'est un serveur specifique (pour, connexion)
+// attention, en SPIP 2.1, on recupere 'POUR' et non plus 'pour' comme en 2.0
+// @param array $p, AST positionne sur la balise
+// @return string nom de la connexion
+function get_nom_serveur($p) {
+ if (isset($p->boucles[$p->id_boucle])) {
+ $s = $p->boucles[$p->id_boucle]->sql_serveur;
+ if ($serveur = strtolower($s)
+ AND $serveur!='pour'
+ AND $serveur!='condition') {
+ return $s;
+ }
+ }
+ return "";
+}
+
+//
+// #URL_ACTION_AUTEUR{converser,arg,redirect} -> ecrire/?action=converser&arg=arg&hash=xxx&redirect=redirect
+//
+// http://doc.spip.org/@balise_URL_ACTION_AUTEUR_dist
+function balise_URL_ACTION_AUTEUR($p) {
+ $p->descr['session'] = true;
+
+ // si serveur externe, ce n'est pas possible
+ if (get_nom_serveur($p)) {
+ $p->code = 'generer_url_public("404")';
+ return $p;
+ }
+
+ $p->code = interprete_argument_balise(1,$p);
+ $args = interprete_argument_balise(2,$p);
+ if (!$args)
+ $args = "''";
+ $p->code .= ",".$args;
+ $redirect = interprete_argument_balise(3,$p);
+ if ($redirect != "''" && $redirect!==NULL)
+ $p->code .= ",".$redirect;
+
+ $p->code = "generer_action_auteur(" . $p->code . ")";
+ $p->interdire_scripts = false;
+ return $p;
+}
+//
+// #URL_ECRIRE{naviguer} -> ecrire/?exec=naviguer
+//
+// http://doc.spip.org/@balise_URL_ECRIRE_dist
+function balise_URL_ECRIRE($p) {
+
+ // si serveur externe, ce n'est pas possible
+ if (get_nom_serveur($p)) {
+ $p->code = 'generer_url_public("404")';
+ return $p;
+ }
+
+ $code = interprete_argument_balise(1,$p);
+ if (!$code)
+ $fonc = "''";
+ else{
+ if (preg_match("/^'[^']*'$/", $code))
+ $fonc = $code;
+ else {$code = "(\$f = $code)"; $fonc = '$f';}
+ $args = interprete_argument_balise(2,$p);
+ if ($args != "''" && $args!==NULL)
+ $fonc .= ',' . $args;
+ }
+ $p->code = 'generer_url_ecrire(' . $fonc .')';
+ if (function_exists('tester_url_ecrire'))
+ if ($code)
+ $p->code = "(tester_url_ecrire($code) ?" . $p->code .' : "")';
+ #$p->interdire_scripts = true;
+ return $p;
+}
+
+
+
+// surplus de #URL_PAGE pour prendre en compte les boucles POUR et CONDITION
+/* // ceci n'est pas suffisant car il faudrait traiter les autres types aussi
+function generer_generer_url_pour($type, $code) {return 'generer_url_public(' . $code .')';}
+function generer_generer_url_condition($type, $code) {return 'generer_url_public(' . $code .')';}
+*/
+function balise_URL_PAGE($p) {
+
+ $p->code = interprete_argument_balise(1,$p);
+ $args = interprete_argument_balise(2,$p);
+ if ($args != "''" && $args!==NULL)
+ $p->code .= ','.$args;
+
+ // autres filtres (???)
+ array_shift($p->param);
+
+ if ($p->id_boucle
+ AND $s = get_nom_serveur($p)) {
+
+ if (!$GLOBALS['connexions'][$s]['spip_connect_version']) {
+ $p->code = "404";
+ } else {
+ // si une fonction de generation des url a ete definie pour ce connect l'utiliser
+ // elle devra aussi traiter le cas derogatoire type=page
+ if (function_exists($f = 'generer_generer_url_'.$s)){
+ $p->code = $f('page', $p->code, $s);
+ return $p;
+ }
+ $p->code .= ", 'connect=" . addslashes($s) . "'";
+ }
+ }
+
+ $p->code = 'generer_url_public(' . $p->code .')';
+ #$p->interdire_scripts = true;
+ return $p;
+}
+
+
+/**
+ * #SET
+ * Affecte une variable locale au squelette
+ * #SET{nom,valeur}
+ *
+ * SURCHARGE DU CORE :
+ * Affecte un filtre a une variable locale au squelette
+ * #SET{nom,filtre,param1,param2,...,paramN}
+ *
+ * @param object $p : objet balise
+ * @return ""
+**/
+/*
+function balise_SET($p){
+ $_code = array();
+
+ $n=1;
+ while ($_v = interprete_argument_balise($n++,$p))
+ $_code[] = $_v;
+
+ $_nom = array_shift($_code);
+ $_valeur = array_shift($_code);
+ if ($_nom AND $_valeur AND count($_code)) {
+ $filtre = str_replace("'", "", strtolower($_valeur));
+ $f = chercher_filtre($filtre);
+ $p->code = "vide(\$Pile['vars'][$_nom]=$f(". join(', ',$_code)."))";
+ } elseif ($_nom AND $_valeur)
+ $p->code = "vide(\$Pile['vars'][$_nom] = $_valeur)";
+ else
+ $p->code = "''";
+
+ $p->interdire_scripts = false; // la balise ne renvoie rien
+ return $p;
+}
+*/
+
+
+/**
+ * Empile un element dans un tableau declare par #SET{tableau,#ARRAY}
+ * #SET_PUSH{tableau,valeur}
+ *
+ * @param object $p : objet balise
+ * @return ""
+**/
+function balise_SET_PUSH_dist($p){
+ $_nom = interprete_argument_balise(1,$p);
+ $_valeur = interprete_argument_balise(2,$p);
+
+ if ($_nom AND $_valeur)
+ // si le tableau n'existe pas encore, on le cree
+ // on ajoute la valeur ensuite (sans passer par array_push)
+ $p->code = "vide((\$cle=$_nom)
+ . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
+ . (\$Pile['vars'][\$cle][]=$_valeur))";
+ else
+ $p->code = "''";
+
+ $p->interdire_scripts = false; // la balise ne renvoie rien
+ return $p;
+}
+
+/**
+ * Si 3 arguments : Cree un tableau nom_tableau de t1 + t2
+ * #SET_MERGE{nom_tableau,t1,t2}
+ * #SET_MERGE{nom_tableau,#GET{tableau},#ARRAY{cle,valeur}}
+ *
+ * Si 2 arguments : Merge t1 dans nom_tableau
+ * #SET_MERGE{nom_tableau,t1}
+ * #SET_MERGE{nom_tableau,#GET{tableau}}
+ *
+ * @param object $p : objet balise
+ * @return ""
+**/
+function balise_SET_MERGE_dist($p){
+ $_nom = interprete_argument_balise(1,$p);
+ $_t1 = interprete_argument_balise(2,$p);
+ $_t2 = interprete_argument_balise(3,$p);
+
+ if ($_nom AND $_t1 AND !$_t2)
+ // 2 arguments : merge de $_nom et $_t1 dans $_nom
+ // si le tableau n'existe pas encore, on le cree
+ $p->code = "vide((\$cle=$_nom)
+ . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
+ . (is_array(\$new=$_t1)?'':\$new=array(\$new))
+ . (\$Pile['vars'][\$cle] = array_merge(\$Pile['vars'][\$cle],\$new)))";
+ elseif ($_nom AND $_t1 AND $_t2)
+ // 3 arguments : merge de $_t1 et $_t2 dans $_nom
+ // si le tableau n'existe pas encore, on le cree
+ $p->code = "vide((\$cle=$_nom)
+ . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
+ . (is_array(\$new1=$_t1)?'':\$new1=array(\$new1))
+ . (is_array(\$new2=$_t2)?'':\$new2=array(\$new2))
+ . (\$Pile['vars'][\$cle] = array_merge(\$new1,\$new2)))";
+ else
+ $p->code = "''";
+
+ $p->interdire_scripts = false; // la balise ne renvoie rien
+ return $p;
+}
+
+/**
+ * Balise #COMPTEUR associee au critere compteur
+ *
+ * @param unknown_type $p
+ * @return unknown
+ */
+function balise_COMPTEUR_dist($p) {
+ calculer_balise_criteres('compteur', $p);
+ if ($p->code=="''")
+ calculer_balise_criteres('compteur', $p, "compteur_left");
+ return $p;
+}
+
+/** Balise #SOMME associee au critere somme */
+function balise_SOMME_dist($p) {
+ return calculer_balise_criteres('somme', $p);
+}
+
+/** Balise #COMPTE associee au critere compte */
+function balise_COMPTE_dist($p) {
+ return calculer_balise_criteres('compte', $p);
+}
+
+/** Balise #MOYENNE associee au critere moyenne */
+function balise_MOYENNE_dist($p) {
+ return calculer_balise_criteres('moyenne', $p);
+}
+
+/** Balise #MINIMUM associee au critere moyenne */
+function balise_MINIMUM_dist($p) {
+ return calculer_balise_criteres('minimum', $p);
+}
+
+/** Balise #MAXIMUM associee au critere moyenne */
+function balise_MAXIMUM_dist($p) {
+ return calculer_balise_criteres('maximum', $p);
+}
+
+/** Balise #STATS associee au critere stats
+ * #STATS{id_article,moyenne}
+ */
+function balise_STATS_dist($p) {
+ if (isset($p->param[0][2][0])
+ AND $nom = ($p->param[0][2][0]->texte)) {
+ return calculer_balise_criteres($nom, $p, 'stats');
+ }
+ return $p;
+}
+
+function calculer_balise_criteres($nom, $p, $motif="") {
+ $p->code = "''";
+ $motif = $motif ? $motif : $nom;
+ if (isset($p->param[0][1][0])
+ AND $champ = ($p->param[0][1][0]->texte)) {
+ return rindex_pile($p, $nom."_$champ", $motif);
+ }
+ return $p;
+}
+
+
+
+/**
+ * #TRI{champ[,libelle]}
+ * champ prend < ou > pour afficher le lien de changement de sens
+ * croissant ou decroissant
+ *
+ * @param unknown_type $p
+ * @param unknown_type $liste
+ * @return unknown
+ */
+function balise_TRI_dist($p, $liste='true') {
+ $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
+
+ // s'il n'y a pas de nom de boucle, on ne peut pas trier
+ if ($b === '') {
+ erreur_squelette(
+ _T('zbug_champ_hors_boucle',
+ array('champ' => '#TRI')
+ ), $p->id_boucle);
+ $p->code = "''";
+ return $p;
+ }
+ $boucle = $p->boucles[$b];
+
+ // s'il n'y a pas de tri_champ, c'est qu'on se trouve
+ // dans un boucle recursive ou qu'on a oublie le critere {tri}
+ if (!isset($boucle->modificateur['tri_champ'])) {
+ erreur_squelette(
+ _T('zbug_tri_sans_critere',
+ array('champ' => '#TRI')
+ ), $p->id_boucle);
+ $p->code = "''";
+ return $p;
+ }
+
+ $_champ = interprete_argument_balise(1,$p);
+ // si pas de champ, renvoyer le critere de tri utilise
+ if (!$_champ){
+ $p->code = $boucle->modificateur['tri_champ'];
+ return $p;
+ }
+
+ $_libelle = interprete_argument_balise(2,$p);
+ $_libelle = $_libelle?$_libelle:$_champ;
+
+ $_class = interprete_argument_balise(3,$p);
+ // si champ = "<" c'est un lien vers le tri croissant : 1<2<3<4 ... ==> 1
+ // si champ = ">" c'est un lien vers le tri decroissant :.. 4>3>2>1 == -1
+ $_issens = "in_array($_champ,array('<','>'))";
+ $_sens = "(strpos('> <',$_champ)-1)";
+
+ $_variable = "((\$s=$_issens)?'sens':'tri').".$boucle->modificateur['tri_nom'];
+ $_url = "parametre_url(self(),$_variable,\$s?$_sens:$_champ)";
+ $_on = "\$s?(".$boucle->modificateur['tri_sens']."==$_sens".'):('.$boucle->modificateur['tri_champ']."==$_champ)";
+
+ $p->code = "lien_ou_expose($_url,$_libelle,$_on".($_class?",$_class":"").")";
+ //$p->code = "''";
+ $p->interdire_scripts = false;
+ return $p;
+}
+
+/**
+ * Generer un bouton d'action en post, ajaxable
+ * a utiliser a la place des liens action_auteur, sous la forme
+ * #BOUTON_ACTION{libelle,url}
+ * ou
+ * #BOUTON_ACTION{libelle,url,ajax} pour que l'action soit ajax comme un lien class='ajax'
+ * ou
+ * #BOUTON_ACTION{libelle,url,ajax,message_confirmation} pour utiliser un message de confirmation
+ * ou
+ * #BOUTON_ACTION{libelle,url,ajax,'',info} pour inserer une bulle d'information
+ *
+ * @param unknown_type $p
+ * @return unknown
+ */
+function balise_BOUTON_ACTION($p){
+
+ $_label = interprete_argument_balise(1,$p);
+ if (!$_label) $_label="''";
+
+ $_url = interprete_argument_balise(2,$p);
+ if (!$_url) $_url="''";
+
+ $_class = interprete_argument_balise(3,$p);
+ if (!$_class) $_class="''";
+
+ $_confirm = interprete_argument_balise(4,$p);
+ if ((!$_confirm) OR ($_confirm=="''")) { $_onclick=''; }
+ else $_onclick = " onclick=\'return confirm(\"' . attribut_html($_confirm) . '\");\'";
+
+ $_title = interprete_argument_balise(5,$p);
+ if (!$_title) $_title="''";
+ else $_title = "' title=\'' . $_title . '\''";
+
+ $p->code = "'<form class=\'bouton_action_post ' . $_class . '\' method=\'post\' action=\'' . (\$u=$_url) . '\'>'
+. '<div>' . form_hidden(\$u)
+. '<button type=\'submit\' class=\'submit\' $_onclick' . $_title . '>' . $_label . '</button>'
+. '</div></form>'";
+ $p->interdire_scripts = false;
+ return $p;
+}
+
+/**
+ * Generer n'importe quel info pour un objet : #INFO_TITRE{article, #ENV{id_article}}
+ * Utilise la fonction generer_info_entite(), se reporter a sa documentation
+ */
+function balise_INFO__dist($p){
+ $info = $p->nom_champ;
+ $type_objet = interprete_argument_balise(1,$p);
+ $id_objet = interprete_argument_balise(2,$p);
+ if ($info === 'INFO_' or !$type_objet or !$id_objet) {
+ $msg = _T('zbug_balise_sans_argument', array('balise' => ' INFO_'));
+ erreur_squelette($msg, $p);
+ $p->interdire_scripts = true;
+ return $p;
+ }else {
+ $p->code = champ_sql($info, $p, false);
+ if (strpos($p->code, '@$Pile[0]') !== false) {
+ $info = strtolower(substr($info,5));
+ $p->code = "generer_info_entite($id_objet, $type_objet, '$info'".($p->etoile?","._q($p->etoile):"").")";
+ }
+ $p->interdire_scripts = true;
+ return $p;
+ }
+}
+
+
+/**
+ * Savoir si on objet est publie ou non
+ *
+ * @param <type> $p
+ * @return <type>
+ */
+function balise_PUBLIE_dist($p) {
+
+ $type = $p->type_requete;
+
+ $_statut = champ_sql('statut',$p);
+
+
+ $_texte = champ_sql('texte', $p);
+ $_descriptif = "''";
+
+ switch ($type){
+ case 'articles':
+ $p->code = "$_statut=='publie'";
+ if ($GLOBALS['meta']["post_dates"] == 'non'){
+ $_date_pub = champ_sql('date',$p);
+ $p->code .= "AND $_date_pub<quete_date_postdates()";
+ }
+ break;
+ case 'auteurs':
+ $_id = champ_sql('id_auteur',$p);
+ $p->code = "sql_countsel('spip_articles AS AR JOIN spip_auteurs_articles AS AU ON AR.id_article=AU.id_article',
+ 'AU.id_auteur=intval('.$_id.') AND AR.statut=\'publie\''"
+ .(($GLOBALS['meta']['post_dates'] == 'non')?".' AND AR.date<'.sql_quote(quete_date_postdates())":'')
+ .")>0";
+ break;
+ // le cas des documents prend directement en compte la mediatheque
+ // car le fonctionnement par defaut de SPIP <=2.0 est trop tordu et insatisfaisant
+ case 'documents':
+ $p->code = "$_statut=='publie'";
+ if ($GLOBALS['meta']["post_dates"] == 'non'){
+ $_date_pub = champ_sql('date_publication',$p);
+ $p->code .= "AND $_date_pub<quete_date_postdates()";
+ }
+ break;
+ default:
+ $p->code = "($_statut=='publie'?' ':'')";
+ break;
+ }
+
+ $p->code = "((".$p->code.")?' ':'')";
+
+ #$p->interdire_scripts = true;
+ return $p;
+}
+
+
+/**
+ * Implementation securisee du saut en avant
+ * pour la balise #SAUTER
+ *
+ * @param resource $res
+ * @param int $pos
+ * @param int $nb
+ * @param int $total
+ */
+function spip_bonux_sauter(&$res, &$pos, $nb, $total){
+ // pas de saut en arriere qu'on ne sait pas faire sans sql_seek
+ if (($nb=intval($nb))<=0) return;
+
+ $saut = $pos + $nb;
+ // si le saut fait depasser le maxi, on libere et on sort
+ if ($saut>=$total) {sql_free($res); return;}
+
+ if (sql_seek($res, $saut))
+ $pos += $nb;
+ else
+ while ($pos<$saut AND sql_fetch($res))
+ $pos++;
+ return;
+}
+
+/**
+ * #SAUTER{n} permet de sauter en avant n resultats dans une boucle
+ * La balise modifie le compteur courant de la boucle, mais pas les autres
+ * champs
+ *
+ * L'argument n doit etre superieur a zero sinon la balise ne fait rien
+ * Lorsque sql_seek est disponible, il est utilise,
+ * sinon le saut est realise par n sql_fetch
+ *
+ * @param <type> $p
+ * @return <type>
+ */
+function balise_SAUTER_dist($p){
+ $_nb = interprete_argument_balise(1,$p);
+ $_compteur = "\$Numrows['".$p->id_boucle."']['compteur_boucle']";
+ $_max = "\$Numrows['".$p->id_boucle."']['total']";
+
+ $p->code = "spip_bonux_sauter(\$result,$_compteur,$_nb,$_max)";
+ $p->interdire_scripts = false;
+ return $p;
+}
+
+/**
+ * Produire un fichier statique a partir d'un squelette dynamique
+ * Permet ensuite a apache de le servir en statique sans repasser
+ * par spip.php a chaque hit sur le fichier
+ * si le format (css ou js) est passe dans contexte['format'], on l'utilise
+ * sinon on regarde si le fond finit par .css ou .js
+ * sinon on utilie "html"
+ *
+ * @param string $fond
+ * @param array $contexte
+ * @param array $options
+ * @param string $connect
+ * @return string
+ */
+function produire_fond_statique($fond, $contexte=array(), $options = array(), $connect=''){
+ if (isset($contexte['format'])){
+ $extension = $contexte['format'];
+ unset($contexte['format']);
+ }
+ else {
+ $extension = "html";
+ if (preg_match(',[.](css|js)$,',$fond,$m))
+ $extension = $m[1];
+ }
+ // recuperer le contenu produit par le squelette
+ $options['raw'] = true;
+ $cache = recuperer_fond($fond,$contexte,$options,$connect);
+
+ // calculer le nom de la css
+ $dir_var = sous_repertoire (_DIR_VAR, 'cache-'.$extension);
+ $filename = $dir_var . $extension."dyn-".md5($fond.serialize($contexte).$connect) .".$extension";
+
+ // mettre a jour le fichier si il n'existe pas
+ // ou trop ancien
+ if (!file_exists($filename)
+ OR filemtime($filename)<$cache['lastmodified']
+ OR $GLOBALS['var_mode']=='recalcul'){
+
+ $contenu = $cache['texte'];
+ // passer les urls en absolu si c'est une css
+ if ($extension=="css")
+ $contenu = urls_absolues_css($contenu, generer_url_public($fond));
+
+ $comment = "/* #PRODUIRE{fond=$fond";
+ foreach($contexte as $k=>$v)
+ $comment .= ",$k=$v";
+ $comment .="} le ".date("Y-m-d H:i:s")." */\n";
+ // et ecrire le fichier
+ ecrire_fichier($filename,$comment.$contenu);
+ }
+
+ return $filename;
+}
+
+function produire_css_fond($fond, $contexte=array(), $options = array(), $connect=''){
+ $contexte['format'] = "css";
+ return produire_fond_statique($fond, $contexte, $options, $connect);
+}
+function produire_js_fond($fond, $contexte=array(), $options = array(), $connect=''){
+ $contexte['format'] = "js";
+ return produire_fond_statique($fond, $contexte, $options, $connect);
+}
+
+/**
+ * #PRODUIRE_CSS_FOND
+ * generer un fichier css statique a partir d'un squelette de CSS
+ * utilisable en
+ *
+ * <link rel="stylesheet" type="text/css" href="#PRODUIRE_CSS_FOND{fond=css/macss,couleur=ffffff}" />
+ * la syntaxe de la balise est la meme que celle de #INCLURE
+ *
+ * @param object $p
+ * @return object
+ */
+function balise_PRODUIRE_CSS_FOND_dist($p){
+ $balise_inclure = charger_fonction('INCLURE','balise');
+ $p = $balise_inclure($p);
+ $p->code = str_replace('recuperer_fond(','produire_css_fond(',$p->code);
+ return $p;
+}
+/**
+ * #PRODUIRE_JS_FOND
+ * generer un fichier js statique a partir d'un squelette de JS
+ * utilisable en
+ *
+ * <script type="text/javascript" src="#PRODUIRE_JS_FOND{fond=js/monscript}" ></script>
+ * la syntaxe de la balise est la meme que celle de #INCLURE
+ *
+ * @param object $p
+ * @return object
+ */
+function balise_PRODUIRE_JS_FOND_dist($p){
+ $balise_inclure = charger_fonction('INCLURE','balise');
+ $p = $balise_inclure($p);
+ $p->code = str_replace('recuperer_fond(','produire_js_fond(',$p->code);
+ return $p;
+}
+/**
+ * #PRODUIRE
+ * generer un fichier statique a partir d'un squelette SPIP
+ *
+ * Le format du fichier sera extrait de la preextension du squelette (typo.css.html, messcripts.js.html)
+ * ou par l'argument format=css ou format=js passe en argument.
+ *
+ * Si pas de format detectable, on utilise .html, comme pour les squelettes
+ *
+ * <link rel="stylesheet" type="text/css" href="#PRODUIRE{fond=css/macss.css,couleur=ffffff}" />
+ * la syntaxe de la balise est la meme que celle de #INCLURE
+ *
+ * @param object $p
+ * @return object
+ */
+function balise_PRODUIRE_dist($p){
+ $balise_inclure = charger_fonction('INCLURE','balise');
+ $p = $balise_inclure($p);
+
+ $p->code = str_replace('recuperer_fond(','produire_fond_statique(',$p->code);
+
+ return $p;
+}
+?>