HEX
Server: Apache
System: Linux webd004.cluster130.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
User: frenchy (106757)
PHP: 7.4.33
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/frenchy/www/_trash/wp-content/plugins/advanced-forms/core/forms/forms-submissions.php
<?php
  
  
/**
 * Handles validation, and saving of forms
 * Refactored out of core-forms.php since 1.5.0.
 *
 * @since 1.0.0
 *
 */
class AF_Core_Forms_Submissions {
  const DEFAULT_COOKIE_NAME = 'af_submission';

  const OPTION_EXPIRY_MINUTES = 5;
  const OPTION_DATA_PREFIX = 'af_submission_data_';
  const OPTION_EXPIRY_PREFIX = 'af_submission_expiry_';
  
  function __construct() {
    add_action( 'init', array( $this, 'pre_form' ), 10, 0 );
    add_action( 'acf/validate_save_post', array( $this, 'validate' ), 10, 0 );
    add_filter( 'acf/upload_prefilter', array( $this, 'intercept_upload_errors' ), 1000, 3 );
  }
  
  
  /**
   * Handles submissions and enqueue of neccessary scripts
   * Relies on default ACF validations
   *
   * @since 1.0.0
   *
   */
  function pre_form() {
    // Make sure this is not an AJAX validation request
    if ( isset ( $_POST['action'] ) ) {
      return;
    }

    // Make sure honeypot field is empty if one exists
    if ( ! $this->is_honeypot_valid() ) {
      wp_die( 'Non-human user detected' );
      exit;
    }

    // Try loading submission data
    if ( ! $this->load_submission_data() ) {
      return;
    }
      
    // Validate the posted data, this validation has already been performed once over AJAX
    if ( ! acf_validate_save_post( true ) ) {
      return;
    }

    $form = AF()->submission['form'];
    $args = AF()->submission['args'];
    $fields = AF()->submission['fields'];

    $this->process_submission( $form, $args, $fields );

    // Redirect to different URL if redirect argument has been passed
    $redirect_url = $args['redirect'];

    // By default the user is redirected back to the form page.
    // Some browsers will prompt to submit the form again if the form page is reloaded.
    // Redirecting back removes the risk of duplicate submissions.
    if ( NULL === $redirect_url ) {
      $redirect_url = $_POST['af_origin_url'];
    }

    if ( $redirect_url && '' !== $redirect_url ) {
      $this->clear_expired_submissions();
      $this->save_submission( AF()->submission );

      wp_redirect( $redirect_url );
      exit;
    }
  }


  /**
   * Check that the honeypot has not been filled.
   * 
   * @since 1.7.2
   */
  function is_honeypot_valid() {
    if ( isset( $_POST['email_for_non_humans'] ) && ! empty( $_POST['email_for_non_humans'] ) ) {
      return false;
    }

    return true;
  }


  /**
   * Process a form submission.
   * 
   * @since 1.7.2
   * 
   */
  function process_submission( $form, $args, $fields ) {
    // Increase the form submissions counter
    if ( $form['post_id'] ) {
      $submissions = get_post_meta( $form['post_id'], 'form_num_of_submissions', true );
      $submissions = $submissions ? $submissions + 1 : 1;
      update_post_meta( $form['post_id'], 'form_num_of_submissions', $submissions );
    }

    do_action( 'af/form/before_submission', $form, $fields, $args );
    do_action( 'af/form/before_submission/id=' . $form['post_id'], $form, $fields, $args );
    do_action( 'af/form/before_submission/key=' . $form['key'], $form, $fields, $args );
    
    if ( ! af_submission_failed() ) {
      do_action( 'af/form/submission', $form, $fields, $args );
      do_action( 'af/form/submission/id=' . $form['post_id'], $form, $fields, $args );
      do_action( 'af/form/submission/key=' . $form['key'], $form, $fields, $args );
    }
  }


  /**
   * Handles validation of a form.
   * Adds custom validation actions specific to forms.
   *
   * @since 1.5.0
   *
   */
  function validate() {
    // Try loading submission data
    if ( ! $this->load_submission_data() ) {
      return;
    }

    $form = AF()->submission['form'];
    $args = AF()->submission['args'];

    do_action( 'af/form/validate', $form, $args );
    do_action( 'af/form/validate/id=' . $form['post_id'], $form, $args );
    do_action( 'af/form/validate/key=' . $form['key'], $form, $args );
  }
  

  /**
   * Populate AF()->submission with submission data
   * Returns boolean indicating whether a submission was loaded
   *
   * @since 1.5.0
   *
   */
  function load_submission_data() {
    // Check if there is a cookie-passed submission
    if ( $submission = $this->get_submission() ) {
      AF()->submission = $submission;

      // Return false to stop the submission from being processed again
      return false;
    }

    // Make sure a form was posted
    if ( ! ( isset( $_POST['af_form'] ) ) ) {
      return false;
    }

    // Bail early if already loaded
    if ( AF()->submission ) {
      return true;
    }

    /**
     * Upload all files in $_FILES using ACFs helper function. Required for basic uploads to work painlessly.
     * TODO: Move to af_save_field() to avoid saving all files?
     *
     * @since 1.3.1
     *
     */
    if ( isset( $_FILES['acf'] ) ) {
      $this->clear_upload_errors();
      acf_upload_files();
      $this->handle_upload_errors();
    }

    // Generate submission from data
    $submission = $this->create_submission();
    if ( ! $submission ) {
      return false;
    }

    // Save submission data to the global AF object
    AF()->submission = $submission;

    return true;
  }


  /**
   * Create a submission object from the request data.
   * Returns a submission array or false on failure.
   *
   * @since 1.6.0
   *
   */
  function create_submission() {
    // Load form by key
    $form_key_or_id = $_POST['af_form'];
      
    $form = af_get_form( $form_key_or_id );

    if ( ! $form ) {
      return false;
    }

    // Retrieve the args used to display the form
    $encoded_args = $_POST['af_form_args'];
    $args = json_decode( base64_decode( $encoded_args ), true );

    // Verify nonce
    $nonce = $_POST['af_form_nonce'];
    $hashed_args = hash( 'sha256', $encoded_args );
    $nonce_value = sprintf( 'af_submission_%s_%s', $form['key'], $hashed_args );
    if ( ! wp_verify_nonce( $nonce, $nonce_value ) ) {
      wp_die( 'This form is no longer valid. Please reload the page and try again.' );
      exit;
    }

    // Retrieve all form fields and their values
    $fields = array();
    
    if ( isset( $_POST['acf'] ) ) {
      foreach ( $_POST['acf'] as $k => $value ) {
        $field = acf_get_field( $k );
        
        $field['_input'] = $value;
        $field['value'] = acf_format_value( $value, 0, $field );
        
        $fields[] = $field;
      }
    }

    return array(
      'form' => $form,
      'args' => $args,
      'fields' => $fields,
      'errors' => array(),
    );
  }

  /**
   * Fetch a submission from options if the submission cookie is set.
   * Will return false if the cookie is not set or the submission does not exist in the database.
   * 
   * @since 1.6.6
   *
   */
  private function get_submission() {
    if ( ! isset( $_COOKIE[ $this->get_cookie_name() ] ) ) {
      return false;
    }

    $key = $_COOKIE[ $this->get_cookie_name() ];
    $submission = get_option( self::OPTION_DATA_PREFIX . $key, false );

    $this->delete_submission( $key );
    setcookie( $this->get_cookie_name(), '', time() - HOUR_IN_SECONDS, '/' );

    return $submission;
  }
  
  /**
   * Save a submission to options and set a cookie with a reference to it.
   * Submissions are identified by a randomly generated key stored in a cookie.
   *
   * @since 1.6.6
   *
   */
  private function save_submission( $submission ) {
    $key = wp_generate_password( 12, false, false );

    $expiration_time = time() + self::OPTION_EXPIRY_MINUTES * MINUTE_IN_SECONDS;

    add_option( self::OPTION_DATA_PREFIX . $key, $submission );
    add_option( self::OPTION_EXPIRY_PREFIX . $key, $expiration_time );

    setcookie( $this->get_cookie_name(), $key, $expiration_time, '/' );
  }

  /**
   * Delete a submission from options based on key
   *
   * @since 1.6.6
   *
   */
  private function delete_submission( $key ) {
    delete_option( self::OPTION_DATA_PREFIX . $key );
    delete_option( self::OPTION_EXPIRY_PREFIX . $key );
  }

  private function get_cookie_name() {
    return apply_filters( 'af/settings/cookie_name', self::DEFAULT_COOKIE_NAME );
  }

  /**
   * Remove any expired submission from options which have not been cleared automatically.
   * If a request fails a created submission could potentially not be removed from the database.
   *
   * @since 1.6.6
   *
   */
  private function clear_expired_submissions() {
    global $wpdb;

    $options_table = $wpdb->prefix . 'options';
    $name_pattern = self::OPTION_EXPIRY_PREFIX . '%';
    $current_time = time();

    // Find all expired submissions in the options table.
    // This query is very efficient because of the index on the name column.
    $expired_submissions = $wpdb->get_col("
      SELECT option_name
      FROM $options_table
      WHERE option_name LIKE '$name_pattern'
        AND option_value < $current_time
    ");

    foreach ( $expired_submissions as $option_name ) {
      // Find submission key by removing prefix from option name.
      $submission_key = substr( $option_name, strlen( self::OPTION_EXPIRY_PREFIX ) );
      $this->delete_submission( $submission_key );
    }
  }

  /**
   * ACF doesn't provide a simple way of catching upload errors when using the basic uploader.
   * This function is hooked into the "acf/upload_prefilter" with a high priority.
   * It will intercept all upload errors and save them together with field data.
   *
   * @since 1.7.0
   *
   */
  function intercept_upload_errors( $errors, $file, $field ) {
    if ( ! empty( $errors ) ) {
      $this->upload_errors[ $field['key'] ] = array(
        'field' => $field,
        'messages' => $errors,
      );
    }

    return $errors;
  }

  /**
   * Removes all intercepted upload errors.
   * Should be run before handling uploads using "acf_upload_files()".
   *
   * @since 1.7.0
   *
   */
  private function clear_upload_errors() {
    $this->upload_errors = array();
  }

  /**
   * Checks if any upload errors have been caught and stops the submission.
   * This is a very rudimentary way of handling upload errors but it's necessary as ACF can't handle errors when using the basic uploader.
   * The errors checks should in the future be implemented client-side for a good user experience and this is mostly meant to be a fallback.
   * 
   *
   * @since 1.7.0
   *
   */
  private function handle_upload_errors() {
    if ( empty( $this->upload_errors ) ) {
      return;
    }

    $message = sprintf( '<h2>%s</h2>', __('Validation failed', 'acf') );
    $message .= '<ul>';
    foreach( $this->upload_errors as $error ) {
      $field = $error['field'];
      foreach ( $error['messages'] as $error_message ) {
        $message .= '<li>' . sprintf( '%s: %s', $field['label'], $error_message ) . '</li>';
      }
    }
    $message .= '</ul>';

    wp_die( $message, __('Validation failed', 'acf') );
  }
}

return new AF_Core_Forms_Submissions();