domenica 29 giugno 2008

Non pensavo eppure... ASP.NET

Stamattina ho voluto dare un'occhiata al framework ASP.NET
Sempre forte del fatto che il quadrinomio LAMP fosse imbattibile (o almeno è uno dei migliori in circolazione), ho voluto provare a vedere perché molti sviluppano siti web con i linguaggi e i framework forniti da mamma Microsoft, dovendo pagare fior di quattrini per le licenze.

Strano ma vero, Microsoft fornisce una versione gratuita di VisualStudio. Visual Studio Express 2008, nello specifico, è la più recente. Richiede solo una registrazione, obbligatoria per usare il prodotto dopo 30 giorni dall'installazione. La suite intera è una ISO di circa 2 GB e mezzo ma è possibile scaricare solo parti di Visual Studio, come Visual C# 2008 Express, Visual Basic 2008 Express, SQL Server 2008 Express e via dicendo. C'é un' applicazione denominata Visual Web Developer 2008 Express, che è specifica per lo sviluppo di applicazioni web, come dice il nome, giusto? Con questa è possibile sviluppare sia in VB che in C#, appena avviate l'applicazione viene creato il server web di test e avviato il browser. Comodo!

Quindi ho creato un piccola pagina web con un form dove inserire dei dati che venivano scritti in una tabella di un database access. E fin qui tutto normale, un po' di sbattimento per capire quale oggetto utilizzare per la connessione al db (ma è normale, è stata la prima che volta che sviluppavo in ASP.NET).

La cosa che mi ha stupito è il fatto che una volta creato un oggetto AccessDataSource, e aggiunto un oggetto GridView associato all'oggetto AccessDataSource, l'oggetto GridView permette senza scrivere una sola riga di codice di avere già paginati,intabellati, formattati e con le colonne ordinabili, i risultati di una query SQL.

Credo che per la maggior parte degli sviluppatori ASP.NET questa sia una ovvietà, ma per me, che non ho mai sviluppato con .NET, questa cosa mi ha davvero stupito in bene. Fare la stessa cosa in PHP puro avrebbe richiesto sicuramente più tempo che trascinare due oggettini sulla pagina web in produzione.

Ovviamente c'é il rovescio della medaglia: bello, facile, intuitivo e veloce, ma sei uno sviluppatore ASP.NET dovvesse fare la stessa cosa senza avere a disposizione il framework .NET ci riuscirebbe lo stesso?

In conclusione: .NET è molto intuitivo e veloce, ma credo che bisogni incominciare ad usarlo nella "maturità". Ovvero: prima si impara a programmare su linguaggi dove "ci si fa le ossa" e poi, quando si è "stanchi" di programmare si passa .NET. Per quel poco che ho visto credo ne valga la pena di pagare la licenza di Windows Server.

Un mio amico (Sergio De Chiara di windowserver.it) inorridirà (probabilmente no,ma essendo un dipendente Microsoft... ;-)) davanti alle mie parole di oggi, ma tanto tempo fa (era forse appena uscito VB 5 per intenderci) mi disse: "I linguaggi di programmazione visuali sono belli, facili e veloci. Ma se ti dovessero chiedere di scriverne uno come fai?"

lunedì 16 giugno 2008

Stampare in Landscape con Visual Basic 6 su Microsoft Office Document Image Writer

Oggi mi sono scontrato con questo problema. Se come stampante, per effettuare i vostri test senza consumare un mucchio di carta, utilizzate Microsoft Office Document Image Writer potreste avere qualche problema con l'orientamento della pagina.
Infatti con tale "stampante" l'oggetto Printer di Visual Basic restituisce un run-time error 380 (valore di proprietà non valido) nel caso vogliate stampare in Orizzontale (Landscape).
L'unico orientamento possibile accettato da MODI Writer è infatti vbPRORPortrait (= 1).
Ho effettuato una ricerca con Google, ma su molti blog e/o forum non si diceva altro che "Mostra la finestra di dialogo della stampante e fai cambiare all'utente l'impostazione di orientamento" o cose del genere.
Per ovviare a questo problema ho aggiunto una routine di gestione dell'errore che anziché cambiare l'orientamento della pagina, cambia le dimensioni del foglio:
Private Sub setOrientation(Optional myOrientation As Integer = vbPRORPortrait)
On error goto or_error
Const usrPRDWA4 = 11907
Const usrPRDHA4 = 16839.9
Printer.Orientation = myOrientation
Exit Sub
or_error:
Printer.PaperSize = vbPRPSUser
If myOrientation = vbPRORLandscape Then
Printer.Height = usrPRDWA4
Printer.Width = usrPRDHA4
Else
Printer.Height = usrPRDHA4
Printer.Width = usrPRDWA4
End If
Resume Next
End Sub

Ovviamente il cambio di dimensioni del foglio avviene solo se va in errore l'assegnamento dell'attributo Orientation dell'oggetto Printer. Quindi su una stampante "normale" non dovrebbe mai accadere.

Le dimensioni che vengono impostate sono le dimensioni di un foglio A4 convertite in Twips. Infatti 1 cm = 567 Twips, quindi i 21 cm di larghezza del foglio A4 diventano 11907 Twips.

A seconda delle vostre esigenze impostate anche dimensioni differenti, ma credo che anche voi come me abbiate in casa solo fogli A4. Se avete qualche idea migliore scrivetemi o lasciatemi un commento a questo post!

giovedì 12 giugno 2008

PHP - come faccio a far scaricare un file fuori dalla document root del server?

Gli utenti di un sito possono, di norma, scaricare e accedere a tutto quanto sia nella document root del web server. Per proteggere i file da uno scaricamento indiscriminato potremmo utilizzare diversi metodi, per esempio utilizzare l'autenticazione HTTP fornita dal server web (.htaccess vi dice niente?).
Oppure tenere i file scaricabili fuori dalla document root. Ma così non potrebbero essere letti direttamente dal web server.
PHP però è in grado di leggere tutto il filesystem del server (ovviamente dove l'utente del server web ne ha i permessi), quindi per proteggere un file dallo scaricamento selvaggio si può leggere il file da scaricare con il comando readfile() e aggiungendo qualche header.

downloadfile.php


<?php
ob_start();
// Eseguo dei check per vedere se l'utente può scaricare il file...
...
...
if (!$user_can_download){
ob_end_flush();
die("Non hai il privilegio di scaricare questo file");
}
// Imposto nome e path del file
$pathname = "/home/web/files/";
$filename = "pippo.pdf";
$mime = "application/x-pdf";
ob_end_clean(); // Eventuali errori che php può dare
// in output vengono eliminati
// Iniziano gli header
header('Cache-Control: public, must-revalidate');
header('Pragma: hack');
header('Content-type: '.$mime);
header('Content-Length: ' .filesize($pathname.$filename));
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary\n');
// Leggo il file e lo mando in output
readfile($pathname.$filename);
?>

Il precedente script legge il file e lo manda in output, forzando oltretutto il download del file (non lo fa aprire in una scheda/finestra del browser!)
.
Bisogna però prestare attenzione che lo script php non contenga spazi o altri caratteri dopo la chiusura del tag php (?>) altrimenti il file risulterebbe corrotto.

mercoledì 11 giugno 2008

PHP - "require" e "include"... questi "conosciuti"

Le istruzioni "require" e "include" (insieme alla varianti "require_once" e "include_once") sono utilizzatissime in PHP.
Appunto per questo talvolta non si presta attenzione alla pagina del manuale.

Esse inserisco uno script nello script corrente. Ma attenzione ai path name!
Se il file da includere è nella stessa directory di dove si trova lo script che include, oppure il file da includere è nel "include_path" del php.ini, basta scrivere il nome del file da includere.

Ma se si trova in un'altra directory e volete usare un path name relativo, dovete sempre tener ben presente che la directory corrente (.) è la directory di dove si trova il file servito dal server web.


E fin qui niente di strano! Qualche problema se non si sta attenti potrebbe verificarsi quando si includono a cascata più file (magari contenenti delle classi).
Faccio un esempio abbastanza banale.

avendo una struttura di directory con i seguenti file (pattern MVC):
- app/classes/user.class.php, una classe che gestisce lettura/scrittura sul db dei dati di un utente
- app/controllers/user_controller.class.php, classe che gestisce la visualizzazione
- app/templates/user.edit.php, template per la modifica di un utente
- app/templates/user.view.php, template per la visualizzazione di un utente
- public_htm/index.php, pagina servita dal server web (istanzia il controller)

In una configurazione simile a questa potremmo pensare che le righe iniziali dei file siano le seguenti:
user.class.php
<?php
class user {
...
}
?>

user_controller.class.php

<?php require_once ("../app/classes/user.class.php");
class user_controller {
...
function edit(){
... // Do some work
include_once ("../app/view/user.edit.php");
}
function view(){
... // Do some work
include_once ("../app/templates/user.view.php
}
?>

index.php

<?php
require_once ("../app/controllers/user_controller.class.php");
$controller = new user_controller();
?>


Ovviamente tralasciando i file della directory app/templates/ perchè conterranno principalmente codice
HTML.


Il problema si porrebbe a questo punto se si vuole aggiungere una directory "public_html/admin/"
dove porre un'area riservata per un amministratore del nostro sito web.
Nel nostro ipotetico file "public_html/admin/edit_user.php", se vogliamo utilizzare sempre le stesse classi
per gestire la scrittura e la lettura da db (il che non sarebbe male, in quanto avremmo scritto del codice
una sola volta!) tutti i path name inseriti fino adesso...vanno a farsi benedire.


La soluzione è dietro l'angolo: basterebbe inserire path name assoluti e non relativi.
Ovviamente cambiando server (banalmente passando da sviluppo a produzione) dovremmo cambiare
tutti i path name. E qui però ci vengono in aiuto le define.

Creiamoci una directory config/ (allo stesso livello di app/ e public_html/).
All'interno, un file path.php che contiene una o più define, per esempio:


<?php
define ('APP_BASE_DIR',"/home/www/app/");
?>

e sostituiamo tutti i path negli include e i require con qualcosa tipo:

require_once (APP_BASE_DIR."classes/user.class.php");

Nei file "index.php" e "edit_user.php", utilizzeremo il path relativo per includere il file "config/paths.php" (nel primo anteponendo "../" e nel secondo "../../),mentre per tutti gli altri file l'inclusione verrà effettuata senza problemi utilizzando il path name assoluto.

Se non sapete quale sia il path name assoluto del vostro server ricordatevi del comando "getCwd()".
Piazzandoci un bel "echo" davanti in uno script, verrete a conoscenza del path name assoluto utilizzato sul vostro server.
Ovviamente ricordatevi di eliminare al più presto tale script, prima che possa essere fonte di vulnerabilità.

Benvenuti!

Ciao a tutti, ho deciso di aprire questo blog sulla programmazione in generale, per mettere a disposizione di tutta la comunità web le mie personali conoscenze in materia e raccogliere nuovi consigli e tecniche. Spero che lo troviate utile e che possiate anche voi arricchirlo!

Un saluto,
Daniele Veratti