Liferay Portal Overview

V parte: Portlet, configurazioni e preferenzedi

In questo articolo vedremo come gestire le Portlet Preference per memorizzare parametri di configurazione. In tal modo è possibile gestire la logica implementata dalla nostra portlet custom.

Introduzione

Le Portlet Preferences ci consentono di salvare in modo persistente sul database le configurazioni delle nostre portlet; questo avviene tramite l'oggetto PortletPreferences che rappresenta una riga della tabella PortletPreferences, all'interno del database di Liferay.

Nella versione 6 è preferibile non utilizzare il Configuration Mode che si poteva utilizzare anche nelle versioni precedenti, poiche', non essendo uno standard nella portlet specification, il team di Liferay ha deciso di dismetterlo gradualmente, in favore dell'Edit Mode che, al contrario, è uno standard delle portlet.

Portlet Preferences, utenti e pagine

È doveroso fare una puntualizzazione riguardo le PortletPreferences e il modo in cui Liferay le implementa. La Java Portlet Specification [1]recita: "Portlets are commonly configured to provide a customized view or behavior for different users. This configuration is represented as a persistent set of name-value pairs and it is referred to as portlet preferences."

Da questa affermazione potremmo dedurre che, tramite le PortletPreferences, le portlet devono avere configurazioni specifiche per ogni utente, e che questo deve essere il comportamento. Bene, in Liferay non è così, o meglio, non è l'unico comportamento possibile.

L'implementazione di Liferay consente diversi livelli di granularità per la condivisione delle preferencze; questo vuol dire che, ad esempio, delle PortletPreferences possono essere condivise a livello utente, a livello pagina o a livello community. I diversi livelli sono pilotati tramite le configurazioni delle portlet, in particolare grazie a tre proprietà presenti nel file liferay-portlet.xml:



Tutte e tre le property possono assumere solo i valori true o false. A seconda di come configuriamo la portlet, avremo un diverso livello di condivisione. Tutte le combinazioni e i risultati che otteniamo sono riassunti nelle tabelle che seguono: in figura 1 siamo nel caso dle portlet non istanziabili (false).

Figura 1 - Possibili livelli di condivisione per portlet non istanziabili.

 

In figura 2, invece, siamo nel caso di portlet che possono essere istanziate (true).

Figura 2 - Possibili livelli di condivisione per portlet istanziabili.

 

Gestione delle PortletPreferences in Edit Mode

Come stabilito dalla portlet specification, esistono 3 Portlet Mode in cui una portlet può trovarsi:

  • View
  • Edit
  • Help

View è la visualizzazione in cui sono mostrate le funzionalità della portlet, in sostanza il "front-end". Edit è la modalità in cui gestiamo le configurazioni della portlet. Help, infine, serve per visualizzare una pagina di aiuto.

La prima, View, è quella che abbiamo usato nei precedenti articoli per interagire con la portlet. Ad esempio eravamo in View Mode sia quando visualizzavamo la lista degli autori che quando veniva visualizzata la form per inserire/modificare un autore.

Se vogliamo gestire anche l'Edit Mode dobbiamo dichiararlo esplicitamente nel file portlet.xml; possiamo farlo manualmente o farlo fare all'IDE; il secondo scenario però è possibile solo nel caso in cui stiamo creando una portlet da zero. Vediamo nel dettaglio come farlo in entrambi i modi

Modifica manuale del file portlet.xml

Dentro individuiamo il tag . Quella che segue è la configurazione che dovremmo avere. In grassetto c'è la riga che dobbiamo aggiungere.


       text/html
       VIEW
       EDIT

Sempre dentro questo file aggiungiamo le seguenti righe


       edit-jsp
       /html/preferencesexampleportlet/edit.jsp

Dentro ci va il percorso alla JSP che sarà visualizzata quando passiamo nell'Edit Mode della portlet. Naturalmente va creata anche questa JSP nel percorso che abbiamo dichiarato.

Modifica automatica tramite IDE

Se stiamo creando una portlet nuova, invece, possiamo lasciare all'IDE l'onere di fare quanto descritto sopra: è semplicissimo, basta mettere una spunta in fase di creazione, in una delle finestre del wizard, come illustrato nel rettangolo rosso in figura 3.

Figura 3 - Creazione dell'Edit Mode per una portlet.

 

Ora che abbiamo la nostra portlet con le giuste configurazioni proviamo a farne il deploy. Se il deploy va a buon fine, aggiungiamo la portlet in una qualsiasi pagina (dobbiamo essere loggati per farlo): se ora clicchiamo sull'icona a forma di chiave inglese, dovremmo vedere un menù come quello in figura 4. Preferences è la voce del menù che ci permette di visualizzare la portlet in Edit Mode. Cliccando, vedremo il contenuto della JSP edit.jsp che abbiamo creato precedentemente.

Figura 4 - Menù della portlet.

 

Come trattare le preferenze

Vediamo ora la parte interessante, ossia come gestire le Preferences. Cosa farne dipenderà dalle esigenze di ciascuna portlet e non è argomento dell'articolo. In questo esempio vedremo come trattare due tipi di preference, il singolo campo di testo e una lista di preferences dello stesso tipo; in particolare per il secondo tipo, scopriremo come Liferay fornisce, ancora una volta, delle facility per la gestione di preference di questo genere.

Decidiamo che il singolo campo di testo lo chiamiamo, appunto, singolo, mentre la lista la chiameremo listaParametri. Vediamo subito la JSP che renderizza l'edit mode della portlet:

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<%@ page import="javax.portlet.PortletPreferences" %>

<%
       //recuperiamo le preferences per questa istanza
       PortletPreferences preferences = renderRequest.getPreferences();
       //leggiamo dalle preferences i nostri valori,prima "singolo"...
       String singoloParametro = preferences.getValue("singolo","");
       //...e poi la lista
       String[] listaParametri =
             preferences.getValues("listaParametri",new String[0]);
%>


       action="<%= saveConfURL.toString() %>" method="post">
       ">
            
             <%
             if (listaParametri.length == 0) {
                    listaParametri = new String[1];
                    listaParametri [0] = "";
             }
             for (int i = 0; i < listaParametri.length; i++) {
             %>
                   

                          

                                                                          label="listaParametri"
name="<%= "listaParametri"+i %>"
                                        value="<%= listaParametri[i] %>" />
                          

                   

             <%
             }
             %>
            
                   
            

      



       new Liferay.AutoFields(
             {
                    contentBox: 'fieldset.prefList',
                    fieldIndexes: 'prefListIndexes'
             }
       ).render();

Analizziamo il pezzo di codice racchiuso tra i tag <% %>, subito dopo il primo blocco di import. La chiamata

PortletPreferences preferences = renderRequest.getPreferences();

recupera un oggetto PortletPreferences; da questo oggetto, se sono state precedentemente salvate, leggiamo le preferences valorizzate, con queste chiamate:

String singoloParametro = preferences.getValue("singolo","");
String[] listaParametri = preferences.getValues("listaParametri",new String[0]);

La prima recupera un valore singolo, la seconda una lista. Per entrambe il secondo parametro è il valore restituito nel caso la preferences sia null. Il restante pezzo di codice è composto sostanzialmente dalla form per l'inserimento dei valori e da uno script per la gestione della lista. Guardiamola nel dettaglio: creata con le taglib Liferay-Aui [2] [3] [4] [5], punta all'action URL saveConf. Dopo l'apertura della form troviamo

">

Questo tag renderizza un fieldset HTML la cui class sarà uguale al contenuto passato dentro cssClass. In questo caso è molto importante specificare una class, ed è ancora più importante che dentro la class ci sia la portlet namespace. Trovandoci già dentro un tag, non possiamo utilizzare un'altra chiamata a una taglib, ossia . Per questo motivo abbiamo utilizzato la notazione "<%= renderResponse.getNamespace()+"prefList"%> dentro cssClass. La portlet namespace è essenziale in questo esempio per un motivo che vedremo poco più avanti. Subito dopo il fieldset troviamo il primo campo di input, eventualmente valorizzato con il contenuto di singolo. Appena sotto troviamo un ciclo for sull'oggetto listaParametri; per ogni valore di listaParametri che troviamo, creiamo il corrispondente campo di input valorizzato. La form viene completata dal bottone per fare submit.

Per ultimo troviamo uno script, fondamentale per la gestione della lista di parametri. Questo gestisce diversi aspetti: la possibilità di aggiungere e rimuovere dinamicamente le righe di input nel nostro form; la possibilità di usare un manger per fare undo delle operazioni (se ad esempio abbiamo rimosso per sbaglio una riga, è possibile recuperarla se non abbiamo ancora fatto submit); inoltre è responsabile della logica che ci permette di avere, appunto, la lista. Tutto questo avviene tramite l'oggetto  Liferay.AutoFields, che abbiamo creato con due parametri, illustrati qui di seguito.

contentBox: 'fieldset.prefList'

Dentro contentBox passiamo il selettore che contiene le righe che vogliamo duplicare, nel nostro caso il

, utilizzando la classe (ecco perche' prima abbiamo detto che era importante dare la classe al fieldset).

fieldIndexes: 'prefListIndexes'

Il nome del parametro da passare in POST alla action e che conterrà la lista di indici per i campi. Sarà più chiaro il suo utilizzo quando vedremo il codice per salvare le preferences.

Si può notare come in entrambi i valori passati c'è . Questo tag è necessario perche' è possibile che nella stessa pagina ci siano due form dello stesso tipo e, avere dei campi con lo stesso nome, potrebbe generare errori inaspettati. Appendere la portlet namespace, unica per ogni istanza di portlet, ci tutela da questo tipo di errori.

Salvare le preferenze

Guardiamo ora il codice grazie al quale possiamo salvare le nostre preferences. Dentro la classe della portlet implementiamo il metodo saveConf al quale punta il form di sopra

public void saveConf(ActionRequest request, ActionResponse response)
                    throws Exception {
             // otteniamo l'oggetto PortletPreferences che "contiene" le preference
             // dell'istanza del nostro portlet
             PortletPreferences portletPreferences = request.getPreferences();
             // Recuperiamo il valore del parametro singolo
             String singolo = ParamUtil.getString(request, "singolo");
             // settiamo il valore del campo "singolo"
             portletPreferences.setValue("singolo", singolo);
             List prefList = new ArrayList();
             //array di indici per le preferences
             int[] prefListIndexes = StringUtil.split(
                           ParamUtil.getString(request, "prefListIndexes"), 0);
             for (int i = 0; i < prefListIndexes.length; i++) {
                    int prefListIndex = prefListIndexes[i];
                    //ciclando sull'array di indici recuperiamo i valori inseriti
                    //nelle righe
                    String pref = ParamUtil.getString(request, "listaParametri"
                                  + prefListIndex);
                    prefList.add(pref);
             }
             //creiamo l'array con i valori da salvare nelle preferences..
             String[] prefListArray = new String[prefList.size()];
             for (int i = 0; i < prefListArray.length; i++) {
                    prefListArray[i] = prefList.get(i);
             }
             //...e qui li settiamo
             portletPreferences.setValues("listaParametri", prefListArray);
            
             //salviamo le preferences sul database
             portletPreferences.store();
             //visualizziamo nuovamente la portlet in edit mode
             response.setRenderParameter("jspPage",
                           "/html/preferencesexampleportlet/edit.jsp");
       }

Il codice è molto semplice, l'unica riga su cui ci soffermiamo è questa:

String pref = ParamUtil.getString(request, "listaParametri"+ prefListIndex);

C'è un motivo per cui i valori della lista li recuperiamo utilizzando come chiave

"listaParametri"+ prefListIndex,

dove prefListIndex è uno dei valori contenuto nell'array prefListIndexes:

Liferay appende al nome di ogni campo di input della nostra lista di parametri un indice. Questi indici vengono passati in POST tramite un array, che leggiamo dalla request tramite questa chiamata:

int[] prefListIndexes = StringUtil.split(ParamUtil.getString(request, "prefListIndexes"), 0);

prefListIndexes è il nome che abbiamo dato al parametro fieldIndexes per creare l'oggetto Liferay.AutoFields. Per meglio capire questo concetto guardiamo l'immagine in figura 5.

Figura 5 - Form per le preferences.

La lista di parametri relativa al "Salva"

Questa è la lista dei parametri passati e i rispettivi valori quando clicchiamo su Salva:

_preferencesexampleportlet_WAR_preferencesexampleportlet_INSTANCE_zW9F_singolo 
= questo è il singolo
_preferencesexampleportlet_WAR_preferencesexampleportlet_INSTANCE_zW9F_listaParametri0
= primo
_preferencesexampleportlet_WAR_preferencesexampleportlet_INSTANCE_zW9F_listaParametri1
= secondo
_preferencesexampleportlet_WAR_preferencesexampleportlet_INSTANCE_zW9F_listaParametri2
= terzo
_preferencesexampleportlet_WAR_preferencesexampleportlet_INSTANCE_zW9F_listaParametri3
= quarto
_preferencesexampleportlet_WAR_preferencesexampleportlet_INSTANCE_zW9F_prefListIndexes
= 0,1,2,3

Una volta lette le preferences dalla request possiamo salvarle sul database, con questa chiamata:

portletPreferences.store();

Undo Manager

Per concludere facciamo un breve accenno ad una funzionalità che ci viene data "gratis" utilizzando l'oggetto AutoField: stiamo parlando dell'UndoManager. La sua implementazione è totalmente trasparente, al contrario della sua funzionalità che può rivelarsi utile in caso di clic sbagliati.

Sempre in Edit Mode, supponendo di avere almeno un campo della lista popolato, proviamo a cliccare sul bottone con il simbolo "meno": il campo sparisce (in realtà viene nascosto) e in cima alla portlet vedremo qualcosa di simile all'immagine di figura 6, nella parte evidenziata dal rettangolo rosso.

Figura 6 - Portlet Preferences, particolare dell'Undo Manager.

 

Se clicchiamo su Undo, vedremo come il nostro campo diventerà nuovamente visibile. L'Undo Manager è in grado di gestire una lista di operazioni e non una sola, memorizzandone la storia.

Conclusioni

In questo articolo abbiamo parlato delle Portlet Preferences e di come utilizzarle per salvare le nostre configurazioni. In particolare abbiamo visto la gestione di una lista di preferences e come, ancora una volta, Liferay ci offra delle facility per utilizzarla. Naturalmente non sono gli unici tipi di campi che possiamo usare, ma questo dipende dalle nostre esigenze; potremmo ad esempio aver bisogno di una select per vincolare l'utente a scegliere solo tra un set limitato di possibilità, piuttosto che una checkbox o un radio button.

La necessità di utilizzare le preferences è assolutamente personale; per fare un esempio di applicazione reale potrei citare un progetto nel quale è stato sviluppato un client mail personalizzato che fa pop solo dei messaggi che corrispondonoalle configurazioni specificate nelle preferenze. In questo caso la lista di preferenze viene utilizzata per memorizzare una serie di indirizzi (completi o parziali) "consentiti": solo le mail il cui mittente corrisponde a questi indirizzi vengono scaricate.

Naturalmente questo è solo un piccolo esempio di come poter sfruttare le preferenze; sicuramente, però, è evidente la loro utilità, ma anche la semplicità di utilizzo e, soprattutto, il beneficio che ne ricava uno sviluppatore in termini di risparmio di costi e tempo non dovendo implementare da zero la logica per gestirle.

Abbiamo visto inoltre come, grazie a una specifica implementazione di Liferay, sia possibile gestirne la granularità del livello di condivisione per utenti, pagine e community; il che aumenta notevolmente la possibile varietà di applicazioni che possiamo sviluppare.

Riferimenti

[1] JSR 286: Portlet Specification 2.0

http://www.jcp.org/en/jsr/detail?id=286

 

[2] Taglib Alloy+UI

http://www.liferay.com/community/wiki/-/wiki/Main/Alloy+UI

 

[3] Alloy UI Form

http://www.liferay.com/community/wiki/-/wiki/Main/Alloy+UI+Forms+%28aui%29

 

[4] Articolo introduttivo a YUI 3, sul quale si basa AlloyUI

http://blog.d-vel.com/web/blog/home/-/blogs/introduzione-a-yui-3

 

[5] Approfondimento su YUI 3: Il modulo IO

http://blog.d-vel.com/web/blog/home/-/blogs/yui-3-:-il-modulo-io

 

Condividi

Pubblicato nel numero
162 maggio 2011
Lorenza Amato è nata a Chieti nel 1982. Si è laureata in Informatica presso l‘Università degli studi di Bologna. Dal 2009 svolge, presso la D‘vel Snc, l‘attività di sviluppatrice software in ambiente Java. In particolare si occupa di sviluppo di progetti per Liferay Portal Server.
Articoli nella stessa serie
Ti potrebbe interessare anche