Abbiamo riunito tutti gli RSS che parlano di Plone.

Vuoi aggiungere il tuo blog al planet? Invia una mail alla lista italiana.

Ultimo aggiornamento: 16/04/2014 11:30:21

09/04/2014

10:07:55

Webinar 1 - Introduzione a Plone, il CMS scritto in Python

Il primo webinar Abstract del 2014, dedicato al nostro amato CMS Plone.

03/04/2014

di dmoro 10:23:45

Gestione avanzata keyword con Plone - appendice (molte keyword)

Appendice finale alla serie di articoli "Gestione avanzata keyword con Plone". Argomento: grossi portali e widget autocomplete

01/04/2014

di sauzher 09:35:21

[solved] AttributeError: type object ‘IThemeSpecific’ has no attribute ‘isOrExtends’

Il Grande Capo Estiqaatsi direbbe:  – eh, grosso problema ora c’è -”. Il problema si manifesta nel caso in cui disinstallate un prodotto del quale avevate customizzato una sua viewlet… qualcosa del tipo: 1) installo mio.prodotto 2) customizzo da zmi -> portal_view_customizations una viewlet di mio.prodotto 3) disinstallo il mio.prodotto (ach! ho dimenticato di cancellare […]
Il Grande Capo Estiqaatsi direbbe:  – eh, grosso problema ora c’è -”. Il problema si manifesta nel caso in cui disinstallate un prodotto del quale avevate customizzato una sua viewlet… qualcosa del tipo: 1) installo mio.prodotto 2) customizzo da zmi -> portal_view_customizations una viewlet di mio.prodotto 3) disinstallo il mio.prodotto (ach! ho dimenticato di cancellare […]

26/03/2014

di Luca Bellenghi 09:30:00

Migrazioni e sorprese: cose che bisogna saper gestire

Oggi voglio condividere con voi un piccolo problema e relativa soluzione capitati pochi giorni fa lavorando a una migrazione da Plone 3 a Plone 4

Diversi giorni dopo aver terminato la migrazione, preparando i contenuti per il nuovo portale, mi sono ritrovato a navigare un'area di contenuti basati sullo stesso tipo di documento di altrettanti già verificati, per cui non mi aspettavo nulla di strano e problematico. Invece...

Il problema

Invece mi sono trovato sul monitor una pagina bianca con scritto (riassumendo) "broken object"! Cosa può essere successo? Problemi nella migrazione? Codice non aggiornato correttamente alla versione Plone 4? Codice completamente mancante per quei tipi di oggetto?

Nel mio caso, si trattava di una versione non aggiornata del prodotto "incriminato": durante il porting a Plone 4, non essendo presente la classe da cui è stato istanziato l'oggetto in questione, il risultato è stato una procedura di migrazione che non ha prodotto errori (non bloccanti quantomeno, magari qualcosa nei log era indicato) e un oggetto corrotto nel sito.

L'errore

In console, in corrispondenza della navigazione sull'oggetto corrotto, l'unica cosa che ho potuto vedere è stata:


Traceback (innermost last): ....... Module plone.app.folder.base, line 65, in index_html Module plone.folder.ordered, line 202, in __contains__ TypeError: argument of type 'NoneType' is not iterable

Mentre cercando di visitare la stessa sezione in zmi:

 
Traceback (innermost last): .......... Module Products.BTreeFolder2.BTreeFolder2, line 332, in objectCount TypeError: 'NoneType' object is not callable

Bene. L'errore indica un problema con oggetti di tipo folderish e come ben sappiamo (o dovremmo sapere) ci sono stati profondi cambiamenti nella struttura delle cartelle da Plone 3 a Plone 4.

Questo mi ha indicato che il problema risiedeva proprio nell'implementazione delle nuove cartelle e nel fatto che quelle corrotte nel sito non sono state migrate correttamente.

Facciamo pulizia

Vediamo come fissare la situazione. Uno degli step di migrazione da Plone 3 a Plone 4 è, appunto, la migrazione delle cartelle.

Controllando il codice, si può verificare che questa migrazione è ottenuta chiamando una vista sulla radice del sito: "@@migrate-btrees" presente dentro al pacchetto plone.app.folder.

Questa vista, fra l'altro, non è necessario chiamarla nella radice del sito perché, come si può vedere nel codice, l'analisi degli oggetti viene fatta a partire dal contesto in cui la vista viene chiamata.

        
....
for path, obj in findObjects(self.context): if isinstance(obj, BTreeFolder): if self.migrate(obj): processed += 1 cpi.next() self.postprocess(obj)
....

Una volta terminato il processo di migrazione delle cartelle, queste ancora non compariranno navigando il sito, ma il più è fatto. A questo punto, infatti, basta fare un update del catalogo oppure indicizzare nuovamente almeno i contenuti di quella cartella.

Immagini di http://www.flickr.com/photos/photowest/

25/02/2014

di Luca Fabbri 09:00:00

Integrazione degli Icon Font in Plone, in modo accessibile

Sempre più i siti Web moderni abbandonano l'uso indiscriminato di icone per sostituirle con la tecnica degli icon font.

Toolbar di comandi su GitHubGuardiamo ad esempio la barra dei comandi di GitHub (un sito sempre aggiornato per quanto riguarda le tecniche di front-end). Credete ci sia anche solo un'immagine qui?

Da profano, posso spiegarvi la tecnica in due parole: sfruttare CSS (e la direttiva font-face) per mostrare un carattere Unicode all'interno del testo.

Dato che si parla di Unicode, ci sono centinaia di simboli che possono sembrare icone, eppure dopo tutto stiamo pur sempre parlando di caratteri! ☼ ✰ ⌂ ♥

Bello, eh?

La mia (inizialmente poca) attenzione sull'argomento parte col rilascio del prodotto di integrazione tra Plone e FontAwesome.
Il prodotto di per sé non fa molto, se non registrare in Plone i CSS di FontAwesome all'interno di Plone, e nulla più.
Sta poi allo sviluppatore/grafico utilizzare o meno queste nuove regole nei propri template, mentre non c'è nessun supporto all'abilitazione di questi nuovi stili all'interno di TinyMCE.

E' l'accessibilità?

Qualche tempo fa mi sono imbattuto nell'articolo Bulletproof Accessible Icon Fonts in cui si parla dei problemi di accessibilità che questa tecnica può introdurre (e qui la mia attenzione si è fatta più viva!).

Il problema è fondamentalmente legato a come gli screen reader interpretano questi caratteri. Vediamo un esempio:

<p>E' nata una <span class="icon-star">stella</span></p>

Il carattere "stella" (&#xf061;) potrebbe semplicemente venire letto come "Black Star" ed essere quindi completamente incomprensibile e al di fuori di ogni contesto.

Se questo utilizzo ha una funzione puramente decorativa, lo screen reader dovrebbe passare oltre. Purtroppo pare che il supporto alla regola CSS "speak: none" sia ancora molto limitato.

Ci sono alternative?

L'articolo descrive come strada meglio percorribile l'uso di ARIA, nello specifico l'applicazione di aria-hidden. Lo scopo dell'attributo è marcare in modo esplicito l'elemento come "nascosto" e la tecnologia assistiva (se supporta ARIA, cosa a dire il vero non scontata) lo ignorerà.

<p>E' nata una <span aria-hidden="true" class="icon-star">stella</span></p>

E ora torniamo a Plone!

Dopo aver letto l'interessante articolo mi sono chiesto come poter rendere accessibile il prodotto collective.fontawesome con la tecnica qui sopra descritta.
Pare che le cose da fare non siano poche, quindi prendete il codice che seguirà come un "proof-of-concept" piuttosto che qualcosa di definitivo!

Aggiunta di stili a TinyMCE

Innanzitutto, come detto poco fa, collective.fontawesome si limita all'aggiunta dei CSS in Plone, ma non c'è nessun aiuto per l'utente che utilizza l'editor TinyMCE di Plone per poter usare questi nuovi stili.

La soluzione in questo caso è semplice: aggiungere alcune nuove opzioni nella configurazione degli stili del sito:

Configurazione di TinyMCE per Font AwesomePreview di TinyMCE con Font Awesome

Fatto questo, gli utenti potranno selezionare del testo dell'editor ed assegnargli lo stile voluto.

Stili FontAwesome applicati

Intercettare e correggere l'uso di icone

Per sistemare l'accessibilità del testo andrebbe quindi applicato l'attributo ARIA a questi elementi. L'idea è quella di intercettare le modifiche fatte al testo (evento onChange di TinyMCE), ma purtroppo in Plone tutto questo non è molto semplice.

L'inizializzazione dell'editor (fatta dal Products.TinyMCE) non permette facilmente di aggiungere dei callback agli eventi lanciati dall'editor.
Dobbiamo quindi modificare il codice di Products.TinyMCE perché venga lanciato l'evento, oppure (in modo meno invasivo) agganciare l'evento con questi semplici comandi JavaScript:

$('.mce_editable').tinymce().onChange.add(monitorTinyMCEIconFont)

monitorTinyMCEIconFont è una funzione JavaScript che si occupa di:

  • analizzare il codice HTML dell'editor
  • trovare tutti gli elementi che usano uno stile di icon font
  • aggiungere a questi (se non già presente) l'attributo aria-hidden
  • aggiornare il contenuto dell'editor (solo se è stato modificato).

Segue la definizione:

function monitorTinyMCEIconFont(ed, l) {
var changesDone = false,
doc = $('<div id="fakeContainer">' + l.content + '</div>');
for (var i=0;i<classes_to_monitor.length;i++) {
doc.find('.' + classes_to_monitor[i] + ':not([aria-hidden="true"])').each(function() {
var element = $(this);
element.attr('aria-hidden', 'true');
changesDone = true;
});
}
if (changesDone) {
ed.setContent(doc.html());
}
};

Da notare il riferimento alla struttura dati classes_to_monitor, un array per limitare il numero di stili supportati.
Vista la configurazione lato server che introdurremo tra poco, definiamo l'array in questo modo:

var classes_to_monitor = ['icon-star', 'icon-envelope', 'icon-user', 'icon-ok', 'icon-remove'];

Attributi permessi di TinyMCE

Sebbene la configurazione dei filtri HTML di Plone non contenga nulla di particolare contro l'attributo aria-hidden, l'approccio qui sopra non funziona ancora: l'attributo viene eliminato senza pietà!

Filtri HTML del sitoIl problema è (di nuovo) in TinyMCE (...tu quoque, Tiny...).
Per quanto gli unici elementi di configurazione che trattano attributi siano relativi ad "Attributi eliminati" (quindi una black-list di attributi), la configurazione di default di TinyMCE permette comunque solo un certo insieme di attributi.

Troviamo questa configurazione nella definizione della ITinyMCE utility. Se prendiamo ad esempio come riferimento l'elemento SPAN che stiamo usando.

Troviamo:
...
'span': COMMON_ATTRS.copy(),
...

Dove COMMON_ATTRS risulta essere:

...
CORE_ATTRS = set('id style title class'.split())
I18N_ATTRS = set('lang dir'.split())
COMMON_ATTRS = CORE_ATTRS | I18N_ATTRS
...

Come potete capire quindi, per l'elemento span tutti gli attributi non compresi in questa lista verranno eliminati dall'editor.
La nostra unica possibilità è modificare l'utility (definendone una nuova) che cambi questo comportamento:

CORE_ATTRS = set('id style title class'.split())
I18N_ATTRS = set('lang dir'.split())
ACCESSIBILITY_ATTRS = set('aria-hidden'.split() )
COMMON_ATTRS = CORE_ATTRS | I18N_ATTRS | ACCESSIBILITY_ATTRS

...e anche questo problema è risolto.

Effetto finale

Non è stato esattamente semplice ma siamo arrivati a una soluzione!
Ecco come viene infine generato il nostro HTML:

Esempio di applicazione FontAwesome accessibile

Alla prossima!

Easter egg

In questo articolo è nascosto un messaggio segreto! Riuscite a capire cosa dice?!

19/02/2014

di Alessandro Pisa 15:35:00

RedTurtle al Plone Cathedral Sprint: verso la prima release di Plone 5

Da anni RedTurtle punta molto su Plone, glorioso gestore di contenuti open source.

Grazie alla scelta open, RedTurtle ha potuto beneficiare del lavoro di una delle comunità più competenti del mondo, comunità nella quale essa stessa è sempre più presenteattiva.

Lo sviluppo di Plone è caratterizzato da eventi chiamati "Sprint": riunioni nelle quali sviluppatori di tutto il mondo lavorano in "full immersion" per conseguire gli obiettivi che si sono preposti prima del loro incontro.

La settimana scorsa Andrew, Giacomo e io abbiamo partecipato, insieme a una folta schiera di Plonisti, al "Cathedral Sprint" di Colonia.

L'evento, che prende il nome dalla cattedrale gotica che domina il panorama della città tedesca, è stato organizzato con lo scopo di accorciare i tempi necessari al prossimo rilascio della nuova versione di Plone, la numero 5.

Plone 5 si sta per presentare al mondo con una interfaccia utente completamente rinnovata, disegnata seguendo i più moderni standard del web, contenente automatismi che facilitano l'utente nel lavoro e che permette un utilizzo efficace anche con i dispositivi mobili. Tutto questo senza dimenticare di garantire, come sempre accade per Plone, la piena accessibilità.

In questo sprint ho lavorato molto sui test delle nuove interfacce utente, utilizzando Robot Framework: un programma che “prende possesso” del browser simulando l'utilizzo del sito da parte di un utente allo scopo di verificare l'effettiva funzionalità dell'applicazione.

Anche i miei colleghi si sono focalizzati sull'interfaccia utente. Andrew si è dedicato principalmente al componente per l'inserimento, la modifica e la formattazione dei testi, Giacomo si è adoperato per rendere più semplice e funzionale le personalizzazioni di elementi chiave del sito come la testata e il piè di pagina.

Purtroppo la settimana a Colonia è passata davvero in fretta e come al solito si torna a casa con l'amara consapevolezza di dover aspettare ancora settimane prima di incontrare nuovamente la magnifica comunità dei Plonisti.

Prima di concludere, un sentito grazie a Timo Stollenwerk per la magnifica organizzazione e a GFU Cyrus AG per l'ospitalità. Tutto era perfetto ed è stato davvero facile essere operativi sin da subito.

Vi lascio con il link a Twitter dedicato allo sprint e con qualche immancabile immagine di quello che sarà il futuro Plone.

 

 

Credits

La foto di gruppo è di Wolfgang Thomas.

La mascotte di Robot Framework è presa da Twitter.

Immagini di testata e per Facebook prese da Wikipedia.

17/02/2014

15:12:05

Plone Cathedral sprint 2014, un altro passo verso Plone 5

Report sullo sprint per Plone che si é tenuto a Colonia. Molti bachi risolti, molte funzionalitá migliorate e molte altre sono state aggiunte: Plone 5 si fa sempre piú vicino. Preparatevi a una nuova versione del nostro CMS preferito come non lo avete mai visto!

29/01/2014

di Luca Bellenghi 13:20:00

Google Maps senza troppi pensieri? Ci aiuta jQuery!

Keep it Simple!!

Quando si programma, come in ogni attività, è inutile complicarsi la vita.

Da plonista, oramai conosco diversi modi complessi per risolvere un task; ma, ad esempio, se dovessi fare un form di invio al server di una stringa, non avrebbe senso impiegare

findxtutta la machinery di z3c.form, quando 5 righe di form in html puro le sanno scrivere tutti (vero...?!).

Lo stesso discorso vale anche al di fuori della programmazione: c'è sempre qualcuno disposto a complicarci la vita... perché favorirgli il lavoro?

Partiamo da uno use case

Un cliente vorrebbe fare l'upgrade di un prodotto per la visualizzazione tramite Products.Maps di diversi layer prestabiliti, ognuno con N marker.

L'ideale per il cliente sarebbe usare collective.geo.bundle per ogni use case presente nel sito ma, almeno con la versione di collective.geo utilizzabile in quel contesto, la funzionalità cercata non è immediata da riprodurre...

Nella versione di collective.geo che può usare il cliente, infatti (non so nella più recente), non è possibile gestire facilmente più layer che caricano diversi marker: ad una prima analisi, è necessario quanto meno creare una vista e un multiadapter che si adatta alla vista e al contesto corrente per mostrare i layer selezionati (e ignoriamo il "selezionati come?").

Sappiamo sicuramente fare un multiadapter, ma.... non possiamo semplificare il task?

Navigando in rete si trovano diversi plugin jQuery utilizzabili con le jAPI javascript di Google Maps. A questo punto, fare una vista con un po' di html e di javascript, potrebbe essere risolutivo oltre che semplice.

Fra le varie librerie che ho trovato mi è piaciuta Maplace.js, semplice e intuitiva da usare. In poco tempo sono riuscito ad ottenere i risultati sperati.

Una semplice mappa

Per utilizzare Maplace.js è sufficiente caricare, nella pagina jQuery, le API js di Google Maps, il sorgente di Maplace.js e avere un div il cui id sia 'gmap' in cui caricare la mappa. Dopodiché, sull'on load della pagina è possibile richiamare questo codice js:

 
  var maplace = new Maplace();
  maplace.Load();

Avremo così una prima semplice mappa:

Mappa-lucabel

Risolviamo il problema iniziale

Leggendo la documentazione della libreria sul sito, è stato abbastanza semplice capire come risolvere il problema. Semplificando, è bastato creare un hastable javascript, contenente i marker in questo formato:


var LocsA = [ { lat: 44.542222, lon: 11.934722, title: 'Voltana', icon: 'http://maps.google.com/mapfiles/marker_green.png' }, .... ] hastable = {'LocsA': LocsA, 'LocsB': LocsB, 'LocsC': LocsC}

Una volta preparati i dati da utilizzare per l'esempio, si può creare l'html necessario:


<ul id="menu"> <li><a href="javascript:void(0)" data-load="LocsA" id="LocsA" title="Group A">Voltanese</a></li> <li><a href="javascript:void(0)" data-load="LocsB" id="LocsB" title="Group B">Alfonsinese</a></li> <li><a href="javascript:void(0)" data-load="LocsC" id="LocsC" title="Group C">Argentano</a></li> </ul> <div id="gmap" style="width: 50%"></div>
E infine il javascript da eseguire sull'on load della pagina (occhio ai commenti):
  
//si crei un oggetto mappa
var maplace = new Maplace();
//Si faccia il bind fra il click e la funzione che gestirà l'evento
//sul menù dei layer $('#menu a').click(function(e) { e.preventDefault(); var index = $(this).attr('data-load'); showGroup(index); });
//Si imposti una classe sul layer attivo del menù, si rimuova il
//vecchio menù che verrà rigenerato e si carichi nella mappa
//i nuovi marker; questo può banalmente essere fatto anche via ajax... function showGroup(index) { $('#menu li').removeClass('active'); $('#menu li #'+index).parent().addClass('active'); $('.gmap_controls').remove(); maplace.Load({ locations: hastable[index], force_generate_controls: true }); }
//Si carichi ora la mappa iniziale con alcune opzioni di base maplace.Load({ map_options: { zoom: 10, set_center: [44.55, 11.966667] }, map_div: '#gmap', controls_type: 'list', controls_on_map: true, generate_controls:true, view_all: false, }); });

Grazie a queste poche righe di codice, è possibile ottenere risultati come questo:

mappa comepleta lucabel

Il menù laterale all'estrema destra (oltre a motivare la presenza dei grafici in questa vita) permette di scegliere quale layer caricare nella mappa; dopodiché, tramite il menù all'interno della mappa, è possibile posizionarsi su un marker piuttosto che sull'altro.

Lascio alla documentazione sul sito della libreria la possibilità di mostrare la moltitudine di opzioni applicabili alle mappe.

A mio avviso questo è un modo molto semplice e diretto di risolvere la richiesta; inoltre, quando ci si costringe a cercare delle alternative, si possono trovare librerie molto comode.

L'immagine per Facebook è presa da: http://www.flickr.com/photos/bulle_de/4672972586/sizes/n/

L'immagine per la testata è presa da: http://www.flickr.com/photos/manitobamaps/2089812938/

19/12/2013

di Luca Fabbri 13:55:00

Come usare i widget Plone in semplici template HTML

Nell'uso di tutti i giorni che facciamo del CMS Plone ci troviamo di fronte a una serie di comodi widget.
Se volessimo tentare una definizione di widget nel contesto Plone, potremmo dire "una porzione di HTML atta a definire la raccolta dati di una singola informazione". Se la definizione che ho tentato di dare vi sembra complicare le cose, proviamo ad essere più pratici:

  • per chiedere all'utente il proprio nome, viene usato un widget di tipo stringa che si traduce in un semplice campo input HTML
  • per chiedere una data ci si trova, invece, di fronte a un più complesso insieme di menù a tendina e a un calendario JavaScript, ma lo scopo finale è quello di raccogliere un'informazione di tipo data/ora.

Perché usare sempre gli stessi widget? Perché in questo modo forniamo un'interfaccia utente che diventa famigliare e ripetuta in tutti i contesti dove viene mostrato un form - che sia di invio mail, di inserimento di contenuti, ecc.

La strada maestra per poter generare i propri form general-purpose (e quindi riutilizzare questi widget) è l'uso di librerie di form (z3c.form in primis).

Cosa succede, però, se ci si trova a voler riutilizzare questi widget per altri scopi, non canonici, come realizzare un semplice form HTML partendo da una pagina bianca? E' quello che andremo a esplorare in questo articolo.

Prima di tutto: perché? In alcuni casi, librerie come z3c.form possono diventare difficili da utilizzare, magari perché si sta tentando di realizzare un form molto complesso e altamente personalizzato.
Queste librerie sono ottime per realizzare velocemente una serie di form che devono mantenere lo stesso layout, ma tendono a diventare via via più complesse nel momento in cui si ha la necessità di cambiare il codice generato.

In questi casi estremi è a volte più semplice (e meno costoso in termini di tempo) realizzare un semplice form all'interno di un template e immettere direttamente il proprio HTML.

Dopotutto, la realizzazione di un template Plone contenente un form si limita a una registrazione di uno ZCML...

<browser:page
for="*"
name="myform"
template="myform.pt"
permission="zope.Public"
/>

...e a una vista, che si può limitare ad un singolo template...

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
metal:use-macro="context/main_template/macros/master">
<body>

<metal:content-core fill-slot="content-core">
<metal:content-core define-macro="content-core">

<form action="@@gestisciform" method="post">

<input type="text" name="name" value="Nome" />
<input type="submit" name="send" value="Invia" />

</form>

</metal:content-core>
</metal:content-core>

</body>
</html>

Semplice form in PloneAncora più interessante: dimenticandosi della registrazione ZCML e aggiunto il template direttamente da ZMI, ci permette di lavorare via web (TTW)! Questo non è possibile ad oggi usando librerie di form.

Andiamo avanti. Dove viene inviato il form? Nell'esempio sopra, a un'altra vista (dal nome "gestisciform") che si occuperà di "far qualcosa" con i dati.
Il form qui sopra è esageratamente semplice, ma da ora è necessaria una semplice conoscenza di HTML (e probabilmente anche di
TAL) e il form può assumere la forma che si preferisce.

Possibile che sia tutto così semplice? Infatti non lo è... I problemi iniziano quando si ha la necessità di replicare i comodissimi widget di Plone nei nostri form, soprattutto quelli che si basano sulla presenza di codice JavaScript.
In questi casi ci sono due strade:

  • tornare alle librerie di form, e gestire la complessità della personalizzazione spinta che si vuole realizzare
  • eseguire un'operazione di reverse engineering e applicare i widget e il loro codice JavaScript al nostro HTML.

Di seguito verrà illustrato come integrare in un semplice template Plone due delle sue funzionalità: l'editor TinyMCE e il widget per eseguire riferimenti all'interno del sito (referencebrowserwidget).

In entrambi i casi il risultato è stato ottenuto partendo dall'uso che viene fatto di questi widget in Plone e analizzando il codice HTML generato per estrarne i segreti. Questo approccio può essere valido e replicabile anche per altri widget qui non discussi! Provate!

Integrazione di TinyMCE

A differenza di quanto capitava con le vecchie versioni di TinyMCE (1.2 e inferiori), integrare la versione 1.3 (pre-installata da Plone 4.3 in poi) è estremamente semplice! L'unica cosa che è necessario fare è realizzare una textarea HTML e aggiungervi alcune classi CSS, più un attributo HTML 5.
Ma andiamo con ordine!

Per ospitare una textarea, il nostro form deve cambiare così (da ora in poi mi limito a includere la parti salienti del codice HTML che va modificato):

<form action="@@gestisciform" method="post">

<textarea name="testo"></textarea>
<input type="submit" name="send" value="Invia" />

</form>

Form con TinyMCE (rotto)Ovviamente questo è tutto tranne che un editor WYSIWYG, ma è anche ciò che verrà mostrato all'utente in caso di JavaScript disabilitato.

Le due classi CSS da aggiungere sono mce_editable e pat-tinymce. La loro presenza viene catturata dall'integrazione tra TinyMCE e jQuery e l'editor viene invocato.

<textarea name="testo" class="pat-tinymce mce_editable"></textarea>

Però ancora non basta: se ci limitiamo a questo, otterremo solo un errore JavaScript; l'effetto visivo è che la textarea sembra sparire, ma non viene sostituita con nulla.
E' necessario aggiungere alla textarea un attributo contenente la configurazione di TinyMCE: data-mce-config. Questa configurazione assume la forma di una complessa struttura JSON, ma esiste un metodo molto semplice per caricare la configurazione predefinita del sito:

<form action="@@gestisciform" method="post">

<textarea name="testo" class="pat-tinymce mce_editable"
tal:define="configuration_method nocall:here/@@tinymce-jsonconfiguration;
configuration_json python:configuration_method(field=None);"
tal:attributes="data-mce-config configuration_json"></textarea>
<input type="submit" name="send" value="Invia" />

</form>

La configurazione viene caricata invocando un'altra vista (fornita dal prodotto Plone per TinyMCE); non resta che inserirla all'interno dell'attributo e il gioco è fatto!

Form Plone con TinyMCE

Integrazione del Browser Reference Widget

Integrare in un form il widget per le referenze a contenuti di Archetypes non è un'operazione semplice come quella vista poco fa.
Il problema principale è che il widget e tutto il suo JavaScript sono stati fatti per essere usati all'interno del framework Archetypes; non seguono, quindi, un vero e proprio "separation of concerns" degli elementi. Sarebbe stato assai più interessante se la funzionalità avesse fornito separatamente un'interfaccia per ricercare e selezionare contenuti del sito e l'integrazione come field di Archetypes.

Questo limite ci traduce in un singolo problema (non da poco a dire il vero): il widget va invocato su un contenuto Archetypes che fornisca un vero e proprio ReferenceField. Nei fatti questo campo può anche essere nascosto e mai mostrato agli utenti, ma deve esistere.

Ad ogni modo, proseguiamo con l'analisi!

La prima versione del nostro form è piena di HTML nascosto, che prenderà vita non appena riusciremo ad "attivare" le funzionalità JavaScript.
Per quanto detto sopra, per testare questo template sarà necessario lanciarlo su un contesto Archetypes che abbia un campo di tipo ReferenceField: una qualunque pagina del sito andrà bene, anche la predefinita front-page, perché contenente il campo "Contenuti correlati" (relatedItems).

<form action="@@gestisciform" method="post">

<div id="atrb_referenze" class="overlay-ajax overlay">
<div class="close"><span>Chiudi</span></div>
<div class="pb-ajax">
<div class="overlaycontent" style="font-size: 125%"></div>
</div>
</div>

<input type="hidden" name="referenze:default:list" value="" />
<input type="hidden" value="1" name="referenze-sortable" />

<div style="float: left"> <!-- don't remove this. it is needed for DOM traversal -->
</div>
<ul class="visualNoMarker" id="ref_browser_items_referenze" >
</ul>

<input type="button" rel="#atrb_referenze" src=""
class="addreference searchButton" value="Aggiungi..."
tal:define="startup_directory context/absolute_url;
helper python:context.restrictedTraverse('refbrowserhelper', nothing);
at_url helper/getAtURL|nothing;"
tal:attributes="src string:${startup_directory}/refbrowser_popup?fieldName=referenze&amp;fieldRealName=relatedItems&amp;at_url=${at_url}&amp;" />

<input type="submit" name="send" value="Invia" />

</form>

Form con BrowserReferenceWidget (rotto)Analizziamone i punti essenziali:

  • deve essere stabilito un nome del campo, che va ripetuto in vari punti del codice: nel nostro caso è stato scelto "referenze". Questo nome compone numerosi id di elementi della pagina, e vanno sistemati tutti
  • c'è un unico riferimento ad un parametro fieldRealName, che deve contenere un nome reale di un ReferenceField
  • c'è un DIV nascosto (id=atrb_reference), che sarà l'overlay AJAX di jQueryTools (pre-installato in Plone) che conterrà la popup di selezione degli elementi
  • esiste un UL inizialmente vuoto (id=ref_browser_items_reference) dove verranno aggiunti gli elementi selezionati
  • esiste un bottone "Aggiungi...", la cui parte principale è un attributo src (invalido... HTML5 non era ancora una realtà al tempo) il cui contenuto sarà sfruttato dal JavaScript del widget.

Il risultato di questo template è un form con un pulsante completamente inattivo.

Quello che manca è un po' di codice JavaScript che ci permetta di attivare le funzionalità del widget. Aggiungiamo quindi un po' di JavaScript all'interno del template (modificandone la parte iniziale):

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
metal:use-macro="context/main_template/macros/master">

<head>

<metal:head fill-slot="javascript_head_slot">
<script type="text/javascript" src=""
tal:attributes="src string:${portal_url}/referencebrowser.js"></script>
</metal:head>

</head>

<body>
...

Questo JavaScript è piuttosto semplice, non fa altro che includere nella pagina corrente la chiamata al file referencebrowser.js. Questo, grazie alla struttura HTML utilizzata, farà tutto il resto del lavoro!

Form con BrowserReferenceWidget

Parlando di dati inviati

Ma questo tipo di campo che cosa invia? Qual è il valore che inviamo al server all'operazione di submit del form?
L'informazione è l'id univoco del contenuto(i): lo uuid. La vista a cui inviamo i dati potrà utilizzare questo dato per caricare il contenuto che abbiamo referenziato e gestirla come meglio crede.

E la referenza singola?

Questo esempio include il formato del ReferenceField per eseguire referenze multiple; la referenza singola è possibile con pochi accorgimenti e differenze nel codice HTML generato, ma soprattutto richiede un campo ReferenceField senza l'attributo multiValued attivo.
Lascio la realizzazione al lettore, come esercizio di analisi Plonesca!

Conclusione

Spero che questa breve carrellata vi abbia fatto capire come Plone, pur rimanendo un CMS di livello enterprise, permetta comunque di utilizzare le proprie funzionalità senza addentrarsi troppo nei meandri della sua architettura.

19/11/2013

di Luca Bellenghi 15:05:00

LinguaPlone e siti già popolati: trattare con attenzione!

Una cosa a cui non avevo mai dato molta importanza è il modo con cui UberSelectionWidget (contenuto in plone.app.form) salva il riferimento all'oggetto tramite il suo path.

 

Installando e configurando LinguaPlone, di solito il primo passo da fare è quello di spostare tutti i documenti del sito nella sezione dell'italiano. "Unico" problema è che tutto quello che si riferisce ad altri elementi tramite un path perde il riferimento, dal momento che si passerà da:


/sitename/my/old/path
 

a:


/sitename/it/my/old/path
 

L'effetto indesiderato è che componenti come le portlet che usano riferimenti per path, non possiedono più una configurazione corretta e, nella migliore delle ipotesi, scompaiono alla vista.

Se siamo davanti a un sito con poche portlet, sanare la situazione a mano può essere molto più veloce che procedere in modo programmatico, reimpostando i percorsi sbagliati.

Ma se gli elementi sono tanti, bisogna pensare a procedere in un modo diverso. Stessa cosa se non si ha assolutamente idea di quante portlet ci possono essere nel sito.

Come procedere?

La prima cosa che si può fare è ispezionare il sito. Il modo più comodo che conosco per verificare le portlet presenti è quello di usare redturtle.portlets.inspector, richiamando la vista "@@inspect-portlets" sulla radice del sito. Una volta scoperto quali portlet sono state create, bisogna verificare se fra i loro campi sono presenti quelli di tipo "Choice" che utilizzano lo UberSelectionWidget (oppure, ovviamente, altri campi con riferimenti basati su path).

Una volta identificate le portlet che possono presentare problemi, siamo già a un ottimo punto per poter sanare il sito. La vista di redturtle.portlet.inspector presenta la parte più importante nel seguente metodo:

    
def assignments(self, obj): ''' Get assignments for object ''' all_assignments = {} for manager_name, manager in self.portlet_managers: manager_assignments = getMultiAdapter((obj, manager)) try: keys = manager_assignments.keys() except: keys = [] if keys: all_assignments[manager_name] = [type(manager_assignments[x]) for x in keys] return all_assignments

Con questo metodo, per ogni oggetto si verifica quali portlet contiene ogni manager associato al contesto.

Quello che possiamo fare è cambiare l'ultimo list comprehension:


all_assignments[manager_name] = [type(manager_assignments[x]) for x in keys]

con:

 
fix_these = ['plone.app.portlets.portlets.navigation', 'rer.portlet.er_navigation.erportletnavigation']: for key in keys: if manager_assignments[key].__module__ in fix_these: if not manager_assignments[key].root.startswith('/it/'): manager_assignments[key].root = "/it%s" % manager_assignments[key].root print "fix navigation in %s" % obj.absolute_url() return all_assignments

In questo caso si verifica se la portlet è un navigatore e si va a sostituire il valore corrente della radice del navigatore con il percorso corretto.

Lo stesso approccio si può usare con tutte le portlet che presentano problemi di questo tipo (plone.portlet.collection, redturtle.portlet.collection, rer.portlet.advanced_static, ecc.).

 

Immagine in testata: http://www.flickr.com/photos/mag3737/5480814627/

Immagine per Facebook: http://www.flickr.com/photos/mig/15964697/

13/11/2013

di Andrea Cecchi 08:05:00

Guida galattica per portlettisti: come trovare contenuti a colpo sicuro

Come ben sapete, ci sono alcune portlet base di Plone (come il navigatore o la portlet collezioni) che permettono di inserire un riferimento a un contenuto del sito mediante il seguente campo:

portlet field

Questi tipi di campi permettono di ricercare i contenuti in due modi:

  • inserendo il titolo dell'elemento voluto
  • navigando tra le cartelle proposte.

I due metodi funzionano bene nel 99% dei casi, in quanto quel campo può essere configurato (nel codice della portlet) per mostrare/far selezionare solamente contenuti di un determinato tipo o partendo da una determinata cartella, ma ci sono alcuni casi limite in cui questi non ci permettono di trovare il contenuto voluto.

Per esempio, a me è capitato di dover creare delle nuove portlet che dovessero permettere ai redattori di inserire il riferimento a un qualunque oggetto nel portale (quindi non solamente una collezione o una determinata cartella), ma essendo un portale pieno di contenuti e documenti, capitava spesso che cercando determinate combinazioni di parole non si riusciva a trovare il contenuto desiderato. Questo per due motivi principali:
  • Il campo mostra solamente i primi 20 risultati e non permette di vedere i successivi. Anche provando a navigare per cartelle, se il contenuto è in una cartella con più di 20 elementi, questo potrebbe non venir visualizzato tra i possibili risultati.
  • La ricerca testuale non esegue una ricerca solo nel titolo ma nell'indice SearchableText, che racchiude i valori di Titolo, Descrizione ed eventuale Testo, quindi spesso ritorna anche risultati indesiderati.

Analizzando meglio il codice, ho scoperto che nel metodo che esegue la ricerca è possibile (senza nessuna personalizzazione particolare) selezionare esattamente il contenuto desiderato utilizzando il parametro "path".

Per esempio, se ci serve linkare il contenuto "Pagina di prova" che si trova in un determinato percorso (cartella-a/cartella-b), basterà scrivere all'interno del campo:

path:cartella-a/cartella-b/pagina-di-prova

Non serve indicare nel percorso l'id del portale, e se il contenuto è stato trovato, vedrete un solo risultato con il titolo e il suo percorso.

Ricerca corretta

Se invece il contenuto non è stato trovato (ad esempio perché il path non era corretto), viene mostrato comunque un ipotetico risultato, ma si riconosce in quanto mostra il path ma non il titolo.

Ricerca sbagliata

Questo è solo un piccolo trucchetto che a volte può tornare utile anche ai semplici redattori dei siti, per semplificarsi la vita.

Addio e grazie di tutti i pesci

05/11/2013

di Alessandro Pisa 07:55:00

mr.scripty: il MacGyver dei tuoi buildout (graffette incluse)

Suppongo che i miei lettori già conoscano, ammirino e temano la potenza di zc.buildout.

Suppongo che i miei lettori già conoscano, ammirino e temano la potenza di MacGyver.

Scrivere una recipe per buildout non è un'operazione super complicata, ma si tratta pur sempre di una cosa non immediata.

A volte è molto meglio usare una soluzione non ottimale purché sia efficace in tempi rapidi. Per questo può venire in vostro aiuto mr.scripty.

Chi è? Cosa fa?

mr.scripty è una ricetta che vi permette di eseguire codice Python, che trova specificato nelle sue opzioni, durante il buildout.

La documentazione ufficiale è molto esaustiva sulla sintassi da utilizzare e contiene tanti esempi in cui questa ricetta si mostra efficace e produttiva, ad esempio:

Io mi limito ad esempi inutili giusto per farvi cogliere le enormi potenzialità insite in questa ricetta.

Vediamolo alle prese con una cosa semplice, il buon vecchio "hello world":

[buildout]
parts = helloworld
[helloworld]
recipe=mr.scripty
main=
    ... print 'Hello world!'

Notate il codice Python preceduto dai tre punti (...). Lanciando questo buildout otterremo:

[ale@padme mr.scripty]$./bin/buildout -Nc example0.cfg
Hello world!
Installing helloworld.

Abbiamo dato la graffetta a MacGyver.

http://images3.wikia.nocookie.net/__cb20071109231952/nonciclopedia/images/thumb/7/71/Genesi_MacGyver.jpg/300px-Genesi_MacGyver.jpg

Ovviamente potete anche importare moduli Python, ad esempio os:

[ale@padme mr.scripty]$cat buildout.cfg
[buildout]
parts = parentfolder
[parentfolder]
recipe=mr.scripty
value=
    ... import os
    ... value = os.path.split(self.buildout['buildout']['directory'])[-1]
    ... print "My parent folder is %s" % value
    ... return value
[ale@padme mr.scripty]$./bin/buildout
My parent folder is mr.scripty
Installing parentfolder.

Oltre alla graffetta, MacGyver ha pure le batterie del pitone. Rabbrividiamo...

http://img713.imageshack.us/img713/4619/video1mp4snapshot002720.jpg

Mi fermo qua, credo questo sia sufficiente per invogliarvi a dare un'occhiata alla documentazione del prodotto e a tenerlo in considerazione per le vostre prossime scorribande nei file cfg.

Caveat

Concludo dicendo di non farvi prendere la mano...

I file di configurazione non sono il posto adatto per scrivere codice Python. Prima di cominciare a sviluppare codice dentro un file di configurazione del buildout pensateci due volte... E' forse più sensato scrivere un'apposita recipe? Chi prenderà in mano i vostri buildout, capirà cosa sta succedendo?

Detto questo... happy buildouting to everybody!

02/10/2013

di Luca Fabbri 09:55:00

Gestione Tabelle in Plone con collective.tablepage

Con lo zampino di una serie di recenti Decreti Legge su Amministrazione Aperta/Trasparente, siamo stati contattati da vari clienti più o meno con la stessa richiesta: "nel mio sito Plone devo essere in grado di mostrare ai cittadini una tabella e gestirla in modo semplice".

Questa improvvisa richiesta di tabelle HTML non è una vendetta di HTML 2.0 che torna dalla tomba: siamo sempre troppo diffidenti verso questo tag quando invece, usato per mostrare dati e non per disporre contenuti, può essere estremamente utile se non addirittura il migliore mezzo a nostra disposizione.

La Richiesta

Le discussioni telefoniche avute con questi clienti (per nostra fortuna tutti piuttosto "tecnici" e ferrati sull'uso di Plone) possono essere riassunte tutte allo stesso modo.

Cliente X
Ho bisogno di poter gestire in modo semplice una tabella, inserire nuove righe, impostare le colonne...
Io (diffidente)
Beh... già conosci come utilizzare le tabelle con TinyMCE dentro alle pagine Plone. Cos'altro ti serve?
Cliente X
Certamente, ma purtroppo non è così semplice... Avrei bisogno di delegare ad altri, a più persone, la gestione di questa tabella. In pratica io devo imbastirne il modello e altri utenti inserire le righe.

Ora... sappiate che a questo punto di solito interviene la mia innata pigrizia, che vorrebbe risolvere la situazione nel modo più semplice possibile (per me). Come sempre, quindi, propongo la soluzione "out-of-the-box" (qualunque essa sia).

Io (col tono del "sono un genio")
Beh.. sarebbe ancora possibile... Tu imposti la tabella con TinyMCE, disegni le intestazioni delle colonne e dai istruzioni ai tuoi utenti su come lavorare!
Cliente X (dubbioso/incredulo)
Fammi capire... io dovrei istruire un certo numero di redattori sul come entrare in modifica della pagina, mettere mano alla tabella usando i controlli di TinyMCE, rispettare il lavoro degli altri redattori, inserrire le righe in modo sensato e non rovinare o modificare il modello pre-impostato?
Io (col tono del "mo' ti insegno come gira il mondo")
Esatto!

Tornato dalla pausa pranzo, il Cliente X non aveva ancora riattaccato il telefono ma era comunque impegnato nella stessa risata... a quel punto ho iniziato ad avere un leggerissimo sospetto che la mia non fosse stata un'idea poi così brillante.

Usare TinyMCE per gestire tabelle in modo cooperativo è una cosa da evitare... è un po' come un invito di Hannibal Lecter a prendere un aperitivo insieme.

La colpa in questo caso non è imputabile ai redattori: scrivere documenti molto complessi con TinyMCE a volte può essere complicato di per sé, ma con le tabelle la difficoltà aumenta.
In ogni caso l'utente va guidato e non ci si può aspettare che un manuale d'uso risolva sempre il problema.

In un mondo perfetto, il Cliente X avrebbe preparato questo documento iniziale:

Tabelle nel mondo perfetto

Sempre in quel mondo perfetto alla fine avremmo avuto una bella tabella piena di dati e ci sarebbe stato consegnato un premio con targa per la brillante idea avuta.

Nel mondo reale, lasciando troppa libertà agli utenti, potreste trovarvi a visitare il documento qualche giorno dopo e trovare questo:

Tabelle nel mondo reale

Non si può fare molto, a questo punto - se non prendere atto che TinyMCE non è la strada giusta (e valutare il prezzo della moto).

Prodotti disponibili

La mia pigrizia ancora non si è data per vinta, e come sempre sono andato a vedere che cosa qualcun altro poteva aver già sviluppato per me; ho quindi eseguito una complessa ricerca di mercato, sfruttando le mie note doti di esperto SEO.
Sono partito con poche speranze, ma ho poi trovato che questo bisogno di tabelle e dati strutturati non è poi così astruso, nel Mondo Plone.

Vale la pena evidenziare due soluzione già disponibili.

collective.table

In collective.table mi ci ero già imbattuto tempo fa.
E' molto orientato al layout della tabella e usa il famoso plugin JavaScript/jQuery DataTable (integrabile in Plone tramite l'add-on collective.js.datatables).
Il problema principale di questo prodotto è che, anche se in modo ridotto rispetto a un uso "puro" dell'editor WYSIWYG, è ancora piuttosto semplice fare disastri nella tabella: nessun concetto di "mia riga", "tua riga".
Va detto, però, che l'uso di DataTable migliora di molto l'esperienza dell'utente.

Per di più: per uno di questi clienti era anche necessario mantenere la compatibilità con Plone 3.3, e collective.js.datatables non la offre.

collective.pfg.soup

A differenza di collective.table, collective.pfg.soup si occupa approfonditamente anche dello storage dei dati e della gestione delle righe in modo granulare.
Più che un prodotto è una piccola suite di prodotti ben congegnati, pluggabili e con un supporto nativo all'immagazzinamento di grosse quantità di dati.

L'idea dietro a questo prodotto sta nell'uso di PloneFormGen per lasciare all'utente che deve imbastire la tabella uno strumento flessibile sulla definizione delle colonne.
Un nuovo adattatore di PloneFormGen si occupa poi di salvare i dati in un "zuppa" (soup) di informazioni mostrate poi sotto forma di tabella.

Al tempo della verifica del prodotto alcune funzionalità di gestione della tabella erano assenti, ma al 90% il lavoro era fatto.

Veniamo agli unici lati negativi che ho trovato:

  1. L'uso di PloneFormGen più un adattatore dà una grossa libertà e ha grandi potenzialità. Usando in passato altri prodotti che si basano su PFG (l'eccellente uwosh.pfg.d2c in primis) ho però notato un problema di complessità della struttura che viene a crearsi, che a volte confonde il redattore (abbiamo un contenuto form, con all'interno un contenuto adattatore, con all'interno i dati... ma dov'è il "documento"?).
  2. Il problema dell'indicizzazione dei contenuti: se avessi fatto una tabella con TinyMCE, il testo dentro alla tabella sarebbe stato ricercabile dal motore interno di Plone. Così invece il comportamento non sarebbe scontato?
  3. Questi limiti forse non ci avrebbero fermato, ma l'assenza di compatibilità con Plone 3 e l'impossibilità di tentarne un backport, viste le numerose dipendenze, è stata letale.

Se dovesse capitarvi: non scartatelo a priori. Se vi trovaste nella situazione di avere questo tipo di necessità, date un occhiata a questo prodotto.

collective.tablepage

A questo punto mi sono arreso e ho capito che avrei dovuto lavorare davvero. Bye bye, pigrizia!

La soluzione perfetta (almeno per il nostro caso) sarebbe stata quella di avere a disposizione una semplice pagina Plone dove:

  • un redattore/amministratore decide la struttura della tabella
  • i redattori non possono toccare la tabella ma solo inserire le righe
  • nessuno tocca le righe degli altri, né può cambiare l'ordine delle righe
  • un utente con opportuni privilegi può gestire tutta la tabella, eliminando o modificando le righe di tutti.

Abbiamo quindi racchiuso tutte queste caratteristiche in collective.tablepage.

Questo nuovo prodotto aggiunge al vostro sito Plone un nuovo tipo di contenuto estremamente simile alla normale Pagine: la "Pagina con Tabella".
L'idea dietro a questo nuovo tipo parte dal presupposto di sembrare identica alla normale pagina Plone se il visitatore corrente non ha poteri particolari sul contenuto.
In pratica: il lavoro svolto all'interno di una Pagina con Tabella sarebbe anche replicabile in una semplice Pagina Plone con le azioni di un utente paziente. E' quindi possibile inserire del testo "prima" e "dopo" la tabella, che rimane comunque il contenuto più importante della pagina.

Abbiamo così tanto tenuto in conto questo obiettivo che vale la pena evidenziare queste due caratteristiche, anche se di poco conto:

  • l'icona del tipo di contenuto è identica a quella della normale Pagina
  • la vista del contenuto è la stessa della Pagina Plone

L'unica opzione particolare (se abilitata) è la comparsa di un link a fine tabella per scaricarne il contenuto in formato CSV.

Non appena si entra nel back-end emergono invece le differenze.

Prima di tutto, la pagina di "Modifica".
Possiamo come sempre gestire i normali dati di Plone quali titolo, descrizione, ecc., e l'utente potrà anche impostare una porzione di testo che preceda la tabella e un'altra che la segua, ma il vero corpo del testo qui non viene gestito, ma solo configurato.

Sto parlando del campo "Colonne" che ci permette di specificare intestazioni e colonne della tabella, che gli utenti andranno a popolare.

Sebbene all'interno di TinyMCE non sia possibile inserire altro che testo, alcune richieste particolari dei clienti ci hanno spinto a sviluppare il concetto di "tipo" di colonna (al momento: Stringa, Testo lungo, Selezione, File).

Uno sviluppatore potrebbe aggiungere facilmente altri tipi di colonna, ma tenete sempre presente che non è nello scopo del prodotto riscrivere PloneFormGen da capo.
Per lo stesso motivo: non esistono validazioni o obbligatorietà delle colonne (e perché dovrebbero esserci? Con TinyMCE nessuno ci può obbligare a scrivere dentro a una cella o forzare un formato!).

Al termine della configurazione tutti gli utenti con ruolo "Può aggiungere" potranno accedere al nuovo tab "Modifica tabella" e accedere a un modulo che permetterà loro di inserire nuove righe della tabella, gestire le righe inserite in precedenza (da loro) o effettuare caricamenti massivi tramite CSV.

Gli utenti con ruolo "Può modificare" potranno invece entrare in modifica/configurazione del documento e, soprattutto, gestire le righe di tutti (oltre che modificare l'ordine delle righe).

In ogni caso, a seconda del numero e del tipo di colonne inserite nella configurazione, il form di inserimento/modifica di una riga cambia di conseguenza.

Il tipo più complesso è il File, il cui scopo è inserire link a File all'interno della cella.
Anche in questo caso si è tentato di ottenere l'obiettivo nel modo più naturale possibile: se questa operazione venisse fatta usando TinyMCE si userebbero gli strumenti dell'editor, creando un collegamento a un contenuto interno al sito. Per lo stesso motivo questo campo offre la possibilità di generare un nuovo contenuto File nel sito e mostrare un link al contenuto, o selezionare un contenuto pre-esistente.

Qualunque sia la configurazione e il numero di righe compilate, il risultato finale è quindi una tabella HTML che va a popolare parte del campo "testo" del contenuto. Per questo motivo i dati della tabella sono indicizzati automaticamente da Plone.
Dal punto di vista del visitatore del sito: in tutto e per tutto una Pagina!

Conclusioni

Probabilmente per quelle (forse poche) tabelle che avete necessità di inserire nei vostri contenuti Plone, TinyMCE sarà una scelta sufficiente. Ma se mai dovreste superare uno di questi limiti spero troviate tra questi prodotti quello che fa per voi.

...e ora scusatemi, devo tornare al lavoro!

Note legali

In questo articolo si è ovviamente e volutamente scherzato sulle capacità dei redattori Plone... a dire il vero di solito sanno usare TinyMCE molto meglio del sottoscritto.

Si dichiara che non è stato fatto del male a nessun redattore Plone durante la scrittura di questo post!

18/09/2013

di Luca Bellenghi 12:15:00

Sfruttiamo al massimo la Zope Component Architecture

Quando aggiungiamo delle funzionalità agli archetypes di un sito Plone tramite la registrazione di utility, adattatori e componenti simili, essi vengono registrati in un site manager.

Tramite questo tipo di componenti possiamo realizzare eventi che, alla creazione o modifica dell'oggetto, eseguono una specifica azione; o adattatori che forniscono comportamenti aggiuntivi a un oggetto, come ad esempio l'aggiunta di campi tramite schemaextender e altre funzionalità simili.

In questo post voglio mostrare come sia possibile associare queste funzionalità aggiuntive ai tipi di contenuto in base alla posizione del sito in cui si trovano. Potremo così avere un tipo di contenuto che in una cartella del sito modifica alcuni suoi dati grazie all'esecuzione di un evento, oppure in altre cartelle potrà generare documenti con campi in più rispetto al documento base, o ancora avere tipi di contenuto che avranno a disposizione delle utility aggiuntive.

Localsitemanager, chi era costui...

Plone, nella sua installazione base, comprende un pacchetto chiamato five.localsitemanager che consente di aggiungere site manager locali alle cartelle del sito; è grazie a questi nuovi site manager che si potranno estendere le funzionalità dei tipi di contenuto già presenti in un sito, senza doverne creare di nuovi.

Supponiamo di avere un sito in cui servono i tipi base di Plone e una sezione particolare in cui si necessita del tipo 'Pagina' con alcune caratteristiche diverse dal tipo originale.

Prima di tutto, è necessario preparare la sezione in cui servono i comportamenti personalizzati, e lo possiamo fare in due modi.

Il primo modo consiste nell'eseguire l'operazione via codice personalizzato e possiamo farlo tramite una vista:


<browser:page
for="*"
class=".create_sm.CreateSM"
name="createsm"
permission="cmf.ManagePortal"
/>

class MakeObjManager(BrowserView): def __call__(self): if not ISite.providedBy(self.context): make_objectmanager_site(self.context)

Una volta preparato il codice, si può chiamare questa vista via browser sulla cartella in cui ci interessa cambiare il comportamento con le funzionalità aggiuntive:


http://www.nomesito.it/cartella/sezione-interessata/@@createsm
 

Così facendo, il contesto su cui si chiama la funzione make_objectmanager_site diventa un site manager locale. Questo metodo può essere comodo se c'è bisogno di effettuare diverse operazioni sulla cartella oltre alla semplice marcatura come nuovo site manager; ad esempio, si potrebbero voler registrare immediatamente degli adattatori o delle utility o altri componenti.

Il secondo metodo lo mette a disposizione five.localsitemanager che registra la vista 'components.html' con un'interfaccia web minimale per marcare e demarcare una cartella come site manager. Anche questa vista sarà richiamabile tramite browser:


http://www.nomesito.it/cartella/sezione-interessata/@@components.html
 

Questo secondo modo è l'ideale se ci interessa semplicemente marcare la cartella come site manager.

Con un nuovo site manager a disposizione, per raggiungere i nostri intenti dobbiamo registrare i nuovi componenti. Non possiamo registrarli via ZCML perché dobbiamo metterli in relazione con il nuovo site manager appena creato, pertanto dobbiamo sfruttare la registrazione tramite python. Supponiamo di voler aggiungere alla pagina un nuovo campo e un evento. Dovremo creare l'adapter e l'handler che ci servono (PageExtender e rename_document):


class ATExtStringField(ExtensionField, atapi.StringField): """ """ class PageExtender(object): """ """ zope.interface.implements(ISchemaExtender) _fields = [ ATExtStringField('productcode', widget=atapi.StringWidget()),
] def __init__(self, context): self.context = context def getFields(self): return self._fields


def rename_document(obj, event):
obj.setTitle('[Prodotto]: %s' % obj.Title()) 

class RegisterAdapters(BrowserView):
def __call__(self):
sm = self.context.getSiteManager()
sm.registerAdapter(
PageExtender,
(IATDocument,),
ISchemaExtender,
name=u'ProductCodePage'
)
sm.registerHandler(rename_document,
(IATDocument, IObjectInitializedEvent))
 

Infine, chiamando la vista RegisterAdapters sulla cartella scelta, rendiamo noto al site manager locale di avere a disposizione queste due funzionalità in più.

Non rimane che testare quanto fatto creando dentro la suddetta cartella una pagina; vedremo che nel form di edit c'è un campo in più (sopra chiamato 'productcode') e che, dopo il salvataggio, il titolo è stato modificato dall'handler che si occupa della gestione dell'evento.

Come controprova è sufficiente creare una pagina fuori dalla suddetta cartella e verificare che i comportamenti descritti qui sopra non si ripetano.

Immagine in testata: http://www.flickr.com/photos/kansasphoto/7871992906/

Immagine per Facebook: http://www.flickr.com/photos/freefoto/5982549938/

10/09/2013

di vito80ba 10:07:26

[diazo] Spostare elementi nel dom del solo “content”

Situation: devo muovere uno span con id “numbers” da una parte all’altra del dom del “content”, ovvero da dove viene printato NEL tag “a” contenuto nel “li” #portaltab-events. Come fare? <replace css:content="#portaltab-events a"> <xsl:copy-of select="." /> <xsl:copy-of select="//*[@id='numbers']" /> <xsl:apply-templates /> </replace> DOM prima della modifica a volo: <ul> [..] <li id="portaltab-events"> <a href="#">Eventi</a> </li> […]
Situation: devo muovere uno span con id “numbers” da una parte all’altra del dom del “content”, ovvero da dove viene printato NEL tag “a” contenuto nel “li” #portaltab-events. Come fare? <replace css:content="#portaltab-events a"> <xsl:copy-of select="." /> <xsl:copy-of select="//*[@id='numbers']" /> <xsl:apply-templates /> </replace> DOM prima della modifica a volo: <ul> [..] <li id="portaltab-events"> <a href="#">Eventi</a> </li> […]