Pulire codice HTML eccetto alcuni tag in Java sfruttando Jsoup

Postato da ROb | nella categoria Java, Sviluppo web | venerdì, 24 settembre 2010

4

Abbiamo già parlato di Jsoup in questo precedente post.
In due parole Jsoup è una potente libreria di parsing html/xml che permette di leggere e manipolare facilmente stringhe e porzioni anche grandi di codice html/xml.

Oggi voglio farvi vedere come possiamo sfruttarla per pulire del codice html (proveniente ad esempio dal campo di un web form) mantenendo solamente i tag che vogliamo.

Questa opzione è infatti molto utile nel caso in cui si voglia permettere agli utenti di arricchire il dato con la formattazione classica dei testi: bold, italico, … senza però rischiare di esporci al pericoloso XSS attack che consiste nell’inserire codice Javascript malevolo che punta verso qualche altro sito.

Con poche righe di codice Jsoup ci permette di definire la nostro lista di tag partendo da alcuni set predefiniti.

        ...
        String unsafe = "<p><a href='http://www.bits4beats.com/' onclick='stealCookies()'>Benvenuto <b>Roberto Rossi</b></a> lo so che che sei di Macerata! <br/>... sei proprio un <i>impiastro</i>!</p>";

		Whitelist myWL = Whitelist.simpleText();
		myWL.addTags("br", "hr");

		String safe = Jsoup.clean(unsafe, myWL);
		System.out.println("stringa sicura: " + safe);
        ...

Se lo eseguiamo otterremo la seguente stringa:

stringa sicura: Benvenuto <b>Roberto Rossi</b> lo sei che sei di Macerata! <br /> sei proprio un <i>impiastro</i>!

In pratica abbiamo detto a Jsoup di partire dalla lista accettata simpleText() che contiene i tag essenziali della formattazione a cui abbiamo aggiunto il tag br e il tag hr.

Output di una query in formato html

Postato da ROb | nella categoria Linux, MySQL | martedì, 4 maggio 2010

0

Il comando mysql permette di generare in modo nativo l’output di una query in formato html.

Per farlo è sufficiente sfruttare l’opzione -html.
Per provare tale funzionalità creiamo un file chiamato script.sql con la seguente interrogazione:

SELECT * FROM user u;

Ora lanciamo la query nel db “mysql” con l’utente root:

mysql -u mysql -p mysql --html < script.sql > output.html

E’ possibile che vi venga richiesta la password dell’utente root se è stata impostata.
Ora possiamo aprire il file output.html con un comunissimo browser.

Sfruttare le Google Picasa API per generare una galleria di immagini

Postato da ROb | nella categoria Java, Linux, Sviluppo web | giovedì, 22 aprile 2010

0

Chi non conosce il bellissimo e potentissimo software Picasa?
E’ sempre più facile vederlo installato nei desktop degli utenti come rimpiazzo dell’applicazione di default di visualizzazione immagini. Anche io che uso Linux, nonostante la scelta di software di gestione e visualizzazione immagini sia molto ricca e il fatto che non è un’applicazione nativa Linux ma emulata tramite Wine, ho eletto Picasa come il “mio” software di gestione immagini.

Google Picasa API

Non voglio però dilungarmi molto in merito ai pregi di questo gestore di immagini. L’oggetto di questo post è la possibilità di sfruttare il servizio web ad esso associato (ma anche utilizzabile in modo indipendente) chiamato Picasa Web Albums.

Picasa Web Albums è un servizio web che ci permette di caricare, organizzare i nostri album di foto e di condividerli con chi vogliamo. Migliorato di mese e mese, si è arricchito ultimamente anche della capacità di riconoscere i volti delle persone nelle foto in modo automatico.

Questo servizio Google, come molti altri dell’azienda di Mountain View, espone delle API, cioè delle librerie utilizzabili dagli sviluppatori, che, dopo un’opportuna autenticazione, permettono di leggere e manipolare i dati del servizio stesso.
Nel caso ad esempio delle API di Picasa Web Albums possiamo richiedere la lista dei nostri album oppure ricevere la lista delle immagini contenuto in uno specifico album oppure caricare una foto nell’album e così via.

Oggi voglio parlarvi di una piccola applicazione scritta in Java, che sfrutta l’API del servizio per questo linguaggio, in grado di generare una porzione di codice html partendo da una lista di immagini di uno specifico album caricato in Picasa Web Albums.

Per poter eseguire l’applicazione dovete avere opportunamente scaricato e configurato il pacchetto delle API di Google, impostando correttamente le dipendenze verso i file jar dei servizi che utilizzate.
Per comodità comunque allego in fondo all’articolo l’intero progetto Eclipse zippato.

Il programma deve essere eseguito in due passaggi.
Nel primo passaggio, dopo aver opportunamente modificato l’utente e password, bisogna attendere che il programma stampi a video la lista degli album caricati e i relativi id. Scegliamo a questo punto l’id del nostro album e impostiamolo come valore della variabile albumId.

Rieseguiamo quindi il programma (secondo passaggio) e a questo punto potremmo vedere un output decisamente più ricco che contiene un html generato partendo dai dati e dalle immagini contenuti nell’album specificato.
L’html viene generato utilizzando il motore di templating Velocity a cui è stato passato l’oggetto feed contenente tutti i dati del nostro album. Il file del template è album.vm che si trova all’interno della cartella del progetto di nome templates.
Ovviamente potete modificare a vostro piacimento tale template utilizzando a pieno il modello dei dati messo a disposizione da Google.

Di seguito propongo il codice Java del programma, ovviamente con delle credenziali fasulle:

import java.io.StringWriter;
import java.net.URL;
import java.util.Properties;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import com.google.gdata.client.photos.PicasawebService;
import com.google.gdata.data.photos.AlbumEntry;
import com.google.gdata.data.photos.AlbumFeed;
import com.google.gdata.data.photos.UserFeed;

public class Main {
	public static void main(String[] args) {
		try {
			String username = "<username>";
			String domain = "@gmail.com";
			String password = "<password>";
			String albumId = "<empy>";

			PicasawebService myService = new PicasawebService(username);
			myService.setUserCredentials(username + domain, password);

			URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" + username + "?kind=album");

			UserFeed myUserFeed = myService.getFeed(feedUrl, UserFeed.class);
			for (AlbumEntry myAlbum : myUserFeed.getAlbumEntries()) {
			    System.out.println(myAlbum.getTitle().getPlainText() + " - " + myAlbum.getId());
			}

			URL feedAlbumUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" + username + "/albumid/" + albumId);

			AlbumFeed feed = null;
			try {
				feed = myService.getFeed(feedAlbumUrl, AlbumFeed.class);
			}
			catch (com.google.gdata.util.ResourceNotFoundException rnfe) {
				System.out.println("\nalbum non trovato, controlla l'id");
			}

			if (feed != null) {
				Properties velProps = new Properties();
				velProps.load(Main.class.getResourceAsStream("velocity.properties"));
				Velocity.init(velProps);

				VelocityContext context = new VelocityContext();

				context.put("feed", feed);

				Template template = null;

				try {
				   template = Velocity.getTemplate("templates/album.vm");
				}
				catch( Exception e ) {
					e.printStackTrace();
				}

				StringWriter sw = new StringWriter();

				template.merge( context, sw );

				System.out.println();
				System.out.println(sw);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Questo è invece il codice del template album.vm :

		<h4>$!feed.title.plainText</h4>
		<ul class="gallery clearfix">
#foreach($photo in $feed.getPhotoEntries())
		    <li>
		       <a rel='prettyPhoto[$feed.id]' title="$photo.getDescription().getPlainText()" href='$photo.getMediaContents().get(0).getUrl()' >
		          <img src='$photo.getMediaThumbnails().get(0).getUrl()' width='100' class='thumb' border='0' />
		       </a>
		    </li>
#end
		</ul>

Ecco infine il file compresso contenente il progetto completo in formato Eclipse.

Invio di mail in Java

Postato da ROb | nella categoria Java | mercoledì, 17 marzo 2010

0

Qualsiasi programmatore, prima o poi, ha necessità di inviare delle mail.

In Java esistono moltissimi modi per farlo e uno di questi è utilizzando la libreria Apache Commons Email.
Le librerie Apache Commons sono state scritte per permettere a tutti i progretti Jakarta ma non solo di poter disporre di componenti, pronti all’uso, integrabili nei rispettivi progetti.
Uno di questi componenti è proprio Commons Email, utilizzabile per inviare mail in formato normale o anche html, con o senza attachment.

Utilizzare tale libreria, per chi ha un minimo di confidenza con Java, è davvero semplice.
Con 7 righe di codice è possibile inviare una mail in formato html senza allegati.
Ecco un esempio:

HtmlEmail email = new HtmlEmail();
email.setSubject("Una nuova mail per Roberto Rossi");
email.setHtmlMsg("Ecco un testo in formato <b>HTML</b><br/>");
email.setHostName("host");
email.setFrom("no-reply@bits4beats.it");
email.addTo("user@bits4beats.it");
email.send();

Se invece vogliamo spedire una mail in formato testo standard basta usare l’oggetto SimpleEmail anziché HtmlEmail e il metodo setMsg anziché setHtmlMsg, in questo modo:

SimpleEmail email = new SimpleEmail();
email.setSubject("Una nuova mail per Roberto Rossi");
email.setMsg("Ecco un testo in formato standard");
email.setHostName("host");
email.setFrom("no-reply@bits4beats.it");
email.addTo("user@bits4beats.it");
email.send();

Infine, se abbiamo la necessità di aggiungere degli allegati alla mail, nel primo caso possiamo utilizzare semplicemente il metodo attach passando come parametro un oggetto EmailAttachment costruito così:

EmailAttachment attachment = new EmailAttachment();

String filePath = "pathtofile";
attachment.setPath(filePath);
attachment.setDisposition(EmailAttachment.ATTACHMENT);
attachment.setDescription("description for this attachment");

emailAdmin.attach(attachment);

Mentre nel secondo caso dobbiamo utilizzare il tipo di email MultiPartEmail anziché SimpleEmail per poter disporre del metodo attach utile per l’inclusione di allegati.

Decisamente comodo e potente.

Come caricare un file sul server in AJAX

Postato da ROb | nella categoria Sviluppo web | martedì, 2 marzo 2010

2

I form html tradizionali negli anni hanno dimostrato numerosi limiti. Quelli più notevoli erano impossibilità di effettuare validazione server side, impossibilità di gestire dei campi autopopolati durante la digitazione, post completo del form senza possibilità di parzializzazione.

Ajax file upload

Tutte queste cose hanno reso il web uno strumento difficilmente utilizzabile per applicazioni che richiedessero un’elevata interazione con l’utente.
Negli ultimi anni AJAX ha rivoluzionato i tradizionali form html permettendo ai programmatori di poter dialogare agilmente con il loro backend e permettendo agli utenti di godere una più dinamica esperienza web.

Recentemente ho avuto la necessità di gestire un campo file in un form ajax. Per form ajax intendo un form che invia i suoi dati serializzati al server e non fa il post dell’intera pagina.
La tecnologia utilizzata era jQuery così ho cercato un plugin che si integrasse bene con tale tecnologia e ho trovato AJAX Upload.

Inseriamo innanzitutto gli script javascript nell’header necessari alla definizione di jQuery e del plugin AJAX Upload:

...
<head>
...
<script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="js/ajaxupload.js"></script>
...
</head>
...

A questo punto bisogna creare il div che cliccato avvierà la procedura di caricamento file:

<div id="upload_button">Upload</div>

Infine dobbiamo inizializzare i campi di tipo file all’interno del nostro form:

$(document).ready(function() {
    new AjaxUpload('upload_button_id', {action: 'uploadServletPath'});
}

Al rendering della pagina cliccando sul div upload_button si aprirà la finestra di dialogo per la scelta del file e il file sarà direttamente inviato via POST (senza però il POST dell’intera pagina) alla servlet di upload che abbiamo specificato nell’inizializzazione del componente.

Nel costruttore di inizializzazione possiamo anche passare altri parametri di configurazione e gestire alcuni eventi precedenti (o successivi) al caricamento utili per valorizzare altre variabili del form o per visualizzare la progressione del caricamento o il nome del file caricato sul server.
E’ anche possibile stabilire il tipo di risposta ricevuta dal server: html, xml o json.

Per chi fosse interessato ecco il codice Java necessario al salvataggio del file:

/*
 *	author: Roberto Rossi
 */
package it.bits4beats.servlets;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;
import java.util.Iterator;
import java.util.List;

public class UploadServlet extends HttpServlet {
	private static Log log = LogFactory.getLog(UploadServlet.class);
	public static final String DEFAULT_UPLOAD_DIR_INIT_PARAM = "DefaultUploadDir";

	/**
	 *
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * This is a default constructor for UploadServlet class.
	 *
	 */
	public UploadServlet(){
	}

	public void init() throws ServletException {
		super.init();
	}

	/**
	 * Receive a file to be uploaded and save it into filesystem
	 */
	public void doGet(HttpServletRequest request,
			HttpServletResponse response)
	throws ServletException,java.io.IOException {
		log.info("method not supported!");
	}

	public void doPost(HttpServletRequest request,
			HttpServletResponse response)
	throws ServletException,java.io.IOException {

		String defaultUploadDir = getServletConfig().getInitParameter(DEFAULT_UPLOAD_DIR_INIT_PARAM);
		if (defaultUploadDir != null && !defaultUploadDir.equals("")) {
			log.debug("> default upload dir: " + defaultUploadDir);
		}

		/* the uploading user */
		String username = request.getParameter("username");
		/* max size of file upload */
		String sizeParam = request.getParameter("sz");
		Long maxBytes = null;
		if (sizeParam != null && !sizeParam.isEmpty()) {
			try {
				/* max 5Mb file uploaded */
				maxBytes = new Long(5242880);
			}
			catch (NumberFormatException nfe) {
				/* do nothing */
			}
		}

		log.debug("remote user: " + request.getRemoteUser());
		log.debug("user principal: " + (request.getUserPrincipal() == null ? "" : request.getUserPrincipal().getName()) );
		log.debug("username: " + username);

		String status = "true";
		String message = "";
		String savedfilename = "";
		String attachmentname = "";
		String originalFilename = "";

		/* reading file streams */
		boolean isMultipart = ServletFileUpload.isMultipartContent(request);

		if (isMultipart) {

			FileItemFactory factory = new DiskFileItemFactory();
			ServletFileUpload upload = new ServletFileUpload(factory);
			List items = null;
			try {
				items = upload.parseRequest(request);

				if (items != null && items.size() > 0) {

					File uploadDirFile = null;
					String uploadDirFileRelativePath = null;

					uploadDirFile = new File(getServletContext().getRealPath(defaultUploadDir));
					uploadDirFileRelativePath = defaultUploadDir;

					Iterator itr = items.iterator();

					FileItem fileItem = null;
					FileItem filenameItem = null;

					while(itr.hasNext()) {
						FileItem item = (FileItem) itr.next();

						if (!item.isFormField()) {
							fileItem = item;
						}
						else {
							if (item.getFieldName().equals("attachmentFilename")) {
								filenameItem = item;
								log.debug("filenameItem name: " + filenameItem.getFieldName() + " valore: " + filenameItem.getString());
							}
						}
					}

					if (fileItem != null) {
						/* there is a file item in the request */
						String fileName = fileItem.getName();
						log.debug("fileItem name: " + fileName);

						if (fileItem.getName().contains("\\")) {
							log.debug("Windows file");

							/* windows file path */
							fileName = fileItem.getName().substring(fileItem.getName().lastIndexOf("\\") +1);
							log.debug("fileItem name: " + fileName);
						}

						File fullFile  = new File(fileName);
						File savedFile = new File(uploadDirFile, fileName);

						if (savedFile != null) {

							/* check file size */
							if (maxBytes != null) {
								if (fileItem.getSize() > maxBytes.longValue()) {
									FileTooBigException ftbe = new FileTooBigException();
									ftbe.setMaxSize(new Long(maxBytes.longValue()/1024/1024).toString());
									throw ftbe;
								}
							}

							if (savedFile.exists()) {
								/* we need to create a file variant, save it and send it to the user */
								String fileNameWithoutExtension = savedFile.getName().substring(0, savedFile.getName().lastIndexOf("."));
								originalFilename = fileNameWithoutExtension;
								String suffix = savedFile.getName().substring(savedFile.getName().lastIndexOf(".") + 1);

								File newFile = File.createTempFile(fileNameWithoutExtension + "_", "." + suffix, uploadDirFile);
								fileItem.write(newFile);

								log.debug("newFile: " + newFile.getAbsolutePath());
								status = "true";
								message = "OK " + newFile.length() + " byte salvati";

								/* relative file path */
								savedfilename = uploadDirFileRelativePath + newFile.getName();
								String filenameWithExt =  extractFilenameWithExtensionFromPath(savedfilename);
								attachmentname = extractFilename(filenameWithExt);
							}
							else {
								log.debug("writing file to: " + savedFile.getAbsolutePath());
								originalFilename = extractFilename(extractFilenameWithExtensionFromPath(savedFile.getAbsolutePath()));
								fileItem.write(savedFile);

								status = "true";
								message = "OK " + savedFile.length() + " byte salvati";

								/* relative file path */
								savedfilename = uploadDirFileRelativePath + fullFile.getName();
								String filenameWithExt =  extractFilenameWithExtensionFromPath(savedfilename);
								attachmentname = extractFilename(filenameWithExt);
							}
						}
						else {
							log.error("cannot create file");
							status = "false";
							message = "Errore generico di salvataggio";
						}
					}

				}
				else {
					log.debug("no item to be saved");
				}
			}
			catch (FileTooBigException ftbe) {
				status = "false";
				message = "File troppo grande, dimensione massima (Mb): " + ftbe.getMaxSize();
			}
			catch (Exception e1) {
				e1.printStackTrace();
				log.error("error in uploading");

				status = "false";
				message = "Errore generico di salvataggio";
			}
		}

		PrintWriter out = response.getWriter();

		String responseMessage = "{\"success\":" + status + ",\"message\":\"" + message + "\",\"savedfilename\":\"" + savedfilename + "\",\"attachmentname\":\"" + attachmentname + "\",\"originalfilename\":\"" + originalFilename + "\"}";
		log.debug("responseMessage: " + responseMessage);
	    out.println(responseMessage);
	}

	/**
	 * This method extract filename (with extension) from filepath
	 *
	 * @param filepath
	 * @return filename without extension
	 */
	public static String extractFilenameWithExtensionFromPath(String filepath) {
		if (filepath != null) {
			int index = filepath.lastIndexOf("/");

			if (index > 0 && index < (filepath.length() -1)) {
				return filepath.substring(index + 1);
			}
			else return null;
		}
		else {
			return null;
		}
	}

	/**
	 * This method extract filename (without extension) from filename with extension
	 *
	 * @param filepath
	 * @return filename without extension
	 */
	public static String extractFilename(String filenameWithExtension) {
		if (filenameWithExtension != null) {
			int index = filenameWithExtension.lastIndexOf(".");

			if (index > 0) {
				return filenameWithExtension.substring(0, index);
			}
			else return filenameWithExtension;
		}
		else {
			return null;
		}
	}

	class FileTooBigException extends Exception {
		private static final long serialVersionUID = 1L;

		private String maxSize = null;

		public String getMaxSize() {
			return maxSize;
		}

		public void setMaxSize(String maxSize) {
			this.maxSize = maxSize;
		}

		public FileTooBigException() {
			super();
		}

		public FileTooBigException(String message) {
			super(message);
		}
	}

}

Nel sito del plugin trovate anche una demo completa e funzionante di AJAX upload.

L’insetto di fuoco (Firebug) invade anche Chrome!

Postato da ROb | nella categoria Sviluppo web | lunedì, 8 febbraio 2010

0

Parlare di Firebug è ormai inutile, lo conoscono tutti.

Nuovo logo Firebug

E’ invece di qualche giorno fa (esattamente il 4 febbraio) la notizia della versione 1.3 Lite di Firebug per Chrome.
La versione Lite consente agilmente di fare debugging del codice css e html della pagina web (ma non di quello Javascript) ma senza poter intervenire direttamente e modificarla al volo.

Qui sotto potete vedere lo screenshot dell’estensione appena installata e funzionante sul mio Chrome per Linux.

Firebug in azione su Chrome per Linux

Estrarre informazioni da un sito attraverso jsoup, html parser per Java

Postato da ROb | nella categoria Java, Sviluppo web | lunedì, 1 febbraio 2010

0

Ieri mi sono divertito a testare una nuova libreria in Java per il parsing di documenti HTML/XML.

Jsoup, questo il nome particolare di tale libreria, permette con una sintassi a selettori (stile JQuery) di inviduare specifici elementi del DOM di un documento html, sia locale che remoto.

Jsoup, html java parser

Dopo aver scaricato la libreria dal sito ufficiale e averla importata nel mio workspace Eclipse, ho modificato leggermente uno degli esempi proposti per provare a estrarre i titoli e gli url di tutti i post dall’homepage del mio blog (http://www.bits4beats.it).

Beh… il risultato è stato veramente interessante e ho molta voglia di farci qualcosa di più costruttivo. Pensavo addirittura a uno script o un programmino per visualizzare il posizionamento di un sito per particolari keyword su più motori di ricerca. Se avrò voglia, tempo e capacità per farlo pubblicherò un post in merito.

Per ora godetevi queste 10 righe di codice:

		Document doc = Jsoup.parse(new URL("http://www.bits4beats.it/"), 2000);

		Elements resultLinks = doc.select("div.post-title > h2 > a");
		System.out.println("Ultimi post di www.bits4beats.it con jsoup");
		for (Element link : resultLinks) {
			System.out.println();
			String href = link.attr("href");
			System.out.println("Titolo: " + link.text());
			System.out.println("Url: " + href);
		}

Nella riga 1 si crea un oggetto Document facendo il download dell’homepage del blog aspettando al massimo 2000 millisecondi.
Successivamente si cercano tutti gli elementi di tipo a figli diretti di elementi di tipo h2 figli diretti a loro volta di elementi div con classe post-title.
Per ognuno di questi elementi trovati, alle righe 8 e 9, vengono stampati rispettivamente l‘anchor text (cioè il testo cliccabile del link) e l’url del post.

Un ringraziamento allo sviluppatore del progetto, Jonathan Hedley, che ha corretto in tempi brevissimi un piccolo errore nel codice del jsoup Cookbook.

controllare il peso del bimbo tramite html e jquery

Postato da ROb | nella categoria Sviluppo web | lunedì, 18 gennaio 2010

4

Poco giorni dopo che e’ nata Giulia ho avuto l’esigenza di misurare con precisione e accuratezza il suo peso e tenerne traccia nel tempo.

All’inizio avevamo memorizzato tutto in un quadernino ma poi il quadernino diventava spesso introvabile e non aveva la cura che meritava, quindi, entusiasmato da un bellissimo plugin JQuery di nome flot ho deciso di scrivere una pagina html locale che potesse visualizzare il grafico del peso, una tabella con gli ultimi dati inseriti e che fosse allo stesso tempo semplice da aggiornare.

Ecco il risultato:

Pagina Peso Giulia

Il tutto consiste in una cartella con dentro un file html di nome peso_giulia.html e alcuni file di jquery e di alcuni suoi plugin (compreso flot).
Per impostare i pesi nei vari giorni e’ sufficiente aggiornare l’array di dati d2.
Aggiungendo o rimuovendo record come questo.

[(new Date("2009/07/01")).getTime(),3120]

Il primo campo contiene la data delle misurazione, il secondo il peso in grammi.

Dopo aver salvato il file lo sì puo’ aprire con Firefox per vedere il risultato, ovviamente potete cambiare anche tutto il resto, tra cui il nome di vostro figlio :-) .

download dello zip contenente tutti i file