Il blocco gutenberg Form è un blocco personalizzato del wordpress Gpci framework. E' un blocco molto potente che va usato insieme al campo del form per definire i campi compilabili e probabilmente anche insieme al button gpci per l'invio del form. Serve principalmente a tre obbiettivi:
- inviare informazioni al server per eseguire operazioni php controllate (come l'invio di email e la creazione o modifica di posts)
- ottenere informazioni dal server ( come campi personalizzati e conteggi )
- filtrare queries nel frontend (per questo si usa in combinazione con il blocco gutenberg Gpci query loop )
Nomi validi, required e valori
Il form controlla automaticamente durante l'invio che i campi non siano stati manomessi dal client in questo modo:
- controlla che tutti i name inviati siano presenti nel form originale
- controlla che tutti i required siano presenti e non vuoti
- controlla che i valori inviati fossero presenti nel form originale (per ora solo per campi di tipo select)
Se i controlli non sono passati si suppone che il form sia stato manomesso e viene ritornato un ErrorMessage.
Input predefiniti
Il form inserisce automaticamente alcuni input type hidden all'interno per eseguire verifiche e passare dati che non devono essere modificati dagli utilizzatori del form. Non inserire quindi campi con gli stessi attributi name di questo elenco altrimenti saranno sovrascritti con quelli predefiniti:
- launch - segnala al launcher di eseguire il form
- $IdForm-nonce - campo necessario alla verifica del nonce di sicurezza wordpress (solo php) - nota: il campo _wp_http_referer è disabilitato (vedi campo successivo)
- origin-url - url dal quale è inviato il form (solo ajax) - sostituisce _wp_http_referer
- GpciBlockForm-nonce - nonce relativo l'azione ajax (solo ajax)
- postid_id - post id corrente ottenuto tramite la funzione wordpress get_the_ID (solo ajax)
- time - timestamp (in secondi) al quale è stata generata l'azione ajax da php (solo ajax)
- max_duration - tempo in secondi entro il quale è valida l'azione (solo ajax) - per ora è sempre settato su 24 ore
- gpci-form-postid - post id in cui è situato il form (solo ajax)
- gpci-form-posttype - post type in cui è situato il form (solo ajax)
- gpci-form-clientid - client id del blocco gutenberg (solo ajax)
- gpci-data-encrypted - dati crittografati (php solo se esistono, ajax sempre)
Dati crittografati
Il form crittografa automaticamente tutti gli input type hidden con l'attributo data-to-encrypt, compresi gli input predefiniti ma escluso il nonce (se il form è inviato via php, altrimenti anche quello). Questo può essere molto utile ad esempio per passare parametri che non devono essere modificati nè visualizzati dall'utente che compila il form. I dati vengono crittografati dalla funzione EncryptionAction e decifrati dopo l'invio poi, se validi (quindi se sono un array di dati utili), reinseriti nella superglobale GET o POST corrispondente pronti per essere utilizzati. La superglobale $_REQUEST invece non sarà mai modificata ne sanificata. Se i dati non sono validi si suppone che il form sia stato manomesso e non viene eseguita alcuna operazione, ritornando un ErrorMessage.
Controlli del blocco Form
Questo blocco è un contenitore di altri blocchi con controlli specializzati sul tag form. Per ora non ho limitato in nessun modo i blocchi che possono essere inseriti all'interno, quindi possono essere utilizzate colonne, contenitori e anche immagini o icone per visualizzare il form come meglio si crede. Essendo un wrapper di blocchi, tutte le proprietà come posizionamento e style inline vengono direttamente applicate al form. Anche in questo caso, appena inserito il blocco, è necessario definire subito alcuni controlli; è necessario specificare che in base ai controlli scelti il form sarà usato in modi diversi. Intanto vediamo di seguito i controlli:
Tipo di valore
Eventuale conversione dinamica relativa l'attributo name/id. Utilizzare se il campo è all'interno di un ciclo query.
Nome/Id
Stringa che verrà usata come attributo name e attributo id, aggiornerà automaticamente anche l'attributo ancora del blocco (in avanzate). Compilare se è necessario accedere ai campi non compilabili direttamente o all'ancora del form, come regola generale utilizzare lettere, trattini medi/bassi e numeri e iniziare con una lettera. In nessun caso il form resterà senza questi attributi che sono necessari al codice javascript e altre funzioni interne, se non specificati verranno automaticamente aggiunti da php con il modello: form-stringa_casuale. Se il form si trova all'interno di una query (quindi se è ripetuto più volte) è necessario nella stragrande maggioranza dei casi far risultare un valore dinamico differente per ogni form per evitare che aggiorni post o commenti errati (vedi esempi).
Action
Url al quale verranno spediti i dati del form (con conseguente redirezione). Tenere presente che questo attributo funzionerà solo se il form è inviato tramite php ma non funzionerà se il form è inviato via javascript. Grazie a questo attributo è possibile creare dei form multipagina nel caso che ci siano molte informazioni da processare. Default lasciare vuoto.
Previeni reinvio
Compare se il form è inviato tramite php e serve a prevenire il reinvio dei dati se si aggiorna la pagina dopo l'invio. Se attivato: dopo l'invio e l'esecuzione del codice php elimina la superglobale $_GET o $_POST tramite php e tramite javascript resetta le query strings se via get modifica la cronologia del browser se via post.
Copia in session
Per i form multipagina php (che vengono spediti alla seconda pagina con l'attributo action), se attivato ogni campo degli array $_GET e $_POST verrà copiato alla $_SESSION mantenendo lo stesso nome. Ricordarsi che questa opzione va attivata nel form che riceve i dati non quello che spedisce (anche la sanificazione dei dati). Se non esistente la sessione sarà inizializzata automaticamente ma per ora va chiusa manualmente in php (probabilmente nell'ultimo form che riceve i dati) con qualcosa come:
$_SESSION = [];
session_destroy();
Method
Attributo method del form. il valore predefinito è GET ma è possibile scegliere anche POST. Al momento non ho trovato casi utili in wordpress per abilitare anche gli altri tipi di protocollo come DELETE, PUT, ecc..
Come regola generale (ma piuttosto ferrea) utilizzare:
- GET - per ottenere dati dal server e filtrare una query
- POST - per creare o aggiornare dati, inviare email o comunque manipolare informazioni sensibili
Sanificazione
Qui è possibile configurare alcune sanificazioni automatiche al form, che sono una mappatura della funzione php SanitizeAction. Attivando queste opzioni sono sanificate, prima di eseguire codice php, le superglobals $_GET e $_POST ($_FILES solo se automatica), mentre $_REQUEST non sarà mai sanificata. Se queste sanificazioni non sono sufficienti è possibile sanificarle direttamente nel codice php.
Automatica
Sanifica automaticamente tutti i campi compilati in base al tipo come da azione auto nelle superglobale di invio. Inoltre, se non è vuota la superglobale $_FILES sanifica anche quella. E' l'unica sanificazione attivata come valore predefinito.
Nome campo
Sanifica automaticamente tutti i campi compilati in base al nome del campo come da azione FieldName nella superglobale di invio.
Context
Contesto di utilizzo dati, compare se attivata la sanificazione automatica oppure per nome campo. Selezionare db se vengono salvati i dati, display se vengono visualizzati.
Escape html
Converte il codice html di tutti i campi compilati a caratteri speciali come da azione EscapeHtml nella superglobale di invio.
Strip tags
Rimuove tutti i tags html dai campi compilati come da azione StripTags nella superglobale di invio.
Strip shortcodes
Rimuove tutti i tags html dai campi compilati come da azione StripShortcodes nella superglobale di invio.
Documentazioni utili:
- sanitizzazione dati con wordpress
- funzione GpciSanitizeForm
- wpvip.com: Validating, sanitizing, and escaping
Js on submit
Se attivato abilita una textarea in cui inserire codice javascript che verrà eseguito subito prima di inviare il form (vedi controllo successivo).
Javascript on submit
In questa textarea è possibile scrivere codice javascript che verrà eseguito subito prima di inviare il form. Questo è il posto ideale in cui inserire delle validazioni avanzate. Non utilizzare nessun tag script in quanto è già aggiunto tutto automaticamente. Esempi basilari:
//Validazione avanzata(sia per form php che javascript) - non invia se il nome inserito non è luigi
let NameInputId = document.getElementById('form-contatti-nome');
let name = NameInputId.value;
if( name != 'luigi' ){
let FormContattiNomeValidation = document.getElementById('form-contatti-nome-validation');
let FormValidationErrorMessage = 'Inserisci un altro nome';
if( FormContattiNomeValidation == null ){
document.getElementById('form-contatti-nome').insertAdjacentHTML('beforebegin', FormValidationErrorMessage )
}
else{
FormContattiNomeValidation.innerHTML.replace( FormValidationErrorMessage );
}
}
else{ event.currentTarget.submit(); }
//Sostituisce il pulsante submit con un testo durante l'invio( per form via js )
document.getElementById('form-contatti-submit').replaceWith( 'inviando..');
Invia tramite javascript
Se attivato abilita tutti i controlli sottostanti, dato che sono basati sulla risposta javascript.
Posizione Js on response php
Se esistono istruzioni php: selezionare dove si vuole che la risposta venga visualizzata. La risposta sarà formulata da php mentre javascript ne deciderà la posizione. Le scelte disponibili sono:
- Nessuna risposta - non succederà nulla dopo le operazioni
- Questo form - Il form corrente sarà sostituito dalla risposta
- Id - scrivere nella textarea l'elemento id che verrà sostituito dalla risposta
- Alert - la risposta comparirà un un alert
- Console.log - la risposta comparirà nella console sviluppatori
- Personalizzato - scrivere nella textarea seguente il codice personalizzato per gestire la risposta
Id risposta/Javascript on response personalizzato
Scrivere il codice personalizzato per gestire la risposta ma anche altro. La variabile preimpostata ResponseText sarà il contenuto della risposta. Ricordarsi che il codice sarà eseguito subito dopo la risposta php ma prima della risposta di eventuali queries aggiornate. Esempio:
//Farà comparire la risposta in ogni sezione della pagina
document.querySelectorAll('.wp-block-gpci-section').forEach( function( selector ){
selector .innerHTML = ResponseText;
} )
Aggiorna queries
Qui possono essere inserite le coordinate di una o più gpci query loop che saranno aggiornate durante la ricerca. E' bene capire subito come funziona l'aggiornamento delle query tramite javascript:
- appena inoltrato l'invio del form il server cerca il blocco gpci query alle coordinate specificate (ricordare che ogni id deve essere unico nella pagina)
- se la query viene trovata il server la riesegue e rimanda indietro come risposta la stringa html della query aggiornata, altrimenti ritorna un messaggio di errore
- il codice html dell'id specificato (ossia l'ancora della query) viene sostituito da javascript con la risposta
I campi relativi ad ogni query aggiunta sono:
- ancora - inserire l'ancora (id) del blocco gpci query loop (ricordarsi di assegnarlo!)
- query postid - inserire l'id numerico del post in cui si trova la query se in un post/pagina/post type/pattern sincronizzato, gpci-child//SLUG (esempio:gpci-child//header-primary) se la query è in un template o una template part
- query location post type - compilare solo se la query è in un template o una template part, altrimenti lasciare vuoto: wp_template o wp_template_part
E' bene precisare che i form inviati via javascript verranno processati tramite wordpress ajax, quindi, in alcuni casi, i filtri delle query se dinamici potrebbero avere valori diversi da quello che ci si aspetta (ad esempio il post id corrente). In questi casi è possibile compensare utilizzando un campo del form di tipo input hidden per memorizzare un certo valore e reperirlo nella query tramite una richiesta GET. Esempio:
Filtro query, funzione get_the_ID:
ci si potrebbe aspettare un filtro con id relativo al post corrente, invece il risultato sarà false (dato che la pagina corrente risulta admin-ajax.php). In questo caso utilizzare un campo del form di tipo input hidden e assegnargli un valore derivante dalla funzione get_the_ID.
Js after responses
Se attivato abilita una textarea in cui inserire codice javascript che verrà eseguito dopo tutte le risposte (php e aggiornamento queries). Va specificato che se non ci sarà risposta il codice non verrà eseguito. Probabilmente saranno comunicazioni addizionali/generiche oltre alla risposta, o magari una redirezione dopo qualche secondo.
Esegui istruzioni php
Se attivato abilita una textarea in cui inserire codice php che verrà eseguito all'invio del form (vedi controllo successivo).
Codice php
In questa textarea è possibile scrivere codice php che verrà eseguito all'invio del form. Il codice viene processato tramite eval con blocco try and catch (se il contesto lo permette e il codice è errato verrà generato un messaggio di errore) quindi attenzione a cosa si scrive qui. Proteggere sempre, se possibile e inerente, il codice con dei controlli sui permessi qui o nel controllo dedicato (vedi sotto). Non utilizzare tag php agli estremi in quanto siamo già dentro php. Ci sono vari modi per usare questo codice e delle variabili/funzioni utili da ricordare, vediamo negli esempi:
//Form inviato via php: il server invia una mail e ritorna un messaggio di conferma che sarà visualizzato al posto del form
wp_mail( ... );
$content = 'Ciao '.$_POST['form-contatti-nome'].', il tuo messaggio è stato inviato!';
Importante è la variabile $content che nel caso di un form php sostituisce il form stesso con il contenuto della variabile, in stato di form inviato. La variabile è gia inizializzata nella funzione che genera il form quindi il suo nome è sempre costante.
//Form inviato via javascript: stesso risultato di prima ma modo di esecuzione diverso:
wp_mail( ... );
echo 'Il tuo nome è: '.$_POST['form-contatti-nome'];
Qui utilizziamo echo per ritornare un messaggio dopo le operazioni, è possibile anche chiudere con return. Questo messaggio però per essere mostrato e dove, sarà configurato dopo nel controllo Posizione Js on response.
Se il form è inviato tramite javascript è' possibile utilizzare, proprio come per le azioni ajax, gli argomenti filtrati da quelli necessari alle verifiche interne utilizzando la globale php $GLOBALS['gpci']['ajax']['current_args']. Esempio:
$current_args = $GLOBALS['gpci']['ajax']['current_args'];
print_rR( $current_args );
Attenzione! Se il form viene inviato via javascript ed è contenuto all'interno di un blocco template part deve essere modificato dalla template part e non dal template che la contiene. Questo perchè il blocco per farsi trovare da php durante il fetch javascript utilizza degli attributi come coordinate per capire la locazione esatta. In caso contrario il form ritornerà come risposta 'Il form non è stato trovato'.
Prima condizione
Eventuale conversione dinamica il cui risultato deve essere true. In caso contrario il codice php non verrà eseguito. Serve a inserire un primo controllo di sicurezza per evitare di inserirlo all'interno del codice php. E' opzionale ma andrebbe sempre usato per proteggere form per utenti autenticati o che devono rispettare certe condizioni. Alternativamente (per protezioni più complesse) proteggere il form all'interno del codice php oppure creare una funzione ad hoc nel file functions.php o utilizzando il modulo aggiunte di codice. Esempio:
- tipo di valore: funzione php
- valore: is_user_logged_in
Coordinate form
Se il form è inviato via javascript ed esegue istruzioni php è possibile compilare manualmente le coordinate nel caso che non venga correttamente trovato durante la richiesta ajax. E' una soluzione alternativa e temporanea per quando quando i form sono in un template o in una parte del template, verrà corretto definitivamente in un secondo momento.
Esempi
Questo blocco ha molte varianti, quindi cercherò di fornire esempi in vari contesti:
Form inviato via php che esegue istruzioni php in base all'input (sostituisce se stesso con un messaggio):
- method: POST
- action: #esempio1
- tipo di invio: php
- istruzioni php:
//qui è possibile aggiornare database, ecc...
$content = "<p class='gpci-ok-messages'>Ciao ".$_POST['esempio-input']."!</p>";
Stessa cosa ma form inviato via javascript:
- method: POST
- action: vuoto
- tipo di invio: javascript
- posizione js on response: questo form
- istruzioni php:
//qui è possibile aggiornare database, ecc...
echo "<p class='gpci-ok-messages'>Ciao ".$_POST['esempio-input']."!</p>";
Form multi pagina inviato via php che alla fine restituisce un resoconto dei campi compilati:
- method: POST
- action: /docs/gpci-framework/blocchi/form/esempio-form-multipagina-2/
- tipo di invio: php
- istruzioni php: nessuna
Le altre 2 pagine correlate a questo esempio sono:
Nota: Se un form ha varie pagine a molti campi da compilare potrebbe essere utile prima creare, poi aggiornare ad ogni pagina un post privato o visibile solo all'utente. In un contesto del genere:
- rimuovere l'attributo action
- abilitare copia in session
- abilitare l'esecuzione del codice php del form, poi aggiornare il database con le funzioni wordpress, esempio:
//proviamo a creare il post
$postid = wp_insert_post( $postarr=[
'post_title' => $_POST['esempio-input'],
'post_status' => 'private',
'post_type' => 'posttypeslug',
'meta_input' => [
'idcampopersonalizzato1' => $_POST['esempio-input']
]
] );
//se il post è stato inserito correttamente:
//memorizziamo il postid per aggiornarlo nelle pagine seguenti
//redirezioniamo alla pagina 2
if( $postid AND !is_wp_error($postid) ){
$_SESSION['postid'] = $postid;
wp_redirect( $location='/docs/gpci-framework/blocchi/form/esempio-form-multipagina-2/' );
}
Nelle pagine successive fare la stessa cosa ma invece di creare un post nuovo lo aggiorneremo specificando il postid memorizzato in sessione:
//aggiorniamo il post creato alla pagina precedente
$postid = wp_insert_post( $postarr=[
'ID' => $_SESSION['postid'],
'meta_input' => [
'idcampopersonalizzato2' => $_POST['esempio-input-pagina2']
]
] );
//se il post è stato aggiornato correttamente:
//redirezioniamo alla pagina 3
if( $postid AND !is_wp_error($postid) ){
wp_redirect( $location='/docs/gpci-framework/blocchi/form/esempio-form-multipagina-3/' );
}
Come esempio sopra ma form inviato via javascript multistep nella stessa pagina. In questo caso probabilmente il metodo più semplice è generare i form nella stessa pagina uno sotto l'altro e fare comparire il seguente appena inviato quello subito prima, quindi:
- method: POST (tutti e 3)
- action: vuoto (tutti e 3)
- tipo di invio: javascript (tutti e 3)
- copia in session: disabilitato (tutti e 3) - in quanto non c'e' comunicazione diretta tra uno e l'altro ma sono separati
- attributo hidden applicato al secondo e al terzo
- dobbiamo fare comparire su submit il form successivo facendo sparire quello appena compilato, quindi abilitiamo js on submit al primo e al secondo e inseriamo qualcosa come:
event.target.setAttribute( 'hidden', 'hidden' );
event.target.nextElementSibling.nextElementSibling.removeAttribute( 'hidden' );
- le istruzioni php saranno questa volta gestite all'invio di ogni form, quindi si farà qualcosa come:
//Primo form
if( isset( $_POST['esempio-input'] ) ){
if( !session_id() ){ session_start(); }
$_SESSION['esempio-input'] = $_POST['esempio-input'];
}
//Secondo form
if( isset( $_POST['esempio-input-pagina2'] ) ){
if( !session_id() ){ session_start(); }
$_SESSION['esempio-input-pagina2'] = $_POST['esempio-input-pagina2'];
}
//Terzo form
if( isset( $_POST['esempio-input-pagina3'] ) ){
if( !session_id() ){ session_start(); }
$_SESSION['esempio-input-pagina3'] = $_POST['esempio-input-pagina3'];
}
$content = '<p>Grazie per avere compilato i 3 form, qui sotto il riepilogo:<p>';
$content .= '<ul>';
$content .= "<li>Il tuo nome è ".$_SESSION['esempio-input']."</li>";
$content .= "<li>Il tuo cognome è ".$_SESSION['esempio-input-pagina2']."</li>";
$content .= "<li>La tua città è ".$_SESSION['esempio-input-pagina3']."</li>";
$content .= '</ul>';
$_SESSION = [];
session_destroy();
echo $content;
Risultato
Rimangono da mostrare gli esempi per fare filtrare una query da un form (come detto in precedenza saranno di tipo GET). Per vedere questi andare alla pagina Gpci query loop.
