4 * Le plugin qui lave plus SPIP que SPIP
5 * (c) 2008 Mathieu Marcillaud, Cedric Morin, Romy Tetue
10 if (!defined("_ECRIRE_INC_VERSION")) return;
12 // recuperer le nom du serveur,
13 // mais pas si c'est un serveur specifique (pour, connexion)
14 // attention, en SPIP 2.1, on recupere 'POUR' et non plus 'pour' comme en 2.0
15 // @param array $p, AST positionne sur la balise
16 // @return string nom de la connexion
17 function get_nom_serveur($p) {
18 if (isset($p->boucles
[$p->id_boucle
])) {
19 $s = $p->boucles
[$p->id_boucle
]->sql_serveur
;
20 if ($serveur = strtolower($s)
22 AND $serveur!='condition') {
30 // #URL_ACTION_AUTEUR{converser,arg,redirect} -> ecrire/?action=converser&arg=arg&hash=xxx&redirect=redirect
32 // http://doc.spip.org/@balise_URL_ACTION_AUTEUR_dist
33 function balise_URL_ACTION_AUTEUR($p) {
34 $p->descr
['session'] = true;
36 $p->code
= interprete_argument_balise(1,$p);
37 $args = interprete_argument_balise(2,$p);
40 $p->code
.= ",".$args;
41 $redirect = interprete_argument_balise(3,$p);
42 if ($redirect != "''" && $redirect!==NULL)
43 $p->code
.= ",".$redirect;
45 $p->code
= "generer_action_auteur(" . $p->code
. ")";
46 $p->interdire_scripts
= false;
50 // #URL_ECRIRE{naviguer} -> ecrire/?exec=naviguer
52 // http://doc.spip.org/@balise_URL_ECRIRE_dist
53 function balise_URL_ECRIRE($p) {
55 // si serveur externe, ce n'est pas possible
56 if (get_nom_serveur($p)) {
57 $p->code
= 'generer_url_public("404")';
61 $code = interprete_argument_balise(1,$p);
65 if (preg_match("/^'[^']*'$/", $code))
67 else {$code = "(\$f = $code)"; $fonc = '$f';}
68 $args = interprete_argument_balise(2,$p);
69 if ($args != "''" && $args!==NULL)
72 $p->code
= 'generer_url_ecrire(' . $fonc .')';
73 if (function_exists('tester_url_ecrire'))
75 $p->code
= "(tester_url_ecrire($code) ?" . $p->code
.' : "")';
76 #$p->interdire_scripts = true;
82 // surplus de #URL_PAGE pour prendre en compte les boucles POUR et CONDITION
83 /* // ceci n'est pas suffisant car il faudrait traiter les autres types aussi
84 function generer_generer_url_pour($type, $code) {return 'generer_url_public(' . $code .')';}
85 function generer_generer_url_condition($type, $code) {return 'generer_url_public(' . $code .')';}
87 function balise_URL_PAGE($p) {
89 $p->code
= interprete_argument_balise(1,$p);
90 $args = interprete_argument_balise(2,$p);
91 if ($args != "''" && $args!==NULL)
92 $p->code
.= ','.$args;
94 // autres filtres (???)
95 array_shift($p->param
);
98 AND $s = get_nom_serveur($p)) {
100 if (!$GLOBALS['connexions'][$s]['spip_connect_version']) {
103 // si une fonction de generation des url a ete definie pour ce connect l'utiliser
104 // elle devra aussi traiter le cas derogatoire type=page
105 if (function_exists($f = 'generer_generer_url_'.$s)){
106 $p->code
= $f('page', $p->code
, $s);
109 $p->code
.= ", 'connect=" . addslashes($s) . "'";
113 $p->code
= 'generer_url_public(' . $p->code
.')';
114 #$p->interdire_scripts = true;
121 * Affecte une variable locale au squelette
124 * SURCHARGE DU CORE :
125 * Affecte un filtre a une variable locale au squelette
126 * #SET{nom,filtre,param1,param2,...,paramN}
128 * @param object $p : objet balise
132 function balise_SET($p){
136 while ($_v = interprete_argument_balise($n++,$p))
139 $_nom = array_shift($_code);
140 $_valeur = array_shift($_code);
141 if ($_nom AND $_valeur AND count($_code)) {
142 $filtre = str_replace("'", "", strtolower($_valeur));
143 $f = chercher_filtre($filtre);
144 $p->code = "vide(\$Pile['vars'][$_nom]=$f(". join(', ',$_code)."))";
145 } elseif ($_nom AND $_valeur)
146 $p->code = "vide(\$Pile['vars'][$_nom] = $_valeur)";
150 $p->interdire_scripts = false; // la balise ne renvoie rien
157 * Empile un element dans un tableau declare par #SET{tableau,#ARRAY}
158 * #SET_PUSH{tableau,valeur}
160 * @param object $p : objet balise
163 function balise_SET_PUSH_dist($p){
164 $_nom = interprete_argument_balise(1,$p);
165 $_valeur = interprete_argument_balise(2,$p);
167 if ($_nom AND $_valeur)
168 // si le tableau n'existe pas encore, on le cree
169 // on ajoute la valeur ensuite (sans passer par array_push)
170 $p->code
= "vide((\$cle=$_nom)
171 . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
172 . (\$Pile['vars'][\$cle][]=$_valeur))";
176 $p->interdire_scripts
= false; // la balise ne renvoie rien
181 * Si 3 arguments : Cree un tableau nom_tableau de t1 + t2
182 * #SET_MERGE{nom_tableau,t1,t2}
183 * #SET_MERGE{nom_tableau,#GET{tableau},#ARRAY{cle,valeur}}
185 * Si 2 arguments : Merge t1 dans nom_tableau
186 * #SET_MERGE{nom_tableau,t1}
187 * #SET_MERGE{nom_tableau,#GET{tableau}}
189 * @param object $p : objet balise
192 function balise_SET_MERGE_dist($p){
193 $_nom = interprete_argument_balise(1,$p);
194 $_t1 = interprete_argument_balise(2,$p);
195 $_t2 = interprete_argument_balise(3,$p);
197 if ($_nom AND $_t1 AND !$_t2)
198 // 2 arguments : merge de $_nom et $_t1 dans $_nom
199 // si le tableau n'existe pas encore, on le cree
200 $p->code
= "vide((\$cle=$_nom)
201 . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
202 . (is_array(\$new=$_t1)?'':\$new=array(\$new))
203 . (\$Pile['vars'][\$cle] = array_merge(\$Pile['vars'][\$cle],\$new)))";
204 elseif ($_nom AND $_t1 AND $_t2)
205 // 3 arguments : merge de $_t1 et $_t2 dans $_nom
206 // si le tableau n'existe pas encore, on le cree
207 $p->code
= "vide((\$cle=$_nom)
208 . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
209 . (is_array(\$new1=$_t1)?'':\$new1=array(\$new1))
210 . (is_array(\$new2=$_t2)?'':\$new2=array(\$new2))
211 . (\$Pile['vars'][\$cle] = array_merge(\$new1,\$new2)))";
215 $p->interdire_scripts
= false; // la balise ne renvoie rien
220 * Balise #COMPTEUR associee au critere compteur
222 * @param unknown_type $p
225 function balise_COMPTEUR_dist($p) {
226 calculer_balise_criteres('compteur', $p);
228 calculer_balise_criteres('compteur', $p, "compteur_left");
232 /** Balise #SOMME associee au critere somme */
233 function balise_SOMME_dist($p) {
234 return calculer_balise_criteres('somme', $p);
237 /** Balise #COMPTE associee au critere compte */
238 function balise_COMPTE_dist($p) {
239 return calculer_balise_criteres('compte', $p);
242 /** Balise #MOYENNE associee au critere moyenne */
243 function balise_MOYENNE_dist($p) {
244 return calculer_balise_criteres('moyenne', $p);
247 /** Balise #MINIMUM associee au critere moyenne */
248 function balise_MINIMUM_dist($p) {
249 return calculer_balise_criteres('minimum', $p);
252 /** Balise #MAXIMUM associee au critere moyenne */
253 function balise_MAXIMUM_dist($p) {
254 return calculer_balise_criteres('maximum', $p);
257 /** Balise #STATS associee au critere stats
258 * #STATS{id_article,moyenne}
260 function balise_STATS_dist($p) {
261 if (isset($p->param
[0][2][0])
262 AND $nom = ($p->param
[0][2][0]->texte
)) {
263 return calculer_balise_criteres($nom, $p, 'stats');
268 function calculer_balise_criteres($nom, $p, $motif="") {
270 $motif = $motif ?
$motif : $nom;
271 if (isset($p->param
[0][1][0])
272 AND $champ = ($p->param
[0][1][0]->texte
)) {
273 return rindex_pile($p, $nom."_$champ", $motif);
281 * #TRI{champ[,libelle]}
282 * champ prend < ou > pour afficher le lien de changement de sens
283 * croissant ou decroissant
285 * @param unknown_type $p
286 * @param unknown_type $liste
289 function balise_TRI_dist($p, $liste='true') {
290 $b = $p->nom_boucle ?
$p->nom_boucle
: $p->descr
['id_mere'];
292 // s'il n'y a pas de nom de boucle, on ne peut pas trier
295 _T('zbug_champ_hors_boucle',
296 array('champ' => '#TRI')
301 $boucle = $p->boucles
[$b];
303 // s'il n'y a pas de tri_champ, c'est qu'on se trouve
304 // dans un boucle recursive ou qu'on a oublie le critere {tri}
305 if (!isset($boucle->modificateur
['tri_champ'])) {
307 _T('zbug_tri_sans_critere',
308 array('champ' => '#TRI')
314 $_champ = interprete_argument_balise(1,$p);
315 // si pas de champ, renvoyer le critere de tri utilise
317 $p->code
= $boucle->modificateur
['tri_champ'];
321 $_libelle = interprete_argument_balise(2,$p);
322 $_libelle = $_libelle?
$_libelle:$_champ;
324 $_class = interprete_argument_balise(3,$p);
325 // si champ = "<" c'est un lien vers le tri croissant : 1<2<3<4 ... ==> 1
326 // si champ = ">" c'est un lien vers le tri decroissant :.. 4>3>2>1 == -1
327 $_issens = "in_array($_champ,array('<','>'))";
328 $_sens = "(strpos('> <',$_champ)-1)";
330 $_variable = "((\$s=$_issens)?'sens':'tri').".$boucle->modificateur
['tri_nom'];
331 $_url = "parametre_url(self(),$_variable,\$s?$_sens:$_champ)";
332 $_on = "\$s?(".$boucle->modificateur
['tri_sens']."==$_sens".'):('.$boucle->modificateur
['tri_champ']."==$_champ)";
334 $p->code
= "lien_ou_expose($_url,$_libelle,$_on".($_class?
",$_class":"").")";
336 $p->interdire_scripts
= false;
341 * Generer un bouton d'action en post, ajaxable
342 * a utiliser a la place des liens action_auteur, sous la forme
343 * #BOUTON_ACTION{libelle,url}
345 * #BOUTON_ACTION{libelle,url,ajax} pour que l'action soit ajax comme un lien class='ajax'
347 * #BOUTON_ACTION{libelle,url,ajax,message_confirmation} pour utiliser un message de confirmation
349 * #BOUTON_ACTION{libelle,url,ajax,'',info} pour inserer une bulle d'information
351 * @param unknown_type $p
354 function balise_BOUTON_ACTION($p){
356 $_label = interprete_argument_balise(1,$p);
357 if (!$_label) $_label="''";
359 $_url = interprete_argument_balise(2,$p);
360 if (!$_url) $_url="''";
362 $_class = interprete_argument_balise(3,$p);
363 if (!$_class) $_class="''";
365 $_confirm = interprete_argument_balise(4,$p);
366 if ((!$_confirm) OR ($_confirm=="''")) { $_onclick=''; }
367 else $_onclick = " onclick=\'return confirm(\"' . attribut_html($_confirm) . '\");\'";
369 $_title = interprete_argument_balise(5,$p);
370 if (!$_title) $_title="''";
371 else $_title = "' title=\'' . $_title . '\''";
373 $p->code
= "'<form class=\'bouton_action_post ' . $_class . '\' method=\'post\' action=\'' . (\$u=$_url) . '\'>'
374 . '<div>' . form_hidden(\$u)
375 . '<button type=\'submit\' class=\'submit\' $_onclick' . $_title . '>' . $_label . '</button>'
377 $p->interdire_scripts
= false;
382 * Generer n'importe quel info pour un objet : #INFO_TITRE{article, #ENV{id_article}}
383 * Utilise la fonction generer_info_entite(), se reporter a sa documentation
385 function balise_INFO__dist($p){
386 $info = $p->nom_champ
;
387 $type_objet = interprete_argument_balise(1,$p);
388 $id_objet = interprete_argument_balise(2,$p);
389 if ($info === 'INFO_' or !$type_objet or !$id_objet) {
390 $msg = _T('zbug_balise_sans_argument', array('balise' => ' INFO_'));
391 erreur_squelette($msg, $p);
392 $p->interdire_scripts
= true;
395 $p->code
= champ_sql($info, $p, false);
396 if (strpos($p->code
, '@$Pile[0]') !== false) {
397 $info = strtolower(substr($info,5));
398 $p->code
= "generer_info_entite($id_objet, $type_objet, '$info'".($p->etoile?
","._q($p->etoile
):"").")";
400 $p->interdire_scripts
= true;
407 * Savoir si on objet est publie ou non
412 function balise_PUBLIE_dist($p) {
414 $type = $p->type_requete
;
416 $_statut = champ_sql('statut',$p);
419 $_texte = champ_sql('texte', $p);
424 $p->code
= "$_statut=='publie'";
425 if ($GLOBALS['meta']["post_dates"] == 'non'){
426 $_date_pub = champ_sql('date',$p);
427 $p->code
.= "AND $_date_pub<quete_date_postdates()";
431 $_id = champ_sql('id_auteur',$p);
432 $p->code
= "sql_countsel('spip_articles AS AR JOIN spip_auteurs_articles AS AU ON AR.id_article=AU.id_article',
433 'AU.id_auteur=intval('.$_id.') AND AR.statut=\'publie\''"
434 .(($GLOBALS['meta']['post_dates'] == 'non')?
".' AND AR.date<'.sql_quote(quete_date_postdates())":'')
437 // le cas des documents prend directement en compte la mediatheque
438 // car le fonctionnement par defaut de SPIP <=2.0 est trop tordu et insatisfaisant
440 $p->code
= "$_statut=='publie'";
441 if ($GLOBALS['meta']["post_dates"] == 'non'){
442 $_date_pub = champ_sql('date_publication',$p);
443 $p->code
.= "AND $_date_pub<quete_date_postdates()";
447 $p->code
= "($_statut=='publie'?' ':'')";
451 $p->code
= "((".$p->code
.")?' ':'')";
453 #$p->interdire_scripts = true;
459 * Implementation securisee du saut en avant
460 * pour la balise #SAUTER
462 * @param resource $res
467 function spip_bonux_sauter(&$res, &$pos, $nb, $total){
468 // pas de saut en arriere qu'on ne sait pas faire sans sql_seek
469 if (($nb=intval($nb))<=0) return;
472 // si le saut fait depasser le maxi, on libere et on sort
473 if ($saut>=$total) {sql_free($res); return;}
475 if (sql_seek($res, $saut))
478 while ($pos<$saut AND sql_fetch($res))
484 * #SAUTER{n} permet de sauter en avant n resultats dans une boucle
485 * La balise modifie le compteur courant de la boucle, mais pas les autres
488 * L'argument n doit etre superieur a zero sinon la balise ne fait rien
489 * Lorsque sql_seek est disponible, il est utilise,
490 * sinon le saut est realise par n sql_fetch
495 function balise_SAUTER_dist($p){
496 $_nb = interprete_argument_balise(1,$p);
497 $_compteur = "\$Numrows['".$p->id_boucle
."']['compteur_boucle']";
498 $_max = "\$Numrows['".$p->id_boucle
."']['total']";
500 $p->code
= "spip_bonux_sauter(\$result,$_compteur,$_nb,$_max)";
501 $p->interdire_scripts
= false;
506 * Produire un fichier statique a partir d'un squelette dynamique
507 * Permet ensuite a apache de le servir en statique sans repasser
508 * par spip.php a chaque hit sur le fichier
509 * si le format (css ou js) est passe dans contexte['format'], on l'utilise
510 * sinon on regarde si le fond finit par .css ou .js
511 * sinon on utilie "html"
513 * @param string $fond
514 * @param array $contexte
515 * @param array $options
516 * @param string $connect
519 function produire_fond_statique($fond, $contexte=array(), $options = array(), $connect=''){
520 if (isset($contexte['format'])){
521 $extension = $contexte['format'];
522 unset($contexte['format']);
526 if (preg_match(',[.](css|js|json)$,',$fond,$m))
529 // recuperer le contenu produit par le squelette
530 $options['raw'] = true;
531 $cache = recuperer_fond($fond,$contexte,$options,$connect);
533 // calculer le nom de la css
534 $dir_var = sous_repertoire (_DIR_VAR
, 'cache-'.$extension);
535 $filename = $dir_var . $extension."dyn-".md5($fond.serialize($contexte).$connect) .".$extension";
537 // mettre a jour le fichier si il n'existe pas
539 if (!file_exists($filename)
540 OR filemtime($filename)<$cache['lastmodified']
541 OR $GLOBALS['var_mode']=='recalcul') {
542 $contenu = $cache['texte'];
543 // passer les urls en absolu si c'est une css
544 if ($extension=="css")
545 $contenu = urls_absolues_css($contenu, generer_url_public($fond));
546 // ne pas insérer de commentaire si c'est du json
547 if ($extension!="json") {
548 $comment = "/* #PRODUIRE{fond=$fond";
549 foreach($contexte as $k=>$v)
550 $comment .= ",$k=$v";
551 $comment .="} le ".date("Y-m-d H:i:s")." */\n";
553 // et ecrire le fichier
554 ecrire_fichier($filename,$comment.$contenu);
560 function produire_css_fond($fond, $contexte=array(), $options = array(), $connect=''){
561 $contexte['format'] = "css";
562 return produire_fond_statique($fond, $contexte, $options, $connect);
564 function produire_js_fond($fond, $contexte=array(), $options = array(), $connect=''){
565 $contexte['format'] = "js";
566 return produire_fond_statique($fond, $contexte, $options, $connect);
571 * generer un fichier css statique a partir d'un squelette de CSS
574 * <link rel="stylesheet" type="text/css" href="#PRODUIRE_CSS_FOND{fond=css/macss,couleur=ffffff}" />
575 * la syntaxe de la balise est la meme que celle de #INCLURE
580 function balise_PRODUIRE_CSS_FOND_dist($p){
581 $balise_inclure = charger_fonction('INCLURE','balise');
582 $p = $balise_inclure($p);
583 $p->code
= str_replace('recuperer_fond(','produire_css_fond(',$p->code
);
588 * generer un fichier js statique a partir d'un squelette de JS
591 * <script type="text/javascript" src="#PRODUIRE_JS_FOND{fond=js/monscript}" ></script>
592 * la syntaxe de la balise est la meme que celle de #INCLURE
597 function balise_PRODUIRE_JS_FOND_dist($p){
598 $balise_inclure = charger_fonction('INCLURE','balise');
599 $p = $balise_inclure($p);
600 $p->code
= str_replace('recuperer_fond(','produire_js_fond(',$p->code
);
605 * generer un fichier statique a partir d'un squelette SPIP
607 * Le format du fichier sera extrait de la preextension du squelette (typo.css.html, messcripts.js.html)
608 * ou par l'argument format=css ou format=js passe en argument.
610 * Si pas de format detectable, on utilise .html, comme pour les squelettes
612 * <link rel="stylesheet" type="text/css" href="#PRODUIRE{fond=css/macss.css,couleur=ffffff}" />
613 * la syntaxe de la balise est la meme que celle de #INCLURE
618 function balise_PRODUIRE_dist($p){
619 $balise_inclure = charger_fonction('INCLURE','balise');
620 $p = $balise_inclure($p);
622 $p->code
= str_replace('recuperer_fond(','produire_fond_statique(',$p->code
);