Carnet Wiki

Compléments techniques pour l’ajout de tables dans SPIP

Version 1 — October 2008 JLuc

Note d’intention


J’ai déroulé le bel itinéraire décrit par Pierre Andrew (alias Mortimer?) sur spip-english http://article.gmane.org/gmane.comp... et ai poursuivi sa documentation de manière durable notamment en recopiant dans le corps de l’article les morceaux pertinents de codes liés.

Pierre Andrew est il Mortimer ? Je l’ajouterai en coauteur.

Il reste
-  à traduire tout en anglais ou tout en français.
-  à structurer le texte en dégageant visiblement les étapes successives (et créer des intertitres)
-  améliorer la présentation en dégageant visiblement les fichiers utilisés
-  les critères à préciser, comme indiqué en fin

J’arrête là pour l’instant, y reviendrai peut être quand j’aurai le temps.

Il semblerai qu’un commit 11997 aie passablement modifié la bonne manière de faire. Cela fait l’objet d’un dernier chapitre d’après citation de Cedric Morin. Je ne crois pas que cela invalide cette doc mais cela en modifie les modalités d’usages.
Ce serait bien de préciser cela :-)

En attendant, à bon rédacteur salut !

JLuc

Mémo technique

There is an article in french that is a bit more detailled here:
http://www.spip-contrib.net/Acces-S...

For creating tables of your own, it is quite easy with spip 1.9.2 and
2.0 as SPIP will gladly do Everything for you if you tell him the schema
of the table.

For an example, check out the plugin:
http://trac.spip.org/trac/spip-zone...

it is unfinished, but has the table declaration/creation already done.
First thing to do is create a description of the schema of the table; In
the plugin this is done for three tables:

-  1ère table : spip_basefiltrageip_whitelist.
En voici la déclaration complète :

$spip_basefiltrageip_whitelist 
  = array(
       "first_ip"     => "long NOT NULL",
       "last_ip"     => "long NOT NULL",
       "maj"         => "TIMESTAMP");

$spip_basefiltrageip_whitelist_key = array(
                                  "PRIMARY KEY" => "first_ip",
                                  "UNIQ" => "last_ip");
         
global $tables_principales;
$tables_principales['spip_basefiltreageip_whitelist'] 
  = array(
       'field' => &$spip_basefiltrageip_whitelist,
       'key' => &$spip_basefiltrageip_whitelist_key);

$tables_principales['spip_basefiltreageip_whitelist'] 
  = array(
       'field' => &$spip_basefiltrageip_whitelist,
       'key' => &$spip_basefiltrageip_whitelist_key);

-  la 2eme table spip_basefiltrageip_log est déclarée de la même manière, sauf qu’au lieu d’avoir une clé primaire et une clé unique, ce sont 2 clés simples qui sont déclarées.
Voici simplement la déclaration des clés

$spip_basefiltrageip_log_key
   = array(
        "KEY" => "ip",
        "KEY" => "reason_id");
 

-  la 3eme table spip_basefiltrageip_reasons est déclarée pareil. La particularité dans la déclaration des clés est qu’elle a juste une clé primaire :

$spip_basefiltrageip_reasons_key 
  = array(
     "PRIMARY KEY" => "reason_id");

Then you declare the relations between them (joints):

global $tables_jointures;
$tables_jointures['spip_basefiltreageip_reasons'][]
    = 'spip_basefiltreageip_log';

The joints is what you want to use to tell SPIP that your new
keywords/documents tables relate to your new object.

For example, in spip by default, you have:

$tables_jointures['spip_articles'][]= 'mots_articles';
$tables_jointures['spip_articles'][]= 'auteurs_articles';
$tables_jointures['spip_articles'][]= 'documents_articles';

You can also define more “complex” joint criterion with:

global  $exceptions_des_jointures;
$exceptions_des_jointures['titre_mot'] = array('spip_mots', 'titre');
$exceptions_des_jointures['type_mot'] = array('spip_mots', 'type');

and also the name of the loop for the table:

global $table_des_tables;
$table_des_tables['basefiltrageip_whitelist']='basefiltrageip_whitelist';
$table_des_tables['basefiltrageip_log']='basefiltrageip_log';

If you do not do this table_des_tables thing, then SPIP doesn’t know
that BOUCLE_(XXX) is about the table spip_xxx

Once you do that, you should tell the plugin how to install itself when
it’s activated :
dans http://trac.spip.org/trac/spip-zone...
c’est la ligne

<install>base/basefiltrageip_upgrade.php</install>

This tells spip to call the functions in /basefiltrageip_upgrade.php

This file should contain a function:
function prefix_install($action)

The function can be called with 3 actions:

-  $action=='test' every time the plugin admin page is loaded (hence, it should be light) and should return true if the plugin is installed
properly and up to date and false otherwise

-  $action=='install' will be called if the test returned false. In this
case, the plugin should install or upgrade itself (i.e. create tables,
etc. etc.)

-  $action=='uninstall' is called when the user click the uninstall
button (and not when he deactivates the plugin). This should remove
everything the plugin installed (i.e. drop tables)

Here, the interesting part is the install part:

function basefiltrageip_doupgrade() {
  include_spip('base/basefiltrageip_db');
  $installe = unserialize($GLOBALS['meta']['basefiltrageip:installe']);
  $uptodate = $installe && ($installe == $GLOBALS['basefiltrageip_version']);
  if(!$uptodate) {
      include_spip('base/create');
      include_spip('base/abstract_sql');
      creer_base();
      ecrire_meta('basefiltrageip:installe',$GLOBALS['basefiltrageip_version']);
   }
   ecrire_metas();
}

This is where tables should be created/updated, this can be done by spip
automatically (as the code exists to do it for spip tables), just do:

include_spip('base/basefiltrageip_db'); 
include_spip('base/create');
include_spip('base/abstract_sql');
creer_base();

creer_base will create the tables... or upgrade them according to the description in $tables_principales.
(beware: this will also upgrade the other tables described in
$tables_principales at the time of call...)

Once you have done all of this, then you can loop on your new tables
without declaring any criterion or any tags functions as SPIP knows how
to map them to the SQL schema.

If you want special processing (propre, etc.) applied to a field before returning it as a tag, you can do:
$table_des_traitements['TEXTE'][]= 'propre(%s)';

The second [] is for specifying a particular processing for a field in
one specific table. (see ecrire/public/interfaces.php)

You can also define more complex criterion and tags by defining
functions if you do not want more than a processed SQL field (like
#INTRODUCTION etc.).

This has a bit more documentation and example around (in french :( ):
-  http://www.spip-contrib.net/CreerSa...
-  http://www.spip-contrib.net/Nouvell...

See also
-  http://trac.spip.org/trac/spip-zone...
for a bunch of basic examples (this plugin has also examples of new loops).

A new tag is created by making a file
balise/new_tag.php
in your plugin directory and defining the function. Here is an example
from the FpipR plugin:

<?php
          function balise_ISFAMILY_dist($p) {
            $isfamily = champ_sql('isfamily',$p);
            $id_photo = champ_sql('id_photo',$p);
            $p->code = 
"(($isfamily)?$isfamily:FpipR_photos_getPerms($id_photo,'isfamily'))";
            return $p;
          }
?>

The tricky part to understand here is that this function is executed
when the template cache is computed... hence, you will NEVER have real
values from a specific instance in the function. This code is executed
BEFORE any call to the sql database is done and before the loop is executed.

The balise_XXX is responsible for computing php code that will be
executed when the loop is executed.

The code can be static code when it
doesn’t depend on the loop context (i.e. db fields), or it can be php
code that will be executed when the html cache is computed.

Also note that the code you return will be used to compute the html
cache, so it cannot depend on specific form entries from the user etc..
in that case, you need to create a form tag which is a whole different
story :D

Anyway, back to the example...

-  champ_sql returns code to get the value of a sql field (but does not
yet contain that value) in the table of the loop where the tag is used.
In the example, we get the fields isfamily and id_photo.

-  $p->code contains the code you want to execute later when the loop is
executed. A good thing to do here is avoid putting complex code (you will get mixup in quotting, etc. etc.) but make a function in your
<function> file of the plugin that will do the hard job. For example,

here we call:
FpipR_photos_getPerms($id_photo,'isfamily')

There are other more complex things you can do with $p, I let you see
the examples around spip-zone and in
http://trac.rezo.net/trac/spip/brow...

You can see the resulting code by adding var_mode=debug and choosing the
code tab. It is very useful to experiment and try things to see what
happens.

I will let you experiment with all these and send a later mail about the
criterion.

Nouvelle gestion de tout cela dans SPIP 2

Le commit 11997 introduit dans SPIP 2.0 une nouvelle interface.
Les plugin agenda et acces restreint utilisent cette nouvelle interface.

En 2.0 mots :
-  avant
Chaque plugin définissait ses tables dans la globale en prenant soin d’inclure avant le base/serial pour avoir la definition de base et passer après. Résultat tout le monde incluait base/serial, et les tables étaient définies bien souvent inutilement.
-  maintenant
le core appelle base/serial, base/aux, et public/interfaces quand il en a besoin. A ce moment, les pipelines respectifs declarer_tables_principales, declarer_tables_auxiliaires et declarer_tables_interfaces sont appelés, ce qui permet a chaque plugin de faire ses definitions.
On peut ainsi optimiser la perfo de l’ensemble en ne chargeant que ce qui est necessaire (souvent les interfaces suffisent)

L’ancienne méthode fonctionne encore, mais est maintenant déconseillée.