J'en avais ras le bol que Zope nécessite des url complètement tordues pour faire du virtual hosting. Ça m'empêchais entre autre d'utiliser Paste#urlmap pour dispatcher certaines url sur d'autres applis que Zope.
Du coup, j'ai tenté un truc tout con: plutôt que d'utiliser les RewriteRule d'Apache, récrire le PATH_INFO en englobant l'application Zope dans une autre. Et ça marche. Fiesta !
Voilà donc à quoi ça ressemble. J'utilise zopeproject. J'ai donc modifier le machin qui créer l'application Zope. A savoir le fichier startup.py comme ceci:
def application_factory(global_conf, conf='zope.conf', vhost='www.gawel.org'):
vhost = '/++vh++http:%s:80/++' % vhost
zopeapp = zope.app.wsgi.getWSGIApplication(zope_conf)
def zopewrapper(environ, start_response):
environ['PATH_INFO'] = vhost + environ['PATH_INFO']
return zopeapp(environ, start_response)
return zopewrapper
Et hop, ça roule. L'avantage, en plus d'avoir une url propre en entrée, c'est que vu que je développe aussi derrière Apache, j'ai juste eu à changer mon fichier debug.ini pour prendre en compte mon virtual host de développement.
En fait j'ai fais un peu mieux que tout ça, car comme dit au début, le but était d'utiliser Paste#urlmap. La source de la bidouille en question est ici.
Aller, pendant que j'y suis, j'en chiais aussi pas mal pour déterminer vers quel backend rediriger les requêtes dans varnish. Tester des ++ dans l'url, ça lui plaisait pas du tout. Vu que j'utilise Apache devant (surtout pour subversion), j'ai trouvé le truc. Il suffit d'activer le module headers:
# a2enmod headers
Puis rajouter un truc du genre dans votre virtualhost Apache:
RequestHeader set VARNISH_BACKEND gawel_org
Vous l'aurez compris, ceci ajoute un header à la requête. Ensuite, dans varnish, on test ce header:
if (req.http.VARNISH_BACKEND ~ "gawel_org") {
set req.backend = gawel_org;
}
Et le tour est joué. Il faut bien sur que toutes les requêtes entrantes aient ce header. Pour moi ce n'est pas un problème vu que tout passe par apache.
Vous êtes comme moi je suis sûr. Editer des document dans des éditeur WYSIWYG, ça vous saoul au plus haut point. Personnellement, je préfère de loin un bon éditeur et du reStructuredText.
La solution je l'avais depuis longtemps. Stocker les billets de mon blog dans un svn et les publier avec un framework quelconque. Ca fait un moment maintenant que j'utilise Zope3 pour ça, mais le code était assez dégueulasse. Je me suis donc dit que si je mettais tout ça un peu au propre, cela pourrait servir à d'autres. Je me suis donc lancé dans un énorme week-end de geeking. le résultat est gp.svnfolder, un package Zope3 permettant de publier un répertoire svn contenant des documents en reStructuredText.
Voyons plutôt. Il nous faut une repository valide:
>>> import os
>>> from os.path import split
>>> curdir = split(os.getcwd())[0]
>>> repos = os.path.join(curdir, 'tests', 'rstfolder')
Avec ça, on peut instancier un dossier svn:
>>> from gp.svnfolder.folder import SVNFolder
>>> folder = SVNFolder()
>>> folder.__name__ = 'blog'
>>> folder.path = u'file://%s' % repos
Et en voir le contenu:
>>> sorted(folder.keys())
[u'doc.rst', u'doc2.rst']
Et accéder aux fichiers:
>>> file = folder['doc.rst']
>>> print file.data
Document 1
==========
<BLANKLINE>
Ensuite il suffit d'utiliser les vues fournies pour rendre ces fichiers en html.
Voila. Je penses qu'avec un peu de motivation je pourrais facilement le rendre compatible avec Mercurial et Bazaar mais ça suffit à faire mon bonheur :)
Comme on le sait tous, écrire des expressions python dans des ZPT, c'est mal. J'ai donc voulu aller plus loin afin de pouvoir utiliser les tree xml sans avoir à faire des expressions python.
Avec Zope3, il est possible de rajouter des espaces de nom au expressions TAL (ah, les joies de la component architecture :). J'ai donc implémenté deux espaces de nom supplémentaire me permettant d'accéder aux méthodes find et findall d'un élément xml.
Ma template a donc maintenant cette jolie bouille:
<dl class="lastfm" tal:define="root view/xml">
<dt class="title" tal:content="view/title" />
<dd tal:repeat="track root">
<a tal:attributes=" href track/find:url/text">
<span tal:content="track/find:name/text" />
<span class="small"
tal:content="track/find:artist/text" />
</a>
</dd>
</dl>
Ce qui est tout de même plus agréable à regarder. Ce qui est déconcertant c'est la facilité d'implémentation. Il suffit de quelques lignes de code pour obtenir ce résultat.
Ce soir j'ai joué avec Zope3 et lxml. Le résultat est plutôt intéressant. On trouve en combinant les ZPT et lxml une bonne alternative au xslt.
Voyez plutôt. Une petite classe de vue:
# -*- coding: utf-8 -*-
import os
import random
from lxml import etree
from zope.publisher.browser import BrowserView
PREFIX = '/tmp'
def getxml(filename):
"""
return a ElementTree parsed from the file
"""
fd = open(os.path.join(PREFIX,filename))
doc = etree.parse(fd)
fd.close()
return doc.getroot()
class LastfmView(BrowserView):
"""
a view to render the xml
"""
views = ((u'Coup de coeur','recentlovedtracks.xml'),
(u'Ecouté récemment','recenttracks.xml'))
def __call__(self):
self.title, filename = random.choice(self.views)
self.xml = getxml(filename)
return super(LastfmView,self).__call__()
Associée à une petite template toute bête:
<dl class="lastfm" tal:define="root view/xml">
<dt class="title" tal:content="view/title" />
<dd tal:repeat="track root">
<a tal:attributes=" href python: track.find('url').text">
<div tal:content="python: track.find('name').text" />
<div class="small"
tal:content="python: track.find('artist').text" />
</a>
</dd>
</dl>
Et hop, vous obtenez le portlet lastfm de mon site :)
Vous pouvez voir le source complet ici avec le script bash qui dowload les xml.
A première vue, le plus simple moyen d'ajouter un objet dans un dossier serait le suivant:
>>> from zope.app.folder import Folder
>>> mycontent = Folder()
>>> mycontent.__name__ = 'myid'
>>> folder = Folder()
>>> folder['myid'] = mycontent
Cela fonctionne bien. L'inconvénient est que l'on perds toute la machinerie de zope.
J'ai donc cherché un moyen plus propre d'ajouter un objet. J'en suis arrivé a la conclusion que le mieux est d'utiliser une vue Adding. Ce qui nous donne:
from zope.app.container.browser.adding import Adding
from zope.app.event.objectevent import ObjectCreatedEvent
from contents import MyContent
class MyContentView(BrowserView):
""" a simple view """
def add(self):
view = Adding( self.context, self.request )
context = view.add( MyContent() )
notify(ObjectCreatedEvent(context))
Par ce moyen, on créer un objet proprement. Par exemple l'id seras généré à l'aide d'un INameChooser, etc.