giovedì 7 febbraio 2008

Free music downloads site in chaos as record giants pull out| News | This is London

Oggi finalmente si poteva scaricare il software di qtrax per poter scaricare legalmente musica. POter scaricare musica gratis da un catalogo di 50000 canzoni, il tutto pagato da qualche banner pubblicitario, sembrava troppo bello... Sembrava in quanto pare che le case discografiche si siano tirate fuori dal business lasciando il sito nel caos!
Free music downloads site in chaos as record giants pull out| News | This is London

lunedì 4 febbraio 2008

Una casella di posta da gettare

Nello sviluppo di Naturalads.net, mi sono trovato a cercare informazioni su Zend Framework in molteplici siti e quasi tutti mi chiedevano la registrazione per poter postare. Solitamente la registrazione richiede necessariamente l'inserimento del proprio indirizzo email. Si tratta di un piccolo pegno che si deve pagare per accedere ad una nuova sorgente di informazione. Questo, in genere non è un problema, se non fosse che a volte ci si ritrova sommersi dallo spam.

Una soluzione è quella di utilizzare dei servizi di email usa e getta (disposable email) che forniscono un indirizzo di posta per pochi minuti, giusto il tempo per effettuare la registrazione, recuperare il link di attivazione, e non ricevere lo spam! L'unico problema è che molte volte questi indirizzi sono 'bannati' e non accettati dai principali siti web. Inoltre non è possibile riutilizzare lo stesso indirizzo alcuni giorni dopo.

Allora, visto che il php è pronto e caldo, ho pensato che si può perdere un giorno di tempo per crearne uno nuovo... Beh già che c'ero ne ho fatti un paio. Qui vi segnalo dispose.it un servizio di email usa e getta, appena nato, che non è sicuramente bloccato da nessune sito e che, tra l'altro, permette di indicare il proprio indirizzo preferito.

Il servizio è troppo semplice per meritare una sola riga di spiegazione. Basta accedere a questo indirizzo di posta usa e getta e fare CTRL-V nel modulo di registrazione del sito in cui ci si vuole registrare.
Sul sito di dispose.it apparirà presto l'email ricevuta!

Da domani, o meglio da stanotte, si riprende lo sviluppo di naturalads... a proposito penso che utilizzerò Zend Form per le mie prossime forms di naturalads.net e quindi sto installando ZF 1.5 (preview naturalmente).

martedì 29 gennaio 2008

Due note sulla sicurezza (worst practices) #1

Ci sono molte idee nuove nella sicurezza dei computer. Ogni mese escono nuove soluzioni e nuove release software aggiungono nuove funzionalità a vecchi apparati. Ma perchè, anche spendendo un sacco di soldi, ci sono ancora problemi di sicurezza?

Forse perchè nel settore esiste ancora una mentalità radicata, e si continua sempre a fare gli stessi errori. Esistono infatti alcune tecniche che possono far sì che il vostro nuovo ASIC da 100.000 Euro, che sniffa, riordina, ripulisce milioni di pacchetti al secondo possa essere perfettamente trasparente e inutile per proteggervi dagli attacchi.

IDEA 1 - Aperto di Default

Questa sicuramente è un'idea che prende numerose forme. Molto diffusa in svariate forme e difficile da sdradicare. Esattamente come i cibi senza calorie, che non si come, fanno ingrassare comunque.

Il tutto risale ai primi tempi della rete quando per mettere in sicurezza un sistema non si faceva altro che impedire l'accesso via Telnet, via FTP e via Rlogin e si lasciava tutto il resto aperto di default. Immagino che vi sia noto il procedimento...

Quando si scopriva una nuova vulnerabilità allora la decisione era, prima di essere hackerati naturalmente, chiudiamo anche questa porta oppure no? Il tutto supportato dal convincimento, 'tanto noi non siamo interessanti per gli hacker'.

Nel 1990, anche con l'avvento dei worms che non guardano in faccia a nessuno, questa idea di 'Aperto di Default' è stata portata avanti lo stesso, senza alcun ritegno e sopravvive ancora oggi.

Ma il concetto di 'Aperto di Default' si applica anche ad altri campi, come ad esempio nell'esecuzione del codice. Su qualsiasi computer qualsiasi codice cliccato può essere eseguito di default se non interviene un qualcosa che lo blocca. Questo qualcosa può essere un Antivirus, un Antispyware, una policy del sistema operativo. Sul mio pc ho circa 20 programmi che uso abitualmente, non capisco perchè il sistema operativo si permetta di eseguire pezzi di virus, mai utilizzati senza nemmeno chiedermi il permesso... Anche questo è 'Aperto di Default'.

Il contrario di questa idea è Chiuso di default che è senz'altro una buona idea, in quanto chiude le porte a tutto quello che vi è sconosciuto e permette di dormire sogni tranquilli.

giovedì 24 gennaio 2008

Aspettando Zend Framework 1.5

Una nuova versione di Zend Framework è in arrivo... oggi dovrebbe essere in giro una prerelease e tra 3 settimane dovremmo vedere una prima Release Candidate.

In pratica le principali modifiche riguardano:

* Zend_Auth_Adapter_Ldap
* Zend_Build/Zend_Console
* Zend_Controller ha nuovi action helpers, tra cuiContextSwitch/AjaxContext, Json, e AutoComplete
* Zend_Form
* Zend_InfoCard
* Zend_Layout
* Zend_OpenId
* Zend_Search_Lucene migliorato, comprende ricerca con wildcard, ricerca per intervalli di date, e supporto per il formato indice di Lucene 2.1
* Zend_View migliorato, comprende azioni, partials, e placeholders
* Zend_Pdf con supporto UTF8
* UN sacco di correzioni di bug e migliore documentazione

Staremo a vedere e saremo pronti a convertire il codice scritto. Io sono soprattutto curioso per Zend_form, che leggendo nelle proposte, permette di gestire facilmente le form e il tanto atteso Zend Layout che permette di trattare le form come avviene in Ruby.

martedì 22 gennaio 2008

Estendere il model con nuovi metodi

ovvero
visualizzare al meglio le colonne del proprio DB

Il mio problema di oggi è il seguente:
Ho una tabella che contiene una colonna che si chiama isChecked di tipo INT che può contenere 0 oppure 1.
Il risultato che voglio ottenere è quello di chiamare un metodo del tipo

$table->status()

che restituisca "verificato" oppure "da verificare" a seconda del valore contenuto nella colonna.
Naturalmente il tutto deve essere fatto in modo naturale ovvero col minimo sforzo possibile.

Vediamo allora di capire dove andare a mettere il metodo magico che ci trasforma il risultato.
Quando si estraggono i dati da db utilizzando Zend_DB_table, quello che si ottiene indietro è un Rowset, ovvero una collezione di Rows.
Con Zend Framework c'è la possibilità di definire una classe personalizzata di Righe (Rows) che estende quella base in modo da aggiungere i metodi che si desiderano.


Passo 1 - estendere la riga


Per aggiungere nuovi metodi alla riga si crea, quindi, una classe che estende Zend_Db_Table_Row_Abstract

nel mio caso uso la seguente:

<?php

class Naturalads_WebsiteRow extends Zend_Db_Table_Row_Abstract
{
/**
* Find the Row in the current Rowset with the
* greatest value in its 'updated_at' column.
*/
public function status()
{
$output = ($this->isChecked == 0)? "da verificare":"verificato";
return $output;
}
}

la classe la salvo naturalmente in library/Naturalads/WebsiteRow.php dove l'autoloader la può trovare comodamente

Passo 2 - aggiungere la classe al modello


Occorre aggiungere al model la classe di riga creata nel passo 1. Questo è molto più semplice del previsto, basta infatti aggiungere al proprio modello una variabile protetta:

protected $_rowClass = 'Naturalads_WebsiteRow';

in pratica io uso il seguente modello che basta e avanza per i miei scopi:

<?php
class Website extends Zend_Db_Table {
protected $_name = 'Websites';
protected $_rowClass = 'Naturalads_WebsiteRow';
}

devo dire che il modello lo salvo come al solito in models/Website.php

Passo 3 - Usare il modello


Adesso il modello è pronto per l'uso e contiene la mia estensione di classe e il mio metodo. L'unica cosa che mi manca è solo richiamarlo. Vediamo come è possibile istanziare il tutto nel controller e poi visualizzarlo in una view:

Nel mio controller posso utilizzare qualcosa del genere


...
$website = new Website();
$this->view->assign('websites', $website->fetchAll()); // fetchAll's signature is unmodified
...


quindi nella view uso qualcosa del genere per richiamare il mio metodo personale:


<?php foreach($this->websites as $website) : ?>
<tr>
<td><?php echo $this->escape($website->url);?></td>
<td><?php echo $this->escape($website->status());?></td>
</tr>
<?php endforeach; ?>


e adesso...


Forse non è velocissimo, ma in questo modo posso 'giocare' con tutte le colonne direttamente nel modello di dati, aggiungere colonne virtuali e richiamare il tutto sempre come metodi dalla view senza dover toccare il controller in alcun modo.
Un ottima cosa per la manutenzione del progetto. Se un domani dovrò gestire il nuovo stato "verificato senza successo", basterà inserire il valore -1 nel campo isChecked e modificare il metodo status(). Tutta l'applicazione continuerà a funzionare e a mostrare il nuovo stato senza problemi.

lunedì 21 gennaio 2008

Accetta le condizioni!

Quando si esegue la verifica dei campi delle form utilizzando Zend Framework a volte ci si scontra con dei piccoli noiosi problemi. Ad esempio nella verifica dei checkbox.

Supponiamo che in una form occorra necessariamente accettare le condizioni cliccando su una checkbox. Se utilizziamo Zend_Filter_Input per la verifica dell'inserimento,
e si utilizzano i seguenti parametri, sembra che la cosa possa funzionare:


$validators = array(
'checkbox' => 'NotEmpty'
);


ma c'è un problema: se la checkbox non è selezionata, non viene inserita nel POST e quindi non arriva proprio alla fase di validazione.

La soluzione è quindi quella di richiedere che la checkbox sia presente e non vuota.


$validators = array(
'checkbox' => array(
'presence' => 'required',
'NotEmpty'
)
);


Adesso se non si accettano le condizioni la form non viene validata, correttamente.

Il fatto poi che il messaggio di errore non sia proprio consono è un altro paio di maniche !

giovedì 17 gennaio 2008

Traduzione & Action Helpers

Il problema che mi ha fatto impazzire ieri notte è trovare un metodo comodo per gestire il sito naturalads.net in più lingue.

Il componente Zend_Translate fa proprio questo: permette di gestire facilmente le traduzioni prendendo le stringe da un adapter che può essere un semplice file csv oppure un file di tipo .mo (vedi gettext).

I problemi a cui sono giunto sono i seguenti:
  • come mantenere lo stato della lingua dell'utente
  • una volta definita la lingua come e dove inizializzare la classe Zend_Translate
  • come permettere all'utente di cambiare lingua senza perdere il contesto
Dopo vario smanettare sono giunto alla conclusione:

  • La lingua dell'utente si tiene dentro la sessione in questo modo non si creano problemi di passaggio parametri negli URL. Inoltre la sessione è comunque già attiva per gestire le autenticazioni e quindi una variabile in più di 2 caratteri non fa troppo male.
  • Quando un controller vede nell'url/get/post/cookie/env un parametro lang=xx si attiva per salvare il parametro nella sessione. In questo modo l'utente può cambiare la lingua senza perdere il contesto di quello che sta facendo
  • L'inzializzazione della classe Zend_translate, così come la modifica della variabile di sessione, è fatto nel controller attivo.
Ora la cosa funziona, ma il terzo punto prevede che l'inizializzazione sia fatta in 'ogni' controller. Sarà, ma scrivere così tanto a me non piace molto e quindi scartabellando il manuale Zend ho visto che una cosa utilizzabile è un Controller Action Helper che viene richiamato dal Front Controller prima di richiamare ogni controller(init).

OK. il posto giusto per non fare troppo lavoro e avere un oggetto facilmente manutenibile.

Perchè ho scartato i Front Controller Plugins invece...
In un primo tempo avevo pensato ai Plugin del Front Controller, però non si sono rivelati così flessibili.
Questo è un plugin, ad esempio, trovato sul blog di naneau.nl, che blocca l'accesso ad ogni pagina se non si è loggati:

class Plugins_CheckLogin extends Zend_Controller_Plugin_Abstract {
public function preDispatch() {
$login = false;
//you will probably want to implement this ;)
if (!$login) {
//no login
$request = $this->getRequest();
//the request
$request->setModuleName('default');
$request->setControllerName('login');
$request->setActionName('index');
//send to default/login/index
}
}

basta metterlo nella directory library/Plugins e richiamarlo nel bootstrap con

$frontController->registerPlugin(new Plugins_CheckLogin());
per vederlo funzionare in tutte le pagine del nostro sito.

L'unico problema è che il Plugin agisce 'fuori' dal controller e quindi non ha possibilità di accedere al controller e magari settare le variabili della view... Quindi se si vuole lavorare sulla request e magari modificarla, la soluzione migliore è sicuramente il Front Controller Plugin. Se invece si vuole agire a livello globale sui controller, come nel mio caso, è meglio usare un action helper che può essere richiamato in modo automatico sugli eventi.

... Aggiornamento .... dopo 4 ore (dovute soprattutto a convincere l'helper broker a digerire il nuovo helper) il mio helper 'translator' è funzionante e si adatta a qualsiasi controller (attuale o futuro) senza bisogno di scrivere una riga in più.
Anzi già che c'ero l'ho infarcito di tutte le cose che mi servono bene o male per tutti i controller...
(per i curiosi eccolo...)

<?php
require_once 'Zend/Controller/Action/Helper/Abstract.php';

class Helpers_Translator extends Zend_Controller_Action_Helper_Abstract {

public function init() {
$controller = $this->getActionController();
// utilità per le view
$controller->view->baseUrl = $controller->getRequest()->getBaseUrl();
//tutti i metodi della view possono usare $this->user per accedere all'utente
$controller->view->user = Zend_Auth::getInstance()->getIdentity();

//get the session
$sex = Zend_Registry::get('sex');
// numero di pagine
$controller->view->numberOfPageRequests =$sex->numberOfPageRequests;
// esiste un parametro di lingua allora lo mettiamo nella sessione
if($lang = $controller->getRequest()->getParam('lang', '')) {
$sex->lang = $lang;
}
//comunichiamo alla view il lang da usare
$controller->view->lang = $sex->lang;
// carichiamo il file di traduzione
$translationFile = '../languages/'. $sex->lang .'-source.csv';
// carichiamo il file di traduzione
if (file_exists($translationFile)) {
$controller->view->trans = new Zend_Translate('csv', $translationFile, $sex->lang, array('separator' => '|'));
} else {
$controller->view->trans = new Zend_Translate('csv', '../languages/nosource.csv');
}
}
}

mercoledì 16 gennaio 2008

Come usare gli helper in Zend Framework

Per prima cosa una cosetta semplice e utile: gli helper, ovvero dei piccoli pezzetti di codice che possono essere richiamati dalla view più volte e che possono mantenere un loro stato.

Ricetta semplice per creare l'helper 'showErrors' che in pratica incrementa una variabile e restituisce una stringa.

Creazione Helper
creare il file showErrors.php nella directory /application/views/helpers/
con il seguente contenuto


<?php
class Zend_View_Helper_ShowErrors
{
protected $_count = 0;
public function showErrors() {
$this->_count++;
$output = "I have seen 'The Jerk' {$this->_count} time(s).";
return htmlspecialchars($output);
}
}



Richiamare l'helper dalla view
nella view basta inserire

<?php echo $this->showErrors(); ?>

e il gioco è fatto.

Convenzioni da seguire:

  • il file che contiene la classe dell'helper si deve chiamare come l'helper con la lettera iniziale minuscola (showErrors.php)
  • la classe dell'helper deve iniziare con Zend_View_Helper_ seguita dal nome dell'helper con la lettera iniziale maiuscola (class Zend_View_Helper_ShowErrors)
  • deve esistere nella classe almeno un metodo che si chiama come l'helper con la lettera minuscola iniziale (public function showErrors())
  • se l'helper è salvato nella directory views/helpers/ viene caricato il tutto in modo automatico.


Non mi sembra difficile dopo tutto... o no?

Accedere dall'helper alle variabili della view
Visto che il mio helper deve mostrare i messaggi di errore, devo accedere alla variabile $messages che si trova nella view. Normalmente nella view accedo alla variabile semplicemente con $this->messages.
Nell'helper devo fare un passo in più: recuperare l'oggetto view:

Ricetta per gli impazienti:

Aggiungo un metodo nell'helper che si chiama setView che riceve l'oggetto view e lo salva in una variabile e poi è possibile utilizzarlo in tutti i metodi.


<?php
class Zend_View_Helper_ShowErrors
{
protected $_count = 0;
public $view;

public function setView(Zend_View_Interface $view) {
$this->view = $view;
}
public function showErrors() {
if (isset($this->view->messages)) {
$this->_count++;
$output = "I have seen 'The Jerk' {$this->_count} time(s).";
return htmlspecialchars($output);
}
}
}


in questo modo posso accedere ad ogni elemento della view semplicemente con
$this->view-> (ad esempio alle variabili impostate dal controller come nel mio caso)

versione definitiva
in ogni caso questo e' l'helper che utilizzo nel mio codice

<?php
class Zend_View_Helper_ShowErrors
{
public $view;

public function setView(Zend_View_Interface $view) {
$this->view = $view;
}
public function showErrors() {
if (isset($this->view->messages)) {
$output = "<div id=\"message\">";
foreach ($this->view->messages as $field => $messages) {
foreach ($messages as $message) {
$output .= $this->view->escape($message) . "<br />\n";
}
$output .= "<br />";
}
$output .= "</div>";
return $output;
}
}
}

NaturalAds Project and Zend Framework

NaturalAds is the first 'real' project that I develop using Zend Framework.

I will post here the history of this journey in developing a new project using a very promising framework.