forms = []; // Filters. add_filter( 'amp_skip_post', [ $this, 'amp_skip_post' ] ); // Actions. add_action( 'wpforms_frontend_output_success', [ $this, 'confirmation' ], 10, 3 ); add_action( 'wpforms_frontend_output', [ $this, 'head' ], 5, 5 ); add_action( 'wpforms_frontend_output', [ $this, 'fields' ], 10, 5 ); add_action( 'wpforms_display_field_before', [ $this, 'field_container_open' ], 5, 2 ); add_action( 'wpforms_display_field_before', [ $this, 'field_label' ], 15, 2 ); add_action( 'wpforms_display_field_before', [ $this, 'field_description' ], 20, 2 ); add_action( 'wpforms_display_field_after', [ $this, 'field_error' ], 3, 2 ); add_action( 'wpforms_display_field_after', [ $this, 'field_description' ], 5, 2 ); add_action( 'wpforms_display_field_after', [ $this, 'field_container_close' ], 15, 2 ); add_action( 'wpforms_frontend_output', [ $this, 'recaptcha' ], 20, 5 ); add_action( 'wpforms_frontend_output', [ $this, 'foot' ], 25, 5 ); add_action( 'wp_enqueue_scripts', [ $this, 'assets_header' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'recaptcha_noconflict' ], 9999 ); add_action( 'wp_footer', [ $this, 'missing_assets_error_js' ] ); add_action( 'wp_footer', [ $this, 'assets_footer' ], 15 ); add_action( 'wp_footer', [ $this, 'recaptcha_noconflict' ], 19 ); add_action( 'wp_footer', [ $this, 'footer_end' ], 99 ); // Register shortcode. add_shortcode( 'wpforms', [ $this, 'shortcode' ] ); } /** * Get the amp-state ID for a given form. * * @param int $form_id Form ID. * @return string State ID. */ protected function get_form_amp_state_id( $form_id ) { return sprintf( 'wpforms_form_state_%d', $form_id ); } /** * Disable AMP if query param is detected. * * This allows the full form to be accessible for Pro users or sites * that do not have SSL. * * @since 1.5.3 * * @param bool $skip Skip AMP mode, display full post. * * @return bool */ public function amp_skip_post( $skip ) { return isset( $_GET['nonamp'] ) ? true : $skip; } /** * Primary function to render a form on the frontend. * * @since 1.0.0 * * @param int $id Form ID. * @param bool $title Whether to display form title. * @param bool $description Whether to display form description. */ public function output( $id, $title = false, $description = false ) { if ( empty( $id ) ) { return; } // Grab the form data, if not found then we bail. $form = wpforms()->form->get( (int) $id ); if ( empty( $form ) ) { return; } // Basic information. $form_data = apply_filters( 'wpforms_frontend_form_data', wpforms_decode( $form->post_content ) ); $form_id = absint( $form->ID ); $settings = $form_data['settings']; $action = esc_url_raw( remove_query_arg( 'wpforms' ) ); $classes = (int) wpforms_setting( 'disable-css', '1' ) === 1 ? [ 'wpforms-container-full' ] : []; $errors = empty( wpforms()->process->errors[ $form_id ] ) ? array() : wpforms()->process->errors[ $form_id ]; $title = filter_var( $title, FILTER_VALIDATE_BOOLEAN ); $description = filter_var( $description, FILTER_VALIDATE_BOOLEAN ); // If the form does not contain any fields - do not proceed. if ( empty( $form_data['fields'] ) ) { echo ''; return; } // We need to stop output processing in case we are on AMP page. if ( wpforms_is_amp( false ) && ( ! current_theme_supports( 'amp' ) || apply_filters( 'wpforms_amp_pro', wpforms()->pro ) || ! is_ssl() || ! defined( 'AMP__VERSION' ) || version_compare( AMP__VERSION, '1.2', '<' ) ) ) { $text = apply_filters( 'wpforms_frontend_shortcode_amp_text', sprintf( wp_kses( /* translators: %s - URL to a non-amp version of a page with the form. */ __( 'Go to the full page to view and submit the form.', 'wpforms-lite' ), array( 'a' => array( 'href' => array(), ), ) ), esc_url( home_url( add_query_arg( 'nonamp', '1' ) . '#wpforms-' . absint( $form->ID ) ) ) ) ); echo '

' . $text . '

'; return; } // Add url query var wpforms_form_id to track post_max_size overflows. if ( in_array( 'file-upload', wp_list_pluck( $form_data['fields'], 'type' ), true ) ) { $action = add_query_arg( 'wpforms_form_id', $form_id, $action ); } // Before output hook. do_action( 'wpforms_frontend_output_before', $form_data, $form ); // Check for return hash. if ( ! empty( $_GET['wpforms_return'] ) && wpforms()->process->valid_hash && absint( wpforms()->process->form_data['id'] ) === $form_id ) { do_action( 'wpforms_frontend_output_success', wpforms()->process->form_data, wpforms()->process->fields, wpforms()->process->entry_id ); wpforms_debug_data( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing return; } // Check for error-free completed form. if ( empty( $errors ) && ! empty( $form_data ) && ! empty( $_POST['wpforms']['id'] ) && absint( $_POST['wpforms']['id'] ) === $form_id ) { do_action( 'wpforms_frontend_output_success', $form_data, false, false ); wpforms_debug_data( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing return; } // Allow filter to return early if some condition is not met. if ( ! apply_filters( 'wpforms_frontend_load', true, $form_data, null ) ) { do_action( 'wpforms_frontend_not_loaded', $form_data, $form ); return; } // All checks have passed, so calculate multi-page details for the form. $pages = wpforms_get_pagebreak_details( $form_data ); if ( $pages ) { $this->pages = $pages; } else { $this->pages = false; } // Allow final action to be customized - 3rd param ($form) has been deprecated. $action = apply_filters( 'wpforms_frontend_form_action', $action, $form_data, null ); // Allow form container classes to be filtered and user defined classes. $classes = apply_filters( 'wpforms_frontend_container_class', $classes, $form_data ); if ( ! empty( $settings['form_class'] ) ) { $classes = array_merge( $classes, explode( ' ', $settings['form_class'] ) ); } $classes = wpforms_sanitize_classes( $classes, true ); $form_classes = array( 'wpforms-validate', 'wpforms-form' ); if ( ! empty( $form_data['settings']['ajax_submit'] ) && ! wpforms_is_amp() ) { $form_classes[] = 'wpforms-ajax-form'; } $form_atts = array( 'id' => sprintf( 'wpforms-form-%d', absint( $form_id ) ), 'class' => $form_classes, 'data' => array( 'formid' => absint( $form_id ), ), 'atts' => array( 'method' => 'post', 'enctype' => 'multipart/form-data', 'action' => esc_url( $action ), ), ); if ( wpforms_is_amp() ) { // Set submitting state. if ( ! isset( $form_atts['atts']['on'] ) ) { $form_atts['atts']['on'] = ''; } else { $form_atts['atts']['on'] .= ';'; } $form_atts['atts']['on'] .= sprintf( 'submit:AMP.setState( %1$s ); submit-success:AMP.setState( %2$s ); submit-error:AMP.setState( %2$s );', wp_json_encode( array( $this->get_form_amp_state_id( $form_id ) => array( 'submitting' => true, ), ) ), wp_json_encode( array( $this->get_form_amp_state_id( $form_id ) => array( 'submitting' => false, ), ) ) ); // Upgrade the form to be an amp-form to avoid sanitizer conversion. if ( isset( $form_atts['atts']['action'] ) ) { $form_atts['atts']['action-xhr'] = $form_atts['atts']['action']; unset( $form_atts['atts']['action'] ); $form_atts['atts']['verify-xhr'] = $form_atts['atts']['action-xhr']; } } $form_atts = apply_filters( 'wpforms_frontend_form_atts', $form_atts, $form_data ); // Begin to build the output. do_action( 'wpforms_frontend_output_container_before', $form_data, $form ); printf( '
', esc_attr( $classes ), absint( $form_id ) ); do_action( 'wpforms_frontend_output_form_before', $form_data, $form ); echo '
'; if ( wpforms_is_amp() ) { $state = array( 'submitting' => false, ); printf( '', $this->get_form_amp_state_id( $form_id ), wp_json_encode( $state ) ); } do_action( 'wpforms_frontend_output', $form_data, null, $title, $description, $errors ); echo '
'; do_action( 'wpforms_frontend_output_form_after', $form_data, $form ); echo '
'; do_action( 'wpforms_frontend_output_container_after', $form_data, $form ); // Add form to class property that tracks all forms in a page. $this->forms[ $form_id ] = $form_data; // Optional debug information if WPFORMS_DEBUG is defined. wpforms_debug_data( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing // After output hook. do_action( 'wpforms_frontend_output_after', $form_data, $form ); } /** * Display form confirmation message. * * @since 1.0.0 * * @param array $form_data Form data and settings. * @param array $fields Sanitized field data. * @param int $entry_id Entry id. */ public function confirmation( $form_data, $fields = array(), $entry_id = 0 ) { $class = intval( wpforms_setting( 'disable-css', '1' ) ) === 1 ? 'wpforms-confirmation-container-full' : 'wpforms-confirmation-container'; // In AMP, just print template. if ( wpforms_is_amp() ) { $this->assets_confirmation(); printf( '
', esc_attr( $class ) ); return; } if ( empty( $fields ) ) { $fields = ! empty( $_POST['wpforms']['complete'] ) ? $_POST['wpforms']['complete'] : array(); } if ( empty( $entry_id ) ) { $entry_id = ! empty( $_POST['wpforms']['entry_id'] ) ? $_POST['wpforms']['entry_id'] : 0; } $confirmation = wpforms()->get( 'process' )->get_current_confirmation(); $confirmation_message = wpforms()->get( 'process' )->get_confirmation_message( $form_data, $fields, $entry_id ); // Only display if a confirmation message has been configured. if ( empty( $confirmation ) || empty( $confirmation_message ) ) { return; } // Load confirmation specific assets. $this->assets_confirmation(); /** * Fires once before the confirmation message. * * @since 1.6.9 * * @param array $confirmation Current confirmation data. * @param array $form_data Form data and settings. * @param array $fields Sanitized field data. * @param int $entry_id Entry id. */ do_action( 'wpforms_frontend_confirmation_message_before', $confirmation, $form_data, $fields, $entry_id ); $class .= $this->confirmation_message_scroll ? ' wpforms-confirmation-scroll' : ''; printf( '
%s
', $class, absint( $form_data['id'] ), $confirmation_message ); /** * Fires once after the confirmation message. * * @since 1.6.9 * * @param array $confirmation Current confirmation data. * @param array $form_data Form data and settings. * @param array $fields Sanitized field data. * @param int $entry_id Entry id. */ do_action( 'wpforms_frontend_confirmation_message_after', $confirmation, $form_data, $fields, $entry_id ); } /** * Form head area, for displaying form title and description if enabled. * * @since 1.0.0 * * @param array $form_data Form data and settings. * @param null $deprecated Deprecated in v1.3.7, previously was $form object. * @param bool $title Whether to display form title. * @param bool $description Whether to display form description. * @param array $errors List of all errors filled in WPForms_Process::process(). */ public function head( $form_data, $deprecated, $title, $description, $errors ) { $settings = $form_data['settings']; // Output title and/or description. if ( true === $title || true === $description ) { echo '
'; if ( true === $title && ! empty( $settings['form_title'] ) ) { echo '
' . esc_html( $settings['form_title'] ) . '
'; } if ( true === $description && ! empty( $settings['form_desc'] ) ) { echo '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo apply_filters( 'wpforms_process_smart_tags', $settings['form_desc'], $form_data ); echo '
'; } echo '
'; } // Output