076a3ba8fa5051f9ab46c21c806aa1ff5aed7e35
[velocampus/web/www.git] / www / plugins / auto / spip-bonux / public / spip_bonux_balises.php
1 <?php
2 /**
3 * Plugin Spip-Bonux
4 * Le plugin qui lave plus SPIP que SPIP
5 * (c) 2008 Mathieu Marcillaud, Cedric Morin, Romy Tetue
6 * Licence GPL
7 *
8 */
9
10 if (!defined("_ECRIRE_INC_VERSION")) return;
11
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)
21 AND $serveur!='pour'
22 AND $serveur!='condition') {
23 return $s;
24 }
25 }
26 return "";
27 }
28
29 //
30 // #URL_ACTION_AUTEUR{converser,arg,redirect} -> ecrire/?action=converser&arg=arg&hash=xxx&redirect=redirect
31 //
32 // http://doc.spip.org/@balise_URL_ACTION_AUTEUR_dist
33 function balise_URL_ACTION_AUTEUR($p) {
34 $p->descr['session'] = true;
35
36 // si serveur externe, ce n'est pas possible
37 if (get_nom_serveur($p)) {
38 $p->code = 'generer_url_public("404")';
39 return $p;
40 }
41
42 $p->code = interprete_argument_balise(1,$p);
43 $args = interprete_argument_balise(2,$p);
44 if (!$args)
45 $args = "''";
46 $p->code .= ",".$args;
47 $redirect = interprete_argument_balise(3,$p);
48 if ($redirect != "''" && $redirect!==NULL)
49 $p->code .= ",".$redirect;
50
51 $p->code = "generer_action_auteur(" . $p->code . ")";
52 $p->interdire_scripts = false;
53 return $p;
54 }
55 //
56 // #URL_ECRIRE{naviguer} -> ecrire/?exec=naviguer
57 //
58 // http://doc.spip.org/@balise_URL_ECRIRE_dist
59 function balise_URL_ECRIRE($p) {
60
61 // si serveur externe, ce n'est pas possible
62 if (get_nom_serveur($p)) {
63 $p->code = 'generer_url_public("404")';
64 return $p;
65 }
66
67 $code = interprete_argument_balise(1,$p);
68 if (!$code)
69 $fonc = "''";
70 else{
71 if (preg_match("/^'[^']*'$/", $code))
72 $fonc = $code;
73 else {$code = "(\$f = $code)"; $fonc = '$f';}
74 $args = interprete_argument_balise(2,$p);
75 if ($args != "''" && $args!==NULL)
76 $fonc .= ',' . $args;
77 }
78 $p->code = 'generer_url_ecrire(' . $fonc .')';
79 if (function_exists('tester_url_ecrire'))
80 if ($code)
81 $p->code = "(tester_url_ecrire($code) ?" . $p->code .' : "")';
82 #$p->interdire_scripts = true;
83 return $p;
84 }
85
86
87
88 // surplus de #URL_PAGE pour prendre en compte les boucles POUR et CONDITION
89 /* // ceci n'est pas suffisant car il faudrait traiter les autres types aussi
90 function generer_generer_url_pour($type, $code) {return 'generer_url_public(' . $code .')';}
91 function generer_generer_url_condition($type, $code) {return 'generer_url_public(' . $code .')';}
92 */
93 function balise_URL_PAGE($p) {
94
95 $p->code = interprete_argument_balise(1,$p);
96 $args = interprete_argument_balise(2,$p);
97 if ($args != "''" && $args!==NULL)
98 $p->code .= ','.$args;
99
100 // autres filtres (???)
101 array_shift($p->param);
102
103 if ($p->id_boucle
104 AND $s = get_nom_serveur($p)) {
105
106 if (!$GLOBALS['connexions'][$s]['spip_connect_version']) {
107 $p->code = "404";
108 } else {
109 // si une fonction de generation des url a ete definie pour ce connect l'utiliser
110 // elle devra aussi traiter le cas derogatoire type=page
111 if (function_exists($f = 'generer_generer_url_'.$s)){
112 $p->code = $f('page', $p->code, $s);
113 return $p;
114 }
115 $p->code .= ", 'connect=" . addslashes($s) . "'";
116 }
117 }
118
119 $p->code = 'generer_url_public(' . $p->code .')';
120 #$p->interdire_scripts = true;
121 return $p;
122 }
123
124
125 /**
126 * #SET
127 * Affecte une variable locale au squelette
128 * #SET{nom,valeur}
129 *
130 * SURCHARGE DU CORE :
131 * Affecte un filtre a une variable locale au squelette
132 * #SET{nom,filtre,param1,param2,...,paramN}
133 *
134 * @param object $p : objet balise
135 * @return ""
136 **/
137 /*
138 function balise_SET($p){
139 $_code = array();
140
141 $n=1;
142 while ($_v = interprete_argument_balise($n++,$p))
143 $_code[] = $_v;
144
145 $_nom = array_shift($_code);
146 $_valeur = array_shift($_code);
147 if ($_nom AND $_valeur AND count($_code)) {
148 $filtre = str_replace("'", "", strtolower($_valeur));
149 $f = chercher_filtre($filtre);
150 $p->code = "vide(\$Pile['vars'][$_nom]=$f(". join(', ',$_code)."))";
151 } elseif ($_nom AND $_valeur)
152 $p->code = "vide(\$Pile['vars'][$_nom] = $_valeur)";
153 else
154 $p->code = "''";
155
156 $p->interdire_scripts = false; // la balise ne renvoie rien
157 return $p;
158 }
159 */
160
161
162 /**
163 * Empile un element dans un tableau declare par #SET{tableau,#ARRAY}
164 * #SET_PUSH{tableau,valeur}
165 *
166 * @param object $p : objet balise
167 * @return ""
168 **/
169 function balise_SET_PUSH_dist($p){
170 $_nom = interprete_argument_balise(1,$p);
171 $_valeur = interprete_argument_balise(2,$p);
172
173 if ($_nom AND $_valeur)
174 // si le tableau n'existe pas encore, on le cree
175 // on ajoute la valeur ensuite (sans passer par array_push)
176 $p->code = "vide((\$cle=$_nom)
177 . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
178 . (\$Pile['vars'][\$cle][]=$_valeur))";
179 else
180 $p->code = "''";
181
182 $p->interdire_scripts = false; // la balise ne renvoie rien
183 return $p;
184 }
185
186 /**
187 * Si 3 arguments : Cree un tableau nom_tableau de t1 + t2
188 * #SET_MERGE{nom_tableau,t1,t2}
189 * #SET_MERGE{nom_tableau,#GET{tableau},#ARRAY{cle,valeur}}
190 *
191 * Si 2 arguments : Merge t1 dans nom_tableau
192 * #SET_MERGE{nom_tableau,t1}
193 * #SET_MERGE{nom_tableau,#GET{tableau}}
194 *
195 * @param object $p : objet balise
196 * @return ""
197 **/
198 function balise_SET_MERGE_dist($p){
199 $_nom = interprete_argument_balise(1,$p);
200 $_t1 = interprete_argument_balise(2,$p);
201 $_t2 = interprete_argument_balise(3,$p);
202
203 if ($_nom AND $_t1 AND !$_t2)
204 // 2 arguments : merge de $_nom et $_t1 dans $_nom
205 // si le tableau n'existe pas encore, on le cree
206 $p->code = "vide((\$cle=$_nom)
207 . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
208 . (is_array(\$new=$_t1)?'':\$new=array(\$new))
209 . (\$Pile['vars'][\$cle] = array_merge(\$Pile['vars'][\$cle],\$new)))";
210 elseif ($_nom AND $_t1 AND $_t2)
211 // 3 arguments : merge de $_t1 et $_t2 dans $_nom
212 // si le tableau n'existe pas encore, on le cree
213 $p->code = "vide((\$cle=$_nom)
214 . (is_array(\$Pile['vars'][\$cle])?'':\$Pile['vars'][\$cle]=array())
215 . (is_array(\$new1=$_t1)?'':\$new1=array(\$new1))
216 . (is_array(\$new2=$_t2)?'':\$new2=array(\$new2))
217 . (\$Pile['vars'][\$cle] = array_merge(\$new1,\$new2)))";
218 else
219 $p->code = "''";
220
221 $p->interdire_scripts = false; // la balise ne renvoie rien
222 return $p;
223 }
224
225 /**
226 * Balise #COMPTEUR associee au critere compteur
227 *
228 * @param unknown_type $p
229 * @return unknown
230 */
231 function balise_COMPTEUR_dist($p) {
232 calculer_balise_criteres('compteur', $p);
233 if ($p->code=="''")
234 calculer_balise_criteres('compteur', $p, "compteur_left");
235 return $p;
236 }
237
238 /** Balise #SOMME associee au critere somme */
239 function balise_SOMME_dist($p) {
240 return calculer_balise_criteres('somme', $p);
241 }
242
243 /** Balise #COMPTE associee au critere compte */
244 function balise_COMPTE_dist($p) {
245 return calculer_balise_criteres('compte', $p);
246 }
247
248 /** Balise #MOYENNE associee au critere moyenne */
249 function balise_MOYENNE_dist($p) {
250 return calculer_balise_criteres('moyenne', $p);
251 }
252
253 /** Balise #MINIMUM associee au critere moyenne */
254 function balise_MINIMUM_dist($p) {
255 return calculer_balise_criteres('minimum', $p);
256 }
257
258 /** Balise #MAXIMUM associee au critere moyenne */
259 function balise_MAXIMUM_dist($p) {
260 return calculer_balise_criteres('maximum', $p);
261 }
262
263 /** Balise #STATS associee au critere stats
264 * #STATS{id_article,moyenne}
265 */
266 function balise_STATS_dist($p) {
267 if (isset($p->param[0][2][0])
268 AND $nom = ($p->param[0][2][0]->texte)) {
269 return calculer_balise_criteres($nom, $p, 'stats');
270 }
271 return $p;
272 }
273
274 function calculer_balise_criteres($nom, $p, $motif="") {
275 $p->code = "''";
276 $motif = $motif ? $motif : $nom;
277 if (isset($p->param[0][1][0])
278 AND $champ = ($p->param[0][1][0]->texte)) {
279 return rindex_pile($p, $nom."_$champ", $motif);
280 }
281 return $p;
282 }
283
284
285
286 /**
287 * #TRI{champ[,libelle]}
288 * champ prend < ou > pour afficher le lien de changement de sens
289 * croissant ou decroissant
290 *
291 * @param unknown_type $p
292 * @param unknown_type $liste
293 * @return unknown
294 */
295 function balise_TRI_dist($p, $liste='true') {
296 $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
297
298 // s'il n'y a pas de nom de boucle, on ne peut pas trier
299 if ($b === '') {
300 erreur_squelette(
301 _T('zbug_champ_hors_boucle',
302 array('champ' => '#TRI')
303 ), $p->id_boucle);
304 $p->code = "''";
305 return $p;
306 }
307 $boucle = $p->boucles[$b];
308
309 // s'il n'y a pas de tri_champ, c'est qu'on se trouve
310 // dans un boucle recursive ou qu'on a oublie le critere {tri}
311 if (!isset($boucle->modificateur['tri_champ'])) {
312 erreur_squelette(
313 _T('zbug_tri_sans_critere',
314 array('champ' => '#TRI')
315 ), $p->id_boucle);
316 $p->code = "''";
317 return $p;
318 }
319
320 $_champ = interprete_argument_balise(1,$p);
321 // si pas de champ, renvoyer le critere de tri utilise
322 if (!$_champ){
323 $p->code = $boucle->modificateur['tri_champ'];
324 return $p;
325 }
326
327 $_libelle = interprete_argument_balise(2,$p);
328 $_libelle = $_libelle?$_libelle:$_champ;
329
330 $_class = interprete_argument_balise(3,$p);
331 // si champ = "<" c'est un lien vers le tri croissant : 1<2<3<4 ... ==> 1
332 // si champ = ">" c'est un lien vers le tri decroissant :.. 4>3>2>1 == -1
333 $_issens = "in_array($_champ,array('<','>'))";
334 $_sens = "(strpos('> <',$_champ)-1)";
335
336 $_variable = "((\$s=$_issens)?'sens':'tri').".$boucle->modificateur['tri_nom'];
337 $_url = "parametre_url(self(),$_variable,\$s?$_sens:$_champ)";
338 $_on = "\$s?(".$boucle->modificateur['tri_sens']."==$_sens".'):('.$boucle->modificateur['tri_champ']."==$_champ)";
339
340 $p->code = "lien_ou_expose($_url,$_libelle,$_on".($_class?",$_class":"").")";
341 //$p->code = "''";
342 $p->interdire_scripts = false;
343 return $p;
344 }
345
346 /**
347 * Generer un bouton d'action en post, ajaxable
348 * a utiliser a la place des liens action_auteur, sous la forme
349 * #BOUTON_ACTION{libelle,url}
350 * ou
351 * #BOUTON_ACTION{libelle,url,ajax} pour que l'action soit ajax comme un lien class='ajax'
352 * ou
353 * #BOUTON_ACTION{libelle,url,ajax,message_confirmation} pour utiliser un message de confirmation
354 * ou
355 * #BOUTON_ACTION{libelle,url,ajax,'',info} pour inserer une bulle d'information
356 *
357 * @param unknown_type $p
358 * @return unknown
359 */
360 function balise_BOUTON_ACTION($p){
361
362 $_label = interprete_argument_balise(1,$p);
363 if (!$_label) $_label="''";
364
365 $_url = interprete_argument_balise(2,$p);
366 if (!$_url) $_url="''";
367
368 $_class = interprete_argument_balise(3,$p);
369 if (!$_class) $_class="''";
370
371 $_confirm = interprete_argument_balise(4,$p);
372 if ((!$_confirm) OR ($_confirm=="''")) { $_onclick=''; }
373 else $_onclick = " onclick=\'return confirm(\"' . attribut_html($_confirm) . '\");\'";
374
375 $_title = interprete_argument_balise(5,$p);
376 if (!$_title) $_title="''";
377 else $_title = "' title=\'' . $_title . '\''";
378
379 $p->code = "'<form class=\'bouton_action_post ' . $_class . '\' method=\'post\' action=\'' . (\$u=$_url) . '\'>'
380 . '<div>' . form_hidden(\$u)
381 . '<button type=\'submit\' class=\'submit\' $_onclick' . $_title . '>' . $_label . '</button>'
382 . '</div></form>'";
383 $p->interdire_scripts = false;
384 return $p;
385 }
386
387 /**
388 * Generer n'importe quel info pour un objet : #INFO_TITRE{article, #ENV{id_article}}
389 * Utilise la fonction generer_info_entite(), se reporter a sa documentation
390 */
391 function balise_INFO__dist($p){
392 $info = $p->nom_champ;
393 $type_objet = interprete_argument_balise(1,$p);
394 $id_objet = interprete_argument_balise(2,$p);
395 if ($info === 'INFO_' or !$type_objet or !$id_objet) {
396 $msg = _T('zbug_balise_sans_argument', array('balise' => ' INFO_'));
397 erreur_squelette($msg, $p);
398 $p->interdire_scripts = true;
399 return $p;
400 }else {
401 $p->code = champ_sql($info, $p, false);
402 if (strpos($p->code, '@$Pile[0]') !== false) {
403 $info = strtolower(substr($info,5));
404 $p->code = "generer_info_entite($id_objet, $type_objet, '$info'".($p->etoile?","._q($p->etoile):"").")";
405 }
406 $p->interdire_scripts = true;
407 return $p;
408 }
409 }
410
411
412 /**
413 * Savoir si on objet est publie ou non
414 *
415 * @param <type> $p
416 * @return <type>
417 */
418 function balise_PUBLIE_dist($p) {
419
420 $type = $p->type_requete;
421
422 $_statut = champ_sql('statut',$p);
423
424
425 $_texte = champ_sql('texte', $p);
426 $_descriptif = "''";
427
428 switch ($type){
429 case 'articles':
430 $p->code = "$_statut=='publie'";
431 if ($GLOBALS['meta']["post_dates"] == 'non'){
432 $_date_pub = champ_sql('date',$p);
433 $p->code .= "AND $_date_pub<quete_date_postdates()";
434 }
435 break;
436 case 'auteurs':
437 $_id = champ_sql('id_auteur',$p);
438 $p->code = "sql_countsel('spip_articles AS AR JOIN spip_auteurs_articles AS AU ON AR.id_article=AU.id_article',
439 'AU.id_auteur=intval('.$_id.') AND AR.statut=\'publie\''"
440 .(($GLOBALS['meta']['post_dates'] == 'non')?".' AND AR.date<'.sql_quote(quete_date_postdates())":'')
441 .")>0";
442 break;
443 // le cas des documents prend directement en compte la mediatheque
444 // car le fonctionnement par defaut de SPIP <=2.0 est trop tordu et insatisfaisant
445 case 'documents':
446 $p->code = "$_statut=='publie'";
447 if ($GLOBALS['meta']["post_dates"] == 'non'){
448 $_date_pub = champ_sql('date_publication',$p);
449 $p->code .= "AND $_date_pub<quete_date_postdates()";
450 }
451 break;
452 default:
453 $p->code = "($_statut=='publie'?' ':'')";
454 break;
455 }
456
457 $p->code = "((".$p->code.")?' ':'')";
458
459 #$p->interdire_scripts = true;
460 return $p;
461 }
462
463
464 /**
465 * Implementation securisee du saut en avant
466 * pour la balise #SAUTER
467 *
468 * @param resource $res
469 * @param int $pos
470 * @param int $nb
471 * @param int $total
472 */
473 function spip_bonux_sauter(&$res, &$pos, $nb, $total){
474 // pas de saut en arriere qu'on ne sait pas faire sans sql_seek
475 if (($nb=intval($nb))<=0) return;
476
477 $saut = $pos + $nb;
478 // si le saut fait depasser le maxi, on libere et on sort
479 if ($saut>=$total) {sql_free($res); return;}
480
481 if (sql_seek($res, $saut))
482 $pos += $nb;
483 else
484 while ($pos<$saut AND sql_fetch($res))
485 $pos++;
486 return;
487 }
488
489 /**
490 * #SAUTER{n} permet de sauter en avant n resultats dans une boucle
491 * La balise modifie le compteur courant de la boucle, mais pas les autres
492 * champs
493 *
494 * L'argument n doit etre superieur a zero sinon la balise ne fait rien
495 * Lorsque sql_seek est disponible, il est utilise,
496 * sinon le saut est realise par n sql_fetch
497 *
498 * @param <type> $p
499 * @return <type>
500 */
501 function balise_SAUTER_dist($p){
502 $_nb = interprete_argument_balise(1,$p);
503 $_compteur = "\$Numrows['".$p->id_boucle."']['compteur_boucle']";
504 $_max = "\$Numrows['".$p->id_boucle."']['total']";
505
506 $p->code = "spip_bonux_sauter(\$result,$_compteur,$_nb,$_max)";
507 $p->interdire_scripts = false;
508 return $p;
509 }
510
511 /**
512 * Produire un fichier statique a partir d'un squelette dynamique
513 * Permet ensuite a apache de le servir en statique sans repasser
514 * par spip.php a chaque hit sur le fichier
515 * si le format (css ou js) est passe dans contexte['format'], on l'utilise
516 * sinon on regarde si le fond finit par .css ou .js
517 * sinon on utilie "html"
518 *
519 * @param string $fond
520 * @param array $contexte
521 * @param array $options
522 * @param string $connect
523 * @return string
524 */
525 function produire_fond_statique($fond, $contexte=array(), $options = array(), $connect=''){
526 if (isset($contexte['format'])){
527 $extension = $contexte['format'];
528 unset($contexte['format']);
529 }
530 else {
531 $extension = "html";
532 if (preg_match(',[.](css|js)$,',$fond,$m))
533 $extension = $m[1];
534 }
535 // recuperer le contenu produit par le squelette
536 $options['raw'] = true;
537 $cache = recuperer_fond($fond,$contexte,$options,$connect);
538
539 // calculer le nom de la css
540 $dir_var = sous_repertoire (_DIR_VAR, 'cache-'.$extension);
541 $filename = $dir_var . $extension."dyn-".md5($fond.serialize($contexte).$connect) .".$extension";
542
543 // mettre a jour le fichier si il n'existe pas
544 // ou trop ancien
545 if (!file_exists($filename)
546 OR filemtime($filename)<$cache['lastmodified']
547 OR $GLOBALS['var_mode']=='recalcul'){
548
549 $contenu = $cache['texte'];
550 // passer les urls en absolu si c'est une css
551 if ($extension=="css")
552 $contenu = urls_absolues_css($contenu, generer_url_public($fond));
553
554 $comment = "/* #PRODUIRE{fond=$fond";
555 foreach($contexte as $k=>$v)
556 $comment .= ",$k=$v";
557 $comment .="} le ".date("Y-m-d H:i:s")." */\n";
558 // et ecrire le fichier
559 ecrire_fichier($filename,$comment.$contenu);
560 }
561
562 return $filename;
563 }
564
565 function produire_css_fond($fond, $contexte=array(), $options = array(), $connect=''){
566 $contexte['format'] = "css";
567 return produire_fond_statique($fond, $contexte, $options, $connect);
568 }
569 function produire_js_fond($fond, $contexte=array(), $options = array(), $connect=''){
570 $contexte['format'] = "js";
571 return produire_fond_statique($fond, $contexte, $options, $connect);
572 }
573
574 /**
575 * #PRODUIRE_CSS_FOND
576 * generer un fichier css statique a partir d'un squelette de CSS
577 * utilisable en
578 *
579 * <link rel="stylesheet" type="text/css" href="#PRODUIRE_CSS_FOND{fond=css/macss,couleur=ffffff}" />
580 * la syntaxe de la balise est la meme que celle de #INCLURE
581 *
582 * @param object $p
583 * @return object
584 */
585 function balise_PRODUIRE_CSS_FOND_dist($p){
586 $balise_inclure = charger_fonction('INCLURE','balise');
587 $p = $balise_inclure($p);
588 $p->code = str_replace('recuperer_fond(','produire_css_fond(',$p->code);
589 return $p;
590 }
591 /**
592 * #PRODUIRE_JS_FOND
593 * generer un fichier js statique a partir d'un squelette de JS
594 * utilisable en
595 *
596 * <script type="text/javascript" src="#PRODUIRE_JS_FOND{fond=js/monscript}" ></script>
597 * la syntaxe de la balise est la meme que celle de #INCLURE
598 *
599 * @param object $p
600 * @return object
601 */
602 function balise_PRODUIRE_JS_FOND_dist($p){
603 $balise_inclure = charger_fonction('INCLURE','balise');
604 $p = $balise_inclure($p);
605 $p->code = str_replace('recuperer_fond(','produire_js_fond(',$p->code);
606 return $p;
607 }
608 /**
609 * #PRODUIRE
610 * generer un fichier statique a partir d'un squelette SPIP
611 *
612 * Le format du fichier sera extrait de la preextension du squelette (typo.css.html, messcripts.js.html)
613 * ou par l'argument format=css ou format=js passe en argument.
614 *
615 * Si pas de format detectable, on utilise .html, comme pour les squelettes
616 *
617 * <link rel="stylesheet" type="text/css" href="#PRODUIRE{fond=css/macss.css,couleur=ffffff}" />
618 * la syntaxe de la balise est la meme que celle de #INCLURE
619 *
620 * @param object $p
621 * @return object
622 */
623 function balise_PRODUIRE_dist($p){
624 $balise_inclure = charger_fonction('INCLURE','balise');
625 $p = $balise_inclure($p);
626
627 $p->code = str_replace('recuperer_fond(','produire_fond_statique(',$p->code);
628
629 return $p;
630 }
631 ?>