:::: MENU ::::

Progetto completo di una chat con Server-Sent Event e HTML5 + Jquery

Continuiamo la panoramica sulle API Server-Sent Event in HTML5. In questo caso scopriremo e analizzaremo passo dopo passo come creare una chat funzionante. La ragione di questo esempio è principalmente applicare i concetti visti negli articoli precedenti, per meglio comprendere la potente tecnologia che HTML5 ci offre.

Avremo bisogno al solito di un web server, va bene anche XAMPP o qualsiasi altro web server Apache+PHP+MySQL.

Il database

La prima cosa da fare è creare il database con la tabella che conterrà i messaggi che gli utenti invieranno alla chat e che saranno consegnati a tutti i client in ascolto. Il database ha una sola tabella, mediante la quale, oltre a fare da intermediario potrebbe anche rappresentare un’ottimo strumento per filtrare parole non gradite. Lo script SQL è il seguente, potete eseguirlo facilmente su PhpMyAdmin.

La chat

 Chiamiamo questo file chat.html, ed è il file che conterrà il codice HTML+CSS e gli script Javascript del client. L’interfaccia è molto semplice ed è composta principalmente da un contenitore DIV in cui vengono aggiunti man mano i sotto-contenitori DIV dei messaggi. Per completare, ho posizionato sotto la chat due caselle di testo per inserire il nome, il messaggio e il pulsante per inviare tutto al server e di conseguenza agli altri client connessi.

Stream chat

Chiamiamo questo file chatlist.php. Lo stream dei messaggi sarà formato da un file PHP, in particolare in ciclo infinito che effettua un’interrogazione al database MySQL per poi “addormentarsi” per un secondo. Le prime due righe di intestazione sono molto importanti per il meccanismo SSE di HTML5, in quanto per il corretto funzionamento della chat, è necessario impostare il Content-Type come “text/event-stream” e definire il Cache-Control uguale a “no-cache”. Non dimenticatelo.

Le operazioni sul database

Tutte le operazioni sul database preferisco scriverle in un file diverso dagli altri, per mettere un po’ di ordine. Lo chiamiamo dbfunctions.inc e contiene tra l’altro la gestione della connessione al database, oltre alla stampa a video del messaggio necessario al Server-Sent Event.

Il blocco più interessante è quello della funzione ListChat(). Questa funzione interroga il messaggio cercando tutti quei messaggi che sono stati spediti al server nel periodo compreso tra l’ultima interrogazione $MM_LastTime e l’ora corrente $MM_Time. In questo modo vengono pescati solo le ultime righe senza far perdere alcun messaggio. Quindi vengono estratti i campi, formattati in HTML e restituiti alla funzione sendMsg() che si occupa di comporre il messaggio per SSE.

File di supporto

Nell’ultimo file, chiamato chatsubmit.php, inserisco la procedura che viene attivata durante la chiamata di chatTimerHandler() nel file chat.html tramite Ajax con metodo GET. Lo script non fa altro che inserire nel database MySQL il nome e il messaggio digitato dall’utente in modo da poter essere “comunicato” a tutti quanti i client in ascolto.

 


Server-Sent Events in HTML5

Tradizionalmente in un’architettura client-server, come mostrato in figura, una pagina web residente nel client invia una richiesta al server per ricevere una risposta con dei dati. Con l’avvento dell’HTML5, e in particolare della libreria API Server-Sent Events, è possibile, per un server, inviare dei dati a una pagina web “in qualsiasi momento”, tramite dei messaggi push, senza che la pagina in questione ne faccia esplicita richiesta. I messaggi in arrivo possono essere trattati come eventi + dati all’interno della pagina web. 

client-server

Lo sviluppo di un’applicazione web che utilizza le API Server-Sent Events è abbastanza facile. Avrete bisogno di un po’ di codice sul server per lo effettuare lo streaming degli eventi al client; per quanto riguarda il lato client, la gestione di tale eventi è identica a qualsiasi altro tipo di evento.

Le API Server-Sent Events sono contenute nell’interfaccia EventSource. Per aprire una connessione al server e iniziare a ricevere gli eventi, bisogna creare un nuovo oggetto EventSource, specificando l’URI dello script sul server che genera lo stream di eventi. Per esempio:

Una volta istanziato l’oggetto, è possibile iniziare  ad intercettare i messaggi che verranno creati dallo script in sse_test.php:

Lo script sopra non fa altro che “ascoltare” eventuali messaggi in arrivo (cioè, dei messaggi standard dal server ) e aggiungere il testo del messaggio in un contenitore div che poi viene mostrato nella pagina.

È anche possibile ascoltare eventi personalizzati, utilizzando la funzione addEventListener():

Questo codice è simile al precedente, tranne per il fatto che verrà chiamato solo quando il server invierà un messaggio di tipo ping al client.

Il formato del messaggio

Il messaggio che viene inviato dal server al client è un semplice streaming di testo in formato JSON, codificati in UTF-8. Ogni messaggio è separato da una coppia di caratteri speciali \n\n. Una riga del emessaggio che inizia con : è, in sostanza, una riga di commento e viene ignorata. La riga di commento può essere usata per impedire il timeout della connessione: un server può inviare periodicamente un commento per mantenere aperta la connessione.

Ogni messaggio è costituito da una o più righe di testo scritte secondo la notazione nomecampo: valore + \n

Campi

I campi che possono essere utilizzati in un messaggio sono 4. Altri campi, al di fuori di questi, vengono sostanzialmente ignorati.

  • event – rappresenta il tipo di evento. Se nel messaggio viene specificato questo campo, l’evento inviato al browser potrà essere intercettato solo utilizzando addEventListener(). L’handler onMessage() invece intercetta qualsiasi messaggio privo del campo event.
  • data – rappresenta il campo dati per il messaggio, cioè il contenuto che dovrebbe essere visualizzato sul browser. Quando EventSource() riceve più linee che iniziano con “data:”, li concatena, e inserisce un ritorno a capo \n tra ciascuno di essi.
  • id – rappresenta l’ID del messaggio.
  • retry – rappresenta il tempo di riconnessione in millisecondi da utilizzare quando si tenta di inviare un messaggio. Se non viene specificato un valore intero, il campo viene ignorato.

Esempi

Messaggi “data only”

In questo esempio, si ipotizza l’invio di tre messaggi. Il primo messaggio è solo un commento, poiché comincia con i due punti. Come accennato in precedenza, può essere utile come keep-alive della connessione se lo streaming di dati non è costante e c’è il rischio di far cadere la connessione. Il secondo messaggio contiene il campo data con il valore “testo di esempio“. Il terzo messaggio contiene il campo data con il valore “un altro messaggio \n con due linee“.

Eventi specifici

In questo esempio abbiamo alcuni eventi specifici. Ciascun messaggio viene settato il campo event, con il nome della tipologia di evento, e il campo data il cui valore è specificato secondo la notazione JSON con i dati che poi il client “parserizzerà” a dovere.

Miscelazione e la congruenza

Ovviamente è possibile mescolare insieme in un unico flusso di dati sia messaggi generici (solo con il campo data) che messaggi personalizzati.

 Risorse utili

http://dev.w3.org/html5/eventsource/


La distanza di Levenshtein, a cosa serve? (2° parte)

Un’implementazione della distanza di Levenshtein può essere fatta in qualsiasi linguaggio. In questo esempio, verrà utilizzato interamente il linguaggio MYSQL.

Per prima cosa, bisogna creare una routine su MySql. Aiutatevi con phpmyadmin per fare ciò. Eseguite direttamente il codice definito in questo listato per creare e aggiungere la funzione levenshtein() al vostro database MySql.

Se volete potete anche aggiungere quest’altra funzione, che restituisce la percentuale di esattezza della parola data.

A questo punto l’unica cosa da fare è quella di creare una query per interrogare il database utilizzando in modo opportuno la funzione definita sopra.

Il risultato sarà questo:

levenshtein2

L’unico vero e grande problema è la lentezza dell’esecuzione dell’algoritmo. Per calcolare la distanza di levenshtein di “romme” su una base dati di 8,092 record, il tempo di esecuzione si aggira attorno ai 12 secondi, e peggiora in maniera esponenziale se la stringa da ricercare è più lunga (es. Castelbuono anzichè Roma). Purtroppo l’algoritmo contiene un doppio WHILE all’interno e questo potrebbe rappresentare un ostacolo, sopratutto in una tabella con migliaia di record.

Tuttavia possiamo cercare di ottimizzare la query MySql in fase di interrogazione in modo da cercare di dimezzare i tempi di ricerca. Una prima ottimizzazione è quella di selezionare solamente i termini più “vicini” a quello che stiamo cercando, confrontando la lunghezza degli stessi. Aggiungo quindi una condizione sulla lunghezza del campo CityName, che deve essere di un carattere più (+1) o meno (-1) lunga rispetto a ciò che sto cercando.

 levenshtein3

Abbiamo un netto miglioramento in termini di prestazioni in fase di esecuzione: 1,17 secondi contro 12 secondi del primo! 

Esistono ovviamente altre ottimizzazioni, infatti ne ho implementata una che mi permette di raggiungere un tempo di esecuzione pari a 0,5740 secondi, ma in questo caso bisogna integrare qualche riga di codice PHP. Vi lancio la sfida di cercarne una! Qui sotto invece trovate un altra ipotesi di ottimizzazione…

Spoiler Inside SelectShow

 


La distanza di Levenshtein, a cosa serve? (1° parte)

In teoria dell’informazione e informatica , la distanza di Levenshtein è un’unità di misura per calcolare la differenza tra due stringhe. In altre parole, la distanza di Levenshtein tra due parole A e B è il numero minimo di modifiche di un carattere singolo (inserimento, cancellazione, sostituzione) necessario per modificare la parola A nella parola B. Con edit distance ci si riferisce spesso alla distanza di Levenshtein. La funzione prende il nome dal suo creatore Vladimir Levenshtein, scienziato russo specializzato in teoria dell’informazione, che la scoprì nel 1965.

Esempio

Calcolare la distanza di Levenshtein tra “kitten” e “sitting”. Ovviamente la risposta è 3, poiché sono necessarie tre modifiche per trasformare una parola nell’altra e non c’è altro modo per farlo con meno di tre modifiche:

  1. kitten → sitten (sostituzione di “k” con”s”)
  2. Sitten → Sittin (sostituzione di “e” con “i”)
  3. Sittin → sitting (inserimento di “g” alla fine).

Alcune precisazioni

La distanza di Levenshtein ha alcuni limiti superiori e inferiori. Questi includono:

  • La lunghezza è sempre almeno la differenza della lunghezza delle due stringhe.
  • La lunghezza può essere al massimo uguale alla lunghezza della stringa più lunga.
  • È zero se, e solo se, le stringhe sono uguali.
  • Se le stringhe sono le stesse dimensioni, la distanza di Hamming è un limite superiore alla distanza Levenshtein.
  • La distanza di Levenshtein tra due stringhe non può essere maggiore della somma delle loro distanze di levenshtein da una terza stringa (disuguaglianza triangolare).

Un classico utilizzo

Avete presente quando sbagliate a digitare una parola da cercare con Google? Il motore di ricerca vi consiglia una correzione della parola errata. Guardate l’esempio qui sotto, vi siete mai chiesti come fa Google a correggere le parole errate? La risposta è semplice: Levenshtein!

levenstein

La funzione levenhstein() in PHP

In php è presente da tempo una funzione chiamata appunto levenhstein(), e viene definita di seguito:

dove:

  • str1: prima stringa in ingresso da confrontare
  • str2: seconda stringa in ingresso da confrontare
  • cost_ins: definisce il “costo” dell’operazione di inserimento di un carattere
  • cost_rep: definisce il “costo” dell’operazione di sostituzione di un carattere
  • cost_del: definisce il “costo” dell’operazione di cancellazione di un carattere

continua…


mysql_connect() o mysql_pconnect(): quale usare?

Quando tentate di connettervi ad un database MySQL utilizzando PHP, solitamente avete a disposizione due funzioni per effettuare il collegamento. Esse sono:

  1. mysql_connect()
  2. mysql_pconnect()

Vediamo la differenza tra le due funzioni, qui di seguito.

funzione mysql_connect()

Questa funzione crea una nuova connessione al database una volta che lo script inizia l’esecuzione e chiude la connessione al database una volta esecuzione dello script termina. Questo significa che PHP effettuerà una connessione al database MySQL ogni volta che il nostro script viene eseguito.

funzione mysql_pconnect()

Quando si effettua un tentativo di connessione al database utilizzando mysql_pconnect(), questa funzione cercherà un collegamento esistente al database utilizzando al solito username e password. Se viene trovata una connessione esistente, la funzione restituisce l’ID della risorsa, altrimenti aprirà una nuova connessione e restituirà l’ID della nuova risorsa.

Dopo un’ora di connessione verrà restituito solo l’ID della risorsa (se esistente) e non cercherà, a differenza di mysql_connect() di aprire una nuova connessione ogni volta che lo script viene richiamato. Ovviamente la connessione non verrà chiusa una volta terminato lo script. Per questo motivo la connessione è persistente.

Tuttavia mysql_pconnect() richiede una certa ottimizzazione del server e potrebbe essere necessario limitare il numero di connessioni (per via delle risorse limitate) e configurare sia il timeout sia come trattare l’inattività (idle) delle connessioni aperte.

Quando usare una o l’altra funzione?

Si dovrebbe usare mysql_pconnect() quando il sito genera un alto traffico e quindi maggiori richieste di connessioni al DB. Se PHP e MySQL risiedono sullo stesso server o sulla stessa rete locale, il tempo di connessione può essere trascurabile, nel qual caso non vi è alcun vantaggio nell’uso delle connessioni persistenti.

Cose da tenere a mente durante l’utilizzo mysql_pconnect

Quando si interroga una tabella, MySQL effettua un lock su una tabella e normalmente la tabella viene sbloccata quando il collegamento si chiude. Dal momento che le connessioni persistenti non si chiudono al termine dello script, le tabelle potrebbero rimanere in uno stato di lock, e l’unico modo per sbloccarle è quello di attendere il timeout della connessione o “killare” il processo.

Le tabelle temporanee in MySQL vengono solitamente eliminate quando la connessione si chiude, ma dal momento che le connessioni persistenti non si chiudono, le tabelle temporanee non sono così temporanei. Se non si cancellano esplicitamente le tabelle temporanee, tali tabelle rimarranno disponibili ad altri client che riutilizzano la stessa connessione.

Il server Apache non funziona bene con le connessioni persistenti. Quando riceve una richiesta da un nuovo cliente, invece di utilizzare una connessione persistente aperta, tende a generare un nuovo figlio, il quale si occuperà di aprire una nuova connessione al database. Questo causa un eccesso di processi dormienti, uno spreco inutile di risorse, causando degli errori quando si raggiungere limite massimo di connessioni aperte, e ciò annulla qualsiasi vantaggio sulle connessioni persistenti.

A voi la scelta!


8 passi per creare un digital storytelling

Le storie ci uniscono, ci incoraggiano a capire ed empatizzare e ci aiutano a comunicare meglio. Molto tempo prima che la carta e l’avvento dei libri diventassero comuni e accessibili a tutti, le informazioni venivano trasmesse di generazione in generazione attraverso la tradizione orale del racconto. Il Digital Storytelling può essere considerato come una versione moderna dell’antica arte di narrazione. Grazie agli strumenti digitali oggigiorno chiunque può creare una storia e condividerla con il mondo. Continue Reading


Una guida (di base) per creare delle password robuste

“Treat your password like your toothbrush. Don’t let anybody else use it, and get a new one every six months” – Clifford Stoll

 

Quando si crea un account su un sito web, si è colti per un momento dalla “sindrome della password”. Il dilemma è se è necessario fornire una password debole che è facile da ricordare o una password complessa che è difficile da ricordare.

Qui di seguito vengono specificate alcune regole e le linee guida che possono aiutare a superare questo vostro dilemma e vi aiuterà a creare una password complessa e sicura.  Continue Reading


Verificare l’esistenza di un URL con PHP

In questo articolo vedremo come controllare l’esistenza di un determinato URL utilizzando PHP. In parole povedere dobbiamo verificare se sul server esistono o meno i contenuti che stiamo richiedendo attraverso l’URL. Possiamo agevolmente verificare l’esistenza di URL in due modi:

  • il primo è la funzione get_header();
  • il secondo metodo è l’uso della libreria CURL.

Fondamentalmente bisogna verificare l’header HTTP dell’URL e sulla base di questo determinare se esiste oppure no. Il codice 200 rappresenta un URL funzionante mentre il codice 404 rappresenta la pagina non trovata, quindi un URL non funzionante o inesistente.

Metodo 1: Funzione get_headers()

Usando la funzione get_headers siamo in grado di ottenere le informazioni dell’Header HTTP dell’URL.

Nota: Se si imposta il secondo parametro della funzione get_headers() a true allora si otterrà il risultato in un array associativo.

Metodo 2: libreria cURL

Nota: Abbiamo usato CURLOPT_NOBODY per controllare solo per l’instestazione, il resto del contenuto non ci interessa.

Condividete le vostre opinioni / commenti / suggerimenti qui sotto.


Sette cose che (forse) non sapevi sulla stampa 3D

3d

 

La stampa 3D sta rivoluzionando il mondo della produzione. Ma cosa sono e di cosa sono fatti gli oggetti “stampati” in 3D, e in che modo tale processo potrebbe influenzare il modo in cui i produttori lavorano? Date un’occhiata a sette cose che probabilmente non sapeva di stampa 3D.

1. LA stampa 3D inizia con il laser

I primi tentativi di stampa 3D iniziarono intorno al 1980. Fu Charles Hull nel 1984 ad sperimentare la stereolitografia 3D: veniva posizionava una piastra perforata appena sotto la superficie di una vasca prina di fotopolimero liquido e un fascio di laser UV tracciava quindi la prima fetta dell’oggetto sulla superficie di questo liquido, causando l’indurimento di un sottilissimo strato di fotopolimero. Attraverso migliaia di passaggi era possibile costruire un oggetto di plastica.

2. Le stampanti 3D moderne funzionano come una stampante a getto d’inchiostro

L’evoluzione della stereolitografia si chiama estrusione. Con questo metodo, una stampante costruisce un oggetto attraverso l’uso di materia che fuoriesce da una testa meccanica provvista di piccoli ugelli, un po’ come accade proprio in una stampante a getto d’inchiostro che produce testo e grafica in  una pagina mediante l’estrusione dell’inchiostro sulla carta.

3. Qualsiasi cosa può essere stampata in 3D (persino il cioccolato!)

La moderna tecnologia consente di stampare in 3D su calcestruzzo, pietra sintetica, ceramica, cioccolato e persino formaggio. Alcune ricercatori stanno studiando metodi per stampare utilizzando dei metalli – acciaio inox o alluminio. In questo caso viene utilizzato un laser o un fascio di elettroni che permetta di incollare i diversi strati.

Al giorno d’oggi attraverso la stampa 3D è possibile di stampare qualsiasi cosa: dalle protesi per il corpo agli articoli per la casa. In un futuro non troppo lontano, gli studiosi credono sia possibile stampare organi e tessuti umani in 3D, e persino motori a reazione di prossima generazione.

4. La stampa 3D significa meno rifiuti in fase di produzione

In passato, per realizzare un oggetto bisognava perforare, tagliare, levigare o riempire con una certa quantità di materiale grezzo degli stampi preformati. La lavorazione del materiale, produceva una significava quantità di scarto che andava perso. Con la stampa 3D tutto ciò non succede perchè si tratta di un processo additivo: l’oggetto si crea dal materiale di base. Pensate un po’ alle implicazioni e ai vantaggi che ciò porterebbe ai produttori, che si vedrebbero in pratica quasi annullare la quantità dei rifiuti dovuti agli scarti di lavorazione.

5. I produttori rischiano meno con la stampa 3D

Con la stampa 3D, l’intera equazione di scala cambia. Invece di avere una macchina per ogni oggetto da costruire, attraverso una singola stampante 3D viene stampato ciò che il software dice di voler estrudere. In questo modo, non è necessario fare migliaia di copie per un singolo prodotto per sopperire e ammortizzare i costi dell’avvio dell’impianto. La produzione diventa più agile e meno rischiosa.

6. Ognuno ottiene ciò che vuole

Ecco alcuni esempi: un produttore di auto potrebbe creare dei componenti per un modello di nicchia senza dover produrre un volume sproporzionato di pezzi. Questo è solo uno degli esempi, ma è chiaro che al giorno d’oggi siamo più disposti a scegliere dei prodotti che rispecchiano il nostro gusto e i nostri interessi. Per questo motivo, sempre più aziende si stanno attrezzando e scelgono una produzione di prodotti personalizzati che possono essere, in taluni casi, co-creati di concerto  con il cliente finale.

7. La stampa 3D potrebbe essere un forte modello di business locale

Noah Lewis, Managing Director at GE Ventures, afferma che “I prossimi 10 anni si svilupperanno su azioni localizzate, compresa la produzione. Anche se questa tendenza non eliminerà del tutto la produzione così come lo conosciamo, ciò trasformerà profondamente i paradigmi di produzione e ci permetterà una ri-localizzazione selettiva e quindi di fabbricare il futuro “.

Tutto questo senza parlare di cosa accadrà quando la stampa 3D si diffonderà nelle nostre case. Un giorno, la domanda principale non sarà dove sono stati prodotti i nostri oggetti 3D; la questione sarà, in quale stanza della nostra casa abbiamo scaricato e creato le cose che desideriamo di più?

 


Output buffering con PHP e ob_flush()

L’output buffering è un’interessante caratteristica di PHP. Quando si esegue uno script PHP e si genera un contenuto di output (codice HTML con la funzione echo ad esempio), tale contenuto HTML viene trasferito dal server al browser. Ma cosa succede se si prova a generare dei contenuti di output mentre si esegue uno script?

Normalmente quando il server è impegnato in qualche operazione, l’output viene momentaneamente sospeso in attesa di terminare lo script. Il motore PHP memorizza gli eventuali contenuti di output in un buffer interno piuttosto che passarli subito al browser. Non appena termina l’esecuzione dello script PHP, il contenuto viene passato al browser e il buffer svuotato. A questo punto entra in gioco l’Output Buffering.

Abbiamo solo bisogno di chiamare una funzione PHP per avviare il buffering PHP. Date un’occhiata a questo blocco di codice:

Come inviare l’output buffer al browser?

Una volta che il buffer di output è stato avviato, nulla sarà visualizzato in output dal browser, a meno che non si utilizzi una specifica funzione oppure  termini l’esecuzione dello script.

Per inviare il contenuto del buffer al browser e quindi visualizzarlo, allora avete bisogno di usare questa funzione:


La funzione ob_flush() passerà il contenuto del buffer al browser, e lo script PHP continuerà a visualizzare il contenuto del buffer. Cosa bisogna fare se si desidera trasmettere il contenuto dell’output buffer al browser e fermare il riempimento del buffer?Avrete bisogno di questa funzione:

Come eliminare l’output buffer?

Se invece si desidera eliminare tutto il contenuto dal buffer allora avrete bisogno della seguente funzione:

Come ob_flush(), ob_clean() non interrompe il riempimento del buffer. Per eliminare il buffer e fermare il riempimento del buffer è necessario utilizzare la funzione:

Come controllare i contenuti all’interno dell’Output Buffer?

Abbiamo visto le funzioni per l’eliminazione e l’outputing del contenuto del buffer. Se invece si desidera solamente controllare il contenuto memorizzato nel buffer è possibile utilizzare questa funzione e una semplice variabile PHP:

Un piccolo esempio

Per vedere in azione meglio, vi consiglio di creare due file PHP e confrontare l’esecuzione dei due file. Date un’occhiata al blocco di codice qui sotto. Come potete vedere ho usato la funzione phpinfo() e subito dopo sleep(10), per “addormentare” lo script di 10 secondi. In questo caso non ho usato ob_start(). Quello che accade è che viene mostrato il contenuto di phpinfo, lo script si addormenta per 10 secondi e poi termina la sua esecuzione.


In quest’altro blocco di codice potete vedere la magia dell’output buffer. Ho usato ob_start() prima di chiamare sleep(10) e phpinfo(). In questo caso lo script attenderà 10 secondi prima di mostrare il contenuto di phpinfo, anche se sleep(10) è stato chiamato dopo phpinfo().

Vantaggi

Uno dei principali vantaggi dell’output buffering è il caching. E’ possibile memorizzare nella cache il contenuto (in realtà pagina intera rendering) con l’aiuto dell’output buffering. Proprio per spiegare come funziona. È possibile avviare il buffering e si può tamponare il contenuto intero e alla fine dello script prima eco l’uscita si avrà il contenuto della pagina intera, che è possibile memorizzare nella cache in qualsiasi modo si desidera.


Pagine:1234567

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close