Nota. Questa unità didattica fa parte di una serie di diversi articoli ed inizia con questo. La lettura del singolo articolo non contestualizzato e disunito dagli altri può risultare incomprensibile se non addirittura fuorviante.
In questa unità didattica vedremo come implementare le funzioni del profilo utente. Come potete vedere direttamente sul forum di test, il front-end di questa pagina è implementato tramite una user interface di jquery, in particolare ui.tabs. Dovremo per tanto ricordarci di includere la libreria necessaria. Andate ora a vedere nelle demo di jqery come viene implementata questa interfaccia. Come sempre in modo molto semplice. Basterà fare una cosa del genere:
<div id="tabs"> //in questa prima parte vengono definite le "linguette" con il titolo che conterranno // ed il riferimento al contenuto <ul> <li><a href="#tabs-1">titolo 1</li> <li><a href="#tabs-2">titolo 2</li> <li><a href="#tabs-3">titolo 3</li> <li><a href="#tabs-4">titolo 4</li> </ul> // in seguito potremo inserire il contenuto relativo alla "linguetta" con dei // div aventi l'id corrispondente al link <div id="tabs-1"> //contenuto per titolo 1 </div> <div id="tabs-2"> //contenuto per titolo 2 </div> // eccetera .... </div>
Molto semplice davvero se si pensa al pregevole effetto finale.
Ora create il file profile_template.php e salvatelo nella cartella template/default.
Ecco il file
<h1>Profilo utente <?php echo $this->_username; ?></h1> <br><br> <div id="tabs"> <ul> <li><a href="#tabs-1">Dati personali</a></li> <li><a href="#tabs-2">Password</a></li> <li><a href="#tabs-3">Immagine</a></li> </ul> <div id="tabs-1"> <h1>Modifica dati personali</h1><br><br> <form action="profile_action.php" method="POST"> Nome:<br> <input type="text" value="<?php echo $this->_name; ?>" name="name" class="input_form ui-corner-all" maxlength="20"/><br><br> Cognome:<br> <input type="text" value="<?php echo $this->_cname; ?>" name="cname" class="input_form ui-corner-all" maxlength="20"/><br><br> Username:<br> <input type="text" value="<?php echo $this->_username; ?>" readonly="readonly" class="input_form ui-corner-all" /><br><br> email:<br> <input type="text" value="<?php echo $this->_email; ?>" readonly="readonly" class="input_form ui-corner-all" /><br><br> <input type="checkbox" value="1" <?php echo $this->_mailer; ?> name="mailer" /> Funzione mailer<br><br> <input type="submit" value="modifica" name="data" class="bott ui-corner-all ui-state-default" /> </form> </div> <div id="tabs-2"> <h1>Modifica password</h1><br><br> <form action="profile_action.php" method="POST"> Nuova password:<br> <input type="password" class="input_form ui-corner-all" name="password" /><br><br> Riscrivi password:<br> <input type="password" class="input_form ui-corner-all" name="password2" /><br><br> <input type="submit" value="modifica" name="pass" class="butt ui-corner-all ui-state-default" /> </form> </div> <div id="tabs-3"> <h1>Carica immagine personale</h1><br><br> <table border="0" cellspacing="30"> <tr> <td> <form action="profile_action.php" method="POST" enctype="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="71680" /> <input type="file" name="upfile" value="" class="ui-corner-all" id="image"/> <input type="hidden" name="control" value="upload" /><br><br> <input type="submit" value="upload" name="upload" class="ui-corner-all ui-state-default"/> </form><br><br> L'immagine può essere al massimo 70Kb ed avere una dimensione di 65x65 px o almeno essere quadrata. In caso contrario verrà deformata. </td> <td valign="top"> <center> Attuale<br><br> <img src="images/users/<?php echo $this->_img; ?>" border="0" width="65" height="65"> </center> </td> </tr> </table> </div> </div>
Come vedete per ogni “tabs” ritroviamo il form rispettivamente per i dati, la modifica password e l’upload immagine. Le variabili già inserite fanno capo alla classe profile che andremo a scrivere ora.
Create il file profile.class.php e salvatelo nella cartella lib
La classe profile sarà un’estensione di newuser in quanto andremo ad ereditare cose che ci interessano.
include_once dirname(__FILE__) . '/newuser.class.php'; class Profile extends NewUser { public $_name; public $_cname; public $_username; public $_mailer; public $_email; public $_img;
Come vedete, all’inizio dichiaro le proprietà che andranno a popolare i form del profilo utente. Ora scriviamo il metodo che si occuperà di leggere i dati dell’utente e di creare la pagina del profilo.
public function ReadUserData() { $sql = "SELECT name,cname,username,email,mailer,image FROM users WHERE id=$_SESSION[user_id]"; $res = mysql_query ($sql, $this->conn); $row = mysql_fetch_array($res); $this->_name = stripslashes($row['name']); $this->_cname = stripslashes($row['cname']); $this->_username = stripslashes($row['username']); $this->_email = $row['email']; $this->_img = $row['image']; $this->_mailer = ""; if($row['mailer']) { $this->_mailer = 'checked="checked"'; } include 'template/' . TEMPLATE_NAME . '/profile_template.php'; return; }
Come potete vedere, una semplice query va a leggere i dati dell’utente. In seguito vengono valorizzate le proprietà che abbiamo precedentemente dichiarato e che vengono utilizzate nel file che abbiamo scritto all’inizio. Unica particolarità è la chekbox per la funzione mailer. Se è selezionata, dovrò aggiungere la proprietà cheked al campo del form. Infine includo il file di template.
Ora scriviamo il metodo DataModify che si occuperà di intercettare i dati inviati dal form per la modifica dei dati personali e di aggiornarli nel database.
public function DataModify() { $name = mysql_real_escape_string($_POST['name']); $cname = mysql_real_escape_string($_POST['cname']); if(isset($_POST['mailer'])) { $mailer = 1; } else { $mailer = 0; } $sql = "UPDATE users SET name='$name',cname='$cname',mailer=$mailer WHERE id=$_SESSION[user_id]"; mysql_query($sql, $this->conn); header("Location: profile.php?alert=13"); return; }
Come vedete si tratta di un metodo piuttosto semplice. Passiamo ora al metodo per la modifica della password
public function PasswordModify() { if($this->VerifyPassword()) { if(!empty($_POST['password'])) { $password = md5($_POST['password']); $sql = "UPDATE users SET password='$password' WHERE id=$_SESSION[user_id]"; mysql_query($sql, $this->conn); header("Location: profile.php?alert=13"); } else { header("Location: profile.php?alert=1"); } } else { header("Location: profile.php?alert=2"); } }
Qui vedete che utilizzo il metodo VerifyPassword() che è un metodo della classe newuser e che ricorderete restituisce TRUE se le due password corrispondo e FALSE se non corrispondono. Dunque se non dovessero corrispondere tornerà a profile.php con alert=2, errore che abbiamo già utilizzato e che comunica che le due password inserite non corrispondono.
Se le due password corrispondono controlliamo che il campo non sia vuoto. Se lo dovesse essere, si tratterebbe di un errore e dunque si verrà reindirizzati a profile.php con alert=1, mentre se anche questo controllo viene superato, ecco che la password verrà aggiornata nel database.
Passiamo ora al metodo che ci permetterà di caricare l’immagine personale. Utilizzeremo la classe upload (leggermente modificata) che abbiamo visto nell’articolo “Le basi dell’upload di file“; vi consiglio vivamente di rileggerlo.
Ora che l’avete riletto, vi posto la classe modificata ed in seguito farò alcune considerazioni.
<?php class Upload { public $SavePath; public $ErrorReport; public $file = array(); public $FinalName; protected $AllowedExtensions = array(); protected $SecurityPostName; protected $SecurityPostKey; protected $HiddenSecurity; public function __construct($path, $name, $extensions = "", $PostName ="", $PostKey = "") { $this->SavePath = $path; $this->file = $_FILES[$name]; if ($extensions) { $this->AllowedExtensions = $this->ParseExtensions($extensions); } else { $this->AllowedExtensions = FALSE; } if($PostName AND $PostKey) { $this->SecurityPostKey = $PostKey; $this->SecurityPostName = $PostName; $this->HiddenSecurity = TRUE; } else { $this->HiddenSecurity = FALSE; } $this->ErrorManagement(); } private function ParseExtensions($string) { $ExtensionsArray = explode(",", $string); return $ExtensionsArray; } private function FileExtention() { $exp = explode(".", $this->file['name']); $exp = array_reverse($exp); return $exp[0]; } protected function ErrorManagement() { if ($this->HiddenSecurity) { if ($_POST[$this->SecurityPostName] != $this->SecurityPostKey) { $this->ErrorReport = 14; return FALSE; } } if ($this->AllowedExtensions) { if (!in_array($this->FileExtention(), $this->AllowedExtensions)) { $this->ErrorReport = 15; return FALSE; } } switch($this->file['error']) { case UPLOAD_ERR_OK: $this->ErrorReport = 0; $this->SaveUploadFile(); break; case UPLOAD_ERR_INI_SIZE: $this->ErrorReport = 16; break; case UPLOAD_ERR_FORM_SIZE: $this->ErrorReport = 17; break; case UPLOAD_ERR_PARTIAL: $this->ErrorReport = 18; break; case UPLOAD_ERR_NO_FILE: $this->ErrorReport = 19; break; case UPLOAD_ERR_NO_TMP_DIR: $this->ErrorReport = 20; break; case UPLOAD_ERR_CANT_WRITE: $this->ErrorReport = 21; break; } } protected function SaveUploadFile() { if(is_uploaded_file($this->file['tmp_name'])) { $this->FinalName = md5(microtime()) . '.' . $this->FileExtention(); move_uploaded_file($this->file['tmp_name'], $this->SavePath . $this->FinalName); return TRUE; } else { $this->ErrorReport = 14; return FALSE; } } } ?>
Le modifiche sono le seguenti:
- Ho sostituito i messaggi con i quali veniva valorizzata la proprietà ErrorReport, con i codici numerici che utilizziamo per la gestione degli errori.
- Ho rimosso il metodo che si occupava di evitare (se richiesto) l’overwriting. Al suo posto, utilizzo ora un metodo più comune che è quello di affidare un nome casuale al file. Dichiaro dunque all’inizio della classe la proprietà FinalName, che come vediamo nel metodo SaveUploadFile, viene valorizzata con il microtime passato per md5, seguito da un punto, seguito dall’estensione del file.
Ora che abbiamo questa classe, possiamo scrivere il metodo che si occuperà dell’upload dell’immagine personale
public function UploadImage() { include_once 'lib/upload.class.php'; $upload = new Upload('images/users/', 'upfile', 'gif,png,jpg,jpeg', 'control', 'upload'); if($upload->ErrorReport == 0) { $this->DeleteOldImage(); $sql = "UPDATE users SET image='$upload->FinalName' WHERE id=$_SESSION[user_id]"; mysql_query($sql, $this->conn); header("Location: profile.php?alert=0"); die; } else { header("Location: profile.php?alert=$upload->ErrorReport"); die; } }
Dunque includiamo la classe upload che abbiamo appena visto e la istanziamo con i parametri del caso.
In seguito se ErrorReport varrà 0 (dunque se il processo di upload è andato a buon fine), cancelliamo la vecchia immagine con DeleteOldImage (che scriveremo in seguito) e procediamo aggiornando il nome dell’immagine personale nel database con FinalName.
Se ErrorReport non sarà 0, semplicemente reindirizziamo a profile.php passando come valore di alert il valore di ErrorReport.
Scriviamo ora il metodo DeleteOldImage, che si occuperà appunto di cancellare l’immagine precedente a due condizioni: che l’immagine esista e soprattutto che l’immagine non sia nn.gif (che è l’immagine di default per tutti gli utenti e quindi non va cancellata).
private function DeleteOldImage() { $query = "SELECT image FROM users WHERE id=$_SESSION[user_id]"; $res = mysql_query($query, $this->conn); $row = mysql_fetch_array($res); if(file_exists("images/users/$row[image]") AND $row['image'] != "nn.gif") { unlink("images/users/$row[image]"); } }
Vi posto la classe completa
<?php include_once dirname(__FILE__) . '/newuser.class.php'; class Profile extends NewUser { public $_name; public $_cname; public $_username; public $_mailer; public $_email; public $_img; public function ReadUserData() { $sql = "SELECT name,cname,username,email,mailer,image FROM users WHERE id=$_SESSION[user_id]"; $res = mysql_query ($sql, $this->conn); $row = mysql_fetch_array($res); $this->_name = stripslashes($row['name']); $this->_cname = stripslashes($row['cname']); $this->_username = stripslashes($row['username']); $this->_email = $row['email']; $this->_img = $row['image']; $this->_mailer = ""; if($row['mailer']) { $this->_mailer = 'checked="checked"'; } include 'template/' . TEMPLATE_NAME . '/profile_template.php'; return; } public function DataModify() { $name = mysql_real_escape_string($_POST['name']); $cname = mysql_real_escape_string($_POST['cname']); if(isset($_POST['mailer'])) { $mailer = 1; } else { $mailer = 0; } $sql = "UPDATE users SET name='$name',cname='$cname',mailer=$mailer WHERE id=$_SESSION[user_id]"; mysql_query($sql, $this->conn); header("Location: profile.php?alert=13"); return; } public function PasswordModify() { if($this->VerifyPassword()) { if(!empty($_POST['password'])) { $password = md5($_POST['password']); $sql = "UPDATE users SET password='$password' WHERE id=$_SESSION[user_id]"; mysql_query($sql, $this->conn); header("Location: profile.php?alert=13"); } else { header("Location: profile.php?alert=1"); } } else { header("Location: profile.php?alert=2"); } } public function UploadImage() { include_once 'lib/upload.class.php'; $upload = new Upload('images/users/', 'upfile', 'gif,png,jpg,jpeg', 'control', 'upload'); if($upload->ErrorReport == 0) { $this->DeleteOldImage(); $sql = "UPDATE users SET image='$upload->FinalName' WHERE id=$_SESSION[user_id]"; mysql_query($sql, $this->conn); header("Location: profile.php?alert=0"); die; } else { header("Location: profile.php?alert=$upload->ErrorReport"); die; } } private function DeleteOldImage() { $query = "SELECT image FROM users WHERE id=$_SESSION[user_id]"; $res = mysql_query($query, $this->conn); $row = mysql_fetch_array($res); if(file_exists("images/users/$row[image]") AND $row['image'] != "nn.gif") { unlink("images/users/$row[image]"); } } public function CompositeContent() { $this->ReadUserData(); } } ?>
Come vedete, alla fine ho aggiunto il metodo CompositeContent. Si tratta di quel metodo apparentemente inutile nella classe Main. Ora, in questa classe viene sovrascritto in modo che esegua il metodo ReadUserData. In questo modo, nella pagina profile.php che andremo tra poco a scrivere, ci mostrerà la pagina del profilo. Vediamo subito il file profile.php da salvare nella cartella principale
<?php include_once 'lib/profile.class.php'; $page = new Profile(); $page->IsAuth(); $page->ExtraHeader[] = '<script type="text/javascript" src="jquery/ui/ui.tabs.js"></script>'; $page->ExtraScript[] = 'profile.js'; include 'template/' . TEMPLATE_NAME . '/template.php'; ?>
Dopo aver incluso ed istanziato la classe Profile verifichiamo che l’utente si autenticato con il metodo IsAuth della classe Main (ovviamente per accedere al profilo utente bisogna essere autenticati).
In seguito aggiungiamo con ExtraHeader la libreria tabs di jquery. Con ExtraScript aggiungiamo il file profile.js che vedremo tra poco ed infine includiamo il template.
Il template verrà valorizzato tramite i metodi e le proprietà dalla classe Main che ereditiamo dalla classe newuser che a sua volta è un’estensione di Main. Il metodo CompositeContent, per contro, verrà sovrascritto eseguendo così il metodo ReadUserData che ci mostrerà i form per la modifica.
il file profile.js da salvare nella cartella js, sarà semplicemente così:
$("#tabs").tabs();
Indichiamo che nel div tabs sarà utilizzata la user interface tabs.
A questo punto non ci resta che scrivere il file profile_action.php da salvare nella directory principale; il file al quale puntano i tre form della pagina del profilo.
<?php include_once 'lib/profile.class.php'; $profile = new Profile(); $profile->IsAuth(); if(isset($_POST['data'])) { $profile->DataModify(); die; } if(isset($_POST['pass'])) { $profile->PasswordModify(); die; } if(isset($_POST['upload'])); { $profile->UploadImage(); die; } ?>
Molto semplicemente, a dipendenza del form che ne fa richiesta, viene eseguita l’operazione necessaria.
Ed ecco fatto il nostro profilo utente.


il 27 maggio 2010 alle 22:38
Ciao Maurizio e complimenti per tutti questi ottimi tutorials che ci regali.
Una curiosità: se voglio trasformare un semplice form in un form a più step (diciamo come il form d’esempio presente in questa pagina) però con un unico form e suddiviso da come dovrei fare?
Pensavo di strutturare, appunto, il form in vari fieldset e creare un “button” che richiami una funziona che a sua volta mostri lo step successivo.
Mi daresti qualche consiglio?
Grazie.
il 27 maggio 2010 alle 22:58
Rieccomi ancora…pardon…
in pratica sono riuscito a realizzare quanto volevo semplicemente facendo una cosa del genere:
………………
………………
………………
In pratica non ho molto chiaro però come:
- mostrare il tasto submit solo una volta arrivati allo step finale;
- mostrare un bottone “prossimo step” che, appunto, visualizza il prossimo step e nel frattempo però si assicura che i campi siano tutti compilati (tramite, magari, il validate.js di jQuery.
Grazie mille.
il 27 maggio 2010 alle 22:59
(scusa l’ennesimo messaggio, ma non mi ha trascritto [penso per motivi di sicurezza] i vari tag html che avevo messo)
il 28 maggio 2010 alle 08:24
Credo che tu voglia realizzare qualcosa del genere
(clicca sulla striscia rossa in alto)
http://www.jankoatwarpspeed.com/examples/webform_to_wizard/
Il tutorial e il plugin li trovi qui
http://www.jankoatwarpspeed.com/post/2009/09/28/webform-wizard-jquery.aspx
il 28 maggio 2010 alle 08:34
Ciao,
).
ti ringrazio per i links ma quel tutoria lo avevo già letto e, diciamo, l’avrei scartato perchè non vorrei aggiungere altro codice al mio form (già ce n’è abbastanta
E poi sto usando un plugin di jQuery (validate.js) che valida e controlla tutti i campi specificati in un altro file (del tipo #campo: required, ).
E poi ho adottato lo stile presente in queste lezioni (come realizzare un’applicazione complessa)e ci terrei a restare su questa linea.
Perciò, avresti da darmi un qualche altro suggerimento su come realizzare quanto detto?
Grazie e buona giornata.
il 30 maggio 2010 alle 21:37
Forse non ho capito il problema. Hai provato a fare un form unico? Apri il form, metti la prima parte nella prima tabs, la seconda nella seconda tabs e solo alla fine chiudi il form .
il 16 maggio 2011 alle 10:46
ciao maurizio sto seguendo le tue guide da diverso tempo! complimenti e spero che continuerai ad aiutarci! volevo chiederti una cosa : ho implementato la gestione tramite tabs con il relativo plugin di jquery, ma ho problemi con explorer 9. ti risulta?
il 18 marzo 2013 alle 15:29
Ciao, ho seguito tutta la serie applicazione complessa. Però il profilo utente non mi funziona.
Appare questo errore:
Profilo utente
Fatal error: Using $this when not in object context in C:\Program Files (x86)\Apache Software Foundation\Apache2.2\htdocs\upmyidea\action\profile_template.php on line 1
In linea 1 c’è Profilo utente _username; ?>
Non riesco a capire come risolvere questo errore!