Différences entre versions de « Plugin SPIP »

De Cliss XXI
Sauter à la navigation Sauter à la recherche
imported>SylvainBeucler
imported>SylvainBeucler
(5 versions intermédiaires par le même utilisateur non affichées)
Ligne 1 : Ligne 1 :
 
= Introduction =
 
= Introduction =
  
* [http://www.spip.net/rubrique205.html Le développement de SPIP et ses outils]: documentation officielle
+
* [http://www.spip.net/rubrique205.html Le développement de SPIP et ses outils]: introduction officielle mais limitée
* [http://programmer.spip.org/ Programmer avec SPIP 2.0]: une introduction plus poussée avec description de quelques pipelines et fonctions importants; pénible à lire dû à un découpage page par page trop important
+
* [http://programmer.spip.org/ Programmer avec SPIP 2.0]: la documentation la plus complète, on y retrouve les concepts de cette page; description de quelques pipelines et fonctions importants; pénible à lire dû à un découpage trop fréquent.
* [http://www.spip-contrib.net/Developper-avec-SPIP Développer avec SPIP]: une liste de liens sur SPIP-Contrib; noter que certaines fonctionnalités des plugins sont des reprises de l'existant < v1.9, par exemple la définition de balises personnalisées.
+
* [http://www.spip-contrib.net/Developper-avec-SPIP Développer avec SPIP]: une liste de liens sur SPIP-Contrib; noter que certaines fonctionnalités des plugins sont des reprises de l'existant < v1.9, par exemple la définition de balises personnalisées; d'autres liens sont dépassés
* [http://doc.spip.org/@Plugin-xml Plugin.xml]: référence de la syntaxe
+
* [http://doc.spip.org/@Plugin-xml Plugin.xml]: référence de la syntaxe de plugin.xml
* [http://www.spip.us/@ Glossaire]: un index de tous les boucles/balises/filtres et autres mots-clefs de SPIP
+
* [http://www.spip.net/@ Glossaire]: un index de tous les boucles/balises/filtres et autres mots-clefs de SPIP
  
 
= Le SPIP_PATH =
 
= Le SPIP_PATH =
Ligne 25 : Ligne 25 :
  
 
La meilleure source de documentation reste la lecture du code source, et l'étude d'autres plugins.
 
La meilleure source de documentation reste la lecture du code source, et l'étude d'autres plugins.
 +
 +
== SQL ==
 +
 +
Mémento pour conversion PHP/MySQL -> SPIP (cf. <code>ecrire/base/abstract_sql.php</code> et <code>ecrire/req/mysql.php</code>):
 +
 +
* $res = mysql_query ->
 +
** sql_select($champs, $from, $where, ...)
 +
** sql_insertq($table, $couples, ...)
 +
** sql_insertq_multi($table, $tab_couples, ...)
 +
** sql_updateq($table, $couples, $where, ...)
 +
** sql_delete($table, $where, ...)
 +
** sql_replace($table, $couples, ...)
 +
** ...
 +
* qqchose IN (...) -> sql_in($val, $valeurs, $not)
 +
* mysql_real_escape_string -> sql_quote($val) ou _q($val) (même si c'est fait de manière sale avec addslashes)
 +
* mysql_fetch_assoc ->
 +
** sql_fetch($res) - la prochaine ligne
 +
** sql_fetsel($select, $from, $where, ...) - une seule ligne
 +
** sql_allfetsel($select, $from, $where, ...) - toutes les lignes
 +
** sql_getfetsel($select, $from, $where, ...) - une seule ligne et un seul champ
 +
* mysql_num_rows ->
 +
** sql_count($req)
 +
** sql_countsel($from, $where)
  
 
= Passer de 1.9 à 2.0 =
 
= Passer de 1.9 à 2.0 =
Ligne 143 : Ligne 166 :
  
 
La balise est définie par trois fonctions: TODO
 
La balise est définie par trois fonctions: TODO
* <code>balise_MABALISE($p)</code>: qui va en général appeler <code>calculer_balise_dynamique($p, 'MABALISE', array('arg1', 'arg2', ...))</code>.
+
* <code>balise_MABALISE($p)</code>: déclaration de la balise et des paramètres constants. Elle va en général appeler <code>calculer_balise_dynamique($p, 'MABALISE', array('nom_arg1', 'nom_arg2', ...))</code>. Ces arguments sont des noms de champs que SPIP doit compléter avec de les passer à <code>_stat</code>.
* <code>balise_MABALISE_stat($args, $filtres)</code>: <code>$args</code> contient les arguments passés à <code>calculer_balise_dynamique</code> suivis de ceux passés à la balise (<code>MABALISE{autrearg1, autrearg2, ...}</code>); retourne un tableau d'arguments à passer à <code>_dyn</code>, ou bien un message d'erreur.
+
* <code>balise_MABALISE_stat($args, $filtres)</code>: <code>$args</code> contient la listes des valeurs des arguments passés à <code>calculer_balise_dynamique</code>, suivis des valeurs de ceux passés à la balise (<code>MABALISE{val_arg3, val_arg4, ...}</code>). <code>$filtres</code> contient des "pseudos-filtres", c'est à dire des paramètres non interprétés passés avec la syntaxe des filtres SPIP <code>[(#MABALISE|filtre1|filtre2|...)]</code>; cette syntaxe est apparemment dépréciée en faveur de la précédente avec <code>$args</code>. La fonction retourne un tableau d'arguments à passer à <code>_dyn</code>, ou bien directement une valeur (qui peut être un message d'erreur ou non), auquel cas <code>_dyn</code> ne sera pas appelée.
 
* <code>balise_MABALISE_dyn(...)</code>: en paramètre, les arguments construits dans <code>_stat</code>. La valeur de retour est un tableau à 3 éléments: squelette, durée du cache (0 pour un traitement de formulaire par POST), environnement du squelette.
 
* <code>balise_MABALISE_dyn(...)</code>: en paramètre, les arguments construits dans <code>_stat</code>. La valeur de retour est un tableau à 3 éléments: squelette, durée du cache (0 pour un traitement de formulaire par POST), environnement du squelette.
 +
 +
Exemple:
 +
TODO
 +
 +
Trace:
 +
#MABALISE{a,b,c,d}
 +
=> balise_MABALISE_dist($p)
 +
  => creer_balise_dynamique($p, 'MABALISE', array('id_auteur'))
 +
=> balise_MABALISE_stat(array(18, 'a', 'b', 'c', 'd'), array())
 +
=> balise_MABALISE_dyn(18, 4)
 +
 +
Pour le traitement des paramètres, voir par exemple <code>ecrire/balise/login_public.php</code> et .<code>ecrire/balise/formulaire_site.php</code>
  
 
Techniquement, la balise dynamique est une balise statique normale, mais son code est triplement exécuté par PHP:
 
Techniquement, la balise dynamique est une balise statique normale, mais son code est triplement exécuté par PHP:
Ligne 151 : Ligne 186 :
 
* <code>$p->code</code>, qui contient un appel à <code>executer_balise_dynamique(...)</code>, est exécuté pour générer le contenu, qui est stocké dans le cache de page; mais ici il ne s'agit pas de HTML comme dans la balise dynamique, mais à nouveau de code PHP
 
* <code>$p->code</code>, qui contient un appel à <code>executer_balise_dynamique(...)</code>, est exécuté pour générer le contenu, qui est stocké dans le cache de page; mais ici il ne s'agit pas de HTML comme dans la balise dynamique, mais à nouveau de code PHP
 
* le cache de la page est exécuté au moment de la visite du site, appelant la fonction <code>_dyn</code>, dont le résultat est passé à <code>inclure_balise_dynamique(...)</code>.
 
* le cache de la page est exécuté au moment de la visite du site, appelant la fonction <code>_dyn</code>, dont le résultat est passé à <code>inclure_balise_dynamique(...)</code>.
 +
* (et vu que <code>_dyn</code> peut faire appel à un squelette, on peut encore avoir du code interprété par la suite!)
  
 
== Formulaire ==
 
== Formulaire ==

Version du 21 avril 2009 à 17:55

Introduction

  • Le développement de SPIP et ses outils: introduction officielle mais limitée
  • Programmer avec SPIP 2.0: la documentation la plus complète, on y retrouve les concepts de cette page; description de quelques pipelines et fonctions importants; pénible à lire dû à un découpage trop fréquent.
  • Développer avec SPIP: une liste de liens sur SPIP-Contrib; noter que certaines fonctionnalités des plugins sont des reprises de l'existant < v1.9, par exemple la définition de balises personnalisées; d'autres liens sont dépassés
  • Plugin.xml: référence de la syntaxe de plugin.xml
  • Glossaire: un index de tous les boucles/balises/filtres et autres mots-clefs de SPIP

Le SPIP_PATH

Il s'agit du répertoire de recherche de SPIP, pour les squelettes, mais aussi pour les fichiers PHP personnalisés, (définitions de balises, de pages privées, etc.), les modèles, etc.

Dans l'ordre: la variable globale $mes_squelettes (chemins séparés par ':'), puis squelettes, plugins/mon_plugin-1, plugins/mon_plugin-2, ..., (racine), squelettes-dist, prive, ecrire

Techniquement, les chemins des squelettes sont définis dans le fichier PHP généré tmp/charger_plugins_options.php, qui définit également les constantes _DIR_PLUGIN_MONPREFIXE.

Références:

API

http://doc.spip.org/ a pour but de documentation l'API de SPIP, une page par fonction, modifiable par tous. En pratique, peu de fonctions sont documentées. Qui plus est, cette documentation étant la documentation officielle, le code source se contente d'y faire référence, souvent sans plus de détails.

La meilleure source de documentation reste la lecture du code source, et l'étude d'autres plugins.

SQL

Mémento pour conversion PHP/MySQL -> SPIP (cf. ecrire/base/abstract_sql.php et ecrire/req/mysql.php):

  • $res = mysql_query ->
    • sql_select($champs, $from, $where, ...)
    • sql_insertq($table, $couples, ...)
    • sql_insertq_multi($table, $tab_couples, ...)
    • sql_updateq($table, $couples, $where, ...)
    • sql_delete($table, $where, ...)
    • sql_replace($table, $couples, ...)
    • ...
  • qqchose IN (...) -> sql_in($val, $valeurs, $not)
  • mysql_real_escape_string -> sql_quote($val) ou _q($val) (même si c'est fait de manière sale avec addslashes)
  • mysql_fetch_assoc ->
    • sql_fetch($res) - la prochaine ligne
    • sql_fetsel($select, $from, $where, ...) - une seule ligne
    • sql_allfetsel($select, $from, $where, ...) - toutes les lignes
    • sql_getfetsel($select, $from, $where, ...) - une seule ligne et un seul champ
  • mysql_num_rows ->
    • sql_count($req)
    • sql_countsel($from, $where)

Passer de 1.9 à 2.0

Certaines fonctions ont changé. Pour convertir le code de votre plugin, une bonne source d'information est ecrire/inc/vieilles_defs.php qui définit des anciennes fonctions avec la nouvelle API 2.0.

Insérer une nouvelle page admin

  • Définir le préfixe de votre plugin (convention de nommage) dans plugin.xml:
<prefix>monprefixe</prefix>

Contenu du fichier:

<?php
if (!defined("_ECRIRE_INC_VERSION")) return;

function exec_monprefixe_index()
{
  $commencer_page = charger_fonction('commencer_page', 'inc');
  echo $commencer_page("Titre (barre de titre du navigateur)");

  echo gros_titre("Titre (dans la page)", '<img src="logo.png" alt="" />', false);


  echo debut_grand_cadre(true);
  echo "Bandeau en haut";
  echo fin_grand_cadre(true);


  echo debut_gauche("ignored", true);
  echo "À gauche<br />";

  echo creer_colonne_droite("", true);
  echo "À droite si grand écran, à gauche sinon<br />";
  echo debut_boite_info(true);
  echo "Encadré";
  echo fin_boite_info(true);

  $res = icone_horizontale("Page 1", generer_url_ecrire("monprefixe_page1"),
			   "../"._DIR_PLUGIN_MONPREFIXE."fond.gif",
			   "../"._DIR_PLUGIN_MONPREFIXE."page1.gif", false);
  echo bloc_des_raccourcis($res); // crée creer_colonne_droite si besoin


  echo debut_droite("ignored", true); // ferme creer_colonne_droite, si utilisé
  echo "Contenu, au milieu";


  echo fin_gauche();
  echo fin_page();
}

Les true et false qui se baladent partout permettent de dire qu'on s'occupe d'afficher le contenu, sans quoi SPIP affiche un avertissement. Il faut utiliser true ou false au cas par cas, selon la fonction, cela manque de cohérence.

Pour le contenu, on peut soit l'écrire avec des echo, soit faire appel à un squelette dans plugins/mon_plugin-0.1/prive/mon_squelette.html:

recuperer_fond('prive/mon_squelette', $_GET);

Note: le préfixe n'est techniquement pas obligatoire pour le nom de la page, mais c'est une bonne habitude à prendre pour éviter les conflits avec d'autres plugins.

Exemples: ecrire/exec/sites_tous.php, et acces_restreint_3_0/exec/acces_restreint dans le plugin "Accès restreint".

Traitement dans la partie publique

Diverses solutions possibles:

Balise statique

La balise statique a pour but de présenter de l'information, si possible mise en cache.

Champ

Une balise #MONCHAMP à l'intérieur d'une boucle doit aller chercher le champ correspondant dans la base SQL.

TODO

Génération de code

SPIP va chercher une fonction balise_MABALISE($p) ou balise_MABALISE_dist($p), et charge automatiquement le fichier balise/mabalise.php s'il existe (vous pouvez donc définir la balise soit dans ce fichier, soit dans un fichier *_fonctions).

La fonction qui fournit la balise modifie et renvoie un paramètre $p qui a les attributs suivants:

  • $p->code: l'expression PHP qui sera substituée à la balise
  • $p->param[]: TODO - les paramètres passés à la balise? (#BALISE{param1, param2, ...})
  • $p->interdire_scripts: booléen, indique si le résultat de la balise doit être filtré avec la fonction interdire_scripts(...)
  • $p->etoile: booléen, détermine si la balise est de type '*' (MABALISE*) ou '**' (MABALISE**). On peut le rédéfinir. Au final, '*' signifie de ne pas appliquer divers post-traitements (cf. $table_des_traitements), et '**' signifie de, en plus, ne pas lancer interdire_scripts(...) sur $p->code (même si $p->interdire_scripts est à true). Cf. ecrire/public/references.php:applique_filtres(...).

Exemple:

function balise_CITATION_dist($p)
{
  $hasard = mt_rand(0, 1);
  if ($hasard == 1)
    $p->code = "'Gel en novembre, Noël en décembre! -- sagesse populaire'";
  else
    $p->code = "'Une de mes journées les plus productives a été de jeter 1000 lignes de code -- Ken Thompson'";
  $p->code .= " . ' Cache squelette = ' . '" . strftime('%T') . "'";
  $p->code .= " . ' Cache page = ' . strftime('%T')";
  return $p;
}

Apparté: notez les deux types de code:

  • D'une part, un traitement effectué au calcul de la balise: le choix de la citation. Le résultat est une expression PHP, ici une chaine de caractère - qui pour être spécifiée en PHP doit être insérée dans une autre chaine de caractères. La citation sélectionnée ne changera pas dans le cache.
  • D'autre part, un traitement effectué au calcul de la page: la date. On enregistre non pas le résultat, mais le code, qui affichera l'heure courante, même si le cache squelette n'a pas changé.

En pratique, il n'est pas très utile de distinguer le cache de la page (tmp/cache/a - calcul) et le cache de la balise (tmp/cache/skel - recalcul). Le cache de la page est recalculé sans toucher celui de la balise dans le cas où l'on passe des paramètres supplémentaires dans l'URL, par exemple. Sinon préférez le cache complet (enregistrement du résultat plutôt que de l'appel de fonction).

Attention: le fichier balise/mabalise.php n'est chargé qu'au calcul de la balise. Si vous avez besoin de fonctions auxiliaires, soit il faut les déclarer dans d'autres fichiers (xxx_fonctions.php), soit il faut passer à une balise dynamique.

Notez qu'il existe des balises génériques, du type MABALISE_ (tout court), qui sont évaluées après pour toutes les balises MABALISE_XXX, même si la balise en question n'existe pas. Cela permet de traiter notamment les balises spéciales FORMULAIRE_XXX; lecode de SPIP définit également LOGO_XXX et URL_XXX. Cf. ecrire/public/references.php:calculer_balise(...) et ecrire/balise/logo_.php. Les balises ainsi crées peuvent être statiques (LOGO_XXX) ou dynamiques (FORMULAIRE_XXX).

Balise dynamique

Ces balises sont prévues pour traiter des données utilisateur, notamment des formulaires. Elle sont rechargées à chaque appel de page. La balise pourra afficher un <form> avec pour cible la page courante, et on utilisera _request dans le code PHP pour effectuer le traitement. Exemple: #FORMULAIRE_ABONNEMENT dans SPIP-Listes, cf. spip-listes_1_9_3/balise/formulaire_abonnement.php.

La balise est définie par trois fonctions: TODO

  • balise_MABALISE($p): déclaration de la balise et des paramètres constants. Elle va en général appeler calculer_balise_dynamique($p, 'MABALISE', array('nom_arg1', 'nom_arg2', ...)). Ces arguments sont des noms de champs que SPIP doit compléter avec de les passer à _stat.
  • balise_MABALISE_stat($args, $filtres): $args contient la listes des valeurs des arguments passés à calculer_balise_dynamique, suivis des valeurs de ceux passés à la balise (MABALISE{val_arg3, val_arg4, ...}). $filtres contient des "pseudos-filtres", c'est à dire des paramètres non interprétés passés avec la syntaxe des filtres SPIP [(#MABALISE|filtre1|filtre2|...)]; cette syntaxe est apparemment dépréciée en faveur de la précédente avec $args. La fonction retourne un tableau d'arguments à passer à _dyn, ou bien directement une valeur (qui peut être un message d'erreur ou non), auquel cas _dyn ne sera pas appelée.
  • balise_MABALISE_dyn(...): en paramètre, les arguments construits dans _stat. La valeur de retour est un tableau à 3 éléments: squelette, durée du cache (0 pour un traitement de formulaire par POST), environnement du squelette.

Exemple:

TODO

Trace:

#MABALISE{a,b,c,d}
=> balise_MABALISE_dist($p)
  => creer_balise_dynamique($p, 'MABALISE', array('id_auteur'))
=> balise_MABALISE_stat(array(18, 'a', 'b', 'c', 'd'), array())
=> balise_MABALISE_dyn(18, 4)

Pour le traitement des paramètres, voir par exemple ecrire/balise/login_public.php et .ecrire/balise/formulaire_site.php

Techniquement, la balise dynamique est une balise statique normale, mais son code est triplement exécuté par PHP:

  • d'abord la balise est calculée en PHP, avec l'appel à calculer_balise_dynamique(...), et renvoie une expression PHP ($p->code) qui est stockée dans le cache de squelette
  • $p->code, qui contient un appel à executer_balise_dynamique(...), est exécuté pour générer le contenu, qui est stocké dans le cache de page; mais ici il ne s'agit pas de HTML comme dans la balise dynamique, mais à nouveau de code PHP
  • le cache de la page est exécuté au moment de la visite du site, appelant la fonction _dyn, dont le résultat est passé à inclure_balise_dynamique(...).
  • (et vu que _dyn peut faire appel à un squelette, on peut encore avoir du code interprété par la suite!)

Formulaire

Techniquement: traitement déclenché par le paramètre formulaire_action

On peut s'appuyer sur les outils "CVT" (charger/vérifier/traiter) de SPIP. Cette approche crée les formulaires prédéfinis, un par squelette - ce n'est pas prévu pour la génération de formulaires à la volée:

De plus, balise_FORMULAIRE_XXX_stat (definit dans balise/formulaire_xxx.php) devrait être appelé avant la génération du formulaire, ce qui permet de renvoyer un tableau de valeurs, qui sera passé via formulaire_action_args sous forme comprimée et signée. Ces arguments seront par la suite passés en paramètre de _charger/_verifier/_traiter. Cf. ecrire/balise/formulaire_inscription.php et squelettes-dist/formulaires/inscription.php.

Techniquement les formulaires CVT sont implémentés via la balise dynamique et générique FORM_. Vous pouvez donc affiner votre formulaire en déclarant une balise dynamique de même nom.

TODO

Modèles

L'utilisation d'une syntaxe <modeleN> dans un article appelle le squelette modeles/modele.html avec un contexte id_modele=N. Ce squelette pourra inclure une balise correspondante, par exemple.

Cf. ecrire/inc/lien.php:traiter_modeles(...). Exemples: prive/modeles/img.html, plugins/forms_et_tables_1_9_1/modeles/form.html.

Boucles

On peut introduire des nouvelles boucles en déclarant des fonctions boucle_MABOUCLE_dist ou critere_MABOUCLE_moncritere_dist. Le plus simple est d'inclure ces déclarations dans un fichier <fonction> de plugin.xml (chargé à chaque recalcul).

Cf. ecrire/public/compiler.php:public_compiler_dist(...) et ecrire/public/criteres.php:calculter_criteres(...).

Exemples: ecrire/public/boucles.php, ecrire/public/criteres.php, forms_et_tables_1_9_1/public/forms_boucles.php, spip-bonux/public/spip_bonux_criteres.php.

Paramètre 'action'

Le fichier action/monprefixe_monaction.php sera exécuté; cependant ce n'est pas prévu pour afficher du contenu, seulement pour du traitement.

Squelette dédié

Utiliser le paramètre page= pour afficher un squelette de votre plugin, qui pourra contenir du PHP. L'inconvénient est le manque d'intégration dans le site public, puisque ce ne sera pas intégré dans les squelettes du webmestre.

Pipeline

Les points d’entrée (pipelines): les hooks, quoi; une description partielle

La listes des hooks SPIP est dans ecrire/inc_version.php. D'autres plugins peuvent en rajouter pour leurs besoins propres (ex: Forms&Tables).

Le plugin d'exemple, même s'il n'a pas été mis à jour pour la version 2, présente quelques points d'entrée importants, classés par catégorie (admin/cron/public/typo).

TODO