Version 14 — Novembre 2014 — RastaPopoulos
La boucle DATA
de SPIP 3.0 permet facilement d’aller chercher des données sur le web dans n’importe quel format (ou presque, un format non supporté nativement ne nécessite qu’une fonction de mise en forme pour pouvoir ensuite être lu).
Il devient ainsi facile de présenter et mettre en forme en HTML ces données distantes au sein de n’importe quelle page d’un site.
Cependant, lorsqu’il s’agit ensuite d’agréger de multiples données de sources différentes dans une même liste, cela devient plus compliqué car la boucle DATA
ne permet pas de fusionner des sources. (Exemple : on ne peut pas dans une même boucle DATA
présenter des vidéos venant de youtube et de dailymotion sur un même thème)
L’idée est de créer une couche intermédiaire de mashup dans un format pivot. Ce format intermédiaire pourrait ensuite être automatiquement transformé en flux RSS ou ATOM ou en d’autres formats JSON, XML, ou même HTML, et/ou être utilisé par syndication dans le site.
Un connecteur est un squelette qui lit les données (externes) et les restitue dans le format mashup-data. C’est typiquement, mais pas exclusivement, un squelette qui utilise DATA
pour lire la source externe. Le squelette connecteur est rangé dans mashup-connectors/
L’idée est d’avoir un format simple a écrire dans le squelette de façon à se concentrer sur l’extraction des données, et ne pas avoir à se battre avec le spécificités techniques du format.
Pour cela YAML paraît tout indiqué. Il est naturellement lisible, et permet de structurer a minima les données sans difficulté (à confirmer à l’usage).
Chaque donnée extraite de la source serait ré-exprimée dans les champs suivants (inspirés de RSS) :
Les données sont une sous-liste du jeu de données, lui même décrit par les informations :
Exemple de connecteur (tiré de http://spip3.quejai.me/rss-afficher-un-flux-de-photos-de-flickr) :
#HTTP_HEADER{Content-type:text/plain}
--- # RSS to Mashup-data
-
id: "http://api.flickr.com/services/feeds/photos_public.gne?tags=parisweb2011&lang=fr-fr&format=rss_200"
<B_flickr>
start: [(#ENV{debutdata,0}|min{#GRAND_TOTAL|moins{#TOTAL_BOUCLE}})]
count: [(#TOTAL_BOUCLE)]
total: [(#GRAND_TOTAL)]
data:
<BOUCLE_flickr(DATA)
{source rss, http://api.flickr.com/services/feeds/photos_public.gne?tags=parisweb2011&lang=fr-fr&format=rss_200}
{pagination #ENV{limit,10} data}
>
-
id: [(#VALEUR{url})]
title: [(#VALEUR{titre})]
date: [(#VAL{Y-m-d H:i:s}|date{#VALEUR{date}})]
author: [(#VALEUR{lesauteurs})]
source: [(#VALEUR{url})]
url: [(#VALEUR{url})]
lang: [(#VALEUR{lang})]
content: [(#VALEUR{descriptif}|html_in_yaml)]
documents:
- [(#VALEUR{enclosures}|extraire_attribut{href})]
tags: ["[(#VALEUR{tags}|implode{'", "'}|strip_tags)]"]
</BOUCLE_flickr>
start: 0
count: 0
total: [(#GRAND_TOTAL)]
data: []
<//B_flickr>
On note le filtre |html_in_yaml
qui se contente d’indenter le html avec un nombre fixé d’espaces, et de le prefixer d’un « | » permettant ainsi la lecture du YAML :
function html_in_yaml($html,$indent=24){
$html = "|\n$html";
$html = str_replace("\r\n","\n",$html);
$html = str_replace("\r","\n",$html);
$html = str_replace("\n","\n".str_pad("",$indent," "),$html);
return $html;
}
La sortie YAML générée par ce connecteur est alors :
--- # RSS to Mashup-data
-
id: "http://api.flickr.com/services/feeds/photos_public.gne?tags=parisweb2011&lang=fr-fr&format=rss_200"
start: 0
count: 10
total: 20
data:
-
id: http://www.flickr.com/photos/parisweb/6806935280/
title: 13 octobre
date: 2012-03-04 20:03:53
author: parisweb
source: http://www.flickr.com/photos/parisweb/6806935280/
url: http://www.flickr.com/photos/parisweb/6806935280/
lang:
content: |
<p><a href="http://www.flickr.com/people/parisweb/">parisweb</a> a posté une photo :</p>
<p><a href="http://www.flickr.com/photos/parisweb/6806935280/" title="13 octobre"><img src="http://farm8.staticflickr.com/7038/6806935280_ce69bb8e60_m.jpg" width="240" height="160" alt="13 octobre" /></a></p>
documents:
- http://farm8.staticflickr.com/7038/6806935280_ce69bb8e60_b.jpg
tags: ["parisweb", "thanhnguyen", "parisweb2011"]
-
id: http://www.flickr.com/photos/parisweb/6953038151/
title: 13 octobre
date: 2012-03-04 20:01:10
author: parisweb
source: http://www.flickr.com/photos/parisweb/6953038151/
url: http://www.flickr.com/photos/parisweb/6953038151/
lang:
content: |
<p><a href="http://www.flickr.com/people/parisweb/">parisweb</a> a posté une photo :</p>
<p><a href="http://www.flickr.com/photos/parisweb/6953038151/" title="13 octobre"><img src="http://farm8.staticflickr.com/7040/6953038151_74626a07ee_m.jpg" width="240" height="160" alt="13 octobre" /></a></p>
documents:
- http://farm8.staticflickr.com/7040/6953038151_74626a07ee_b.jpg
tags: ["parisweb", "thanhnguyen", "parisweb2011"]
...
On y note le contenu humainement lisible, sans échappement du HTML notamment.
L’API de sortie est une URL de la forme
/mashup.api/nomduconnecteur.format?arg1=value&...
avec :
mashup-connectors/
qui fournit les données dans le format intermédiaire du mashupUn connecteur peut être spécifique à un jeu de données, ou plus générique, utilisant alors les arguments fournis dans l’appel à mashup.api
pour déterminer sa source.
Proxy
L’API de mashup peut être tout simplement utilisée comme un proxy qui va unifier et homogénéiser des flux de données externes hétérogènes. Chaque source de données prise en charge par un connecteur est alors disponible dans un des formats au choix pris en charge automatiquement par le plugin, pour n’importe quel usage par un site externe.
Sites syndiqués dans SPIP
Chaque flux de donnée peut être rediffusé naturellement sous n’importe quel format supporté par le plugin. En particulier, la rediffusion au format RSS permet de syndiquer les différentes sources de données dans SPIP, et de les agréger dans le site sous forme d’articles syndiqués.
Un exemple d’utilisation serait d’écrire un connecteur mashup-connectors/videoyoutube.html
qui va chercher des vidéos sur un thème donné dans Youtube (comme c’est fait ici par exemple http://spip3.quejai.me/afficher-des-videos-de-youtube), ainsi qu’un autre connecteur analogue mashup-connectors/videodailymotion.html
pour aller chercher les vidéos sur le même thème sur DailyMotion.
Ensuite, on peut ajouter les deux sources dans SPIP sous formes de sites syndiqués, par leurs urls respectives /mashup.api/videoyoutube.rss?query=spip
et /mashup.api/videodailymotion.rss?query=spip
.
Dans le site public, il devient facile, avec une boucle SYNDIC_ARTICLES
de présenter les deux sources en une liste unique de vidéos sur le même thème, provenant des deux sources.
Syndication en Articles
Toutefois, pour organiser un site à vocation éditoriale et constitué en partie de contenu propre et en partie de contenus externes syndiqués, il devient vite préférable de syndiquer les contenus externes dans des articles SPIP, qui offrent une plus grande liberté éditoriale, et permettent aussi l’édition manuelle pour corriger, compléter, annoter... un contenu venant d’une source externe. Par ailleurs, cela permet de mettre au même niveau de présentation, dans le site public, les contenus d’origine externe et les contenus propres du site.
Pour ce faire, le plugin ajouterait également une interface au backoffice pour administrer et syndiquer directement ses sources de données et construire son Mashup sans passer par les sites syndiqués.
Une source de donnée pourrait ainsi être utilisée pour créer automatiquement des articles à partir du format mashup-data avec les options de suivantes configurables source par source :
Le remplissage de l’article se ferait par une règle générique de conversion depuis mashup-data vers les champs de l’article, sauf si la donnée fournie par le connecteur contient un item « article » qui explicite le remplissage de l’article (au format html ou raccourcis SPIP)
-article :
- titre : "..."
- descriptif : "..."
- chapo : "..."
...
Ainsi, pour chaque source, il suffirait de réaliser son connecteur, puis d’aller dans l’interface et de déclarer une nouvelle source de données s’appuyant sur ce connecteur.
On peut ainsi construire un site complet de Mashup en y mixant du contenu rédactionnel propre ainsi que du contenu issu de différentes sources externes et hétérogènes.
En particulier, cela vaut pour les flux RSS : un connecteur mashup-connectors/fromrss.html
prendrait un flux RSS fourni par l’argument « url » et le mettrait au format data-mashup.
Il peut ensuite être syndiqué dans des articles SPIP, et cela permet de régler notamment le besoin fonctionnel de syndication d’un flux RSS en articles, sans stocker en doublon les données dans des articles syndiqués (ce que font déjà différemment au moins 3 plugins comme par exemple http://plugins.spip.net/rssarticle.html).
Le connecteur peut utiliser les arguments fournis dans l’url de l’API, mais attention, car cela peut poser des problèmes de sécurité. Par exemple le connecteur mashup-connectors/fromrss.html
, décrit ci-dessus qui utilise un argument url pour aller chercher n’importe quelle source RSS, permet à quelqu’un de mal intentionné de rediffuser un flux RSS de données malveillantes (ou pirates, illégales...) en usurpant l’identité du site SPIP.
Il faudrait prévoir deux types d’arguments : ceux passés par URL dans l’API, qui ne sont pas sûrs, et doivent être utilisés avec prudence, et ceux passés par le back-office quand on déclare une source de données externe via l’interface.
On peut imaginer que #ENV{safe/...}
contiendrait uniquement les arguments passés par déclaration dans le back-office, mais #ENV
contiendrait tous les arguments (que ce soit par url de l’API ou par déclaration).
On peut ainsi se référer à #ENV{safe/url}
pour utiliser une source externe de confiance dans un connecteur générique. Corolairement, il faudrait qu’une source de données déclarée dans le back-office dispose d’une URL propre pour pouvoir la rediffuser.
Question : est-ce que ça a un sens du coup de permettre de passer des arguments depuis l’url de l’API ? ou doit-on toujours obliger à passer par déclaration dans le back-office dans ce cas ?
Dans Drupal, il y a un module « Feeds » qui gère cette problématique de mélanger des données venant de plusieurs endroits dans un même flux normalisé. Puis ce flux peut être utilisé, pour insertion basique, pour créer du contenu, pour créer des utilisateurs, ou n’importe quoi d’autre.
La page du projet
La genèse du projet dans un article de 2009
Des captures de l’interface avec notamment ce commentaire :
Feeds distinguishes between Fetchers that pull or push content into Drupal, parsers that normalize this content into a PHP array and processors that « do stuff » with this normalized data.
</blockquote>