Connectify è un applicazione web simil-twitter molto semplice e intuitiva. E' stata realizzata per il progetto finale del corso di Progettazione di Servizi Web e Reti di Calcolatori di Ingegneria Gestionale L8 da Stefan Patru. L'obiettivo di questo file è quello di descrivere i passaggi che hanno portato alla realizzazione del sito e di spiegare come questo è organizzato, con un focus particolare su determinate scelte che sono state fatte piuttosto che altre. Inoltre, essendo state fatte varie aggiunte rispetto alla traccia iniziale, ho deciso di spiegare il motivo che mi ha portato a implementare alcune funzionalità non richieste. Bisogna comunque considerare che il codice sorgente dell'applicazione è pieno di commenti che spiegano tutte le funzionalità implementate e il modo in cui ciò è stato fatto.
Il codice del sito web è suddiviso in cartelle per garantire una struttura più chiara e modulare. Le pagine principali del sito, come home.php, bacheca.php ecc. si trovano nella cartella sorgente del sito, Connectify. Dopo di che, i restanti file si trovano nelle seguenti cartelle:
- Components: La cartella “Components” contiene file .php che vengono inclusi nelle pagine principali del sito, poiché rappresentano segmenti di codice riutilizzati frequentemente. Ho deciso di organizzare questi blocchi di codice in file separati come aside.php, footer.php, head.php e header.php, per poi includerli nelle diverse pagine utilizzando funzioni PHP per ridurre la duplicazione di codice. Solitamente, per l’inclusione di questi file si utilizza la funzione require_once(), in quanto:
- L’inclusione è necessaria una sola volta per ogni singola pagina.
- Si vuole garantire che il rendering della pagina venga bloccato se l’inclusione fallisce, contrariamente a quanto avviene con include().
- Helpers: La cartella “Helpers” contiene blocchi di codice PHP utilizzati per eseguire diverse procedure. Ad esempio:
- db_connection_normale.php: gestisce la connessione al database per un utente con accesso in sola lettura.
- db_connection_privilegiato.php: gestisce la connessione al database per un utente privilegiato.
- registration_process.php: si occupa della validazione lato server e dei controlli necessari per la registrazione di un nuovo utente, includendo l’inserimento delle informazioni nella tabella appropriata.
- login_process.php: gestisce l’autenticazione dell’utente, inclusa la validazione dei dati e la creazione della sessione.
- new_tweet.php: gestisce l’inserimento di un nuovo tweet nella tabella ‘tweets’.
-
Images: Cartella contenente le immagini utilizzate per il sito web.
-
Resources: La cartella “Resources” contiene le risorse esterne utilizzate per aggiungere dinamicità al sito, come i file JavaScript, e per migliorarne lo stile, come il file CSS. La scelta di utilizzare codice JS e CSS in file esterni, anziché inline, è stata fatta per migliorarne la manutenibilità, infatti separando la logica e lo stile dal markup HTML è più facile aggiornare e gestire il codice, e per promuove la riusabilità, in questo modo gli stessi file JS e CSS possono essere utilizzati su più pagine, riducendo la duplicazione del codice.
Tutte le pagine del sito sono caratterizzate da un head, un header e un footer in comune, che si trovano nella cartella "Components" e che vengono incluse con require_once() in tutte le pagine principali del sito.
L'head serve per indicizzare le pagine e contiene i meta dati, il collegamento al foglio di stile CSS e il titolo della pagina. Essendo l'head incluso in ciascuna pagina, il titolo viene assegnato dinamicamente mediante la variabile $pageTitle definita in PHP e che assumerà dei valori arbitrari a seconda della pagina .php in cui l'head viene incluso.
L'header è diviso in tre sezioni principalmente: in alto a sinistra, qualora l'utente non fosse autenticato, è presente un invito a unirsi alla nostra community. In caso di utente autenticato, invece, è presente un saluto indicando lo username dell'utente presente e gli ultimi 30 caratteri dell'ultimo tweet che l'utente ha pubblicato. Nella sezione centrale è presente il logo di Connectify. Infine, a destra, è presente la barra di navigazione: le voci Home, Registrati, Login, Scopri, Scrivi, Bacheca e Logout sono sempre presenti ma, quando l'utente non è loggato, è stato aggiunta la classe "disabled" a Bacheca, Scrivi, Logout, non permettendo all'utente di accedervi dalla navbar. Al contrario, quando l'utente è loggato, potrà accedere alla propria bacheca e alla pagina scrivi, ma saranno disabilitate le voci Registrati e Login dalla navbar (essendo per l'appunto l'utente già loggato, non ha senso permettergli di effettuare un nuovo login!).
Il footer invece presenta il mio nome, il mio numero di matricola, e l'informazione sulla pagina .php che si sta visualizzando al momento. Questa informazione viene salvata in modo dinamico all'interno del footer tramite la funzione basename($_SERVER['PHP_SELF']).
La pagina principale del sito è home.php. La home è caratterizzata da una presentazione generale del sito in cui vengono descritte le principali attività che possono essere fatte, ed è presente una sezione di testimonianze, con un paio di recensioni scritte dagli utenti di Connectify. Sulla sinistra è presente un aside, contenente al suo interno un menù di navigazione che porta ad altre due pagine: chi_siamo.php, che fornisce alcune informazioni sul creatore del sito e sui proprio valori, e faq.php, con una serie di domande che potrebbero sorgere agli utenti del sito. Le pagine chi siamo e FAQ non erano richieste dalla traccia, ma ho pensato di realizzarle perchè potrebbero essere molto utili per fornire maggiore trasparenza e supporto agli utenti, migliorando così l’esperienza complessiva di utilizzo. L'aside è presente in tutte e tre le pagine, quindi è molto semplice navigare tra di esse direttamente cliccando i link al suo interno. Il nome della pagina che si visualizza in quel momento viene evidenziata di arancione, colore dominante di Connectify.
Dalla barra di navigazione si può accedere facilmente alla pagina registrazione.php (se l'utente non è autenticato). Questa pagina è caratterizzata da un form reso stilisticamente simile alle restanti pagine dell'applicazione. All'interno di questo form ci sono diversi campi di input, ciascuno caratterizzato da una propria label, che l'utente utilizzerà per inserire i propri dati personali utili alla registrazione. Se i dati inseriti rispettano i requisiti richiesti e superano la validazione lato client, l'utente può inviare il form al server, e viene reindirizzato alla pagina registration_process.php. In questa pagina, inizialmente avviene la validazione lato server di tutti i dati tramite espressioni regolari e vari controlli (come ad esempio l'utilizzo di username già utilizzati nell'applicazione). Se c'è qualche errore, l'utente viene reindirizzato alla pagina di registrazione con un messaggio di errore, altrimenti i suoi dati vengono inseriti nel DB nella tabella utenti con una query SQL di INSERT e viene indirizzato alla home con un messaggio di successo, e avrà la possibilità di effettuare il login. Per quanto riguarda la data di nascita, vengono fatti due controlli non richiesti dalla traccia che ci tengo a specificare: si verifica che la data di nascita inserita sia valida (quindi una data effettivamente esistente, non il 31 novembre ad esempio), e viene verificata l’età dell’utente calcolando la differenza tra la data di nascita inserita e la data corrente. Se l’età risultante è inferiore a 13 anni, l’utente viene reindirizzato nuovamente alla pagina di registrazione con un messaggio di errore specifico relativo al requisito dell’età minima. L’età di 13 anni è stata scelta perché la maggior parte dei social network richiedono questa età minima.
Per il login è stata creata una pagina apposita login.php. In questa pagina è presente un form con due campi, username e password, che l'utente deve compilare per effettuare il login. Sono presenti due pulsanti. Il primo, "Continua senza autenticarsi", farà skippare il login all'utente che verrà indirizzato direttamente alla pagina scopri.php. Il secondo, "Invia", trasmetterà i dati inseriti nel form dall'utente alla pagina login_process.php, dove oltre alla validazione lato server viene gestita la creazione della sessione di autenticazione nel caso di dati correttamente inseriti. La sessione di autenticazione viene creata mediante due variabili principali nell'array associativo globale $_SESSION:
- $_SESSION['user']: all'interno di questa variabile salvo un array associativo dove mi salvo nome, cognome, e tutti i dati dell'utente
- $_SESSION['autenticato']: variabile booleana che assume il valore true quando l'utente effettua l'autenticazione Oltre alla sessione, viene creato anche un cookie chiamato 'last_username' dalla durata di 16 ore all'interno del quale viene salvato lo username dell'utente che sta facendo correttamente l'accesso. Questo cookie viene utilizzato nella pagina di login per precompilare lo username con l'ultimo username che ha fatto l'accesso correttamente a Connectify su quel browser (e questo dato lo trovo per all'interno del cookie!). Se il login va a buon fine, l'utente viene reindirizzato alla propria bacheca con un messaggio di successo, altrimenti viene nuovamente indirizzato alla pagina di login con un messaggio di errore.
La bacheca dell'utente si trova all'interno del file bacheca.php. L'utente autenticato può accedervi dalla navbar. Questa pagina è caratterizzata da una sezione contenente le informazioni personali dell'utente, come username, nome, cognome, data di nascita, indirizzo (tutti dati presi dalla variabile globale $_SESSION) e da un'altra sezione contenente i tweet che l'utente ha inviato in ordine (per prima vengono visualizzati i tweet più recenti). Qualora l'utente non avesse pubblicato tweet su Connectify, viene visualizzato un messaggio che invoglia a farlo con un link diretto alla pagina scrivi.php. I tweet sono presentati "uno per riga", e all'interno dell'article contenente il tweet sono presenti lo username e il nome dell'utente che ha pubblicato il tweet, il testo del tweet e la data e orario in cui il tweet è stato pubblicato su Connectify dall'utente. All'interno della bacheca, quando è presente almeno un tweet dell'utente, viene visualizzato anche un filtro per data. Questo filtro permette di inserire una data di inizio e una data di fine e, tramite uno script in JavaScript, consente di visualizzare solamente i tweet che rientrano nell'intervallo temporale selezionato. L'eventListener che attiva la funzione di filtraggio è aggiunto ai due campi di input con l'evento 'change', quindi ogni volta che uno dei due campi viene modificato, la funzione di filtraggio si attiva. Questo significa che, inserendo solo una data di inizio, verranno visualizzati tutti i tweet pubblicati successivamente a quella data; selezionando invece solo una data di fine, verranno visualizzati tutti i tweet pubblicati fino a quella data. Non è quindi necessario indicare sia una data di inizio che una data di fine per attivare il filtro. È stato scelto di implementare il filtro lato client e non lato server per diversi vantaggi. Filtrando i tweet direttamente nel browser dell’utente, si riducono i tempi di attesa e si migliora l’esperienza utente, poiché non è necessario fare richieste al server. Questo rende l’interfaccia più reattiva. Inoltre, riducendo le richieste al server, si diminuisce il carico di lavoro del server stesso, utile soprattutto in caso di numerosi utenti simultanei. Il filtraggio lato client permette di sfruttare la cache del browser, poiché i dati vengono scaricati una sola volta e poi manipolati localmente, riducendo ulteriormente i tempi di caricamento e il consumo di banda.
Scrivi.php è la pagina che permette di pubblicare nuovi tweet. E' caratterizzata da un form semplice con un textarea all'interno del quale l'utente può inserire il proprio tweet. Il tweet per poter essere sottomesso dovrà avere almeno 1 carattere, ma meno di 140. E' presente inoltre un span sotto al textarea che permette all'utente di visualizzare il numero di caratteri a disposizione rimanenti per scrivere il tweet. Se la validazione lato client non dà problemi, l'utente può pubblicare il tweet mediante il pulsante "Invia". Questo passa i dati al server nella pagina new_tweet.php degli Helpers, dove il tweet verrà validato anche backend, e in seguito caricato nella tabella 'tweets' all'interno del DB. L'utente viene cosi reindirizzato nella propria bacheca dove potrà visualizzare il tweet in prima posizione (perchè sarà il più recente).
La pagina scopri.php invece è accedibile anche dagli utenti non autenticati. All'interno di questa pagina sono presenti i tweet di tutti gli utenti, in ordine di caricamento (i tweet più recenti vengono visualizzati più in alto). Ho deciso inoltre di aggiungere un filtro all'interno dell'pagina: si tratta di un campo di input type = "text" all'interno del quale l'utente può inserire una parola o una frase, e tramite uno script JS, verrano visualizzati i soli tweet contenenti all'interno del proprio testo la parola/frase inserita dall'utente. L’ho fatto perché ritengo che questa funzionalità possa migliorare notevolmente l’esperienza di navigazione, permettendo agli utenti di trovare più facilmente i contenuti di loro interesse e rendendo la ricerca dei tweet più efficiente e personalizzata.
Infine, la pagina di logout.php ha lo scopo di distruggere la sessione presente (con i dati dell'utente salvati al proprio interno), e ne creerà una nuova che servirà per contenere un messaggio di successo che verrà mostrato nella home quando l'utente verrà reindirizzato.
Se un utente non autenticato tenta di accedere alla bacheca o alla pagina di scrittura tramite URL, verrà visualizzata una pagina di errore che spiega il motivo e fornisce un link per registrarsi sul sito, insieme a un'icona che, se cliccata, riporterà l'utente alla homepage. Analogamente, se un utente autenticato prova ad accedere alla pagina di registrazione o di login tramite URL, verrà mostrata una pagina di errore molto simile.
La validazione dei dati dei form su Connectify è sempre stata fatta sia lato server che lato client. Questo perché la validazione lato client migliora l’usabilità, fornendo feedback immediato agli utenti e riducendo il carico sul server, mentre la validazione lato server garantisce la sicurezza e l’integrità dei dati, prevenendo l’invio di dati malformati o potenzialmente dannosi (che l'utente potrebbe potenzialmente inviare disabilitando JavaScript sul proprio browser). La validazione lato client è quasi sempre effettuata tramite script JS, con l'aggiunta di eventListener su determinati elementi HTML, e mediante l'utilizzo della funzione test() per verificare la correttezza dei dati con le espressioni regolari. La validazione lato server è stata fatta in egual modo a quella lato client, mediante l'utilizzo di funzioni PHP che verificano l'esistenza delle variabili, come isset() e empty(), e mediante la funzione preg_match() per verificare la correttezza dei dati rispetto alle espressioni regolari.
Nel contesto della sicurezza informatica, sono state implementate diverse metodologie apprese a lezione. Tra le più importanti vi è l’utilizzo del metodo POST per l’invio dei form al server, anziché il metodo GET. Il metodo POST è preferibile perché non appende i dati del form all’URL, migliorando la gestione della privacy dei dati e rendendoli meno visibili nei log del server e nella cronologia del browser. Un’altra pratica fondamentale implementata è l’uso delle query parametriche tramite prepared statement. Queste query separano la logica della query dai dati forniti dall’utente, proteggendo da SQL Injection, una delle vulnerabilità più comuni e pericolose nelle applicazioni web. Inoltre, i prepared statement permettono al database di pre-compilare la query una volta e riutilizzarla con diversi parametri, migliorando le prestazioni dell’applicazione e riducendo il carico di lavoro sul database. Avrei voluto anche procedere con l'hashing della password, ma il professore ha specificato che utilizzera il DB di partenza per fare dei test sul sito e quindi ci sarebbero stati dei problemi, non essendo criptate la password degli utenti iniziali presenti nel DB.
Per migliorare l'usabilità del sito, è stato implementato un sistema che fornisce sempre messaggi chiari e precisi agli utenti riguardo la buona riuscita delle azioni svolte. Ogni volta che un utente esegue un'azione su Connectify, viene impostato un messaggio nelle variabili di sessione $_SESSION['error'] o $_SESSION['success'], che viene poi visualizzato nella pagina verso cui l'utente è reindirizzato. Dopo aver mostrato il messaggio, la variabile di sessione viene distrutta tramite la funzione unset() per evitare che il messaggio venga visualizzato nuovamente nel caso in cui la pagina venga ricaricata. Inoltre, il sito è stato reso completamente responsive per garantire una visualizzazione ottimale su diversi dispositivi come telefoni, tablet e desktop. Questo è stato realizzato utilizzando media query nel CSS che applicano stili specifici in base alla larghezza dello schermo del dispositivo dell’utente. Grazie a questo approccio, gli utenti possono navigare su Connectify con facilità indipendentemente dal dispositivo utilizzato, godendo di un’esperienza coerente e intuitiva. In aggiunta, è stato utilizzato @media print per definire uno stile apposito per la stampa. Questo garantisce che quando gli utenti stampano una pagina del sito, il layout e lo stile siano ottimizzati per la carta, assicurando una qualità di stampa professionale. Gli elementi non essenziali per la stampa, come le barre di navigazione e i contenuti interattivi, vengono nascosti, mentre il testo e le immagini principali vengono formattati per garantire leggibilità e chiarezza su carta, in accordo con le nozioni apprese dalle slide del corso riguardo le pagine web printer-friendly (Tecniche di buona progettazione di pagine web - slide 11). Infine, per gestire i casi in cui gli utenti abbiano disabilitato JavaScript nel proprio browser, è stato aggiunto il tag . Questo tag contiene un messaggio che avvisa gli utenti dell’importanza di abilitare JavaScript per un’esperienza completa e senza problemi su Connectify. Il messaggio all’interno del tag informa gli utenti che alcune funzionalità del sito potrebbero non essere disponibili senza JavaScript e li invita a riattivarlo per usufruire appieno di tutte le caratteristiche offerte.