6 maggio 2017

Area riservata di WebSite X5 13: avatar utente

Questo articolo nasce da una richiesta emersa nella community di WebSite X5 ed elaborata dall'utente Giorgio C. che ci ha coinvolto: mostrare un'immagine associata all'utente registrato, un avatar quindi, che un utente possa cambiare a piacere.
Partendo dal codice di Giorgio, con cui l'utente può visualizzare il proprio avatar nell'header del sito una volta loggato, noi abbiamo fatto una variazione sul tema, creando una pagina profilo e facendo in modo che l'immagine associata ad ogni utente sia visibile a tutti nel momento in cui l'utente inserisce un commento nel guestbook e/o in un articolo del blog.

Qui la nostra demo:

demo avatar utente area riservata
DEMO

Non entriamo nel merito di come creare un'area riservata, poiché la guida del programma è già esaustiva. Sottolineamo però che, per il corretto funzionamento del codice, occorre che nel progetto venga impostata in Gestione Dati, una Cartella su Server con accesso in scrittura verificando che abbia i permessi necessari. Accertarsi inoltre di aver scritto correttamente l'Indirizzo URL del Sito al passo 1 (Attenzione: NON scrivere il nome della pagina index nel percorso).

Vediamo ora come procedere:

La pagina profilo


La pagina profilo è una pagina protetta in cui vengono richiamati i dati dell'utente loggato: l'output è dinamico cioè dipende da chi la visualizza. In essa è presente anche il form con cui l'utente può cambiare il proprio avatar. Può essere impostata come Pagina d'ingresso per tutti gli utenti, o anche raggiungibile con un link.
Inseriamo in essa un Oggetto Codice HTML con il seguente codice:

<!-- Code by Giorgio C. & MAeSI - quellidelcucuzzolo.blogspot.it
     Please do not remove credit  -->
<div id="ms-datiUtente">
<!-- dati utente -->
<?php
$qfolder  = $imSettings['general']['public_folder'];
$n        = time();
$pa       = Configuration::getPrivateArea();
$dati     = $pa->whoIsLogged();
$username = $dati['username'];
$datiUser = $pa->getUserByUsername($username); // SOLO EDIZIONE PROFESSIONAL 
$avatar   = glob($qfolder . "/avatar-" . $username . ".*");
$tsfoto = 0;
if (empty($avatar))
    $foto = $qfolder . "/default.png";
else {
    foreach ($avatar as $value) {
        if (filemtime($value) > $tsfoto) {
            $tsfoto = filemtime($value);
            $foto   = $value;
        }
    }
}
echo '<div id="datiUser">', 
'<img src="' . $foto . '?' . $n . '" alt="avatar" />', 
'<span>Username:</span> ' . $username . '<br />', 
'<span>Nome completo:</span> ' . $dati['realname'] . '<br />', 
'<span>E-mail:</span> ' . $datiUser['email'] . '<br />', // SOLO EDIZIONE PROFESSIONAL 
    '</div>';
?>
</div>
<div style="clear:both"></div>

<!-- form per cambio avatar -->
<form id="cambioAvatar" enctype="multipart/form-data" method="POST" action="uploadavatar.php">
<div><label for="avatar">Cambia l'immagine del profilo:</label></div>
<input type="file" name="image" id="avatar" />
<input type="submit" value="invia" />
</form>

<!-- messaggio di conferma/errore upload -->
<div id="messaggio">
<?php
if (!empty($_SESSION['message'])) {
    echo $_SESSION['message'];
    unset($_SESSION['message']);
}
?>
</div>

Chi utilizza la Evolution deve rimuovere le due righe di codice con il commento //SOLO EDIZIONE PROFESSIONAL: si tratta del dato e-mail che è presente unicamente se viene utilizzata la registrazione automatica, prevista solo nell'edizione Professional. Si sottolinea inoltre che questo codice può essere utilizzato solo in pagine protette, dove il richiamo del file x5engine.php, necessario per il corretto funzionamento del codice, è già incluso.

Il codice richiede la presenza, nella Cartella su Server con accesso in scrittura indicata in Gestione Dati, di una immagine di default che verrà utilizzata finché l'utente non la cambierà. Noi abbiamo usato questa immagine: default.png. Nel caso si utilizzi un'immagine con diverso nome e/o estensione occorrerà apportare le opportune modifiche al codice.

Ovviamente il risultato sarà visibile solo online. Se vogliamo tenere pulita la pagina dell'anteprima, evitando di visualizzare parti di codice, è possibile inserire questo script in Proprietà Pagina, Esperto, Codice personalizzato, Prima della chiusura del tag HEAD:

<script>
$(document).ready(function() {
  // funzionalità offline
  if (location.host == "127.0.0.1:8080")
    $("#ms-datiUtente").html("I dati saranno visibili solo quando il sito verrà esportato su Internet");
});
</script>


Nella scheda Esperto dell'Oggetto Codice HTML possiamo scrivere le regole CSS per definire l'aspetto grafico che preferiamo. Noi abbiamo utilizzato questo codice:

#datiUser,
#cambioAvatar {
  line-height: 26px;
  font-size: 16px;
  padding: 10px;
}
#datiUser span {
  width: 125px;
  font-weight: bold;
  display: inline-block;
}
#datiUser img {
  max-width: 256px;
  float: left;
  padding: 6px 10px 6px 0;
}
#cambioAvatar input {
  padding-bottom: 10px;
}
#messaggio {
  color: red;
}
#messaggio span {
  color: green;
}

Lo script per l'upload


L'upload del nuovo avatar è gestito dal file uploadavatar.php, che si occupa di validare l'estensione del file caricato, le sue dimensioni in byte, e di rinominare il file in modo che il suo nome contenga l'username dell'utente: nel momento in cui l'utente cambierà il suo avatar, la vecchia immagine verrà cancellata dal server e sostituita da quella nuova.
<?php
/* =================================================================
   PHP Code by Giorgio C. & MAeSI - quellidelcucuzzolo.blogspot.it
   Please do not remove credit    
   =================================================================  */
if (!isset($_SERVER['HTTP_REFERER'])){
exit;
}
//apertura e lettura dati di sessione
require_once("res/x5engine.php"); 
$qfolder = $imSettings['general']['public_folder']; 
$pa      = Configuration::getPrivateArea(); 
$dati    = $pa->whoIsLogged();
$username = $dati['username'];

//informazioni sull'immagine caricata
$max_size = 102400; //dimensioni massime in byte dell'immagine da caricare
$tmp      = $_FILES['image']['tmp_name']; 
$type     = mime_content_type($tmp);
$size     = $_FILES['image']['size'];  
  
//controllo delle estensioni accettate  
function check_ext($tipo){
 switch ($tipo) {
  case "image/png":
  return true;
  break;
  case "image/jpg":
  return true;
  break;
  case "image/jpeg":
  return true;
  break;
  case "image/gif":
  return true;
  break;
  default:
  return false;
  break;
 }
}

//estensione dell'immagine
function get_ext($tipo) {
 switch ($tipo) {
  case "image/png":
  return ".png";
  break;
  case "image/jpg":
  return ".jpg";
  break;
  case "image/jpeg":
  return ".jpg";
  break;
  case "image/gif":
  return ".gif";
  break;
  default:
  return false;
  break;
 }
}

//gestione degli errori
function get_error($tmp, $type, $size, $max_size){
    if (!is_uploaded_file($tmp)) {
  $_SESSION['message'] = 'File caricato in modo non corretto';
    }
    else if (!check_ext($type)) {
  $_SESSION['message'] = 'Estensione del file non ammessa';
    }
    else if($size > $max_size) {
        $_SESSION['message'] = 'Dimensione del file troppo grande';
    }
}


//caricamento nuova immagine
if (is_uploaded_file($tmp) && check_ext($type) && $size <= $max_size) {
    $ext     = get_ext($type);
    $newfoto = $qfolder . "/avatar-" . $username . $ext;
    if (move_uploaded_file($tmp, $newfoto)) {
        $_SESSION['message'] = '<span>Immagine caricata con succcesso</span>';
    } else {
        $_SESSION['message'] = 'Non è stato possibile caricare l\'immagine';

    }
} else {
    get_error($tmp, $type, $size, $max_size);
}

//ritorno alla pagina contenente il form per l'upload
header('Location: ' . $_SERVER['HTTP_REFERER']);
?>
Il codice è completamente commentato per rendere più agevole la sua comprensione. Sono personalizzabili le dimensioni massime e le estensioni accettate ed anche i messaggi di errore.
Per utilizzare questo codice occorre incollarlo in un file creato con Notepad++ o il Blocco Note, e salvarlo con nome uploadavatar e con estensione .php. I più pigri possono scaricare il file QUI.
Il file uploadavatar.php deve risiedere nella root del sito. Si può allegare al progetto attraverso la scheda Esperto dello stesso Oggetto Codice HTML inserito nella pagina profilo, avendo cura di lasciare vuoto il campo Percorso relativo sul Server, oppure trasferirlo sul server con un programma FTP.

I commenti del guestbook e del blog


Avere un avatar che solo l'utente può vedere è, secondo noi, uno spreco di risorse. Abbiamo provato allora ad associare l'avatar ad un commento che l'utente loggato inserisce nel guestbook e/o nel blog del sito.

Per associare stabilmente l'avatar al commento sono necessarie due condizioni:
1. identificare l'utente loggato grazie all'username
2. memorizzare il percorso dell'immagine nei dati del commento.

Poiché il programma non permette l'inserimento di campi aggiuntivi nel form per l'invio dei commenti, abbiamo pensato di sacrificare il campo Sito Internet, utilizzandolo per memorizzare il percorso assoluto del file immagine. Ovviamente nasconderemo al visitatore questo campo con JavaScript, ma esso risulterà riempito in modo automatico in funzione di chi in quel momento visualizza la pagina: se il visitatore non è un utente loggato, il campo verrà riempito con il percorso corrispondente all'immagine di default.

Questo è il risultato ottenuto:
Nelle Proprietà della pagina che contiene l'Oggetto Guestbook e/o della pagina speciale Blog, scheda Esperto, Codice Personalizzato, Prima della chiusura del tag HEAD, inseriamo questo codice:

<!-- Code by Giorgio C. & MAeSI - quellidelcucuzzolo.blogspot.it
     Please do not remove credit   -->
<?php
$qfolder = $imSettings['general']['public_folder']; 
$qurl = pathCombine(array($imSettings['general']['url'], $qfolder));
$qblog = $_SERVER['PHP_SELF'];
if (strpos($qblog, '/blog/') !== false)
    $qpath = '../';
else
    $qpath = '';
$pa = Configuration::getPrivateArea();
$dati    = $pa->whoIsLogged();
$username = $dati['username'];
$realname = $dati['realname'];
$datiUser = $pa->getUserByUsername($username);  //SOLO EDIZIONE PROFESSIONAL 
$email = $datiUser['email'];      //SOLO EDIZIONE PROFESSIONAL
$avatar = glob($qpath.$qfolder."/avatar-" . $username . ".*"); 
$tsfoto = 0;
if (empty($avatar))
    $foto = $qurl . "/default.png";
else {
    foreach ($avatar as $value) {
        if (filemtime($value) > $tsfoto) {
            $tsfoto  = filemtime($value);
            $fotonew = $value;
        }
    }
    $foto = str_replace("../", "", $fotonew);
    $foto = str_replace($qfolder, $qurl, $foto);
}
?>
<script>
$(document).ready(function() {
    $("input[id$='topic-form-name']").val("<?php echo $realname ?>");
    $('input[id$="topic-form-email"]').val('<?php echo $email ?>'); //SOLO EDIZIONE PROFESSIONAL
    $('input[id$="topic-form-url"]').val('<?php echo $foto ?>').parent().hide();
  $(".topic-comments-user").each(function() {
    var $item = $(this).find("a");
    if ($item.length != 0) {
      var customCampo = $item.attr("href");
      var customNome = $item.contents();
      $item.replaceWith(customNome);
      $(this).prepend("<img class='campoCustomGb' src='" + customCampo + "' alt='avatar' />");
    }
  })
});
</script>

Il codice si occupa di identificare l'eventuale utente loggato, riempire automaticamente i campi del form con Nome completo e E-mail (solo per l'edizione Professional), inserire nel campo nascosto il percorso dell'immagine utilizzata dall'utente come avatar e, infine, visualizzare l'avatar nel relativo commento.


Anche in questo caso chi utilizza l'edizione Evolution deve rimuovere le tre righe di codice con il commento //SOLO EDIZIONE PROFESSIONAL. 

Di seguito al codice precedente, possiamo inserire le regole CSS che gestiscono la formattazione dell'avatar. Noi abbiamo utilizzato regole di stile minimali, ma qui ci si può sbizzarrire:
<style>
.topic-comment:after {
    content: '';
    display: block;
    clear: both;
}
.topic-comments-user img {
    float: left;
    margin-right: 12px;
    width: 64px;
}
</style>
La prima regola scritta è fondamentale poiché interrompe il flusso degli elementi, evitando sovrapposizioni nel caso in cui l'immagine sia più alta del relativo commento.
Se nel sito sono presenti sia il guestbook che il blog è preferibile inserire il codice CSS una volta sola nel progetto, in Impostazioni Avanzate, Statistiche, SEO e Codice, Esperto, Prima della chiusura del tag HEAD.

Si fa presente che il percorso dell'avatar viene memorizzato, per ogni commento, nel database o in un file di testo quindi, se un utente cambia l'immagine del profilo scegliendo una nuova immagine con uguale estensione, tutti i commenti (vecchi e nuovi) visualizzeranno la nuova immagine poiché la nuova immagine sovrascrive sul server quella vecchia; se però un utente dovesse caricare un'immagine con estensione diversa da quella precedentemente utilizzata, solo i nuovi commenti inseriti avranno memorizzato l'estensione della nuova immagine, quindi i vecchi commenti visualizzeranno l'avatar vecchio, i nuovi commenti quello nuovo.


Un grande ringraziamento a Giorgio per tutto il lavoro che ha svolto e ha condiviso.


Aggiornamento del 10/05/2017

Aggiornato il codice per mantenere sul server avatar dello stesso utente con diversa estensione.

8 aprile 2017

Unite Gallery, una manciata di database e WebSite X5 q.b.

Questa volta, dalla cucina è uscito un mix delle nostre esperienze precedenti: invio e lettura dei dati al database, generazione dinamica di una galleria immagini a cui abbiamo abbinato un plugin veramente versatile, ricco di effetti tutti rigorosamente responsive: Unite Gallery.

Rispetto all'articolo precedente, in cui abbiamo visto come leggere dinamicamente le immagini contenute in una cartella, senza occuparci di come queste vengono caricate sul server, in questo articolo vedremo come rendere il tutto più semplice, anche per un utente poco esperto, prevedendo il caricamento delle immagini attraverso l'Oggetto Modulo Invio E-mail di WebSite X5 e la gestione delle stesse attraverso l'Oggetto opzionale DataBase Viewer.

Ecco la nostra demo:

demo unite gallery su db
DEMO

Vediamo ora come procedere:

Plugin Unite Gallery


Scarichiamo dal sito dell'autore i file del plugin Unite Gallery e, dopo aver estratto il file .zip, creiamo sul server una cartella che chiameremo unite-gallery e carichiamo in essa il contenuto della cartella dist del file estratto. In questo modo avremo a disposizione tutti i temi che il plugin prevede, con le relative risorse.


Nella nostra demo abbiamo provato tre tipi di gallerie: Tiles Justified, Grid Theme e Carousel. Ogni tipo di galleria richiede l'inclusione del file JavaScript unitegallery.js (o nella versione compatta unitegallery.min.js) e del file CSS unite-gallery.css che costituiscono "il motore" del plugin, più altri file .css e .js che dipendono dal tema scelto.

Quando avremo scelto il tema da utilizzare potremo anche cancellare dal server i file non necessari.

Gestione dati


Al Passo 1, Impostazioni Avanzate, Gestione Dati inseriamo i dati di accesso al database forniti dal nostro provider. Indichiamo anche il nome della Cartella su Server con accesso in scrittura poiché qui verranno salvate le immagini che invieremo sul server con il modulo e-mail.

Pagina inserimento e gestione delle immagini


Il form che noi abbiamo preparato prevede, oltre al campo per l'inserimento dell'immagine, alcune opzioni:
  • la possibilità di associare ad ogni immagine una descrizione che verrà visualizzata nell'immagine ingrandita oppure, in alternativa, la possibilità di scegliere come descrizione il nome del file dell'immagine. La descrizione, comunque inserita, varrà anche come valore dell'attributo alt. Qualora nessun campo venisse compilato la descrizione dell'immagine ingrandita e l'attributo alt resteranno vuoti;
  • la possibilità di associare un ulteriore collegamento sull'immagine, oltre a quello all'immagine ingrandita.
Impostiamo quindi nel progetto una pagina protetta ed inseriamo in essa un Oggetto Modulo Invio E-mail.
Nella scheda Elenco inseriamo i campi necessari, come da immagine:


E' importante, oltre al tipo di campi presenti, definire anche, nella scheda Opzioni di ciascun campo, i valori di Attributo <name> e Nome del Campo del Database, valori che verranno utilizzati sia nel codice PHP per la visualizzazione finale delle immagini che nel codice JavaScript (vedremo questo più avanti) che si occupa di mostrare/nascondere il campo relativo alla descrizione in base all'opzione scelta.









Nella scheda Invio come metodo di Invio dei dati scegliamo dal menu a tendina Invia i dati ad un database, valorizzando tutti i campi necessari: il database (nel menu a tendina compare il database precedentemente impostato in Gestione Dati), il nome della tabella in cui verranno memorizzati i dati e l'eventuale indirizzo e-mail a cui verrà notificato ogni nuovo inserimento.

Inseriamo quindi anche l'oggetto opzionale Database Viewer da utilizzare per la modifica dei dati inseriti attraverso il modulo e-mail, impostando i parametri necessari. Nel caso in cui questa pagina venga realizzata per essere utilizzata da un utente poco esperto, si può impedire l'aggiunta di un nuovo record, funzione superflua dal momento che c'è il modulo e-mail che svolge questo compito, con questo CSS che nasconde il relativo controllo, da inserire in Proprietà Pagina, Esperto, Prima della chiusura del tag HEAD:

<style>
.jtable-toolbar-item-add-record {
  display: none !important;
}
</style> 

Sempre in Proprietà Pagina, Esperto, Prima della chiusura del tag HEAD, inseriamo questo codice JavaScript che permette di mostrare/nascondere, nel form, il campo "Descrizione" in base alla scelta effettuata nel campo precedente:

<script>
$(document).ready(function () {
    $("input[name='checkdesc[]']").change(function () {
         if ($(this).is(":checked")) { 
       $("textarea[name='descrizione']").parents(".imObjectFormFieldContainer").css('display', 'none');
       $("textarea[name='descrizione']").val('');
    } else {
         $("textarea[name='descrizione']").parents(".imObjectFormFieldContainer").css('display', 'inline');
    }
   });
   $('input:reset').click(function () {
        $("textarea[name='descrizione']").parents(".imObjectFormFieldContainer").css('display', 'inline');
    })
});
</script> 

Ovviamente se decidiamo di cambiare il valore dell'attributo name dei campi interessati, dovremo modificare il codice.

QUI è visibile un duplicato della nostra pagina di gestione che, per ovvi motivi, è stata modificata in modo che sia non funzionante.


Pagina visualizzazione gallery


Nella pagina dove vogliamo visualizzare le immagini, inseriamo un Oggetto Codice HTML impostando l'altezza dello stesso a 0 (zero) ed inseriamo questo codice:
<!-- ===============================================
Unite Gallery by Max Valiano http://unitegallery.net
Database class: by Incomedia Srl https://www.incomedia.eu  
PHP code  by https://quellidelcucuzzolo.blogspot.it
Please do not remove credit  
================================================ -->
<div id="UniteGallery" style="display:none;">
<?php

//DA PERSONALIZZARE

//Inserire il nome della tabella del database
$qtable = "ugallery";

//Inserire i nomi dei campi del database
$qimmagine = 'immagine';    //campo File Allegato
$qcheck    = 'checkdesc';   //campo Scelta Multipla
$qdesc     = 'descrizione'; //campo Area di Testo
$qlink     = 'link';        //campo Testo

//Inserire l'altezza massima delle miniature
$thumbsH = 300;

//CONNESSIONE

//Prelievo automatico dei dati inseriti nel programma
$qchiave   = array_keys($imSettings['databases']);
$qdati     = getDbData($qchiave[0]);
$qhost     = $qdati['host'];
$quser     = $qdati['user'];
$qpassword = $qdati['password'];
$qdatabase = $qdati['database'];
$qfolder   = $imSettings['general']['public_folder'];
$qdb       = new ImDb($qhost, $quser, $qpassword, $qdatabase);
if (!$qdb->testConnection())
    die("Connessione non riuscita");

//SELECT

//Selezione di tutti i record della tabella
$qitem = $qdb->query("SELECT * FROM $qtable");

//OUTPUT

$urlfile = dirname($_SERVER['PHP_SELF']);
//Iterazione dell'array e creazione della struttura HTML
if ($qitem) {
    $qitem = array_reverse($qitem);
    foreach ($qitem as $val) { 
        if (isset($val[$qdesc]) && $val[$qdesc] != '')
            $qdescimg = $val[$qdesc];
        else if (isset($val[$qcheck]) && $val[$qcheck] != '') {
            $qdescimg = preg_replace('/_\d+/', '', $val[$qimmagine]);
        } else
            $qdescimg = "";
        if (isset($val[$qlink]) && $val[$qlink] != '') {
            echo '<a href="' . $val[$qlink] . '">' . "\n";
        }
        if (isset($val[$qimmagine]) && $val[$qimmagine] != '') {
            echo '<img src="image.php?height=' . $thumbsH .'&image=' . $urlfile . '/' . $qfolder . '/' . $val[$qimmagine] . '" data-image="' . $qfolder . '/' . $val[$qimmagine] . '" alt="' . $qdescimg . '" />' . "\n";
        }
        if (isset($val[$qlink]) && $val[$qlink] != '') {
            echo '</a>' . "\n";
        }
    }
}

//CHIUSURA DELLA CONNESSIONE

$qdb->closeConnection();
?>
</div>
<!-- richiamo funzione Unite Gallery e impostazione stile -->
<script>
jQuery("#UniteGallery").unitegallery({
  tiles_type: "justified",
  tile_show_link_icon: true,
  lightbox_overlay_opacity: 0.95
});
</script>
<!-- Unite Gallery END -->

Le variabili da personalizzare sono quelle indicate all'inizio del codice.
Il richiamo della funzione Unite Gallery, alla fine del codice, dovrà essere personalizzato in base al tema scelto e alle opzioni che si intendono utilizzare e per questo si rimanda al sito dell'autore.

Per la generazione automatica delle miniature, abbiamo utilizzato anche questa volta lo script Smart Image Resize, versione di luciorota: scarichiamo da QUI il file .zip, scompattiamolo e carichiamo nella root del server il file image.php  con un programma FTP o allegandolo nella scheda Esperto dell'Oggetto Codice HTML stesso, lasciando vuoto il campo Percorso relativo sul Server.
Ricordiamo che questo script richiede che sul server siano abilitate le librerie GD.

In Proprietà Pagina, Esperto, impostiamo php come estensione del file generato. Sempre in questa scheda, inseriamo il seguente codice personalizzato Prima dell'apertura del tag HTML:
<?php
require_once("res/x5engine.php");
?>

Inoltre, Prima della chiusura del tag HEAD, inseriamo i richiami ai file JavaScript e CSS del plugin Unite Gallery, file che cambiano in funzione del tema scelto.
Ad esempio, per la galleria Tiles Justified, inseriamo questo codice:

<link rel="stylesheet" href="unite-gallery/css/unite-gallery.css" type="text/css" />
<script type="text/javascript" src="unite-gallery/js/unitegallery.min.js"></script> 
<script type="text/javascript" src="unite-gallery/themes/tiles/ug-theme-tiles.js"></script>

Ovviamente la galleria sarà visibile solo online. Se si vuole scrivere un avviso sulla pagina dell'anteprima è possibile inserire anche questo script:

<script>
$(document).ready(function() {
  // funzionalità offline
  if (location.host == "127.0.0.1:8080")
    $("#UniteGallery").after("La gallery si visualizzerà solo quando il sito verrà esportato su Internet");
});
</script>


Buon appetito... sperando che non facciate indigestione :)