Vista browser
medio
Le viste di Zope 2 sono definite nella classe, come i metodi invocati dalla vista; ciò non va bene, e la classe cresce rapidamente. Oltretutto, poichè la gente non vuole rendere la propria classe più grande, comincia a inserire logica all'interno delle loro ZPT, mescolando codice e interfaccia. Altri preferiscono scrivere script Python o usare metodi esterni (external method) (che sono assolutamente migliori delle direttiva python: inglobate in ZPT). Tutto questo dimostra che c' è molta differenza fra logica e presentazione.Il problema è stato risolto dalle viste di Zope 3.
A che cosa penseresti se dicessimo che una vista su un oggetto è semplicemente un adattatore? L'obiettivo è quello di far sparire tutto quanto riguarda le viste da qualche altra parte. E questo è ciò che faremo. Spostiamo tutte queste cose in una classe vista Browser e nella configurazione zcml, in modo che la classe di base non deve più preoccuparsi di come i consumatori vedono i suoi contenuti, e non deve più fornire i metodi per ottenere i contenuti da visualizzare.
Un classe vista browser è un multi-adattatore, intendendo con questo che adatta un oggetto e la request.
Definiamo una semplice vista Browser per il nostro Archiver. Vogliamo poter chiamare http://myhostname/plone/Afolder/zip ed ottenere una file .zip contenente tutti gli oggetti nella cartella.
Inoltre desideriamo potere chiamare http://myhostname/plone/Adocument/zip ed ottenere il contenuto compresso del documento.
Poichè non possiamo usare direttamente le viste di Zope 3, dobbiamo usare le viste di Five:
>>> from Products.Five import BrowserView
>>> from Products.ATContentTypes.interface.archive import IArchiver
Definiamo la nostra vista, ereditando da BrowserView::
>>> class ArchiveView(BrowserView):
... """
... Vista su un oggetto per ottenere il suo contenuti compresso
... """
... def getZipFile(self,**kwargs):
... """
... """
... adapted = IArchiver(self.context)
... self.request.RESPONSE.setHeader('Content-Type','application/zip')
... self.request.RESPONSE.addHeader("Content-Disposition","filename=%s.zip" % self.context.getId())
... self.request.RESPONSE.write(adapted.getRawArchive(**kwargs)) Nota che qui stiamo usando un multi-adattatore, possiamo usare self.context come l'oggetto adattato e il self.request come la richiesta adattata (capisci da questo che l'init è qualcosa del tipo __init__ (self, context, request)).
Dai uno sguardo a adapted = IArchiver (self.context): qui usiamo di nuovo la magia del lookup degli adattatori per ottenere un archiver dell'oggetto adattato dalla vista. Questo ci permette di archiviare un documento o una cartella chiamando sempre gli stessi metodi (definiti nell'interfaccia IArchiver) senza preoccuparci di come sia stato fatto! È semplicemente magia!
Ok, ora abbiamo la nostra classe BrowserView, dovremmo mettergli un po' di colla intorno ed attaccarla a degli oggetti… Indovinate? useremo ancora delle direttive ZCML per farlo.
Diciamo di voler essere in grado di ottenere un file compresso solo per le cartelle:
<configure
xmlns="http://namespaces.zope.org/five"
xmlns:browser="http://namespaces.zope.org/browser">
<browser:page
for=".interface.IATFolder"
name="zip"
class=".browser.archive.ArchiveView"
attribute="getZipFile"
permission="zope2.View"
/>
<traversable class=".content.folder.ATFolder"/>
</configure> In questo modo definiamo una vista browser, proveniente da Products.ATContentTypes.browser.archive.ArchiveView, per tutti gli oggetti che implementano l'interfaccia IATFolder per l'utente avente il permesso View sull'oggetto. Questa vista può essere richiamata come metodo zip sull'oggetto. Non dimenticate la direttiva traversable sulla classe che implementa l'interfaccia nell'opzione for della direttiva browser. Siamo obbligati a fare questo perché il publisher di Zope 2 non conosce le viste di Zope 3 (che ci permetteranno di richiamare http://myhostname/plone/Afolder/zip).
Ora diciamo di voler essere in grado di comprimere documenti e cartelle e di farlo nella stessa direttiva zcml… Qui abbiamo un problema perché l'attributo for="A" (come per l'adattatore) nella direttiva browser accetta solo un'interfaccia… Vedremo un concetto utile (di cui avrei dovuto parlare nella sezione Interfaccia…): le interfacce Marker.
Interfacce Marker
Dal momento che non vogliamo definire una vista browser per ogni oggetto che implementa una specifica interfaccia, realizzeremo un'interfaccia Marker generale: IArchivable, e marcheremo gli oggetti Document e Folder come IArchivable. Ciò genererà una specie di gerarchia. Marker vuol dire: gli oggetti Document e Folder sono archiviabili (Archivable).
La prima cosa è definire l'interfaccia Marker.
Un'interfaccia Marker non è nient'altro che un'interfaccia fittizia:
>>> from zope.interface import Interface
>>> class IArchivable(Interface):
... """
... interfaccia marker per oggetti che si possono archiviare
... """ Dopo averla ottenuta, mettiamole di nuovo un po' di colla intorno con le nostre amichevoli direttive ZCML:
<configure
xmlns="http://namespaces.zope.org/five">
<implements
class=".content.document.ATDocument"
interface=".interface.archive.IArchivable"
/> In questo modo ATDocument e ATFolder sono conosciute come IArchivable.Ora possiamo usare una sola direttiva di vista browser che punta alla nostra interfaccia Marker nuova di zecca:
<configure
xmlns="http://namespaces.zope.org/five"
xmlns:browser="http://namespaces.zope.org/browser">
<browser:page
for=".interface.IArchivable"
name="zip"
class=".browser.archive.ArchiveView"
attribute="getZipFile"
permission="zope2.View"
/> Adesso sia la cartella che il documento “possono essere visti” come un archivio zip, e poichè non abbiamo dimenticato la direttiva traversable possiamo richiamare: http://myhostname/plone/Adocument/zip e http://myhostname/plone/Afolder/zip. Tutto questo grazie alla nostra vista Browser, alla nostra interfaccia Marker, ai nostri adattatori, alle nostre interfacce ed alle nostre classi.
