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:
2026-01-16 12:30:42 +09:00
parent 64f25041ad
commit d1d71a5e4e

View 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;
}
}