Azioni sul documento
Keep It Simple! Come tenere sotto controllo le viste dei contenuti Plone
Questo aiuto si applica a:
Plone3, Plone2.5
Questo aiuto è destinato per:
Sviluppatori, Amministratori
Scopo
Questo documento vuole mostrare testimoniare come le "vecchie" tecnologie Zope 2 siano molto spesso sottovalutate, in quanto permettono ancora oggi agli utenti di ottenere risultati con il minimo sforzo, minima conoscenza di programmazione e senza "adattare" nulla! Keep It Simple!
Lo scopo di questo how-to è quello di migliorare la gestione delle viste predefinite (come un contenuto mostra il proprio contenuto ai visitatori) nelle cartelle del CMS Plone.
Il risultato finale del documento è la realizzazione di un prodotto estremamente semplice.
Dalle versioni di Plone che vanno dalla 2.1 in poi le cartelle (ATFolder e tipi derivati) hanno le seguenti funzionalità:
- Permettere di scegliere una vista predefinita selezionandola tra un insieme di possibili modelli.
- Permettere di scegliere un documento contenuto nella cartella come vista predefinita.
Entrambi i punti sono utilizzabili tramite l'onnipresente menù azioni. La funzionalità di selezione della vista predefinita è disponibile anche per altri contenuti che non siano cartelle. Molto spesso però i contenuti documentali hanno una sola vista, ma ci sono prodotti di terze parti che offrono contenuti con più viste selezionabili (ricordo ad esempio PloneArticle).
Sebbene la maggior parte dei benefici di questo documento sia subito visibile sulle cartelle (e su cartelle verranno fatti gli esempi) non dimentichiamo che le funzionalità aggiunte saranno globali a tutti i contenuti del sito in grado di utilizzare il menù "vista".
La scelta della vista predefinita dipende da una proprietà del tipo di documento che si sta visualizzando (nel nostro caso appunto la Cartella o Folder). E' possibile modificare questa lista di modelli andando a personalizzate la proprietà "Available view methods" del tipo voluto dal tool portal_types, accessibile tramite ZMI.
Per personalizzare (a livello di portale) i tipi di documenti selezionabili come vista predefinita del contenitore, è invece necessario (sempre via ZMI) accedere alla proprietà default_page_types, sotto al property sheet (scheda delle proprietà) site_properties. Questo è contenuto nel tool portal_properties.
Entrambe le funzionalità hanno un piccolo ma fastidioso limite che dipende dalla loro "globalità". Nel caso delle viste disponibili, queste sono le stesse per ogni tipo di documento. Le pagine predefinite sono invece globali a livello di portale.
Nell'esperienza di tutti i giorni con Plone risulta piuttosto fastidioso il primo problema: poter personalizzare e limitare la vista predefinita per una specifica cartella del portale. Ancora meglio spesso vorremmo impostare una vista particolare per una sola cartella del sito, bloccando anche la possibilità da parte utente di cambiare questa scelta.
La soluzione "semplice" in questi casi è la seguente: non usare una normale Cartella Plone (ATFolder), ma creare un tipo personalizzato (che magari estenda il tipo base). E' uno sforzo di programmazione minimo, ma questo significa anche una probabile esplosione dei tipi disponibili nel portale ("Cartella", "Cartella che mostra eventi", "Cartella ordinata per dimensione dei file", ...).
Un'altra vecchia soluzione che probabilmente ha salvato la pelle a molti sviluppatori ai tempi di Plone 2.0, è l'inclusione nella cartella di un template con id index_html. Questo metodo funziona ancora oggi e blocca in modo assoluto la funzionalità di cambio di vista, ma è a dir poco una tecnica da evitare in quanto mischia ciò che è layout con i contenuti stessi... Eliminando infatti la cartella viene anche perso il template (il codice del template!) usato come pagina index.
Vediamo come modificare il comportamento base di Plone per arrivare ad una soluzione flessibile che eviti nella maggior parte dei casi di doversi inventare nuovi tipi di contenuto.
Prerequisiti
Questa guida si riferisce alla versione 3 di Plone, ma vuole essere valida anche per la versione 2.5.
Verrà illustrato come costruire un prodotto che modifichi le funzionalità base di Plone per ottenere lo scopo. Una minima conoscenza di Python è necessaria!
Passi da eseguire
Creazione del prodotto
Prima di tutto questa guida necessita della scrittura di un po' di codice Python.
Ma dove inserire queste modifiche ai comportamenti del codice Plone?
Dato che la modifica del sorgente nativo di Plone non è mai una buona idea (si perdono facilmente le modifiche apportate ad ogni aggiornamento, soprattutto oggi giorno che viviamo nella fantastica era del buildout) scriviamo passo a passo un prodotto che si integri con Plone.
Per fare questo usiamo paster così come è insegnato nella guida sull'uso di zc.buildout fornita da Martin Aspeli (è disponibile una traduzione italiana nella sezione dei riferimenti).
paster create -t plone Products.ATCustomizableView
La creazione del nostro prodotto avviene in modo automatico dopo aver risposto alle domande di rito. Da evidenziare solo come alla domanda...
Enter zope2product (Are you creating a Zope 2 Product?) [False]:
...si debba rispondere True. In questo modo possiamo scrivere un egg che sia utilizzabile anche in Plone 2.5. Allo stesso modo è stata presa la scelta di mantenere il "vecchio" namespace Products.
Analisi del problema
Si tratta ora di trovare le porzioni di codice Plone che controllano funzionalità quali la vista predefinita e/o la selezione del documento predefinito.
Per sapere se l'utente corrente ha o non ha il potere di cambiare vista predefinita o selezionare un documento come vista predefinita è dato dalla chiamata di due metodi che sono condivisi da tutti gli ATContentTypes.
Questi metodi sono canSetDefaultPage e canSetLayout. Se scrivessimo un nostro tipo personalizzato e non volessimo in nessun caso la funzionalità di cambio di vista predefinita, ci basterebbe ridefinire questi due metodi nel nostro nuovo tipo per far loro ritornare sempre False.
Vogliamo effettivamente lavorare in questa direzione, ma non vogliamo che questo venga fatto per un ipotetico nuovo tipo di cartella... ma come?
Monkey patch: quell'oscuro alleato
Tra i "trucchi" più odiati nella comunità Plone (in buona parte per ottimi motivi) spicca l'uso di monkey patch. Questo è anche un modo estremamente potente di modificare codice altrui in modo non invasivo. Python è un linguaggio che a differenza di altri permette questo tipo di interventi che, non può essere nascosto, da ottimi risultati.
Consiste in breve nella modifica runtime del codice sorgente, ridefinendone una funzione, un metodo, una funzionalità senza toccare il codice originale.
Perché allora è una tecnica così sconsigliata? Il problema principale sta nel fatto che può diventare veramente difficile trovare problemi introdotti dall'uso di monkey patch. Il programmatore alla ricerca di un baco si troverebbe ad analizzare del codice che probabilmente non viene più eseguito poiché oscurato dalla monkey patch. Nel codice originale infatti non si ha nessun sentore dell'uso di questo tipo di tecnica.
Altro problema sono i possibili conflitti tra diverse applicazioni di questa tecnica allo stesso pezzo di codice: è estremamente facile (come faremo ora) sviluppare un prodotto che modifichi il codice runtime, ma è poi impossibile che due monkey patch diverse possano agire sulle stesse parti di codice. Semplicemente una oscurerà l'altra e il codice originale.
Detto questo la strada che si vuole qui prendere è esattamente quella della monkey patch, poiché è sicuramente il modo più rapido per ottenere il nostro risultato.
Modifichiamo il codice nel nostro __init__.py generato da paster (posto in Products.ATCustomizableView/Products/ATCustomizableView) come segue:
from Products.ATContentTypes.content.folder import ATFolder
def cvCanSetLayout(self):
return False
def cvCanSetDefaultPage(self):
return False
ATFolder.canSetLayout = cvCanSetLayout
ATFolder.canSetDefaultPage = cvCanSetDefaultPage
def initialize(context):
"""Initializer called when used as a Zope 2 product."""
Ecco come applichiamo la nostra mokey patch: all'avvio di Zope viene importata la classe ATFolder e i metodi canSetLayout e canSetDefaultPage vengono sovrascritti con versioni locali degli stessi metodi, che non fatto altro che ritornare sempre False. L'effetto finale è aver completamente disabilitato queste funzionalità per tutte le cartelle del portale (e per tutti i sottotipi di ATFolder)!
La soluzione proposta
Andate alla pagina iniziale di un sito Plone standard e impostate una vista (ad esempio "vista standard"). Ora accedere al portale tramite l'interfaccia ZMI. Posizionatevi sull'oggetto portale (Plone Site) e accedere alla scheda Properties.
Le properietà sono vecchie funzionalità di praticamente ogni oggetto Zope e sono estremamente utili e usate un po' dappertutto nell'architettura Plone.
Un loro pregio sta nel fatto che è molto semplice accedere e modificare queste proprietà, e che ne esistono di molti tipi diversi (stringhe, multiriga, selezioni, ...). Un loro difetto (ma è davvero un difetto?!) è che sono caratteristiche puramente a livello Zope e non sono modificabili da interfacce Plone, ne hanno tutte le funzionalità dei campi di Archetype.
Noterete come associata alla property layout c'è la stringa folder_listing che è appunto l'id del template che fornisce la vista standard selezionata. Se infatti modifichiamo manualmente questa stringa con folder_summary_view, otteniamo la "vista riassuntiva" in Plone.
Allo stesso modo è impostata e salvata nelle property dell'oggetto l'uso di un documento come vista predefinita. Selezionate la solita front-page o altro contenuto come vista predefinita e noterete che nelle proprietà nel sito Plone verrà scritto l'id del documento associato alla chiave default_page.
Per poter usare una vista predefinita che non sia tra quelle standard, ci basta quindi scrivere da ZMI nella property layout l'id di un template o di una vista Zope 3.
Il problema è che se ci limitiamo a questo un utente con sufficenti diritti potrà modificare la nostra impostazione da Plone, per poi non essere più in grado di tornare indietro (poiché la nostra vista potrebbe non fare parte delle viste definite per quel tipo di documento, non potrebbe selezionare di nuovo questo template).
La prima cosa che vogliamo fare è quindi non permettere (in alcuni casi) di poter modificare la vista predefinita. Per fare questo usiamo di nuovo le semplicissime funzionalità delle proprietà Zope.
Inventiamo quindi una convenzione per cui se la cartella corrente ha una proprietà quale fixed_layout (di tipo booleano), nessun cambio di vista sia possibile da Plone.
Modifichiamo il codice precedente con quello che segue:
from Products.ATContentTypes.content.folder import ATFolder
from Products.CMFDynamicViewFTI.browserdefault import BrowserDefaultMixin
originalCanSetLayout = BrowserDefaultMixin.canSetLayout
originalCanSetDefaultPage = BrowserDefaultMixin.canSetDefaultPage
def cvCanSetLayout(self):
if self.getProperty('fixed_layout', False):
return False
return originalCanSetLayout(self)
def cvCanSetDefaultPage(self):
if self.getProperty('fixed_layout', False):
return False
return originalCanSetDefaultPage(self)
BrowserDefaultMixin.canSetLayout = cvCanSetLayout
BrowserDefaultMixin.canSetDefaultPage = cvCanSetDefaultPage
def initialize(context):
"""Initializer called when used as a Zope 2 product."""
Il gioco è fatto. Provate a creare una cartella, impostare un layout (anche non previsto dalle viste disponibili del tipo Folder) e impostare la properità fixed_layout a True.
Noterete come il codice sopra non si limita all'uso e all'inclusione di ATFolder, ma agisce su tutti i contenuti che derivano da BrowserDefaultMixin. Sarebbe quindi ora possibile modificare un singolo documento di tipo "Pagina" del portale per usare una vista non standard!
L'assenza della proprietà fixed_layout è equivalente al suo valore lasciato a falso: il funzionamento di Plone quindi non risente minimamente della modifica.
Ulteriori sviluppi
E' di certo possibile migliorare questo prodotto fornendo:
- La possibilità di aumentare le viste disponibili per un dato oggetto (tramite patch del metodo getAvailableLayouts).
- Fornire un'interfaccia Plone per fissare il layout di una cartella.
- Disabilitare la singola funzionalità di impostare la vista predefinita o un oggetto predefinito, in modo indipendente.
- ... e così via.
Altre note
Sarebbe stato possibile (e forse sarebbe stata anche una tecnica più moderna) non basarsi sulla proprietà fixed_layout, ma inserire una nuova interfaccia quale IFixedLayout, da impostare via ZMI allo stesso modo delle proprietà (Esiste infatti la scheda "Interfaces").
Per l'esempio mostrato si è comunque voluti rimanere in tena di properties Zope, così come già utilizzato da ATContentTypes.
Riferimenti

twitter
