Configurazione di JSF (MyFaces) con Eclipse e Tomcat

31 ottobre 2012 Nessun commento

Java Server Faces (JSF) è un framework java basato sul design pattern architetturale Model-View-Controller. Esistono diverse implementazioni del framework: oltre a quella Sun, sono disponibili anche le implementazioni Oracle e quella Apache (MyFaces), che analizzeremo in questo breve esempio nella sua versione 1.2 (al momento siamo giunti alla versione 2.0).
Per procedere con la creazione del nostro progetto, che chiameremo JSF_MyFaces_Tutorial, creiamo in Eclipse un nuovo Dynamic Web Project settando le impostazioni come segue:

Nella sezione Configuration cliccare sul pulsante Modify… e spuntare le opzioni che si presentano come segue, prestando attenzione alla versione di JavaServer Faces che deve essere la 1.2 (nel nostro caso perché in seguito andremo ad aggiungere librerie compatibili con tale versione):

quindi procedere fino all’ultima schermata, quella per impostare le librerie, ricordandosi strada facendo di spuntare l’opzione Generate web.xml deployment descriptor.
Come detto utilizzeremo l’implementazione delle JSF fornita da Apache. Per ottenerla basta cliccare sull’icona a destra della schermata a forma di dischetto (Download library…), che permette di scaricare la libreria da internet.
A questo punto bisogna scaricare manualmente da internet la libreria JSTL (nel nostro caso abbiamo scaricato il file jstl-1.2.jar), quindi cliccando sull’icona sopra quella del dischetto (Manage libraries…) creare una nuova cartella tramite il comando New… da nominare JSTL e importare al suo interno il file appena scaricato cliccando sul pulsante Add External JARs…
Terminate le configurazioni delle librerie si dovrebbe avere una schermata simile a questa:

La fase di settaggio dell’applicazione è terminata.

HELLOWORLDBEAN.JAVA
Prendiamo confidenza con il framework appena configurato creando la nostra prima pagina JSF, l’immancabile Hello World! in versione MyFaces…
Dentro la cartella Java Resources/srccreiamo il package helloworldpackage e dentro creiamo una nuova classe java, HelloWorldBean.java, come segue:

package helloworldpackage;

public class HelloWorldBean {

    private String messaggio = "Ciao Mondo!";

    public HelloWorldBean() {}

    public void setMessaggio(String messaggio) {
        this.messaggio = messaggio;
    }
    public String getMessaggio() {
        return this.messaggio;
    }

}

FACES-CONFIG.XML
Questo file, presente nella cartella WebContent/WEB-INF, è fondamentale, contenendo le configurazioni per le jsf. Vediamo com’è strutturato:

<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
    version="2.0">

    <managed-bean>
        <managed-bean-name>hello_world_bean</managed-bean-name>
        <managed-bean-class>helloworldpackage.HelloWorldBean</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>

</faces-config>

All’interno del tag managed-bean si specificano le caratteristiche di un determinato bean (va utilizzato un tag per ogni bean presente): il primo tag è il nome che vogliamo dare al bean (per convenzione si usa il nome della classe del bean con l’iniziale maiuscola, ma in questo caso abbiamo fatto un’eccezione per rendere successivamente più comprensibile l’esempio); il secondo tag è la classe a cui il bean fa riferimento (va utilizzato l’intero percorso del package); infine il terzo tag descrive lo scope del bean (in questo esempio abbiamo usato il valore request, ma è possibile usare anche session e none).

WEB.XML
Questo è il file di configurazione del web server ed Eclipse si occupa di servircene una configurazione iniziale funzionante. Vediamo la prima parte (quella che ci interessa in questo momento)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://java.sun.com/xml/ns/javaee"  
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"   
    id="WebApp_ID"
    version="3.0">

    <display-name>JSF_MyFaces_Tutorial</display-name>

    <welcome-file-list>
        <welcome-file>faces/pages/index.jsp</welcome-file>
    </welcome-file-list>

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>

    ...

</web-app>

INDEX.JSP
Passiamo alla realizzazione della pagina di visualizzazione del nostro esempio.
In WebContent creiamo la cartella pages e al suo interno il file index.jsp, con il codice proposto di seguito:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>

<f:view>
	<h:outputLabel value="#{hello_world_bean.messaggio}"></h:outputLabel>
</f:view>

</body>
</html>

Le due righe subito dopo la dichiarazione del doctype specificano la presenza di due prefissi i quali si riferiscono alle definizioni contenute negli indirizzi alla loro destra. I prefissi vengono usati al posto dei tag per riferirsi a dei componenti più complessi che sfruttano le possibilità del framework. All’interno del body il tag <f:view> è necessario per poter usare tutti gli altri tag JSF. Senza di esso infatti il framework lancerebbe un messaggio, lamentando l’assenza di un tag “root”. Molto importante è notare come ci si riferisce alle proprietà dei bean. HelloWorldBean è la classe creata in precedenza, mentre hello_world_bean è il bean registrato nel faces-config e riferito alla nostra classe java. Quest’ultimo, come si può vedere, è in grado di riferirsi direttamente all’attributo della nostra classe. Avendo infatti nominato correttamente i getter ed i setter, sarà il framework a richiamarli opportunamente per ritornare il valore della variabile. Senza getter e setter impostati questo meccanismo non può funzionare.
A questo punto non resta che visualizzare la nostra applicazione sul browser andando all’indirizzo:
http://127.0.0.1:8080/JSF_MyFaces_Tutorial/faces/pages/index.jsp
o semplicemente:
http://127.0.0.1:8080/JSF_MyFaces_Tutorial/

Categorie:jsf Tag: , ,

Proxy JSP

25 novembre 2011 Nessun commento

In linea generale non è bene che uno script Javascript possa accedere ai dati di un sito remoto, ed infatti i browser lo impediscono.

In alcuni casi, però, può essere comodo aggirare questa limitazione: ad esempio state sviluppando un’applicazione ajax che usa i dati messi a disposizione da un webserver, ma non potete lavorare direttamente sul server in questione. Siccome mi sono trovato in questa situazione, ho rimediato con questo semplice proxy jsp che non fa altro che ricevere le chiamate ajax, rigirarle al server remoto e riprodurre il risultato.

Nel mio caso si trattava di consumare dei servizi soap, quindi di spedire/ricevere dati xml; ho  impostato nel codice Javascript l’indirizzo del proxy e nel proxy l’url dell’enpoint finale e tutto è filato a meraviglia.

<%@ page    import="java.io.*, java.net.*"  contentType="text/xml; charset=UTF-8" trimDirectiveWhitespaces="true" %>
<%
StringBuffer sbf = new StringBuffer();
StringBuffer data =  new StringBuffer();
String inputLine;

try {

    BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream()));
    while ( (inputLine = in.readLine()) != null) data.append(inputLine);
    in.close();

    URL url = new URL("http://?.?.?.?:8080/axis2/services/Servizio.ServizioHttpSoap11Endpoint/");
    URLConnection conn = url.openConnection();
    conn.setRequestProperty("Content-Type", "text/xml; charset=\"utf-8\"");
    conn.setDoOutput(true);
    OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
    wr.write(data.toString());
    wr.flush();

    in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    while ( (inputLine = in.readLine()) != null) {sbf.append(inputLine); }
    wr.close();
    in.close();

} catch (MalformedURLException e) {

   StringWriter sw = new StringWriter();
   PrintWriter pw = new PrintWriter(sw, true);
   e.printStackTrace(pw);
   pw.flush();
   sw.flush();
   out.print("<br>MalformedURLException: "+sw.toString());

} catch (IOException e) {

   StringWriter sw = new StringWriter();
   PrintWriter pw = new PrintWriter(sw, true);
   e.printStackTrace(pw);
   pw.flush();
   sw.flush();
   out.print("<br>IOException: "+sw.toString());

}

%>
<%= sbf.toString() %>
Categorie:java, programmazione Tag:

Impostare la JVM di default su Ubuntu

11 marzo 2009 Nessun commento

Può capitare di aver installato più versioni di Java sul proprio computer. Per impostare in modo semplice e veloce la JVM da usare di default:

$ sudo update-alternatives --config java

Ci sono 5 alternative che forniscono `java`.

  Selezione    Alternativa
-----------------------------------------------
          1    /usr/bin/gij-4.1
 +        2    /usr/lib/jvm/java-gcj/jre/bin/java
*         3    /usr/lib/jvm/java-1.5.0-sun/jre/bin/java
          4    /usr/lib/jvm/java-6-sun/jre/bin/java
          5    /usr/bin/gij-4.2

Premi invio per mantenere il default[*], o inserisci il numero da selezionare:4
Viene usato "/usr/lib/jvm/java-6-sun/jre/bin/java" per fornire "java".

Basta inserire il numero della JVM da usare e premere invio.

 

Categorie:linux Tag: ,

Zen Cart, come aggiungere un campo oggetto alla form di contatto standard

26 febbraio 2009 Nessun commento

In quest’articolo mostro come aggiungere un oggetto alla form di contatto standard di Zen Cart v1.3.8
Con questa modifica, il cliente che desidera inviare una comunicazione ai gestori del negozio deve selezionare un oggetto tra quelli proposti nella form di contatto.

Cos’è Zen Cart

Zen Cart è un software open source per la creazione di negozi online. È basato su PHP e MySQL. Supporta diverse lingue e valute, è disponibile gratuitamente con la licenza GNU General Public.

La form di contatto standard

La form di contatto standard permette al cliente di specificare:

  • nome, cognome;
  • e-mail del mittente;
  • il messaggio da inviare.

Zen Cart imposta in automatico un oggetto che contiene il nome del negozio e basta.

Aggiungere il campo oggetto

Come prima cosa bisogna definire

  • un’etichetta con il nome per il campo oggetto
  • ed una variabile con la lista degli oggetti da cui scegliere.

Queste variabili devono essere messe nel file contact_us.php della lingua, o delle lingue, in uso. Per l’italiano, supponendo che Zen Cart sia installato nella directory /public_html, il file è:
/public_html/includes/languages/italian/contact_us.php

Vanno aggiunte le seguenti definizioni:

define('ENTRY_SUBJECT', 'Oggetto:');
define('SUBJECT_LIST', ',Pagamenti,Servizi,Suggerimenti,Informazioni');
define('ENTRY_EMAIL_SUBJECT_CHECK_ERROR','Hai dimenticato l\'oggetto? Per favore seleziona un oggetto dalla lista.');

La prima definisce la costante ENTRY_SUBJECT che rappresenta l’etichetta da mostrare all’utente.
La costante SUBJECT_LIST contiene la lista degli oggetti separati da virgole. Notare che il primo carattere della stringa è una virgola.Questo accorgimento permette di creare un oggetto “vuoto” che verrà usato per controllare che l’utente abbia selezionato qualcosa prima di spedire la mail. Se l’utente non seleziona niente, allora gli verrà mostrato l’errore ENTRY_EMAIL_SUBJECT_CHECK_ERROR.

Adesso bisogna fare in modo che nella form di contatto sia visualizzata la select per la scelta dell’oggetto. Nel file:
/public_html/templates/template_default/templates/tpl_contact_us_default.php
inserire sopra la riga <label class=”inputLabel” for=”contactname”><?php echo ENTRY_NAME; ?></label>:



' . ENTRY_REQUIRED_SYMBOL . ''; 
?>

Per finire, è necessario aggiungere il controllo che l’utente abbia selezionato un oggetto e usare questo oggetto nell’invio della mail.
Queste azioni si impostano nel file:
/public_html//includes/modules/pages/contact_us/header_php.php
che riporto per intero (vedi righe 15, 23, 66 e 71):

bindVars($sql, ':customersID', $_SESSION['customer_id'], 'integer');
      $check_customer = $db->Execute($sql);
      $customer_email= $check_customer->fields['customers_email_address'];
      $customer_name= $check_customer->fields['customers_firstname'] . ' ' . $check_customer->fields['customers_lastname'];
    } else {
      $customer_email = NOT_LOGGED_IN_TEXT;
      $customer_name = NOT_LOGGED_IN_TEXT;
    }

    // use contact us dropdown if defined
    if (CONTACT_US_LIST !=''){
      $send_to_array=explode("," ,CONTACT_US_LIST);
      preg_match('/\<[^>]+\>/', $send_to_array[$_POST['send_to']], $send_email_array);
      $send_to_email= eregi_replace (">", "", $send_email_array[0]);
      $send_to_email= eregi_replace ("<", "", $send_to_email);
      $send_to_name = preg_replace('/\<[^*]*/', '', $send_to_array[$_POST['send_to']]);
    } else {  //otherwise default to EMAIL_FROM and store name
    $send_to_email = EMAIL_FROM;
    $send_to_name =  STORE_NAME;
    }

    // Prepare extra-info details
    $extra_info = email_collect_extra_info($name, $email_address, $customer_name, $customer_email);
    // Prepare Text-only portion of message
    $text_message = OFFICE_FROM . "\t" . $name . "\n" .
    OFFICE_EMAIL . "\t" . $email_address . "\n\n" .
    '------------------------------------------------------' . "\n\n" .
    strip_tags($_POST['enquiry']) .  "\n\n" .
    '------------------------------------------------------' . "\n\n" .
    $extra_info['TEXT'];
    // Prepare HTML-portion of message
    $html_msg['EMAIL_MESSAGE_HTML'] = strip_tags($_POST['enquiry']);
    $html_msg['CONTACT_US_OFFICE_FROM'] = OFFICE_FROM . ' ' . $name . '
' . OFFICE_EMAIL . '(' . $email_address . ')'; $html_msg['EXTRA_INFO'] = $extra_info['HTML']; // Send message // uso $subject che contiene l'oggetto scelto dall'utente zen_mail($send_to_name, $send_to_email, $subject, $text_message, $name, $email_address, $html_msg,'contact_us'); zen_redirect(zen_href_link(FILENAME_CONTACT_US, 'action=success')); } else { $error = true; if (empty($subject)) { //se $subject è vuoto allora l'utente non ha selezionato niente, // mostro il messaggio d'errore specifico $messageStack->add('contact', ENTRY_EMAIL_SUBJECT_CHECK_ERROR); } if (empty($name)) { $messageStack->add('contact', ENTRY_EMAIL_NAME_CHECK_ERROR); } if ($zc_validate_email == false) { $messageStack->add('contact', ENTRY_EMAIL_ADDRESS_CHECK_ERROR); } if (empty($enquiry)) { $messageStack->add('contact', ENTRY_EMAIL_CONTENT_CHECK_ERROR); } } } // end action==send // default email and name if customer is logged in if($_SESSION['customer_id']) { $sql = "SELECT customers_id, customers_firstname, customers_lastname, customers_password, customers_email_address, customers_default_address_id FROM " . TABLE_CUSTOMERS . " WHERE customers_id = :customersID"; $sql = $db->bindVars($sql, ':customersID', $_SESSION['customer_id'], 'integer'); $check_customer = $db->Execute($sql); $email= $check_customer->fields['customers_email_address']; $name= $check_customer->fields['customers_firstname'] . ' ' . $check_customer->fields['customers_lastname']; } if (CONTACT_US_LIST !=''){ foreach(explode(",", CONTACT_US_LIST) as $k => $v) { $send_to_array[] = array('id' => $k, 'text' => preg_replace('/\<[^*]*/', '', $v)); } } // include template specific file name defines $define_page = zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . '/html_includes/', FILENAME_DEFINE_CONTACT_US, 'false'); $breadcrumb->add(NAVBAR_TITLE); ?>
Categorie:programmazione, web Tag: ,

Uso Bash perché sono pigro

21 gennaio 2009 Nessun commento

Lo ammetto, sono pigro. Dirò di più, non sono semplicemente pigro, sono MOLTO pigro.
Probabilmente è proprio per questo che preferisco Linux a Winzoz: mi permette di fare le cose più velocemente e con minor fatica, 2 grandi virtù per uno pigro come me! Sicuramente, lo strumento principale che permette il concretarsi di queste virtù è la shell, nel mio caso BASH.

Semplificando all’osso, BASH mi permette di eseguire programmi e di ridizionarne l’output ad un file o ad altri programmi, tutto direttamente dalla linea di comando. A questa flessibilità di base si aggiunge il fatto che anche un’installazione minimale di Linux mi mette a disposizione una miriade di programmini utili: per manipolare i file in tutti i modi, per gestire gli utenti, per gestire i database installati, per qualsiasi cosa vi venga in mente… o quasi 😉

Certo questa ricchezza viene ad un prezzo, che è quello dell’apprendimento iniziale: una shell è meno intuitiva di un’interfaccia grafica e richiede che sappiate a priori i nomi dei comandi da usare. Questo prezzo iniziale, però, è ampiamente ripagato dal tempo risparmiato in seguito. Ed il tempo risparmiato è tempo guadagnato per poltrire!

Da buon pigro, una delle cose per cui sono grato a BASH è  che si ricorda per me la lista dei comandi che ho eseguito. Così, se devo utilizzare di nuovo un comando molto lungo, invece di riscriverlo da capo posso premere il tasto freccia in alto e risalire nella lista finché non lo trovo: la prima volta che premo il tasto freccia  BASH mi presenta l’ultimo comando che ho digitato, poi il penultimo, poi il terzultimo, e così via. Alla fine, quando trovo il comando che mi interessa, posso modificarlo od eseguirlo così com’è.

Ma se il comando l’ho eseguito parecchio tempo prima, dovrò premere molte volte il tasto freccia in alto. Una fatica inaccettabile! Per fortuna BASH è comprensiva e mette a disposizione una funzione di ricerca: premendo i tasti CTRL-R e poi iniziando a digitare le lettere del comando, BASH ci presenterà il primo comando nella lista che contiene tutte lettere digitate. Se il comando indicato è il nostro possiamo premere INVIO per eseguirlo nuovamente oppure modificarlo. Se invece vogliamo cercare una linea di comando più antica con le stesse lettere, possiamo proseguire a premere CTRL-R finché non la troviamo.

Se voglio semplicemente ripetere l’ultimo comando eseguito così com’è, allora non ho neanche bisogno di spostare la mano verso i tasti freccia, basta scrivere:

$!!

Volendo invcece eseguire l’ultima linea di comando che contiene una certa parola, non sono costretto ad usare  CTRL-R per cercarla, ma posso usare la scorciatoia !?. Ad esempio, se voglio ripetere la ricerca di un file che ho fatto ieri e so che nel frattempo non ho compiuto altre ricerche, tutto quello che devo scrivere è:

$!?find

e BASH provvederà a cercare nella lista l’ultimo comando che contiene la parola find e lo eseguirà.

Molto spesso capita di dover eseguire l’ultimo comando digitato ma con parametri diversi. Ad esempio, se chiedessi di visualizzare con ls il contenuto della cartella /home/ottavio/Documents/testo/ ma invece di scrivere testo/ scrivessi test/, BASH mi direbbe che la directory non esiste. A questo punto, invece di riscrivere il “lungo” comando

$ls /home/ottavio/Documents/testo/

potrei semplicemente eseguire:

$^test^testo^

che dice a BASH di eseguire l’ultimo comando sostituendo la stringa test con testo.

Questo è un piccolo esempio di come BASH mi aiuta a perserverare nella mia pigrizia scrivendo il meno possibile.

E se non siete ancora convinti che la pigrizia sia una virtù, vi ricorderò cosa dice Larry Wall:

The three chief virtues of a programmer are: Laziness, Impatience and Hubris.


Categorie:linux Tag: ,

Flash e Lightbox, galleria completamente funzionante

14 gennaio 2009 2 commenti

Nel post precedente avevo mostrato come integrare in modo semplice e veloce una galleria di immagini fatta in Flash con Lightbox. Rimaneva, però, un inconveniente: dopo aver aperto un’immagine con Lightbox non era possibile passare direttamente alla successiva, ma era necessario chiuderla e poi cliccare su un’altra immagine della galleria. Ho usato l’idea della soluzione precedente per risolvere anche questo problema.

Per vedere un esempio del risultato potete cliccare sull’immagine qui sotto:

Demo integrazione flash-lightbox

Demo integrazione flash-lightbox

Come utilizzare questa soluzione nelle tue pagine

Prima di tutto, per usare questa soluzione, occorre che la galleria Flash carichi i dati delle immagini da un file in formato xml, chiamato data.xml, con la seguente struttura:

<?xml version="1.0" encoding="utf-8"?>
<thumbs thumbsToScale="5" showReflection="yes" showTitle="no">
  <thumbItem title="Pinguino 1" thumb="foto/01.jpg"
     link="javascript:flashLightbox('foto/01.jpg');"  targetWindow="" />
  <thumbItem title="Pinguino 2" thumb="foto/02.jpg"
     link="javascript:flashLightbox('foto/02.jpg');"  targetWindow="" />
  <thumbItem title="Pinguino 3" thumb="foto/03.jpg"
     link="javascript:flashLightbox('foto/03.jpg');"  targetWindow="" />
  <thumbItem title="" thumb="foto/04.jpg"
     link="javascript:flashLightbox('foto/04.jpg');"  targetWindow="" />
</thumbs>

Gli unici attributi dei thumbItem che vengono utilizzati da Lightbox sono link, che deve contenere l’indirizzo dell’immagine, e title, che può contenere una descrizione dell’immagine.

E’ necessario un modo per leggere questo file e fornire il suo contenuto al codice javascript. Di questo si occupa data_photo_parser.php, che deve essere messo nella stessa posizione di data.xml. Se cambiate il nome (o la posizione) di data.xml, allora dovete modificare di conseguenza la variabile $imgDataFile in data_photo_parser.php.

Una volta installato data_photo_parser.php nella posizione corretta, bisogna includere nella pagina con la galleria Flash il foglio di stile e le librerie javascript esattamente come si farebbe per la versione standard di Lightbox:

<link rel="stylesheet" href="css/lightbox.css" type="text/css" media="screen"/>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/scriptaculous.js?load=effects,builder"></script>
<script type="text/javascript" src="js/lightbox_x_flash.js"></script>

Queste inclusioni, però, non sono sufficienti ad inizilizare la galleria per Lightbox: occorre richiamare esplicitamente la funzione initImageListFlashLightbox() prima della chiusura del </body>, passandogli come parametro l’indirizzo a cui trovare la pagina data_photo_parser.php:

<script type="text/javascript">
initImageListFlashLightbox('http://www.ottaviodinale.it/demo/flash_lightbox_gallery/data_photo_parser.php');
</script>
</body>

A questo punto tutto dovrebbe essere pronto all’uso.

ATTENZIONE: una cosa che potrebbe non essere evidente da quanto detto è che, per funzionare correttamente, la pagina che contiene la galleria d’immagini deve essere ospitata sullo stesso server della pagina  pagina data_photo_parser.php, altrimenti il javascript non potrà recuperare le informazioni in data.xml.

Qui si trovano tutti i file necessari: flash_lightbox_gallery.zip.

Qui la versione dello script modificate e il file php: lightbox_x_flash.js e data_photo_parser.zip.

Lo script modificato può ancora essere utilizzato in modo identico a quanto scritto nel post precedente, usando la nuova funzione flashLightboxSingle(img) al post di flashLightbox(img), nei link della galleria Flash.

Come funziona

Lo scopo dello script è integrare Flash e Lightbox nel modo più semplice possibile. Tutte le informazioni relative alle immagini sono già state specificate nel file data.xml, quindi volevo evitare di doverle ripetere all’interno della pagina con la galleria. D’altra parte, queste informazioni sono necessarie per poter utilizzare Lightbox ed in qualche modo devono essere elaborate tramite javascript.

Una soluzione poteva essere quella di far chiamare direttamente dal codice Flash un funzione javascript di inizializzazione, passando come parametro tutte le info necessarie. Così, però, si è costretti a ripetere le informazioni contenute nel file data.xml, cioé proprio quello che volevo evitare. Inoltre, sarebbe necessario moficare anche il codice sorgente Flash per fare in modo che chiami questa nuova funzione.

Per questi motivi ho preferito adottare un’altra strategia: far parsare il file data.xml da una pagina php che ne renda  disponibile il contenuto in modo “comprensibile” a javascript. In questo modo la funzione di inizializzazione può essere chiamata direttamente dalla pagina html con la galleria, passando come parametro l’indirizzo di questa pagina php. Un ulteriore vantaggio è che anche modificando il contenuto del file data.xml, le differenze si propagano automaticamente tramite la pagina php senza necessità di cambiare nulla manualmente.

data_photo_parser.php

Il funzionamento è molto semplice:

  1. apre il file data.xml e lo legge una riga alla volta;
  2. per ogni riga, controlla se contiene informazioni relative ad un’immagine;
  3. se il controllo è positivo, allora estrae link e titolo e li salva in un array;
  4. per finire usa l’array per stampare la lista delle immagini in formato json.

Il nome del file e la posizione sono specifati nella variabile $imgDataFile:

$imgDataFile = "./data.xml";

Anche se questo file è in formato xml, non uso parser ma preferisco scansionarlo una riga alla volta ed estrarre le informazioni che mi servono tramite espressioni regolari. Questa è probabilmente la soluzione più semplice e veloce per un file xml con una struttura molto semplice, sempre che non si abbia la necessità di validarlo.

//Leggo il file che contiene le informazioni sulle immagini una riga alla volta
while (!feof($fh)) {
  $data = fgets($fh);

  //Controllo se questa riga contiene info su un'immagine
  if (preg_match("/^\s*<thumbItem/",$data)) {
     $img = Array();
     $img['link'] = '';
     $img['title'] = '';

     //Estraggo l'informazione sul link
     if (preg_match('/link="(.*\?)"/',$data,$matches)) {
       $img['link'] = $matches[1];

       //Il link potrebbe contenere una chiamata ad una funzione,
       // nel qual caso elimino la funzione
       if (preg_match('/^\s*javascript:.+?("(.+)")/',$img['link'],$matches)
        || preg_match("/^\s*javascript:.+?('(.+)')/",$img['link'],$matches)) {
          $img['link'] = $matches[2];
       }
     }

     //Se il link e' vuoto proseguo nella scansione del file senza fare niente
     if (preg_match("/^\s*$/",$img['link'])) {
       continue;
     }

     //Titolo
     if (preg_match('/title="(.*?)"/',$data,$matches)) {
       $img['title'] = $matches[1];
     }

     $images[$i++] = $img;
  }
}
fclose($fh);

Ora si pone il problema di come rendere disponibili  le informazioni estratte. In questo caso ho scelto di usare il formato json per due motivi:

  • è più semplice da usare del formato xml quando si vogliono rappresentare strutture dati semplici, come la nostra lista con i dati delle immagini;
  • una stringa in formato json è codice javascript corretto, quindi può essere  interpretata e tradotta direttamente in un array usando la funzione eval().

ATTENZIONE: interpretare direttamente una stringa json con la funzione eval() è molto comodo ma può essere anche MOLTO pericoloso, perché espone ad attacchi di tipo javascript injection. Mostro come mitigare questo pericolo nella funzione javascript che interpreta la stringa json.

In json un oggetto è racchiuso tra parentesi graffe ed è rappresentato da una serie di coppie chiave – valore separate da virgole. In questo coso voglio creare un array di nome lista_immagini (le parentesi quadre servono ad indicare che un oggetto è un array):

{"lista_immagini":[]}

Supponendo che il file data.xml contenga informazioni riguardanti due immagini, con link rispettivamente ‘foto/01.jpg’ e ‘foto/02.jpg’ e titoli ‘Foto 1’ e ‘Foto 2’, la stringa json risultante sarebbe:

{"lista_immagini":[{"link":"foto/01.jog","title":"Foto 1"}
                  ,{"link":"foto/02.jog","title":"Foto 2"}]
}

Ecco finalmente il codice php che crea la stringa json e la stampa:

$sep = false;
foreach ($images as $img) {

  if ($sep) { $json .= ','; }
  else       { $sep = true; }

  $json .= '{';
  $json .= '"link":"'.escapeJSON($img['link']).'"';
  $json .= ',"title":"'.escapeJSON($img['title']).'"';
  $json .= '}';
}

$json .= ']';
$json .= '}';

echo $json;

flash_x_lightbox.js

Come facciamo a far arrivarela stringa json con i dati delle immagini allo script javascript che la deve interpretare?

La soluzione è usare Ajax per connettersi a data_photo_parser.php. Per fortuna Lightbox si appoggia sul framework Prototype che permette di gestire Ajax in modo molto semplice. Di seguito è presentata la funzione initImageListFlashLightbox(url) che non fa altro che collegarsi all’url indicata (dove si trova data_photo_parser.php) e specificare quale funzione tratterà il risultato:

  var myAjax = new Ajax.Request(
      url,
	  {
       method: 'post',
       onComplete: initFlashLightBox
    });

La funzione initFlashLightBox(originalRequest) si occupa di interpretare la stringa json ed inizializzare la galleria per Lightbox:

  1. controlla che la stringa abbia il formato json indicato sopra;
  2. se si, allora traduce la stringa in un oggetto javascript, usando un metodo di Prototype che in qualche modo cerca di prevenire usi “malevoli” (http://www.prototypejs.org/learn/json);
  3. crea un div con display:none e vi pone all’interno un link per ogni immagine, formattato come richiesto da Lightbox.

Ognuno di questi link deve avere un’id unico, che viene ottenuto aggiungendo l’indice della posizione nell’array al prefisso ‘light_link’. Diventa però necessario memorizzare l’indice di ciascuna immagine, in modo che quando si clicca su una foto nella gallery sia poi possibile passare come attributo a slight.tart() il link corretto. Per questo si usa l’oggetto var img2index = new Object();

function initFlashLightBox(originalRequest) {

  if (/^\{"lista_immagini":\[\{"link":".+?","title":".+?"\}(,\{"link":".+?","title":".+?"\})*\]\}/.test(originalRequest.responseText)) {
    var result = originalRequest.responseText.evalJSON(true);
    var lista_immagini = result.lista_immagini;

    if (lista_immagini.length>0) {
      var objBody = $$('body')[0];
      var div = Builder.node('div',{style:'display:none;'});
      objBody.appendChild(div);
      for (var i=0; i<lista_immagini.length; i++) {
        var img = lista_immagini[i];
        img2index[img['link']] = i;
        div.appendChild(
          Builder.node('a',{id:'light_link'+i, href: img['link'], 
                title:img['title'], rel: 'lightbox[flash_gallery]'})
        );
      }
      
    }    
  } else {
    alert('Errore nel reperire la lista delle immagini!');
  }
}

Infine, bisogna definire la funzione da usare per ogni immagine della galleria Flash, che si occupa semplicemente di chiamare il metodo slight.tart() passando il link corretto:

function flashLightbox(img) {
 var id = 'light_link'+img2index[img];
 light.start($(id));
}

Flash e Lightbox

11 gennaio 2009 2 commenti

Come integrare in modo semplice una photo gallery fatta in Flash con Lightbox.
Per vedere un esempio del risultato potete cliccare sull’immagine qui sotto:

Demo integrazione flash-lightbox

Demo integrazione flash-lightbox

Come utilizzare questa soluzione nelle tue pagine

Questa soluzione utilizza una versione di Lightbox leggermente modificata che si usa esattamente come Lightbox, cioé importando lo script ed il foglio di stile nell’header dalla pagina html:

<link rel="stylesheet" href="css/lightbox.css" type="text/css" media="screen"/>
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="js/scriptaculous.js?load=effects,builder"></script>
<script type="text/javascript" src="js/lightbox_x_flash.js"></script>

Prototype è un framework javascript, mentre Scriptaculous è una libreria che permette di creare effetti grafici con javascript in modo semplice e veloce. Entrambe sono richieste da Lightbox.

Lightbox richiede che i link relativi alle immagini che si vogliono visualizzare ingrandite contengano il tag rel=”lightbox”, o rel=”lightbox[nome_galleria]” per le gallerie di immagini. Questo passaggio, però, può essere tranquillamente ignorato perché è lo stesso script modficato che si occupa di creare il link per Lightbox.

Infine, le immagini nella galleria Flash devono essere collegate ad un link che attiva la funzione flashLightbox, che vuole come parametro il path e il nome della foto:

javascript:flashLightbox('foto/01.jpg')

Qui si trovano tutti i file necessari: flash_lightbox.zip

Se invece ti interessa solo lo script modificato: lightbox_x_flash.js

Come funziona

Normalmente Lightbox funziona in questo modo:

  • aspetta che sia stato completato il caricamento della pagina html;
  • cerca tutti i link che contengono rel=”lightbox” e registra le funzioni da eseguire quando sono selezionati;
  • inserisce nella pagina i div usati per l’overlay dell’immagine.

L’idea, molto semplice, è di creare un link invisibile e, ogni volta che si clicca su un’immagine nella galleria flash, cambiare al volo l’immagine di questo link e poi usare Lightbox per visualizzare l’immagine in modo “standard”.

Questo link viene creato direttamente dallo script modificato (nella funzione initialize) ed ha questa forma:

<div style="display:none">
  <a href="#" rel="lightbox" id="light_link">
    <img src="" alt="immagine" id="light_img_src" />
  </a>
</div>

Nel div usato come contenitore è specificato display:none perché non vogliamo che l’immagine contenuta diventi visibile: deve potersi vedere solo nella galleria o nell’overlay.
Al link e all’img all’interno del link sono stati assegnati degli id unici che saranno usati per richiamare questi elementi ed impostare dinamicamente l’immagine negli attributi href e src, rispettivamente.

Infine è stata aggiunta la funzione che gestisce i click nella galleria Flash:

function flashLightbox(img) {
 $('light_link').href = img;
 $('light_img_src').src = img;
 light.start($('light_link'));
}

La funzione $ è definita in Prototype e serve a richiamare l’elemento con l’id specificato. In questo caso è utilizzata per modificare l’immagine del (e nel) link.

La versione standard di Lightbox crea un oggetto senza assegnarlo esplicitamente ad una variabile. Qui, però, è necessario chiamare il metodo start() (che fa partire la visualizzazione nell’overlay)  ogni volta che si clicca su un’immagine nella galleria. Per questo motivo il comando di inizializzazione di Lighbox (ultima riga di lightbox.js)  è stato modificato in modo da fornire un oggetto sul quale sia possibile richiamare il metodo esplicitamente:

document.observe('dom:loaded', function () { light = new Lightbox(); });

Storia

Qualche giorno fa un amico mi ha chiesto di dare un’occhiata ad uno script Javascript che aveva scaricato da internet.
Aveva bisogno di integrare una photo gallery fatta in Flash con Lightbox. Lo script scaricato permetteva di farlo ma ingrandiva l’immagine tutta d’un botto, senza gli effetti grafici carini tipici di Lightbox.
Mettendo a confronto il sorgente originale di Lightbox con quello modificato per Flash, mi sono accorto che la libreria era stata pesantemente modificata e la parte degli effetti era stata eliminata.
Ho pensato che invece di riscrivere lo script di Lightbox o modificarlo così pesantemente, doveva esserci un modo semplice per usare le funzionalità di Lightbox così come sono. In effetti, il modo c’è ed è anche MOLTO semplice.