Version 39 — Mai 2013 — YannX
(voir aussi Astuces courtes pour SPIP)
Question :
Dans ZPIP, la structure de base est définie à coup d’inclusion de grands blocs logiques du style
... <div id="contenu">
<INCLURE{fond=contenu/#ENV{type}, env}>
</div>
<div id="navigation">
<INCLURE{fond=navigation/#ENV{type}, env}>
<INCLURE{fond=extra/#ENV{type}, env}>
</div>....
Supposons par exemple que nous soyons dans un contexte « rubrique » http///monsite.fr/spip.php?rubriqueXX
et que le squelette contenu/rubrique.html
se comporte de manière à afficher l’article n°yy
Comment peut-on transmettre cet id_article
dans l’inclusion extra
ou dans l’inclusion navigation
pour, par exemple, lister les autres articles de la rubrique ? Il faut bien que le squelette appelé connaisse l’id de l’article à exclure que seul contenu/rubrique.html
connait !
Pour que extra/rubrique.html
ou navigation/rubrique.html
puisse tenir compte de la situation, il faut disposer d’un moyen pour leur transmettre cette valeur. Or, seul le squelette appelant peut transmettre une variable...
Réponse :
L’astuce (alambiquée) pour remonter des variables d’un squelette appelé, est d’affecter le résultat sérialisé de ce squelette à une variable tableau dans le squelette appelant :
Dans structure.html
:
[(#SET{myvars, [(#INCLURE{fond=vars/#ENV{type}, env})]})]
Dans vars/rubrique.html
:
#CACHE{3600}
[(#SET{vars, #ARRAY{
objet, rubrique,
id_objet, #ENV{id_rubrique},
titreRubrique, Rubriques,
titrePage, #ENV{id_rubrique}?
}})]
<BOUCLE_rub(RUBRIQUES) {id_rubrique}>
[(#SET{vars, #GET{vars}|array_merge{#ARRAY{titreRubrique, #TITRE, idRubrique, #ID_RUBRIQUE}}})]
</BOUCLE_rub>
[(#GET{vars}|serialize)]
#FILTRE{trim}
Puis dans structure.html
:
<INCLURE{fond=extra/#ENV{type}, myvars=#GET{myvars}, env}>
dans le body de ZPIP, charger ou non un INCLURE qui contient #ENV{type}
[(#ID_RUBRIQUE|=={10}|?{
''
,
<INCLURE{fond=extra/#ENV{type}, env}>
})]
ne fonctionne pas à cause de accolades de #ENV, donc contourner avec
[(#ID_RUBRIQUE|=={10}|non) <INCLURE{fond=extra/#ENV{type}, env}> ]
Tout le monde le sait (beaucoup le déplorent) :
[(#ENV{var}|=={val}|oui) <BOUCLE_test(TABLES) {critere}>...</BOUCLE_test> ]
ne fonctionne pas.
En SPIP3, le critère {si ...}
permet de définir la condition d’exécution depuis la boucle elle-même.
En SPIP2, il faut utiliser le plugin « bonux » et sa boucle « CONDITION ».
Il existe néanmoins un autre moyen pour arriver à nos fins...
#SET{test, #ENV{truc}|=={coucou}|?{0, 999999}}
<BOUCLE_de_test(TABLES) {id_table > #GET{test}}>
coucou !
</BOUCLE_de_test>
Explications :
#GET{test}
vaut donc soit « zéro », soit « neuf cent quatre-vingt-dix-neuf mille neuf cent quatre-vingt-dix-neuf ».Dans un squelette, afficher la date du jour (la date courante) n’est pas toujours aisé.
deux variables peuvent entrer en jeu :
Pour chaque cas, néanmoins, il existe une possibilité d’afficher la date du jour et ce sans que cette date soit tributaire du cache du squelette (ce sera toujours la date courante qui sera affichée)
dans une boucle dont la table possède un champ date ou date_redac |
hors boucle | |
pas de date ni de date_redac en url ni passées par formulaire |
#ENV{date_redac} ou #ENV{date} |
#DATE_REDAC ou #DATE |
variable date passée en url ou par formulaire | #ENV{date_redac} |
#DATE_REDAC |
variable date_redac passée en url ou par formulaire | #ENV{date} |
#DATE |
variables date et date_redac passées en url ou par formulaire |
définir en début de squelette la date courante avec par exemple : #SET{cur_date, #VAL{Y-m-d h:i:s}|date} puis l’utiliser dans le squelette avec : #GET{cur_date} |
voir aussi saisie des dates de publication et de rédaction antérieure (spip 2.0.x et 2.1.x)
</blockquote>- La date du prochain jeudi qui suit #DATE
:
[(#VAL{Y-m-d}|date{#VAL{next Thursday}|strtotime{#DATE|affdate{U}}})]
- Le nom du jour de l’avant-veille de #DATE
:
[(#VAL{Y-m-d}|date{#VAL{-2 days}|strtotime{#DATE|affdate{U}}}|nom_jour)]
- La date de lundi prochain :
[(#SET{lundi_prochain, #VAL{Y-m-d 00:00:00}|date{#VAL{next Monday}|strtotime{#DATE|affdate{U}}}})]
#GET{lundi_prochain}
- La date du dimanche d’après lundi prochain :
[(#SET{dimanche_apres, #VAL{Y-m-d 23:59:59}|date{#VAL{+6days}|strtotime{#GET{lundi_prochain}|affdate{U}}}})]
#GET{dimanche_apres}
- Afficher le label nouveau ! pour un article publié ou modifié il y a moins de 10 jours :
#SET{periode, -10days}
#TITRE[ (#DATE_MODIF|>{#VAL{Y-m-d H:i:s}|date{#GET{periode}|strtotime}}|oui) nouveau !]
et _fil_ (sur irc) propose encore plus simple :
#TITRE[ (#DATE|strtotime|>{#VAL{-10days}|strtotime}|?{ nouveau !, ''})]
Triton nous confie un petit exemple qui marche pour MS Excel et openOffice (il y a des particularités d’encodage de caractères) :
#CACHE{0} #HTTP_HEADER{Content-Type: text/csv; charset=windows-1252/Winlatin1} #HTTP_HEADER{Content-Disposition: attachment; filename=inscriptions.csv}
"date"; "nom_participant";"prenom";"paiement effectue"; "tarif_valide"; "promo";"telephone"; "email";"societe"; "nbr_part";"conjoint"; "mobile_dom";"type_paiement"; "souscripteur"
<BOUCLE_les_inscriptions_ouvertes(EVENEMENTS_LESINSCRIPTIONS) {id_evenement} {nbr_part > 0}>"[(#DATE|affdate{'d-m-Y'})]";"[(#NOM_PARTICIPANT|utf8_decode)]";"[(#PRENOM|utf8_decode)]";"[(#VERIFIER_PAIEMENT_INSCRIPTION{#ID_INSCRIPTION}|utf8_decode)]";"[(#TARIF_VALIDE|utf8_decode)]";"[(#PROMO|utf8_decode)]";"[(#TELEPHONE|utf8_decode)]";"[(#EMAIL|utf8_decode)]";"[(#SOCIETE|utf8_decode)]";"[(#NBR_PART|utf8_decode)]";"[(#CONJOINT|utf8_decode)]";"[(#MOBILE_DOM|utf8_decode)]";"[(#TYPE_PAIEMENT|utf8_decode)]";"[(#SOUSCRIPTEUR|utf8_decode)]"
</BOUCLE_les_inscriptions_ouvertes>
Il faut, souvent, coller toutes les lignes comme ci-dessus, sinon, on peut, ou pas, se retrouver avec des lignes vides dans le csv....
Si tu colles ça dans un squelette (nommé squelette_cvs), il suffit ensuite d’appeler la page spip.php ?page=squelette_cvs et le navigateur te proposera de sauvegarder le fichier (ici inscriptions.csv - voir le filename au dessus) ou de l’ouvrir avec le logiciel de ton choix....
Après, faut voir à quoi doit servir ton cvs, à être lu directement par une appli type MS/ Open Office (cet exemple) ou à autre chose, import dans une base sql par exemple, auquel cas, il faudra peut-être modifier l’encodage de caractères...
Par ailleurs, paulbe indique qu’il faut d’abord passer les champs par le filtre textebrut avant de les passer à la moulinette de utf8_decode
on peut aussi « jolifier » et simplifier le code (surtout dans la boucle) :
#CACHE{0}
#HTTP_HEADER{Content-Type: text/csv; charset=windows-1252/Winlatin1}
#HTTP_HEADER{Content-Disposition: attachment; filename=inscriptions.csv}
"ordre"; "date"; "nom_participant"; "prenom"; "paiement effectue"; "tarif_valide"; "promo"; "telephone"; "email"; "societe"; "nbr_part"; "conjoint"; "mobile_dom"; "type_paiement"; "souscripteur"
<BOUCLE_inscriptions_ouvertes(EVENEMENTS_LESINSCRIPTIONS) {id_evenement} {nbr_part > 0}>#VAL{#COMPTEUR_BOUCLE;"[(#DATE|affdate{'d-m-Y'})]"; "#NOM_PARTICIPANT"; "#PRENOM"; "#VERIFIER_PAIEMENT_INSCRIPTION{#ID_INSCRIPTION}"; "#TARIF_VALIDE"; "#PROMO"; "#TELEPHONE"; "#EMAIL"; "#SOCIETE"; "#NBR_PART"; "#CONJOINT"; "#MOBILE_DOM"; "#TYPE_PAIEMENT"; "#SOUSCRIPTEUR"}|textebrut|utf8_decode)]
</BOUCLE_inscriptions_ouvertes>
l’astuce consiste à ajouter #COMPTEUR_BOUCLE;
comme premier champ de #VAL
(donc aussi "ordre";
en première ligne de la feuille)
Technique de BoOz
Pour mes exports excel avec des champs utf-8 et des accents j’utilise un squelette
export.xls.html
#HTTP_HEADER{Content-Type: application/vnd.ms-excel; charset=UTF-16LE}
#HTTP_HEADER{Content-Disposition: attachment; filename=mon_export.xls}
ici du code avec des lignes et des champs séparés par des tabulations
et aussi dans un fichier export.xls_fonctions.php
spécial pour le squelette une fonction de conversion en utf16LE utilisée par Excel (merci Fil)
<?php
include_spip('inc/charsets');
init_mb_string();
ob_start('convert_utf16');
function convert_utf16($txt) {
return "\xFF\xFE" . mb_convert_encoding(trim($txt), 'UTF-16LE', 'UTF-8');
}
?>
dans mes_fonctions.php :
/**
* Le filtre extraire_email permet de récuperer toutes les adresses email
* présentes dans le texte retourné par la balise sur laquelle il s'applique.
*
* @param string $texte
* l'affichage retourné par la balise
*
* @return array
* un tableau dédoublonné des adresses email
**/
function extraire_email($texte) {
// /toto@truc.net étant une adresse valide ne pas confondre
// avec toto@truc.net dans href="mailto://toto@truc.net"
$texte = preg_replace(',mailto://,', '', $texte);
// contrôle : voir http://tools.ietf.org/html/rfc3696#page-6
if (preg_match_all(";[a-z0-9!#$%&'*+/=?^_<span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+e3x9fi1dKyg/OlwuW2EtejAtOSEjJCUmYW1wOycqKy89P15fPC9jb2RlPg=="></span>{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?;ims", $texte, $matches))
return array_unique($matches[0]);
return array();
}
exemple d’utilisation dans un squelette :
#SET{res, #ARRAY}
<BOUCLE_art(ARTICLES) {tout}>
[(#SET{res, #GET{res}|array_merge{#TEXTE|extraire_email}})]
</BOUCLE_art>
[(#GET{res}|foreach)]
Copiée sur la fonction du débugueur debusquer_contexte() (de ecrire/public/debusquer.php), cette fonction est à déposer dans le fichier mes_options.php
/**
* Une fonction récursive pour joliment afficher #ENV, #GET, #SESSION...
* en squelette : [(#ENV|bel_env)], [(#GET|bel_env)], [(#SESSION|bel_env)]
* ou encore [(#ARRAY{0,1, a,#SESSION, 1,#ARRAY{x,y}}|bel_env)]
*
* @param string|array $env
* si une string est passée elle doit être le serialize d'un array
*
* @return string
* une chaîne html affichant une <table>
**/
function bel_env($env) {
$env = str_replace(array('"', '''), array('"', '\''), $env);
if (is_array($env_tab = @unserialize($env))) {
$env = $env_tab;
}
if (!is_array($env)) {
return '';
}
$style = " style='border:1px solid #ddd;'";
$res = "<table style='border-collapse:collapse;'>\n";
foreach ($env as $nom => $val) {
if (is_array($val) || is_array(@unserialize($val))) {
$val = bel_env($val);
}
else {
$val = entites_html($val);
}
$res .= "<tr>\n<td$style><strong>". entites_html($nom).
" : </strong></td><td$style>" .$val. "</td>\n</tr>\n";
}
$res .= "</table>";
return $res;
}
ne reste plus qu’à l’appeler (que l’on soit loggé ou pas) dans un squelette avec :[(#ENV|bel_env)]
ou encore :[(#GET|bel_env)]
ou encore :[(#SESSION|bel_env)]
Noter que cette fonction est incluse dans le plugin « dev »
- Avec SPIP3 c’est même pas drôle :
#PROFONDEUR
dans une boucle (RUBRIQUES)
#INFO_x{objet, id_objet}
en dehors d’une boucle- Avec SPIP2.1 deux solutions :
<BOUCLE_niveau(HIERARCHIE) {id_rubrique} {tout}>
</BOUCLE_niveau>
#SET{niveau,#TOTAL_BOUCLE}
<//B_niveau>
et on récupère l’info avec un #GET{niveau}
function filtre_profondeur_dist($id_rubrique) {
$id_rubrique = intval($id_rubrique);
// sauver les calculs déjà faits
static $profs = array();
if (isset($profs[$id_rubrique])) {
return $profs[$id_rubrique];
}
// récupérer le parent.
$id_parent = sql_getfetsel('id_parent', 'spip_rubriques', 'id_rubrique='.$id_rubrique);
// pas de parent : id_rubrique n'existe pas
if (is_null($id_parent)) {
return '';
}
// parent zéro : on est tout en haut (racine)
if ($id_parent == '') {
return $profs[$id_rubrique] = 1;
}
// sinon, on trouve la profondeur du parent
$parent = filtre_profondeur_dist($id_parent);
$profs[$id_rubrique] = ($parent + 1);
return $profs[$id_rubrique];
}
à appeler dans un squelette avec : [(#ID_RUBRIQUE|profondeur)]
En spip2 le contexte d’appel d’un modèle n’était pas connu lors de la compilation de ce modèle. Ainsi un modèle appelé depuis le corps du texte de l’article 18 ne pouvait savoir qu’il était appelé depuis cet article précis.
Cela a changé avec spip3 ; désormais le modèle véhicule le contexte de son objet appelant. Cela veut dire que appelé depuis l’article 18, le modèle intègre la donnée id_article = 18 ; donnée que l’on peut donc utiliser dans le modèle avec :
#ID_ARTICLE
,#ENV{id_article}
,#ENV{args/id_article}
.Mais plusieurs cas peuvent se présenter :
<mon_modele1>
#ID_ARTICLE
vaudra la valeur id_article de l’article appelant (celui où se trouve l’appel du modèle)#ENV{id_article}
vaudra la valeur id_article de l’article appelant (celui où se trouve l’appel du modèle)#ENV{args/id_article}
vaudra ’’ (vide) <mon_modele1|id_article>
#ID_ARTICLE
vaudra la valeur id_article de l’article appelant (celui où se trouve l’appel du modèle)#ENV{id_article}
vaudra la valeur id_article de l’article appelant (celui où se trouve l’appel du modèle)#ENV{args/id_article}
vaudra ’’ (vide) <mon_modele1|id_article=>
#ID_ARTICLE
vaudra ’’ (vide)#ENV{id_article}
vaudra ’’ (vide)#ENV{args/id_article}
vaudra ’’ (vide) <mon_modele1|id_article=25>
#ID_ARTICLE
vaudra la valeur passée en argument : 25#ENV{id_article}
vaudra la valeur passée en argument : 25#ENV{args/id_article}
vaudra la valeur passée en argument : 25Et alors ?
Connaissant l’id de l’objet (article, rubrique, mot, site...) depuis lequel notre modèle est appelé, il est désormais possible d’utiliser des boucles utilisant cette information, de telle sorte que le modèle devient ainsi contextuel en affichant des informations dépendantes de son lieu d’appel. Un même modèle appelé depuis plusieurs articles retournera ainsi des informations spécifiques différentes pour chaque article.
Attention toutefois !
Comme désormais l’id de l’objet appelant (id_article, id_rubrique, id_mot...) est toujours transmis au modèle, il devient impossible d’utiliser dans le modèle une boucle avec le critère conditionnel {id_article?}
(ou {id_rubrique?}
, ou {id_mot?}
...)
L’astuce consistera à utiliser 2 critères dépendants de #ENV{args/id_article}
car seul #ENV{args/id_article}
nous renseigne sur l’argument « id_article » passé explicitement au modèle.
<BOUCLE_a_cond(ARTICLES) {id_article >= #ENV{args/id_article, 0}} {id_article <= #ENV{args/id_article, 99999999}} {"<br />"}>
#ID_ARTICLE - #TITRE
</BOUCLE_a_cond>
Explications :
#ENV{args/id_article}
existe c’est que id_article
a été explicitement renseigné dans l’appel du modèle (exemple : <mon_modele1|id_article=25>
).id_article >= 25
et id_article <= 25
; soit (tout le monde aura trouvé) : id_article = 25
;#ENV{args/id_article}
n’existe pas (id_article
n’a pas été explicitement renseigné dans l’appel du modèle : <mon_modele1>
), alors la boucle cherchera id_article >= 0
et id_article <= 99999999
; soit (tout le monde aura trouvé) : tous les articles.on reproduit bien là le comportement de {id_article?}
.
Si l’on veut que ce soit systématique sur l’ensemble du site, il faut ajouter dans un fichier « mes_fonctions.php » à placer dans« squelettes/ » :
define('_EXTRAIRE_LIENS', '//');
Mais on peut aussi vouloir ne réserver ce traitement qu’à certains articles.
Dans une copie de « article.html » que l’on aura placée dans « squelettes/ » on écrira alors :
[(#ID_ARTICLE|=={5}|ou{#ID_ARTICLE|=={62}}|oui)
[(#EVAL{define('_EXTRAIRE_LIENS', '//')})]
]
<BOUCLE_principale(ARTICLES) {id_article}>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1....
ceci pour que sur les seuls articles 5 et 62 les urls saisies directement ne soient pas transformées en lien cliquable. Les urls saisies dans tous les autres articles continuant de recevoir le traitement standard de spip seront donc cliquables.
SPIP permet d’aller piocher des données dans une base externe (à condition qu’elle ait été déclarée depuis ecrire/?exec=admin_tech
).
Il peut arriver que cette base externe contienne des tables dont les champs ont été définis en « CamelCase » (un mélange de minuscules et majuscules, comme par exemple : « NumOrdre »). Dans ce cas, SPIP est incapable d’afficher tant #NUMORDRE
que #CHAMP_SQL{NumOrdre}
dans la boucle.
Pour remédier à ça, nous fabriquerons une boucle DATA ainsi :
#SET{req, "SELECT table_ext.ChampCamelCase FROM table_ext AS <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+dGFibGVfZXh0PC9jb2RlPg=="></span>"}
<BOUCLE_test(DATA) {source sql, connecteur:#GET{req}}>
#VALEUR{ChampCamelCase}<br />
</BOUCLE_test>
Dans ce code, connecteur
correspond au nom de la base externe déclarée (on peut le retrouver à partir de ecrire/?exec=admin_tech
dans la liste « Bases supplémentaires déjà interrogeables :» ou en parcourant le dossier config/
pour y repérer le fichier de connexion « nom_du connecteur.php
»
Récupère les textes des intertitres et génère une table des matières. Comme le javascript se lance après l’affichage de la page, il peut y avoir un petit saut de l’affichage, selon l’endroit où la table des matières est insérée dans le HTML.
L’image utilisée pour le retour au sommaire encodée en base64 et transmise par le javascript.
/*
* Générer une table des matières
* à partir des h3 ou .h3 ou .tdm_ajouter
* sauf si class="no_tdm"
* l'afficher dans l'élément d'id="tdm" par défaut
*
* Auteur : cy_altern
*/
function generer_tdm(id_tdm){
if (jQuery("h3, .h3, .tdm_ajouter").length < 1)
return;
if (id_tdm == '')
id_tdm = "tdm";
var html_tdm = '<a name="tdm"></a><h2>Table des matieres</h2><ul>' + "\r\n";
var cteur = 1;
// générer le contenu de la tdm et ajouter liens et ancres
jQuery("h3, .h3, .tdm_ajouter").each( function(){
if (jQuery(this).hasClass("no_tdm"))
return;
var titre = jQuery(this).text();
html_tdm += '<li><a href="#tdm_' + cteur + '">' + titre.substr(0,250) + '</a></li>' + "\r\n";
// l'image du lien vers top encodée en base64
var img_tdm = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAANbY1E9YMgAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKlSURBVHjaYmAAgu/fv9szfPjwzn7JkkX/AQKIEcwDAsa3b1//Z2ZmZgAIILC8ra3t/+fPn/7X09P9z/ju3Rv77z++Hfj+7QeDoKAgA0AAwfVAwQMGPT29/9XV1f/l5OT+z5w58z/D6zcv/wNl/gOtAWOWr9++MLx4+Yzhz58/DIxMjAwAAYgcYxwEgSgKzv8LEvQQlngiuCs0FCZ6jKWF+LFxlw7ZjYnNvGYyeb/GPiZNcmb/84CmbZob73XF+4m27bJwri95NcYPj+cddcIw9JgZIQRetrBtkfT7utjsq1NFWZQ4V6CqqCgiwlcAgdwgDzRJgQEL4OTkPAhU8PX///+MQNUwYagjgRQjI5MD06ZNmxnCw8MY3NzcwRItLa1AndwMGhqaEPVAf//n5+f//+3bV3DogEwEBWx1ddV/cBC8ev3i/5u3r/4DHft/6tQp/61trOGhBookxsdPHvxnYQG5nJnBxNiM4eDB/QxAE8E+AAIHll+/f4GtAqphWL9hLQMvLy/Qmf/BbgUpYvn77y8DTJG0tBTDv///GJj+MYHDGOi3BwAB+iZ/HQKCIA7PxtVXiNBZL+LiAWgoeRKPIRpeRDSoheIaWtdcyW2CBjtmfuJPTpjLJnfJ3szsN9+CA7Pfmfc5c8HfX1pAOEhbNZ1VzblMrMrAhjn/y3dkst85RxiAojgLKs2rmNI0FWRnSKVyPQTr83Q64fF4hH29Xpe32w0wBprGS9nT6QifDKRiiuOYWq0mJUlC1lqKogjV61GdbNW+ugnUJpEcgpinILI0weGwB/LZbE6dThsFKuUyLHsGfL1cLzD8druSF+ree1qtlxSGIdZwOHiNRuHxByVcM9F1EYimehELMnDomusIjzEfx8ScG3hT538p/Sd2xWIpuQNSgVvHc9dkkwAAAABJRU5ErkJggg==" />';
jQuery(this).append('<a href="#tdm" title="">' + img_tdm +'</a>').before('<a name="tdm_' + cteur + '"></a>');
cteur += 1;
});
html_tdm += '</ul>' + "\r\n";
// insérer la tdm
jQuery("#tdm").html(html_tdm);
}
jQuery(document).ready(generer_tdm);
[(#VAL{IMG/}|preg_files{rubon#ID_DE_LA_RUBRIQUE\.(jpg|gif|png)}|table_valeur{0}|balise_img|image_reduire{120,120})]
où #ID_DE_LA_RUBRIQUE
peut être soit un nombre écrit « en dur » dans le squelette, soit un #ID_RUBRIQUE ou #ID_PARENT, soit encore un #GET défini antérieurement.
peut aussi s’appliquer à un article (on utilisera « arton » au lieu de « rubon ») ou un site syndiqué (« siteon ») ou un auteur (« auton »)...
Nous créons le modèle « squelettes/modeles/logo.html » :
[(#SET{objet, #ENV{article}|?{
arton#ENV{article}, #ENV{rubrique}|?{
rubon#ENV{rubrique}, #ENV{auteur}|?{
auton#ENV{auteur}, #ENV{site}|?{
siteon#ENV{site}, #ENV{breve}|?{
breveon#ENV{breve}}
}
}
}
}
})]
[(#VAL{IMG/}|preg_files{#GET{objet}\.(jpg|gif|png)}|table_valeur{0})]
Ce modèle retourne le chemin d’accès à un logo stocké à la racine de IMG/
par exemple : « IMG/arton25.png » ou « IMG/auton1.jpg » ... et ce, quel que soit l’objet (article, rubrique, auteur...) et quelle que soit l’extension du fichier logo (gif, png ou jpg).
On peut l’utiliser (y compris en dehors d’une boucle) pour afficher l’image logo de n’importe quel objet :
[(#MODELE{logo, article=25}|balise_img)]
ou un lien vers cette image logo :
<a href="[(#MODELE{logo, article=25})]">...</a>
Nous pouvons ainsi avoir un logo qui pointe vers son fichier :
<a href="[(#MODELE{logo, article=25}|url_absolue)]">
[(#MODELE{logo, article=25}|balise_img|image_reduire{120,120})]
</a>
L’usage de {critère ?opérateur #ENV{variable}}
est limité par l’obligation que variable
soit homonyme de critère
.
On peut (on doit) écrire {titre ?= #ENV{titre}}
mais pas {titre ?= #ENV{nimportequoi}}
.
Pour contrecarrer cette interdiction, on peut néanmoins, dans certains cas, utiliser des écritures particulières pour chaque opérateur.
LIKE
:
#SET{ok, #ENV{nimportequoi}}
<BOUCLE_a(ARTICLES){chapo LIKE %#GET{ok}%}>
#CHAPO
</BOUCLE_a>
=
:
#SET{ok, #ENV{nimportequoi}|sinon{.*}}
<BOUCLE_b(ARTICLES){titre == #GET{ok}}>
#TITRE
</BOUCLE_b>
<
sur un champ numérique (id_article) :
#SET{ok, #ENV{nimportequoi}|sinon{999999}}
<BOUCLE_c(ARTICLES){id_article < #GET{ok}}>
#ID_ARTICLE
</BOUCLE_c>
<
sur un champ non numérique (texte ou date) :
#SET{ok, #ENV{nimportequoi}|sinon{zzzzzz}}
<BOUCLE_d(ARTICLES){titre < #GET{ok}}>
#TITRE
</BOUCLE_d>
>
(là, c’est facile...) :
<BOUCLE_e(ARTICLES){id_article > #ENV{nimportequoi}}>
#ID_ARTICLE
</BOUCLE_e>
<BOUCLE_f(ARTICLES){titre > #ENV{nimportequoi}}>
#TITRE
</BOUCLE_f>
Pouvoir afficher ou tester n’importe où dans le squelette (en dehors de toute boucle et y compris dans un MODELE ou un INCLURE) « l’objet » et l’« id_objet » concerné par la page courante.
dans mes_fonctions.php :
/*
* Cette fonction retourne soit l'objet soit l'id_objet concerné par
* la page en cours et ce, en-dehors de toute boucle.
* Elle se base sur l'url #SELF indépendamment du type d'url choisi.
*
* appelable par :
* [(#SELF|reference{objet})]
* [(#SEL|reference{id_objet})]
* [(#SELF|reference{rubrique})]
* [(#SEL|reference{id_auteur})]
*
* permet des choses comme :
* [(#SELF|reference{rubrique}) je suis sur une page rubrique]
* [(#SELF|reference{id_auteur}) je suis sur la page de l'auteur n]
* [(#INFO_TITRE{#SELF|reference{objet}, #SELF|reference{id_objet}})]
*
* @param string $self
* l'url retournée par #SELF
* @param string $req
* le mot générique 'objet'
* le mot générique 'id_objet'
* un nom littéral d'objet ('rubrique', 'auteur', 'evenement'...)
* un nom littéral d'id_objet ('id_rubrique', 'id_auteur'...)
*
* @return string
* soit le nom réel de 'objet' ('article', 'rubrique', 'evenement'...) ;
* soit la valeur de 'id_objet' ;
* soit TRUE si le nom littéral d'objet demandé est bien celui de
* l'objet en cours
* soit la valeur de l'id_objet explicitement demandé
* soit FALSE
*/
function reference($self, $req = '') {
$ret = array();
$ret['obj'] = $ret['id'] = '';
if (lire_config('type_urls') != 'arbo') {
$elements = urls_decoder_url($self);
if(!empty($elements[0])) {
$ret['obj'] = $elements[0];
$ret['id'] = $elements[1]['id_' . $ret['obj']];
}
}
else {
$elements = urls_arbo_dist($self,'');
if(!empty($elements[0])) {
$ret['obj'] = $elements[1];
$ret['id'] = $elements[0]['id_' . $ret['obj']];
}
}
if ($req == $ret['obj']) return ' ';
elseif ($req == 'id_' . $ret['obj']) return $ret['id'];
elseif ($req == 'objet') return $ret['obj'];
elseif ($req == 'id_objet') return $ret['id'];
return '';
}
/* La balise #SELF_REF{arg}
*
* S'utilise dans les squelettes par appel direct accompagnée
* d'un seul argument :
* le mot générique 'objet'
* le mot générique 'id_objet'
* un nom littéral d'objet ('rubrique', 'auteur', 'evenement'...)
* un nom littéral d'id_objet ('id_rubrique', 'id_auteur'...)
*
* par exemple :
* [c'est ok : je m'affiche dans la page article de l'article n°
* (#SELF_REF{id_article})]
*
*/
function balise_SELF_REF($p) {
$p->code = interprete_argument_balise(1, $p);
$p->code = 'reference(self(),' .$p->code . ')';
$p->interdire_scripts = false;
return $p;
}
dans mes_fonctions.php :
/*
* Le filtre : [(#DATE|decompte)] retourne la valeur de
* l'intervalle de temps entre la date courante et la date passée
* en argument. Si l'écart est négatif (#DATE est passée) la fonction
* préfixe le retour d'un signe 'moins'.
*
* @param string $dat_x
* une date quelconque : 'aaaa-mm-jj hh:mm:ss' ou 'aaaa-mm-jj'
*
* @return string
* l'intervalle de temps entre la date courante et celle passée en argument.
*/
function decompte($dat_x) {
$aujourd_hui = new DateTime();
$date_compare = new DateTime($dat_x);
$ect = $aujourd_hui->diff($date_compare);
$sens = ($ect->invert == 1) ? '- ' : '';
$format = $sens . '%1$04d-%2$02d-%3$02d %4$02d:%5$02d:%6$02d';
return sprintf($format, $ect->y, $ect->m, $ect->d, $ect->h, $ect->i, $ect->s);
}
/* La balise : #DECOMPTE{aaaa-mm-jj hh:mm:ss}
*
* Affiche (sous la forme 'aaaa-mm-jj hh:mm:ss') la valeur de l'intervalle
* de temps entre la date courante et la date passée en paramètre.
* Si 'hh:mm:ss' est omis dans l'argument, 00:00:00 sera utilisé.
* Si l'écart est négatif le retour est préfixé d'un signe 'moins'.
*
*/
function balise_DECOMPTE($p) {
$p->code = interprete_argument_balise(1, $p);
$p->code = 'decompte(' . $p->code . ')';
$p->interdire_scripts = false;
return $p;
}
Comme indiqué dans la documentation technique de SPIP, on peut utiliser un pipeline sans avoir à passer systématiquement par la création d’un plugin.
Après un rapide coup d’oeil dans dans la liste des pipelines, nous nous arrêtons sur le pipeline pre_insertion qui permet d’intervenir au moment de l’insertion d’un nouvel élément éditorial.
Suivant les indications mentionnées plus haut, on ajoute ceci dans le fichier config/mes_options.php :
// Utilisation du pipeline pre_insertion qui va exécuter la fonction ma_fonction_pre_insertion
$GLOBALS['spip_pipeline']['pre_insertion'] .= "|ma_fonction_pre_insertion";
// Fonction qui passe le statut à l'état publié des articles de la rubrique X au moment de l'insertion
function ma_fonction_pre_insertion($flux) {
if ($flux['args']['table']=='spip_articles' && $flux['data']['id_rubrique'] == X )) {
$flux['data']['statut'] = 'publie';
}
return $flux;
}
En adaptant le contenu de la fonction ci-dessus, on peut appliquer ce traitement à tout type d’objet éditorial, ainsi que modifier un autre champ que le statut.
Pour finir, Il est recommandé de se rendre sur la page de gestion des plugins dans l’espace privé. Ceci va avoir pour effet de rafraîchir le cache pipeline (fichier tmp/cache/charger_pipelines.php). Les modifications effectuées ci-dessus seront alors prises en compte.
À partir de ce qui est expliqué ici, on peut systématiser l’application d’un traitement particulier, sur les balises issues des boucles.
Dans notre exemple, si on souhaite cibler la balise #TEXTE des articles pour lui appliquer un traitement particulier avant affichage dans une boucle :
Dans le fichier config/mes_options.php, on ajoute :
$table_des_traitements['TEXTE']['articles'] = 'mafonction(%s)';
function mafonction($texte) {
// rajouter ici le traitement souhaité
// ...
// ...
return $texte;
}
Selon ses propres besoins, on peut bien évidement utiliser une fonction existante de spip (un filtre par exemple), au lieu de déclarer sa propre fonction.
Comme pour les pipelines, il est nécessaire d’aller dans l’espace privé -> gestion des plugins pour rafraîchir le cache.
On peut vouloir utiliser les résultats retournés par une BOUCLE en-dehors de cette BOUCLE (dans un tout autre endroit du squelette). L’exemple ci-dessous permet de disposer d’un tableau (array php) dont on pourra extraire les informations souhaitées.
Déclarer le tableau et le remplir avec une boucle :
#SET{tab, #ARRAY}
<BOUCLE_u(ARTICLES) {tout}>
[(#SET{tab, #GET{tab}|push{#LISTE{#ID_ARTICLE, #TITRE}}})]
</BOUCLE_u>
À ce stade, nous avons un tableau peuplé ainsi :
+---+---+------------------+
! 0 ! 0 ! 1 !
! ! 1 ! titre article 1 !
+---+---+------------------+
! 1 ! 0 ! 2 !
! ! 1 ! titre article 2 !
+---+---+------------------+
! 2 ! 0 ! 3 !
! ! 1 ! titre article 4 !
+---+---+------------------+
Utiliser les informations de ce tableau :
[(#GET{tab}|table_valeur{1/1})] affichera «titre article 2»
[(#GET{tab}|table_valeur{2/0})] affichera «3»