<?php
/**
* Form submission handler.
*
* @package TonItaliaRegistration
*/
defined( 'ABSPATH' ) || exit;
/**
* Class TON_Reg_Registration_Handler
*/
class TON_Reg_Registration_Handler {
/**
* Register hooks.
*/
public static function register() {
add_action( 'template_redirect', array( __CLASS__, 'maybe_handle_frontend_submit' ), 1 );
add_action( 'admin_post_nopriv_ton_reg_submit', array( __CLASS__, 'handle' ) );
add_action( 'admin_post_ton_reg_submit', array( __CLASS__, 'handle' ) );
}
/**
* Handle POST on the public form page (avoids wp-admin/admin-post.php).
*/
public static function maybe_handle_frontend_submit() {
if ( is_admin() || 'POST' !== ( $_SERVER['REQUEST_METHOD'] ?? '' ) ) {
return;
}
if ( empty( $_POST['ton_reg_frontend'] ) || '1' !== (string) wp_unslash( $_POST['ton_reg_frontend'] ) ) {
return;
}
self::handle();
}
/**
* Handle POST.
*/
public static function handle() {
$redirect = self::resolve_redirect_url();
if ( ! isset( $_POST['ton_reg_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['ton_reg_nonce'] ) ), TON_Reg_Captcha::NONCE_ACTION ) ) {
self::redirect_error( $redirect, __( 'Sessione non valida.', 'ton-italia-registration' ) );
}
if ( ! TON_Reg_Captcha::check_rate_limit() ) {
self::redirect_error( $redirect, __( 'Troppi tentativi. Riprova più tardi.', 'ton-italia-registration' ) );
}
$captcha = TON_Reg_Captcha::validate( $_POST );
if ( is_wp_error( $captcha ) ) {
self::redirect_error( $redirect, $captcha->get_error_message() );
}
$data = self::sanitize_post( $_POST );
$valid = self::validate( $data );
if ( is_wp_error( $valid ) ) {
self::redirect_error( $redirect, $valid->get_error_message() );
}
$existing = TON_Reg_Database::find_active_by_email_or_cf( $data['email'], $data['codice_fiscale'] );
if ( $existing ) {
self::redirect_error( $redirect, __( 'Esiste già una richiesta attiva con questa email o codice fiscale.', 'ton-italia-registration' ) );
}
$user_id = TON_Reg_User_Manager::create_subscriber( $data );
if ( is_wp_error( $user_id ) ) {
self::redirect_error( $redirect, $user_id->get_error_message() );
}
$now = current_time( 'mysql', true );
$privacy = (string) get_option( 'ton_reg_privacy_version', '1' );
$row = array(
'user_id' => $user_id,
'cognome' => $data['cognome'],
'nome' => $data['nome'],
'luogo_nascita' => $data['luogo_nascita'],
'provincia_nascita' => $data['provincia_nascita'],
'data_nascita' => $data['data_nascita'],
'codice_fiscale' => $data['codice_fiscale'],
'comune_residenza' => $data['comune_residenza'],
'provincia_residenza' => $data['provincia_residenza'],
'indirizzo' => $data['indirizzo'],
'numero_civico' => $data['numero_civico'],
'cap' => $data['cap'],
'telefono' => $data['telefono'],
'email' => $data['email'],
'luogo_dichiarazione' => $data['luogo_dichiarazione'],
'data_dichiarazione' => $data['data_dichiarazione'],
'bonifico_effettuato' => $data['bonifico_effettuato'],
'consenso_statuto' => 1,
'consenso_privacy' => 1,
'consenso_newsletter' => $data['consenso_newsletter'],
'registration_ip' => TON_Reg_Captcha::get_client_ip(),
'user_agent' => self::user_agent(),
'privacy_text_version' => $privacy,
'consent_statuto_at' => $now,
'consent_privacy_at' => $now,
'status' => 'pending',
);
$reg_id = TON_Reg_Database::insert( $row );
if ( ! $reg_id ) {
wp_delete_user( $user_id );
self::redirect_error( $redirect, __( 'Errore nel salvataggio. Riprova.', 'ton-italia-registration' ) );
}
update_user_meta( $user_id, 'ton_reg_registration_id', $reg_id );
$data['registration_id'] = $reg_id;
TON_Reg_User_Manager::sync_user_meta( $user_id, $data );
$registration = TON_Reg_Database::get( $reg_id );
TON_Reg_Documents::ensure_member_folder( $registration );
TON_Reg_Mailer::send_admin_notification( $registration, $user_id );
TON_Reg_Mailer::send_user_confirmation( $registration, $user_id );
/**
* Fires after a registration is stored and notification emails are sent.
*
* @param int $reg_id Registration ID.
* @param array<string,mixed> $data Sanitized form data.
* @param object $registration Registration row.
*/
do_action( 'ton_reg_registration_complete', $reg_id, $data, $registration );
if ( ! empty( $data['consenso_newsletter'] ) && TON_Reg_Mailchimp::is_configured() ) {
TON_Reg_Mailchimp::schedule_sync( $reg_id );
}
$success_redirect = self::get_success_redirect_url();
if ( ! $success_redirect ) {
$success_redirect = self::resolve_redirect_url();
}
$success_url = add_query_arg(
array(
'ton_reg' => 'success',
),
$success_redirect
);
wp_safe_redirect( $success_url );
exit;
}
/**
* @param array<string,mixed> $post Raw POST.
* @return array<string,mixed>
*/
private static function sanitize_post( $post ) {
$cf = isset( $post['codice_fiscale'] ) ? strtoupper( preg_replace( '/\s+/', '', sanitize_text_field( wp_unslash( $post['codice_fiscale'] ) ) ) ) : '';
return array(
'cognome' => sanitize_text_field( wp_unslash( $post['cognome'] ?? '' ) ),
'nome' => sanitize_text_field( wp_unslash( $post['nome'] ?? '' ) ),
'luogo_nascita' => sanitize_text_field( wp_unslash( $post['luogo_nascita'] ?? '' ) ),
'provincia_nascita' => strtoupper( sanitize_text_field( wp_unslash( $post['provincia_nascita'] ?? '' ) ) ),
'data_nascita' => self::parse_date_input( sanitize_text_field( wp_unslash( $post['data_nascita'] ?? '' ) ) ),
'codice_fiscale' => $cf,
'comune_residenza' => sanitize_text_field( wp_unslash( $post['comune_residenza'] ?? '' ) ),
'provincia_residenza' => strtoupper( sanitize_text_field( wp_unslash( $post['provincia_residenza'] ?? '' ) ) ),
'indirizzo' => sanitize_text_field( wp_unslash( $post['indirizzo'] ?? '' ) ),
'numero_civico' => sanitize_text_field( wp_unslash( $post['numero_civico'] ?? '' ) ),
'cap' => sanitize_text_field( wp_unslash( $post['cap'] ?? '' ) ),
'telefono' => sanitize_text_field( wp_unslash( $post['telefono'] ?? '' ) ),
'email' => sanitize_email( wp_unslash( $post['email'] ?? '' ) ),
'luogo_dichiarazione' => sanitize_text_field( wp_unslash( $post['luogo_dichiarazione'] ?? '' ) ),
'data_dichiarazione' => self::parse_date_input( sanitize_text_field( wp_unslash( $post['data_dichiarazione'] ?? '' ) ) ),
'bonifico_effettuato' => ! empty( $post['bonifico_effettuato'] ) ? 1 : 0,
'consenso_statuto' => ! empty( $post['accettazione_statuto'] ) ? 1 : 0,
'consenso_privacy' => ! empty( $post['consenso_privacy'] ) ? 1 : 0,
'consenso_newsletter' => ! empty( $post['consenso_newsletter'] ) ? 1 : 0,
);
}
/**
* @param array<string,mixed> $data Data.
* @return true|WP_Error
*/
private static function validate( $data ) {
$required = array(
'cognome' => __( 'Cognome', 'ton-italia-registration' ),
'nome' => __( 'Nome', 'ton-italia-registration' ),
'luogo_nascita' => __( 'Luogo di nascita', 'ton-italia-registration' ),
'provincia_nascita' => __( 'Provincia di nascita', 'ton-italia-registration' ),
'data_nascita' => __( 'Data di nascita', 'ton-italia-registration' ),
'codice_fiscale' => __( 'Codice fiscale', 'ton-italia-registration' ),
'comune_residenza' => __( 'Comune di residenza', 'ton-italia-registration' ),
'provincia_residenza' => __( 'Provincia di residenza', 'ton-italia-registration' ),
'indirizzo' => __( 'Indirizzo', 'ton-italia-registration' ),
'numero_civico' => __( 'Numero civico', 'ton-italia-registration' ),
'cap' => __( 'CAP', 'ton-italia-registration' ),
'telefono' => __( 'Telefono', 'ton-italia-registration' ),
'email' => __( 'Email', 'ton-italia-registration' ),
'luogo_dichiarazione' => __( 'Luogo dichiarazione', 'ton-italia-registration' ),
'data_dichiarazione' => __( 'Data dichiarazione', 'ton-italia-registration' ),
);
foreach ( $required as $key => $label ) {
if ( empty( $data[ $key ] ) ) {
return new WP_Error( 'required', sprintf( __( 'Il campo %s è obbligatorio.', 'ton-italia-registration' ), $label ) );
}
}
if ( ! is_email( $data['email'] ) ) {
return new WP_Error( 'email', __( 'Email non valida.', 'ton-italia-registration' ) );
}
if ( ! TON_Reg_User_Manager::validate_codice_fiscale( $data['codice_fiscale'] ) ) {
return new WP_Error( 'cf', __( 'Codice fiscale non valido.', 'ton-italia-registration' ) );
}
if ( ! preg_match( '/^\d{5}$/', $data['cap'] ) ) {
return new WP_Error( 'cap', __( 'CAP non valido.', 'ton-italia-registration' ) );
}
if ( ! preg_match( '/^[A-Z]{2}$/', $data['provincia_nascita'] ) || ! preg_match( '/^[A-Z]{2}$/', $data['provincia_residenza'] ) ) {
return new WP_Error( 'prov', __( 'Provincia non valida (2 lettere).', 'ton-italia-registration' ) );
}
if ( ! self::is_valid_date( $data['data_nascita'] ) || ! self::is_valid_date( $data['data_dichiarazione'] ) ) {
return new WP_Error( 'date', __( 'Data non valida. Usa il formato gg-mm-aaaa.', 'ton-italia-registration' ) );
}
if ( ! self::is_date_not_in_future( $data['data_dichiarazione'] ) ) {
return new WP_Error( 'date_future', __( 'La data di dichiarazione non può essere futura.', 'ton-italia-registration' ) );
}
if ( empty( $data['consenso_statuto'] ) ) {
return new WP_Error( 'statuto', __( 'Devi accettare la dichiarazione sullo statuto.', 'ton-italia-registration' ) );
}
if ( empty( $data['consenso_privacy'] ) ) {
return new WP_Error( 'privacy', __( 'Devi accettare l\'informativa privacy.', 'ton-italia-registration' ) );
}
if ( '1' === get_option( 'ton_reg_newsletter_required', '0' ) && empty( $data['consenso_newsletter'] ) ) {
return new WP_Error( 'newsletter', __( 'Devi accettare il consenso newsletter.', 'ton-italia-registration' ) );
}
return true;
}
/**
* Pad single-digit day/month (1-9) in gg-mm-aaaa input.
*
* @param string $part Day or month segment.
* @return string
*/
private static function pad_date_part( $part ) {
$num = (int) $part;
if ( 1 === strlen( $part ) && $num >= 1 && $num <= 9 ) {
return '0' . $part;
}
return $part;
}
/**
* Parse gg-mm-aaaa (or already normalized aaaa-mm-gg) to DB format Y-m-d.
*
* @param string $value User input.
* @return string Empty if invalid.
*/
private static function parse_date_input( $value ) {
$value = str_replace( array( '/', '.' ), '-', trim( $value ) );
if ( preg_match( '/^(\d{1,2})-(\d{1,2})-(\d{4})$/', $value, $m ) ) {
$day_str = self::pad_date_part( $m[1] );
$month_str = self::pad_date_part( $m[2] );
$day = (int) $day_str;
$month = (int) $month_str;
$year = (int) $m[3];
if ( checkdate( $month, $day, $year ) ) {
return sprintf( '%04d-%02d-%02d', $year, $month, $day );
}
return '';
}
if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value ) ) {
$parts = array_map( 'intval', explode( '-', $value ) );
if ( checkdate( $parts[1], $parts[2], $parts[0] ) ) {
return $value;
}
}
return '';
}
/**
* @param string $date Date Y-m-d.
* @return bool
*/
private static function is_valid_date( $date ) {
if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) ) {
return false;
}
$parts = array_map( 'intval', explode( '-', $date ) );
return checkdate( $parts[1], $parts[2], $parts[0] );
}
/**
* @param string $date Date in Y-m-d format.
* @return bool
*/
private static function is_date_not_in_future( $date ) {
if ( ! self::is_valid_date( $date ) ) {
return false;
}
return $date <= current_time( 'Y-m-d' );
}
/**
* Resolve redirect URL after form submit (public form page only).
*
* @return string
*/
private static function resolve_redirect_url() {
if ( ! empty( $_POST['ton_reg_page_id'] ) ) {
$post_id = (int) wp_unslash( $_POST['ton_reg_page_id'] );
if ( self::is_publicly_viewable_page( $post_id ) ) {
return get_permalink( $post_id );
}
}
if ( ! empty( $_POST['ton_reg_redirect'] ) ) {
$public_url = self::get_public_redirect_url(
esc_url_raw( wp_unslash( $_POST['ton_reg_redirect'] ) )
);
if ( $public_url ) {
return $public_url;
}
}
return home_url( '/' );
}
/**
* Public permalink of the configured success page.
*
* @return string Empty when not configured.
*/
public static function get_success_redirect_url() {
$page_id = (int) get_option( 'ton_reg_success_page_id', 0 );
if ( ! $page_id || ! self::is_publicly_viewable_page( $page_id ) ) {
return '';
}
return get_permalink( $page_id );
}
/**
* Public URL used as form action (same page, never wp-admin).
*
* @return string
*/
public static function get_form_page_action_url() {
global $post;
if ( $post instanceof WP_Post && self::is_publicly_viewable_page( $post->ID ) ) {
return get_permalink( $post );
}
return home_url( '/' );
}
/**
* @return int Page ID when the form is on a public page, else 0.
*/
public static function get_form_page_id() {
global $post;
if ( $post instanceof WP_Post && self::is_publicly_viewable_page( $post->ID ) ) {
return (int) $post->ID;
}
return 0;
}
/**
* Permalink for the current form page when it is publicly viewable.
*
* @return string
*/
public static function get_form_page_redirect_url() {
return self::get_form_page_action_url();
}
/**
* @param string $url Candidate URL.
* @return string Sanitized public URL or empty string.
*/
public static function get_public_redirect_url( $url ) {
$url = wp_validate_redirect( esc_url_raw( (string) $url ), false );
if ( ! $url || self::is_restricted_redirect_url( $url ) ) {
return '';
}
$post_id = url_to_postid( $url );
if ( $post_id && self::is_publicly_viewable_page( $post_id ) ) {
return get_permalink( $post_id );
}
if ( self::is_public_home_url( $url ) ) {
return $url;
}
return '';
}
/**
* @param int $post_id Post ID.
* @return bool
*/
public static function is_publicly_viewable_page( $post_id ) {
$post = get_post( (int) $post_id );
if ( ! $post instanceof WP_Post ) {
return false;
}
if ( function_exists( 'is_post_publicly_viewable' ) ) {
return (bool) is_post_publicly_viewable( $post );
}
return 'publish' === $post->post_status && '' === $post->post_password;
}
/**
* @param string $url URL.
* @return bool
*/
private static function is_restricted_redirect_url( $url ) {
$path = wp_parse_url( $url, PHP_URL_PATH );
if ( ! is_string( $path ) || '' === $path ) {
return false;
}
$path = strtolower( untrailingslashit( $path ) );
return str_contains( $path, '/wp-admin' )
|| str_contains( $path, '/wp-login.php' )
|| str_contains( $path, 'admin-post.php' );
}
/**
* @param string $url URL.
* @return bool
*/
private static function is_public_home_url( $url ) {
if ( self::is_restricted_redirect_url( $url ) ) {
return false;
}
$home_host = wp_parse_url( home_url( '/' ), PHP_URL_HOST );
$url_host = wp_parse_url( $url, PHP_URL_HOST );
if ( $home_host && $url_host && strtolower( (string) $home_host ) !== strtolower( (string) $url_host ) ) {
return false;
}
$home_path = wp_parse_url( home_url( '/' ), PHP_URL_PATH );
$url_path = wp_parse_url( $url, PHP_URL_PATH );
$home_path = is_string( $home_path ) ? untrailingslashit( $home_path ) : '';
$url_path = is_string( $url_path ) ? untrailingslashit( $url_path ) : '';
return $url_path === $home_path || '' === $url_path;
}
/**
* @return string
*/
private static function user_agent() {
if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
return '';
}
return substr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), 0, 255 );
}
/**
* @param string $url URL.
* @param string $message Message.
*/
private static function redirect_error( $url, $message ) {
$url = add_query_arg(
array(
'ton_reg' => 'error',
'ton_msg' => rawurlencode( $message ),
),
$url
);
wp_safe_redirect( $url );
exit;
}
}