realizzazione interfacce web dinamiche con tecnologia ajax ed

Transcript

realizzazione interfacce web dinamiche con tecnologia ajax ed
Università Politecnica delle Marche
Facoltà di Ingegneria
Corso di Laurea in Ingegneria Informatica e dell’Automazione
Dipartimento di Ingegneria Informatica, Gestionale e dell’Automazione
TESI di LAUREA
REALIZZAZIONE INTERFACCE WEB DINAMICHE CON
TECNOLOGIA AJAX ED IMPLEMENTAZIONE
IN UN CMS
Relatore
Laureando
Prof. Luca Spalazzi
Paolo De Gruttola
Anno Accademico 2005/2006
A mamma
e
papà
INDICE
• Introduzione...................................................................................... 1
• CAPITOLO 1 – Analisi sistema esistente........................................ 6
1.1. Content Management System................................................. 6
1.2. Applicazione generata............................................................ 11
1.3. Librerie utilizzate...................................................................
16
1.4. Limiti e funzionalità richiesta................................................. 18
• CAPITOLO 2 – Realizzazione specifiche........................................ 20
2.1. Realizzazione live order......................................................... 20
2.2. Implementazione live order nel CMS....................................
27
2.3. Ulteriori modifiche e loro implementazione nel CMS........... 34
2.4. Rico Accordion....................................................................... 38
• CAPITOLO 3 – Test live order........................................................ 42
• Conclusioni....................................................................................... 44
• Bibliografia....................................................................................... 46
Introduzione
INTRODUZIONE
Originariamente il Web è stato concepito come un modo per visualizzare documenti
ipertestuali statici. Successivamente, grazie all’integrazione con database e
all’utilizzo di linguaggi di programmazione, come Javascript e PHP, di elementi
dinamici e di fogli di stile (dall'inglese CSS Cascading Style Sheets), si è evoluto
con siti dinamici, discostandosi dal vecchio concetto di semplici ipertesti e
puntando a somigliare ad applicazioni tradizionali per computer.
In quest’ottica di sviluppo un’azienda che fornisce ai propri clienti un’applicazione
web-based deve adattarsi a tali innovazioni, rendendo la propria applicazione facile
e veloce da usare. Spesso per realizzare il tutto vengono usate tecnologie di
programmazione particolari come AJAX.
AJAX, acronimo di Asynchronous JavaScript and XML, è una tecnica di
sviluppo web per creare applicazioni web interattive. L'intento di tale tecnica è
quello di ottenere pagine web che rispondono in maniera più rapida, grazie allo
scambio in background di piccoli pacchetti di dati con il server, così che l'intera
pagina web non debba essere ricaricata ogni volta che l'utente effettua una modifica.
Questa tecnica riesce, quindi, a migliorare l'interattività, la velocità e l'usabilità di
una pagina web.
La tecnologia AJAX utilizza una combinazione di:
•
HTML (o XHTML) e CSS per il markup e lo stile;
•
DOM (Document Object Model) manipolato attraverso un linguaggio
ECMAScript come JavaScript per mostrare le informazioni ed interagirvi;
•
l'oggetto XMLHttpRequest per l'interscambio asincrono dei dati tra il
browser dell'utente e il web server. In alcuni framework Ajax e in certe
1
Introduzione
situazioni, può essere usato un oggetto IFrame invece di XMLHttpRequest
per scambiare i dati con il server;
•
in genere viene usato XML come formato di scambio dei dati, anche se di
fatto qualunque formato può essere utilizzato, incluso testo semplice, HTML
preformattato. Questi file sono solitamente generati dinamicamente da script
lato server.
AJAX non è una tecnologia individuale, piuttosto è un gruppo di tecnologie
utilizzate insieme.
Le applicazioni web che usano AJAX richiedono browser che supportano le
tecnologie necessarie descritte sopra, come Mozilla Firefox e Internet Explorer.
Le applicazioni web tradizionali consentono agli utenti di compilare moduli e,
quando questi vengono inviati, viene inviata una richiesta al web server. Questo
agisce in base a ciò che è stato trasmesso dal modulo e risponde bloccando o
mostrando una nuova pagina. Dato che molto codice HTML della prima pagina è
identico a quello della seconda, viene sprecata moltissima banda. Dato che una
richiesta fatta al web server deve essere trasmessa su ogni interazione con
l'applicazione, il tempo di reazione dell'applicazione dipende dal tempo di reazione
del web server. Questo comporta che l'interfaccia utente diventa molto più lenta di
quanto dovrebbe essere.
Le applicazioni AJAX, d'altra parte, possono inviare richieste al web server per
ottenere solo i dati che sono necessari (generalmente usando JavaScript per
mostrare la risposta del server nel browser) ottenendo applicazioni più veloci (dato
che la quantità di dati interscambiati fra il browser ed il server si riduce). Anche il
tempo di elaborazione da parte del web server si riduce poiché la maggior parte di
dati della richiesta sono già stati elaborati.
Vediamo graficamente come un'applicazione web tradizionale elabora l'interazione
dell'utente e come lo fa invece un'applicazione AJAX.
2
Introduzione
Figura 1 – Elaborazione client tradizionale vs client Ajax
Ora invece vediamo come i componenti delle applicazioni AJAX interagiscono tra
loro comparandolo con quello che accade nelle applicazioni web classiche.
Figura 2 - Interazione client/server tradizionale vs AJAX
3
Introduzione
Dopo aver compreso i vantaggi a livello di velocità di esecuzione il lavoro descritto
in questa tesi consiste nell’aggiungere in un’applicazione web-based fornita ai
clienti da parte di un’azienda di telecomunicazioni alcuni servizi basati sulla
tecnologia AJAX.
Premettiamo che l’ambiente di lavoro è basato su:
• server LINUX;
• DBMS di tipo MySQL;
• Applicazione sviluppata in HTML, PHP, JavaScript, XML;
• Content
Management
System,
che
genera
dinamicamente
l’intera
applicazione, sviluppato in PHP.
Il primo servizio da aggiungere rappresenta un classico esempio di utilizzo di AJAX:
creare liste ordinabili dinamicamente ed implementare l’algoritmo creato all’interno
del CMS.
Molti siti usano le tabelle per visualizzare un insieme di dati memorizzati in una
tabella: per cambiare l'ordine di visualizzazione dei dati, con un'applicazione
tradizionale l'utente dovrebbe cliccare magari su un link, che attiverebbe una
richiesta al server per ricaricare interamente la pagina con il nuovo ordine. Invece
usando la tecnologia AJAX non ricarichiamo l’intera pagina ma solo il contenuto
che ci interessa. Dopo un periodo di studio di varie soluzioni, la scelta è caduta
sull’uso di una libreria, prototype.js, che offre una serie di oggetti per facilitare la
creazione di interfacce web dinamiche con AJAX. Poi di seguito sono state create
altre librerie, che implementino la precedente, in grado di rendere il tutto più
modulare.
Il secondo modulo aggiunto riguarda un effetto grafico particolare offerto da
un’altra libreria rico.js.
L’azienda era interessata a mostrare a tutti i propri clienti alcune comunicazioni
nell’applicazione web, invece che usare mezzi di comunicazione standard, come
lettere o e-mail. Per rendere il servizio graficamente interessante e con
4
Introduzione
un’interfaccia pratica e veloce da usare, è stato usato l’Accordion di rico.js, che
permette di visualizzare i messaggi in vari pannelli, ridimensionabili con un
semplice click.
Inoltre, durante la fase si analisi del sistema sono emerse delle ulteriori modifiche
da poter apportare all’applicazione web, e di conseguenza al CMS che la genera, per
migliorare la leggibilità e l’usabilità del codice.
5
Capitolo 1
Analisi sistema esistente
CAPITOLO 1
Analisi sistema esistente
Il sistema in esame è composto da un Content Management System e da
un’applicazione web generata dal CMS. In questo capitolo analizzeremo entrambi al
fine di comprenderne la struttura e il funzionamento.
1.1 CONTENT MANAGEMENT SYSTEM
Il primo task che deve essere concepito per sviluppare una applicazione web di tipo
gestionale è quello di analizzare il processo di business che si intende modellare e
quindi la base di dati che dovrà supportare tale applicazione gestionale. Tale base di
dati può essere descritta attraverso un file XML che il CMS poi elabora.
Il Content Management System utilizzato lavora come un parser XML di tipo SAX
che solitamente avviamo attraverso la seguente riga di comando:
$ php alienante.php –f file_test.xml –o folder_name –l it
I parser SAX, acronimo di Simple API for XML, sono basati sulle “sequenze di
eventi”. Dato un file XML, essi lo scorrono per intero, un carattere dopo l'altro; il
tipo SAX non produce una rappresentazione di ciò che legge, ma memorizza
soltanto gli eventi che lo sviluppatore decide di allocare ed esegue unicamente le
azioni relative a quell’evento.
Gli eventi di un parsing SAX sono fondamentalmente tre: l'incontro con un tag
d'apertura (open), l'incontro con un tag di chiusura (close) e l'incontro con un
6
Capitolo 1
Analisi sistema esistente
contenuto compreso fra i primi due tag; ad ognuno di essi potrà essere associato o
meno un determinato evento.
Il file che viene dato in input al file alienante.php, file_test.xml, descrive l'astrazione
funzionale del processo di business che si intende modellare rispetto alla struttura
dati DBMS che ne dovrà essere alla base.
Osserviamone un esempio:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xml PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "conf/alienante.dtd">
<alienante>
<object name="last_update_content" group="last_update" help_description="Help per
last_update_content">
<fields>
<field table_reference="last_update_content"
sql_label="head_id"
sql_type="int(11)"
io_label="Head id"
in_html_type="foreign_key"
decode_value="no"
in_list="yes"
in_search="yes"
obligatory="yes"
primary_key="no"
help_description="Descrizione di head_id"
foreign_key_sql="yes"
foreign_key_object="last_update_head"
foreign_key_table="last_update_head"
foreign_key_column="id"
foreign_key_columns_description="data,contesto">head_id
</field>
</fields>
<from>
<table>last_update_content</table>
</from>
<where>
</where>
</object>
</alienante>
Per ogni tabella del database corrisponde un tag <object> con i relativi attributi e un
tag <fields> contenente tutti i campi della tabella in questione. Inoltre per ogni
campo della tabella esiste un tag <field> con una serie di attributi che descrivono sia
le caratteristiche SQL, quali sql_type, sql_label, sia le caratteristiche per la
visualizzazione dopo la generazione dell’ambiente di lavoro, quali in_list, in_search.
7
Capitolo 1
Analisi sistema esistente
Al termine dell’elaborazione del file_test.xml, viene generata l’applicazione web al
path folder_name.
A livello di file system il CMS è strutturato nel seguente modo:
• Insieme di file predefiniti
Una serie di file che saranno direttamente copiati nelle directory di output.
Tali file sono fogli di stile, librerie JS, immagini, dati relativi alla
connessione al database e tutti quei file statici, non dipendenti dal lavoro del
parser.
• Costanti di configurazione
Una
serie
di
costanti
utilizzate
per
la
formattazione
standard
dell’applicazione web, come nomi di directory di output, nomi proibiti
all’interno
dell’applicazione,
che
verranno
richiamate
durante
la
parserizzazione per evitare ripetizione di codice. L’utilizzo di tale file è
vantaggioso dal punto di vista della riusabilità del codice: infatti per
modificare una stringa utilizzata tante volte dal parser basta modificare solo
l’attributo nel file contenente le costanti.
• Librerie CMS
Il CMS è dotato di una serie di librerie la cui funzione è quella di generare il
codice sorgente delle primitive dell’applicazione web, quali inserimento di
dati in una tabella MYSQL, ricerca dati, elencazione dati, modifica,
eliminazione.
Ogni libreria, ad eccezione di alcune che raccolgono le funzioni più utilizzate,
è rappresentata da una classe che definisce diversi metodi chiamati da
alienante.php.
Al costruttore di tale classe sono passate, da alienante.php, variabili
contenenti tutte le informazioni ricavate dalla parserizzazione del file XML.
Gli altri metodi analizzano tali parametri e in base a questi costruiscono
8
Capitolo 1
Analisi sistema esistente
dinamicamente il codice sorgente, a livello di query SQL e a livello web, sia
client che server. Durante la costruzione del codice vengono utilizzate le
costanti definite nel file descritto precedentemente. Il tutto viene
memorizzato in variabili d’istanza, utilizzati come buffer di memoria, che
verranno restituiti al chiamante, in questo caso alienante.php.
• Alienante.php
Questo è il file portante del Content Management System che regola il processo
di parserizzazione.
Dopo aver constatato la correttezza del comando di input alienante.php parserizza il
file XML memorizzando per ogni tag incontrato, il nome, lo stato(open, close,
complete), il livello di gerarchia, tutti gli attributi specificati per tale tag e il valore
in esso contenuto se presente.
Questi dati saranno passati come parametri quando alienante.php istanzierà tutti gli
oggetti corrispondenti alle classi che si occuperanno di generare il codice sorgente
per tutte le pagine dell’applicativo. Una volta creati, verranno utilizzati per accedere
ai relativi metodi definiti nelle rispettive classi.
Il primo metodo ad essere invocato è add_out: questo si occupa di memorizzare i
buffer secondo un contenuto che può essere dinamico per quanto riguarda le
operazioni dipendenti dalle informazioni ricavate dal file XML, o statico per alcune
parti che invece sono indipendenti.
Per ogni tag XML esistono tre stati:
1. OPEN: corrisponde all’apertura di un tag;
2. COMPLETE: se il tag possiede degli attributi;
3. CLOSE: corrisponde al tag di chiusura.
Per ognuno di questi step esiste un insieme di istruzioni da svolgere, ma il centro
delle operazioni viene svolto nel secondo. Nel primo solitamente il buffer viene
riempito con codice statico, indipendente dal file XML; nel terzo invece viene
9
Capitolo 1
utilizzato
Analisi sistema esistente
il
risultato
emerso
dall’analisi
degli
attributi
del
tag
XML
memorizzandolo in un buffer, a volte in seguito ad un’altra elaborazione.
Il secondo step è quello che permette di scrivere nei buffer il codice dipendente
dalle informazioni ricavate dall’analisi degli attributi del tag XML in esame. Tutti
gli attributi vengono memorizzati in variabili locali per facilitarne la successiva
analisi.
Dopo aver invocato il metodo add_out, che ha costruito il contenuto dinamico,
alienante.php chiama i metodi get_out, get_out_page, get_out_action_page, che si
occupano di completare il contenuto delle pagine web, rispettivamente
name.cnf.php e name.lib.php, function.php e function_action.php. Tali metodi
aggiungono la parte di codice sorgente che non si basa sull’analisi dei dati del file
XML, al massimo riprende alcuni parametri derivati dalla precedente analisi.
Durante la costruzione del contenuto dei file vengono utilizzate delle funzioni
definite in librerie esterne costruite per evitare la ripetizione di codice. Queste sono:
• alienante_0_base.php: contenente le funzioni per la scrittura di codice PHP;
• alienante_1_base_HTML.php: contenente le funzioni per la scrittura di
codice HTML;
• alienante_2_base_js.php: contenente le funzioni per la scrittura di codice JS.
Nell’analisi di tali librerie sono state messe in evidenza alcune funzioni che
svolgevano azioni simili ma ripetendo del codice inutilmente. Inoltre in determinate
situazioni, come nel caso di un campo input <form> di tipo readonly, era presente
una gestione errata che doveva essere corretta. Nel capitolo successivo
analizzeremo le soluzioni adottate per tali problematiche.
Una volta catturato l’output delle pagine ad alienante.php non rimane che scrivere il
contenuto dei buffer nel file vero e proprio, generando in questo modo
10
Capitolo 1
Analisi sistema esistente
l’applicazione web, pronta, configurando gli opportuni parametri, per essere
utilizzata dall’utente.
1.2 APPLICAZIONE GENERATA
Il risultato finale del lavoro del CMS analizzato è un’applicazione gestionale webbased in grado di interfacciarsi graficamente con un database di tipo MySQL e
implementandone tutte le funzioni primitive per gestire le tabelle create.
Queste funzioni implementate sono:
• inserimento di un nuovo record in una tabella;
• ricerca di un record;
• modifica di un record;
• cancellazione di un record;
• ricerca filtrata di un record;
• elenco di tutti i record presenti in una tabella.
Partendo da una database e da un file XML siamo riusciti a creare un’applicazione
web con le funzioni sopra citate già implementate con un semplice comando da
prompt, mentre senza l’aiuto del CMS i tempi di sviluppo dell’applicazione
gestionale sarebbero stati sicuramente molto più lunghi.
All’interno del file system il CMS fa corrispondere una cartella con il folder_name
specificato, all’interno della quale vengono create sottocartelle alcune standard, che
vengono direttamente copiate dalla cartella alienanate.empty, altre corrispondenti
alle tabelle del database:
• conf: contiene una serie di file di configurazione, come i dati d’accesso al
database;
• css: contiene i fogli di stile dell’applicazione web;
• error: contiene le pagine chiamate in caso di errore;
11
Capitolo 1
Analisi sistema esistente
• img: contiene le immagini presenti nell’interfaccia;
• js: contiene tutte le librerie JS utilizzate;
• lib: contiene tutti i file, che estendono le classi contenute in conf, con le
definizioni di metodi e proprietà, quali ad esempio le query SQL da eseguire
sul database;
• login: contiene i file per l’autenticazione;
• menu: contiene i file che disegnano l’interfaccia;
• upload: contiene i file caricati sul server mediante un’operazione di upload.
A queste cartelle si aggiungono le cartelle corrispondenti alle tabelle presenti nel
database, al cui interno ci sono i file necessari per il funzionamento delle azioni
primitive sopra citate.
L’applicazione possiede un sistema di autenticazione basato sui cookie che regola
l’accesso al gestionale. Una volta effettuato il login possiamo interagire con il
nostro database attraverso l’interfaccia grafica generata di default dal CMS.
Figura 3 - Menu iniziale per la navigazione nel gestionale
Il menu in alto permette di esplorare le tabelle del database su cui si opera,
consentendo, cliccando sulla tabella interessata, di visualizzare l’elenco dei record
presenti nella tabella selezionata.
12
Capitolo 1
Analisi sistema esistente
Figura 4 -Elenco tabella
Attraverso i link sovrastanti o sottostanti la tabella si può inserire un nuovo record o
cercare un record già presente, mentre tramite le icone presenti ad ogni inizio riga è
possibile modificare, cancellare e vedere in dettaglio il record corrispondente.
Dettaglio
Per visualizzare un record in dettaglio, bisogna interagire con un click con
l’opportuna icona.
Figura 5 - Record tabella visto in dettaglio
13
Capitolo 1
Analisi sistema esistente
Copia
Dalla pagina di dettaglio record è possibile memorizzare nella tabella corrente una
copia identica o modificata del record visualizzato.
Inserimento
Figura 6 - Form inserimento nuovo record
In caso di inserimento di un nuovo record l’interfaccia grafica offre una semplice
form con i campi da inserire. Se nella tabella esiste un vincolo di foreign key (vedi
Figura 6) allora c’è la possibilità di accedere, tramite un’opportuna icona, alla
tabella a cui tale vincolo si riferisce e selezionare il record di riferimento.
Modifica
Se l’utente sceglie di modificare un record, viene visualizzata una form contenente i
dati relativi al record selezionato, che offre la possibilità di modificare tutti i dati ad
eccezione di quelli che nel file XML hanno l’attributo obligatory impostato a yes.
Eliminazione
In caso l’utente intende eliminare un record, cliccando sulla rispettiva icona, viene
visualizzato il record in dettaglio. Una volta confermata l’eliminazione, viene
visualizzato il record appena eliminato in dettaglio con la possibilità di ripristinarlo.
Ricerca
Se l’utente clicca sul link [Cerca], viene visualizzata una maschera di ricerca, con
la possibilità di affinare la ricerca sulla tabella.
14
Capitolo 1
Analisi sistema esistente
Figura 7 - Form per effettuare ricerca
Una volta confermato il filtro di ricerca appare la tabella risultante. Di default non
vengono visualizzati tutti i campi della tabella, ma solo quelli in cui l’attributo
XML in_list è stato settato a yes. Per visualizzare interamente la tabella basta
cliccare sul link [< | >].
Figura 8 - Risultato ricerca
15
Capitolo 1
Analisi sistema esistente
1.3 LIBRERIE UTILIZZATE
Nel mondo web esistono strumenti che rendono meno difficile la vita del
programmatore. Fra queste ci sono le librerie JavaScript prototype.js e rico.js, già
incluse nell’applicazione esaminata, che estendono il linguaggio JS.
Prototype.js è una libreria che fornisce utili funzionalità per lo sviluppo in JS e
facilita la creazione di applicazioni web dinamiche. Essa estende le funzionalità già
presenti in JS, implementando metodi veloci per accedere ad elementi del DOM,
estendendo lo stesso oggetto DOM ed evitando così di scrivere codice ripetitivo. Un
esempio utilizzato anche nello sviluppo dell’applicazione in esame è la funzione $( ):
questa rappresenta un collegamento alla funzione document.getElementById() del
DOM, che ritorna l'elemento che ha l'ID passato come argomento.
Diversamente dalla funzione DOM si può passare più di un ID. In questo caso la
funzione $( ) restituisce un oggetto array con tutti gli elementi richiesti.
Oltre a tante funzioni di utilità, comode ma non essenziali, l'interesse in prototype.js
è guidato principalmente dalle sue capacità di utilizzo di AJAX. L'oggetto Ajax è un
oggetto predefinito, creato dalla libreria per raggruppare e semplificare l'intricato
codice che sviluppa le funzionalità AJAX. Questo oggetto contiene un numero di
classi che stabiliscono l'incapsulamente logico di AJAX: una di queste è
Ajax.Request,
che nell’applicazione gestionale viene utilizzata nella classe
AjaxPrototype.js, che implementa prototype.js. Per la documentazione completa di
prototype.js si rimanda a [1].
In AjaxPrototype.js, libreria già presente nel sistema in esame, è definito il metodo
request, poi richiamato dalla libreria creata per l’ordinamento, per la creazione
dell’oggetto XMLHttpRequest attraverso l’utilizzo della classe Ajax.Request.
16
Capitolo 1
Analisi sistema esistente
this.xmlhttp = new Ajax.Request(
preString + "://" + this.webServer + "/" + this.page,
{
method: this.connectMethod
, parameters: this._varSend
, onComplete: this._responseFuction
, onLoading: this._loadingFuction
, onFailure: function(){ alert('configuration error'); }
});
Al costruttore di Ajax.Request sono passati due parametri: il primo rappresenta
l’indirizzo URL dove si trova la pagina che elaborerà la richiesta del client, mentre
il secondo rappresenta un oggetto anonimo nella notazione letterale. Significa che si
sta passando un oggetto che ha una proprietà method contenente il metodo di
connessione (GET/POST); una proprietà parameters che conterrà la stringa di query
della richiesta del client; una serie di proprietà onComplete, onFailure, onLoading,
corrispondenti alle azioni da svolgere a seconda dello stato in cui si trova la
chiamata http eseguita dall’utente, ovvero Loading, Loaded, Interactive, Complete.
Oltre al metodo request è definito anche il metodo AddVarSend, utilizzato dalla
libreria AjaxLiveOrderSearch.js , che verrà analizzata nel capitolo successivo, che
permette la costruzione della query string concatenando i parametri passati in input.
Rico.js è una libreria che estende prototype.js, con lo scopo di rendere ancora più
facile l’utilizzo delle tecnologia AJAX. Essa fornisce un oggetto JS denominato
AjaxEngine, che semplifica l’aggiornamento dei contenuti all’interno di una pagina
web tramite l’uso di AJAX. Il motore ha le seguenti caratteristiche:
• Una definizione standard XML per la risposta di base di AJAX;
• Un modo per precisare che la risposta è mirata ad uno specifico elemento
HTML;
• Aggiornamento automatico dell’innerHTML dell’elemento HTML desiderato
con il contenuto della risposta;
17
Capitolo 1
Analisi sistema esistente
• Un modo per puntualizzare che una risposta è mirata da parte di un unico
gestore di risposta in JS di AJAX;
• Meccanismo standard per la ricezione di messaggi di risposta da parte di un
gestore di riposte AJAX;
• Aggiornamenti multipli di elementi e/o oggetti JS;
• API per registrare l’Ajax Request Handler e l’Ajax Response Handler.
In breve Rico offre uno strumento utile per semplificare l’aggiornamento del
contenuto della pagina con AJAX. Fornisce un metodo diretto per cambiare il
contenuto HTML di ogni elemento HTML. Offre anche un modo aperto di riposta a
set più complessi di dati attraverso un oggetto JS.
Inoltre offre sorprendenti e rutilanti effetti speciali per il web, come il drag&drop,
elementi animati e fading.
Per ulteriori informazioni e esempi sulla libreria si rimanda a [2].
1.4 LIMITI E FUNZIONALITÀ RICHIESTE
L’obiettivo principale era quello di inserire un algoritmo di ordinamento dinamico
nell’interfaccia già esistente (nello specifico nella tabella visualizzata dopo una
ricerca) usando la tecnologia AJAX, in modo tale da rendere l’applicazione più
facile e veloce da usare. Poi una volta applicato tale cambiamento all’applicazione
generata, era necessario implementare il tutto nel CMS aziendale.
Inoltre per aggiungere un servizio di comunicazione con il cliente, l’azienda ha
deciso di mettere a disposizione dell’utente un modo per poter leggere in real-time
tutte le comunicazioni di servizio, quali modifiche software o hardware,
aggiornamenti, notifiche e così via. La ricerca di una soluzione è stata indirizzata
verso soluzioni web dinamiche, senza la necessità di implementare il risultato
ottenuto nel CMS.
18
Capitolo 1
Analisi sistema esistente
Per quanto riguarda il CMS, oltre ad un aggiornamento dovuto all’implementazione
dell’ordinamento dinamico richiesto, esisteva la possibilità di apportare modifiche
al codice, in modo da compattarlo ed evitare la ripetizione di porzioni con lo stesso
codice.
Dall’analisi del codice relativo alle azioni di copia ed inserimento, è emerso una
peculiarità: infatti entrambe le pagine, insert.php e copy.php, utilizzavano al loro
interno una form, la prima vuota mentre la seconda con dei valori inizializzati.
Entrambe però realizzavano lo stesso compito, ovvero inserire un nuovo record in
una tabella.
Il confronto fra le funzioni di inserimento e copia si potrebbe estendere anche alla
modifica: anche in update.php c’è una form, ma a differenza delle prime due, in
questa c’è la possibilità di non poter modificare alcuni campi.
Analizzando il codice del file view.php, si osserva che, dopo le opportune
operazioni di autenticazione e di query sulla tabella desiderata, viene inclusa una
pagina, table_view.php, usata per visualizzare il risultato della richiesta. Tale
soluzione è stata preferita per un corretto riuso del codice e per poter utilizzare tale
pagina in più contesti.
Nell’eliminazione di un record viene visualizzato nuovamente il record in maniera
analoga a quanto descritto per il dettaglio. Ma a livello di codice sorgente si osserva
come le due soluzioni siano diverse: infatti nella delete.php viene stampata la
tabella con le informazioni sul record invece che includere una pagina apposita.
Dopo queste ultime osservazioni si è deciso di modificare anche le parti sopra
descritte in modo tale da correggerle e implementare i cambiamenti apportati
direttamente nel CMS.
19
Capitolo 2
Realizzazione specifiche
CAPITOLO 2
Realizzazione specifiche
In questo capitolo tratteremo gli aspetti realizzativi del progetto in esame, a partire
dal live order fino all’effetto di Accordion, senza tralasciare le modifiche eseguite
in itinere.
2.1 REALIZZAZIONE LIVE ORDER
L’applicazione generata, come già visto nel capitolo precedente, fornisce una
funzione di ricerca con immissione dei parametri in una form e il risultato viene
visualizzato in una tabella.
Se l’utente volesse visualizzare tale tabella ordinata secondo un determinato campo,
utilizzando il default dell’applicazione attuale sarebbe impossibile. Un modo per
ottenere un simile risultato, ma in maniera statica, sarebbe quello di modificare la
query SQL aggiungendo una clausola ORDER BY name_field. Così avremmo
realizzato una lista ordinata ma non dinamicamente.
In maniera dinamica si potrebbe, tramite un click sul nome del campo secondo il
quale l’utente intende ordinare, effettuare una nuova interrogazione al server
passandogli un parametro aggiuntivo relativo all’ordinamento. In un’applicazione
tradizionale il server, dopo la nuova interrogazione, invierebbe come risposta
un’intera pagina. Oltre all’inconveniente di dover ricaricare l’intera pagina,
nonostante la nuova ricerca ne modifichi solo una parte, esiste il tempo di latenza
fra la richiesta al server e la sua risposta, durante il quale il client rimane in attesa.
Sfruttando invece la tecnologia AJAX l’interazione client / server, oltre a risultare
asincrona, permette non ricaricare l’intera pagina: il client effettua la richiesta ma
20
Capitolo 2
Realizzazione specifiche
come risposta il server invia soltanto la porzione modificata della pagina web
mostrata al browser client senza il bisogno di ricaricare completamente la pagina.
Questa dinamica in generale si basa sui seguenti eventi:
1. L’utente interagisce con la pagina web magari cliccando su un link;
2. Una funzione Javascript raccoglie questa richiesta creando un oggetto
XMLHttpRequest, fornito direttamente dal browser;
3. Attraverso un metodo “send” di questo oggetto il JavaScript invia una
richiesta al server;
4. Il server web riceve ed elabora tale richiesta;
5. La risposta del server web viene intercettata dal Javascript che ne estrapola i
dati presenti e modifica solo determinate porzioni di HTML della pagina web.
Osserviamo di seguito come nel caso in esame sono stati implementati tali passi.
La pagina search_action.php mostra il risultato della ricerca includendo due pagine
php: table_list_header.php e table_list_content.php. Nella versione precedente
invece tutta la tabella veniva stampata in un’unica pagina, ma per rendere il codice
più riusabile è stata effettuata tale separazione. Ora la search_action.php si occupa
di recuperare i valori relativi all’interrogazione del server e inizializzare le variabili
con tali valori, mentre le altre due pagine servono a visualizzare la tabella. La
pagina table_list_headert.php visualizza l’intestazione della tabella, ovvero la prima
riga contenente tutti i nomi dei campi, mentre la table_list_content.php visualizza il
contenuto vero e proprio della tabella.
Nella prima pagina relativa all’intestazione è stata realizzata l’implementazione del
primo step dell’interazione client/server AJAX.
21
Capitolo 2
Realizzazione specifiche
<th
<? if ($ordinable["data"]) { ?>
title="Ordina per Data
<?if (isset($SORT_COL_data)) { echo $SORT_COL_data;}
else {echo $SORT_DEFAULT;} ?>"
onClick="javascript:live_order( this.id,
'<? if (isset($SORT_COL_data))
{echo $SORT_COL_data;}
else
{ echo $SORT_DEFAULT;} ?>',
'<? echo $in_service_var["restrict"]; ?>')"
<? }else{} ?>
id="data" align="left" valign="middle"><table><tr><td>Data</td><td>
<? if (isset($SORT_COL_data)) { ?>
<img src="../img/<? echo $image; ?>" height="5" width="9" border="0"/>
<? }else{echo "&nbsp;";} ?></td></tr></table>
</th>
La porzione di codice precedente descrive un campo intestazione della tabella.
Innanzitutto precisiamo che esiste la possibilità di stabilire se un campo può essere
ordinato oppure no, semplicemente settando la variabile di tipo array
$TABLE_ORDER nel file .cnf relativo alla tabella in esame. L’utente attiva
l’interazione con il server attraverso un click sul campo di testo secondo il quale
desidera ordinare: tale click attiva l’esecuzione della funzione JS live_order, definita
in search_action.php, tramite il gestore di eventi onClick. Inoltre una serie di
controlli di tipo if (isset(var_name)) preclude la possibilità di avere un errore di
variabile non esistente in quanto nel caso non è inizializzata $SORT_COL_data
stampa una variabile di default $SORT_DEFAULT inizializzata in search_action.php.
In quest’ultima pagina è presente la funzione JS che realizza il secondo step
dell’interazione client/server, la quale sfrutta tre librerie che aiutano nel creare gli
oggetti per l’elaborazione della richiesta da inviare al server.
22
Capitolo 2
Realizzazione specifiche
<script src="../js/prototype.js"></script>
<script src="../js/AjaxLiveOrderSearch.js"></script>
<script src="../js/AjaxPrototype.js"></script>
<script language="javascript">
function live_order(name_col,sort_col,restrict){
var list=new Ajax_list(
"<? echo AJAX_DEFAULT_SERVER.$my->getProtected("AJAX_BASE_DOMAIN");?>",
"post","ajax_search.php", "<? echo $my_session->out_login; ?>",
"<? echo $my_session->out_cookie_session; ?>","<? echo $fkID; ?>",
"<? echo $actionID; ?>","<? echo $service_varID; ?>",name_col,sort_col,restrict);
}
</script>
La funzione live_order accetta tre parametri:
• name_col : nome SQL del campo secondo cui si vuole ordinare;
• sort_col : il tipo di ordinamento (ASC/DESC);
• restrict : parametro che ci permette di visualizzare o meno tutti i campi della
tabella, e in caso di campi con contenuto esteso, permette di visualizzare tutti
i caratteri o solo un numero predefinito.
Come prima istruzione la funzione istanzia un oggetto di tipo Ajax_list, definito in
AjaxLiveOrderSearch.js, passandogli, oltre ai parametri ricevuti in input, altri
parametri quali:
I. percorso per accedere alla pagina che produce l’elaborazione, composta da una
concatenazione fra il contenuto di una variabile d’ambiente inizializzata nel
file di configurazione global.inc.php secondo il proprio ambiente di lavoro e il
percorso relativo per indicare la posizione del file cercato, settato nel file
table_name.cnf.php nella cartella cnf;
II. metodo in cui i dati saranno passati al server;
III. pagina che gestisce la comunicazione con il server;
IV. dati di login dell’utente corrente per la corretta autenticazione;
V. cookie dell’utente per la corretta autenticazione;
VI. oggetto serializzato contenente dati riguardanti gli attributi di foreign key;
23
Capitolo 2
Realizzazione specifiche
VII. oggetto serializzato contenente i dati per l’interrogazione del database;
VIII. oggetto serializzato contenente i dati per la visualizzazione dei menu di
navigazione.
Per gestire la chiamata AJAX è stata creata la libreria AjaxLiveOrderSearch.js, che
implementa AjaxPrototype.js. Proprio la cooperazione tra le librerie JS permette di
realizzare il terzo step dell’interazione client/server in esame.
La creazione dell’oggetto list implica la chiamata al metodo Ajax_list che si occupa
di creare la query string da passare alla pagina ajax_search.php e di istanziare un
oggetto di tipo AjaxPrototype nel modo seguente:
var ajax = new AjaxPrototype(BASE_DOMAIN, c_method, PAGE_D, showResponse,
onLoadDefault).
Con questa chiamata invochiamo la funzione che si occupa di istanziare l’oggetto
Ajax.Request della libreria prototype.js, passando come parametri oltre al nostro
indirizzo base, il metodo di passaggio dei dati(POST/GET), e due funzioni, di cui la
seconda inoperosa, definite entrambe in AjaxLiveOrderSearch.js. Il metodo
showResponse gestisce la risposta ricevuta dal server, che in caso di informazione
trovata nel database restituisce i dati trovati direttamente in HTML, mentre in caso
di errore dovuto ad un’autenticazione errata o di record non presente nel database,
risponde con un documento XML.
function showResponse(ajaxRequest){
var response=ajaxRequest.responseText;
var responseXml=ajaxRequest.responseXML;
var responseType=ajaxRequest.getResponseHeader("Content-Type");
if (responseType=="text/xml"){
window.location='../error/error.php?code='+
responseXml.getElementsByTagName('cError')[0].childNodes[0].nodeValue+
'&error=' +
responseXml.getElementsByTagName('dError')[0].childNodes[0].nodeValue;
}
else
$('result').innerHTML=response;
}
24
Capitolo 2
Realizzazione specifiche
La risposta del server viene recuperata tramite due proprietà dell’oggetto
Ajax.Request, ovvero responseText e responseXML. Entrambi contengono i risultati
elaborati dal server ad operazione ultimata. La differenza principale tra i due è che
mentre responseText è un dato di tipo stringa che riceve sempre informazioni dal
server, responseXML potrebbe essere un oggetto null qualora i dati restituiti non
dovessero essere un documento XML.
Per vedere il tipo di risposta del server, utilizzando nuovamente un metodo proprio
dell’oggetto, getResponseHeader(), leggiamo il Content-Type del documento di
risposta. Se è di tipo XML viene chiamata la pagina di gestione errore passando i
parametri recuperati dal documento XML, altrimenti si visualizza la risposta
inserendola all’interno del div result. In quest’operazione si nota l’utilizzo, oltre a
quello della funzione $() di prototype.js, della proprietà innerHTML che permette di
scrivere all’interno dell’elemento HTML selezionato.
Proprio $('result').innerHTML=response permette di implementare il passo 5 della
nostra interazione client/server in AJAX.
La pagina web che si occupa di ricevere ed elaborare le richieste del cliente è
ajax_search.php, che dunque realizza il passo 4 dell’interazione in esame.
Inizialmente sono presenti una serie di inclusioni di librerie PHP utili per la
connessione al server, l’autenticazione e per varie funzioni d’utilità. In seguito va a
leggere il contenuto della query string inviata dal client e a deserializzare gli oggetti
serializzati in precedenza. La serializzazione è stata utilizzata sia per motivi di
sicurezza, criptare i dati, sia per comodità di spostare strutture dati, come array,
evitando di avere una query string di lunghezza infinita.
Ogni volta che viene effettuata una chiamata ad un metodo che interagisce con una
tabella del database, c’è il controllo per eventuali errori. A differenza della versione
precedente
nell’applicazione
attuale
è
stata
implementata,
nella
libreria
error.lib.php una funzione di gestione dell’errore ajax_error, appositamente per
l’interazione AJAX. Tale funzione accetta in input due parametri, il codice di errore
e la sua descrizione, e costruisce il documento XML per la stampa dell’errore nel
seguente modo: i parametri ricevuti sono inseriti in tag appositi, rispettivamente
25
Capitolo 2
Realizzazione specifiche
cError e dError, entrambi contenuti in un tag esterno <ajax-response>. Il documento
creato viene poi intercettato dalla funzione JS showResponse grazie all’istruzione
header('Content-Type: text/xml'), che permette di settare il responseXML.
Quando un utente opera una ricerca su una tabella, prima di effettuare qualsiasi
operazione, bisogna accertarsi che sia autenticato. Il meccanismo dell’applicazione
prevede l’aggiornamento del cookie dell’utente ad ogni operazione, sempre se il
cookie precedente corrisponde all’utente attuale. Durante la realizzazione di
ajax_search.php, sfruttando tale meccanismo di autenticazione, in caso di richieste
di ordinamento quasi simultanee, generava un errore. Questo perché mentre il
sistema aggiornava il cookie dell’utente per la prima chiamata, cancellava dal
database il cookie vecchio; per la seconda chiamata il sistema non trovava più la
corrispondenza utente – cookie perché nel frattempo l’ultimo era cambiato e quindi
generava un errore che aveva come conseguenza il doversi autenticare di nuovo.
Per
tale
motivo
in
ajax_search.php
è
stata
utilizzata
una
funzione,
check_session_by_cookie, che controlla solamente se il cookie corrisponde all’utente
che esegue la richiesta di ordinamento senza modificarlo.
Per visualizzare il contenuto vengono incluse, come nella search_action.php, le
pagine table_list_header.php e table_list_content.php, però a differenza della search
cambia la funzione associata all’attributo href dell’oggetto HTML di tipo anchor
[< | >].
Infatti mentre nella search_action.php era
href=”javascript:document.restrict.submit();”,
nella pagina ajax_search.php invece viene richiamata la funzione live_order, con gli
stessi parametri relativi alla ricerca ordinata e con un parametro restrict che varia
(0/1) a seconda del valore assunto attualmente. In questo modo sfruttiamo la stessa
funzione AJAX e offriamo all’utente la possibilità di testare direttamente la
differenza tra un’interazione client/server tradizionale e interazione client/server
con l’utilizzo di AJAX.
Analizzando la pagina web visualizzata dopo il filtro di ricerca, si osserva come
dopo un numero predefinito di record, viene ristampata l’intestazione della tabella,
26
Capitolo 2
Realizzazione specifiche
permettendo all’utente in caso di numerosi record di non dover tornare all’inizio
pagina per effettuare l’ordinamento.
Una volta realizzata la lista ordinabile dinamicamente con l’uso della tecnologia
AJAX e testato il suo funzionamento, non rimane che implementarla nel CMS
dell’azienda.
2.2 IMPLEMENTAZIONE LIVE ORDER NEL CMS
Dopo la realizzazione del live order per un’applicazione specifica, c’era la
necessità che ogni applicazione generata dal CMS ereditasse tale funzionalità.
Il primo passo è stato analizzare il codice sorgente del CMS, per comprenderne il
funzionamento e capire dove era necessario creare una libreria apposita e dove era
necessario invece modificarne una già esistente.
La libreria che precedentemente si occupava di generare il sorgente delle pagine
relative alla ricerca era alienante_80_method_search.php. Essa interessava:
• la costruzione del method_search nella libreria table_name.lib.php, ovvero la
query SQL di ricerca, eseguito dal metodo get_out;
• la scrittura del contenuto delle pagina search.php e search_action.php,
rispettivamente eseguito dai metodi get_out_page e get_out_action _page.
Inoltre un altro metodo add_out, in base al contenuto del file XML, crea i contenuti,
utilizzati in seguito dai metodi citati in precedenza, da scrivere in seguito nei file
corrispondenti, memorizzandoli in variabili d’istanza utilizzati come buffer di
memoria.
La prima modifica interessava la query SQL dove era necessario aggiungere una
clausola ORDER BY, quindi modificheremo il metodo add_out:
$this->tmp_search_header .= "\t\t\$order_by = \"\"; # Parametro per ordinamento
dinamico\n\n";
27
Capitolo 2
Realizzazione specifiche
Nel buffer relativo alla query SQL già esistente è stata concatenata una stringa
contenente il parametro per memorizzare la clausola ORDER BY, che poi sarà
concatenata direttamente alla query SQL. C’era il bisogno di inserire un campo per
l’ordinamento di default iniziale: così la scelta è caduta sul campo SQL di tipo autoincrement o, nel caso non esistesse, nella primary_key della tabella.
# caso chiavi primarie senza auto-increment
if(strcmp($primary_key,"yes")==0 && !$this->flag_auto_increment){
$this->tmp_http_param_pk .= "$value=<? echo \$my->".$value."[\$i]; ?>&";
#Parametro per ordinamento di default secondo la chiave primaria
$this->tmp_search_header .="\t\t\$order_by = \"ORDER BY $table_reference.$sql_label\";
$this->order_default=$sql_label;
}
#$table_reference = nome tabella;
$sql_label = nome campo della tabella
Durante l’analisi di un tag field del documento XML, se l’attributo primary_key è
impostato al valore yes e contemporaneamente non c’è un campo auto_increment,
allora tale tag sarà il campo designato per l’ordinamento iniziale.
Dopo aver pensato al default, è stata realizzata la stampa di codice relativo al
controllo che nei parametri passati al method_search di libreria vi fosse un valore di
ordine, ovvero il campo secondo il quale ordinare, e un valore di sort_col, ovvero la
direzione dell’ordinamento.
Successivamente alla creazione della parte relativa alla generazione della query
SQL, è stata aggiunta la parte corrispondente alla codifica degli oggetti visti
precedentemente nell’applicazione generata, semplicemente concatenando al buffer,
in un punto ben definito, il codice desiderato.
……
$this->out_page_view .="#codifico le variabili di foreign key per passarle alla funzione ajax\n
\$fkID=\$my_utility->encodeUrl(serialize(\$fk));\n\n";
…….
In maniera simile, nel metodo get_out_action_page, è stata aggiunta la porzione di
codice relativa allo script della funzione JS live_order, con la creazione di una
funzione di libreria, inserita in alienante_2_base_js.php, che scrivesse sia la
28
Capitolo 2
Realizzazione specifiche
funzione sia le relative inclusioni delle librerie JS utilizzate. Creare una funzione di
libreria è una soluzione pensata per gli sviluppi futuri dell’applicazione, in caso si
volesse inserire la funzione live_order in altre pagine.
Una soluzione analoga è stata adottata per lo script per l’inclusione delle due pagine
per la visualizzazione della tabella risultato della ricerca, inserita però nella libreria
alienante_1_base_HTML.php.
In questa parte l’unica operazione dinamica è la scelta del parametro di ordinamento
iniziale, per il resto sono tutte concatenazioni di tipo statiche.
Infine sono state eliminate tutte le porzioni che generavano codice corrispondente
alla stampa per la visualizzazione della tabella risultante dalla ricerca, perché sono
state create due librerie apposite per svolgere tale compito:
•
alienante_46_method_table_list_header.php;
•
alienante_46_method_table_list_content.php.
La prima è una classe per la generazione del codice relativo all'intestazione della
tabella della search. Oltre al costruttore, sono presenti altri due metodi: add_out e
get_out_page.
Il metodo add_out prepara i buffer contenenti il codice da scrivere successivamente,
attraverso l’altro metodo, nel file di intestazione tabella. Tale metodo svolge la sua
azione principale se il tag XML è COMPLETE, ovvero possiede attributi. Durante la
lettura degli attributi del tag <field> in esame, alcuni attributi quali sql_label e io_label,
vengono memorizzati oltre che in variabili locali, in variabili d’istanza, per
consentirne il loro uso anche dopo l’uscita dal ciclo di lettura. Inoltre, per lo stesso
motivo, vengono settate altre due variabili d’istanza: la prima dipendente della
presenza di una foreign_key, in cui sono memorizzati i campi da prendere dalla
tabella a cui tale campo si riferisce; l’altra dipendente dall’attributo in_list, che
stabilisce se un campo deve essere inizialmente visualizzato nell’elencazione di una
tabella.
Una volta terminata l’individuazione degli attributi, dopo la chiusura del tag <fields>,
viene completato il codice sorgente della pagina table_list_header.php in base
all’analisi precedente, attraverso l’uso di due funzioni:
29
Capitolo 2
Realizzazione specifiche
• print_switch_table_list: definita in alienante_0_base.php;
• print_header: definita in alienante_1_base_HTML.php.
for($i=0;$i<count($this->valori_sql);$i++){
$this->tmp_page_view.=print_switch_table_list($this->valori_sql[$i]);
$this->tmp_page_view.=
print_header($this->valori_sql,$this->value_array,$this->restrict,$this->foreign);
if ($this->foreign[$i]){
$foreign_key = explode(",",$this->foreign[$i]);
foreach($foreign_key as $val){
$this->tmp_page_view.=print_switch_table_list($val);
$this->tmp_page_view.=
print_header($this->valori_sql,$this->value_array,$this->restrict,$this->foreign);
}
}
}
La prima scrive una serie di switch, uno per ogni campo della tabella, che permette,
durante l’uso dell’ordinamento, di capire automaticamente il valore in base a cui si
sta ordinando: di conseguenza sono settati il valore di sort_col e l’immagine da
visualizzare a seconda del valore precedente.
Se il campo in esame si riferisce ad un altro campo di una tabella, allora lo switch
viene stampato anche per quei campi della tabella di riferimento che vogliamo
visualizzare e in base ai quali è possibile ordinare (campi specificati dall’attributo
foreign_key_columns_description del file XML) .
La seconda funzione usata invece si occupa di scrivere il codice relativo
all’intestazione della tabella, ovvero i nomi dei campi della tabella inseriti nei tag
HTML <th>. I parametri che passiamo alla funzione sono quattro array della stessa
lunghezza, pari al numero dei campi field trovati nel file XML:
I. nomi SQL di tali campi;
II. etichetta assegnata a tali nome nell’interfaccia dell’applicazione;
30
Capitolo 2
III. i
Realizzazione specifiche
campi
da
visualizzare
dopo
la
ricerca
nella
tabella
(1=>coperto,0=>visualizzato);
IV. array che stabilisce quale campo ha una o più foreign key (formato da 0 o dai
nomi campi di riferimento).
La funzione esegue un ciclo per un numero di volte pari alla lunghezza di uno dei
parametri passati in input. Ad ogni iterazione stampa una riga d’intestazione della
tabella: in caso il campo in esame abbia una foreign key, stampa una riga per ogni
campo aggiuntivo presente.
Nella porzione di codice sopra riportata si nota che in caso di presenza di una
foreign_key, viene effettuata una chiamata simile alla precedente. Questo perché
doveva essere presente uno switch anche per i campi aggiuntivi, e poter così
permettere l’ordinamento dinamico anche per i campi esterni alla tabella.
Completato il buffer non rimane che incrementare il contatore per far andare avanti
la
parserizzazione,
per
poi
procedere
al
completamento
della
pagina
table_list_header.php tramite il metodo get_out_page.
In quest’ultimo metodo viene completata le generazione del codice della pagina
citata aggiungendo una serie di istruzioni statiche, come l’inizializzazione del
parametro $SORT_DEFAULT, il recupero dell’array che stabilisce quali campi
possibile ordinare e quali invece no.
La seconda classe invece, alienante_46_method_table_list_content.php, è una classe
per la generazione del codice relativo al contenuto della tabella di search. Anche in
questa sono presenti, oltre al costruttore, altri due metodi: add_out e get_out_page.
A differenza dell’altra classe, nella lettura dei parametri ciò che interessa è il valore
dell’attributo in_html_type, in quanto per alcuni tipi, quali date, datetime, checkbox,
password, si ricorre ad un’azione diversa a seconda del valore dell’attributo.
Nel
metodo
get_out_page
viene
completato
il
contenuto
della
pagina
table_list_content.php semplicemente aggiungendo alcune porzioni di codice
predefinito.
31
Capitolo 2
Realizzazione specifiche
Entrambe le pagine generate da queste ultime due librerie, saranno incluse anche in
ajax_search.php. Come già detto, nel sistema precedente tale pagina non esisteva,
quindi nel CMS è stata creata una libreria apposita per la generazione di tale pagina,
alienante_81_method_ajax_search.php.
Al suo interno oltre al costruttore, è presente un altro metodo, get_out_page il cui
compito è costruire il contenuto della pagina. A differenza delle altre manca il
metodo add_out che costruiva il codice sorgente del file dinamicamente, in base al
contenuto del file XML.
L’unica cosa dipendente da tale file è il nome del campo, memorizzato in una
variabile d’istanza come in tutte le altre librerie del CMS che generano il codice di
una pagina. Tale valore viene passato, nel metodo get_out_page, ad alcune funzioni
utilizzate per scrivere le inclusioni delle librerie usate da ajax_search.php per
svolgere il suo compito.
Durante la creazione di questa libreria sono state create, pensando a uno sviluppo
futuro, alcune funzioni e di seguito inserite nelle raccolte di funzioni
alienante_0_base.php e alienante_1_base_HTML.php.
La prima è print_ajax_search_page_content( ), che si occupa di scrivere le istruzioni
relative alla lettura della query string, compresa la deserializzazione degli oggetti
serializzati. Vengono usate funzioni già presenti in alienante_0_base.php, fra cui
print_query_string_page(), opportunamente modificata per permettere di riconoscere,
attraverso un parametro passato in input, quando deve rispondere alla chiamata
della libreria che genera ajax_search.php.
Un’altra funzione creata è print_ajax_search_check_session(), che stampa la parte
relativa all'autenticazione con check_session_by_cookie(). Questa poi utilizza
print_ajax_check_error(), creata per scrivere la funzione per la gestione dell’errore per
la pagina di ajax_search.php.
L’ultima funzione creata e inserita però in alienante_1_base_HTML.php è
print_menu_form_restrict_ajax_search(): stampa il menu di navigazione per gestire la
funzionalità di restrict senza usare una form ma usando la chiamata AJAX alla
funzione live_order.
32
Capitolo 2
Realizzazione specifiche
Il metodo get_out_page conclude il suo operato con la chiamata alla funzione che si
occupa di inserire gli script per le inclusioni delle pagine per la visualizzazione della
tabella.
La libreria alienante_81_method_ajax_search.php non è dipendente, se non per il
nome del campo, dal file XML, in quanto la pagina generata non fa altro che
leggere e analizzare oggetti il cui identificatore è predefinito e standard per tutta
l’applicazione web.
Ritornando alla funzione JS live_order, nella creazione di uno oggetto Ajax_list si
passava un primo parametro
<?
echo
AJAX_DEFAULT_SERVER.$my->getProtected("AJAX_BASE_DOMAIN");?>,
che è il risultato della concatenazione del contenuto di due variabili: la prima
contenuta in global.cnf.php e l’altra nel file di configurazione della classe di
appartenenza dell’oggetto $my. Di conseguenza occorreva modificare anche le
classi che si occupavano di generare i suddetti file. La prima modifica viene fatta
manualmente
sul
file
global.inc.php
che
verrà
poi
copiato
dal
CMS
nell’applicazione generata. Per quanto riguarda la seconda invece è stata aggiunta in
alienante_10_class_cnf.php una riga di codice per scrivere tale variabile, che di
default è il nome del campo in esame durante la parserizzazione del file XML, che
rappresenta la cartella locale di lavoro.
Sempre in quest’ultima libreria è stata aggiunta un’istruzione per la scrittura di una
costante, $TABLE_HEADER_OFFSET, corrispondente all’intervallo di ripetizione
dell’intestazione della tabella nella sua visualizzazione dopo l’operazione di ricerca.
Inoltre
nel
metodo
add_out
viene
completato
il
contenuto
dell’array
$TABLE_ORDER, inizializzato nel costruttore, usato dall’applicazione web per
controllare se un campo è ordinabile, analizzando se il valore associato al nome del
campo è 1 (default) oppure 0. In questo modo è possibile, una volta generata
l’applicazione, settare manualmente tale array per impedire di ordinare secondo un
determinato campo.
33
Capitolo 2
Realizzazione specifiche
Conclusa la realizzazione per generare tutto l’occorrente per il funzionamento del
live order, non rimane che modificare alienante.php, aggiungendo i riferimenti ai
nuovi moduli creati. Per prima cosa sono state aggiunte le istruzioni per includere le
nuove librerie create; di seguito sono stati istanziati gli oggetti relativi a tali classi e
utilizzando questi oggetti sono state eseguite tutte le istruzioni per il corretto
funzionamento del processo di creazione dell’applicazione web con la nuova
funzionalità aggiunta.
2.3 ULTERIORI MODIFICHE E LORO IMPLEMENTAZIONE NEL CMS
Dall’analisi dell’applicazione iniziale sono emerse alcuni limiti, introdotti nel
Capitolo 1, per quanto riguarda la riusabilità del codice.
Nelle pagine di inserimento e copia, rispettivamente insert.php e copy.php, erano
presenti due form distinte anche se entrambe avevano lo stesso compito. In questo
modo se ci fosse l’intenzione di cambiare graficamente la form, si dovrebbero
modificare entrambe. Estendendo questa modifica a tutte le tabelle il lavoro da
svolgere sarebbe doppio.
Per rendere il codice più compatto e facilmente modificabile, è stata creata una
pagina, table_form.php, inclusa da entrambe le pagine. Al suo interno sono presenti
gli oggetti HTML di tipo input relativi alla tabella, con la possibilità di gestire sia il
caso di una form vuota sia una form con dei valori di default, semplicemente tramite
la seguente istruzione:
<input type="text" value=
"<? if (!empty($my->varName[0])) echo $my->varName[0]; else echo ""; ?>"...>
In questo caso si controlla se la variabile destinata a contenere il valore ritornato
dalla view.php, varName[0], è stata settata: in caso affermativo significa che la
pagina table_view.php è stata inclusa dalla copy.php e quindi stampa il valore;
altrimenti è stata inclusa dalla insert.php che non ha nessuna variabile varName[0]
inizializzata e quindi l’attributo value sarà vuoto.
34
Capitolo 2
Realizzazione specifiche
Nella pagina visualizzata per eliminare un record, delete.php, viene ristampata la
tabella contenente i dati del record selezionato. Ma questa tabella è la stessa che la
view.php usa attraverso l’inclusione della pagina table_view.php. Il ragionamento è
analogo a quello effettuato per la insert.php e la copy.php in caso di modifica dello
standard di visualizzazione di un record. Di conseguenza anche nella pagina
delete.php è stata inclusa la table_view.php rimpiazzando le precedenti righe di
codice che stampavano lo stesso contenuto della pagina inclusa.
Anche queste ulteriori cambiamenti saranno implementati nel Content Management
System, in modo tale che tutte le successive applicazioni generate ereditino tali
cambiamenti.
Per includere la table_view.php dopo l’azione di cancellazione di un record, è stata
modificata la pagina alienante_60_method_delete.php, in particolare nel metodo
get_out_page, dove è stata aggiunta la chiamata alla funzione già esistente
print_include_table_view( ), contenuta nella raccolta di funzioni alienante_0_base.php,
che stampa lo script PHP per includere la pagina table_view.php. Inoltre è stata
eliminata la porzione di codice, nel metodo add_out, che aveva il compito di
stampare le proprietà del record in dettaglio.
Per aggiungere la table_form.php nelle pagine di insert.php e copy.php sono state
modificate le due librerie che generavano le precedenti pagine ed è stata aggiunta
una nuova libreria, alienante_49_method_table_form.php. Questa classe si occupa
della generazione del codice sorgente della pagina table_form.php, costruendo il
codice HTML della form in base al valore dell’attributo in_html_type di ogni tag
<field> del documento XML. Infatti, dopo la lettura dei valori dipendenti dal file
XML, è stato inserito uno switch : esso in base al contenuto di in_html_type, che può
assumere i seguenti valori text, textarea, checkbox, radio, select, password, file, date,
datetime,foreign_key, chiama una funzione della libreria alienate_1_base_HTML.php
che costruisce il codice HTML corrispondente.
35
Capitolo 2
Realizzazione specifiche
Il tutto viene memorizzato in un buffer, risultato di una serie di concatenazioni di
codice, che verrà poi scritto sul file table_form.php da alienante.php.
Con la creazione di questa classe è stato necessario sostituire, nelle librerie che
generavano il codice delle pagine di insert.php e copy.php, il codice che si occupava
di scrivere le precedenti form con una serie di istruzioni per stampare lo script per
includere la nuova pagina creata. Per implementare questa serie di istruzioni,
essendo richieste in due classi distinte, è stata creata una funzione apposita,
print_include_table_form( ), in alienante_1_base_HTML.php, in modo da evitare di
ripetere lo stesso codice.
Nella libreria appena citata sono state apportate delle modifiche per rendere il
codice meno ripetitivo e per correggere alcuni “bug” relativi all’attributo readonly di
alcuni oggetti HTML di tipo input.
Infatti durante la progettazione e l’implementazione di table_form.php, è emerso
che alcune funzioni di stampa che svolgevano lo stesso compito ma per pagine
differenti, usavano lo stesso codice ad eccezione di alcune istruzioni. Ad esempio
esistevano due funzioni per la stampa del campo di input di tipo checkbox per la
pagina di table_form.php e quella di update.php: l’unica differenza era che per la
generazione del codice della pagina di update.php serviva un controllo per settare a
checked il campo preesistente memorizzato nel database. Questa osservazione si
poteva estendere a tutte quelle funzioni che dovevano stampare il codice di un
campo di tipo radio, checkbox, select. La soluzione adottata è stata semplicemente
quella di aggiungere un parametro in input alla funzione, che permettesse di
distinguere quando la funzione veniva chiamata dalla libreria per la generazione
della pagina update.php e quando invece dalla libreria corrispondente alla
table_form.php. In base al valore di tale parametro, tramite un controllo di tipo if,
viene stampata la porzione relativa di codice. In teoria con l’aggiunta di tale
parametro sarebbe necessario andare a modificare tutte le chiamate alle funzioni
modificate nelle librerie interessate. Ma grazie a una caratteristica del linguaggio
PHP è stata modificata solo libreria relativa alla pagina di update.php, in quanto
36
Capitolo 2
Realizzazione specifiche
nella definizione delle funzioni modificate il parametro è stato inserito come
parametro opzionale: in caso il chiamante non passa quel parametro non viene
segnalato nessun errore ma viene inizializzato con il valore di default specificato
nella definizione della funzione.
function print_row_checkbox_insert_update_page($io_label, $value,$update=FALSE)
Stesso problema e stessa soluzione per la stampa di campi input di tipo readonly:
anche in questo caso erano previste due funzioni uguali tranne che per qualche riga
di codice. Nella libreria alienante_70_method_update.php a seconda del valore
dell’attributo obligatory del file XML, venivano chiamate due funzioni: in caso di
valore yes la funzione chiamata stampava il codice relativo a un campo input in
readonly; altrimenti stampava normalmente come descritto precedentemente. Quindi
unendo le due funzioni si è aggiunto un ulteriore parametro in input:
function
print_row_checkbox_insert_update_page($io_label,
$value,$update=FALSE,
$readonly=FALSE)
Ma per alcuni oggetti HTML, l’attributo readonly non aveva effetto: infatti se
scrivessimo <input type="radio" readonly> potremmo comunque selezionare la casella.
Quindi bisognava ridisegnare la gestione del readonly in modo tale che anche i
campi input di tipo radio, checkbox, select, file, supportassero tale proprietà. La
modifica di un record, nel sistema in esame, prevede che sia possibile modificare
solo i campi non obbligatori, ma comunque tutti i campi sono passati nuovamente al
database per permettere l’aggiornamento del record per intero. Una soluzione al
problema readonly poteva essere quella di non visualizzare i campi che non
potevano essere modificati, e passare soltanto i campi cambiati. Ma, per una
questione di chiarezza nel mostrare tutti i campi di un record, si è optato sull’uso
dell’attributo disabled, inserendo un campo input di tipo hidden per passare il valore
al server. In questo modo il value del campo input non può essere cambiato in quanto
l’attributo disabled disabilita per intero il tag input, ma tramite l’uso di un tag input di
tipo hidden, in cui viene memorizzato il value del campo disabilitato, è possibile
passare tale valore intoccato nella query string e aggiornare il record.
37
Capitolo 2
Realizzazione specifiche
Il risultato finale della nuova modifica dopo la generazione del codice della pagina
update.php è:
……
<select disabled name="contesto" size="1" class="normal">
<option value="">---</option>
<option value="HD0" >CRM HD</option>
<option value="IMP" selected>IMPRESA</option>
<option value="MAN" >MANUTENZIONE</option>
</select>
<input type="hidden" name ="contesto" value="IMP">
……
2.4 RICO ACCORDION
All’interno dell’applicazione gestionale occorreva aggiungere un modulo attraverso
cui poter comunicare con il cliente in real-time, mettendolo al corrente di tutte le
modifiche apportate al sistema o di tutte le comunicazioni di servizio.
La soluzione trovata, Accordion, un effetto messo a disposizione dalla libreria
rico.js, rispecchia le caratteristiche richieste: infatti è funzionale, dinamica e
graficamente interessante.
38
Capitolo 2
Realizzazione specifiche
Figura 7 – Esempio pratico di Rico Accordion
Il comportamento di Rico.Accordion fa uso di Effect.AccordionSize, un effetto che
simultaneamente aumenta l’altezza di un elemento mentre ne diminuisce l’altezza di
un altro.
<div id=accordionDiv >
<div id=panel >
<div id="panel0header" class=title2 >
<strong>Last Update - Titolo</strong>
</div>
<div id="panel0Content" class=tab1 >
In questa sezione potrete trovare gli ultimi aggiornamenti ordinati
per data.
</div>
</div>
</div>
<script>new Rico.Accordion( $('accordionDiv'), {onLoadShowTab: '1', panelHeight: 350});</script>
39
Capitolo 2
Realizzazione specifiche
Nel codice di GestioneLastUpdate.php, osservabile nel listato precedente, è
presente un div esterno, accordionDiv, che racchiude tutti i pannelli. Poi ad ogni
pannello corrisponde una coppia di div, uno per l’header e uno per il content,
racchiusi a loro volta in un altro div identificante il pannello.
Per associare il comportamento di Accordion al div contenitore, costruiamo un
oggetto Rico.Accordion passandogli il div contenitore, catturato attraverso la funzione
$( ), definita in prototype.js, e un oggetto contenente parametri di configurazione per
settare vari aspetti grafici dell’Accordion, come il pannello da mostrate all’inizio e
l’altezza del pannello aperto.
In caso si voglia applicare un certo stile, tipo bordi, margini, sfondo, potrebbe essere
utile annidare un ulteriore div all’interno del content div da modellare.
Nel realizzare tale modulo si è introdotto un filtro di ricerca per data e contesto,
strutturalmente e graficamente simile a quelli già presenti nell’applicazione
originale, in modo da facilitare all’utente la lettura dei messaggi relativi al proprio
contesto lavorativo e ad un certo periodo di tempo.
Dietro il filtro è stata creata una libreria, GestioneLastUpdate.lib.php, adottando le
innovazioni introdotte dal PHP 5, come l’adozione del metodo costruttore usando la
parola chiave __construct invece che il nome della classe, dove definire i metodi e le
variabili d’istanza della classe; come i modificatori di visibilità public, private,
protected;
come
la
deferenziazione
degli
oggetti,
tipo
$this->repository-
>out_error_description.
Tale libreria estende la nuova classe GestioneLastUpdate.cnf.php, contenente la
variabile array $MESI, usata in seguito per convertire il numero del mese con il
rispettivo nome italiano.
La pagina GestioneLastUpdate.php, creata secondo lo standard di programmazione
di tutta l’applicazione originale, è quella destinata a contenere il modulo aggiunto
dell’Accordion e ad utilizzare le funzioni di libreria definite nelle librerie sopra
descritte.
40
Capitolo 2
Realizzazione specifiche
Una volta realizzato tutto l’occorrente per il corretto funzionamento, il passo
successivo è stato quello di aggiungere la voce corrispondente al menu di
navigazione.
Per quanto riguarda l’implementazione nel CMS aziendale in questo caso non è
stata richiesta dall’azienda, perché non è una funzionalità che dovrà essere presente
in tutte le applicazioni web generate dal CMS.
41
Capitolo 3
Test live order
CAPITOLO 3
Test Live Order
Per osservare effettivamente i risultati del lavoro svolto, analizzeremo i risultati dei
test effettuati per confrontare i tempi di un’interazione client/server tradizionale con
i tempi di un’interazione con tecnologia AJAX.
Nella seguente tabella riportiamo i risultati di un test eseguito con browser Mozilla
Firefox su una tabella di 10 campi contenente 200 record:
N° Prova
Tempo Interazione Classica Tempo Interazione AJAX
(sec.)
(sec.)
1
8,2
5,8
2
8,4
5,1
3
7,6
5,5
4
8,1
4,9
5
11,7
6,1
6
6,9
4,8
7
7,8
5,6
8
6,8
5,4
Media
8,2
5,4
Nonostante la pagina della ricerca nell’interfaccia dell’applicazione non sia molto
pesante, osserviamo comunque la differente velocità di esecuzione tra
un’interazione client/server tradizionale(in media 8,2 sec) e un’interazione
client/server con tecnologia AJAX (in media 5,4 sec).
42
Capitolo 3
Test live order
Maggiori vantaggi si noterebbero con un’applicazione web che deve ricaricare più
componenti di una pagina rispetto a quanto avviene nell’applicazione in esame dove
i vantaggi a livello di velocità sono minimi.
43
Conclusioni
Conclusioni
Ricordando le richieste dell’azienda possiamo affermare di aver raggiunto gli
obiettivi prefissati: infatti è stato realizzato il live order per un’applicazione webbased e tale funzionalità è stata implementata nel Content Management System
aziendale. Per il servizio di comunicazione è stata creata un’interfaccia dinamica
che permette al cliente di leggere facilmente i messaggi dell’azienda semplicemente
selezionando il messaggio desiderato con un semplice click. Infine durante lo
sviluppo del progetto sono state individuati e di conseguenza corretti alcuni bug
presenti nel sistema CMS.
Con la realizzazione dell’ordinamento dinamico e con l’utilizzo di Accordion
l’interfaccia dell’applicazione è diventata maggiormente user-friendly e soprattutto
ad alta dinamicità. Infatti l’obiettivo dell’azienda, specialmente in un’ottica futura, è
quello di fornire al cliente un’applicazione con un’interfaccia veloce, dinamica,
interattiva e semplice da usare.
Utilizzando un CMS che genera l’intera applicazione notiamo numerosi vantaggi.
Infatti il CMS in esame permette di generare un’intera applicazione web, già
sviluppata all’80%, senza necessità di scrivere una riga di HTML e senza conoscere
linguaggi di programmazione lato server, come PHP. Inoltre, vista la mancata
necessità di scrivere codice, c’è una riduzione drastica degli errori di circa il 100% e
i tempi di sviluppo sono notevolmente abbreviati. L'aspetto grafico può essere
personalizzato scegliendo un foglio di stile CSS appositamente progettato e può
essere ampliato aggiungendo nuovi moduli per nuove funzionalità, come abbiamo
fatto per l’ordinamento dinamico.
44
Conclusioni
Per quanto riguarda gli sviluppi futuri dell’ordinamento dinamico, si potrebbe
pensare alla non corrispondenza dei nomi dei campi sulla tabella del database ed i
rispettivi nomi attribuiti a questi campi nelle pagine dell’applicazione web, oppure
alla possibilità che ogni campo provenga da una tabella diversa, al di fuori del
vincolo di foreign key. In questi casi la funzionalità sviluppata dovrebbe essere
modificata in alcune sue parti per permetterne il corretto comportamento.
Anche la ricerca di un record potrebbe essere svolta in maniera dinamica usando la
tecnologia AJAX. Infatti se dopo una prima ricerca si volesse effettuarne un’altra
sul risultato ottenuto, si dovrebbe tornare al filtro di ricerca e perdere il risultato
ottenuto in precedenza. Una soluzione dinamica potrebbe essere quella di inserire
un filtro di ricerca nella stessa pagina dove viene visualizzata la tabella, che in
tempo reale effettua la ricerca ad ogni lettera o numero inserito dall’utente. Un
esempio concreto possiamo trovarlo in [3].
Invece il CMS potrebbe essere dotato di un’interfaccia grafica invece di essere
avviato tramite riga di comando. Così qualsiasi utente, anche inesperto, potrebbe
generare un’applicazione in maniera più semplice di quanto avviene tuttora.
45
Bibliografia
BIBLIOGRAFIA
[1] http://www.prototypejs.org
[2] http://www.openrico.org
[3] http://www.dowdybrown.com/dbprod/rico-test9/php/ex3.php
[4] http://www.it.wikipedia.org
[5] http://www.w3schools.com
[6] http://www.sergiopereira.com/articles/prototype.js.html
46
RINGRAZIAMENTI
Eccoci arrivati alla parte veramente più importante della tesi…ma soprattutto la
parte che potranno leggere e capire proprio tutti, anche i non “addetti ai lavori”!!
Il primo GRAZIE
va ai miei genitori per tutti i sacrifici economici e non che hanno fatto per
permettermi di raggiungere questo prestigioso traguardo. In questi anni, forse per
voi un po’ troppi, non mi avete fatto mancare nulla, neanche qualche ramanzina per
il mio voler pensare poco a studiare e tanto a divertire! Grazie per tutte le volte che
mi avete incoraggiato…per tutte le volte che mi avete sostenuto e anche per tutta la
pazienza che avete avuto…questa laurea la dedico a voi…
GRAZIE
alla mia adorata sorella Sabrina, onnipresente in ogni momento, sia felice che
difficile, sempre pronta ad ascoltarmi ed aiutarmi…
GRAZIE
a mio fratello Angelo e a mia cognata Patricia, per il loro sostegno che non è mai
mancato in questi anni e per i loro consigli preziosi…
GRAZIE
ad Alessio, il mio piccolo ma vivace nipotino, capace con un semplice “Ghio
Pajio” (Zio Paolo) di regalarmi ogni volta una gioia infinita…
GRAZIE
a tutti i miei familiari, le mie nonne,Velia e Vincenza, i miei tanti zii e i miei
tantissimi cugini che mi sono stati sempre vicini in questi anni...
GRAZIE
al mio tutor aziendale, l’Ing. Fabio Panaioli, oltre che un tutor un grande amico,
capace di dispensare consigli utili non solo in ambito lavorativo…è stata una
fortuna e un piacere aver potuto svolgere questo lavoro con lui…
GRAZIE
anche al mio tutor accademico, il Prof. Luca Spalazzi, sempre gentile e disponibile
ogni qualvolta è stato chiamato in causa…
GRAZIE
alla mia dolce metà, Silvia, che è stata sempre al mio fianco, sopportando tutte le
mie paranoie con tanta pazienza, con tanti utili e amorevoli incoraggiamenti e
soprattutto con grande ed immenso amore. Senza di lei sarebbero stati sicuramente
anni meno piacevoli e sarei rimasto un ragazzo qualunque. Grazie amore mio e tanti
auguri perché oggi ti laurei anche tu…
GRAZIE
ai miei AMICI coinquilini, il Capitano e il Nano, perché senza di loro questi anni
sarebbero stati forse di meno ma senza dubbio meno divertenti…grazie per tutti i
momenti di svago, sempre maggiori dei momenti di studio, dedicati a migliorare le
tecniche di gioco a PES (Pro Evolution Soccer)…grazie per tutti quei momenti in
cui mi avete ascoltato e per tutti i consigli che mi avete dato…
GRAZIE
a tutti i miei amici ingegneri, Danilo, Tommaso, Andrea, Christian, che mi hanno
aiutato nello studio con i loro consigli e appunti, ma soprattutto mi hanno aiutato
nel dopo-studio…
GRAZIE
a tutti i miei compagni di squadra del torneo universitario, soprattutto i sempre
presenti Antonio, Marco, Fabrizio, oltre agli ingegneri sopra citati…grazie per
avermi aiutato a vincere due tornei e soprattutto grazie per le belle serate dedicate ai
festeggiamenti dopo ogni vittoria…
GRAZIE
a tutti i miei amici della mia città, Antonello, Maria, Francesca M., Francesca F.,
Michele, Matteo, Azzurra, Alessandro, Martina, Federico, Massimo, Betty e tutti gli
altri, che mi hanno fatto passare tanti momenti spensierati e divertenti…
GRAZIE
a tutti quelli che nel bene o nel male, consapevolmente o inconsapevolmente, che
ora dimentico, mi hanno aiutato in questi anni importanti…
Paolo De Gruttola