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 " ";} ?></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