SPIP 4.0

Memorandum of changes in SPIP 4.0


Rebranded private area

A great deal of ergonomic work has been initiated to make the private area adaptable to all screen sizes in order to facilitate its use.

The private area adapts better to the width of the screen, and starts to use different CSS variables to facilitate its maintenance.

The private area in SPIP4.0
Article Page with a blue theme
Editing an article (with the blue theme)

An elegant login screen

The default login page (login.html) is all re-styled, with a pink SPIP background.

Default login screen to SPIP 4.0

And if the pink surprises you, don’t panic! There is a new form in the site identity configuration. It allows you to choose the colour you want. And even better: it even allows you to add a beautiful background image.

Login screen with an image in SPIP 4.0

Iconography in SVG

A large part of the icons and illustrations in the private area of SPIP and its plugins have been converted to SVG.

  • Tickets #4468 and #4562: Harmonisation in the private area of the HTML & CSS of buttons
  • New icon set based on Numix theme (in SVG) for document types in the media library

Documents & Logos

A lot of work has been done on documents and logos:

  • The logos of articles and other editorial objects are stored like documents (in spip_documents in the database, and in IMG/logo/ on the disk, keeping the file name). A migration phase moves the logos to their new location on the disk.
  • Logos, image documents and image filters accept and support the SVG format
  • The img tag filter allows you to manage SVG files and declare their size
  • The new balise_svg filter allows you to embed the source code of an SVG image file into the html code directly. Can be handy for example for small icons.
  • Documents / logos can be uploaded by drag’n’drop, whatever their size (integration of the BigUp plugin in plugins-dist, improved for the occasion). The maximum size allowed is configurable.
  • Document templates (<docXX>, <imgXX>, ...) are revised and simplified and use the <figure> tag. Commit fa13018a.Warning: Templates doc.html, img.html and emb.html files are no longer used (and so any old overloading of these files into plugins or templates will no longer be used). Instead, we use the templates image.html, audio.html, video.html and file.html, corresponding to the document type. The document mode is no longer taken into account for the calculation of the template.
  • Possibility of declining the HTML files of the document templates by type of file (detailed mime type): file_text_csv.html or main: file_text.html ; or per extension: video_mp4.html. As a result your custom variants doc_[variant].html corresponding to the shortcut <docN|variant> will no longer work and should be declined: image_[variante].html, video_[variante].html, etc.
  • Writing SPIP shortcuts for document templates <docXX>, <imgXX> ou <embXX> becomes equivalent. The recommended writing is <docXX> (where XX is the document number).
  • The notion of Portfolio and document/image mode on documents disappears. Commit 9cf774b9. The constant _COMPORTEMENT_HISTORIQUE_PORTFOLIO declared to true allows the old functioning to be restored.

PHP compatibility

Limitation of the compatibility of SPIP 4.0 with PHP from 7.3 to 8.0. Numerous notices and deprecations have also been corrected. Note that SPIP 3.2 will be the last version of SPIP to be compatible with PHP 5.


SPIP 4.0 works with Mysql or Sqlite. It can update a database from a SPIP >= 2.0.0.

For an update of an earlier SPIP, a migration step to SPIP 3.2 will be necessary.

Extended template syntax

Added support for anonymous loops: <BOUCLE(ARTICLES)>

Tired of labelling? An anonymous loop does not need to specify its name!
It will automatically be assigned an identifier internally.

[(#REM) A loop named ]
[(#REM) An anonymous loop]

And if you are ambitious, you can put as many as you want in a template, and even nest them! But think about your successors, so that the code doesn’t become too anonymous to find their way around :)

    <ul class="rubriques">
<BOUCLE(RUBRIQUES){racine}{par num titre, titre}>
            <ul class="articles">
        <BOUCLE(ARTICLES){id_rubrique}{!par date}{0,5}>
            <li><a href="#URL_ARTICLE">#TITRE</a></li>

Adding non-conditional parts of loops, before <BB_boucle> and after </BB_boucle>

A syntax for non-conditional parts of loops is added: this content will always be displayed, whether there is a result or not, while allowing the use of loop-specific tags

This syntax is particularly useful for arrays of elements with sorting and filtering. If the loop does not return any elements, we still want to display the filters (to uncheck/change them).

[(#REM) A non-conditional part before/after the loop ]
    [(#REM) this part is displayed even if the loop does not return any articles  ]
    <h3>The articles</h3>
    Nombre :  #TOTAL_BOUCLE<br>
<BOUCLE_non-conditional(ARTICLES){id_article?}{id_rubrique?}{!par date}{0,5}{', '}>
    <a href="#URL_ARTICLE">#TITRE</a>
   [(#REM) this part is displayed even if the loop does not return any articles ]
   <nav class="pagination" role="navigation">#PAGINATION</nav>

This completes the conditional parts of the loops before <B_boucle> and after </B_boucle> which are only displayed if the loop has at least one result.

[(#REM) A conditional part before/after the loop ]
    [(#REM) this part is only displayed if the loop returns articles ]
    <h3>The articles</h3>
    Nombre :  #TOTAL_BOUCLE<br>
<BOUCLE_conditional(ARTICLES){id_article}{id_article?}{id_rubrique?}{!par date}{0,5}{', '}>
    <a href="#URL_ARTICLE">#TITRE</a>
    [(#REM) this part is only displayed if the loop returns articles  ]
    <nav class="pagination" role="navigation">#PAGINATION</nav>
    <h3>No article</h3>

The unconditional part of the loops can be coupled with the conditional part: in this case it frames the conditional part.

[(#REM) Mix non conditional / conditional before / after ]
<div class="articles">
   <h3>The articles</h3>
<BOUCLE_mix(ARTICLES){id_article}{id_article}{!par date}{0,5}{', '}>
    <nav class="pagination" role="navigation">#PAGINATION</nav>

Added support for loops in the conditional part of tags

Yes, we can now put loops in tags!
Indeed we can! In the conditional part of tags.

Some examples:



<BOUCLE_docs(DOCUMENTS){id_article}{par num titre, titre}{mode=document}>
        <a href="#URL_DOCUMENT">
        [(#TITRE|sinon{Document n°#ID_DOCUMENT})] \(#EXTENSION\)

Changes and inputs for templates

The tags #PAGINATION require a change in syntax and arguments.

The change is 1) to the wrapper tag, which should be nav 2) to the arguments.

1) The bounding tag must now be <nav class="pagination">.

Until SPIP 3.2, for example, we wrote:

[<p class='pagination'>(#PAGINATION)</p>]

With SPIP 4.0, you must now use:

[<nav class='pagination' role='navigation'>(#PAGINATION)</nav>]

2) There is now only one pagination template. As a result, differences in pagination rendering are made by passing arguments to it.

  • afficher_lien_precedent=oui to display the < to previous results
  • afficher_lien_suivant=oui to display the > which leads to the following results
  • afficher_lien_tous=oui to display a link to view all results simultaneously. In this case, the link text is "" by default, but it is possible to specify another label with the label_tous. Example : label_tous=tous.
  • nombre_liens_max is used to define the maximum number of pagination links displayed by the template. You can also use the constant _PAGINATION_NOMBRE_LIENS_MAX for the public area _PAGINATION_NOMBRE_LIENS_MAX_ECRIRE for the private area.

The first argument can indicate what type of pagination is needed. It can also be indicated with an argument type_pagination.
Example : #PAGINATION{naturel} is equivalent to #PAGINATION{type_pagination=naturel}.

  • type_pagination=page to display the page numbers: 1, 2, 3, 4...
  • type_pagination=rang to display the ranks: 0, 10, 20, 30...
  • type_pagination=naturel to display the ranks with 1 instead of 0 : 1, 10, 20, 30...
  • type_pagination=resultats to display the ranks from 10 to 10 starting at 1 : 1, 11, 21, 31...
  • type_pagination=page_precedent_suivant is the combination of a pagination of type page with previous and next links.

The use of the tag in the private area is detected automatically, making it unnecessary to pass the argument type_pagination=prive. However, since compatibility with the old syntax is assured on this point, the argument of the old version can be kept on old templates as follows:

[<nav class='pagination' role='navigation'>(#PAGINATION{prive})</nav>]

Criterion {id_?}

The {id_?} criterion allows you to make all possible connections with the environment variables. It therefore behaves as many criteria as possible {id_xxx ?} for the given loop.

Voir id_ ?

Function lister_champs_id_conditionnel and pipeline exclure_id_conditionnel

The {id_?} criterion is based directly on the function lister_champs_id_conditionnel($table) which returns a list of possible conditional selection fields for this table.

The calculation is made from the fields beginning with ’id_’ in the table, as well as a possible ’object’ field. It is then completed with the primary keys of the editorial tables which can be easily linked.

Finally, the result goes through the pipeline exclure_id_conditionnel which allows you to exclude certain fields from the list calculated by the function lister_champs_id_conditionnel. See examples in the plug-ins Brèves or SVP.

Criterion {par_ordre_liste champ,#LISTE{...}}

New criterion par_ordre_liste to order a loop in a specific sequence.

Criterion {fusion_supprimer}
This criterion is part of the Core. It was previously defined in the SPIP-Bonux plug-in. Important: You must update the SPIP-Bonux plug-in, if it is active, to version >= 3.7.1 before updating the site to SPIP 4.0 to avoid a conflict.

Filter and function identifiant_slug

Ticket #4628: adding the function identifier_slug(). Transforms plain text into a short name that can be used as an identifier, class, id, url... by keeping only alphanumeric characters and a separator.

Filters label_nettoyer and label_ponctuer

These filters are added to manage and make better use of SPIP’s historical language strings (sometimes with :, sometimes without).

  • label_nettoyer remove the: at end of text
  • label_ponctuer add : at end of text

Evolution of criteria {tri} and {par num xxx}

  • The {tri xxx} criterion accepts #TRI{par numéro, num xxx} behaving as {par num xxx} (elements without numbers are ordered after elements with numbers)
  • The {par num xxx} criterion integrate automatically criterion {par sinum xxx} before it: thus in (ARTICLES){par num titre}, items without numbers (or with the number 0) go after items with a number

Other points about templates

  • New filters ajouter_class, supprimer_class and commuter_class
  • Filter lien_ou_expose improved
  • Filter |singulier_ou_pluriel can be extended for each language by creating its own singulier_ou_pluriel_<lang> filter, which will be called if the language matches
  • New filter appliquer_si_filtre as a complement to appliquer_filtre: the 1st returns the initial content if the filter sought does not exist, the 2nd returns nothing.

Squelettes-dist (public area theme by default)

  • The default templates squelettes-dist now use an HTML5 syntax

Other inputs

  • Integration of the Javascript library Sortable.js to support drag and drop in a more fluid way, in particular to order documents, replacing the jQuery UI module.
  • A new lightweight, accessible, responsive and prettier modal box based on Lity
  • An XSLT stylesheet provides styled RSS feeds
  • The statistics graph redesigned (it uses d3.js)
  • The companion can be ignored for each author who already knows SPIP
  • The "limit to HTML4" option for the public area is removed. The default templates (the dist) are now HTML 5 compliant (backwards compatibility plug-in)
  • The summary attribute for tables is removed
  • Default h2 level headings, not h3 level as before (backwards compatibility plug-in)
  • The autoriser_exception() function accepts an * argument as an id: this allows it for any id. When we cancel an exception with *, it cancels all exceptions for that object, even those that would have been thrown individually with ids.
  • Filter heure_minutes accepts as an optional argument abbr when you do not wish to indicate the unit of minutes. Thus [(#DATE|heures_minutes{abbr})] will display, for example: 17h26
  • The constant _AUTO_SELECTION_RUBRIQUE, which automatically assigns a field when an article is created if its value is true now additionally accepts an integer: in this case it corresponds to the field identifier to be assigned by default.
  • If the 3rd parameter (class) of #BUTTON_ACTION{link, url, class ,confirmation message} contains "ajax", this class is added to the form that contains the button.


  • Update of all used JS libraries
  • [login] Ticket #3957: Do not display the padlock on the login page, too ambiguous with the https padlock of the browser.
  • [admin] The link "Show visitors" is not shown if there are no users with this status on the site.
  • [svp][plugins] If there is an XML error on a package.xml, it is directly explained on the plug-in management page.



  • Files plugin.xml from SPIP 2.x plug-ins are no longer interpreted. Only the paquet.xml are now. A plug-in that would therefore only have plugin.xml (without paquet.xml beside) will therefore no longer work from SPIP 4.0.


  • Ticket #4060 : Code cleaning < PHP 5.4
  • Cleaning code & CSS <= IE9
  • Cleaning htaccess (no longer compatible on URLs .php(3) for SPIP <= 1.8


  • Deletion of inc/mail deprecated since SPIP 2. Use for example :
    $envoyer_mail = charger_fonction('envoyer_mail', 'inc');
  • Deletion of action/preferer.php unused since SPIP 3.2 (deletion of ecrire/oo/).
  • Deletion of exec/valider_xml.php not functional, moved to the plug-in to be fixed Valider XML
  • Deletion of exec/fond_monobloc.php. Create the templates for the private area in prive/squelettes.

Tags / Filters / Criteria

  • Deletion of tags #DEBUT_SURLIGNE and #FIN_SURLIGNE deprecated since SPIP 2. Use CSS classes surlignable and pas_surlignable.
  • Filters aligner, aligner_gauche, aligner_droite, centrer, justifier and style_align are deleted. Use CSS code to obtain the same result.
  • Deletion of #NOOP. Use #VAL
  • Deletion of {datasource ...}. Use directly criterion {source ...}

PHP Functions

  • Deletion of echo_log() (useless now).
  • Deletion of spip_fetch_array(). Use sql_fetch() instead.
  • Deletion of generer_url_retour(). Use parametre_url() instead, with the parameter ’redirect’.
  • Deletion of charger_php_extension() (Use extension_loaded())
  • Deletion of revisions_articles(). Use article_modifier()
  • Deletion of revision_article(). Use article_modifier()
  • Deletion of articles_set(). Use article_modifier()
  • Deletion of insert_article(). Use article_inserer()
  • Deletion of instituer_article(). Use article_instituer()
  • Deletion of insert_auteur(). Use auteur_inserer()
  • Deletion of auteurs_set(). Use auteur_modifier()
  • Deletion of instituer_auteur(). Use auteur_instituer()
  • Deletion of revision_auteur(). Use auteur_modifier()
  • Deletion of insert_rubrique(). Use rubrique_inserer()
  • Deletion of revisions_rubriques(). Use rubrique_modifier()
  • Deletion of instituer_rubrique(). Use rubrique_instituer()
  • Deletion of admin_repair_plat(). The .plat files are no longer used. This function is no longer called since r14292
  • Deletion of version_svn_courante(). Use version_vcs_courante()
  • Deletion of lire_meta(). Use $GLOBALS['meta'][$nom] ou lire_config('nom')
  • Deletion of auteur_referent(). Use auteur_associer()
  • Deletion of table_jointure(). Use the API editer_liens or the link tables spip_xx_liens ou spip_yy_liens, accordingly.
  • Deletion of modifier_contenu(). Use the generic functions for the content modification API.
  • Deletion of revision_objet(). Use objet_modifier().
  • Deletion of notifier_publication_article().
  • Deletion of notifier_proposition_article().
  • Deletion of calcul_branche(). Use calcul_branche_in().
  • Deletion of ecrire_metas().
  • Deletion of spip_query_db(). Use sql_query() ou other.
  • Deletion of spip_get_lock() and spip_release_lock() which were no longer used.
  • Deletion of recuperer_entetes(). Use recuperer_entetes_complets().
  • Deletion of maj_version() and upgrade_vers() which are no longer useful.
  • Deletion of upgrade_types_documents(). Use directly creer_base_types_doc() from Medias plugin.


  • The <BOUCLE(POUR) and its criterion {tableau ...} are deprecated in favour of the <BOUCLE(DATA)> and its criterion {source table, ...}.
  • Similarly, the |foreach filter is deprecated in favour of the loop-based syntax (DATA).


-  Plug-ins breves, jquery_ui, organiseur, petitions, vertebres, squelettes_par_rubriques are no longer distributed with SPIP by default.
-  But breves, organiseur, petitions, squelettes_par_rubriques are still maintained!
-  jquery_ui is maintained by the community, but depreciated
-  vertebres although functional in SPIP 4.0 is abandoned: the Adminer plug-in is a more complete alternative.
To facilitate the deletion of their tables in the database during the 3.2 -> 4.0 migration, the dedicated plug-in Leon is made available to you: https://contrib.spip.net/leon-le-nettoyeur


PHP Functions

  • Deletion of insert_breve(). Use breve_inserer()
  • Deletion of revisions_breves(). Use breve_modifier()

Grenier plug-in

The grenier plug-in is cleaned up and only contains the functions removed from SPIP 3.2 and SPIP 4.0.
Anything prior to SPIP 3.2 is no longer present in it.

It contains the various functions and files removed from SPIP 4.0 that could potentially be used by templates or plug-ins.


PHP Functions

  • Deletion of document_set(). Use document_modifier()
  • Deletion of insert_document(). Use document_inserer()
  • Deletion of revision_document(). Use document_modifier()
  • Deletion of afficher_documents_colonne(). Use the expected inclusion or a true editorial object declaration (the document column is then automatically displayed on the object’s edit page)
  • Deletion of lien_objet(). Use generer_lien_entite()
  • Deletion of instituer_document(). Use document_instituer()


PHP Functions

  • Deletion of groupemots_inserer(). Use groupe_mots_inserer() or objet_inserer()
  • Deletion of groupemots_modifier(). Use groupe_mots_modifier() or objet_modifier()
  • Deletion of revision_groupe_mot(). Use groupe_mots_modifier()
  • Deletion of insert_mot(). Use mot_inserer()
  • Deletion of mots_set(). Use mot_modifier()
  • Deletion of revision_mot(). Use mot_modifier()


The use of petitions is now optional: they are disabled by default on new installations [1]. You can activate them in the site content configuration.

PHP Functions

  • Deletion of insert_syndic(). Use site_inserer()
  • Deletion of revisions_sites(). Use site_modifier()
  • Deletion of syndic_set(). Use site_modifier()
  • Deletion of instituer_syndic(). Use objet_instituer()


[1On existing installations, this is only the case if they were not used before

Author jack Published : Updated : 21/04/23

Translations : English, Español, français