archetype
Nov 04, 2011
Image handling personalizzato negli AT
Un'applicazione mobile e la necessità di usare un certo formato immagine mi hanno spinto a capire come Plone gestisca la creazione delle immagini scalate e come ci si può inserire nel processo per fare delle personalizzazioni
Il caso d'uso
Lo sviluppo di una applicazione mobile che deve mostrare una lista di eventi ha fatto sorgere la necessità di avere un formato di immagine piccolo (facile con Plone), quadrato (mmhm...) e senza distorsioni dell'immagine (ok, non ci siamo): qualora l'immagine non fosse quadrata, la si dovrà "croppare".
Il comportamento standard IN plone
Plone base non fornisce la possibilità di prendere una immagine qualsiasi e restituirla in un formato miniaturizzato e sicuramente quadrato.
Prendiamo, ad esempio, questa immagine:

Immagine originale di dimensione 200x150
Agendo sulle funzionalità standard di plone, andiamo in ZMI nelle 'portal_properties/image_properties' del nostro sito, e aggiungiamo fra le 'allowed_size' una 'custom_size 100:100'; poi possiamo verificare caricando l'immagine (ha dimensione 200x150) che se chiamiamo sull'oggetto 'image_custom_size', ci viene tornata un'immagine 100x75:

Immagine ridimensionata con il comportamento standard di Plone
L'immagine è stata scalata in proporzione, non è questo che vogliamo! E quindi?
Studiamo la situazione
La prima domanda che è lecito e doveroso farsi è:
Cosa succede quando facciamo traversing su un oggetto usando come attributo da "attraversare" uno degli scaling disponibili per le immagini?
Stiamo lavorando su Plone 4 e indagando un poco si scopre che è stato registrato un adattatore per gestire il traversing in caso di immagini dentro a plone.app.imaging:
class ImageTraverser(DefaultPublishTraverse):
""" traversal adapter for scaled down versions of image content """
adapts(IBaseObject, IRequest)
def fallback(self, request, name):
return super(ImageTraverser, self).publishTraverse(request, name)
def publishTraverse(self, request, name):
schema = self.context.Schema()
if '_' in name:
fieldname, scale = name.split('_', 1)
else:
fieldname, scale = name, None
field = schema.get(fieldname)
handler = IImageScaleHandler(field, None)
if handler is not None:
image = handler.getScale(self.context, scale)
if image is not None:
return image
return self.fallback(request, name)
All'interno di questo adapter abbiamo il metodo publishTraverse da cui partire e un metodo che si occupa del fallback al traverser di default nel caso non si stia lavorando allo scaling delle immagini.
Inoltre, se si lavora allo scaling delle immagini, si chiama un altro adapter (IImageScaleHandler) che fornirà i metodi per creare l'oggetto con l'immagine ridimensionata:
handler = IImageScaleHandler(field, None)
if handler is not None:
image = handler.getScale(self.context, scale)
if image is not None:
return image
La soluzione
Ok, è sufficiente. Abbiamo tutto quello che ci serve sapere! Come nella maggior parte dello sviluppo che si fa per Plone/Zope, la Zope Component Architecture permetterà di fare tutto in modo relativamente veloce.
Io sto lavorando con delle immagini in un archetype customizzato, per cui la cosa più semplice da fare per me sarà registrare un adapter più specifico per l'interfaccia del mio tipo, ma possiamo banalmente proseguire l'esempio registrandone uno per IATImage:
class MyImageTraverser(DefaultPublishTraverse): """ traversal adapter for scaled down versions of image content """ adapts(IATImage, IRequest) def fallback(self, request, name): ... def publishTraverse(self, request, name): ...
Ora abbiamo il traverser per gli oggetti che implementano IATImage, e siamo già a metà del lavoro. Il successivo e ultimo passo sarà applicare la trasformazione vera e propria.
Come abbiamo già avuto modo di vedere, si richiama un handler nel traverser che si occupa di ottenere l'immagine scalata. Quello che possiamo fare è registrare un nostro handler personalizzato per il field che contiene l'immagine; magari chiamare un handler personalizzato in modo condizionato, per gestire soltanto i casi che ci interessano; qualcosa come:
if scale in crop_and_scale: handler = ICustomImageScaleHandler(field, None) else: handler = IImageScaleHandler(field, None)
L'handler originale si trova in plone.app.image, nello stesso modulo del traverser; analizzando il codice ci si rende conto che se nel sistema è presente plone.app.blob, di default, si userà il BlobImageScaleHandler presente in quest'ultimo pacchetto.
Noi potremo creare un handler personalizzato come adapter sul field che si usa per contenere l'immagine, e essendo presente plone.app.blob, lo creeremo ereditando dalla classe BlobImageScaleHandler.
All'interno dell'handler, ci sono tutti i metodi per ottenere l'immagine scalata.
Per raggiungre il nostro obiettivo, ereditando dal BlobImageScaleHandler, possiamo scrivere un handler personalizzato che conterrà un unico metodo, createScale: l'unico che ci serve personalizzare. Qui potremo applicare le trasformazioni necessarie. Il metodo originale esegue i seguenti passi:
def createScale(self, instance, scale, width, height, data=None):
""" create & return a scaled version of the image as retrieved
from the field or optionally given data """
field = self.context
if HAS_PIL and width and height:
if data is None:
image = field.getRaw(instance)
if not image:
return None
data = str(image.data)
if data:
id = field.getName() + '_' + scale
try:
imgdata, format = field.scale(data, width, height)
except (ConflictError, KeyboardInterrupt):
raise
except Exception:
if not field.swallowResizeExceptions:
raise
else:
exception('could not scale ImageField "%s" of %s',
field.getName(), instance.absolute_url())
return None
content_type = 'image/%s' % format.lower()
filename = field.getFilename(instance)
return dict(id=id, data=imgdata.getvalue(),
content_type=content_type, filename=filename)
return None
Per fare la modifica, nell'handler personalizzato potremo cambiare questa riga
imgdata, format = field.scale(data, width, height)
con quello che fa più comodo, e usando il crop delle PIL potremo trasformare l'immagine come necessario, arrivando a un risultato come questo.

Immagine ridimensionata e croppata fino alla dimensione 100x100
Jun 09, 2010
Expensive security check in constraintypes
If you are using Plone 3.3 or older you are probably suffering an expensive security check when creating an Archetype. It can be EASILY fix.
In one of our projects we are still using Plone 3.1.x. with a pack of customer specific content_types and some additional PAS plugins (Lotus). I have noticed recently that calling invokeFactory become VERY slow. After debugging we have found the problem - getDefaultAddableTypes - method that is provided by constraintypes.py (ATContentTypes package). Original implementation is more then 4 years old. What it does - it checks isConstructionAllowed for every content_type register in portal_types. If you have registered them more then 40 ;-) ...
In most of the cases folderish types are filtering allowed_types. Thanks to Vincent we have now a nice fix:
Modified lib/constraintypes.py:getDefaultAddableTypes method to check isConstructionAllowed only for allowed types, not for all content types in portal_types. isConstructionAllowed was called twice for each types.
This fix is in ATContentTypes version 1.3 and started to be used by Plone 3.3.1. For us it was a good argument to upgrade!
Nov 09, 2009
Ploneconf2009: un breve sommario
Archetypes è morto: lunga vita ad Archetypes
Trovo finalmente il tempo di riassumere i punti salienti della Ploneconf 2009 di Bupadest.
La grande verità
Per la prima volta dai tempi di Plone 2.0 sono davvero convinto che il nostro amato e ben conosciuto Archetypes venga lentamente mandato a morte. La sua sarà una lenta e non facile caduta e molto probabilmente i suoi pezzi vivranno fino ai tempi di Plone 5, ma a differenza di tutte le altre volte l'alternativa che si sta disegnando è reale e soprattutto da qualcosa che Archetypes non aveva.
Sto ovviamente parlando di Dexterity. Questo nuovo framework promette maggiore velocità (il talk di David Glick intitolato "Building Content Types with Dexterity" ha anche mostrato qualche cifra, ma nulla di eccezionale a dire il vero, un particolare confermato anche da Wichert Akkerman nel suo intervento di cui parlo sotto), si integra in modo completo con la ZCA e l'uso delle interfacce... ma non sono questi i particolari che credo determineranno il suo successo.
Per la prima volta non si sta semplicemente fornendo una modo alternativo, ideologicamente più bello ma nella pratica più limitato, per ottenere esattamente quello che Archetypes già fornisce (ho visto vari di questi tentativi in passato, e anche Rok Garbas ci ha riprovato nel suo talk intitolato "Complex Forms with z3c.form"). Dexterity offre una grande nuova funzionalità che è la generazione dei contenuti via Web!
Per chi di voi ha già usato prodotti come ATSchemaEditorNG e derivati, questo potrebbe non sembrare niente di veramente nuovo, ma questa volta la differenza sta nel fatto che questi contenuti saranno esportabili come codice perfettamente funzionante e modificabile.
L'utente Plone potrebbe quindi generare autonomamente i propri modelli dati e richiedere interventi di sviluppo sono per ottenere risultati che l'interfaccia Web non offre... eccezionale!
Le slide e gli esempi sono da analizzare (forse un tantino esagerata anche l'inclusione opzionale dei componenti Grok... ma lo perdoniamo).
Il prodotto attualmente presenta alcuni limiti e mancate funzionalità che non lo fa consigliare per la produzione, ma sono tutte cose che verranno facilmente superate nel breve. Probabilmente Dexterity diventerà lo standard dello sviluppo in Plone 4, convivendo (forse non troppo pacificamente) con Archetypes, per diventare l'unico ospite in Plone 5.
De-cosa?
Era dappertutto e se ne parlava dappertutto... Deco!
Confesso che non mi sono assolutamente interessato all'argomento fino alla conferenza e anche durante la stessa ho schivato tutti i talk a riguardo, poi mi sono ritrovato ad uno degli incontri nella terza giornata (dedicata agli Open Space) in cui l'argomento era "pagine composte" e l'intervento di Limi ha portato l'attenzione su questo nuovo giocattolo.
Deco si dovrebbe occupare solo del layout. Tutti noi potremmo continuare ad utilizzare l'approccio di oggi (creo un contenuto, genero la sua vista) oppure basarci sul layout Deco che permetterà il movimento dei componenti della pagina (tiles) tramite drag & drop.
A sentire Limi l'accessibilità della pagina (intesa come validazione del codice) rimane garantita se si parte da un buon XHTML. Vedremo!
A ruota libera
Essendo finiti gli argomenti principali, finisco con una breve panoramica su alcuni interventi minori che ho trovato per qualche motivo interessanti.
Euphorie: combining grok, dexterity sql content in a single application - Wichert Akkerman.
Wichert ci ha offerto un interessante talk su come usare Plone senza usare Plone. La descrizione di un progetto dove Plone veniva completamente privato di ogni suo componente (sharing, versioning, ...) e dove i dati venivano presi da database relazionale...
...forse conveniva non usare Plone? :-)
plone.app.discussion - Timo
Un veloce incontro negli openspace su un vecchio argomento mai completamente risolto (speriamo sia la volta buona) un nuovo prodotto, speriamo il nuovo prodotto, per gestire i commenti ai contenuti in Plone. Sarà compatibile con Plone 3.3 e diventerà parte del sistema in Plone 4.1.
plone.app.discussion promette la moderazione dei commenti e un workflow per questi, ovviamente personalizzabile. Era ora!
Very frequently asked questions answered for the last time :-) - Andreas Jung
Come potevo non partecipare? Chi di voi non è mai stato ripreso da A.J. almeno una volta?
La persona più "diretta" della mailing list plone-users che ripeteva per l'ennesima volta, nel modo più gentile possibile, alle domande che troppo spesso si è sentito fare (lo so... non erano davvero dirette a lui...).
Non mi sono stupito del suo intervento dopo la lunga discussione che è sfociata nel flaming poche settimane prima della conferenza...
Ho trovato divertente vedere come il giorno dopo qualcuno abbia comunque fatto domande troppo banali nella lista... e con lo stesso approccio di sempre abbia ricevuto in regalo le risposte di A. Jung!
Sep 24, 2009
Sostituire i contenuti base di Plone con i propri archetype
Ho scritto un tutorial sul portale Plone Italia relativamente a questo delicato argomento.
Non dilunghiamoci troppo, dato che il tutorial può essere letto direttamente dal portale plone.it, ma riassumiamo solo i concetti che vi stanno dietro.
- Aumentare/modificare le funzionalità dei tipi nativi di Plone.
- Sostituire quindi questi tipi primitivi con i nostri tipi.
Fin qui tutto facile, il problema è sempre e solo uno: se sviluppiamo un nostro tipo di contenuto "News" e vogliamo che questo venga usato da Plone proprio come se fosse l'originale "News Item" di ATContentTypes, allora dovremmo combattere con l'integrazione nel sistema:
- Far capire a Plone che, dove prima veniva usato "News Item", ora venga usata la nostra news (piuttosto facile, ma noioso).
- Prodotti di terze parti (magari non ancora installati) dovranno usare il nostro contenuto se internamente facevano affidamento su "News Item"
Leggetevi quindi la guida all'applicazione di questo metodo.

