Azioni sul documento
FormLib e Tipi di Contenuto
Schema Tradizionale Archetypes
Attualmente tutta la comunità Plone utilizza generalmente il framework Archetypes per sviluppare i propri tipi di contenuto. Questo framework ha introdotto il suo concetto di schema, di widget e di campi che molti sviluppatori hanno trovato soddisfacente. Il punto è che da lì a poco (forse anche in contemporanea) la comunità Zope 3 stava già sviluppando i propri costrutti degli stessi concetti. Il risultato è stato zope.interface, zope.schema e zope.app.form. Infine è arrivata zope.formlib che dovrebbe aiutare a unire tutti i precedenti in una unica interfaccia utente.
Zope 3 Schema
Uno schema Zope 3 funziona in modo molto simile ad uno schema di Archetypes se lo si osserva da una prospettiva concettuale. Entrambi condividono i prinicipi basilari della definizione dei campi di un tipo. Una differenza eclatante è che un campo dello schema di Zope 3 non ha una associazione diretta (all'interno dello schema stesso) con nessuna logica di interfaccia. Cioè, esso non conosce quale widget verrà utilizzato per mostrare il suo contenuto in un form web. In questo modo vengono trattati separatamente il contenuto e l'interfaccia utente. Il contenuto, dopo tutto, non dovrebbe mai aver bisogno di conoscere o preoccuparsi di ciò che avviene nell'interfaccia. Si deve preoccupare solo dei dati.
L'Esempio
Affinchè i concetti possano essere chiari, i precedenti codici di esempio verranno smembrati in più comuni moduli python. Questo vuol dire che bisognerà migrare l'interfaccia di ISearch dal modulo browser al nuovo modulo interfaces.
Uno Schema
In Zope 3 gli schema sono definiti costruendo interfacce Zope 3, molto simili a quella creata nella seconda parte di questo tutorial. Una cosa su cui non è stata fatta chiarezza è che in effetti è stato creato uno schema di rappresentazione dei campi di ricerca.
Si inizia creando una nuova interfaccia nel modulo interfaces chiamato IExampleContent. Questa interfaccia avrà vari campi e dovrebbe presentarsi più o meno così:
class IExampleContent(interface.Interface):
title = schema.TextLine(title=u'Title',
required=True)
description = schema.Text(title=u'Description',
required=False)
funny = schema.Bool(title=u'Am I Funny?',
default=False,
required=True)
really_funny = schema.Bool(title=u'Am I Really and Truly Funny?',
default=False,
required=True)
Si può dare uno sguardo al risultante modulo interfaces per osservare cosa questo file debba includere e come si debba presentare.
Come menzionato poc'anzi, l'esempio dimostra che nello schema stesso non è disponibile alcuna proprietà di interfaccia utente. Tutto ciò che è descritto riguardo lo schema e i suoi campi è ciò che è richiesto per definire il tipo di dati che esso rappresenta.
Viste Formlib
Ora che è disponibile lo schema che descrive quali tipologie di campi si hanno, è possibile usare lo stesso approccio utilizzato per costruire le viste per il contenuto.
Lo schema che è stato definito contiene una buona quantità di campi di tipo Bool. Il widget predefinito per questa tipologia di campi mostra le opzioni Vero/Falso. Per gli scopi di questo esempio, si ha la necessità di mostrare Si/No invece che Vero/Falso e si dovrà modificare un apposito widget.
def YesNoWidget(field, request, true=_('yes'), false=_('no')):
vocabulary = schemavocab.SimpleVocabulary.fromItems(((true, True),
(false, False)))
return form_browser.RadioWidget(field, vocabulary, request)
Quello che è stato creato qui è un tipo di costrutto che fornisce il widget con le caratteristiche richieste, basato su un widget esistente: il RadioWidget. RadioWidget fa riferimento ad un vocabolario che ha come voci "yes" e "no". Quindi sarà possibile definire i campi del form basati sul nuovo schema.
example_content_fields = form.FormFields(interfaces.IExampleContent)
example_content_fields['really_funny'].custom_widget = YesNoWidget
Come nell'esempio fatto nella seconda parte del tutorial, i campi del form vengono generati usando form.FormFields(). Questa classe utilizza un'interfaccia come argomento e genera i campi del form (l'associazione al campo dello schema nell'interfaccia utente) che verranno usati per le viste. La seconda riga configura il widget personalizzato.
Ora non resta che definire la vista predefinita e la form di modifica:
class ExampleContentView(formbase.DisplayForm):
form_fields = example_content_fields
def __init__(self, *args, **kwargs):
formbase.DisplayForm.__init__(self, *args, **kwargs)
# a hack to make the content tab work
self.template.getId = lambda: 'index.html'
class ExampleContentEditForm(formbase.EditForm):
form_fields = example_content_fields
def __init__(self, *args, **kwargs):
formbase.EditForm.__init__(self, *args, **kwargs)
# a hack to make the content tab work
self.template.getId = lambda: 'edit.html'
E' stata definita ExampleContentView come estensione di formbase.DisplayForm e che fa in modo che formlib possa comprendere che questa è la vista normale e così tutti i widget dovrebbero essere mostrati in modalità view. La form di modifica, ExampleContentEditForm è stata definita per estendere formbase.EditForm così che formlib sia in grado di mostrare i widget in modalità di modifica. Ma questa non è la sola cosa che formlib conosce con il form di modifica. Come impostazione predefinita, formbase.EditForm definisce una unica action apply per questo form. Quando questo form è inviato attraverso la action apply, formlib sa che deve effettivamente aggiornare l'oggetto corrente con i valori immessi. Bisogna notare che un'azione apply andata a buon fine invocherà un IObjectModifiedEvent per salvare i dati immessi.
Certamente una volta che queste viste sono state definite, necessitano di essere registrate come componente di Zope 3. Questa operazione si compie attraverso configure.zcml
<browser:page
name="index.html"
for=".interfaces.IExampleContent"
class=".browser.ExampleContentView"
permission="zope2.View"
/>
<browser:page
name="edit.html"
for=".interfaces.IExampleContent"
class=".browser.ExampleContentEditForm"
permission="cmf.ModifyPortalContent"
/>
Questo frammento di zcml mostra che la vista predefinita ha il nome index.html, mentre la form di modifica edit.html.
E' possibile dare uno sguardo al modulo browser.py e al configure.zcml per osservarne i risultati.
Tipi
Ora che lo schema e le viste sono state tutte definite per questo tipo, è tempo di costruire l'effettiva classe del tipo. Per una migliore esercitazione, si definisce questa classe nel modulo content.
class FormlibExampleContent(atapi.BaseContent):
interface.implements(interfaces.IExampleContent)
title = fieldproperty.FieldProperty(interfaces.IExampleContent['title'])
description = fieldproperty.FieldProperty(interfaces.IExampleContent['description'])
funny = fieldproperty.FieldProperty(interfaces.IExampleContent['funny'])
really_funny = fieldproperty.FieldProperty(interfaces.IExampleContent['really_funny'])
La prima riga dopo la dichiarazione della classe fa in modo che essa implementi l'interfaccia IExampleContent che è stata definita in precedenza nel modulo interfaces (uno schema è quindi un'interfaccia con i campi nel zope.schema). Le rimanenti righe configurano le proprietà python per ognuno dei campi richiesti. Per esempio, la linea funny descrive un attributo funny che è modellato dopo il campo funny dello schema in IExampleContent.
Questo assicura che la validazione di base è impostata sul tipo stesso. Se si prova a richiamare direttamente il tipo myobj con myobj.funny = 'foo' si può osservare che si solleva un'eccezione perchè 'foo' non è un valore di tipo Bool. Il tipo Bool si aspetta vero o falso.
Questo è quanto per la classe stessa. Si può osservare il modulo content.py per vederne i risultati.
Catalogazione dei Tipi
Un aspetto che è stato trascurato è che poichè non si stanno più usando i form auto-generati da Archetypes l'oggetto non può essere catalogato. Nell'universo Zope 3 queste cose dovrebbero essere compiute usando gli eventi. Poichè fomlib richiama un IObjectModifiedEvent quando avviene un salvataggio corretto, tutto ciò che è necessario fare è definire un gestore per questo evento.
def catalog_content(obj, event):
obj.reindexObject()
Il gestore stesso è estremamente semplice: esso prende come argomenti l'effettivo oggetto e l'evento (in questo caso l'istanza IObjectModifiedEvent). Sicomme si è in possesso dell'oggetto, a questo punto non resta che invocare la reindexObject().
Ovviamente è necessario aggangiarlo con l'architettura di Zope 3 e quindi bisogna andare a toccare il file configure.zcml.
<subscriber
for=".interfaces.IExampleContent
zope.app.event.interfaces.IObjectModifiedEvent"
handler=".content.catalog_content"
/>
In questo modo è possibile gestire ogni IObjectModifiedEvent che è stato invocato con un'istanza di tipo IExampleContent come oggetto. Nel caso in questione, FormlibExampleContent implementa l'interfaccia IExampleContent, quindi il gestore che è stato appena creato verrà chiamato quando l'oggetto è stato modificato.
L'ultima cosa che resta da fare è registrare il tipo con CMF/GenericSetup.
Installazione del Tipo con GenericSetup
Siccome la versione di Plone utilizzata per questo tutorial è la 2.5, è possibile utilizzare il tool GenericSetup per configurare il nuovo tipo nel sito Plone. Maggiori informazioni su GenericSetup e su Plone si possono trovare nell'eccellente tutorial di Rob Miller Understanding and Using GenericSetup in Plone.
I passi basilari per configurare il Tipo attraverso GenericSetup sono:
- Creare una directory profilo con la struttura profiles/default/types all'interno della directory formlib.
- Costruire un nuovo file types.xml nella directory default.
- Creare un file chiamato FormlibExampleContent.xml nella directory types.
- Registrare un nuovo profilo che usa questi file attraverso GenericSetup nel file __init__.py.
L'effettiva costruzione di questi file va oltre gli scopi di questo tutoria, ma essi possono essere trovati nella directory base formlib.
Bisogna ricordarsi che per attivare questi Tipi in Plone 2.5 bisogna andare nel tool portal_setup, selezionare ploneexample.formlib sample content come profilo attivo del sito e quindi eseguire tutte le operazioni di importazione.
Selezionare il Profilo Attivo

Passi di Esportazione

Conclusioni
Lo schema Zope 3 è arrivato così in Plone. E grazie alla zope.formlib sono state auto-generato le viste e i form da usare. Il widget e le impostazioni dei campi da scegliere in Zope 3 sono minori che nel mondo Archetypes, ma in Zope 3 lo sta rapidamente raggiungendo.
Il principale pezzo mancante (dalla prospettiva della formlib) è l'utilizzo di una form di aggiunta auto-generata. Mentre è possibile svilupparli, non è facile inserirli in Plone poichè in Plone è necessario creare prima il contenuto per poi visualizzare i form.
Aggiunta di un Nuovo Oggetto ExampleContent

Form di Modifica

Vista Predefinita


twitter
