[SPIP] v3.2.1-->v3.2.2
[lhc/web/www.git] / www / ecrire / public / balises.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2019 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 /**
14 * Ce fichier regroupe la quasi totalité des définitions de `#BALISES` de SPIP.
15 *
16 * Pour chaque balise, il est possible de surcharger, dans son fichier
17 * mes_fonctions.php, la fonction `balise_TOTO_dist()` par une fonction
18 * `balise_TOTO()` respectant la même API : elle reçoit en entrée un objet
19 * de classe `Champ`, le modifie et le retourne. Cette classe est définie
20 * dans public/interfaces.
21 *
22 * Des balises dites «dynamiques» sont également déclarées dans le
23 * répertoire ecrire/balise/
24 *
25 * @package SPIP\Core\Compilateur\Balises
26 **/
27
28 if (!defined('_ECRIRE_INC_VERSION')) {
29 return;
30 }
31
32 /**
33 * Retourne le code PHP d'un argument de balise s'il est présent
34 *
35 * @uses calculer_liste()
36 * @example
37 * ```
38 * // Retourne le premier argument de la balise
39 * // #BALISE{premier,deuxieme}
40 * $arg = interprete_argument_balise(1,$p);
41 * ```
42 *
43 * @param int $n
44 * Numéro de l'argument
45 * @param Champ $p
46 * Pile au niveau de la balise
47 * @return string|null
48 * Code PHP si cet argument est présent, sinon null
49 **/
50 function interprete_argument_balise($n, $p) {
51 if (($p->param) && (!$p->param[0][0]) && (count($p->param[0]) > $n)) {
52 return calculer_liste($p->param[0][$n],
53 $p->descr,
54 $p->boucles,
55 $p->id_boucle);
56 } else {
57 return null;
58 }
59 }
60
61
62 //
63 // Définition des balises
64 //
65
66 /**
67 * Compile la balise `#NOM_SITE_SPIP` retournant le nom du site
68 *
69 * @balise
70 * @link http://www.spip.net/4622
71 *
72 * @param Champ $p
73 * Pile au niveau de la balise
74 * @return Champ
75 * Pile complétée par le code à générer
76 **/
77 function balise_NOM_SITE_SPIP_dist($p) {
78 $p->code = "\$GLOBALS['meta']['nom_site']";
79
80 #$p->interdire_scripts = true;
81 return $p;
82 }
83
84 /**
85 * Compile la balise `#EMAIL_WEBMASTER` retournant l'adresse courriel
86 * du webmestre
87 *
88 * @balise
89 * @link http://www.spip.net/4586
90 *
91 * @param Champ $p
92 * Pile au niveau de la balise
93 * @return Champ
94 * Pile complétée par le code à générer
95 **/
96 function balise_EMAIL_WEBMASTER_dist($p) {
97 $p->code = "\$GLOBALS['meta']['email_webmaster']";
98
99 #$p->interdire_scripts = true;
100 return $p;
101 }
102
103 /**
104 * Compile la balise `#DESCRIPTIF_SITE_SPIP` qui retourne le descriptif
105 * du site !
106 *
107 * @balise
108 * @link http://www.spip.net/4338
109 *
110 * @param Champ $p
111 * Pile au niveau de la balise
112 * @return Champ
113 * Pile complétée par le code à générer
114 **/
115 function balise_DESCRIPTIF_SITE_SPIP_dist($p) {
116 $p->code = "\$GLOBALS['meta']['descriptif_site']";
117
118 #$p->interdire_scripts = true;
119 return $p;
120 }
121
122
123 /**
124 * Compile la balise `#CHARSET` qui retourne le nom du jeu de caractères
125 * utilisé par le site tel que `utf-8`
126 *
127 * @balise
128 * @link http://www.spip.net/4331
129 * @example
130 * ```
131 * <meta http-equiv="Content-Type" content="text/html; charset=#CHARSET" />
132 * ```
133 *
134 * @param Champ $p
135 * Pile au niveau de la balise
136 * @return Champ
137 * Pile complétée par le code à générer
138 **/
139 function balise_CHARSET_dist($p) {
140 $p->code = "\$GLOBALS['meta']['charset']";
141
142 #$p->interdire_scripts = true;
143 return $p;
144 }
145
146 /**
147 * Compile la balise `#LANG_LEFT` retournant 'left' si la langue s'écrit
148 * de gauche à droite, sinon 'right'
149 *
150 * @note
151 * Peut servir à l'écriture de code CSS dans un squelette, mais
152 * pour inclure un fichier css, il vaut mieux utiliser le filtre
153 * `direction_css` si on le souhaite sensible à la langue utilisé.
154 *
155 * @balise
156 * @link http://www.spip.net/4625
157 * @see lang_dir()
158 * @see balise_LANG_RIGHT_dist()
159 * @see balise_LANG_DIR_dist()
160 * @see direction_css()
161 *
162 * @param Champ $p
163 * Pile au niveau de la balise
164 * @return Champ
165 * Pile complétée par le code à générer
166 **/
167 function balise_LANG_LEFT_dist($p) {
168 $_lang = champ_sql('lang', $p);
169 $p->code = "lang_dir($_lang, 'left','right')";
170 $p->interdire_scripts = false;
171
172 return $p;
173 }
174
175 /**
176 * Compile la balise `#LANG_RIGHT` retournant 'right' si la langue s'écrit
177 * de gauche à droite, sinon 'left'
178 *
179 * @balise
180 * @link http://www.spip.net/4625
181 * @see lang_dir()
182 * @see balise_LANG_LEFT_dist()
183 * @see balise_LANG_DIR_dist()
184 * @see direction_css()
185 *
186 * @param Champ $p
187 * Pile au niveau de la balise
188 * @return Champ
189 * Pile complétée par le code à générer
190 **/
191 function balise_LANG_RIGHT_dist($p) {
192 $_lang = champ_sql('lang', $p);
193 $p->code = "lang_dir($_lang, 'right','left')";
194 $p->interdire_scripts = false;
195
196 return $p;
197 }
198
199 /**
200 * Compile la balise `#LANG_DIR` retournant 'ltr' si la langue s'écrit
201 * de gauche à droite, sinon 'rtl'
202 *
203 * @balise
204 * @link http://www.spip.net/4625
205 * @see lang_dir()
206 * @see balise_LANG_LEFT_dist()
207 * @see balise_LANG_RIGHT_dist()
208 * @example
209 * ```
210 * <html dir="#LANG_DIR" lang="#LANG"
211 * xmlns="http://www.w3.org/1999/xhtml"
212 * xml:lang="#LANG" class="[(#LANG_DIR)][ (#LANG)] no-js">
213 * ```
214 *
215 * @param Champ $p
216 * Pile au niveau de la balise
217 * @return Champ
218 * Pile complétée par le code à générer
219 **/
220 function balise_LANG_DIR_dist($p) {
221 $_lang = champ_sql('lang', $p);
222 $p->code = "lang_dir($_lang, 'ltr','rtl')";
223 $p->interdire_scripts = false;
224
225 return $p;
226 }
227
228
229 /**
230 * Compile la balise `#PUCE` affichant une puce
231 *
232 * @balise
233 * @link http://www.spip.net/4628
234 * @see definir_puce()
235 *
236 * @param Champ $p
237 * Pile au niveau de la balise
238 * @return Champ
239 * Pile complétée par le code à générer
240 **/
241 function balise_PUCE_dist($p) {
242 $p->code = "definir_puce()";
243 $p->interdire_scripts = false;
244
245 return $p;
246 }
247
248
249 /**
250 * Compile la balise `#DATE` qui retourne la date de mise en ligne
251 *
252 * Cette balise retourne soit le champ `date` d'une table si elle est
253 * utilisée dans une boucle, sinon la date de calcul du squelette.
254 *
255 * @balise
256 * @link http://www.spip.net/4336 Balise DATE
257 * @link http://www.spip.net/1971 La gestion des dates
258 * @example
259 * ```
260 * <td>[(#DATE|affdate_jourcourt)]</td>
261 * ```
262 *
263 * @param Champ $p
264 * Pile au niveau de la balise.
265 * @return Champ
266 * Pile completée du code PHP d'exécution de la balise
267 */
268 function balise_DATE_dist($p) {
269 $d = champ_sql('date', $p);
270 # if ($d === "@\$Pile[0]['date']")
271 # $d = "isset(\$Pile[0]['date']) ? $d : time()";
272 $p->code = $d;
273
274 return $p;
275 }
276
277
278 /**
279 * Compile la balise `#DATE_REDAC` qui retourne la date de première publication
280 *
281 * Cette balise retourne le champ `date_redac` d'une table
282 *
283 * @balise
284 * @link http://www.spip.net/3858 Balises DATE_MODIF et DATE_REDAC
285 * @link http://www.spip.net/1971 La gestion des dates
286 * @see balise_DATE_MODIF_dist()
287 *
288 * @param Champ $p
289 * Pile au niveau de la balise.
290 * @return Champ
291 * Pile completée du code PHP d'exécution de la balise
292 */
293 function balise_DATE_REDAC_dist($p) {
294 $d = champ_sql('date_redac', $p);
295 # if ($d === "@\$Pile[0]['date_redac']")
296 # $d = "isset(\$Pile[0]['date_redac']) ? $d : time()";
297 $p->code = $d;
298 $p->interdire_scripts = false;
299
300 return $p;
301 }
302
303 /**
304 * Compile la balise `#DATE_MODIF` qui retourne la date de dernière modification
305 *
306 * Cette balise retourne le champ `date_modif` d'une table
307 *
308 * @balise
309 * @link http://www.spip.net/3858 Balises DATE_MODIF et DATE_REDAC
310 * @link http://www.spip.net/1971 La gestion des dates
311 * @see balise_DATE_REDAC_dist()
312 *
313 * @param Champ $p
314 * Pile au niveau de la balise.
315 * @return Champ
316 * Pile completée du code PHP d'exécution de la balise
317 */
318 function balise_DATE_MODIF_dist($p) {
319 $p->code = champ_sql('date_modif', $p);
320 $p->interdire_scripts = false;
321
322 return $p;
323 }
324
325 /**
326 * Compile la balise `#DATE_NOUVEAUTES` indiquant la date de dernier envoi
327 * du mail de nouveautés
328 *
329 * @balise
330 * @link http://www.spip.net/4337 Balise DATE_NOUVEAUTES
331 * @link http://www.spip.net/1971 La gestion des dates
332 * @see balise_DATE_REDAC_dist()
333 *
334 * @param Champ $p
335 * Pile au niveau de la balise.
336 * @return Champ
337 * Pile completée du code PHP d'exécution de la balise
338 */
339 function balise_DATE_NOUVEAUTES_dist($p) {
340 $p->code = "((\$GLOBALS['meta']['quoi_de_neuf'] == 'oui'
341 AND isset(\$GLOBALS['meta']['dernier_envoi_neuf'])) ?
342 \$GLOBALS['meta']['dernier_envoi_neuf'] :
343 \"'0000-00-00'\")";
344 $p->interdire_scripts = false;
345
346 return $p;
347 }
348
349
350 /**
351 * Compile la balise `#DOSSIER_SQUELETTE` retournant le chemin vers le
352 * répertoire de squelettes actuellement utilisé
353 *
354 * @balise
355 * @deprecated Utiliser `#CHEMIN`
356 * @link http://www.spip.net/4627
357 * @see balise_CHEMIN_dist()
358 *
359 * @param Champ $p
360 * Pile au niveau de la balise.
361 * @return Champ
362 * Pile completée du code PHP d'exécution de la balise
363 */
364 function balise_DOSSIER_SQUELETTE_dist($p) {
365 $code = substr(addslashes(dirname($p->descr['sourcefile'])), strlen(_DIR_RACINE));
366 $p->code = "_DIR_RACINE . '$code'" .
367 $p->interdire_scripts = false;
368
369 return $p;
370 }
371
372 /**
373 * Compile la balise `#SQUELETTE` retournant le chemin du squelette courant
374 *
375 * @balise
376 * @link http://www.spip.net/4027
377 *
378 * @param Champ $p
379 * Pile au niveau de la balise.
380 * @return Champ
381 * Pile completée du code PHP d'exécution de la balise
382 */
383 function balise_SQUELETTE_dist($p) {
384 $code = addslashes($p->descr['sourcefile']);
385 $p->code = "'$code'" .
386 $p->interdire_scripts = false;
387
388 return $p;
389 }
390
391 /**
392 * Compile la balise `#SPIP_VERSION` qui affiche la version de SPIP
393 *
394 * @balise
395 * @see spip_version()
396 * @example
397 * ```
398 * <meta name="generator" content="SPIP[ (#SPIP_VERSION)]" />
399 * ```
400 *
401 * @param Champ $p
402 * Pile au niveau de la balise.
403 * @return Champ
404 * Pile completée du code PHP d'exécution de la balise
405 */
406 function balise_SPIP_VERSION_dist($p) {
407 $p->code = "spip_version()";
408 $p->interdire_scripts = false;
409
410 return $p;
411 }
412
413
414 /**
415 * Compile la balise `#NOM_SITE` qui affiche le nom du site.
416 *
417 * Affiche le nom du site ou sinon l'URL ou le titre de l'objet
418 * Utiliser `#NOM_SITE*` pour avoir le nom du site ou rien.
419 *
420 * Cette balise interroge les colonnes `nom_site` ou `url_site`
421 * dans la boucle la plus proche.
422 *
423 * @balise
424 * @see calculer_url()
425 * @example
426 * ```
427 * <a href="#URL_SITE">#NOM_SITE</a>
428 * ```
429 *
430 * @param Champ $p
431 * Pile au niveau de la balise
432 * @return Champ
433 * Pile complétée par le code à générer
434 **/
435 function balise_NOM_SITE_dist($p) {
436 if (!$p->etoile) {
437 $p->code = "supprimer_numero(calculer_url(" .
438 champ_sql('url_site', $p) . "," .
439 champ_sql('nom_site', $p) .
440 ", 'titre', \$connect, false))";
441 } else {
442 $p->code = champ_sql('nom_site', $p);
443 }
444
445 $p->interdire_scripts = true;
446
447 return $p;
448 }
449
450
451 /**
452 * Compile la balise `#NOTE` qui affiche les notes de bas de page
453 *
454 * @balise
455 * @link http://www.spip.net/3964
456 * @see calculer_notes()
457 *
458 * @param Champ $p
459 * Pile au niveau de la balise
460 * @return Champ
461 * Pile complétée par le code à générer
462 **/
463 function balise_NOTES_dist($p) {
464 // Recuperer les notes
465 $p->code = 'calculer_notes()';
466
467 #$p->interdire_scripts = true;
468 return $p;
469 }
470
471
472 /**
473 * Compile la balise `#RECHERCHE` qui retourne le terme de recherche demandé
474 *
475 * Retourne un terme demandé en recherche, en le prenant dans _request()
476 * sous la clé `recherche`.
477 *
478 * @balise
479 * @example
480 * ```
481 * <h3>Recherche de : #RECHERCHE</h3>
482 * ```
483 *
484 * @param Champ $p
485 * Pile au niveau de la balise
486 * @return Champ
487 * Pile complétée par le code à générer
488 **/
489 function balise_RECHERCHE_dist($p) {
490 $p->code = 'entites_html(_request("recherche"))';
491 $p->interdire_scripts = false;
492
493 return $p;
494 }
495
496
497 /**
498 * Compile la balise `#COMPTEUR_BOUCLE` qui retourne le numéro de l’itération
499 * actuelle de la boucle
500 *
501 * @balise
502 * @link http://www.spip.net/4333
503 * @see balise_TOTAL_BOUCLE_dist()
504 *
505 * @param Champ $p
506 * Pile au niveau de la balise
507 * @return Champ
508 * Pile complétée par le code à générer
509 **/
510 function balise_COMPTEUR_BOUCLE_dist($p) {
511 $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
512 if ($b === '') {
513 $msg = array(
514 'zbug_champ_hors_boucle',
515 array('champ' => '#COMPTEUR_BOUCLE')
516 );
517 erreur_squelette($msg, $p);
518 } else {
519 $p->code = "\$Numrows['$b']['compteur_boucle']";
520 $p->boucles[$b]->cptrows = true;
521 $p->interdire_scripts = false;
522
523 return $p;
524 }
525 }
526
527 /**
528 * Compile la balise `#TOTAL_BOUCLE` qui retourne le nombre de résultats
529 * affichés par la boucle
530 *
531 * @balise
532 * @link http://www.spip.net/4334
533 * @see balise_COMPTEUR_BOUCLE_dist()
534 * @see balise_GRAND_TOTAL_dist()
535 *
536 * @param Champ $p
537 * Pile au niveau de la balise
538 * @return Champ
539 * Pile complétée par le code à générer
540 **/
541 function balise_TOTAL_BOUCLE_dist($p) {
542 $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
543 if ($b === '' || !isset($p->boucles[$b])) {
544 $msg = array(
545 'zbug_champ_hors_boucle',
546 array('champ' => "#$b" . 'TOTAL_BOUCLE')
547 );
548 erreur_squelette($msg, $p);
549 } else {
550 $p->code = "\$Numrows['$b']['total']";
551 $p->boucles[$b]->numrows = true;
552 $p->interdire_scripts = false;
553 }
554
555 return $p;
556 }
557
558
559 /**
560 * Compile la balise `#POINTS` qui affiche la pertinence des résultats
561 *
562 * Retourne le calcul `points` réalisé par le critère `recherche`.
563 * Cette balise nécessite donc la présence de ce critère.
564 *
565 * @balise
566 * @link http://www.spip.net/903 Boucles et balises de recherche
567 * @see critere_recherche_dist()
568 *
569 * @param Champ $p
570 * Pile au niveau de la balise
571 * @return Champ
572 * Pile complétée par le code à générer
573 **/
574 function balise_POINTS_dist($p) {
575 return rindex_pile($p, 'points', 'recherche');
576 }
577
578
579 /**
580 * Compile la balise `#POPULARITE_ABSOLUE` qui affiche la popularité absolue
581 *
582 * Cela correspond à la popularité quotidienne de l'article
583 *
584 * @balise
585 * @link http://www.spip.net/1846 La popularité
586 * @see balise_POPULARITE_dist()
587 * @see balise_POPULARITE_MAX_dist()
588 * @see balise_POPULARITE_SITE_dist()
589 *
590 * @param Champ $p
591 * Pile au niveau de la balise
592 * @return Champ
593 * Pile complétée par le code à générer
594 **/
595 function balise_POPULARITE_ABSOLUE_dist($p) {
596 $p->code = 'ceil(' .
597 champ_sql('popularite', $p) .
598 ')';
599 $p->interdire_scripts = false;
600
601 return $p;
602 }
603
604 /**
605 * Compile la balise `#POPULARITE_SITE` qui affiche la popularité du site
606 *
607 * La popularité du site est la somme de toutes les popularités absolues.
608 *
609 * @balise
610 * @link http://www.spip.net/1846 La popularité
611 * @see balise_POPULARITE_ABSOLUE_dist()
612 * @see balise_POPULARITE_dist()
613 * @see balise_POPULARITE_MAX_dist()
614 *
615 * @param Champ $p
616 * Pile au niveau de la balise
617 * @return Champ
618 * Pile complétée par le code à générer
619 **/
620 function balise_POPULARITE_SITE_dist($p) {
621 $p->code = 'ceil($GLOBALS["meta"][\'popularite_total\'])';
622 $p->interdire_scripts = false;
623
624 return $p;
625 }
626
627 /**
628 * Compile la balise `#POPULARITE_MAX` qui affiche la popularité maximum
629 * parmis les popularités des articles
630 *
631 * Cela correspond à la popularité quotidienne de l'article
632 *
633 * @balise
634 * @link http://www.spip.net/1846 La popularité
635 * @see balise_POPULARITE_ABSOLUE_dist()
636 * @see balise_POPULARITE_dist()
637 * @see balise_POPULARITE_SITE_dist()
638 *
639 * @param Champ $p
640 * Pile au niveau de la balise
641 * @return Champ
642 * Pile complétée par le code à générer
643 **/
644 function balise_POPULARITE_MAX_dist($p) {
645 $p->code = 'ceil($GLOBALS["meta"][\'popularite_max\'])';
646 $p->interdire_scripts = false;
647
648 return $p;
649 }
650
651
652 /**
653 * Compile la balise `#VALEUR` retournant le champ `valeur`
654 *
655 * Utile dans une boucle DATA pour retourner une valeur.
656 *
657 * @balise
658 * @link http://www.spip.net/5546 #CLE et #VALEUR
659 * @see table_valeur()
660 * @example
661 * ```
662 * #VALEUR renvoie le champ valeur
663 * #VALEUR{x} renvoie #VALEUR|table_valeur{x},
664 * équivalent à #X (si X n'est pas une balise spécifique à SPIP)
665 * #VALEUR{a/b} renvoie #VALEUR|table_valeur{a/b}
666 * ```
667 *
668 * @param Champ $p
669 * Pile au niveau de la balise
670 * @return Champ
671 * Pile complétée par le code à générer
672 **/
673 function balise_VALEUR_dist($p) {
674 $b = $p->nom_boucle ? $p->nom_boucle : $p->id_boucle;
675 $p->code = index_pile($p->id_boucle, 'valeur', $p->boucles, $b);;
676 if (($v = interprete_argument_balise(1, $p)) !== null) {
677 $p->code = 'table_valeur(' . $p->code . ', ' . $v . ')';
678 }
679 $p->interdire_scripts = true;
680
681 return $p;
682 }
683
684 /**
685 * Compile la balise `#EXPOSE` qui met en évidence l'élément sur lequel
686 * la page se trouve
687 *
688 * Expose dans une boucle l'élément de la page sur laquelle on se trouve,
689 * en retournant `on` si l'élément correspond à la page, une chaîne vide sinon.
690 *
691 * On peut passer les paramètres à faire retourner par la balise.
692 *
693 * @example
694 * ```
695 * <a href="#URL_ARTICLE"[ class="(#EXPOSE)"]>
696 * <a href="#URL_ARTICLE"[ class="(#EXPOSE{actif})"]>
697 * <a href="#URL_ARTICLE"[ class="(#EXPOSE{on,off})"]>
698 * ```
699 *
700 * @balise
701 * @link http://www.spip.net/2319 Exposer un article
702 * @uses calculer_balise_expose()
703 *
704 * @param Champ $p
705 * Pile au niveau de la balise
706 * @return Champ
707 * Pile complétée par le code à générer
708 **/
709 function balise_EXPOSE_dist($p) {
710 $on = "'on'";
711 $off = "''";
712 if (($v = interprete_argument_balise(1, $p)) !== null) {
713 $on = $v;
714 if (($v = interprete_argument_balise(2, $p)) !== null) {
715 $off = $v;
716 }
717
718 }
719
720 return calculer_balise_expose($p, $on, $off);
721 }
722
723 /**
724 * Calcul de la balise expose
725 *
726 * @see calcul_exposer()
727 *
728 * @param Champ $p
729 * Pile au niveau de la balise
730 * @param string $on
731 * Texte à afficher si l'élément est exposé (code à écrire tel que "'on'")
732 * @param string $off
733 * Texte à afficher si l'élément n'est pas exposé (code à écrire tel que "''")
734 * @return Champ
735 * Pile complétée par le code à générer
736 **/
737 function calculer_balise_expose($p, $on, $off) {
738 $b = $p->nom_boucle ? $p->nom_boucle : $p->id_boucle;
739 if (empty($p->boucles[$b]->primary)) {
740 $msg = array('zbug_champ_hors_boucle', array('champ' => '#EXPOSER'));
741 erreur_squelette($msg, $p);
742 } else {
743
744 $key = $p->boucles[$b]->primary;
745 $type = $p->boucles[$p->id_boucle]->primary;
746 $desc = $p->boucles[$b]->show;
747 $connect = sql_quote($p->boucles[$b]->sql_serveur);
748
749 // Ne pas utiliser champ_sql, on jongle avec le nom boucle explicite
750 $c = index_pile($p->id_boucle, $type, $p->boucles);
751
752 if (isset($desc['field']['id_parent'])) {
753 $parent = 0; // pour if (!$parent) dans calculer_expose
754 } elseif (isset($desc['field']['id_rubrique'])) {
755 $parent = index_pile($p->id_boucle, 'id_rubrique', $p->boucles, $b);
756 } elseif (isset($desc['field']['id_groupe'])) {
757 $parent = index_pile($p->id_boucle, 'id_groupe', $p->boucles, $b);
758 } else {
759 $parent = "''";
760 }
761
762 $p->code = "(calcul_exposer($c, '$type', \$Pile[0], $parent, '$key', $connect) ? $on : $off)";
763 }
764
765 $p->interdire_scripts = false;
766
767 return $p;
768 }
769
770
771 /**
772 * Compile la balise `#DEBUT_SURLIGNE` qui permettait le surlignage
773 * des mots d'une recherche
774 *
775 * @note
776 * Cette balise n'a plus d'effet depuis r9343
777 *
778 * @balise
779 * @see balise_FIN_SURLIGNE_dist()
780 * @deprecated Utiliser les classes CSS `surlignable` ou `pas_surlignable`
781 *
782 * @param Champ $p
783 * Pile au niveau de la balise
784 * @return Champ
785 * Pile complétée par le code à générer
786 **/
787 function balise_DEBUT_SURLIGNE_dist($p) {
788 include_spip('inc/surligne');
789 $p->code = "'<!-- " . MARQUEUR_SURLIGNE . " -->'";
790
791 return $p;
792 }
793
794
795 /**
796 * Compile la balise `#FIN_SURLIGNE` qui arrêtait le surlignage
797 * des mots d'une recherche
798 *
799 * @note
800 * Cette balise n'a plus d'effet depuis r9343
801 *
802 * @balise
803 * @see balise_DEBUT_SURLIGNE_dist()
804 * @deprecated Utiliser les classes CSS `surlignable` ou `pas_surlignable`
805 *
806 * @param Champ $p
807 * Pile au niveau de la balise
808 * @return Champ
809 * Pile complétée par le code à générer
810 **/
811 function balise_FIN_SURLIGNE_dist($p) {
812 include_spip('inc/surligne');
813 $p->code = "'<!-- " . MARQUEUR_FSURLIGNE . "-->'";
814
815 return $p;
816 }
817
818
819 /**
820 * Compile la balise `#INTRODUCTION`
821 *
822 * Retourne une introduction d'un objet éditorial, c'est à dire les 600
823 * premiers caractères environ du champ 'texte' de l'objet ou le contenu
824 * indiqué entre `<intro>` et `</intro>` de ce même champ.
825 *
826 * Pour les articles, l'introduction utilisée est celle du champ `descriptif`
827 * s'il est renseigné, sinon il est pris dans les champs `chapo` et `texte` et
828 * est par défaut limité à 500 caractères.
829 *
830 * Pour les rubriques, l'introduction utilisée est celle du champ `descriptif`
831 * s'il est renseigné, sinon du champ texte.
832 *
833 * La balise accèpte 1 paramètre indiquant la longueur en nombre de caractères
834 * de l'introduction.
835 *
836 * @see filtre_introduction_dist()
837 * @example
838 * ```
839 * #INTRODUCTION
840 * #INTRODUCTION{300}
841 * ```
842 *
843 * @balise
844 * @link http://www.spip.net/@introduction
845 *
846 * @param Champ $p
847 * Pile au niveau de la balise
848 * @return Champ
849 * Pile complétée par le code à générer
850 **/
851 function balise_INTRODUCTION_dist($p) {
852
853 $type = $p->type_requete;
854
855 $_texte = champ_sql('texte', $p);
856 $_descriptif = ($type == 'articles' or $type == 'rubriques') ? champ_sql('descriptif', $p) : "''";
857
858 if ($type == 'articles') {
859 $_chapo = champ_sql('chapo', $p);
860 $_texte = "(strlen($_descriptif))
861 ? ''
862 : $_chapo . \"\\n\\n\" . $_texte";
863 }
864
865 // longueur en parametre, ou valeur par defaut
866 $longueur_defaut = objet_info($type, 'introduction_longueur');
867 if (!$longueur_defaut) {
868 $longueur_defaut = 600;
869 }
870
871 $_suite = 'null';
872 $_longueur = $longueur_defaut;
873 if (($v = interprete_argument_balise(1, $p)) !== null) {
874 $_longueur = 'is_numeric(' . $v . ')?intval(' . $v . '):' . $longueur_defaut;
875 $_suite = '!is_numeric(' . $v . ')?' . $v . ':null';
876 }
877 if (($v2 = interprete_argument_balise(2, $p)) !== null) {
878 $_suite = $v2;
879 }
880
881 $f = chercher_filtre('introduction');
882 $p->code = "$f($_descriptif, $_texte, $_longueur, \$connect, $_suite)";
883
884 #$p->interdire_scripts = true;
885 $p->etoile = '*'; // propre est deja fait dans le calcul de l'intro
886 return $p;
887 }
888
889
890 /**
891 * Compile la balise `#LANG` qui affiche la langue de l'objet (ou d'une boucle supérieure),
892 * et à defaut la langue courante
893 *
894 * La langue courante est celle du site ou celle qui a été passée dans l'URL par le visiteur.
895 * L'étoile `#LANG*` n'affiche rien si aucune langue n'est trouvée dans le SQL ou le contexte.
896 *
897 * @balise
898 * @link http://www.spip.net/3864
899 *
900 * @param Champ $p
901 * Pile au niveau de la balise
902 * @return Champ
903 * Pile complétée par le code à générer
904 **/
905 function balise_LANG_dist($p) {
906 $_lang = champ_sql('lang', $p);
907 if (!$p->etoile) {
908 $p->code = "spip_htmlentities($_lang ? $_lang : \$GLOBALS['spip_lang'])";
909 } else {
910 $p->code = "spip_htmlentities($_lang)";
911 }
912 $p->interdire_scripts = false;
913
914 return $p;
915 }
916
917 /**
918 * Compile la balise `#LESAUTEURS` chargée d'afficher la liste des auteurs d'un objet
919 *
920 * - Soit le champ `lesauteurs` existe dans la table et à ce moment là,
921 * la balise retourne son contenu,
922 * - soit la balise appelle le modele `lesauteurs.html` en lui passant
923 * le couple `objet` et `id_objet` dans son environnement.
924 *
925 * @balise
926 * @link http://www.spip.net/3966 Description de la balise
927 * @link http://www.spip.net/902 Description de la boucle ARTICLES
928 * @link http://www.spip.net/911 Description de la boucle SYNDIC_ARTICLES
929 *
930 * @param Champ $p
931 * Pile au niveau de la balise
932 * @return Champ
933 * Pile complétée par le code à générer
934 */
935 function balise_LESAUTEURS_dist($p) {
936 // Cherche le champ 'lesauteurs' dans la pile
937 $_lesauteurs = champ_sql('lesauteurs', $p, false);
938
939 // Si le champ n'existe pas (cas de spip_articles), on applique
940 // le modele lesauteurs.html en passant id_article dans le contexte;
941 // dans le cas contraire on prend le champ 'lesauteurs'
942 // (cf extension sites/)
943 if ($_lesauteurs
944 and $_lesauteurs != '@$Pile[0][\'lesauteurs\']'
945 ) {
946 $p->code = "safehtml($_lesauteurs)";
947 // $p->interdire_scripts = true;
948 } else {
949 if (!$p->id_boucle) {
950 $connect = '';
951 $objet = 'article';
952 $id_table_objet = 'id_article';
953 } else {
954 $b = $p->nom_boucle ? $p->nom_boucle : $p->id_boucle;
955 $connect = $p->boucles[$b]->sql_serveur;
956 $type_boucle = $p->boucles[$b]->type_requete;
957 $objet = objet_type($type_boucle);
958 $id_table_objet = id_table_objet($type_boucle);
959 }
960 $c = memoriser_contexte_compil($p);
961
962 $p->code = sprintf(CODE_RECUPERER_FOND, "'modeles/lesauteurs'",
963 "array('objet'=>'" . $objet .
964 "','id_objet' => " . champ_sql($id_table_objet, $p) .
965 ",'$id_table_objet' => " . champ_sql($id_table_objet, $p) .
966 ($objet == 'article' ? "" : ",'id_article' => " . champ_sql('id_article', $p)) .
967 ")",
968 "'trim'=>true, 'compil'=>array($c)",
969 _q($connect));
970 $p->interdire_scripts = false; // securite apposee par recuperer_fond()
971 }
972
973 return $p;
974 }
975
976
977 /**
978 * Compile la balise `#RANG` chargée d'afficher le numéro de l'objet
979 *
980 * Affiche le « numero de l'objet ». Soit `1` quand on a un titre `1. Premier article`.
981 *
982 * Ceci est transitoire afin de préparer une migration vers un vrai système de
983 * tri des articles dans une rubrique (et plus si affinités).
984 * La balise permet d'extraire le numero masqué par le filtre `supprimer_numero`.
985 *
986 * La balise recupère le champ declaré dans la définition `table_titre`
987 * de l'objet, ou à defaut du champ `titre`
988 *
989 * Si un champ `rang` existe, il est pris en priorité.
990 *
991 * @balise
992 * @link http://www.spip.net/5495
993 *
994 * @param Champ $p
995 * Pile au niveau de la balise
996 * @return Champ
997 * Pile complétée par le code à générer
998 */
999 function balise_RANG_dist($p) {
1000 $b = index_boucle($p);
1001 if ($b === '') {
1002 $msg = array(
1003 'zbug_champ_hors_boucle',
1004 array('champ' => '#RANG')
1005 );
1006 erreur_squelette($msg, $p);
1007 } else {
1008 // chercher d'abord un champ sql rang (mais pas dans le env : defaut '' si on trouve pas de champ sql)
1009 // dans la boucle immediatement englobante uniquement
1010 // sinon on compose le champ calcule
1011 $_rang = champ_sql('rang', $p, '', false);
1012
1013 // si pas trouve de champ sql rang :
1014 if (!$_rang or $_rang == "''") {
1015 $boucle = &$p->boucles[$b];
1016 $trouver_table = charger_fonction('trouver_table', 'base');
1017 $desc = $trouver_table($boucle->id_table);
1018 $_titre = ''; # où extraire le numero ?
1019
1020 if (isset($desc['titre'])) {
1021 $t = $desc['titre'];
1022 if (
1023 // Soit on trouve avec la déclaration de la lang AVANT
1024 preg_match(';(?:lang\s*,)\s*(.*?titre)\s*(,|$);', $t, $m)
1025 // Soit on prend depuis le début
1026 or preg_match(';^(.*?titre)\s*(,|$);', $t, $m)
1027 ) {
1028 $m = preg_replace(',as\s+titre$,i', '', $m[1]);
1029 $m = trim($m);
1030 if ($m != "''") {
1031 if (!preg_match(",\W,", $m)) {
1032 $m = $boucle->id_table . ".$m";
1033 }
1034
1035 $m .= " AS titre_rang";
1036
1037 $boucle->select[] = $m;
1038 $_titre = '$Pile[$SP][\'titre_rang\']';
1039 }
1040 }
1041 }
1042
1043 // si on n'a rien trouvé, on utilise le champ titre classique
1044 if (!$_titre) {
1045 $_titre = champ_sql('titre', $p);
1046 }
1047
1048 $_rang = "recuperer_numero($_titre)";
1049 }
1050
1051 $p->code = $_rang;
1052 $p->interdire_scripts = false;
1053 }
1054
1055 return $p;
1056 }
1057
1058
1059 /**
1060 * Compile la balise `#POPULARITE` qui affiche la popularité relative.
1061 *
1062 * C'est à dire le pourcentage de la fréquentation de l'article
1063 * (la popularité absolue) par rapport à la popularité maximum.
1064 *
1065 * @balise
1066 * @link http://www.spip.net/1846 La popularité
1067 * @see balise_POPULARITE_ABSOLUE_dist()
1068 * @see balise_POPULARITE_MAX_dist()
1069 * @see balise_POPULARITE_SITE_dist()
1070 *
1071 * @param Champ $p
1072 * Pile au niveau de la balise
1073 * @return Champ
1074 * Pile complétée par le code à générer
1075 **/
1076 function balise_POPULARITE_dist($p) {
1077 $_popularite = champ_sql('popularite', $p);
1078 $p->code = "(ceil(min(100, 100 * $_popularite
1079 / max(1 , 0 + \$GLOBALS['meta']['popularite_max']))))";
1080 $p->interdire_scripts = false;
1081
1082 return $p;
1083 }
1084
1085 /**
1086 * Code de compilation pour la balise `#PAGINATION`
1087 *
1088 * Le code produit est trompeur, car les modèles ne fournissent pas Pile[0].
1089 * On produit un appel à `_request` si on ne l'a pas, mais c'est inexact:
1090 * l'absence peut-être due à une faute de frappe dans le contexte inclus.
1091 */
1092 define('CODE_PAGINATION',
1093 '%s($Numrows["%s"]["grand_total"],
1094 %s,
1095 isset($Pile[0][%4$s])?$Pile[0][%4$s]:intval(_request(%4$s)),
1096 %5$s, %6$s, %7$s, %8$s, array(%9$s))');
1097
1098 /**
1099 * Compile la balise `#PAGINATION` chargée d'afficher une pagination
1100 *
1101 * Elle charge le modèle `pagination.html` (par défaut), mais un paramètre
1102 * permet d'indiquer d'autres modèles. `#PAGINATION{nom}` utilisera le
1103 * modèle `pagination_nom.html`.
1104 *
1105 * Cette balise nécessite le critère `pagination` sur la boucle où elle
1106 * est utilisée.
1107 *
1108 * @balise
1109 * @link http://www.spip.net/3367 Le système de pagination
1110 * @see filtre_pagination_dist()
1111 * @see critere_pagination_dist()
1112 * @see balise_ANCRE_PAGINATION_dist()
1113 * @example
1114 * ```
1115 * [<p class="pagination">(#PAGINATION{prive})</p>]
1116 * ```
1117 *
1118 * @param Champ $p
1119 * Pile au niveau de la balise
1120 * @param string $liste
1121 * Afficher ou non les liens de pagination (variable de type `string`
1122 * car code à faire écrire au compilateur) :
1123 * - `true` pour les afficher
1124 * - `false` pour afficher uniquement l'ancre.
1125 * @return Champ
1126 * Pile complétée par le code à générer
1127 */
1128 function balise_PAGINATION_dist($p, $liste = 'true') {
1129 $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
1130
1131 // s'il n'y a pas de nom de boucle, on ne peut pas paginer
1132 if ($b === '') {
1133 $msg = array(
1134 'zbug_champ_hors_boucle',
1135 array('champ' => $liste ? 'PAGINATION' : 'ANCRE_PAGINATION')
1136 );
1137 erreur_squelette($msg, $p);
1138
1139 return $p;
1140 }
1141
1142 // s'il n'y a pas de mode_partie, c'est qu'on se trouve
1143 // dans un boucle recursive ou qu'on a oublie le critere {pagination}
1144 if (!$p->boucles[$b]->mode_partie) {
1145 if (!$p->boucles[$b]->table_optionnelle) {
1146 $msg = array(
1147 'zbug_pagination_sans_critere',
1148 array('champ' => '#PAGINATION')
1149 );
1150 erreur_squelette($msg, $p);
1151 }
1152
1153 return $p;
1154 }
1155
1156 // a priori true
1157 // si false, le compilo va bloquer sur des syntaxes avec un filtre sans argument qui suit la balise
1158 // si true, les arguments simples (sans truc=chose) vont degager
1159 $_contexte = argumenter_inclure($p->param, true, $p, $p->boucles, $p->id_boucle, false, false);
1160 if (count($_contexte)) {
1161 list($key, $val) = each($_contexte);
1162 if (is_numeric($key)) {
1163 array_shift($_contexte);
1164 $__modele = interprete_argument_balise(1, $p);
1165 }
1166 }
1167
1168 if (count($_contexte)) {
1169 $code_contexte = implode(',', $_contexte);
1170 } else {
1171 $code_contexte = '';
1172 }
1173
1174 $connect = $p->boucles[$b]->sql_serveur;
1175 $pas = $p->boucles[$b]->total_parties;
1176 $f_pagination = chercher_filtre('pagination');
1177 $type = $p->boucles[$b]->modificateur['debut_nom'];
1178 $modif = ($type[0] !== "'") ? "'debut'.$type"
1179 : ("'debut" . substr($type, 1));
1180
1181 $p->code = sprintf(CODE_PAGINATION, $f_pagination, $b, $type, $modif, $pas, $liste,
1182 ((isset($__modele) and $__modele) ? $__modele : "''"), _q($connect), $code_contexte);
1183
1184 $p->boucles[$b]->numrows = true;
1185 $p->interdire_scripts = false;
1186
1187 return $p;
1188 }
1189
1190
1191 /**
1192 * Compile la balise `#ANCRE_PAGINATION` chargée d'afficher l'ancre
1193 * de la pagination
1194 *
1195 * Cette ancre peut ainsi être placée au-dessus la liste des éléments
1196 * de la boucle alors qu'on mettra les liens de pagination en-dessous de
1197 * cette liste paginée.
1198 *
1199 * Cette balise nécessite le critère `pagination` sur la boucle où elle
1200 * est utilisée.
1201 *
1202 * @balise
1203 * @link http://www.spip.net/3367 Le système de pagination
1204 * @link http://www.spip.net/4328 Balise ANCRE_PAGINATION
1205 * @see critere_pagination_dist()
1206 * @see balise_PAGINATION_dist()
1207 *
1208 * @param Champ $p
1209 * Pile au niveau de la balise
1210 * @return Champ
1211 * Pile complétée par le code à générer
1212 **/
1213 function balise_ANCRE_PAGINATION_dist($p) {
1214 if ($f = charger_fonction('PAGINATION', 'balise', true)) {
1215 return $f($p, $liste = 'false');
1216 } else {
1217 return null;
1218 } // ou une erreur ?
1219 }
1220
1221
1222 /**
1223 * Compile la balise `#GRAND_TOTAL` qui retourne le nombre total de résultats
1224 * d'une boucle
1225 *
1226 * Cette balise set équivalente à `#TOTAL_BOUCLE` sauf pour les boucles paginées.
1227 * Dans ce cas elle indique le nombre total d'éléments répondant aux critères
1228 * hors pagination.
1229 *
1230 * @balise
1231 * @see balise_GRAND_TOTAL_dist()
1232 *
1233 * @param Champ $p
1234 * Pile au niveau de la balise
1235 * @return Champ
1236 * Pile complétée par le code à générer
1237 **/
1238 function balise_GRAND_TOTAL_dist($p) {
1239 $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
1240 if ($b === '' || !isset($p->boucles[$b])) {
1241 $msg = array(
1242 'zbug_champ_hors_boucle',
1243 array('champ' => "#$b" . 'TOTAL_BOUCLE')
1244 );
1245 erreur_squelette($msg, $p);
1246 } else {
1247 $p->code = "(isset(\$Numrows['$b']['grand_total'])
1248 ? \$Numrows['$b']['grand_total'] : \$Numrows['$b']['total'])";
1249 $p->boucles[$b]->numrows = true;
1250 $p->interdire_scripts = false;
1251 }
1252
1253 return $p;
1254 }
1255
1256
1257 /**
1258 * Compile la balise `#SELF` qui retourne l’URL de la page appelée.
1259 *
1260 * Cette URL est nettoyée des variables propres à l’exécution de SPIP
1261 * tel que `var_mode`.
1262 *
1263 * @note
1264 * Attention dans un `INCLURE()` ou une balise dynamique, on n'a pas le droit de
1265 * mettre en cache `#SELF` car il peut correspondre à une autre page (attaque XSS)
1266 * (Dans ce cas faire <INCLURE{self=#SELF}> pour différencier les caches.)
1267 *
1268 * @balise
1269 * @link http://www.spip.net/4574
1270 * @example
1271 * ```
1272 * <a href="[(#SELF|parametre_url{id_mot,#ID_MOT})]">...
1273 * ```
1274 *
1275 * @param Champ $p
1276 * Pile au niveau de la balise
1277 * @return Champ
1278 * Pile complétée par le code à générer
1279 **/
1280 function balise_SELF_dist($p) {
1281 $p->code = 'self()';
1282 $p->interdire_scripts = false;
1283
1284 return $p;
1285 }
1286
1287
1288 /**
1289 * Compile la balise `#CHEMIN` qui cherche un fichier dans les chemins
1290 * connus de SPIP et retourne son chemin complet depuis la racine
1291 *
1292 * Signature : `#CHEMIN{chemin/vers/fichier.ext}`
1293 *
1294 * Retourne une chaîne vide si le fichier n'est pas trouvé.
1295 *
1296 * @balise
1297 * @link http://www.spip.net/4332
1298 * @see find_in_path() Recherche de chemin
1299 * @example
1300 * ```
1301 * [<script type="text/javascript" src="(#CHEMIN{javascript/jquery.flot.js})"></script>]
1302 * [<link rel="stylesheet" href="(#CHEMIN{css/perso.css}|direction_css)" type="text/css" />]
1303 * ```
1304 *
1305 * @param Champ $p
1306 * Pile au niveau de la balise
1307 * @return Champ
1308 * Pile complétée par le code à générer
1309 **/
1310 function balise_CHEMIN_dist($p) {
1311 $arg = interprete_argument_balise(1, $p);
1312 if (!$arg) {
1313 $msg = array('zbug_balise_sans_argument', array('balise' => ' CHEMIN'));
1314 erreur_squelette($msg, $p);
1315 } else {
1316 $p->code = 'find_in_path(' . $arg . ')';
1317 }
1318
1319 $p->interdire_scripts = false;
1320
1321 return $p;
1322 }
1323
1324 /**
1325 * Compile la balise `#CHEMIN_IMAGE` qui cherche une image dans le thème
1326 * de l'espace privé utilisé par SPIP et retourne son chemin complet depuis
1327 * la racine
1328 *
1329 * Signature : `#CHEMIN_IMAGE{image.png}`
1330 *
1331 * Retourne une chaîne vide si le fichier n'est pas trouvé.
1332 *
1333 * @balise
1334 * @see chemin_image()
1335 * @example
1336 * ```
1337 * #CHEMIN_IMAGE{article-24.png}
1338 * ```
1339 *
1340 * @param Champ $p
1341 * Pile au niveau de la balise
1342 * @return Champ
1343 * Pile complétée par le code à générer
1344 **/
1345 function balise_CHEMIN_IMAGE_dist($p) {
1346 $arg = interprete_argument_balise(1, $p);
1347 if (!$arg) {
1348 $msg = array('zbug_balise_sans_argument', array('balise' => ' CHEMIN_IMAGE'));
1349 erreur_squelette($msg, $p);
1350 } else {
1351 $p->code = 'chemin_image(' . $arg . ')';
1352 }
1353
1354 #$p->interdire_scripts = true;
1355 return $p;
1356 }
1357
1358
1359 /**
1360 * Compile la balise `#ENV` qui permet de récupérer le contexte d'environnement
1361 * transmis à un squelette.
1362 *
1363 * La syntaxe `#ENV{toto, valeur par defaut}`
1364 * renverra `valeur par defaut` si `$toto` est vide.
1365 *
1366 * La recherche de la clé s'appuyant sur la fonction `table_valeur`
1367 * il est possible de demander un sous élément d'un tableau :
1368 * `#ENV{toto/sous/element, valeur par defaut}` retournera l'équivalent de
1369 * `#ENV{toto}|table_valeur{sous/element}` c'est-à-dire en quelque sorte
1370 * `$env['toto']['sous']['element']` s'il existe, sinon la valeur par défaut.
1371 *
1372 * Si le tableau est vide on renvoie `''` (utile pour `#SESSION`)
1373 *
1374 * Enfin, la balise utilisée seule `#ENV` retourne le tableau complet
1375 * de l'environnement. À noter que ce tableau est retourné sérialisé.
1376 *
1377 * En standard est appliqué le filtre `entites_html`, mais si l'étoile est
1378 * utilisée pour désactiver les filtres par défaut, par exemple avec
1379 * `[(#ENV*{toto})]` , il *faut* s'assurer de la sécurité
1380 * anti-javascript, par exemple en filtrant avec `safehtml` : `[(#ENV*{toto}|safehtml)]`
1381 *
1382 *
1383 * @param Champ $p
1384 * Pile ; arbre de syntaxe abstrait positionné au niveau de la balise.
1385 * @param array $src
1386 * Tableau dans lequel chercher la clé demandée en paramètre de la balise.
1387 * Par defaut prend dans le contexte du squelette.
1388 * @return Champ
1389 * Pile completée du code PHP d'exécution de la balise
1390 **/
1391 function balise_ENV_dist($p, $src = null) {
1392
1393 // cle du tableau desiree
1394 $_nom = interprete_argument_balise(1, $p);
1395 // valeur par defaut
1396 $_sinon = interprete_argument_balise(2, $p);
1397
1398 // $src est un tableau de donnees sources eventuellement transmis
1399 // en absence, on utilise l'environnement du squelette $Pile[0]
1400
1401 if (!$_nom) {
1402 // cas de #ENV sans argument : on retourne le serialize() du tableau
1403 // une belle fonction [(#ENV|affiche_env)] serait pratique
1404 if ($src) {
1405 $p->code = '(is_array($a = (' . $src . ')) ? serialize($a) : "")';
1406 } else {
1407 $p->code = '@serialize($Pile[0])';
1408 }
1409 } else {
1410 if (!$src) {
1411 $src = '@$Pile[0]';
1412 }
1413 if ($_sinon) {
1414 $p->code = "sinon(table_valeur($src, (string)$_nom, null), $_sinon)";
1415 } else {
1416 $p->code = "table_valeur($src, (string)$_nom, null)";
1417 }
1418 }
1419
1420 #$p->interdire_scripts = true;
1421
1422 return $p;
1423 }
1424
1425 /**
1426 * Compile la balise `#CONFIG` qui retourne une valeur de configuration
1427 *
1428 * Cette balise appelle la fonction `lire_config()` pour obtenir les
1429 * configurations du site.
1430 *
1431 * Par exemple `#CONFIG{gerer_trad}` donne 'oui ou 'non' selon le réglage.
1432 *
1433 * Le 3ème argument permet de contrôler la sérialisation du résultat
1434 * (mais ne sert que pour le dépot `meta`) qui doit parfois désérialiser,
1435 * par exemple avec `|in_array{#CONFIG{toto,#ARRAY,1}}`. Ceci n'affecte
1436 * pas d'autres dépots et `|in_array{#CONFIG{toto/,#ARRAY}}` sera
1437 * équivalent.
1438 *
1439 * Òn peut appeler d'autres tables que `spip_meta` avec un
1440 * `#CONFIG{/infos/champ,defaut}` qui lit la valeur de `champ`
1441 * dans une table des meta qui serait `spip_infos`
1442 *
1443 * @balise
1444 * @link http://www.spip.net/4335
1445 *
1446 * @param Champ $p
1447 * Pile au niveau de la balise.
1448 * @return Champ
1449 * Pile completée du code PHP d'exécution de la balise
1450 */
1451 function balise_CONFIG_dist($p) {
1452 if (!$arg = interprete_argument_balise(1, $p)) {
1453 $arg = "''";
1454 }
1455 $_sinon = interprete_argument_balise(2, $p);
1456 $_unserialize = sinon(interprete_argument_balise(3, $p), "false");
1457
1458 $p->code = '(include_spip(\'inc/config\')?lire_config(' . $arg . ',' .
1459 ($_sinon && $_sinon != "''" ? $_sinon : 'null') . ',' . $_unserialize . "):'')";
1460
1461 return $p;
1462 }
1463
1464
1465 /**
1466 * Compile la balise `#CONNECT` qui retourne le nom du connecteur
1467 * de base de données
1468 *
1469 * Retourne le nom du connecteur de base de données utilisé (le nom
1470 * du fichier `config/xx.php` sans l'extension, utilisé pour calculer
1471 * les données du squelette).
1472 *
1473 * Retourne `NULL` si le connecteur utilisé est celui par défaut de SPIP
1474 * (connect.php), sinon retourne son nom.
1475 *
1476 * @balise
1477 *
1478 * @param Champ $p
1479 * Pile au niveau de la balise.
1480 * @return Champ
1481 * Pile completée du code PHP d'exécution de la balise
1482 */
1483 function balise_CONNECT_dist($p) {
1484 $p->code = '($connect ? $connect : NULL)';
1485 $p->interdire_scripts = false;
1486
1487 return $p;
1488 }
1489
1490
1491 /**
1492 * Compile la balise `#SESSION` qui permet d’accéder aux informations
1493 * liées au visiteur authentifié et de différencier automatiquement
1494 * le cache en fonction du visiteur.
1495 *
1496 * Cette balise est un tableau des données du visiteur (nom, email etc).
1497 * Si elle est invoquée, elle lève un drapeau dans le fichier cache, qui
1498 * permet à public/cacher de créer un cache différent par visiteur
1499 *
1500 * @balise
1501 * @link http://www.spip.net/3979
1502 * @see balise_AUTORISER_dist()
1503 * @see balise_SESSION_SET_dist()
1504 * @example
1505 * ```
1506 * #SESSION{nom}
1507 * ```
1508 *
1509 * @param Champ $p
1510 * Pile au niveau de la balise.
1511 * @return Champ
1512 * Pile completée du code PHP d'exécution de la balise
1513 **/
1514 function balise_SESSION_dist($p) {
1515 $p->descr['session'] = true;
1516
1517 $f = function_exists('balise_ENV')
1518 ? 'balise_ENV'
1519 : 'balise_ENV_dist';
1520
1521 $p = $f($p, '$GLOBALS["visiteur_session"]');
1522
1523 return $p;
1524 }
1525
1526
1527 /**
1528 * Compile la balise `#SESSION_SET` qui d’insérer dans la session
1529 * des données supplémentaires
1530 *
1531 * @balise
1532 * @link http://www.spip.net/3984
1533 * @see balise_AUTORISER_dist()
1534 * @see balise_SESSION_SET_dist()
1535 * @example
1536 * ```
1537 * #SESSION_SET{x,y} ajoute x=y dans la session du visiteur
1538 * ```
1539 *
1540 * @param Champ $p
1541 * Pile au niveau de la balise.
1542 * @return Champ
1543 * Pile completée du code PHP d'exécution de la balise
1544 **/
1545 function balise_SESSION_SET_dist($p) {
1546 $_nom = interprete_argument_balise(1, $p);
1547 $_val = interprete_argument_balise(2, $p);
1548 if (!$_nom or !$_val) {
1549 $err_b_s_a = array('zbug_balise_sans_argument', array('balise' => 'SESSION_SET'));
1550 erreur_squelette($err_b_s_a, $p);
1551 } else {
1552 $p->code = '(include_spip("inc/session") AND session_set(' . $_nom . ',' . $_val . '))';
1553 }
1554
1555 $p->interdire_scripts = false;
1556
1557 return $p;
1558 }
1559
1560
1561 /**
1562 * Compile la balise `#EVAL` qui évalue un code PHP
1563 *
1564 * À utiliser avec précautions !
1565 *
1566 * @balise
1567 * @link http://www.spip.net/4587
1568 * @example
1569 * ```
1570 * #EVAL{6+9}
1571 * #EVAL{'date("Y-m-d")'}
1572 * #EVAL{$_SERVER['REQUEST_URI']}
1573 * #EVAL{'str_replace("r","z", "roger")'} (attention les "'" sont interdits)
1574 * ```
1575 *
1576 * @note
1577 * `#EVAL{code}` produit `eval('return code;')`
1578 * mais si le code est une expression sans balise, on se dispense
1579 * de passer par une construction si compliquée, et le code est
1580 * passé tel quel (entre parenthèses, et protégé par interdire_scripts)
1581 *
1582 * @param Champ $p
1583 * Pile au niveau de la balise.
1584 * @return Champ
1585 * Pile completée du code PHP d'exécution de la balise
1586 **/
1587 function balise_EVAL_dist($p) {
1588 $php = interprete_argument_balise(1, $p);
1589 if ($php) {
1590 # optimisation sur les #EVAL{une expression sans #BALISE}
1591 # attention au commentaire "// x signes" qui precede
1592 if (preg_match(",^([[:space:]]*//[^\n]*\n)'([^']+)'$,ms",
1593 $php, $r)) {
1594 $p->code = /* $r[1]. */
1595 '(' . $r[2] . ')';
1596 } else {
1597 $p->code = "eval('return '.$php.';')";
1598 }
1599 } else {
1600 $msg = array('zbug_balise_sans_argument', array('balise' => ' EVAL'));
1601 erreur_squelette($msg, $p);
1602 }
1603
1604 #$p->interdire_scripts = true;
1605
1606 return $p;
1607 }
1608
1609
1610 /**
1611 * Compile la balise `#CHAMP_SQL` qui renvoie la valeur d'un champ SQL
1612 *
1613 * Signature : `#CHAMP_SQL{champ}`
1614 *
1615 * Cette balise permet de récupérer par exemple un champ `notes` dans une table
1616 * SQL externe (impossible avec la balise `#NOTES` qui est une balise calculée).
1617 *
1618 * Ne permet pas de passer une expression comme argument, qui ne peut
1619 * être qu'un texte statique !
1620 *
1621 * @balise
1622 * @link http://www.spip.net/4041
1623 * @see champ_sql()
1624 * @example
1625 * ```
1626 * #CHAMP_SQL{notes}
1627 * ```
1628 *
1629 * @param Champ $p
1630 * Pile au niveau de la balise
1631 * @return Champ
1632 * Pile complétée par le code à générer
1633 **/
1634 function balise_CHAMP_SQL_dist($p) {
1635
1636 if ($p->param
1637 and isset($p->param[0][1][0])
1638 and $champ = ($p->param[0][1][0]->texte)
1639 ) {
1640 $p->code = champ_sql($champ, $p);
1641 } else {
1642 $err_b_s_a = array('zbug_balise_sans_argument', array('balise' => ' CHAMP_SQL'));
1643 erreur_squelette($err_b_s_a, $p);
1644 }
1645
1646 #$p->interdire_scripts = true;
1647 return $p;
1648 }
1649
1650 /**
1651 * Compile la balise `#VAL` qui retourne simplement le premier argument
1652 * qui lui est transmis
1653 *
1654 * Cela permet d'appliquer un filtre à une chaîne de caractère
1655 *
1656 * @balise
1657 * @link http://www.spip.net/4026
1658 * @example
1659 * ```
1660 * #VAL retourne ''
1661 * #VAL{x} retourne 'x'
1662 * #VAL{1,2} renvoie '1' (2 est considéré comme un autre paramètre)
1663 * #VAL{'1,2'} renvoie '1,2'
1664 * [(#VAL{a_suivre}|bouton_spip_rss)]
1665 * ```
1666 *
1667 * @param Champ $p
1668 * Pile au niveau de la balise
1669 * @return Champ
1670 * Pile complétée par le code à générer
1671 **/
1672 function balise_VAL_dist($p) {
1673 $p->code = interprete_argument_balise(1, $p);
1674 if (!strlen($p->code)) {
1675 $p->code = "''";
1676 }
1677 $p->interdire_scripts = false;
1678
1679 return $p;
1680 }
1681
1682 /**
1683 * Compile la balise `#NOOP`, alias (déprécié) de `#VAL`
1684 *
1685 * Alias pour regler #948. Ne plus utiliser.
1686 *
1687 * @balise
1688 * @see balise_VAL_dist()
1689 * @deprecated Utiliser #VAL
1690 *
1691 * @param Champ $p
1692 * Pile au niveau de la balise
1693 * @return Champ
1694 * Pile complétée par le code à générer
1695 **/
1696 function balise_NOOP_dist($p) { return balise_VAL_dist($p); }
1697
1698
1699 /**
1700 * Compile la balise `#REM` servant à commenter du texte
1701 *
1702 * Retourne toujours une chaîne vide.
1703 *
1704 * @balise
1705 * @link http://www.spip.net/4578
1706 * @example
1707 * ```
1708 * [(#REM)
1709 * Ceci est une remarque ou un commentaire,
1710 * non affiché dans le code généré
1711 * ]
1712 * ```
1713 *
1714 * @note
1715 * La balise `#REM` n'empêche pas l'exécution des balises SPIP contenues
1716 * dedans (elle ne sert pas à commenter du code pour empêcher son
1717 * exécution).
1718 *
1719 * @param Champ $p
1720 * Pile au niveau de la balise
1721 * @return Champ
1722 * Pile complétée par le code à générer
1723 **/
1724 function balise_REM_dist($p) {
1725 $p->code = "''";
1726 $p->interdire_scripts = false;
1727
1728 return $p;
1729 }
1730
1731
1732 /**
1733 * Compile la balise `#HTTP_HEADER` envoyant des entêtes de retour HTTP
1734 *
1735 * Doit être placée en tête de fichier et ne fonctionne pas dans une
1736 * inclusion.
1737 *
1738 * @balise
1739 * @link http://www.spip.net/4631
1740 * @example
1741 * ```
1742 * #HTTP_HEADER{Content-Type: text/csv; charset=#CHARSET}
1743 * ```
1744 *
1745 * @param Champ $p
1746 * Pile au niveau de la balise
1747 * @return Champ
1748 * Pile complétée par le code à générer
1749 **/
1750 function balise_HTTP_HEADER_dist($p) {
1751
1752 $header = interprete_argument_balise(1, $p);
1753 if (!$header) {
1754 $err_b_s_a = array('zbug_balise_sans_argument', array('balise' => 'HTTP_HEADER'));
1755 erreur_squelette($err_b_s_a, $p);
1756 } else {
1757 $p->code = "'<'.'?php header(' . _q("
1758 . $header
1759 . ") . '); ?'.'>'";
1760 }
1761 $p->interdire_scripts = false;
1762
1763 return $p;
1764 }
1765
1766
1767 /**
1768 * Compile la balise `#FILTRE` qui exécute un filtre à l'ensemble du squelette
1769 * une fois calculé.
1770 *
1771 * Le filtrage se fait au niveau du squelette, sans s'appliquer aux `<INCLURE>`.
1772 * Plusieurs filtres peuvent être indiqués, séparés par des barres verticales `|`
1773 *
1774 * @balise
1775 * @link http://www.spip.net/4894
1776 * @example
1777 * ```
1778 * #FILTRE{compacte_head}
1779 * #FILTRE{supprimer_tags|filtrer_entites|trim}
1780 * ```
1781 *
1782 * @param Champ $p
1783 * Pile au niveau de la balise
1784 * @return Champ
1785 * Pile complétée par le code à générer
1786 **/
1787 function balise_FILTRE_dist($p) {
1788 if ($p->param) {
1789 $args = array();
1790 foreach ($p->param as $i => $ignore) {
1791 $args[] = interprete_argument_balise($i + 1, $p);
1792 }
1793 $p->code = "'<' . '"
1794 . '?php header("X-Spip-Filtre: \'.'
1795 . join('.\'|\'.', $args)
1796 . " . '\"); ?'.'>'";
1797
1798 $p->interdire_scripts = false;
1799
1800 return $p;
1801 }
1802 }
1803
1804
1805 /**
1806 * Compile la balise `#CACHE` definissant la durée de validité du cache du squelette
1807 *
1808 * Signature : `#CACHE{duree[,type]}`
1809 *
1810 * Le premier argument est la durée en seconde du cache. Le second
1811 * (par défaut `statique`) indique le type de cache :
1812 *
1813 * - `cache-client` autorise gestion du IF_MODIFIED_SINCE
1814 * - `statique` ne respecte pas l'invalidation par modif de la base
1815 * (mais s'invalide tout de même à l'expiration du delai)
1816 *
1817 * @balise
1818 * @see ecrire/public/cacher.php
1819 * @link http://www.spip.net/4330
1820 * @example
1821 * ```
1822 * #CACHE{24*3600}
1823 * #CACHE{24*3600, cache-client}
1824 * #CACHE{0} pas de cache
1825 * ```
1826 * @note
1827 * En absence de cette balise la durée est du cache est donné
1828 * par la constante `_DUREE_CACHE_DEFAUT`
1829 *
1830 * @param Champ $p
1831 * Pile au niveau de la balise
1832 * @return Champ
1833 * Pile complétée par le code à générer
1834 **/
1835 function balise_CACHE_dist($p) {
1836
1837 if ($p->param) {
1838 $duree = valeur_numerique($p->param[0][1][0]->texte);
1839
1840 // noter la duree du cache dans un entete proprietaire
1841
1842 $code = "'<'.'" . '?php header("X-Spip-Cache: '
1843 . $duree
1844 . '"); ?' . "'.'>'";
1845
1846 // Remplir le header Cache-Control
1847 // cas #CACHE{0}
1848 if ($duree == 0) {
1849 $code .= ".'<'.'"
1850 . '?php header("Cache-Control: no-cache, must-revalidate"); ?'
1851 . "'.'><'.'"
1852 . '?php header("Pragma: no-cache"); ?'
1853 . "'.'>'";
1854 }
1855
1856 // recuperer les parametres suivants
1857 $i = 1;
1858 while (isset($p->param[0][++$i])) {
1859 $pa = ($p->param[0][$i][0]->texte);
1860
1861 if ($pa == 'cache-client'
1862 and $duree > 0
1863 ) {
1864 $code .= ".'<'.'" . '?php header("Cache-Control: max-age='
1865 . $duree
1866 . '"); ?' . "'.'>'";
1867 // il semble logique, si on cache-client, de ne pas invalider
1868 $pa = 'statique';
1869 }
1870
1871 if ($pa == 'statique'
1872 and $duree > 0
1873 ) {
1874 $code .= ".'<'.'" . '?php header("X-Spip-Statique: oui"); ?' . "'.'>'";
1875 }
1876 }
1877 } else {
1878 $code = "''";
1879 }
1880 $p->code = $code;
1881 $p->interdire_scripts = false;
1882
1883 return $p;
1884 }
1885
1886
1887 /**
1888 * Compile la balise `#INSERT_HEAD` permettant d'insérer du contenu dans
1889 * le `<head>` d'une page HTML
1890 *
1891 * La balise permet aux plugins d'insérer des styles, js ou autre
1892 * dans l'entête sans modification du squelette.
1893 * Les css doivent être inserées de préférence par `#INSERT_HEAD_CSS`
1894 * pour en faciliter la surcharge.
1895 *
1896 * On insère ici aussi un morceau de PHP qui verifiera à l'exécution
1897 * que le pipeline `insert_head_css` a bien été vu
1898 * et dans le cas contraire l'appelera. Ceal permet de ne pas oublier
1899 * les css de `#INSERT_HEAD_CSS` même si cette balise n'est pas presente.
1900 *
1901 * Il faut mettre ce php avant le `insert_head` car le compresseur y mets
1902 * ensuite un php du meme type pour collecter
1903 * CSS et JS, et on ne veut pas qu'il rate les css insérées en fallback
1904 * par `insert_head_css_conditionnel`.
1905 *
1906 * @link http://www.spip.net/4629
1907 * @see balise_INSERT_HEAD_CSS_dist()
1908 *
1909 * @param Champ $p
1910 * Pile au niveau de la balise
1911 * @return Champ
1912 * Pile complétée par le code à générer
1913 */
1914 function balise_INSERT_HEAD_dist($p) {
1915 $p->code = "'<'.'"
1916 . '?php header("X-Spip-Filtre: insert_head_css_conditionnel"); ?'
1917 . "'.'>'";
1918 $p->code .= ". pipeline('insert_head','<!-- insert_head -->')";
1919 $p->interdire_scripts = false;
1920
1921 return $p;
1922 }
1923
1924 /**
1925 * Compile la balise `#INSERT_HEAD_CSS` homologue de `#INSERT_HEAD` pour les CSS
1926 *
1927 * Et par extension pour le JS inline qui doit préférentiellement
1928 * être inséré avant les CSS car bloquant sinon.
1929 *
1930 * @link http://www.spip.net/4605
1931 * @see balise_INSERT_HEAD_dist()
1932 *
1933 * @param Champ $p
1934 * Pile au niveau de la balise
1935 * @return Champ
1936 * Pile complétée par le code à générer
1937 */
1938 function balise_INSERT_HEAD_CSS_dist($p) {
1939 $p->code = "pipeline('insert_head_css','<!-- insert_head_css -->')";
1940 $p->interdire_scripts = false;
1941
1942 return $p;
1943 }
1944
1945 /**
1946 * Compile la balise `#INCLUDE` alias de `#INCLURE`
1947 *
1948 * @balise
1949 * @see balise_INCLURE_dist()
1950 *
1951 * @param Champ $p
1952 * Pile au niveau de la balise
1953 * @return Champ
1954 * Pile complétée par le code à générer
1955 **/
1956 function balise_INCLUDE_dist($p) {
1957 if (function_exists('balise_INCLURE')) {
1958 return balise_INCLURE($p);
1959 } else {
1960 return balise_INCLURE_dist($p);
1961 }
1962 }
1963
1964 /**
1965 * Compile la balise `#INCLURE` qui inclut un résultat de squelette
1966 *
1967 * Signature : `[(#INCLURE{fond=nom_du_squelette, argument, argument=xx})]`
1968 *
1969 * L'argument `env` permet de transmettre tout l'environnement du squelette
1970 * en cours au squelette inclus.
1971 *
1972 * On parle d’inclusion « statique » car le résultat de compilation est
1973 * ajouté au squelette en cours, dans le même fichier de cache.
1974 * Cette balise est donc différente d’une inclusion « dynamique » avec
1975 * `<INCLURE.../>` qui, elle, crée un fichier de cache séparé
1976 * (avec une durée de cache qui lui est propre).
1977 *
1978 * L'inclusion est realisée au calcul du squelette, pas au service
1979 * ainsi le produit du squelette peut être utilisé en entrée de filtres
1980 * à suivre. On peut faire un `#INCLURE{fichier}` sans squelette
1981 * (Incompatible avec les balises dynamiques).
1982 *
1983 * @balise
1984 * @example
1985 * ```
1986 * [(#INCLURE{fond=inclure/documents,id_article, env})]
1987 * ```
1988 *
1989 * @param Champ $p
1990 * Pile au niveau de la balise
1991 * @return Champ
1992 * Pile complétée par le code à générer
1993 **/
1994 function balise_INCLURE_dist($p) {
1995 $id_boucle = $p->id_boucle;
1996 // la lang n'est pas passe de facon automatique par argumenter
1997 // mais le sera pas recuperer_fond, sauf si etoile=>true est passe
1998 // en option
1999
2000 $_contexte = argumenter_inclure($p->param, true, $p, $p->boucles, $id_boucle, false, false);
2001
2002 // erreur de syntaxe = fond absent
2003 // (2 messages d'erreur SPIP pour le prix d'un, mais pas d'erreur PHP
2004 if (!$_contexte) {
2005 $contexte = array();
2006 }
2007
2008 if (isset($_contexte['fond'])) {
2009
2010 $f = $_contexte['fond'];
2011 // toujours vrai :
2012 if (preg_match('/^.fond.\s*=>(.*)$/s', $f, $r)) {
2013 $f = $r[1];
2014 unset($_contexte['fond']);
2015 } else {
2016 spip_log("compilation de #INCLURE a revoir");
2017 }
2018
2019 // #INCLURE{doublons}
2020 if (isset($_contexte['doublons'])) {
2021 $_contexte['doublons'] = "'doublons' => \$doublons";
2022 }
2023
2024 // Critere d'inclusion {env} (et {self} pour compatibilite ascendante)
2025 $flag_env = false;
2026 if (isset($_contexte['env']) or isset($_contexte['self'])) {
2027 $flag_env = true;
2028 unset($_contexte['env']);
2029 }
2030
2031 $_options = array();
2032 if (isset($_contexte['ajax'])) {
2033 $_options[] = preg_replace(",=>(.*)$,ims", '=> ($v=(\\1))?$v:true', $_contexte['ajax']);
2034 unset($_contexte['ajax']);
2035 }
2036 if ($p->etoile) {
2037 $_options[] = "'etoile'=>true";
2038 }
2039 $_options[] = "'compil'=>array(" . memoriser_contexte_compil($p) . ")";
2040
2041 $_l = 'array(' . join(",\n\t", $_contexte) . ')';
2042 if ($flag_env) {
2043 $_l = "array_merge(\$Pile[0],$_l)";
2044 }
2045
2046 $p->code = sprintf(CODE_RECUPERER_FOND, $f, $_l, join(',', $_options), "_request('connect')");
2047
2048 } elseif (!isset($_contexte[1])) {
2049 $msg = array('zbug_balise_sans_argument', array('balise' => ' INCLURE'));
2050 erreur_squelette($msg, $p);
2051 } else {
2052 $p->code = 'charge_scripts(' . $_contexte[1] . ',false)';
2053 }
2054
2055 $p->interdire_scripts = false; // la securite est assuree par recuperer_fond
2056 return $p;
2057 }
2058
2059
2060 /**
2061 * Compile la balise `#MODELE` qui inclut un résultat de squelette de modèle
2062 *
2063 * `#MODELE{nom}` insère le résultat d’un squelette contenu dans le
2064 * répertoire `modeles/`. L’identifiant de la boucle parente est transmis
2065 * par défaut avec le paramètre `id` à cette inclusion.
2066 *
2067 * Des arguments supplémentaires peuvent être transmis :
2068 * `[(#MODELE{nom, argument=xx, argument})]`
2069 *
2070 * @balise
2071 * @see balise_INCLURE_dist()
2072 * @example
2073 * ```
2074 * #MODELE{article_traductions}
2075 * ```
2076 *
2077 * @param Champ $p
2078 * Pile au niveau de la balise
2079 * @return Champ
2080 * Pile complétée par le code à générer
2081 **/
2082 function balise_MODELE_dist($p) {
2083
2084 $_contexte = argumenter_inclure($p->param, true, $p, $p->boucles, $p->id_boucle, false);
2085
2086 // erreur de syntaxe = fond absent
2087 // (2 messages d'erreur SPIP pour le prix d'un, mais pas d'erreur PHP
2088 if (!$_contexte) {
2089 $contexte = array();
2090 }
2091
2092 if (!isset($_contexte[1])) {
2093 $msg = array('zbug_balise_sans_argument', array('balise' => ' MODELE'));
2094 erreur_squelette($msg, $p);
2095 } else {
2096 $nom = $_contexte[1];
2097 unset($_contexte[1]);
2098
2099 if (preg_match("/^\s*'[^']*'/s", $nom)) {
2100 $nom = "'modeles/" . substr($nom, 1);
2101 } else {
2102 $nom = "'modeles/' . $nom";
2103 }
2104
2105 $flag_env = false;
2106 if (isset($_contexte['env'])) {
2107 $flag_env = true;
2108 unset($_contexte['env']);
2109 }
2110
2111 // Incoherence dans la syntaxe du contexte. A revoir.
2112 // Reserver la cle primaire de la boucle courante si elle existe
2113 if (isset($p->boucles[$p->id_boucle]->primary)) {
2114 $primary = $p->boucles[$p->id_boucle]->primary;
2115 if (!strpos($primary, ',')) {
2116 $id = champ_sql($primary, $p);
2117 $_contexte[] = "'$primary'=>" . $id;
2118 $_contexte[] = "'id'=>" . $id;
2119 }
2120 }
2121 $_contexte[] = "'recurs'=>(++\$recurs)";
2122 $connect = '';
2123 if (isset($p->boucles[$p->id_boucle])) {
2124 $connect = $p->boucles[$p->id_boucle]->sql_serveur;
2125 }
2126
2127 $_options = memoriser_contexte_compil($p);
2128 $_options = "'compil'=>array($_options), 'trim'=>true";
2129 if (isset($_contexte['ajax'])) {
2130 $_options .= ", " . preg_replace(",=>(.*)$,ims", '=> ($v=(\\1))?$v:true', $_contexte['ajax']);
2131 unset($_contexte['ajax']);
2132 }
2133
2134 $_l = 'array(' . join(",\n\t", $_contexte) . ')';
2135 if ($flag_env) {
2136 $_l = "array_merge(\$Pile[0],$_l)";
2137 }
2138
2139 $page = sprintf(CODE_RECUPERER_FOND, $nom, $_l, $_options, _q($connect));
2140
2141 $p->code = "\n\t(((\$recurs=(isset(\$Pile[0]['recurs'])?\$Pile[0]['recurs']:0))>=5)? '' :\n\t$page)\n";
2142
2143 $p->interdire_scripts = false; // securite assuree par le squelette
2144 }
2145
2146 return $p;
2147 }
2148
2149
2150 /**
2151 * Compile la balise `#SET` qui affecte une variable locale au squelette
2152 *
2153 * Signature : `#SET{cle,valeur}`
2154 *
2155 * @balise
2156 * @link http://www.spip.net/3990 Balises #SET et #GET
2157 * @see balise_GET_dist()
2158 * @example
2159 * ```
2160 * #SET{nb,5}
2161 * #GET{nb} // affiche 5
2162 * ```
2163 *
2164 * @param Champ $p
2165 * Pile au niveau de la balise
2166 * @return Champ
2167 * Pile complétée par le code à générer
2168 **/
2169 function balise_SET_dist($p) {
2170 $_nom = interprete_argument_balise(1, $p);
2171 $_val = interprete_argument_balise(2, $p);
2172
2173 if (!$_nom or !$_val) {
2174 $err_b_s_a = array('zbug_balise_sans_argument', array('balise' => 'SET'));
2175 erreur_squelette($err_b_s_a, $p);
2176 }
2177 // affectation $_zzz inutile, mais permet de contourner un bug OpCode cache sous PHP 5.5.4
2178 // cf https://bugs.php.net/bug.php?id=65845
2179 else {
2180 $p->code = "vide(\$Pile['vars'][\$_zzz=(string)$_nom] = $_val)";
2181 }
2182
2183 $p->interdire_scripts = false; // la balise ne renvoie rien
2184 return $p;
2185 }
2186
2187
2188 /**
2189 * Compile la balise `#GET` qui récupère une variable locale au squelette
2190 *
2191 * Signature : `#GET{cle[,defaut]}`
2192 *
2193 * La clé peut obtenir des sous clés séparés par des `/`
2194 *
2195 * @balise
2196 * @link http://www.spip.net/3990 Balises #SET et #GET
2197 * @see balise_SET_dist()
2198 * @example
2199 * ```
2200 * #SET{nb,5}
2201 * #GET{nb} affiche 5
2202 * #GET{nb,3} affiche la valeur de nb, sinon 3
2203 *
2204 * #SET{nb,#ARRAY{boucles,3}}
2205 * #GET{nb/boucles} affiche 3, équivalent à #GET{nb}|table_valeur{boucles}
2206 * ```
2207 *
2208 * @param Champ $p
2209 * Pile au niveau de la balise
2210 * @return Champ
2211 * Pile complétée par le code à générer
2212 **/
2213 function balise_GET_dist($p) {
2214 $p->interdire_scripts = false; // le contenu vient de #SET, donc il est de confiance
2215 if (function_exists('balise_ENV')) {
2216 return balise_ENV($p, '$Pile["vars"]');
2217 } else {
2218 return balise_ENV_dist($p, '$Pile["vars"]');
2219 }
2220 }
2221
2222
2223 /**
2224 * Compile la balise `#DOUBLONS` qui redonne les doublons enregistrés
2225 *
2226 * - `#DOUBLONS{mots}` ou `#DOUBLONS{mots,famille}`
2227 * donne l'état des doublons `(MOTS)` à cet endroit
2228 * sous forme de tableau d'id_mot comme `array(1,2,3,...)`
2229 * - `#DOUBLONS` tout seul donne la liste brute de tous les doublons
2230 * - `#DOUBLONS*{mots}` donne la chaine brute `,1,2,3,...`
2231 * (changera si la gestion des doublons evolue)
2232 *
2233 * @balise
2234 * @link http://www.spip.net/4123
2235 *
2236 * @param Champ $p
2237 * Pile au niveau de la balise
2238 * @return Champ
2239 * Pile complétée par le code à générer
2240 **/
2241 function balise_DOUBLONS_dist($p) {
2242 if ($type = interprete_argument_balise(1, $p)) {
2243 if ($famille = interprete_argument_balise(2, $p)) {
2244 $type .= '.' . $famille;
2245 }
2246 $p->code = '(isset($doublons[' . $type . ']) ? $doublons[' . $type . '] : "")';
2247 if (!$p->etoile) {
2248 $p->code = 'array_filter(array_map("intval",explode(",",'
2249 . $p->code . ')))';
2250 }
2251 } else {
2252 $p->code = '$doublons';
2253 }
2254
2255 $p->interdire_scripts = false;
2256
2257 return $p;
2258 }
2259
2260
2261 /**
2262 * Compile la balise `#PIPELINE` pour permettre d'insérer des sorties de
2263 * pipeline dans un squelette
2264 *
2265 * @balise
2266 * @see pipeline()
2267 * @example
2268 * ```
2269 * #PIPELINE{nom}
2270 * #PIPELINE{nom,données}
2271 * #PIPELINE{boite_infos,#ARRAY{data,'',args,#ARRAY{type,rubrique,id,#ENV{id_rubrique}}}}
2272 * ```
2273 *
2274 * @param Champ $p
2275 * Pile au niveau de la balise
2276 * @return Champ
2277 * Pile complétée par le code à générer
2278 **/
2279 function balise_PIPELINE_dist($p) {
2280 $_pipe = interprete_argument_balise(1, $p);
2281 if (!$_pipe) {
2282 $err_b_s_a = array('zbug_balise_sans_argument', array('balise' => 'PIPELINE'));
2283 erreur_squelette($err_b_s_a, $p);
2284 } else {
2285 $_flux = interprete_argument_balise(2, $p);
2286 $_flux = $_flux ? $_flux : "''";
2287 $p->code = "pipeline( $_pipe , $_flux )";
2288 $p->interdire_scripts = false;
2289 }
2290
2291 return $p;
2292 }
2293
2294
2295 /**
2296 * Compile la balise `#EDIT` qui ne fait rien dans SPIP
2297 *
2298 * Cette balise ne retourne rien mais permet d'indiquer, pour certains plugins
2299 * qui redéfinissent cette balise, le nom du champ SQL (ou le nom d'un contrôleur)
2300 * correspondant à ce qui est édité. Cela sert particulièrement au plugin Crayons.
2301 * Ainsi en absence du plugin, la balise est toujours reconnue (mais n'a aucune action).
2302 *
2303 * @balise
2304 * @link http://www.spip.net/4584
2305 * @example
2306 * ```
2307 * [<div class="#EDIT{texte} texte">(#TEXTE)</div>]
2308 * ```
2309 *
2310 * @param Champ $p
2311 * Pile au niveau de la balise
2312 * @return Champ
2313 * Pile complétée par le code à générer
2314 **/
2315 function balise_EDIT_dist($p) {
2316 $p->code = "''";
2317 $p->interdire_scripts = false;
2318
2319 return $p;
2320 }
2321
2322
2323 /**
2324 * Compile la balise `#TOTAL_UNIQUE` qui récupère le nombre d'éléments
2325 * différents affichés par le filtre `unique`
2326 *
2327 * @balise
2328 * @link http://www.spip.net/4374
2329 * @see unique()
2330 * @example
2331 * ```
2332 * #TOTAL_UNIQUE affiche le nombre de #BALISE|unique
2333 * #TOTAL_UNIQUE{famille} afiche le nombre de #BALISE|unique{famille}
2334 * ```
2335 *
2336 * @param Champ $p
2337 * Pile au niveau de la balise
2338 * @return Champ
2339 * Pile complétée par le code à générer
2340 **/
2341 function balise_TOTAL_UNIQUE_dist($p) {
2342 $_famille = interprete_argument_balise(1, $p);
2343 $_famille = $_famille ? $_famille : "''";
2344 $p->code = "unique('', $_famille, true)";
2345
2346 return $p;
2347 }
2348
2349 /**
2350 * Compile la balise `#ARRAY` créant un tableau PHP associatif
2351 *
2352 * Crée un `array` PHP à partir d'arguments calculés.
2353 * Chaque paire d'arguments représente la clé et la valeur du tableau.
2354 *
2355 * @balise
2356 * @link http://www.spip.net/4009
2357 * @example
2358 * ```
2359 * #ARRAY{key1,val1,key2,val2 ...} retourne
2360 * array( key1 => val1, key2 => val2, ...)
2361 * ```
2362 *
2363 * @param Champ $p
2364 * Pile au niveau de la balise
2365 * @return Champ
2366 * Pile complétée par le code à générer
2367 **/
2368 function balise_ARRAY_dist($p) {
2369 $_code = array();
2370 $n = 1;
2371 do {
2372 $_key = interprete_argument_balise($n++, $p);
2373 $_val = interprete_argument_balise($n++, $p);
2374 if ($_key and $_val) {
2375 $_code[] = "$_key => $_val";
2376 }
2377 } while ($_key && $_val);
2378 $p->code = 'array(' . join(', ', $_code) . ')';
2379 $p->interdire_scripts = false;
2380
2381 return $p;
2382 }
2383
2384 /**
2385 * Compile la balise `#LISTE` qui crée un tableau PHP avec les valeurs, sans préciser les clés
2386 *
2387 * @balise
2388 * @link http://www.spip.net/5547
2389 * @example
2390 * ```
2391 * #LISTE{a,b,c,d,e}
2392 * ```
2393 *
2394 * @param Champ $p
2395 * Pile au niveau de la balise
2396 * @return Champ
2397 * Pile complétée par le code à générer
2398 */
2399 function balise_LISTE_dist($p) {
2400 $_code = array();
2401 $n = 1;
2402 while ($_val = interprete_argument_balise($n++, $p)) {
2403 $_code[] = $_val;
2404 }
2405 $p->code = 'array(' . join(', ', $_code) . ')';
2406 $p->interdire_scripts = false;
2407
2408 return $p;
2409 }
2410
2411
2412 /**
2413 * Compile la balise `#AUTORISER` qui teste une autorisation
2414 *
2415 * Appelle la fonction `autoriser()` avec les mêmes arguments,
2416 * et renvoie un espace ' ' si OK (l'action est autorisée),
2417 * sinon une chaine vide '' (l'action n'est pas autorisée).
2418 *
2419 * Cette balise créée un cache par session.
2420 *
2421 * Signature : `#AUTORISER{faire[,type[,id[,auteur[,options]]]}`
2422 *
2423 * @note
2424 * La priorité des opérateurs exige && plutot que AND
2425 *
2426 * @balise
2427 * @link http://www.spip.net/3896
2428 * @see autoriser()
2429 * @see sinon_interdire_acces()
2430 * @example
2431 * ```
2432 * [(#AUTORISER{modifier,rubrique,#ID_RUBRIQUE}) ... ]
2433 * [(#AUTORISER{voir,rubrique,#ID_RUBRIQUE}|sinon_interdire_acces)]
2434 * ```
2435 *
2436 * @param Champ $p
2437 * Pile au niveau de la balise
2438 * @return Champ
2439 * Pile complétée par le code à générer
2440 **/
2441 function balise_AUTORISER_dist($p) {
2442 $_code = array();
2443 $p->descr['session'] = true; // faire un cache par session
2444
2445 $n = 1;
2446 while ($_v = interprete_argument_balise($n++, $p)) {
2447 $_code[] = $_v;
2448 }
2449
2450 $p->code = '((function_exists("autoriser")||include_spip("inc/autoriser"))&&autoriser(' . join(', ',
2451 $_code) . ')?" ":"")';
2452 $p->interdire_scripts = false;
2453
2454 return $p;
2455 }
2456
2457
2458 /**
2459 * Compile la balise `#PLUGIN` qui permet d’afficher les informations d'un plugin actif
2460 *
2461 * @balise
2462 * @see filtre_info_plugin_dist()
2463 * @link http://www.spip.net/4591
2464 * @example
2465 * ```
2466 * #PLUGIN Retourne la liste sérialisée des préfixes de plugins actifs
2467 * #PLUGIN{prefixe} Renvoie true si le plugin avec ce préfixe est actif
2468 * #PLUGIN{prefixe, x} Renvoie l'information x du plugin (s'il est actif)
2469 * #PLUGIN{prefixe, tout} Renvoie toutes les informations du plugin (s'il est actif)
2470 * ```
2471 *
2472 * @param Champ $p
2473 * Pile au niveau de la balise
2474 * @return Champ
2475 * Pile complétée par le code à générer
2476 **/
2477 function balise_PLUGIN_dist($p) {
2478 $plugin = interprete_argument_balise(1, $p);
2479 $plugin = isset($plugin) ? str_replace('\'', '"', $plugin) : '""';
2480 $type_info = interprete_argument_balise(2, $p);
2481 $type_info = isset($type_info) ? str_replace('\'', '"', $type_info) : '"est_actif"';
2482
2483 $f = chercher_filtre('info_plugin');
2484 $p->code = $f . '(' . $plugin . ', ' . $type_info . ')';
2485
2486 return $p;
2487 }
2488
2489 /**
2490 * Compile la balise `#AIDER` qui permet d’afficher l’icone de l’aide
2491 * au sein des squelettes.
2492 *
2493 * @balise
2494 * @see inc_aide_dist()
2495 * @link http://www.spip.net/4733
2496 * @example
2497 * ```
2498 * #AIDER{titre}
2499 * ```
2500 *
2501 * @param Champ $p
2502 * Pile au niveau de la balise
2503 * @return Champ
2504 * Pile complétée par le code à générer
2505 **/
2506 function balise_AIDER_dist($p) {
2507 $_motif = interprete_argument_balise(1, $p);
2508 $s = "'" . addslashes($p->descr['sourcefile']) . "'";
2509 $p->code = "((\$aider=charger_fonction('aide','inc',true))?\$aider($_motif,$s, \$Pile[0]):'')";
2510
2511 return $p;
2512 }
2513
2514 /**
2515 * Compile la balise `#ACTION_FORMULAIRE` qui insère le contexte
2516 * des formulaires charger / vérifier / traiter avec les hidden de
2517 * l'URL d'action
2518 *
2519 * Accèpte 2 arguments optionnels :
2520 * - L'url de l'action (par défaut `#ENV{action}`
2521 * - Le nom du formulaire (par défaut `#ENV{form}`
2522 *
2523 * @balise
2524 * @see form_hidden()
2525 * @example
2526 * ```
2527 * <form method='post' action='#ENV{action}'><div>
2528 * #ACTION_FORMULAIRE
2529 * ```
2530 *
2531 * @param Champ $p
2532 * Pile au niveau de la balise
2533 * @return Champ
2534 * Pile complétée par le code à générer
2535 **/
2536 function balise_ACTION_FORMULAIRE($p) {
2537 if (!$_url = interprete_argument_balise(1, $p)) {
2538 $_url = "@\$Pile[0]['action']";
2539 }
2540 if (!$_form = interprete_argument_balise(2, $p)) {
2541 $_form = "@\$Pile[0]['form']";
2542 }
2543
2544 // envoyer le nom du formulaire que l'on traite
2545 // transmettre les eventuels args de la balise formulaire
2546 $p->code = " '<div>' .
2547 form_hidden($_url) .
2548 '<input name=\'formulaire_action\' type=\'hidden\'
2549 value=\'' . $_form . '\' />' .
2550 '<input name=\'formulaire_action_args\' type=\'hidden\'
2551 value=\'' . @\$Pile[0]['formulaire_args']. '\' />' .
2552 (!empty(\$Pile[0]['_hidden']) ? @\$Pile[0]['_hidden'] : '') .
2553 '</div>'";
2554
2555 $p->interdire_scripts = false;
2556
2557 return $p;
2558 }
2559
2560
2561 /**
2562 * Compile la balise `#BOUTON_ACTION` qui génère un bouton d'action en post, ajaxable
2563 *
2564 * Cette balise s'utilise à la place des liens `action_auteur`, sous la forme
2565 *
2566 * - `#BOUTON_ACTION{libelle,url}`
2567 * - ou `#BOUTON_ACTION{libelle,url,ajax}` pour que l'action soit ajax comme un lien `class='ajax'`
2568 * - ou `#BOUTON_ACTION{libelle,url,ajax,message_confirmation}` pour utiliser un message de confirmation
2569 * - ou encore `#BOUTON_ACTION{libelle[,url[,ajax[,message_confirmation[,title[,callback]]]]]}`
2570 *
2571 * @balise
2572 * @link http://www.spip.net/4583
2573 * @example
2574 * ```
2575 * [(#AUTORISER{reparer,base})
2576 * [(#BOUTON_ACTION{<:bouton_tenter_recuperation:>,#URL_ECRIRE{base_repair}})]
2577 * ]
2578 * ```
2579 *
2580 * @param Champ $p
2581 * Pile au niveau de la balise
2582 * @return Champ
2583 * Pile complétée par le code à générer
2584 */
2585 function balise_BOUTON_ACTION_dist($p) {
2586
2587 $args = array();
2588 for ($k = 1; $k <= 6; $k++) {
2589 $_a = interprete_argument_balise($k, $p);
2590 if (!$_a) {
2591 $_a = "''";
2592 }
2593 $args[] = $_a;
2594 }
2595 // supprimer les args vides
2596 while (end($args) == "''" and count($args) > 2) {
2597 array_pop($args);
2598 }
2599 $args = implode(",", $args);
2600
2601 $bouton_action = chercher_filtre("bouton_action");
2602 $p->code = "$bouton_action($args)";
2603 $p->interdire_scripts = false;
2604
2605 return $p;
2606 }
2607
2608
2609 /**
2610 * Compile la balise `#SLOGAN_SITE_SPIP` qui retourne le slogan du site
2611 *
2612 * @balise
2613 * @example
2614 * ```
2615 * [<p id="slogan">(#SLOGAN_SITE_SPIP)</p>]
2616 * ```
2617 *
2618 * @param Champ $p
2619 * Pile au niveau de la balise
2620 * @return Champ
2621 * Pile complétée par le code à générer
2622 */
2623 function balise_SLOGAN_SITE_SPIP_dist($p) {
2624 $p->code = "\$GLOBALS['meta']['slogan_site']";
2625
2626 #$p->interdire_scripts = true;
2627 return $p;
2628 }
2629
2630
2631 /**
2632 * Compile la balise `#HTML5` indiquant si l'espace public peut utiliser du HTML5
2633 *
2634 * Renvoie `' '` si le webmestre souhaite que SPIP génère du code (X)HTML5 sur
2635 * le site public, et `''` si le code doit être strictement compatible HTML4
2636 *
2637 * @balise
2638 * @uses html5_permis()
2639 * @example
2640 * ```
2641 * [(#HTML5) required="required"]
2642 * <input[ (#HTML5|?{type="email",type="text"})] ... />
2643 * ```
2644 *
2645 * @param Champ $p
2646 * Pile au niveau de la balise
2647 * @return Champ
2648 * Pile complétée par le code à générer
2649 */
2650 function balise_HTML5_dist($p) {
2651 $p->code = html5_permis() ? "' '" : "''";
2652 $p->interdire_scripts = false;
2653
2654 return $p;
2655 }
2656
2657
2658 /**
2659 * Compile la balise `#TRI` permettant d'afficher un lien de changement d'ordre de tri
2660 * d'une colonne de la boucle
2661 *
2662 * La balise `#TRI{champ[,libelle]}` champ prend `>` ou `<` pour afficher
2663 * le lien de changement de sens croissant ou decroissant (`>` `<` indiquent
2664 * un sens par une flèche)
2665 *
2666 * @balise
2667 * @example
2668 * ```
2669 * <th>[(#TRI{titre,<:info_titre:>,ajax})]</th>
2670 * ```
2671 *
2672 * @param Champ $p
2673 * Pile au niveau de la balise
2674 * @param string $liste
2675 * Inutilisé
2676 * @return Champ
2677 * Pile complétée par le code à générer
2678 */
2679 function balise_TRI_dist($p, $liste = 'true') {
2680 $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
2681
2682 // s'il n'y a pas de nom de boucle, on ne peut pas trier
2683 if ($b === '') {
2684 erreur_squelette(
2685 _T('zbug_champ_hors_boucle',
2686 array('champ' => '#TRI')
2687 ), $p->id_boucle);
2688 $p->code = "''";
2689
2690 return $p;
2691 }
2692 $boucle = $p->boucles[$b];
2693
2694 // s'il n'y a pas de tri_champ, c'est qu'on se trouve
2695 // dans un boucle recursive ou qu'on a oublie le critere {tri}
2696 if (!isset($boucle->modificateur['tri_champ'])) {
2697 erreur_squelette(
2698 _T('zbug_tri_sans_critere',
2699 array('champ' => '#TRI')
2700 ), $p->id_boucle);
2701 $p->code = "''";
2702
2703 return $p;
2704 }
2705
2706 $_champ = interprete_argument_balise(1, $p);
2707 // si pas de champ, renvoyer le critere de tri utilise
2708 if (!$_champ) {
2709 $p->code = $boucle->modificateur['tri_champ'];
2710
2711 return $p;
2712 }
2713 // forcer la jointure si besoin, et si le champ est statique
2714 if (preg_match(",^'([\w.]+)'$,i", $_champ, $m)) {
2715 index_pile($b, $m[1], $p->boucles, '', null, true, false);
2716 }
2717
2718 $_libelle = interprete_argument_balise(2, $p);
2719 $_libelle = $_libelle ? $_libelle : $_champ;
2720
2721 $_class = interprete_argument_balise(3, $p);
2722 // si champ = ">" c'est un lien vers le tri croissant : de gauche a droite ==> 1
2723 // si champ = "<" c'est un lien vers le tri decroissant : (sens inverse) == -1
2724 $_issens = "in_array($_champ,array('>','<'))";
2725 $_sens = "(strpos('< >',$_champ)-1)";
2726
2727 $_variable = "((\$s=$_issens)?'sens':'tri')." . $boucle->modificateur['tri_nom'];
2728 $_url = "parametre_url(self(),$_variable,\$s?$_sens:$_champ)";
2729 $_url = "parametre_url($_url,'var_memotri',strncmp(" . $boucle->modificateur['tri_nom'] . ",'session',7)==0?$_variable:'')";
2730 $_on = "\$s?(" . $boucle->modificateur['tri_sens'] . "==$_sens" . '):(' . $boucle->modificateur['tri_champ'] . "==$_champ)";
2731
2732 $p->code = "lien_ou_expose($_url,$_libelle,$_on" . ($_class ? ",$_class" : "") . ")";
2733 //$p->code = "''";
2734 $p->interdire_scripts = false;
2735
2736 return $p;
2737 }
2738
2739
2740 /**
2741 * Compile la balise `#SAUTER{n}` qui permet de sauter en avant n resultats dans une boucle
2742 *
2743 * La balise modifie le compteur courant de la boucle, mais pas les autres
2744 * champs qui restent les valeurs de la boucle avant le saut. Il est donc
2745 * preferable d'utiliser la balise juste avant la fermeture `</BOUCLE>`
2746 *
2747 * L'argument `n` doit être supérieur à zéro sinon la balise ne fait rien
2748 *
2749 * @balise
2750 *
2751 * @param Champ $p
2752 * Pile au niveau de la balise
2753 * @return Champ
2754 * Pile complétée par le code à générer
2755 */
2756 function balise_SAUTER_dist($p) {
2757 $id_boucle = $p->id_boucle;
2758
2759 if (empty($p->boucles[$id_boucle])) {
2760 $msg = array('zbug_champ_hors_boucle', array('champ' => '#SAUTER'));
2761 erreur_squelette($msg, $p);
2762 } else {
2763 $boucle = $p->boucles[$id_boucle];
2764 $_saut = interprete_argument_balise(1, $p);
2765 $_compteur = "\$Numrows['$id_boucle']['compteur_boucle']";
2766 $_total = "\$Numrows['$id_boucle']['total']";
2767
2768 $p->code = "vide($_compteur=\$iter->skip($_saut,$_total))";
2769 }
2770 $p->interdire_scripts = false;
2771
2772 return $p;
2773 }
2774
2775
2776 /**
2777 * Compile la balise `#PUBLIE` qui indique si un objet est publié ou non
2778 *
2779 * @balise
2780 * @link http://www.spip.net/5545
2781 * @see objet_test_si_publie()
2782 * @example
2783 * ```
2784 * #PUBLIE : porte sur la boucle en cours
2785 * [(#PUBLIE{article, 3}|oui) ... ] : pour l'objet indiqué
2786 * ```
2787 *
2788 * @param Champ $p
2789 * Pile au niveau de la balise
2790 * @return Champ
2791 * Pile complétée par le code à générer
2792 */
2793 function balise_PUBLIE_dist($p) {
2794 if (!$_type = interprete_argument_balise(1, $p)) {
2795 $_type = _q($p->type_requete);
2796 $_id = champ_sql($p->boucles[$p->id_boucle]->primary, $p);
2797 } else {
2798 $_id = interprete_argument_balise(2, $p);
2799 }
2800
2801 $connect = '';
2802 if (isset($p->boucles[$p->id_boucle])) {
2803 $connect = $p->boucles[$p->id_boucle]->sql_serveur;
2804 }
2805
2806 $p->code = "(objet_test_si_publie(" . $_type . ",intval(" . $_id . ")," . _q($connect) . ")?' ':'')";
2807 $p->interdire_scripts = false;
2808
2809 return $p;
2810 }
2811
2812 /**
2813 * Compile la balise `#PRODUIRE` qui génère un fichier statique à partir
2814 * d'un squelette SPIP
2815 *
2816 * Le format du fichier sera extrait de la pre-extension du squelette
2817 * (typo.css.html, messcripts.js.html)
2818 * ou par l'argument `format=css` ou `format=js` passé en argument.
2819 *
2820 * S'il n'y a pas de format détectable, on utilise `.html`, comme pour les squelettes.
2821 *
2822 * La syntaxe de la balise est la même que celle de `#INCLURE`.
2823 *
2824 * @balise
2825 * @see balise_INCLURE_dist()
2826 * @link http://www.spip.net/5505
2827 * @example
2828 * ```
2829 * <link rel="stylesheet" type="text/css" href="#PRODUIRE{fond=css/macss.css,couleur=ffffff}" />
2830 * ```
2831 *
2832 * @param Champ $p
2833 * Pile au niveau de la balise
2834 * @return Champ
2835 * Pile complétée par le code à générer
2836 */
2837 function balise_PRODUIRE_dist($p) {
2838 $balise_inclure = charger_fonction('INCLURE', 'balise');
2839 $p = $balise_inclure($p);
2840
2841 $p->code = str_replace('recuperer_fond(', 'produire_fond_statique(', $p->code);
2842
2843 return $p;
2844 }
2845
2846 /**
2847 * Compile la balise `#LARGEUR_ECRAN` qui définit la largeur d'écran
2848 * dans l'espace privé
2849 *
2850 * @balise
2851 * @example
2852 * ```
2853 * #LARGEUR_ECRAN{pleine_largeur}
2854 * ```
2855 *
2856 * @param Champ $p
2857 * Pile au niveau de la balise
2858 * @return Champ
2859 * Pile complétée par le code à générer
2860 */
2861 function balise_LARGEUR_ECRAN_dist($p) {
2862 $_class = interprete_argument_balise(1, $p);
2863 if (!$_class) {
2864 $_class = 'null';
2865 }
2866 $p->code = "(is_string($_class)?vide(\$GLOBALS['largeur_ecran']=$_class):(isset(\$GLOBALS['largeur_ecran'])?\$GLOBALS['largeur_ecran']:''))";
2867
2868 return $p;
2869 }
2870
2871
2872 /**
2873 * Compile la balise `#CONST` qui retourne la valeur de la constante passée en argument
2874 *
2875 * @balise
2876 * @example `#CONST{_DIR_IMG}`
2877 *
2878 * @param Champ $p
2879 * Pile au niveau de la balise
2880 * @return Champ
2881 * Pile complétée par le code à générer
2882 **/
2883 function balise_CONST_dist($p) {
2884 $_const = interprete_argument_balise(1, $p);
2885 if (!strlen($_const)) {
2886 $p->code = "''";
2887 }
2888 else {
2889 $p->code = "(defined($_const)?constant($_const):'')";
2890 }
2891 $p->interdire_scripts = false;
2892
2893 return $p;
2894 }