[SPIP] ~maj v2.1.25-->2.1.26
[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 $p->code = interprete_argument_balise(1,$p);
37 $args = interprete_argument_balise(2,$p);
38 if (!$args)
39 $args = "''";
40 $p->code .= ",".$args;
41 $redirect = interprete_argument_balise(3,$p);
42 if ($redirect != "''" && $redirect!==NULL)
43 $p->code .= ",".$redirect;
44
45 $p->code = "generer_action_auteur(" . $p->code . ")";
46 $p->interdire_scripts = false;
47 return $p;
48 }
49 //
50 // #URL_ECRIRE{naviguer} -> ecrire/?exec=naviguer
51 //
52 // http://doc.spip.org/@balise_URL_ECRIRE_dist
53 function balise_URL_ECRIRE($p) {
54
55 // si serveur externe, ce n'est pas possible
56 if (get_nom_serveur($p)) {
57 $p->code = 'generer_url_public("404")';
58 return $p;
59 }
60
61 $code = interprete_argument_balise(1,$p);
62 if (!$code)
63 $fonc = "''";
64 else{
65 if (preg_match("/^'[^']*'$/", $code))
66 $fonc = $code;
67 else {$code = "(\$f = $code)"; $fonc = '$f';}
68 $args = interprete_argument_balise(2,$p);
69 if ($args != "''" && $args!==NULL)
70 $fonc .= ',' . $args;
71 }
72 $p->code = 'generer_url_ecrire(' . $fonc .')';
73 if (function_exists('tester_url_ecrire'))
74 if ($code)
75 $p->code = "(tester_url_ecrire($code) ?" . $p->code .' : "")';
76 #$p->interdire_scripts = true;
77 return $p;
78 }
79
80
81
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 .')';}
86 */
87 function balise_URL_PAGE($p) {
88
89 $p->code = interprete_argument_balise(1,$p);
90 $args = interprete_argument_balise(2,$p);
91 if ($args != "''" && $args!==NULL)
92 $p->code .= ','.$args;
93
94 // autres filtres (???)
95 array_shift($p->param);
96
97 if ($p->id_boucle
98 AND $s = get_nom_serveur($p)) {
99
100 if (!$GLOBALS['connexions'][$s]['spip_connect_version']) {
101 $p->code = "404";
102 } else {
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);
107 return $p;
108 }
109 $p->code .= ", 'connect=" . addslashes($s) . "'";
110 }
111 }
112
113 $p->code = 'generer_url_public(' . $p->code .')';
114 #$p->interdire_scripts = true;
115 return $p;
116 }
117
118
119 /**
120 * #SET
121 * Affecte une variable locale au squelette
122 * #SET{nom,valeur}
123 *
124 * SURCHARGE DU CORE :
125 * Affecte un filtre a une variable locale au squelette
126 * #SET{nom,filtre,param1,param2,...,paramN}
127 *
128 * @param object $p : objet balise
129 * @return ""
130 **/
131 /*
132 function balise_SET($p){
133 $_code = array();
134
135 $n=1;
136 while ($_v = interprete_argument_balise($n++,$p))
137 $_code[] = $_v;
138
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)";
147 else
148 $p->code = "''";
149
150 $p->interdire_scripts = false; // la balise ne renvoie rien
151 return $p;
152 }
153 */
154
155
156 /**
157 * Empile un element dans un tableau declare par #SET{tableau,#ARRAY}
158 * #SET_PUSH{tableau,valeur}
159 *
160 * @param object $p : objet balise
161 * @return ""
162 **/
163 function balise_SET_PUSH_dist($p){
164 $_nom = interprete_argument_balise(1,$p);
165 $_valeur = interprete_argument_balise(2,$p);
166
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))";
173 else
174 $p->code = "''";
175
176 $p->interdire_scripts = false; // la balise ne renvoie rien
177 return $p;
178 }
179
180 /**
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}}
184 *
185 * Si 2 arguments : Merge t1 dans nom_tableau
186 * #SET_MERGE{nom_tableau,t1}
187 * #SET_MERGE{nom_tableau,#GET{tableau}}
188 *
189 * @param object $p : objet balise
190 * @return ""
191 **/
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);
196
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)))";
212 else
213 $p->code = "''";
214
215 $p->interdire_scripts = false; // la balise ne renvoie rien
216 return $p;
217 }
218
219 /**
220 * Balise #COMPTEUR associee au critere compteur
221 *
222 * @param unknown_type $p
223 * @return unknown
224 */
225 function balise_COMPTEUR_dist($p) {
226 calculer_balise_criteres('compteur', $p);
227 if ($p->code=="''")
228 calculer_balise_criteres('compteur', $p, "compteur_left");
229 return $p;
230 }
231
232 /** Balise #SOMME associee au critere somme */
233 function balise_SOMME_dist($p) {
234 return calculer_balise_criteres('somme', $p);
235 }
236
237 /** Balise #COMPTE associee au critere compte */
238 function balise_COMPTE_dist($p) {
239 return calculer_balise_criteres('compte', $p);
240 }
241
242 /** Balise #MOYENNE associee au critere moyenne */
243 function balise_MOYENNE_dist($p) {
244 return calculer_balise_criteres('moyenne', $p);
245 }
246
247 /** Balise #MINIMUM associee au critere moyenne */
248 function balise_MINIMUM_dist($p) {
249 return calculer_balise_criteres('minimum', $p);
250 }
251
252 /** Balise #MAXIMUM associee au critere moyenne */
253 function balise_MAXIMUM_dist($p) {
254 return calculer_balise_criteres('maximum', $p);
255 }
256
257 /** Balise #STATS associee au critere stats
258 * #STATS{id_article,moyenne}
259 */
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');
264 }
265 return $p;
266 }
267
268 function calculer_balise_criteres($nom, $p, $motif="") {
269 $p->code = "''";
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);
274 }
275 return $p;
276 }
277
278
279
280 /**
281 * #TRI{champ[,libelle]}
282 * champ prend < ou > pour afficher le lien de changement de sens
283 * croissant ou decroissant
284 *
285 * @param unknown_type $p
286 * @param unknown_type $liste
287 * @return unknown
288 */
289 function balise_TRI_dist($p, $liste='true') {
290 $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
291
292 // s'il n'y a pas de nom de boucle, on ne peut pas trier
293 if ($b === '') {
294 erreur_squelette(
295 _T('zbug_champ_hors_boucle',
296 array('champ' => '#TRI')
297 ), $p->id_boucle);
298 $p->code = "''";
299 return $p;
300 }
301 $boucle = $p->boucles[$b];
302
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'])) {
306 erreur_squelette(
307 _T('zbug_tri_sans_critere',
308 array('champ' => '#TRI')
309 ), $p->id_boucle);
310 $p->code = "''";
311 return $p;
312 }
313
314 $_champ = interprete_argument_balise(1,$p);
315 // si pas de champ, renvoyer le critere de tri utilise
316 if (!$_champ){
317 $p->code = $boucle->modificateur['tri_champ'];
318 return $p;
319 }
320
321 $_libelle = interprete_argument_balise(2,$p);
322 $_libelle = $_libelle?$_libelle:$_champ;
323
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)";
329
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)";
333
334 $p->code = "lien_ou_expose($_url,$_libelle,$_on".($_class?",$_class":"").")";
335 //$p->code = "''";
336 $p->interdire_scripts = false;
337 return $p;
338 }
339
340 /**
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}
344 * ou
345 * #BOUTON_ACTION{libelle,url,ajax} pour que l'action soit ajax comme un lien class='ajax'
346 * ou
347 * #BOUTON_ACTION{libelle,url,ajax,message_confirmation} pour utiliser un message de confirmation
348 * ou
349 * #BOUTON_ACTION{libelle,url,ajax,'',info} pour inserer une bulle d'information
350 *
351 * @param unknown_type $p
352 * @return unknown
353 */
354 function balise_BOUTON_ACTION($p){
355
356 $_label = interprete_argument_balise(1,$p);
357 if (!$_label) $_label="''";
358
359 $_url = interprete_argument_balise(2,$p);
360 if (!$_url) $_url="''";
361
362 $_class = interprete_argument_balise(3,$p);
363 if (!$_class) $_class="''";
364
365 $_confirm = interprete_argument_balise(4,$p);
366 if ((!$_confirm) OR ($_confirm=="''")) { $_onclick=''; }
367 else $_onclick = " onclick=\'return confirm(\"' . attribut_html($_confirm) . '\");\'";
368
369 $_title = interprete_argument_balise(5,$p);
370 if (!$_title) $_title="''";
371 else $_title = "' title=\'' . $_title . '\''";
372
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>'
376 . '</div></form>'";
377 $p->interdire_scripts = false;
378 return $p;
379 }
380
381 /**
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
384 */
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;
393 return $p;
394 }else {
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):"").")";
399 }
400 $p->interdire_scripts = true;
401 return $p;
402 }
403 }
404
405
406 /**
407 * Savoir si on objet est publie ou non
408 *
409 * @param <type> $p
410 * @return <type>
411 */
412 function balise_PUBLIE_dist($p) {
413
414 $type = $p->type_requete;
415
416 $_statut = champ_sql('statut',$p);
417
418
419 $_texte = champ_sql('texte', $p);
420 $_descriptif = "''";
421
422 switch ($type){
423 case 'articles':
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()";
428 }
429 break;
430 case 'auteurs':
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())":'')
435 .")>0";
436 break;
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
439 case 'documents':
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()";
444 }
445 break;
446 default:
447 $p->code = "($_statut=='publie'?' ':'')";
448 break;
449 }
450
451 $p->code = "((".$p->code.")?' ':'')";
452
453 #$p->interdire_scripts = true;
454 return $p;
455 }
456
457
458 /**
459 * Implementation securisee du saut en avant
460 * pour la balise #SAUTER
461 *
462 * @param resource $res
463 * @param int $pos
464 * @param int $nb
465 * @param int $total
466 */
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;
470
471 $saut = $pos + $nb;
472 // si le saut fait depasser le maxi, on libere et on sort
473 if ($saut>=$total) {sql_free($res); return;}
474
475 if (sql_seek($res, $saut))
476 $pos += $nb;
477 else
478 while ($pos<$saut AND sql_fetch($res))
479 $pos++;
480 return;
481 }
482
483 /**
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
486 * champs
487 *
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
491 *
492 * @param <type> $p
493 * @return <type>
494 */
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']";
499
500 $p->code = "spip_bonux_sauter(\$result,$_compteur,$_nb,$_max)";
501 $p->interdire_scripts = false;
502 return $p;
503 }
504
505 /**
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"
512 *
513 * @param string $fond
514 * @param array $contexte
515 * @param array $options
516 * @param string $connect
517 * @return string
518 */
519 function produire_fond_statique($fond, $contexte=array(), $options = array(), $connect=''){
520 if (isset($contexte['format'])){
521 $extension = $contexte['format'];
522 unset($contexte['format']);
523 }
524 else {
525 $extension = "html";
526 if (preg_match(',[.](css|js|json)$,',$fond,$m))
527 $extension = $m[1];
528 }
529 // recuperer le contenu produit par le squelette
530 $options['raw'] = true;
531 $cache = recuperer_fond($fond,$contexte,$options,$connect);
532
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";
536
537 // mettre a jour le fichier si il n'existe pas
538 // ou trop ancien
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";
552 }
553 // et ecrire le fichier
554 ecrire_fichier($filename,$comment.$contenu);
555 }
556
557 return $filename;
558 }
559
560 function produire_css_fond($fond, $contexte=array(), $options = array(), $connect=''){
561 $contexte['format'] = "css";
562 return produire_fond_statique($fond, $contexte, $options, $connect);
563 }
564 function produire_js_fond($fond, $contexte=array(), $options = array(), $connect=''){
565 $contexte['format'] = "js";
566 return produire_fond_statique($fond, $contexte, $options, $connect);
567 }
568
569 /**
570 * #PRODUIRE_CSS_FOND
571 * generer un fichier css statique a partir d'un squelette de CSS
572 * utilisable en
573 *
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
576 *
577 * @param object $p
578 * @return object
579 */
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);
584 return $p;
585 }
586 /**
587 * #PRODUIRE_JS_FOND
588 * generer un fichier js statique a partir d'un squelette de JS
589 * utilisable en
590 *
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
593 *
594 * @param object $p
595 * @return object
596 */
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);
601 return $p;
602 }
603 /**
604 * #PRODUIRE
605 * generer un fichier statique a partir d'un squelette SPIP
606 *
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.
609 *
610 * Si pas de format detectable, on utilise .html, comme pour les squelettes
611 *
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
614 *
615 * @param object $p
616 * @return object
617 */
618 function balise_PRODUIRE_dist($p){
619 $balise_inclure = charger_fonction('INCLURE','balise');
620 $p = $balise_inclure($p);
621
622 $p->code = str_replace('recuperer_fond(','produire_fond_statique(',$p->code);
623
624 return $p;
625 }
626 ?>