Adding tables, loops, and tags

Hi folks,

I'm writing a plug-in to add a new type of "thing" to SPIP. I've started my plug-in (using the information on doc.spip.net) and have it creating my table and am now trying to write my loops and tags based on the examples in ecrire/public/boucles.php and also those in plugins/forms_et_plugins_1_9_1/public/forms_boucles.php. Alas, I have been unable to find any sort of pattern in the source nor any documentation for doing this. Can anyone recommend an example that I can use as a guide to:

1) Creating the database tables for a new type of thing (similar to ARTICLES, SITES, and the rest).
2) Create a new type of loop to query this table (I've got this working).
3) Create tags to access the data inside these loops. I'll need my own tags (#DESTINATION) and built-in ones (#TITRE, #DESCRIPTIF) to work on these.
4) Add join tables so that I can attach documents and mots to these things.
5) Create new criteria like {id_thing} so that I can get the documents attached to a 'thing'.
6) Tie this all together with an admin interface.

I've got some of this working in files mentioned in <fonctions> tags in plugin.xml:

1) I add $tables_principale['spip_things'] = array('field' => ..., 'key' => ...); and call creer_base(); and my table is created.
2) I've added a function boucle_THINGS_dist($id, &$bs) based similar to the functions for ARTICLES, etc. This works so long as I don't
3) I'm able to create "static" tags, that don't depend on the loops, but getting it to fetch data from within a loop is beyond me. I've tried to use champ_sql to fetch fields in my own tags but this just fails with ''Unknown SQL table "things", '_loop'' and doesn't log anything in tmp/*.log.
4) ?
5) ?
6) I've used the affiche_milieu to start adding my list of things, new thing link, etc. when exec==naviguer, and it'll call my exec/ function, so this looks like it'll be OK.

I've looked through <http://plugins.spip.net/> (incidentally, should every page really have the same title?) but couldn't find anything that looks similar, and I've been staring at various files from SPIP and Forms et Tables for a day and a half now (which hasn't really helped) so I'm stuck.

Is there a reference to the data model ($tables_principales, and the other magical variables, champ_sql, etc. etc.) and loops/tags for plug-in writers? Failing that, is there a good example that I can look at? Everything I look at seems to do it differently. I'm particularly worried about getting data from my own table through #TITRE, #TEXTE, #DESCRIPTIF, etc. Is this even possible?

Incidentally, this is for 1.9.2e, but I'll be updating it for 2.0 after it's released.

Thanks,

Thomas Sutton

Web Developer
bouncingorange
graphic + web design

Hi list,

Next time, I'll have lunch *before* sending such a message. I went and sat in the park for an hour, came back and found <http://doc.spip.org/@La-base-de-donnees,4391&gt;\. While a little bit sparse (it describes only four of the thirteen arrays mentioned), this says that I need a line like:

  $table_des_tables['things'] = 'things';

in my base/things.php file. I don't understand *why* this makes a difference (my loop ran the correct number times before, it just wouldn't access any data), but I can now use {numeric_column > 0} and #TITRE, and it works.

Now to figure out how to do documents and keywords...

Regards,

Thomas Sutton

Web Developer
bouncingorange
graphic + web design

On 07/10/2008, at 12:42 PM, Thomas Sutton wrote:

Hi folks,

I'm writing a plug-in to add a new type of "thing" to SPIP. I've started my plug-in (using the information on doc.spip.net) and have it creating my table and am now trying to write my loops and tags based on the examples in ecrire/public/boucles.php and also those in plugins/forms_et_plugins_1_9_1/public/forms_boucles.php. Alas, I have been unable to find any sort of pattern in the source nor any documentation for doing this. Can anyone recommend an example that I can use as a guide to:

1) Creating the database tables for a new type of thing (similar to ARTICLES, SITES, and the rest).
2) Create a new type of loop to query this table (I've got this working).
3) Create tags to access the data inside these loops. I'll need my own tags (#DESTINATION) and built-in ones (#TITRE, #DESCRIPTIF) to work on these.
4) Add join tables so that I can attach documents and mots to these things.
5) Create new criteria like {id_thing} so that I can get the documents attached to a 'thing'.
6) Tie this all together with an admin interface.

I've got some of this working in files mentioned in <fonctions> tags in plugin.xml:

1) I add $tables_principale['spip_things'] = array('field' => ..., 'key' => ...); and call creer_base(); and my table is created.
2) I've added a function boucle_THINGS_dist($id, &$bs) based similar to the functions for ARTICLES, etc. This works so long as I don't
3) I'm able to create "static" tags, that don't depend on the loops, but getting it to fetch data from within a loop is beyond me. I've tried to use champ_sql to fetch fields in my own tags but this just fails with ''Unknown SQL table "things", '_loop'' and doesn't log anything in tmp/*.log.
4) ?
5) ?
6) I've used the affiche_milieu to start adding my list of things, new thing link, etc. when exec==naviguer, and it'll call my exec/ function, so this looks like it'll be OK.

I've looked through <http://plugins.spip.net/&gt; (incidentally, should every page really have the same title?) but couldn't find anything that looks similar, and I've been staring at various files from SPIP and Forms et Tables for a day and a half now (which hasn't really helped) so I'm stuck.

Is there a reference to the data model ($tables_principales, and the other magical variables, champ_sql, etc. etc.) and loops/tags for plug-in writers? Failing that, is there a good example that I can look at? Everything I look at seems to do it differently. I'm particularly worried about getting data from my own table through #TITRE, #TEXTE, #DESCRIPTIF, etc. Is this even possible?

Incidentally, this is for 1.9.2e, but I'll be updating it for 2.0 after it's released.

Hi

2008/10/7 Thomas Sutton <thomas@bouncingorange.com>:

<http://doc.spip.org/@La-base-de-donnees,4391&gt;\. While a little bit sparse
(it describes only four of the thirteen arrays mentioned), this says that I
need a line like:

       $table_des_tables['things'] = 'things';

The description of the table is better calculated in the version 2.0.
I think that you won't need this declaration.

Now to figure out how to do documents and keywords...

Documents and keywords :
------------------------------------

From memory you just have to create the tables spip_things_documents

and spip_things_mots.
I haven't tested it with the prefix of the plugin, but it should work.
There is no reason, because normally, when SPIP calculates the queries
from the loops, it doesn't take into account the prefix of the tables
(except in case of collisions)

The id of these tables must be id_thing, id_document, and id_mot :
that's necessary for SPIP to detect automatically the join queries.

Now you can use
<BOUCLE(THINGS){id_document=#ID_DOCUMENT}> : Displays the things
related to a document
<BOUCLE(THINGS){id_mot=#ID_MOT}> : Displays the things that are tagged
with a keyword
or even <BOUCLE(THINGS){titre_mot=The important keyword name}> : The
same , but the keyword id is calculated (it's easier to maintain)

You can also experiment other joins (but here, I'm not really certain
about the order)
<BOUCLE(DOCUMENTS things){id_rubrique=#ID_RUBRIQUE}>#ID_THING</BOUCLE>
It should display the list of the things that are related to
documents that are attached to a specific section
(Note that the first is in uppercase : this is the main table, that
will be first used to start the construction of the multiple join
query)

Hope that my memory still works a little,

.Gilles
---

Regards,

Thomas Sutton

Web Developer
bouncingorange
graphic + web design

On 07/10/2008, at 12:42 PM, Thomas Sutton wrote:

Hi folks,

I'm writing a plug-in to add a new type of "thing" to SPIP. I've started
my plug-in (using the information on doc.spip.net) and have it creating my
table and am now trying to write my loops and tags based on the examples in
ecrire/public/boucles.php and also those in
plugins/forms_et_plugins_1_9_1/public/forms_boucles.php. Alas, I have been
unable to find any sort of pattern in the source nor any documentation for
doing this. Can anyone recommend an example that I can use as a guide to:

1) Creating the database tables for a new type of thing (similar to
ARTICLES, SITES, and the rest).
2) Create a new type of loop to query this table (I've got this working).
3) Create tags to access the data inside these loops. I'll need my own
tags (#DESTINATION) and built-in ones (#TITRE, #DESCRIPTIF) to work on
these.
4) Add join tables so that I can attach documents and mots to these
things.
5) Create new criteria like {id_thing} so that I can get the documents
attached to a 'thing'.
6) Tie this all together with an admin interface.

I've got some of this working in files mentioned in <fonctions> tags in
plugin.xml:

1) I add $tables_principale['spip_things'] = array('field' => ..., 'key'
=> ...); and call creer_base(); and my table is created.
2) I've added a function boucle_THINGS_dist($id, &$bs) based similar to
the functions for ARTICLES, etc. This works so long as I don't
3) I'm able to create "static" tags, that don't depend on the loops, but
getting it to fetch data from within a loop is beyond me. I've tried to use
champ_sql to fetch fields in my own tags but this just fails with ''Unknown
SQL table "things", '_loop'' and doesn't log anything in tmp/*.log.
4) ?
5) ?
6) I've used the affiche_milieu to start adding my list of things, new
thing link, etc. when exec==naviguer, and it'll call my exec/ function, so
this looks like it'll be OK.

I've looked through <http://plugins.spip.net/&gt; (incidentally, should every
page really have the same title?) but couldn't find anything that looks
similar, and I've been staring at various files from SPIP and Forms et
Tables for a day and a half now (which hasn't really helped) so I'm stuck.

Is there a reference to the data model ($tables_principales, and the other
magical variables, champ_sql, etc. etc.) and loops/tags for plug-in writers?
Failing that, is there a good example that I can look at? Everything I look
at seems to do it differently. I'm particularly worried about getting data
from my own table through #TITRE, #TEXTE, #DESCRIPTIF, etc. Is this even
possible?

Incidentally, this is for 1.9.2e, but I'll be updating it for 2.0 after
it's released.

_______________________________________________
spip-en@rezo.net - http://listes.rezo.net/mailman/listinfo/spip-en

2008/10/7 Gilles VINCENT <gilles.vincent@gmail.com>:

Now you can use
<BOUCLE(THINGS){id_document=#ID_DOCUMENT}> : Displays the things
related to a document

Of course the opposite join automatically works :
<BOUCLE(DOCUMENTS){id_thing=#ENV{myParameter}}> : Displays the
documents related to the thing which id is equal to myParameter

.Gilles

Hi Gilles,

On 07/10/2008, at 4:51 PM, Gilles VINCENT wrote:

2008/10/7 Thomas Sutton <thomas@bouncingorange.com>:

<http://doc.spip.org/@La-base-de-donnees,4391&gt;\. While a little bit sparse
(it describes only four of the thirteen arrays mentioned), this says that I
need a line like:

       $table_des_tables['things'] = 'things';

The description of the table is better calculated in the version 2.0.
I think that you won't need this declaration.

Great! I did seem really odd that some stuff works without it (the loop runs the right number of times) but other stuff does not (none of the tags inside the loop work). Everything I've seen of 2.0 looks fantastic. I'm looking forward to the release.

Now to figure out how to do documents and keywords...

Documents and keywords :
------------------------------------
From memory you just have to create the tables spip_things_documents
and spip_things_mots.
I haven't tested it with the prefix of the plugin, but it should work.
There is no reason, because normally, when SPIP calculates the queries
from the loops, it doesn't take into account the prefix of the tables
(except in case of collisions)

The id of these tables must be id_thing, id_document, and id_mot :
that's necessary for SPIP to detect automatically the join queries.

Now you can use
<BOUCLE(THINGS){id_document=#ID_DOCUMENT}> : Displays the things
related to a document
<BOUCLE(THINGS){id_mot=#ID_MOT}> : Displays the things that are tagged
with a keyword
or even <BOUCLE(THINGS){titre_mot=The important keyword name}> : The
same , but the keyword id is calculated (it's easier to maintain)

You can also experiment other joins (but here, I'm not really certain
about the order)
<BOUCLE(DOCUMENTS things){id_rubrique=#ID_RUBRIQUE}>#ID_THING</BOUCLE>
It should display the list of the things that are related to
documents that are attached to a specific section
(Note that the first is in uppercase : this is the main table, that
will be first used to start the construction of the multiple join
query)

Hope that my memory still works a little,

I'd hoped it would be this simple, and it is. Perhaps this won't take me two weeks after all. :wink:

Is it possible to create a loop that takes it's data from some source other than the database? Data in a PHP array, for example?

I'm writing this all up as a tutorial on our internal wiki. I'll have to put the finished document online somewhere...

Thanks!

Thomas Sutton

Web Developer
bouncingorange
graphic + web design

2008/10/7 Thomas Sutton <thomas@bouncingorange.com>:

I'd hoped it would be this simple, and it is. Perhaps this won't take me two
weeks after all. :wink:

I hope also.
SPIP is sometimes really magic :slight_smile:

Is it possible to create a loop that takes it's data from some source other
than the database? Data in a PHP array, for example?

The only solution to get some PHP values in the loops is to use #EVAL{}
Mixed with #SET, #GET, and #ARRAY, you should make it possible.
(#ARRAY may read a serialized array)

I'm writing this all up as a tutorial on our internal wiki. I'll have to put
the finished document online somewhere...

Great news !!

There is a website that id dedicaced to plugins : http://plugins.spip.net
Maybe you contribution could be published here.
(There is also http://doc.spip.org for the developpers)

Both sites aren't translated yet, but your contribution should be a good start

.Gilles

Hi Gilles,

On 07/10/2008, at 5:34 PM, Gilles VINCENT wrote:

2008/10/7 Thomas Sutton <thomas@bouncingorange.com>:

I'd hoped it would be this simple, and it is. Perhaps this won't take me two
weeks after all. :wink:

I hope also.
SPIP is sometimes really magic :slight_smile:

Is it possible to create a loop that takes it's data from some source other
than the database? Data in a PHP array, for example?

The only solution to get some PHP values in the loops is to use #EVAL{}
Mixed with #SET, #GET, and #ARRAY, you should make it possible.
(#ARRAY may read a serialized array)

I was thinking more of writing

<?php

function boucle_ALOOP_dist($id_boucle, &$boucles) {
  // Something
  return calculer_boucle($id_boucle, $boucles);
}

?>

where the objects that <BOUCLE(ALOOP)> loops over are not from a database query but instead, a PHP array, a DOM tree, an Iterable object, etc. Perhaps by stuffing the data for the loop into some variable in $boucles[$id_boucle] before the call to calculer_boucle()? Is this at all possible, or should I just import the data into a table before wanting to use it?

I'm writing this all up as a tutorial on our internal wiki. I'll have to put
the finished document online somewhere...

Great news !!

There is a website that id dedicaced to plugins : http://plugins.spip.net
Maybe you contribution could be published here.
(There is also http://doc.spip.org for the developpers)

Both sites aren't translated yet, but your contribution should be a good start

I've had a look at both. <http://plugins.spip.net/&gt; is going to be a fantastic resource. I've already found a few plug-ins that we're interested in using through it. :slight_smile:

<http://doc.spip.org/&gt; has been good in places, but horribly frustrating in others. After I get this plug-in finished and tested on the commissioning client's web-site, we're going to use it on a few more sites. Once I'm sure it's not going to embarrass us, we'll be releasing it under an open source licence. We've also got a second plug-in on the way in a month or two which we'll be releasing.

Regards,

Thomas Sutton

Web Developer
bouncingorange
graphic + web design

where the objects that <BOUCLE(ALOOP)> loops over are not from a database
query but instead, a PHP array, a DOM tree, an Iterable object, etc. Perhaps
by stuffing the data for the loop into some variable in $boucles[$id_boucle]
before the call to calculer_boucle()? Is this at all possible, or should I
just import the data into a table before wanting to use it?

This was nearly put into SPIP 2.0, but we're still not quite sure
about the correct syntax, so in the meantime it is in the "SPIP 2.0
Reloaded" plugin by Cédric (no documentation yet).

svn://zone.spip.org/spip-zone/_plugins_/_test_/spip_2_reloaded

-- Fil

Doesn't this answer your request?

George

Quoting Thomas Sutton <thomas@bouncingorange.com>:

Is it possible to create a loop that takes it's data from some source other
than the database? Data in a PHP array, for example?

where the objects that <BOUCLE(ALOOP)> loops over are not from a
database query but instead, a PHP array, a DOM tree, an Iterable
object, etc. Perhaps by stuffing the data for the loop into some
variable in $boucles[$id_boucle] before the call to calculer_boucle ()?
Is this at all possible, or should I just import the data into a table
before wanting to use it?

Hi George,

On 07/10/2008, at 9:25 PM, george@diwanalarab.com wrote:

Doesn't this answer your request?

Boucler sur un tableau, un compteur, les langues, une condition ... - SPIP-Contrib

The code you linked to looks like a good starting place. I'll need to have a play around with it and see if I can understand it and get it to do what I want. Thanks.

I notice that it adds it's information to $tables_principales.This will result in the table being created it creer_base() is called (if a new plug-in gets installed, for example) won't it?

Quoting Thomas Sutton <thomas@bouncingorange.com>:

Is it possible to create a loop that takes it's data from some source other
than the database? Data in a PHP array, for example?

where the objects that <BOUCLE(ALOOP)> loops over are not from a
database query but instead, a PHP array, a DOM tree, an Iterable
object, etc. Perhaps by stuffing the data for the loop into some
variable in $boucles[$id_boucle] before the call to calculer_boucle ()?
Is this at all possible, or should I just import the data into a table
before wanting to use it?

Cheers,

Thomas Sutton
web developer
bouncingorange

Quoting Thomas Sutton <thomas@bouncingorange.com>:

Hi George,

On 07/10/2008, at 9:25 PM, george@diwanalarab.com wrote:

Doesn't this answer your request?

Boucler sur un tableau, un compteur, les langues, une condition ... - SPIP-Contrib

The code you linked to looks like a good starting place. I'll need to
have a play around with it and see if I can understand it and get it to
do what I want. Thanks.

I notice that it adds it's information to $tables_principales.This will
result in the table being created it creer_base() is called (if a new
plug-in gets installed, for example) won't it?

I haven't tried it, but I think it doesn't adds information, rather it checks the presence of an entry in $tables_principales corresponding to the loop's object that's why the article advises to add a dummy entry in that variable.

George

Hello Thomas,

There is an article in french that is a bit more detailled here:

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/browser/_plugins_/_dev_/basefiltrageip

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- http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_db.php#L15
2- http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_db.php#L32
3- http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_db.php#L44

Then you declare the relations between them (joints):
http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_db.php#L52
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:
http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_db.php#L55
(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:
http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/plugin.xml#L25
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:
http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_upgrade.php#L7
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 withou 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 :frowning: ):
http://www.spip-contrib.net/CreerSaBalise
http://www.spip-contrib.net/NouvelleBalise

See http://trac.spip.org/trac/spip-zone/browser/_plugins_/_stable_/FpipR/1.9.2/balise 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 :smiley:

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 you <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/browser/branches/spip-1.9.2/ecrire/public/balises.php

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.

Pierre

PS: So, you have an internal documentation wiki? why not just directly having it public on doc.spip.org and spip-contrib wiki? it will help everyone :wink:
PPS: (I haven't checked how all these things evolved with spip2, but I guess the compiler hasn't changed much on this)

Thomas Sutton wrote:

Hi list,

Next time, I'll have lunch *before* sending such a message. I went and
sat in the park for an hour, came back and found
<http://doc.spip.org/@La-base-de-donnees,4391&gt;\. While a little bit
sparse (it describes only four of the thirteen arrays mentioned), this
says that I need a line like:

$table_des_tables['things'] = 'things';

in my base/things.php file. I don't understand *why* this makes a
difference (my loop ran the correct number times before, it just
wouldn't access any data), but I can now use {numeric_column > 0} and
#TITRE, and it works.

Now to figure out how to do documents and keywords...

Regards,

Thomas Sutton

Web Developer
bouncingorange
graphic + web design

On 07/10/2008, at 12:42 PM, Thomas Sutton wrote:

Hi folks,

I'm writing a plug-in to add a new type of "thing" to SPIP. I've
started my plug-in (using the information on doc.spip.net) and have it
creating my table and am now trying to write my loops and tags based
on the examples in ecrire/public/boucles.php and also those in
plugins/forms_et_plugins_1_9_1/public/forms_boucles.php. Alas, I have
been unable to find any sort of pattern in the source nor any
documentation for doing this. Can anyone recommend an example that I
can use as a guide to:

1) Creating the database tables for a new type of thing (similar to
ARTICLES, SITES, and the rest).
2) Create a new type of loop to query this table (I've got this working).
3) Create tags to access the data inside these loops. I'll need my own
tags (#DESTINATION) and built-in ones (#TITRE, #DESCRIPTIF) to work on
these.
4) Add join tables so that I can attach documents and mots to these
things.
5) Create new criteria like {id_thing} so that I can get the documents
attached to a 'thing'.
6) Tie this all together with an admin interface.

I've got some of this working in files mentioned in <fonctions> tags
in plugin.xml:

1) I add $tables_principale['spip_things'] = array('field' => ...,
'key' => ...); and call creer_base(); and my table is created.
2) I've added a function boucle_THINGS_dist($id, &$bs) based similar
to the functions for ARTICLES, etc. This works so long as I don't
3) I'm able to create "static" tags, that don't depend on the loops,
but getting it to fetch data from within a loop is beyond me. I've
tried to use champ_sql to fetch fields in my own tags but this just
fails with ''Unknown SQL table "things", '_loop'' and doesn't log
anything in tmp/*.log.
4) ?
5) ?
6) I've used the affiche_milieu to start adding my list of things, new
thing link, etc. when exec==naviguer, and it'll call my exec/
function, so this looks like it'll be OK.

I've looked through <http://plugins.spip.net/&gt; (incidentally, should
every page really have the same title?) but couldn't find anything
that looks similar, and I've been staring at various files from SPIP
and Forms et Tables for a day and a half now (which hasn't really
helped) so I'm stuck.

Is there a reference to the data model ($tables_principales, and the
other magical variables, champ_sql, etc. etc.) and loops/tags for
plug-in writers? Failing that, is there a good example that I can look
at? Everything I look at seems to do it differently. I'm particularly
worried about getting data from my own table through #TITRE, #TEXTE,
#DESCRIPTIF, etc. Is this even possible?

Incidentally, this is for 1.9.2e, but I'll be updating it for 2.0
after it's released.

Hi Pierre,

Thanks for the (very detailed) reply.

On 08/10/2008, at 12:02 AM, Pierre Andrews wrote:

Hello Thomas,

There is an article in french that is a bit more detailled here:
Accés SPIP aux tables non-SPIP et jointures - SPIP-Contrib

Yet another interesting document I missed. From now on, the spip-contrib.net "site plan" and my web-browser's find function will be my first port of call. I'm learning more and more French search terms every day. :slight_smile:

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/browser/_plugins_/_dev_/basefiltrageip

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;

This is reasonably straight forward.

Then you declare the relations between them (joints):
http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_db.php#L52
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';

Does this tell SPIP to join spip_articles with spip_mots_articles? Or to join spip_articles to spip_mots THROUGH spip_mots_articles?

Also: can you explain why the key have 'spip_' and the values do not? If I understood the reasons for these sorts of differences, I think I'd be less confused.

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:
http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_db.php#L55
(if you do not do this table_des_tables thing, then SPIP doesn't know that BOUCLE_(XXX) is about the table spip_xxx)

If I use the code:

  $table_des_tables['xxx'] = 'yyy';

Does this mean that loop XXX is about table yyy, or that loop YYY is about table xxx?

Once you do that, you should tell the plugin how to install itself when it's activated:
http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/plugin.xml#L25
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)

This is pretty clear from the documentation and examples. Thanks.

Here, the interesting part is the install part:
http://trac.spip.org/trac/spip-zone/browser/_plugins_/_dev_/basefiltrageip/base/basefiltrageip_upgrade.php#L7
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...)

This is what I was thinking (in my reply to George's link to the Boucles_sans_tables plugin). Does SPIP just check that the tables exist? Or does it check the schema (columns, types, etc) as well?

Once you have done all of this, then you can loop on your new tables withou 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)

So this means that #TEXTE (which comes from the `texte` column) will be processed with propre(). Can I do:

  $table_des_traitements['TEXTE']= 'propre(%s)';
  $table_des_traitements['TEXTE']= 'translate_to_pirate(%s)';

To do propre(), then translate_to_pirate()? Or does that need a single line like be 'translate_to_pirate(propre(%s))'? If I only want to process `spip_things.texte` is it like this?:

  $table_des_traitements['TEXTE']['things']= 'translate_to_pirate(%s)';

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 :frowning: ):
CreerSaBalise
http://www.spip-contrib.net/NouvelleBalise

I haven't seen these pages, but the code for balise_INTRODUCTION_dist() was pretty clear.

See http://trac.spip.org/trac/spip-zone/browser/_plugins_/_stable_/FpipR/1.9.2/balise 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 :smiley:

I'll need to figure out how to create dynamic tags later on, but I've seen a few example of that, so it shouldn't be too bad. :slight_smile:

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 you <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/browser/branches/spip-1.9.2/ecrire/public/balises.php

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.

Thanks again for the information. Reading the code is fine, but it's much better when someone explains it.

Pierre

PS: So, you have an internal documentation wiki? why not just directly having it public on doc.spip.org and spip-contrib wiki? it will help everyone :wink:

We use a wiki to document the way we do things at Bouncing Orange, including the way we use SPIP. Most of the SPIP pages are written when we've just figured out how to do something after fighting with SPIP for an hour or two, so there's sometimes a little bad language involved. :slight_smile: There are a few things in there that might be useful to other SPIP users, so I'll see if I can find some time to clean-up and post a few articles.

Cheers,

Thomas Sutton

Web Developer
bouncingorange
graphic + web design