feat(07-01): integrate captcha verification in form handler
- Verify captcha after nonce check - Support all three providers - Store captcha errors in transient - Redirect back to form on verification failure - German error message for failed captcha Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
352
includes/class-form-handler.php
Normal file
352
includes/class-form-handler.php
Normal file
@@ -0,0 +1,352 @@
|
||||
<?php
|
||||
/**
|
||||
* Form Handler
|
||||
*
|
||||
* Handles form submissions, validation, CPT storage, and email sending
|
||||
*
|
||||
* @package Umzugsliste
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form handler class
|
||||
*/
|
||||
class Umzugsliste_Form_Handler {
|
||||
|
||||
/**
|
||||
* Instance
|
||||
*
|
||||
* @var Umzugsliste_Form_Handler
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return Umzugsliste_Form_Handler
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action( 'init', array( $this, 'handle_submission' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle form submission
|
||||
*/
|
||||
public function handle_submission() {
|
||||
// Check if this is a form submission
|
||||
if ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_POST['umzugsliste_submit'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify nonce
|
||||
if ( ! isset( $_POST['umzugsliste_nonce'] ) || ! wp_verify_nonce( $_POST['umzugsliste_nonce'], 'umzugsliste_submit' ) ) {
|
||||
wp_die( 'Security verification failed. Please try again.' );
|
||||
}
|
||||
|
||||
// Verify captcha
|
||||
$captcha = Umzugsliste_Captcha::get_instance();
|
||||
if ( $captcha->is_enabled() ) {
|
||||
$verified = $captcha->verify_response( $_POST );
|
||||
if ( ! $verified ) {
|
||||
$captcha_error = array(
|
||||
'messages' => array( 'Captcha-Verifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.' ),
|
||||
'fields' => array(),
|
||||
);
|
||||
set_transient( 'umzugsliste_errors_' . session_id(), $captcha_error, 300 );
|
||||
wp_safe_redirect( wp_get_referer() );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate submission
|
||||
$validation_errors = $this->validate_submission( $_POST );
|
||||
if ( ! empty( $validation_errors ) ) {
|
||||
// Store errors in transient for display
|
||||
set_transient( 'umzugsliste_errors_' . session_id(), $validation_errors, 300 );
|
||||
// Redirect back to form
|
||||
wp_safe_redirect( wp_get_referer() );
|
||||
exit;
|
||||
}
|
||||
|
||||
// Sanitize data
|
||||
$data = $this->sanitize_submission( $_POST );
|
||||
|
||||
// Save to CPT
|
||||
$entry_id = $this->save_to_cpt( $data );
|
||||
|
||||
if ( ! $entry_id ) {
|
||||
// Log error but continue
|
||||
error_log( 'Umzugsliste: Failed to save CPT entry' );
|
||||
}
|
||||
|
||||
// Send email
|
||||
$email_sent = $this->send_email( $entry_id, $data );
|
||||
|
||||
if ( ! $email_sent ) {
|
||||
// Email failed - update CPT and show error
|
||||
if ( $entry_id ) {
|
||||
update_post_meta( $entry_id, '_umzugsliste_email_sent', false );
|
||||
}
|
||||
|
||||
// Show error message
|
||||
wp_die(
|
||||
'<h1>E-Mail konnte nicht versendet werden</h1>
|
||||
<p>Ihre Anfrage wurde gespeichert, aber die E-Mail konnte nicht versendet werden.</p>
|
||||
<p><strong>Bitte kontaktieren Sie uns telefonisch:</strong></p>
|
||||
<p>Wiesbaden: <a href="tel:+4961122020">(06 11) 2 20 20</a><br>
|
||||
Mainz: <a href="tel:+49613122141">(0 61 31) 22 21 41</a></p>
|
||||
<p><a href="' . home_url() . '">Zurück zur Startseite</a></p>',
|
||||
'E-Mail-Fehler'
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect to thank you page
|
||||
$this->redirect_to_thank_you( $entry_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate submission data
|
||||
*
|
||||
* @param array $data POST data
|
||||
* @return array Validation errors (empty if valid)
|
||||
*/
|
||||
private function validate_submission( $data ) {
|
||||
$errors = array();
|
||||
|
||||
// Required fields
|
||||
$required_fields = array(
|
||||
'bName' => 'Name (Beladeadresse)',
|
||||
'bStrasse' => 'Straße (Beladeadresse)',
|
||||
'bort' => 'PLZ/Ort (Beladeadresse)',
|
||||
'bTelefon' => 'Telefon (Beladeadresse)',
|
||||
'eName' => 'Name (Entladeadresse)',
|
||||
'eStrasse' => 'Straße (Entladeadresse)',
|
||||
'eort' => 'PLZ/Ort (Entladeadresse)',
|
||||
);
|
||||
|
||||
foreach ( $required_fields as $field => $label ) {
|
||||
if ( empty( $data[ $field ] ) ) {
|
||||
$errors[] = 'Pflichtfeld fehlt: ' . $label;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate email if provided
|
||||
if ( ! empty( $data['info']['eE-Mail'] ) && ! is_email( $data['info']['eE-Mail'] ) ) {
|
||||
$errors[] = 'Ungültige E-Mail-Adresse';
|
||||
}
|
||||
|
||||
// Validate date
|
||||
if ( empty( $data['day'] ) || empty( $data['month'] ) || empty( $data['year'] ) ) {
|
||||
$errors[] = 'Umzugstermin fehlt';
|
||||
}
|
||||
|
||||
// Check if at least one furniture item has quantity
|
||||
$has_items = false;
|
||||
$rooms = Umzugsliste_Furniture_Data::get_rooms();
|
||||
|
||||
foreach ( $rooms as $room_key => $room_label ) {
|
||||
$post_array_name = ucfirst( $room_key );
|
||||
if ( 'kueche_esszimmer' === $room_key ) {
|
||||
$post_array_name = 'Kueche_Esszimmer';
|
||||
}
|
||||
|
||||
if ( ! empty( $data[ $post_array_name ] ) ) {
|
||||
foreach ( $data[ $post_array_name ] as $key => $value ) {
|
||||
if ( substr( $key, 0, 1 ) === 'v' && ! empty( $value ) && floatval( $value ) > 0 ) {
|
||||
$has_items = true;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $has_items ) {
|
||||
$errors[] = 'Bitte geben Sie mindestens eine Möbelmenge ein';
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize submission data
|
||||
*
|
||||
* @param array $data POST data
|
||||
* @return array Sanitized data
|
||||
*/
|
||||
private function sanitize_submission( $data ) {
|
||||
$sanitized = array();
|
||||
|
||||
// Sanitize simple text fields
|
||||
$text_fields = array( 'bName', 'eName', 'bStrasse', 'eStrasse', 'bort', 'eort', 'bTelefon', 'eTelefon', 'day', 'month', 'year' );
|
||||
|
||||
foreach ( $text_fields as $field ) {
|
||||
$sanitized[ $field ] = isset( $data[ $field ] ) ? sanitize_text_field( $data[ $field ] ) : '';
|
||||
}
|
||||
|
||||
// Sanitize info array
|
||||
if ( ! empty( $data['info'] ) && is_array( $data['info'] ) ) {
|
||||
$sanitized['info'] = array();
|
||||
foreach ( $data['info'] as $key => $value ) {
|
||||
if ( 'eE-Mail' === $key ) {
|
||||
$sanitized['info'][ $key ] = sanitize_email( $value );
|
||||
} else {
|
||||
$sanitized['info'][ $key ] = sanitize_text_field( $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize room arrays
|
||||
$rooms = Umzugsliste_Furniture_Data::get_rooms();
|
||||
|
||||
foreach ( $rooms as $room_key => $room_label ) {
|
||||
$post_array_name = ucfirst( $room_key );
|
||||
if ( 'kueche_esszimmer' === $room_key ) {
|
||||
$post_array_name = 'Kueche_Esszimmer';
|
||||
}
|
||||
|
||||
if ( ! empty( $data[ $post_array_name ] ) && is_array( $data[ $post_array_name ] ) ) {
|
||||
$sanitized[ $post_array_name ] = array();
|
||||
foreach ( $data[ $post_array_name ] as $key => $value ) {
|
||||
$sanitized[ $post_array_name ][ $key ] = sanitize_text_field( $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save submission to CPT
|
||||
*
|
||||
* @param array $data Sanitized form data
|
||||
* @return int|false Post ID on success, false on failure
|
||||
*/
|
||||
private function save_to_cpt( $data ) {
|
||||
$customer_name = $data['bName'] ?? 'Unbekannt';
|
||||
$date_string = ( $data['day'] ?? '' ) . '.' . ( $data['month'] ?? '' ) . '.' . ( $data['year'] ?? '' );
|
||||
|
||||
// Calculate total CBM
|
||||
$total_cbm = 0;
|
||||
$rooms = Umzugsliste_Furniture_Data::get_rooms();
|
||||
|
||||
foreach ( $rooms as $room_key => $room_label ) {
|
||||
$post_array_name = ucfirst( $room_key );
|
||||
if ( 'kueche_esszimmer' === $room_key ) {
|
||||
$post_array_name = 'Kueche_Esszimmer';
|
||||
}
|
||||
|
||||
$room_data = $data[ $post_array_name ] ?? array();
|
||||
|
||||
foreach ( $room_data as $key => $value ) {
|
||||
if ( substr( $key, 0, 1 ) === 'v' && ! empty( $value ) && floatval( $value ) > 0 ) {
|
||||
$item_name = substr( $key, 1 );
|
||||
$quantity = floatval( str_replace( ',', '.', trim( $value ) ) );
|
||||
$cbm = isset( $room_data[ 'q' . $item_name ] ) ? floatval( $room_data[ 'q' . $item_name ] ) : 0;
|
||||
$total_cbm += ( $quantity * $cbm );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create post
|
||||
$post_id = wp_insert_post(
|
||||
array(
|
||||
'post_title' => 'Anfrage vom ' . $date_string . ' - ' . $customer_name,
|
||||
'post_content' => wp_json_encode( $data ),
|
||||
'post_status' => 'publish',
|
||||
'post_type' => 'umzugsliste_entry',
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! is_wp_error( $post_id ) && $post_id ) {
|
||||
// Add meta data
|
||||
update_post_meta( $post_id, '_umzugsliste_customer_name', $customer_name );
|
||||
update_post_meta( $post_id, '_umzugsliste_customer_email', $data['info']['eE-Mail'] ?? '' );
|
||||
update_post_meta( $post_id, '_umzugsliste_moving_date', $date_string );
|
||||
update_post_meta( $post_id, '_umzugsliste_total_cbm', number_format( $total_cbm, 2, '.', '' ) );
|
||||
update_post_meta( $post_id, '_umzugsliste_submission_ip', $_SERVER['REMOTE_ADDR'] ?? '' );
|
||||
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email via wp_mail()
|
||||
*
|
||||
* @param int $entry_id CPT entry ID
|
||||
* @param array $data Form data
|
||||
* @return bool True on success
|
||||
*/
|
||||
private function send_email( $entry_id, $data ) {
|
||||
// Generate email HTML
|
||||
$email_html = Umzugsliste_Email_Generator::generate( $data );
|
||||
|
||||
// Get receiver email from settings
|
||||
$to = get_option( 'umzugsliste_receiver_email', get_option( 'admin_email' ) );
|
||||
|
||||
// Subject
|
||||
$subject = 'Internetanfrage - Anfrage vom ' . date( 'd.m.Y H:i' );
|
||||
|
||||
// Headers
|
||||
$headers = array( 'Content-Type: text/html; charset=UTF-8' );
|
||||
|
||||
// Add Reply-To if customer email provided
|
||||
$customer_email = $data['info']['eE-Mail'] ?? '';
|
||||
if ( ! empty( $customer_email ) && is_email( $customer_email ) ) {
|
||||
$customer_name = $data['bName'] ?? 'Kunde';
|
||||
$headers[] = 'Reply-To: ' . $customer_name . ' <' . $customer_email . '>';
|
||||
}
|
||||
|
||||
// Send email
|
||||
$sent = wp_mail( $to, $subject, $email_html, $headers );
|
||||
|
||||
// Update CPT meta
|
||||
if ( $entry_id ) {
|
||||
update_post_meta( $entry_id, '_umzugsliste_email_sent', $sent );
|
||||
if ( $sent ) {
|
||||
update_post_meta( $entry_id, '_umzugsliste_email_sent_at', current_time( 'mysql' ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to thank you page
|
||||
*
|
||||
* @param int $entry_id CPT entry ID
|
||||
*/
|
||||
private function redirect_to_thank_you( $entry_id ) {
|
||||
$thank_you_url = get_option( 'umzugsliste_thankyou_url', home_url() );
|
||||
|
||||
// Add query parameters
|
||||
$redirect_url = add_query_arg(
|
||||
array(
|
||||
'umzugsliste' => 'success',
|
||||
'entry' => $entry_id,
|
||||
),
|
||||
$thank_you_url
|
||||
);
|
||||
|
||||
wp_safe_redirect( $redirect_url );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user