Carnet Wiki

Astuces longues pour SPIP

Version 96 — il y a 5 mois — raptor

Voir aussi : les Astuces courtes.


  1. Partager des données entre plusieurs squelettes inclus (Zpip)
  2. ZPIP : faire un inclure de squelette suivant une condition
  3. Une boucle qui ne doit être exécutée que dans un certain contexte
  4. Afficher la date du jour
  5. Jouons avec les dates
  6. Exporter au format CSV
  7. Extraire les adresses email contenues dans un(des) texte(s)
  8. Une fonction pour joliment afficher les variables de #ENV, #GET ou #SESSION
  9. Connaître le niveau de profondeur d’une rubrique
  10. Les modèles et leur contexte en SPIP3
  11. Ne pas transformer en lien cliquable les urls saisies en dur dans un texte
  12. Utiliser une boucle DATA pour récupérer des champs en CamelCase sur une base externe
  13. Table des matières tout en javascript
  14. Afficher n’importe où (hors BOUCLE) le LOGO d’une rubrique particulière
  15. Afficher le logo (même hors boucle) d’un « objet spip » en utilisant un modèle
  16. Pouvoir utiliser un critère conditionnel avec un nom de variable quelconque
  17. Retrouver l’objet et l’id_objet concerné par la page en cours
  18. Afficher l’intervalle de temps entre une date donnée et la date courante
  19. Auto publier les articles d’une rubrique par l’intermédiaire d’un pipeline (Utilisation hors plugin)
  20. Automatiser un traitement particulier sur la balise #TEXTE des articles
  21. Remplir un tableau de tableaux avec une boucle
  22. Présenter un usage de la balise <code> à l’intérieur même de balises <code>
  23. Utiliser la boucle DATA pour afficher une liste d’albums Flickr
  24. Ne boucler sur la table sql d’un plugin que si ce plugin est installé et activé.
  25. Avertir un administrateur restreint qu’il administre telle ou telle rubrique.
  26. Un critère de tri alphabétique qui ne prend pas en compte les num titre
  27. Découper un texte en colonnes selon sa taille
  28. Se passer du filtre |_T dans les squelettes
  29. Afficher les objets selon une date en paramètre URL
  30. Créer un lien pour forcer le téléchargement du fichier d’un document
  31. Afficher les initiales du nom d’un auteur
  32. Afficher le numéro de version SPIP dans un article, une rubrique...
  33. Un critère pour comparer l’âge en minutes, heures, mois ou années
  34. Surcharger deux fois une autorisation
  35. Ne pas charger le jQuery fourni par SPIP

1- Partager des données entre plusieurs squelettes inclus (Zpip)

Références balises #SET et #GET balise #FILTRE

Question :
Dans ZPIP, la structure de base est définie à coup d’inclusion de grands blocs logiques du style

  1. ... <div id="contenu">
  2.       <INCLURE{fond=contenu/#ENV{type}, env}>
  3.     </div>
  4.     <div id="navigation">
  5.       <INCLURE{fond=navigation/#ENV{type}, env}>
  6.       <INCLURE{fond=extra/#ENV{type}, env}>
  7.     </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 :

  1. [(#SET{myvars, [(#INCLURE{fond=vars/#ENV{type}, env})]})]

Dans vars/rubrique.html :

  1. #CACHE{3600}
  2. [(#SET{vars, #ARRAY{
  3.    objet, rubrique,
  4.    id_objet, #ENV{id_rubrique},
  5.    titreRubrique, Rubriques,
  6.    titrePage, #ENV{id_rubrique}?
  7. }})]
  8.  
  9.  
  10. <BOUCLE_rub(RUBRIQUES) {id_rubrique}>
  11.     [(#SET{vars, #GET{vars}|array_merge{#ARRAY{titreRubrique, #TITRE, idRubrique, #ID_RUBRIQUE}}})]
  12. </BOUCLE_rub>
  13.  
  14.  
  15. [(#GET{vars}|serialize)]
  16. #FILTRE{trim}

Puis dans structure.html :
<INCLURE{fond=extra/#ENV{type}, env}>


2- ZPIP : faire un inclure de squelette suivant une condition

Références filtre |non

dans le body de ZPIP, charger ou non un INCLURE qui contient #ENV{type}

  1. [(#ID_RUBRIQUE|=={10}|?{
  2.  ''
  3. ,
  4.  <INCLURE{fond=extra/#ENV{type}, env}>
  5. })]

ne fonctionne pas à cause de accolades de #ENV, donc contourner avec

  1. [(#ID_RUBRIQUE|=={10}|non) <INCLURE{fond=extra/#ENV{type}, env}> ]

3- Une boucle qui ne doit être exécutée que dans un certain contexte

Références balise #ENV balises #SET et #GET critère {si…}

Tout le monde le sait (beaucoup le déplorent) :

  1. [(#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...

  1. #SET{test, #ENV{truc}|=={coucou}|?{0, 999999}}
  2. <BOUCLE_de_test(TABLES) {id_table > #GET{test}}>
  3. coucou !
  4. </BOUCLE_de_test>

Explications :

  • en premier lieu nous définissons notre test (présence de la variable « truc » dans l’environnement) et nous affectons à une variable « test » la valeur« 0 » ou « 999999 ».
    à ce stade, #GET{test} vaut donc soit « zéro », soit « neuf cent quatre-vingt-dix-neuf mille neuf cent quatre-vingt-dix-neuf ».
  • nous lançons alors la boucle dans laquelle nous demandons que l’« id_table » soit supérieur à notre valeur de test définie juste au-dessus.
    et le tour est joué...

4- Afficher la date du jour

Références balise #DATE balise #ENV balise #VAL

Dans un squelette, afficher la date du jour (la date courante) n’est pas toujours aisé.
deux variables peuvent entrer en jeu :

  • la date retournée par la boucle dans laquelle on veut afficher notre date du jour
  • une variable nommée date passée en url ou par le biais d’un formulaire

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}


5- Jouons avec les dates

Références balise #VAL balise #DATE filtre |affdate

<blockquote class="spip">

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 :

  1. [(#VAL{Y-m-d}|date{#VAL{next Thursday}|strtotime{#DATE|affdate{U}}})]

-  Le nom du jour de l’avant-veille de #DATE :

  1. [(#VAL{Y-m-d}|date{#VAL{-2 days}|strtotime{#DATE|affdate{U}}}|nom_jour)]

-  La date de lundi prochain :

  1. [(#SET{lundi_prochain, #VAL{Y-m-d 00:00:00}|date{#VAL{next Monday}|strtotime{#DATE|affdate{U}}}})]
  2. #GET{lundi_prochain}

-  La date du dimanche d’après lundi prochain :

  1. [(#SET{dimanche_apres, #VAL{Y-m-d 23:59:59}|date{#VAL{+6days}|strtotime{#GET{lundi_prochain}|affdate{U}}}})]
  2. #GET{dimanche_apres}

-  Afficher le label nouveau ! pour un article publié ou modifié il y a moins de 10 jours :

  1. #SET{periode, -10days}
  2. #TITRE[ (#DATE_MODIF|>{#VAL{Y-m-d H:i:s}|date{#GET{periode}|strtotime}}|oui) nouveau !]

et _fil_ (sur irc) propose encore plus simple :

  1. #TITRE[ (#DATE|strtotime|>{#VAL{-10days}|strtotime}|?{ nouveau !, ''})]

6- Exporter au format CSV

Références balise #VAL filtre |textebrut balise #COMPTEUR_BOUCLE

Triton nous confie un petit exemple qui marche pour MS Excel et openOffice (il y a des particularités d’encodage de caractères) :

  1. #CACHE{0} #HTTP_HEADER{Content-Type: text/csv; charset=windows-1252/Winlatin1} #HTTP_HEADER{Content-Disposition: attachment; filename=inscriptions.csv}
  2. "date"; "nom_participant";"prenom";"paiement effectue"; "tarif_valide"; "promo";"telephone"; "email";"societe"; "nbr_part";"conjoint"; "mobile_dom";"type_paiement"; "souscripteur"
  3. <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)]"
  4. </BOUCLE_les_inscriptions_ouvertes>
  • le http_header indique qu’il s’agit d’un document csv encodé pour MS excel
  • la premiere ligne c’est les têtières (en tete de colonne) du fichier csv
  • La boucle écrit ensuite le contenu ligne par ligne

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) :

  1. #CACHE{0}
  2. #HTTP_HEADER{Content-Type: text/csv; charset=windows-1252/Winlatin1}
  3. #HTTP_HEADER{Content-Disposition: attachment; filename=inscriptions.csv}
  4.  
  5.  
  6. "ordre"; "date"; "nom_participant"; "prenom"; "paiement effectue"; "tarif_valide"; "promo"; "telephone"; "email"; "societe"; "nbr_part"; "conjoint"; "mobile_dom"; "type_paiement"; "souscripteur"
  7.  
  8.  
  9. <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)]
  10. </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

  1. #HTTP_HEADER{Content-Type: application/vnd.ms-excel; charset=UTF-16LE}
  2. #HTTP_HEADER{Content-Disposition: attachment; filename=mon_export.xls}
  3.  
  4.  
  5. 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)

  1. <?php
  2. include_spip('inc/charsets');
  3. init_mb_string();
  4. ob_start('convert_utf16');
  5. function convert_utf16($txt) {
  6.    return "\xFF\xFE" . mb_convert_encoding(trim($txt), 'UTF-16LE', 'UTF-8');
  7. }
  8. ?>

Technique de RealET

C’est un exemple qui va prendre des valeurs dans des réponses du plugin formidable (avec dédoublonage sur l’email).

L’important, c’est :

  • l’envoi explicite du BOM UTF-8 : #CHR{239}#CHR{187}#CHR{191}
  • Et l’envoi du charset UTF-8
  1. #CACHE{0}#CHR{239}#CHR{187}#CHR{191}
  2. #HTTP_HEADER{Content-Encoding: #CHARSET}
  3. #HTTP_HEADER{Content-Type: text/csv; charset=#CHARSET}
  4. #HTTP_HEADER{Content-Disposition: attachment; filename=exportCSV.csv}
  5. <B_reponse>"Qui";"Quand";"Où"<BOUCLE_reponse(FORMULAIRES_REPONSES){id_formulaire=3}{!par date}><B_reponse_champ1>
  6. <BOUCLE_reponse_champ1(FORMULAIRES_REPONSES_CHAMPS){id_formulaires_reponse}{nom=input_2}>["(#VALEUR|unique)]</BOUCLE_reponse_champ1> <BOUCLE_reponse_champ2(FORMULAIRES_REPONSES_CHAMPS){id_formulaires_reponse}{nom=input_1}>[<(#VALEUR|unique)>";]</BOUCLE_reponse_champ2><BOUCLE_reponse_champ3(FORMULAIRES_REPONSES_CHAMPS){id_formulaires_reponse}{nom=radio_1}>["(#VALEUR)";#SET{ou,Évry}]</BOUCLE_reponse_champ3><BOUCLE_reponse_champ4(FORMULAIRES_REPONSES_CHAMPS){id_formulaires_reponse}{nom=radio_2}>["(#VALEUR)";#SET{ou,Kourou}]</BOUCLE_reponse_champ4>"#GET{ou}"</B_reponse_champ1></BOUCLE_reponse></B_reponse>

Télécharger


7- Extraire les adresses email contenues dans un(des) texte(s)

Références fichier mes_fonctions.php

dans mes_fonctions.php :

  1. /**
  2.  * Le filtre extraire_email permet de récuperer toutes les adresses email
  3.  * présentes dans le texte retourné par la balise sur laquelle il s'applique.
  4.  *
  5.  * @param string $texte
  6.  *     l'affichage retourné par la balise
  7.  *             
  8.  * @return array
  9.  *     un tableau dédoublonné des adresses email
  10. **/
  11. function extraire_email($texte) {
  12.     //  /toto@truc.net étant une adresse valide ne pas confondre
  13.     //  avec toto@truc.net dans href="mailto://toto@truc.net"
  14.     $texte = preg_replace(',mailto://,', '', $texte);
  15.  
  16.  
  17. //  contrôle : voir http://tools.ietf.org/html/rfc3696#page-6
  18.     if (preg_match_all(";[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?;ims", $texte, $matches))
  19.         return array_unique($matches[0]);
  20.     return array();
  21. }

exemple d’utilisation dans un squelette :

  1. #SET{res, #ARRAY}
  2. <BOUCLE_art(ARTICLES) {tout}>
  3. [(#SET{res, #GET{res}|array_merge{#TEXTE|extraire_email}})]
  4. </BOUCLE_art>
  5.  
  6.  
  7. [(#GET{res}|foreach)]

8- Une fonction pour joliment afficher les variables de #ENV, #GET ou #SESSION

Références fichier mes_options.php

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

  1. /**
  2.  * Une fonction récursive pour joliment afficher #ENV, #GET, #SESSION...
  3.  *    en squelette : [(#ENV|bel_env)], [(#GET|bel_env)], [(#SESSION|bel_env)]
  4.  *    ou encore [(#ARRAY{0,1, a,#SESSION, 1,#ARRAY{x,y}}|bel_env)]
  5.  *
  6.  * @param string|array $env
  7.  *    si une string est passée elle doit être le serialize d'un array
  8.  *
  9.  * @return string
  10.  *    une chaîne html affichant une <table>
  11. **/
  12. function bel_env($env) {
  13.     $env = str_replace(array('"', '&#039;'), array('"', '\''), $env);
  14.     if (is_array($env_tab = @unserialize($env))) {
  15.         $env = $env_tab;
  16.     }
  17.     if (!is_array($env)) {
  18.         return '';
  19.     }
  20.     $style = " style='border:1px solid #ddd;'";
  21.     $res = "<table style='border-collapse:collapse;'>\n";
  22.     foreach ($env as $nom => $val) {
  23.         if (is_array($val) || is_array(@unserialize($val))) {
  24.             $val = bel_env($val);
  25.         }
  26.         else {
  27.             $val = entites_html($val);
  28.         }
  29.         $res .= "<tr>\n<td$style><strong>". entites_html($nom).
  30.                    " : </strong></td><td$style>" .$val. "</td>\n</tr>\n";
  31.     }
  32.     $res .= "</table>";
  33.     return $res;
  34. }

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 ».


9- Connaître le niveau de profondeur d’une rubrique

Références balise #PROFONDEUR balise #INFO_xx boucle (HIERARCHIE) fichier mes_fonctions.php

-  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 :

    1. <BOUCLE_niveau(HIERARCHIE) {id_rubrique} {tout}>
    2. </BOUCLE_niveau>
    3.       #SET{niveau,#TOTAL_BOUCLE}
    4. <//B_niveau>

    et on récupère l’info avec un #GET{niveau}

  • une fonction à poser dans squelettes/mes_fonctions.php :
    1. function filtre_profondeur_dist($id_rubrique) {
    2.     $id_rubrique = intval($id_rubrique);
    3.     // sauver les calculs déjà faits
    4.     static $profs = array();
    5.     if (isset($profs[$id_rubrique])) {
    6.       return $profs[$id_rubrique];
    7.     }
    8.     // récupérer le parent.
    9.     $id_parent = sql_getfetsel('id_parent', 'spip_rubriques', 'id_rubrique='.$id_rubrique);
    10.     // pas de parent : id_rubrique n'existe pas
    11.     if (is_null($id_parent)) {
    12.       return '';
    13.     }
    14.     // parent zéro : on est tout en haut (racine)
    15.     if ($id_parent == '') {
    16.       return $profs[$id_rubrique] = 1;
    17.     }
    18.     // sinon, on trouve la profondeur du parent
    19.     $parent = filtre_profondeur_dist($id_parent);
    20.     $profs[$id_rubrique] = ($parent + 1);
    21.     return $profs[$id_rubrique];
    22. }

    à appeler dans un squelette avec : [(#ID_RUBRIQUE|profondeur)]


10- Les modèles et leur contexte en SPIP3

Références les <modele> balise #ENV

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 :

  • soit #ID_ARTICLE,
  • soit #ENV{id_article},
  • soit encore #ENV{args/id_article}.

Mais plusieurs cas peuvent se présenter :

  • modèle appelé depuis un article sans argument explicite :
    <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)
  • modèle appelé depuis un article avec argument explicite 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)
  • modèle appelé depuis un article avec argument explicite sans valeur :
    <mon_modele1|id_article=>
    • #ID_ARTICLE vaudra ’’ (vide)
    • #ENV{id_article} vaudra ’’ (vide)
    • #ENV{args/id_article} vaudra ’’ (vide)
  • modèle appelé depuis un article avec argument explicite renseigné :
    <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 : 25

Et 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.

  1. <BOUCLE_a_cond(ARTICLES) {id_article >= #ENV{args/id_article, 0}} {id_article <= #ENV{args/id_article, 99999999}} {"<br />"}>
  2. #ID_ARTICLE - #TITRE
  3. </BOUCLE_a_cond>

Explications :

  • si #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>).
    Dans ce cas, la boucle cherchera id_article >= 25 et id_article <= 25 ; soit (tout le monde aura trouvé) : id_article = 25 ;
  • si #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?}.


11- Ne pas transformer en lien cliquable les urls saisies en dur dans un texte

Références fichier mes_fonctions.php filtre |ou balise #EVAL

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/ » :

  1. 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 :

  1. [(#ID_ARTICLE|=={5}|ou{#ID_ARTICLE|=={62}}|oui)
  2.     [(#EVAL{define('_EXTRAIRE_LIENS', '//')})]
  3. ]
  4. <BOUCLE_principale(ARTICLES) {id_article}>
  5. <!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.


12- Utiliser une boucle DATA pour récupérer des champs en CamelCase sur une base externe

Références boucle (DATA) base externe

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 :

  1. #SET{req, "SELECT table_ext.ChampCamelCase FROM table_ext AS `table_ext`"}
  2. <BOUCLE_test(DATA) {source sql, connecteur:#GET{req}}>
  3.   #VALEUR{ChampCamelCase}<br />
  4. </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 »


13- Table des matières tout en javascript

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.

  1. /*
  2.  * Générer une table des matières
  3.  * à partir des h3 ou .h3 ou .tdm_ajouter
  4.  * sauf si class="no_tdm"
  5.  * l'afficher dans l'élément d'id="tdm" par défaut
  6.  *
  7.  * Auteur : cy_altern
  8.  */
  9. function generer_tdm(id_tdm){
  10.         if (jQuery("h3, .h3, .tdm_ajouter").length < 1)
  11.                 return;
  12.         if (id_tdm == '')
  13.                 id_tdm = "tdm";
  14.         var html_tdm = '<a name="tdm"></a><h2>Table des matieres</h2><ul>' + "\r\n";
  15.         var cteur = 1;
  16.  
  17.  
  18. // générer le contenu de la tdm et ajouter liens et ancres
  19.         jQuery("h3, .h3, .tdm_ajouter").each( function(){
  20.                 if (jQuery(this).hasClass("no_tdm"))
  21.                         return;
  22.                 var titre = jQuery(this).text();
  23.                 html_tdm += '<li><a href="#tdm_' + cteur + '">' + titre.substr(0,250) + '</a></li>' + "\r\n";
  24.  
  25.  
  26. // l'image du lien vers top encodée en base64
  27.                 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==" />';
  28.                 jQuery(this).append('<a href="#tdm" title="">' + img_tdm +'</a>').before('<a name="tdm_' + cteur + '"></a>');
  29.  
  30.  
  31. cteur += 1;
  32.         });
  33.         html_tdm += '</ul>' + "\r\n";
  34.  
  35.  
  36. // insérer la tdm
  37.         jQuery("#tdm").html(html_tdm);
  38. }
  39. jQuery(document).ready(generer_tdm);

14- Afficher n’importe où (hors BOUCLE) le LOGO d’une rubrique particulière

Références balise #VAL fonction preg_files() filtre |table_valeur filtre |balise_img

  1.  
  2. [(#VAL{IMG/}|preg_files{rubon#ID_DE_LA_RUBRIQUE\.(jpg|gif|png)}|table_valeur{0}|balise_img|image_reduire{120,120})]
  3.  

#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 »)...


15- Afficher le logo (même hors boucle) d’un « objet spip » en utilisant un modèle

Références balise #VAL filtre |table_valeur filtre |balise_img

Nous créons le modèle « squelettes/modeles/logo.html » :

  1. [(#SET{objet, #ENV{article}|?{
  2.    arton#ENV{article}, #ENV{rubrique}|?{
  3.        rubon#ENV{rubrique}, #ENV{auteur}|?{
  4.            auton#ENV{auteur}, #ENV{site}|?{
  5.                siteon#ENV{site}, #ENV{breve}|?{
  6.                    breveon#ENV{breve}}
  7.                }
  8.            }
  9.        }
  10.    }
  11. })]
  12. [(#VAL{IMG/}|preg_files{#GET{objet}\.(jpg|gif|png)}|table_valeur{0})]
  13.  

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 :

  1.   [(#MODELE{logo, article=25}|balise_img)]
  2.  

ou un lien vers cette image logo :

  1.   <a href="[(#MODELE{logo, article=25})]">...</a>
  2.  

Nous pouvons ainsi avoir un logo qui pointe vers son fichier :

  1.   <a href="[(#MODELE{logo, article=25}|url_absolue)]">
  2.   [(#MODELE{logo, article=25}|balise_img|image_reduire{120,120})]
  3.   </a>
  4.  

16- Pouvoir utiliser un critère conditionnel avec un nom de variable quelconque

Références critère conditionnel balise #ENV balises #SET et #GET

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.

  • pour le critère LIKE  :
    1.     #SET{ok, #ENV{nimportequoi}}
    2.     <BOUCLE_a(ARTICLES){chapo LIKE %#GET{ok}%}>
    3.     #CHAPO
    4.     </BOUCLE_a>
  • pour le critère =  :
    1.     #SET{ok, #ENV{nimportequoi}|sinon{.*}}
    2.     <BOUCLE_b(ARTICLES){titre == #GET{ok}}>
    3.     #TITRE
    4.     </BOUCLE_b>
  • pour le critère < sur un champ numérique (id_article) :
    1.     #SET{ok, #ENV{nimportequoi}|sinon{999999}}
    2.     <BOUCLE_c(ARTICLES){id_article < #GET{ok}}>
    3.     #ID_ARTICLE
    4.     </BOUCLE_c>
  • pour le critère < sur un champ non numérique (texte ou date) :
    1.     #SET{ok, #ENV{nimportequoi}|sinon{zzzzzz}}
    2.     <BOUCLE_d(ARTICLES){titre < #GET{ok}}>
    3.     #TITRE
    4.     </BOUCLE_d>
  • pour le critère > (là, c’est facile...) :
    1.     <BOUCLE_e(ARTICLES){id_article > #ENV{nimportequoi}}>
    2.     #ID_ARTICLE
    3.     </BOUCLE_e>
    4.     <BOUCLE_f(ARTICLES){titre  > #ENV{nimportequoi}}>
    5.     #TITRE
    6.     </BOUCLE_f>

17- Retrouver l’objet et l’id_objet concerné par la page en cours

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 :

  1. /*
  2.  * Cette fonction retourne soit l'objet soit l'id_objet concerné par
  3.  * la page en cours et ce, en-dehors de toute boucle.
  4.  * Elle se base sur l'url #SELF indépendamment du type d'url choisi.
  5.  *
  6.  * appelable par :
  7.  *   [(#SELF|reference{objet})]
  8.  *   [(#SELF|reference{id_objet})]
  9.  *   [(#SELF|reference{rubrique})]
  10.  *   [(#SELF|reference{id_auteur})]
  11.  *
  12.  * permet des choses comme :
  13.  *   [(#SELF|reference{rubrique}) je suis sur une page rubrique]
  14.  *   [(#SELF|reference{id_auteur}) je suis sur la page de l'auteur n]
  15.  *   [(#INFO_TITRE{#SELF|reference{objet}, #SELF|reference{id_objet}})]
  16.  *
  17.  * @param string $self
  18.  *   l'url retournée par #SELF
  19.  * @param string $req
  20.  *   le mot générique 'objet'
  21.  *   le mot générique 'id_objet'
  22.  *   un nom littéral d'objet ('rubrique', 'auteur', 'evenement'...)
  23.  *   un nom littéral d'id_objet ('id_rubrique', 'id_auteur'...)
  24.  *    
  25.  * @return string
  26.  *   soit le nom réel de 'objet' ('article', 'rubrique', 'evenement'...) ;
  27.  *   soit la valeur de 'id_objet' ;
  28.  *   soit TRUE si le nom littéral d'objet demandé est bien celui de
  29.  *      l'objet en cours
  30.  *   soit la valeur de l'id_objet explicitement demandé
  31.  *   soit FALSE
  32.  */
  33. function reference($self, $req = '') {
  34.   $ret = array();
  35.   $ret['obj'] = $ret['id'] = '';
  36.   if (lire_config('type_urls') != 'arbo') {
  37.     $elements = urls_decoder_url($self);
  38.     if(!empty($elements[0])) {
  39.       $ret['obj'] = $elements[0];
  40.       $ret['id'] = $elements[1]['id_' . $ret['obj']];
  41.     }
  42.   }
  43.   else {
  44.     $elements = urls_arbo_dist($self,'');
  45.     if(!empty($elements[0])) {
  46.       $ret['obj'] = $elements[1];
  47.       $ret['id'] = $elements[0]['id_' . $ret['obj']];
  48.     }
  49.   }
  50.   if ($req == $ret['obj']) return ' ';
  51.   elseif ($req == 'id_' . $ret['obj']) return $ret['id'];
  52.   elseif ($req == 'objet') return $ret['obj'];
  53.   elseif ($req == 'id_objet') return $ret['id'];
  54.   return '';
  55. }
  56.  
  57.  
  58. /* La balise #SELF_REF{arg}
  59.  *
  60.  * S'utilise dans les squelettes par appel direct accompagnée
  61.  * d'un seul argument :
  62.  *   le mot générique 'objet'
  63.  *   le mot générique 'id_objet'
  64.  *   un nom littéral d'objet ('rubrique', 'auteur', 'evenement'...)
  65.  *   un nom littéral d'id_objet ('id_rubrique', 'id_auteur'...)
  66.  *
  67.  * par exemple :
  68.  *   [c'est ok : je m'affiche dans la page article de l'article n°
  69.  *   (#SELF_REF{id_article})]
  70.  *
  71.  */
  72. function balise_SELF_REF($p) {
  73.   $p->code = interprete_argument_balise(1, $p);
  74.   $p->code = 'reference(self(),' .$p->code . ')';
  75.   $p->interdire_scripts = false;
  76.   return $p;
  77. }

18- Afficher l’intervalle de temps entre une date donnée et la date courante

dans mes_fonctions.php :

  1. /*
  2.  * Le filtre : [(#DATE|decompte)] retourne la valeur de
  3.  * l'intervalle de temps entre la date courante et la date passée
  4.  * en argument. Si l'écart est négatif (#DATE est passée) la fonction
  5.  * préfixe le retour d'un signe 'moins'.
  6.  *
  7.  * @param string $dat_x
  8.  *   une date quelconque : 'aaaa-mm-jj hh:mm:ss' ou 'aaaa-mm-jj'
  9.  *  
  10.  * @return string
  11.  *   l'intervalle de temps entre la date courante et celle passée en argument.
  12.  */
  13. function decompte($dat_x) {
  14.   $aujourd_hui = new DateTime();
  15.   $date_compare = new DateTime($dat_x);
  16.   $ect = $aujourd_hui->diff($date_compare);
  17.   $sens = ($ect->invert == 1) ? '- ' : '';
  18.   $format = $sens . '%1$04d-%2$02d-%3$02d %4$02d:%5$02d:%6$02d';
  19.   return sprintf($format, $ect->y, $ect->m, $ect->d, $ect->h, $ect->i, $ect->s);
  20. }
  21.  
  22.  
  23. /* La balise : #DECOMPTE{aaaa-mm-jj hh:mm:ss}
  24.  *
  25.  * Affiche (sous la forme 'aaaa-mm-jj hh:mm:ss') la valeur de l'intervalle
  26.  * de temps entre la date courante et la date passée en paramètre.
  27.  * Si 'hh:mm:ss' est omis dans l'argument, 00:00:00 sera utilisé.
  28.  * Si l'écart est négatif le retour est préfixé  d'un signe 'moins'.
  29.  *
  30.  */
  31. function balise_DECOMPTE($p) {
  32.   $p->code = interprete_argument_balise(1, $p);
  33.   $p->code = 'decompte(' . $p->code . ')';
  34.   $p->interdire_scripts = false;
  35.   return $p;
  36. }

19- Auto publier les articles d’une rubrique par l’intermédiaire d’un pipeline (Utilisation hors plugin)

Références pipeline

Cette astuce fait suite à une discussion tenue dans le forum public SPIP.

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’agir 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 :

  1. // Utilisation du pipeline pre_insertion qui va exécuter la fonction ma_fonction_pre_insertion
  2. $GLOBALS['spip_pipeline']['pre_insertion'] .= "|ma_fonction_pre_insertion";
  3.  
  4.  
  5. // Fonction qui passe le statut à l'état publié des articles de la rubrique X au moment de l'insertion
  6. function ma_fonction_pre_insertion($flux) {
  7.     if ($flux['args']['table']=='spip_articles' && $flux['data']['id_rubrique'] == X) {
  8.         $flux['data']['statut'] = 'publie';
  9.     }
  10.     return $flux;
  11. }

On peut bien entendu adapter le contenu de la fonction pour appliquer ce traitement à tout type d’objet éditorial, ainsi que modifier un autre champ que le champ statut.

Pour finir, le simple fait de se rendre sur la page de gestion des plugins, dans l’espace privé, va rafraîchir le cache pipeline (fichier tmp/cache/charger_pipelines.php). Les modifications effectuées ci-dessus seront alors prises en compte.


20- Automatiser un traitement particulier sur la balise #TEXTE des articles

Références traitements automatiques des balises fichier mes_options.php

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 :

  1. $table_des_traitements['TEXTE']['articles'] = 'mafonction(%s)';
  2. function mafonction($texte) {
  3.      // rajouter ici le traitement souhaité
  4.     // ...
  5.     // ...
  6.     return $texte;
  7. }

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.


21- Remplir un tableau de tableaux avec une boucle

Références balise #ARRAY filtre |table_valeur

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 :

  1. #SET{tab, #ARRAY}
  2. <BOUCLE_u(ARTICLES) {tout}>
  3.    [(#SET{tab, #GET{tab}|push{#LISTE{#ID_ARTICLE, #TITRE}}})]
  4. </BOUCLE_u>
  5.    

À ce stade, nous avons un tableau peuplé ainsi :

+---+---+------------------+
!  !  ! 1                !
!   ! 1 ! titre article 1  !
+---+---+------------------+
! 1 !  ! 2                !
!   ! 1 ! titre article 2  !
+---+---+------------------+
! 2 !  ! 3                !
!   ! 1 ! titre article 4  !
+---+---+------------------+
 

Utiliser les informations de ce tableau :

  1. [(#GET{tab}|table_valeur{1/1})]  affichera «titre article 2»
  2. [(#GET{tab}|table_valeur{2/0})]  affichera «3»

22- Présenter un usage de la balise <code> à l’intérieur même de balises <code>

pour afficher ceci :
bla bla
<code> ... </code>
bla bla
il faudra saisir ceci :

et pour afficher ceci :
bla bla
 <pre>
   <code>
     $foo = 'bar';
     $bar = "foo";
   </code>
 </pre>
bla bla
il faudra saisir ceci :


23- Utiliser la boucle DATA pour afficher une liste d’albums Flickr

Références boucle (DATA)

Petite variante de manipulation de l’API Flickr via la boucle DATA http://spip-love-opendata.nursit.com/xml.
Mission : récupérer une liste d’albums d’un utilisateur Flickr.

La gestion des clés se fait par l’API Garden de Flickr :

http://www.flickr.com/services/api/

On définit la méthode API pour récupérer les albums : http://www.flickr.com/services/api/flickr.galleries.getPhotos.html

[(#SET{flickr_method,flickr.photosets.getList})]

Puis le format, JSON avec le paramètre nojsoncallback pour un JSON brut, sans enveloppe de fonction.

[(#SET{flickr_format,format=json&nojsoncallback=1})]

L’URL REST (avec l’api_key et user_id passées en constantes dans mes_options.php)

[(#SET{flickr_url, http://api.flickr.com/services/rest/?api_key=#EVAL{flickr_key}&user_id=#EVAL{flickr_userid}&method=#GET{flickr_method}&#GET{flickr_format}})]

Et on boucle :

  1. <B_flickrJson>
  2. <ul>
  3. <BOUCLE_flickrJson(DATA)
  4.         {source json, #GET{flickr_url}} {datapath photosets/photoset}{0,6}>
  5.         [(#SET{flickr_img, <img src="http://farm#VALEUR{farm}.static.flickr.com/#VALEUR{server}/
  6.                 #VALEUR{primary}_#VALEUR{secret}_q.jpg" alt="flickr">} )]
  7.         <li>
  8.                 <a href="http://www.flickr.com/photos/#EVAL{flickr_userid}/sets/#VALEUR{id}" title="Voir image ..">
  9.                         <div>
  10.                         [(#GET{flickr_img}|image_reduire{100})]
  11.                         </div>
  12.                         <div>                                                                          
  13.                         <h3>[(#VALEUR{title/_content})]</h3>
  14.                         [<p>(#VALEUR{description/_content})<br>
  15.                                 Photos : #VALEUR{photos}</p>]
  16.                         </div> 
  17.                 </a>
  18.         </li>
  19. </BOUCLE_flickrJson>                                                           
  20. </ul>
  21. </B_flickrJson><//B_flickrJson>

24- Ne boucler sur la table sql d’un plugin que si ce plugin est installé et activé.

Références critère {si…} table absente

Une particularité du compilateur de SPIP est que l’on ne peux pas lui demander d’évaluer les critères avant de savoir sur quelle table ils portent. Ainsi :

  1. <BOUCLE_h(MON_PLUGIN) {si #PLUGIN{mon_plugin}}>

retournera l’erreur Unknown SQL table MON_PLUGIN si mon_plugin n’est pas installé et activé.
Pour conditionner l’exécution d’un boucle à la présence d’un plugin, il faut donc écrire :

  1. <BOUCLE_h(MON_PLUGIN ?) {si #PLUGIN{mon_plugin}}>

Dans ce cas, si mon_plugin est actif, alors sa table existe ; s’il est inactif, alors le ? empêchera l’exécution de la boucle, puisque sa table est considérée comme n’existant pas.


25- Avertir un administrateur restreint qu’il administre telle ou telle rubrique.

Références balise #AUTORISER

On peut vouloir afficher sur le site public des informations concernant une rubrique qu’aux administrateurs (restreints ou complets) qui « gèrent » cette rubrique.
Pour cela nous testerons l’autorisation de publier dans cette rubrique :

  1. [(#AUTORISER{publierdans, rubrique, #ID_RUBRIQUE|intval|sinon{-1}, #SESSION{id_auteur}})
  2. ceci ne s'affichera que si le visiteur loggué est soit administrateur complet, soit administrateur restreint de cette rubrique.]

Autre exemple d’utilisation :

Nous aurons besoin de 2 squelettes d’INCLURE :

  • squelettes/skel_pour_complet.html
  • squelettes/skel_pour_restreint.html

L’appel se fera depuis un squelette avec :

  1. [(#AUTORISER{publierdans, rubrique, #ID_RUBRIQUE|intval|sinon{-1}, #SESSION{id_auteur}})<INCLURE{fond=skel_pour_#AUTORISER{configurer}|?{complet, restreint}}>]

explications :

d’abord on teste si l’auteur loggué #SESSION{id_auteur} a le droit de publier dans la rubrique #ID_RUBRIQUE.

  1. #AUTORISER{publierdans, rubrique, #ID_RUBRIQUE|intval|sinon{-1}, #SESSION{id_auteur}}

dans le test précédent, on récupère l’ID_RUBRIQUE de l’environnement (soit il est déclaré dans une BOUCLE, soit il est passé en URL).
si #ID_RUBRIQUE n’existe pas ou n’est pas un entier, alors on lui affecte la valeur -1 (valeur qui ne peut exister comme identifiant de rubrique).

  1. #ID_RUBRIQUE|intval|sinon{-1}

à ce stade, on sait que l’auteur est administrateur.
on teste alors s’il a le droit de configurer le site. si oui, c’est un administrateur complet, si non c’est un administrateur restreint.

  1. #AUTORISER{configurer}|?{complet, restreint}

il ne reste qu’à appeler le bon squelette d’INCLURE selon les droits de l’administrateur reconnu.

  1. <INCLURE{fond=skel_pour_#AUTORISER{configurer}|?{complet, restreint}}>

26- Un critère de tri alphabétique qui ne prend pas en compte les num titre

Références critère {par ...} fichier mes_options.php

C’est une demande récurrente sur les listes et les forums : pouvoir ordonner alphabétiquement une liste d’objets par leurs titres sans tenir compte des préfixes numérotés qui peuvent leur avoir été associés.

actuellement le critère {par num titre} affiche :

  • Abracadabra
  • C’est la vie, c’est la vie...
  • 02. Au secours !
  • 08. Du bo, du bon, du bonnet.
  • 99. À la claire fontaine

le critère {par titre}, lui, affiche :

  • 02. Au secours !
  • 08. Du bo, du bon, du bonnet.
  • 99. À la claire fontaine
  • Abracadabra
  • C’est la vie, c’est la vie...

voir également {par sinum titre, num titre, titre} à partir de SPIP 3.2

notre nouveau critère {par alpha titre} devra afficher :

  • 99. À la claire fontaine
  • Abracadabra
  • 02. Au secours !
  • C’est la vie, c’est la vie...
  • 08. Du bo, du bon, du bonnet.

dans mes_options.php :

  1. /*
  2.  * surcharge de la fonction critere_par_dist
  3.  * pour pouvoir appeler un critère de tri spécifique
  4.  */
  5. function critere_par($idb, &$boucles, $crit) {
  6.   if (isset($crit->param[0][0]->texte) && preg_match(",^alpha (.*)$,m", $crit->param[0][0]->texte, $m)) {
  7.     return critere_par_alpha($idb, $boucles, $crit);
  8.   }
  9.   return critere_parinverse($idb, $boucles, $crit);
  10. }
  11.  
  12.  
  13. /*
  14.  * critère de tri alphabétique ne tenant pas compte
  15.  * des éventuels préfixes numérotés
  16.  *
  17.  * s'utilise ainsi :
  18.  * {par alpha titre} ou bien {par alpha #ENV{truc}|sinon{titre}}
  19.  */
  20. function critere_par_alpha($idb, &$boucles, $crit, $sens = '') {
  21.   $boucle = &$boucles[$idb];
  22.   // {par alpha ...} ou {!par alpha ...}
  23.   $sens = $crit->not ? " . ' DESC'" : "";
  24.  
  25.  
  26. $par = array_shift($crit->param[0]);
  27.   $par = $par->texte;
  28.   // type de connexion (merci cerdic !)
  29.   $connexion = spip_connect();
  30.   $sqlite = stristr($connexion['type'], "sqlite") ? true : false;
  31.  
  32.  
  33. // test déjà fait plus haut, mais permet de récupérer $champ
  34.   if (preg_match(",^alpha (.*)$,m", $par, $m)) {
  35.     $champ = trim($m[1]);
  36.     $cle = $boucle->id_table .'.';
  37.  
  38.  
  39. // cas simple : {par alpha titre}
  40.     // base sqlite
  41.     if ($sqlite) {
  42.       $texte = 'LTRIM(REPLACE('. $cle.$champ .', (0+'. $cle.$champ .')||\'. \', \'\'), )';
  43.     }
  44.     // base mysql
  45.     else {
  46.       $texte = 'TRIM(LEADING \'\' FROM(REPLACE('. $cle.$champ .', CONCAT(0+'. $cle.$champ .', \'. \'), \'\')))';
  47.     }
  48.  
  49.  
  50. // balise passée en argument : {par alpha #ENV{truc}|sinon{titre}}
  51.     $suite = calculer_liste($crit->param[0], array(), $boucles, $boucle->id_parent);
  52.     if ($suite !== "''") {
  53.       // base sqlite
  54.       if ($sqlite) {
  55.         $texte = "\".((\$x = $suite) ? ('LTRIM(REPLACE('. \$x .', (0+'. \$x .')||\'. \', \'\'), )') : '0')".".\"";
  56.       }
  57.       // base mysql
  58.       else {
  59.         $texte = "\".((\$x = $suite) ? ('TRIM(LEADING \'\' FROM(REPLACE('.\$x.', CONCAT(0+'.\$x.', \'. \'), \'\')))') : '0')".".\"";
  60.       }
  61.     }
  62.  
  63.  
  64. // nommer l'alias de champ
  65.     $as = 'alpha'.($boucle->order ? count($boucle->order) : "");
  66.     $boucle->select[] = $texte." AS $as";
  67.     if ($sqlite) {
  68.       $order = "'$as COLLATE NOCASE'";
  69.     }
  70.     else {
  71.       $order = "'$as'";
  72.     }
  73.   }
  74.  
  75.  
  76. // construire le order by
  77.   $t = $order.$sens;
  78.   if (preg_match("/^(.*)'\s*\.\s*'([^']*')$/", $t, $r)) {
  79.     $t = $r[1].$r[2];
  80.   }
  81.   $boucle->order[] = $t;
  82. }

27- Découper un texte en colonnes selon sa taille

Références balises #SET et #GET filtre |couper filtre |replace

Nous voulons afficher un texte en le répartissant sur 2 colonnes, mais s’il est très long (par exemple + de 6 000 caractères) nous voudrions afficher sa première moitié sur 2 colonnes côte-à-côte, puis sa seconde moitié au-dessous, toujours sur 2 colonnes.

si texte court    si texte long


+-----++-----+   +-----++-----+
|     ||     |   |     ||     |
|     ||     |   |     ||     |
|  1  ||  2  |   |  1  ||  2  |
|     ||     |   |     ||     |
+-----++-----+   +-----++-----+
                 |     ||     |
                 |     ||     |
                 |  3  ||  4  |
                 |     ||     |
                 +-----++-----+

Nous commençons par compter le nombre de caractères de notre texte, ce qui nous permet de choisir un affichage en 2 colonnes ou en 2 fois 2 colonnes.
Si le texte n’est pas trop long, nous utilisons uniquement les propriétés css3 de colonnage pour l’afficher.
Sinon, nous préparons 2 blocs :
-  la première moitié du texte en utilisant le filtre |couper sur la demi longueur du texte ;
-  la seconde moitié (la fin donc) en supprimant du texte sa première moitié
-  Puis nous utilisons css3 pour l’affichage de chacune des moitiés.

  1. <style type="text/css">
  2. .deux_col {
  3.   -moz-column-count: 2;
  4.   -moz-column-gap: 2em;
  5.   -moz-column-rule: 1px dotted #e7e7e7;
  6.   -webkit-column-count: 2;
  7.   -webkit-column-gap: 2em;
  8.   -webkit-column-rule: 1px dotted #e7e7e7;
  9.   margin-bottom: 2em;
  10. }
  11. </style>
  1. <BOUCLE_col(ARTICLES){id_article}>
  2. [(#TEXTE|strlen|>{6000}|non)
  3.   <div class="deux_col">
  4.   #TEXTE
  5.   </div>
  6. ]
  7. [(#TEXTE|strlen|>{6000}|oui)
  8.   #SET{text1, #TEXTE|couper{#TEXTE|strlen|div{2}, ''}}
  9.   #SET{text2, #TEXTE*|replace{#TEXTE*|couper{#TEXTE|strlen|div{2}, ''}, ''}}
  10.   <div class="deux_col">
  11.   [(#GET{text1}|propre)]
  12.   </div>
  13.   <div class="deux_col">
  14.   [(#GET{text2}|propre)]
  15.   </div>
  16. ]
  17. </BOUCLE_col>

28- Se passer du filtre |_T dans les squelettes

Références balise #ENV les balises de langue

depuis spip 2.1.23 et 3..13 (commits
20035, 20775 et 20900) il est désormais possible d’utiliser les balises de langue ainsi :

avec en url : ?nom_module=petitions&cle_item=form_pet_envoi_mail_confirmation
et dans le squelette : #SET{arg, toto@titi.com}

ces 5 écritures sont fonctionnelles :

<:petitions:form_pet_envoi_mail_confirmation{email=toto@titi.com}:>


<:petitions:form_pet_envoi_mail_confirmation{email=#GET{arg}}:>


<:petitions:{=#ENV{cle_item}, email=#GET{arg}}:>


<:{=#ENV{nom_module}:form_pet_envoi_mail_confirmation, email=#GET{arg}}:>


<:{=#ENV{nom_module}:#ENV{cle_item}, email=#GET{arg}}:>

elles afficheront toutes :

<blockquote class="spip">

Un courrier électronique de confirmation vient d’être envoyé à toto@titi.com. Vous devrez visiter l’adresse web mentionnée dans ce courrier pour valider votre signature.

</blockquote>

cette nouvelle syntaxe permet donc de se passer de :  [(#VAL{#ENV{exec}}|concat{':label_',#VALEUR}|_T)]
pour un bien plus simple :  <:{=#ENV{exec}:label_#VALEUR}:>

on s’affranchit ainsi (au coût certes d’une nouvelle gymnastique syntaxique) de l’usage du filtre |_T dans les squelettes.

On peut noter le cas suivant pour afficher une chaîne de langue depuis une balise quelconque :
<:{=fichier:#BALISE}:>

avec le fichier de langue lang/fichier_fr.php


29- Afficher les objets selon une date en paramètre URL

Il est possible d’afficher les objets ayant une date (AAAA-MM-DD HH:MM:SS en base de données) selon un paramètre URL de type &parametre=AAAA-MM-DD.
Prenons pour exemple une boucle ARTICLES :

<ul>
<BOUCLE_articles(ARTICLES) {date LIKE %#ENV{la_date}%} {par date}>
<li>#TITRE </li>
</BOUCLE>
</ul>

Il suffit passer dans l’url de notre page le paramètre la_date=2013-12-06.

ATTENTION : il ne faut pas nommer le paramètre en date car si la variable d’environnement #ENV{date} n’est pas renseignée, alors SPIP considérera que #ENV{date} doit retourner la date d’aujourd’hui.
cf. paragraphe 4 de cette page.


30- Créer un lien pour forcer le téléchargement du fichier d’un document

  • créer un fichier de squelette avec un header qui imposera le téléchargement du contenu du document.
    Ici on appelle ce fichier "telechargement.html" dans lequel on mettra le contenu suivant :
    <BOUCLE_principale(DOCUMENTS){id_document=#ENV{id_document}}{0,1}>[(#HTTP_HEADER{Content-Disposition: attachment; filename="[(#FICHIER|basename)]"})][(#HTTP_HEADER{'Content-Type:[(#FICHIER|extraire_attribut{src}|mime_content_type)]'})][(#FICHIER|file_get_contents)]</BOUCLE_principale>
    Attention ! il est important de ne pas avoir d’espaces ou de sauts de ligne en dehors de ce code sous peine de foirer le contenu du fichier !
  • dans le squelette on fait alors un lien vers cette page en donnant l’id_document comme paramètre d’URL :
    <a href="[(#URL_PAGE{telechargement,id_document=#ID_DOCUMENT})]">#TITRE</a>

Alternative en HTML5, utiliser l’attribut download dans une balise <a>.
<a href="[(#URL_PAGE{telechargement,id_document=#ID_DOCUMENT})]" [download="(#FICHIER|basename|attribut_html)"]>#TITRE</a>


31- Afficher les initiales du nom d’un auteur

Il arrive parfois qu’on ait besoin d’afficher que les initiales d’un auteur et pas tout son nom saisi dans l’espace privé. Si l’auteur a un pseudonyme d’un mot, le filtre |couper{1} suffit amplement (ce filtre prend en compte les accents). Mais lorsque l’auteur a comme nom "Guy de Maupassant", ou "Marie-Cécile d’Autemont", le filtre couper ne suffit pas.
Voici une fonction PHP "maison" qui pourra être utilisé comme filtre :

  1. // Avoir la première lettre de chaque mot.
  2. // cf. http://stackoverflow.com/a/9706476
  3. // Utilisation : [(#NOM|initiales)]
  4.  
  5.  
  6. function initiales($words) {
  7.     $words = trim($words); // On enlève les espaces avant et après superflus
  8.     $words = preg_split("/[\s,_\-'’]+/", $words); // On transforme les mots en tableau
  9.     $words = array_filter($words); // On supprime les tableaux "vides", c'est juste une sécurité
  10.     $acronym = '';
  11.  
  12.  
  13. foreach ($words as $w) {
  14.         $acronym .= mb_substr($w, , 1); // On prend le premier caractère du mot
  15.     }
  16.     return $acronym;
  17. }

Télécharger

Copier-coller le code ci-dessus dans votre fichier mes_fonctions.php. Vous pouvez l’utiliser dans vos squelettes de cette façon : [(#NOM|intiales)]


32- Afficher le numéro de version SPIP dans un article, une rubrique...

La balise #SPIP_VERSION permet d’afficher la version de SPIP installée sur le site ainsi que le numéro de révision, tels qu’ils apparaissent en pied des pages publiques, par exemple 3..22 [22914]. Pour l’afficher dans un article, il faut écrire un modèle spip_version.html (à placer dans squelettes/modeles/) qui appelle cette balise.

Le modèle ci-dessous offre des possibilités supplémentaires. En l’appelant avec un paramètre "afficher", on peut moduler le résultat et choisir de n’afficher qu’une partie de la chaîne de caractères.

  • <spip_version|afficher=x.y.z> ou <spip_version|afficher=num_version> n’affiche que le numéro de version au format "x.y.z", par exemple 3..22.
  • <spip_version|afficher=revision> n’affiche que le numéro de révision, entre crochets. Exemple [22914]
  • <spip_version|afficher=num_rev> n’affiche que le numéro de révision mais sans les crochets. Exemple 22914
  • <spip_version|afficher=tout> affiche toute la chaîne, comme s’il n’y avait pas de paramètre. C’est l’équivalent de <spip_version|>.
  1. [(#REM) "Modèle" pour afficher la version de SPIP.
  2.  
  3.  
  4. ]#SET{quoi,#ENV{afficher}|sinon{tout}}[
  5. (#REM)
  6. On est obligé de pré-découper la chaîne pour extraire le numéro de version simple, sinon le traitement des crochets perturbe le dernier test.
  7. ]#SET{
  8. version,#SPIP_VERSION|explode{' '}}#SET{
  9. rev,#GET{version}|table_valeur{1}}#SET{
  10. nr,#GET{rev}|explode{']'}|array_shift}#SET{
  11. num_r,#GET{nr}|explode{'['}|table_valeur{1}}[
  12.  
  13.  
  14. (#REM) Renvoi de la chaîne demandée en fonction du paramètre transmis.
  15. ][(#GET{
  16. quoi}|=={tout}|?{#SPIP_VERSION})][(#GET{
  17. quoi}|=={x.y.z}|ou{#GET{quoi}|=={num_version}}|?{#GET{version}|array_shift})][(#GET{
  18. quoi}|=={revision}|?{#GET{rev}})][(#GET{
  19. quoi}|=={num_rev}|?{#GET{num_r}})]

33- Un critère pour comparer l’âge en minutes, heures, mois ou années

Le critère ’age’ et sa famille (’age_relatif’ et ’age_debut’ qui compare date_debut) portent sur des valeurs en jours. Pour comparer avec des durées en secondes, minutes, heures, semaines, mois ou années etc, on pourra définir le critère suivant (dans mes_options) :

  1. /**
  2.  * Critère pour avoir l'équivalent du critère `age` en minutes ou autre
  3.  *
  4.  * 4 paramètres : le champ de date à utiliser, l'opérateur, la valeur comparée et l'unité (en anglais).
  5.  * L'unité parmi : MICROSECOND (microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or YEAR (toujours au singulier)
  6.  *
  7.  * @syntaxe
  8.  *      - `{ages champ_date,opérateur,valeur,unité}`
  9.  *      - exemple : `{ages date,<,1000,minute}`
  10.  *
  11.  * @param string $idb Identifiant de la boucle
  12.  * @param array $boucles AST du squelette
  13.  * @param Critere $crit Paramètres du critère dans cette boucle
  14. **/
  15. function critere_ages_dist($idb, &$boucles, $crit) {
  16.         $boucle = &$boucles[$idb];
  17.         $id_table = $boucle->id_table;   // articles
  18.         $primary = $boucle->primary;     // id_article
  19.         $objet = objet_type($id_table);  // article
  20.  
  21.  
  22. if (
  23.                 // y ait 4 paramètres obligatoirement
  24.                 count($crit->param) == 4
  25.         ){
  26.                 $champ_date = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
  27.                 $operateur  = calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent);
  28.                 $valeur     = calculer_liste($crit->param[2], array(), $boucles, $boucles[$idb]->id_parent);
  29.                 $unite      = calculer_liste($crit->param[3], array(), $boucles, $boucles[$idb]->id_parent);
  30.  
  31.  
  32. #$boucle->where[] = array($operateur, "\"TIMESTAMPDIFF(\" . $unite . \", \" . $champ_date . \", NOW())\"", $valeur);
  33.                 $boucle->having[] = array($operateur, "'ages'", $valeur);
  34.                 $boucle->select[] = "TIMESTAMPDIFF(\" . $unite . \", \" . $champ_date . \", NOW()) AS ages";
  35.         } else {
  36.                 return (array('zbug_critere_inconnu', array('critere' => $crit->op)));
  37.         }
  38.  
  39.  
  40. }
  41.  
  42.  
  43. /**
  44.  * Balise #AGES issue du critère {ages ...}
  45.  *
  46.  * @param unknown_type $p
  47.  */
  48. function balise_ages_dist($p) {
  49.         return rindex_pile($p, 'ages', 'ages');
  50. }

Télécharger

Voici un exemple d’usage :

  1. <BOUCLE_r(ARTICLES){!par date}{0,20}{ages date,<,6000,MINUTE}>
  2. [(#DATE|affdate{'Y-m-d H:i:s'})] - #AGES<br />
  3. </BOUCLE_r>

Télécharger


34- Surcharger deux fois une autorisation

Si une autorisation autoriser_quoi_action_dist() existe, on peut créer une surcharge en la renommant autoriser_quoi_action(), mais il ne peut pas en exister plusieurs du même nom. Donc...

Si un plugin B nécessite un plugin A, si A surcharge une autorisation que B devra surcharger à son tour, alors prendre exemple sur :
https://zone.spip.org/trac/spip-zone/browser/_plugins_/info_sites/trunk/info_sites_autorisations.php#L281

Le principe : la surcharge d’une autorisation peut-être réglée par une nouvelle autorisation qui elle pourra être surchargée normalement....

Autorisation du plugin A qui surcharge la fonction autoriser_page_convertir_dist() du plugin Pages :

/**
* on ne veut pas la possibilite de convertir un article en page, donc on surcharge la fonction du plugin Pages
  en utilisant une nouvelle fonction d'autorisation avec *_dist
*/
function autoriser_page_convertir($faire, $type, $id, $qui, $opt) {
        return autoriser('pageconvertir', 'pref', $qui);
}
function autoriser_pref_pageconvertir_dist($faire, $type, $id, $qui, $opt) {
        return false;
}

Du coup rien n’empêche B de surcharger l’autorisation de A avec autoriser_pref_pageconvertir() :

/**
* on veut la possibilite de convertir
*/
function autoriser_pref_pageconvertir($faire, $type, $id, $qui, $opt) {
        return true;
}

35- Ne pas charger le jQuery fourni par SPIP

Parfois, on désire ne pas charger le ou les jQuery fournis par SPIP en conservant quand même la balise #INSERT_HEAD pour les autres insertions de plugins.
Exemple typique : on intègre un thème conçu pour jquery 1.1 et la migration vers jQuery récent est trop lourde.

Voici la méthode d’après https://forum.spip.net/fr_249697.html

1. Ajouter dans mes_options.php

  1. <?php
  2. // desactiver l'insertion de jquery  dans le insert_head
  3. // https://forum.spip.net/fr_249697.html
  4. $GLOBALS['spip_pipeline']['jquery_plugins'] .= "|limiter_javascript";
  5.  
  6.  
  7. function limiter_javascript($plugins){
  8.         include_spip('inc/utils');
  9.  
  10.  
  11. // uniquement sur les pages publiques de SPIP (ne pas casser l'admin)
  12.      if(!test_espace_prive()) {
  13.                 //return array('javascript/jquery.js');  // exemple dans lequel on garde uniquement jQuery mais pas les autres jQuery UI
  14.                 $plugins = array(); // Aucun jQuery
  15.         }
  16.  
  17.  
  18. return $plugins;
  19. }

Télécharger

2. Dans la configuration privée du SPIP, choisir la configuration suivante

  • Configurer les barres d’outils > Ne pas charger ( ecrire/ ?exec=configurer_avancees )
  • Squelettes > boite multimédia > Ne pas activer ( ecrire/ ?exec=configurer_mediabox )

Retour à la version courante

Toutes les versions