Si vous devez ouvrir votre site Plone pour de la syndication de contenus, vous risquez de rencontrer pas mal de problèmes avec les caractères accentués.

D'abord il faut savoir que le monde XML travaille essentiellement avec de l'UTF-8 et que les outils d'affichage de flux XML sont très souvent incompatibles avec du XML codé en iso-8859-1.

Si votre site est en UTF-8, aucun problème, les flux RSS générés par Plone en standard pourront être lus par tout le monde.

Dans le cas contraire, ce qui est le cas le plus général, plusieurs problèmes se posent :

  • Imaginons que vous êtes dans un intranet et que vous désiriez syndiquer des contenus d'un site à l'autre, si les applications XML d'extraction et d'affichage du flux savent lire le XML en ISO-8859-1, il suffit que vos flux RSS générés par Plone soient également codés en ISO-8859-1.
    Ca fait beaucoup de "si", et en plus si vous ne changez rien sur Plone2, et bien ça ne marchera pas car le XML n'est pas permissif, et car les flux RSS standards de Plone n'ont pas l'entête iso-8859-1.
    Pour y remédier , procéder de la manière suivante :
    • Customisez le DTML-Method appelé rssBody situé dans portal_skins/cmf_Legacy
    • Changez  <?xml version="1.0"?>  en   <?xml version="1.0" encoding="iso-8859-1" ?>  
    • Attention n'utilisez pas le ZPT appelé rssBody situé dans portal_skins/plone_templates sinon c'est un plantage assuré au premier accent rencontré.
  • Par contre, si plus sérieusement, vous voulez que vos contenus soient lus par tout le monde sur le net via une syndication, il n'y a pas d'autre choix que de passer par l'UTF-8 et incorporer dans votre rssBody une fonction de traduction pour les titres et les descriptions. Donc, une fonction du type :
    unicode(VarContent, ’iso-8859-1’).encode(’utf-8’)
    Tant qu'à faire on utilisera aussi le rssBody en ZPT  (situé dans portal_skins/plone_templates )
    Cette solution marchera aussi sur un intranet franco-frenchie, car même si les programmes d'extraction et d'affichage des flux prévoient un affichage en iso-8859-1, il ne devrait pas y avoir de souci dans l'autre sens car le codage UTF-8 est universel, ou alors l'appli d'extraction XML est très mal faite.

    Mais il ne suffit pas de le dire, car par défaut ça plante (bogue signalé sur Plone.org) :
    • Votre site Plone est codé en iso-8859-1, et en français. Très bien, à priori mieux vaut ne pas toucher sauf pour des besoins particuliers (c'est dans portal_properties/site_properties qu'on voit ça)
    • Par contre votre instance Zope est peut-être paramétrée avec le langage par défaut, il faut que la directive locale soit instanciée à "fr" ou à "fr_FR" ou à "french" (tout dépend du système d'exploitation, sous Windows c'est "fr", sous Debian "fr_FR" si je me souviens bien ...). Avec Zope 2.7 c'est dans le fichier zope.conf qu'on change ça. Ce fichier est situé dans le dossier instance_zope/etc sur le système de fichiers local au serveur.
      Chez moi, si je ne change pas cette directive la fonction de traduction ISO > UTF plante.
    • Redémarrez votre instance Zope, si ça plante, pas de stress,  c'est que votre directive locale n'est pas bonne.
    • Maintenant la fonction unicode(VarContent, ’iso-8859-1’).encode(’utf-8’) va marcher. Donc on crée dans le dossier Custom un script python appelé iso2utf avec un paramètre appelé chaineIso dont voici le code :

      from Products.CMFCore.utils import getToolByName
      prop   = getToolByName(context, "portal_properties")
      errors="strict"
      default="utf-8"
      try:
        charset = prop.site_properties.getProperty("default_charset", default)
      except:
        charset=default
      if charset.lower() in ("utf-8", "utf8"):
        return unicode(chaineIso, "utf-8", errors)
      else:
        return unicode(chaineIso, charset, errors).encode("utf-8", errors)

    • Par sécurité on attribue un Proxy rôle Manager à ce script (onglet Proxy sur iso2utf)
    • On va complètement refaire les fonctions de RSS car suivant qu'on se trouve dans un dossier ou dans une rubrique (Topic) ça ne fonctionne pas de la même manière. Et pour laisser de côté les différentes fonctions RSS de Plone qui ne fonctionnent pas correctement, on va créer deux nouveaux flux rss différents, l'un pour les dossiers RSSfolder et l'autre pour les rubrique RSStopic. A titre d'exemple on fait le RSS des dossiers en ZPT et celui des rubriques en DTML.
    • On commence par supprimer l'ancienne action rss.
      On va dans la ZMI, votre_site/portal_actions, on supprime l'action avec l'Id rss.
    • On va ajouter deux nouvelles actions (en bas de page add action)  :

      La première action pour les dossiers (avec le même Id que l'ancienne) :
      Name : flux RSS de ce dossier 
      Id : rss
      Action : string:$object_url/RSSfolder
      Condition : python:(object.meta_type == 'Plone Folder') and (portal.portal_syndication.isSyndicationAllowed(object))
      Permission : View
      Category : document_actions
      Visible : coché

      La deuxième action pour les rubriques :
      Name : flux RSS de cette rubrique
      Id : rssTopic
      Action : string:$object_url/RSStopic
      Condition : python:(object.meta_type == 'Portal Topic') and (portal.portal_syndication.isSyndicationAllowed(object))
      Permission : View
      Category : document_actions
      Visible : coché
    • On va associer la nouvelle action à l'icône rss.gif 
      On va dans la ZMI site_plone/portal_actionicons
      Dans les champs en bas de page on remplit de la manière suivante :
      Category : Plone
      Action ID : rssTopic
      Action Title : Syndicate Topic
      Priority : 0
      Icone : rss.gif
      On clique sur Add
    • Il faut également ajouter une action dans site_plone/portal_syndication pour les onglets d'activation de la syndication des Topics
      Name: Syndication
      Id: syndication
      Action: string:${object_url}/portal_form/synPropertiesForm
      Condition: python: (object.meta_type == 'Portal Topic') and (portal.portal_syndication.isSiteSyndicationAllowed())
      Permission: Manage properties
      Category: object
      Visible: coché
    • On s'occupe du flux RSS des dossiers :

      Dans site_plone/portal_skins/custom, on crée d'abord un nouveau Page Template appelé RSSBodyFolder.
      Le code est le suivant (il fait plusieurs fois appel à iso2utf) :

      <?xml version="1.0"?>
      <rdf:RDF
      xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:dc="http://purl.org/dc/elements/1.1/"
      xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
      xmlns="http://purl.org/rss/1.0/"
      xmlns:tal="http://xml.zope.org/namespaces/tal"
      >
      <channel tal:attributes="rdf:about here/absolute_url">

      <title tal:define="portal_title python: here.iso2utf(here.portal_properties.title());
      here_title python: here.iso2utf(here.title_or_id());
      template_title python: here.iso2utf(template.title_or_id());
      here_title python:test( portal_title != here_title,
      here_title,
      template_title )"
      tal:content="string:$portal_title - $here_title">Title of page</title>
      <link tal:content="here/portal_url"></link>
      <description tal:content="python: here.iso2utf(here.Description())">

      </description>
      <image rdf:resource="logo.jpg" />
      <sy:updatePeriod><tal:replace replace="here/portal_syndication/getUpdatePeriod" /></sy:updatePeriod>
      <sy:updateFrequency><tal:replace replace="here/portal_syndication/getUpdateFrequency" /></sy:updateFrequency>
      <sy:updateBase><tal:replace replace="python: here.portal_syndication.getHTML4UpdateBase(here)" /></sy:updateBase>
      <items>
      <rdf:Seq>
      <tal:define define="max_items here/portal_syndication/getMaxItems">
      <tal:repeat repeat="item python:here.portal_syndication.getSyndicatableContent(here)">
      <rdf:li tal:attributes="resource item/absolute_url" />
      </tal:repeat>
      </tal:define>
      </rdf:Seq>
      </items>
      </channel>
      <tal:define define="max_items here/portal_syndication/getMaxItems">
      <tal:repeat repeat="item python: here.portal_syndication.getSyndicatableContent(here)">
      <item tal:attributes="rdf:about item/Identifier">
      <title tal:content="python:here.iso2utf(item.Title())"></title>
      <link tal:content="item/Identifier"></link>
      <description tal:content="python:here.iso2utf(item.Description())">
      </description>
      <dc:publisher tal:content="python:here.iso2utf(item.Publisher())"></dc:publisher>
      <dc:creator tal:content="python:here.iso2utf(item.Creator())"></dc:creator>
      <dc:rights tal:content="python:here.iso2utf(item.Rights())"></dc:rights>
      <tal:repeat repeat="subj item/Subject">
      <dc:subject tal:content="python:here.iso2utf(subj)">
      </dc:subject>
      </tal:repeat>
      <dc:date tal:content="python: DateTime(item.Date()).strftime(’%Y-%m-%dT%H:%M+00:00’)"></dc:date>
      </item>
      </tal:repeat>
      </tal:define>
      </rdf:RDF>

      On crée ensuite un DTML Method qui lancera le flux RSS si la syndication est activée sur le dossier.
      Donc créer toujours dans portal_skins/custom une DTML Method appelée RSSfolder (c'est elle qui sera appelée par le clic sur l'icône RSS des folder).  Voici le code :

      <dtml-if expr="portal_syndication.isSyndicationAllowed(this())">
      Content-type: text/xml

      <dtml-var RSSBodyFolder>
      <dtml-else>
      <dtml-call "REQUEST[’RESPONSE’].redirect(absolute_url()+’/rssDisabled?portal_status_message=Syndication+is+Disabled’)">
      </dtml-if>

    • Passons aux RSS des rubriques. Pour l'exemple on fait tout en DTML :

      Créons une DTML Method appelée RSSBodyTopic, voici le code :

      <?xml version="1.0" ?>

      <rdf:RDF
      xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:dc="http://purl.org/dc/elements/1.1/"
      xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
      xmlns="http://purl.org/rss/1.0/"
      >
      <channel rdf:about="<dtml-var absolute_url>">
      <dtml-let uTitle="iso2utf(this().title_or_id())">
      <dtml-let uDescription="iso2utf(this().Description())">
      <title>&dtml-uTitle;</title>
      <link>&dtml-portal_url;</link>
      <description>
      &dtml-uDescription;
      </description>
      </dtml-let>
      </dtml-let>

      <image rdf:resource="logo.jpg" />
      <sy:updatePeriod><dtml-var expr="portal_syndication.getUpdatePeriod(this())"></sy:updatePeriod>
      <sy:updateFrequency><dtml-var expr="portal_syndication.getUpdateFrequency(this())"></sy:updateFrequency>
      <sy:updateBase><dtml-var expr="portal_syndication.getHTML4UpdateBase(this())"></sy:updateBase>

      <dtml-comment>Start Items Elements</dtml-comment>
      <items>
      <rdf:Seq>
      <dtml-let max_items="portal_syndication.getMaxItems(this())">
      <dtml-in expr="portal_syndication.getSyndicatableContent(this())"
      size="max_items" reverse sort="modified" orphan="1" skip_unauthorized>
      <dtml-let obj="_.getitem(’sequence-item’, 0 )">
      <rdf:li resource="<dtml-var expr="obj.getObject().absolute_url()">" />

      </dtml-let>
      </dtml-in>
      </dtml-let>
      </rdf:Seq>
      </items>
      </channel>
      <dtml-comment>End Items Elements</dtml-comment>

      <dtml-comment>Start Item Elements</dtml-comment>
      <dtml-let max_items="portal_syndication.getMaxItems(this())">
      <dtml-in expr="portal_syndication.getSyndicatableContent(this())"
      size="max_items" reverse sort="modified" orphan="1" skip_unauthorized>
      <dtml-let obj="_.getitem(’sequence-item’, 0 )">
      <dtml-var expr="iso2utf(itemRSS(obj.getObject()))">
      </dtml-let>
      </dtml-in>
      </dtml-let>
      <dtml-comment>End Item Elements</dtml-comment>
      </rdf:RDF>


      Créons comme tout à l'heure la DTML Method associée à l'action et qui lance RSSBodyTopic si la syndication est activée sur la rubrique.
      Donc une nouvelle DTML Method dans Custom appelée RSStopic et voici le code :

      <dtml-if expr="portal_syndication.isSyndicationAllowed(this())">
      Content-type: text/xml

      <dtml-var RSSBodyTopic>
      <dtml-else>
      <dtml-call "REQUEST[’RESPONSE’].redirect(absolute_url() + ’/rssDisabled?portal_status_message=Syndication+is+Disabled’)">
      </dtml-if>


Voilà ce fut fastidieux mais en attendant que tout celà soit corrigé sur la prochaine version de Plone, et bien ça marche. Si quelqu'un de courageux veut bien prendre tout celà pour l'intégrer au code officiel de Plone. Il n'y a aucun problème, moi je ne sais pas faire.

Ensuite pour intégrer des flux XML externes dans votre site Plone c'est un autre problème et il existe des produits pour ça. Personnellement mon premier souci était d'intégrer les flux d'un site Plone sur des systèmes externes (sites en Php par exemple pour lesquels j'ai essayé toutes les astuces et toutes les ressources disponibles sur le web pendant plusieurs jours, pour enfin m'apercevoir qu'il y avait un bogue dans Plone).

Jean-Mat@macadames.com