SPIP 3.0

SPIP 3 is a major upgrade. The following features are of special note: the private area has been completely revamped using customisable templates, a high degree of modularity (all non-core functionalities are now in plugins), and a revolutionary DATA loop which allows SPIP to connect to many new kinds of data.

  • New in : SPIP 3.0

SPIP 2.0 could already be used as a “framework” for developing Web applications, well beyond SPIP’s original purpose as a simple CMS. SPIP 3 pushes the logic of the “framework” further so that it can now be applied to SPIP itself: The whole private area of SPIP has been revamped into templates, themselves based on the tools and features offered by SPIP’s template language.

This rewrite of the private area was an opportunity to rethink all the editorial objects and standardise their use to make them as generic as possible: most of the historical particularities of each object (and associated processing exceptions in SPIP code) have been reduced to a simple declaration.

In this way the creation of new editorial objects and the customisation of existing objects have become much easier and quicker.

SPIP 3 also concludes the modularisation of the software into plugins which SPIP 2 had begun: all the features offered by SPIP 2 are now based on a core SPIP 3 accompanied by 23 plugins.

This segmentation of the core has made it possible to complete the system of APIs and entry points for plugin developers.

SPIP 3 has drawn heavily on developments made by the SPIP-Zone community, and this marks a reversal: it is no longer the core programme which is encouraging the development of plugins, but instead development of plugins which, by their experimentation, is now fuelling the advance of SPIP. [1].

Among its new functions, SPIP 3 introduces a new loop, DATA which now makes it possible to loop over any kind of data, not only SQL tables. You can now browse an enum or a CSV, XML, YAML file. And furthermore, the DATA loop can even be used directly on a URL: thus it becomes possible to directly integrate data from a Google spreadsheet, an online calendar, a list of Youtube videos, photos on Flickr ... the Web becomes your database if not your oyster!

System requirement:
In order to function correctly, SPIP 3.0 needs PHP version 5.1.0 or newer.

A new private area [2]

Visually, the private area remains in continuity with previous versions, while using new icons:
-  * the main icons for navigation are a set made for SPIP by Sebastian Desbenoit
-  * the other icons are from a set which was derived from FatCow.

The revamping of the interface is essentially technical, though it has also solved a number of ergonomic flaws.

The navigation system has been redesigned. The drop-down menu, now fully navigable with the keyboard, also integrates delays for ease of use with the mouse and to be more robust against poorly controlled movements.

All of the private area is now in the form of templates stored in the prive/squelettes directory, organised by subdirectories corresponding to a division of the page into blocks. This division is mainly used for reloading pieces of a page with Ajax, avoiding the full reload of pages and making actions more fluid [3].

Many entry points (primarily used by the core plugins) are available as pipelines to allow plugins to extend the functionality of the private area.

Pages of the private area have been reorganised and renamed. In particular all pages of editorial objects use a consistent naming scheme based on the object name.

The rewrite also allowed for an upgrade of the HTML structure and its accessibility. A programming guide has been set up so that plugin developers can use the same structures, and that the interfaces they add remain consistent with the core.

All objects lists are templates, and therefore easy to customize. They all have built-in sorting by column and ajax pagination.

A scaffolding system of object pages can automatically build a minimal interface for new objects declared by plugins. Each element of the default interface can then be customized according to the particularities of each object: [4].

All interactive forms are written as CVT forms, which ensures good management of error messages, input checking, ajax robust control, and multi-page forms are available if necessary. They can all be easily extended by plugins.

In edit mode SPIP 3 takes carriage returns into account (it is no longer necessary to use “_”).

Lastly, the private area includes a mechanism for themes, allowing them to customize the style sheets, or some or all of the icons. This mechanism can be used by a plugin offering the user to choose a preferred presentation.

Modularity with plugins

SPIP 3 has concluded the process of fragmenting the system into plugins. The core retains the management of articles, sections and authors, the template language and the structure of the private area. All other functions are now outsourced in plugins which are automatically installed with the default distribution of SPIP 3. These default plugins are storedin the directory plugins-dist/ [5]

This separation of functions has made it possible to define a number of implicit APIs, which are now available to plugin developers who can add functionalities without limit, modelled on SPIP’s native plugins.

In the field of plugins, SPIP 3 also brings a major overhaul of the formalism of the XML declaration. Besides improved readability, the question of translations of plugins is treated and there are automatic tools for listing plugins and for loading them.

Plugins can also make use of the upgrading functions of the core database (with recovery after timeout) by a simple declaration of the procedure.

Plugins distributed with SPIP default are:

  • Brèves deals with the editorial object News Items.
  • Companion displays information and support during the initial access to the private area.
  • Compressor) (already in SPIP 2.1) optimizes performance by compressing and concatenating CSS and JavaScript files used. This version of the plugin improves sheets by media. Using @media to concatenate all media into one file without changing the load order. Absolute urls are managed without protocol so that compressed style sheets work with both http and https.
  • Dump manages backup and recovery. The feature has been completely rewritten to provide a full and reliable backup. The backup format is now SQLite and all tables are systematically preserved.
  • Forum manages the editorial object forum, both for the public site and the private area. In addition to a redesigned moderation interface, forums can now be used on all SPIP objects (whether native or added through plugins).
  • Images (already in SPIP 2.1) manages all the image and colour filters which can be used in templates.
  • jQueryUI implements this library in SPIP. It facilitates the creation of dynamic graphical components: tabs, drag & drop, progress bars, widgets, effects ...
  • Mediabox integrates a default pop-in box to view media or offer interactions [6].
  • Medias [7] manages documents and images. It redesigns the interface and allows documents to be attached to any editorial object.
  • Mots manages keywords and keyword groups. The keywords can now be used on all editorial objects.
  • Organiseur provides the messaging functions and internal calendar of the editing area. The interface is completely redesigned. Internal messaging has email notification, and the calendar is based on the library FullCalendar allowing smooth navigation.
  • Pétitions handles petition items and offers a redesigned moderation interface.
  • Quill (Porte-plume) (already in SPIP 2.1) provides input assistance.
  • Revisions provides versioning which can be used on all SPIP editorial objects. The management interface has been updated.
  • SafeHTML (already in SPIP 2.1) takes care of potentially threatening external content.
  • Sites makes the “Syndicated sites” object work as well as syndicated articles. An interface to moderate syndicated articles is available.
  • Squelettes par rubriques (Templates per section) makes templates with suffixes work (article-2.html for articles of section 2 and its sub-sections, etc.).
  • Statistics provides the calculation of statistics for the sites and articles, with a completely redesigned interface.
  • Support of older browsers (already in SPIP 2.1) offers JavaScript functions which can be activated to allow older browsers to display your site correctly.
  • SVP manages plugins: search, installation, activation, update, ...
  • TextWheel is an engine that supports SPIP’s typographical shortcuts. They are now defined in a rule file in YAML format. This results in a significantly quicker calculation of shortcuts (up to twice as fast with some content), and facilitates the development and customization of shortcuts.
  • Urls Etendues supports contextual URLs or hierarchical URLSs, and provides a configuration interface and an optional advanced management interface to give fine control over the URLs of each page.
  • Vertebrae (already in SPIP 2.1) allows the webmaster to display the content of a SPIP SQL table using an automatically generated template based on the structure of the SQL table. This display is now in the private area.

A new set of default templates

To mark the occasion the default templates have been revised. With the division of styles into various independently reusable stylesheets (see Daisy method and One, two, three ... CSS), there is now a modular HTML/CSS basis on which a site can be built straight away. A full, harmonious typographical treatment, based on the Blueprint framework, forms one part of the CSS. The old stylesheet spip_style.css gives way to spip.css, which can be used as a complement to your usual framework.

The templates adopt a new HTML base structure which already put in place some HTML5 elements; they are responsive by default, and load conditional selectors for greater CSS flexibility.

New features

SQLite support has been greatly improved, and this, because of its simplicity of installation, is now the default database format (if supported by the server) when a new site is installed. [8]

The management of editorial objects has been made generic. Therefore authors, documents, keywords, forums, versioning, and logos can be used on any object (all SPIP objects and also any editorial object added by a plugin following the new declaration API).
The status, date of publication, language and translation is also generalised and can be used on any object by simple declaration.
When the signalling of concurrent editing is enabled, any editable object is now included in the signalling.

The identity of the site is complemented by a site slogan, something between the title and the description. It is displayed with the tag #SLOGAN_SITE_SPIP.

The safety screen is included as standard.

The level of tracking recorded in log files can be managed. By default, the logs are much less verbose in production and only report anomalies or important information. The log level can be adjusted for debug or development.

The occasional cron job which was file-based has been deleted in favour of a queue of pending tasks (which also manages periodic tasks). This job queue makes it possible to program asynchronous actions [9] and offers an easy to use API for plugin developers.

The Ajax in templates ({ajax} on INCLURE) supports ARIA attributes, improving its accessibility. In addition, the browsing history is also handled automatically through the API “History” of HTML5 [10]. The browser URL is updated automatically on ajax links allowing for problem-free backtracking.

Models used in text now automatically receive the environment of the template which display them.

The forms of the private area use HTML5 features, for example the required and placeholder attributes.

The characters # [] () {} <> can now be escaped in templates by using a slash: \. This makes it possible, for example, to write conditions on a check-box in a much more elegant way, without the square brackets of the name attribute interfering with those of the SPIP tag:

[(#ENV{param}|yes) 
   <label for='kindness'>With kindness?</label>
   <input type='checkbox' name='what_with\[\]' id='kindness' checked='checked' value='kindness' /> 
] 

Shortcuts

There is now a shortcut for abbreviations: [NHS|National Health Service] (or [SNCF|Société Nationale des Chemins de fer Français{fr}] to indicate a different language from the main text) generates an abbr tag [11].

Document models accept width or height arguments to limit their size: <docxx|largeur=150>.

Double-entry tables (whether horizontally or vertically) are now handled correctly.

It is no longer necessary to use the shortcut " _ " to produce a simple carriage return or line break. A carriage return in the text does the job. To maintain the previous behaviour, this feature can be disabled by placing define ('_AUTOBR', '') in mes_options.php.

Loops

SPIP’s system of loops has been generalised so that it applies no longer directly to a SQL table but to an iterator. An SQL iterator maintains the traditional operation of loops on SQL tables, but it is now possible to iterate over any given set of data: Iterators for SPIP - the (DATA) loop

A first example of an application for this: the loop DATA extends the operation of loops to structured information in tabular form. It becomes possible to loop over a CSV file, or on a remote url that returns JSON information ... :

To ensure the easy migration of templates containing POUR and CONDITION loops, these loops are also supported as special cases of the DATA loop, with the previous syntax.

Criteria

{si ...}: lets you place a condition on the execution of a loop which depends on the context of the template rather than on values in the database.

{tri ...}: used with the #TRI tag for easy sorting.

{feuille}: makes it possible to select sections without children (those at the bottom of the hierarchy).

{noeud}: makes it possible to select sections which have child sections.

{!racine}: excludes the first level sections (sectors).

{profondeur=3}: selects the only third-level sections (the root is level 0, sectors are level 1, then underneath, level 2 etc.).

Tags

Models can have a cache if they contain the #CACHE tag, but by default they do not, as before.

In the private area, templates are not cached either, unless #CACHE is present.

#LOGO_DOCUMENT may take arguments which determine the display mode of the logo:

  • auto (by default, corresponding to the mode of functioning in previous versions) automatically displays the thumbnail of the document if available, otherwise an overview, or else the icon for the document type.
  • icone indicates that the icon should be displayed.
  • apercu displays a preview image only, even if a thumbnail exists.
  • vignette displays the thumbnail of the document if it exists, otherwise nothing.

The tag #SPIP_CRON is no longer used. If it is present in a template, it will be ignored.

#BOUTON_ACTION{label, url, class, confirm, title, callback} generates a mini HTML form with a button labelled “label” and clicking on it triggers a POST to “url”. It is better to use this button than a link when the “url” page will modify the database. If “class” contains the value “ajax”, the button will trigger a reload of the ajax block which contains it. “confirm” allows you to specify a message asking for user confirmation. “title” is the title attribute of the button, and “callback” the javascript function to call when the button is clicked.

#INFO_XXX{article, 13}: makes it possible to find the element #XXX without needing an ARTICLES loop. A similar code can be used for any loop and any tag of the loop: #INFO_TITRE{article, 13},#INFO_NOM{author, 2}, ...

#CONFIG{name} displays the value of the configuration meta name. If the meta is an array, it is possible to directly access the values using the syntax #CONFIG{name/subvalue} which will return the subvalue of the meta name. To access the meta for a specific table of a plugin, use the syntax #CONFIG{/metamyplugin/name}: by starting with a /, we indicate that we want the meta name of the table spip_metamyplugin instead of using the spip_meta table of SPIP.
In all cases, it is possible to specify a second argument to provide a default to be used if the meta does not exist: #CONFIG{name, default_value}.

Like #CONFIG{name/subvalue}, the tags #ENV, #GET and #SESSION allow slashes to reach sub-array values, thus: #ENV{name/subvalue}, #GET{table/key/subkey}, #SESSION{prefs/colour}.

#PUBLISHED: tests the status (published or unpublished) of an object. Used in a loop, it implicitly deals with the current object. Otherwise it can be used with explicit arguments: [(#PUBLISHED{article, 3}|oui) ... ] on any other object.

#CLE and #VALEUR are the two tags that display the key and value in a DATA loop.
#VALEUR{x} display the subvalue x if #VALEUR is an array (so, equivalent to [(#VALEUR|table_valeur{x}) ]). Several sub indexes can be stringed together, #VALEUR{x/y/z} in order to display a value in a sub-level of the array.

#PRODUIRE returns the name of a static file produced from a template. This is useful for a calculated stylesheet or a javascript file. The syntax is the same as for the #INCLURE tag: #PRODUIRE{fond=mystyle.css, color=ffffff} to use the template mystyle.css.html

#LISTE{a,b,c} simply returns an array containing the values a, b and c. This is a simplified notation equivalent to #ARRAY{0,a,1,b,2,c}.

#TOTAL_UNIQUE returns the number of items displayed through the filter |unique. If |unique{name} is used, the same name must be referenced in the tag: #TOTAL_UNIQUE{name}.

In the private area:

#AIDER{subtitle} will display a link to the section of SPIP’s help under the heading subtitle.

#BOITE_OUVRIR, #BOITE_PIED and #BOITE_FERMER make the styled boxes of the editing area available for wider use:

  • #BOITE_OUVRIR{title, class} opens a box (title can be omitted). The styled classes are: simple, info, notes, shortcuts and important.
  • Any HTML after this tag is put in the box.
  • #BOITE_PIED to go to the bottom of the box when a footer is required.
  • #BOITE_FERMER closes the dialogue box.

For many examples, see the page of the private area ecrire/?exec=charte_boites of the plugin http://plugins.spip.net/dev

#FORMULAIRE_RECHERCHE_ECRIRE displays the search form of the private area. It can take two arguments. The first argument gives the URL to which it points, and the second, a class. In the presence of the javascript the form behaves as a link to the url passing the search parameter provided by the entered value. If the second argument is ajax, the form causes the reloading of the containing ajax block, in other words it acts as an Ajax link: #FORMULAIRE_RECHERCHE_ECRIRE{#SELF, ajax}.

#CHEMIN_IMAGE{article-24.png} returns the path to the icon article-24.png of the editing area theme currently in use by the logged-in user.

Filters

|lien_ou_expose can be used, for example, to build a menu with several links and marking the currently selected item with <strong> while maintaining a link <a> for other items. Example: [(#URL_PAGE{mypage}|{lien_ou_expose{label, #ENV{page}|}=={mypage, class, title, rel})].

|singulier_ou_pluriel displays one string or another depending on the number to which it is applied: [(#TOTAL_BOUCLE|singulier_ou_pluriel{1_article,nb_articles})]. 1_article and nb_articles are language strings that receive the number as an argument @nb@. If the number is zero, the filter is ignored.

|balise_img can quickly build an HTML <img> tag from a file name which includes the width and height for faster page rendering: [(#CHEMIN{myimage.png}|balise_img{alt, class})]

|affdate_debut_fin displays an interval of time in a reader-friendly way, taking into account whether the start date and end date are or are not the same day, month or year, and displaying the time or not according to the second argument (oui or non. Example: [(#DATE_DEBUT|date_debut_fin{#DATE_FIN, oui})].

|timestamp adds a timestamp in the form ?1234567890 to a filename, where the number represents the file date in seconds since 1 January 1970. This filter is useful, for example, when referencing a style sheet, in order to make sure that the browser will reload the file when it is changed:
[<link rel="stylesheet" href="(#CHEMIN{css/perso.css}|timestamp)"  type="text/css" />]

|objet_icone returns the standard icon of a SPIP object. By default, the 24px version is returned, unless the size 16 or 32 is requested as an argument: [(#OBJECT|objet_icone{16})].

|objet_info returns a property of a SPIP object as declared (or automatically supplied by SPIP) via the API declarer_tables_objets_sql.

|objet_afficher_nb displays the number of objects by taking into account the number to which the filter is applied and language strings declared for the object: [(#TOTAL_BOUCLE|objet_afficher_nb{auteur})], for example, will display1 author or 23 authors.

|wrap wraps an html tag around the text returned by the SPIP tag: [(#TITLE|wrap{<h3>})] will produce the same result as [<h3>(#TITLE)</h3>].

|generer_info_entite displays a field of a SPIP object: [(#ID_ARTICLE|generer_info_entite{article,titre})] returns the title of the article #ID_ARTICLE. This is equivalent to [(#INFO_TITRE{article,#ID_ARTICLE})].

In the private area:

|icone_horizontale displays an icon of the private area in horizontal format. The syntax is: [(#URL|{icone_horizontale{label,icon,function})]. icon can be designated by an abbreviation (e.g.article) for a format of 24px, or else explicitly (article-24.png). The third argument, function, specifies the kind of action associated with the icon and can be one of the following: add, del edit, new, or else omitted. Example: [(#URL_ECRIRE{auteur_edit,new=oui}|icone_horizontale{<:icone_creer_nouvel_auteur:>,auteur,new})]

|icone_verticale displays an icon of the private area in a vertical format. The syntax is the same as for |icone_horizontale.

|bouton_action_horizontal uses the same syntax as|icone_horizontale and displays a visually similar result. However the HTML markup will be of the same type as #BOUTON_ACTION to trigger a POST to the url and is therefore useful when the url makes a change to the database.

|sinon_interdire_acces placed anywhere in the page allows you to block access to it when the expression to which it applies is false. In this case an error page is displayed, unless a redirection URL is provided as an argument (you can then also provide an http redirect status as the second argument). It is generally used with an authorization check: [(#AUTORISER{modifier,article,#ID_ARTICLE}|sinon_interdire_acces)].

JavaScript

SPIP 3 uses the version 1.7.2 of jQuery and also includes jQuery UI (v. 1.8.20). It is possible to use a different version of jQuery in the public site simply by overloading javascript /jquery.js in the squelettes/ directory without any risk of breaking the private area.

Ajax links:
.ajax links no longer break the browsing history on browsers that support the HTML5 History API (Firefox, Safari, Chrome at the time of writing). That is to say that when you click on a link that reloads the SPIP ajax part of the page, the URL is updated in the browser and the visitor can click on the back button to return to the previous view.

Special classes on ajax links:

  • .nohistory indicates that the link should not affect the browser history when clicked.
  • .preload tells SPIP that the content of the ajax link should be preloaded when the page loads. Thus a click on the link will produce immediate update.
  • .nocache tells SPIP that the contents loaded by the ajax link should not be cached. So several clicks on the same link will cause as many loads from the server (by default, only the first load is made for a given url and the content is then cached by the browser).

Reloading remote ajax blocks
An .ajax link, by default, reloads the block that contains it, but sometimes it is necessary to reload an ajax block elsewhere on the page.

To achieve this, ajax blocks can be named when they are included:
<INCLURE{fond=...,ajax=blockname} />. The named block can then be reloaded by a call from ajaxReload('blockname'). A list of options can be passed as a second argument:

  • callback: callback function to be called after loading the ajax block.
  • args: list of arguments to be passed to the url when loading the block (makes it possible to change #ENV of an updated block).
  • history: whether or not the loading should affect the browsing history (default: false).
    Example:
    ajaxReload('blockname', { 
       callback:function(){alert('done');}, 
       args:{id_article:3}, 
       history:false 
       });
    

ajaxReload can also be used on a jQuery selector in which case it will cause a reload of the smallest ajax block containing the target element. It then accepts only the one possible argument (the array of options): $('#content').ajaxReload({args:{id_article:3}})

Javascript animations
During interactions triggered by the user, it is possible to trigger several generic animations which are all applied to a jQuery selector:

  • jQuery('#myid').animateLoading() triggers the loading animation on #myid (automatically triggered by ajax reloading).
  • jQuery('#myid').endLoading() stops the loading animation on #myid (automatically triggered by ajax reloading).
  • jQuery('#myid').animateAppend() animates the block#myid to show it was just added by the interaction.
  • jQuery('#myid').animateRemove() animates the block#myid to show it was deleted by the interaction.

Utility functions:
parametre_url() is used as a PHP version of the filter of the same name (|parametre_url) to add, delete, or retrieve parameters of a URL:

  • parametre_url(url,arg,value) adds arg=value to url and returns the modified URL.
  • parametre_url(url,arg,'') removes the value of arg from url and returns the modified URL.
  • parametre_url(url,arg) returns the value of arg in url.

$('a').followLink() follows a link by simulating a click on it (takes into account whether the link is an ajax link or not).

Popins in the private area:
A link to a page in the private area with the class .popin opens a popin containing the centre of the target page (the content/ block). The link retains its ability to be opened in new tab by a right click and in this case the full page is displayed.

API

SPIP 3 brings several generic APIs to the management of editorial objects:

  • The API editer_liens manages tables of links between any two objects.
  • The creation of new editorial objects is simplified by a declarative API.
  • Management of the functions controlling the insertion, modification or publication of editorial objects: API editer_objet.
  • objet_test_si_publie($object, $id_object) tests if an object is published or not according to its status as declared (and according to a possible post-publication).
  • generer_url_ecrire_objet($object,$id_object,$args,$anchor) returns the url to the page of the object in the editing area. $args and $anchor are optional.
  • The pipeline optimiser_base_disparus is called when cleaning the database of objects which have been binned and the resulting dead links.

SPIP 3 also has management of a task queue which makes it possible to program tasks for future execution or simply set them as asynchronous (ASAP but without making the user wait).

The implementation of plugin configuration is simplified by the automated support for forms: #FORMULAIRE_CONFIGURER_XXX, and by the functions lire_config, ecrire_config and effacer_config (see Configurer une fonctionnalité de votre site, ou un plugin).

CVT forms Make it easy to construct multi-page forms, and also support automatic backup of text entry upon simple declaration.
The pipeline formulaire_fond provides a way to customize the background of forms (i.e. to edit the HTML of the form) [12]

In the SQL API:

  • The sql_skip function is added: it allows you to skip a set number of results.
  • The values null in sql_updateq and sql_insertq are translated into NULL for SQL and are no longer treated as an empty string.
  • The functions sql_demarrer_transaction and sql_terminer_transaction as the names suggest, are used respectively to start or close an SQL transaction [13].

A sandbox entry point, (public/sandbox.php) for template compilation, allows a plugin to manage the compilation of templates in a set of filters, to whitelist or blacklist functions, and to forbid PHP in templates.

Specialisation of criteria:
It is possible to prefix the name of the criterion function with the server name and/or the name of the table. The search is carried out in this order:

  1. critere_serveur_TABLE_mycriterion_dist,
  2. critere_serveur_TABLE_mycriterion,
  3. critere_serveur_mycriterion_dist,
  4. critere_serveur_mycriterion,
  5. critere_TABLE_mycriterion_dist,
  6. critere_TABLE_mycriterion,
  7. critere_mycriterion_dist,
  8. critere_mycriterion

In the file options.php of a plugin, or in the mes_options.php file, $GLOBALS['marqueur_skel'] .= ":prefix" can be used to differentiate the cache of compiled templates just as $GLOBALS['marker'] .= ":prefix" used to differentiate the cache of templates.

Footnotes

[1Besides The Quill which was already integrated to SPIP 2, this movement includes Afficher Objets, Base CSS, multi-page CVT forms, Forum, Job queue, Mediabox, Media Library, TextWheel, Editable URLs, Comments, and others. Some of these have inspired the development of SPIP’s core code, while others have been integrated after some alterations as new plugins.

[2This project has begun some time ago on SPIP-Zone, and those who have already used the plugins Navigation du privé or Média Library will recognise the changes.

[3For example, opening an article for editing becomes instantaneous thanks to Ajax pre-loading.

[5In SPIP 2.1 they were placed in the extensions/ which now disappears.

[6This is an alternative, based on the library Colorbox, to plugins such as ThickBox, LightBox ...

[7Known in SPIP 2 under the name “Media Library”

[8Support for PostGreSQL, however, should be considered experimental and incomplete for production use.

[9for example, to send an email “when it becomes possible,” allowing the user to continue working without waiting for that email to actually be sent

[10in all modern browsers, except Internet Explorer, which does not yet support it

[11The tag <acronym> is no longer used in HTML5.

[13Another function added, experimentally, is sql_preferer_transaction to bypass a problem of slowness on multiple insertions on SQLite which are executed faster in transactional mode. This function returnstrue in SQLite only.

Author George, Paolo Published : Updated : 19/04/23

Translations : عربي, English, français, Nederlands, українська