';
// After output hook.
do_action( 'wpforms_frontend_output_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( $form_data );
}
/**
* 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 ) {
// Only display if a confirmation message has been configured.
if ( empty( $this->confirmation_message ) ) {
return;
}
// Load confirmation specific assets.
$this->assets_confirmation();
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;
}
$message = apply_filters( 'wpforms_process_smart_tags', $this->confirmation_message, $form_data, $fields, $entry_id );
$message = apply_filters( 'wpforms_frontend_confirmation_message', wpautop( $message ), $form_data, $fields, $entry_id );
$class = wpforms_setting( 'disable-css', '1' ) == '1' ? 'wpforms-confirmation-container-full' : 'wpforms-confirmation-container';
$class .= $this->confirmation_message_scroll ? ' wpforms-confirmation-scroll' : '';
printf(
'
%s
',
$class,
absint( $form_data['id'] ),
$message
);
}
/**
* 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 '
';
}
}
/**
* Form field area.
*
* @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 fields( $form_data, $deprecated, $title, $description, $errors ) {
// Obviously we need to have form fields to proceed.
if ( empty( $form_data['fields'] ) ) {
return;
}
// Form fields area.
echo '
';
/**
* Core actions on this hook:
* Priority / Description
* 20 Pagebreak markup (open first page)
*/
do_action( 'wpforms_display_fields_before', $form_data );
// Loop through all the fields we have.
foreach ( $form_data['fields'] as $field ) :
$field = apply_filters( 'wpforms_field_data', $field, $form_data );
if ( empty( $field ) ) {
continue;
}
// Get field attributes. Deprecated; Customizations should use
// field properties instead.
$attributes = $this->get_field_attributes( $field, $form_data );
// Add properties to the field so it's available everywhere.
$field['properties'] = $this->get_field_properties( $field, $form_data, $attributes );
/**
* Core actions on this hook:
* Priority / Description
* 5 Field opening container markup.
* 15 Field label.
* 20 Field description (depending on position).
*/
do_action( 'wpforms_display_field_before', $field, $form_data );
/**
* Individual field classes use this hook to display the actual
* field form elements.
* See `field_display` methods in /includes/fields.
*/
do_action( "wpforms_display_field_{$field['type']}", $field, $attributes, $form_data );
/**
* Core actions on this hook:
* Priority / Description
* 3 Field error messages.
* 5 Field description (depending on position).
* 15 Field closing container markup.
* 20 Pagebreak markup (close previous page, open next)
*/
do_action( 'wpforms_display_field_after', $field, $form_data );
endforeach;
/**
* Core actions on this hook:
* Priority / Description
* 5 Pagebreak markup (close last page)
*/
do_action( 'wpforms_display_fields_after', $form_data );
echo '
',
wpforms_html_attributes( $container['id'], $container['class'], $container['data'], $container['attr'] )
);
}
/**
* Display the label for each field.
*
* @since 1.3.7
*
* @param array $field Field data and settings.
* @param array $form_data Form data and settings.
*/
public function field_label( $field, $form_data ) {
$label = $field['properties']['label'];
// If the label is empty or disabled don't proceed.
if ( empty( $label['value'] ) || $label['disabled'] ) {
return;
}
$required = $label['required'] ? wpforms_get_field_required_label() : '';
printf( '',
wpforms_html_attributes( $label['id'], $label['class'], $label['data'], $label['attr'] ),
esc_html( $label['value'] ),
$required
);
}
/**
* Display any errors for each field.
*
* @since 1.3.7
*
* @param array $field Field data and settings.
* @param array $form_data Form data and settings.
*/
public function field_error( $field, $form_data ) {
$error = $field['properties']['error'];
// If there are no errors don't proceed.
// Advanced fields with multiple inputs (address, name, etc) errors
// will be an array and are handled within the respective field class.
if ( empty( $error['value'] ) || is_array( $error['value'] ) ) {
return;
}
printf( '',
wpforms_html_attributes( $error['id'], $error['class'], $error['data'], $error['attr'] ),
esc_html( $error['value'] )
);
}
/**
* Display the description for each field.
*
* @since 1.3.7
*
* @param array $field Field data and settings.
* @param array $form_data Form data and settings.
*/
public function field_description( $field, $form_data ) {
$action = current_action();
$description = $field['properties']['description'];
// If the description is empty don't proceed.
if ( empty( $description['value'] ) ) {
return;
}
// Determine positioning.
if ( 'wpforms_display_field_before' === $action && 'before' !== $description['position'] ) {
return;
}
if ( 'wpforms_display_field_after' === $action && 'after' !== $description['position'] ) {
return;
}
if ( 'before' === $description['position'] ) {
$description['class'][] = 'before';
}
printf( '
%s
',
wpforms_html_attributes( $description['id'], $description['class'], $description['data'], $description['attr'] ),
$description['value']
);
}
/**
* Display the closing container markup for each field.
*
* @since 1.3.7
*
* @param array $field Field data and settings.
* @param array $form_data Form data and settings.
*/
public function field_container_close( $field, $form_data ) {
echo '
';
}
/**
* Anti-spam honeypot output if configured.
*
* @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 honeypot( $form_data, $deprecated, $title, $description, $errors ) {
if (
empty( $form_data['settings']['honeypot'] ) ||
'1' !== $form_data['settings']['honeypot']
) {
return;
}
$names = array( 'Name', 'Phone', 'Comment', 'Message', 'Email', 'Website' );
echo '
';
}
/**
* Determine if we should load assets globally.
* If false assets will load conditionally (default).
*
* @since 1.2.4
*
* @return bool
*/
public function assets_global() {
return apply_filters( 'wpforms_global_assets', wpforms_setting( 'global-assets', false ) );
}
/**
* Load the necessary CSS for single pages/posts earlier if possible.
*
* If we are viewing a singular page, then we can check the content early
* to see if the shortcode was used. If not we fallback and load the assets
* later on during the page (widgets, archives, etc).
*
* @since 1.0.0
*/
public function assets_header() {
if ( ! is_singular() ) {
return;
}
global $post;
if ( has_shortcode( $post->post_content, 'wpforms' ) ) {
$this->assets_css();
}
}
/**
* Load the CSS assets for frontend output.
*
* @since 1.0.0
*/
public function assets_css() {
do_action( 'wpforms_frontend_css', $this->forms );
// jQuery date/time library CSS.
if (
$this->assets_global() ||
true === wpforms_has_field_type( 'date-time', $this->forms, true )
) {
wp_enqueue_style(
'wpforms-jquery-timepicker',
WPFORMS_PLUGIN_URL . 'assets/css/jquery.timepicker.css',
array(),
'1.11.5'
);
wp_enqueue_style(
'wpforms-flatpickr',
WPFORMS_PLUGIN_URL . 'assets/css/flatpickr.min.css',
array(),
'4.5.5'
);
}
// Load CSS per global setting.
if ( wpforms_setting( 'disable-css', '1' ) == '1' ) {
wp_enqueue_style(
'wpforms-full',
WPFORMS_PLUGIN_URL . 'assets/css/wpforms-full.css',
array(),
WPFORMS_VERSION
);
}
if ( wpforms_setting( 'disable-css', '1' ) == '2' ) {
wp_enqueue_style(
'wpforms-base',
WPFORMS_PLUGIN_URL . 'assets/css/wpforms-base.css',
array(),
WPFORMS_VERSION
);
}
}
/**
* Load the JS assets for frontend output.
*
* @since 1.0.0
*/
public function assets_js() {
do_action( 'wpforms_frontend_js', $this->forms );
// Load jQuery validation library - https://jqueryvalidation.org/.
wp_enqueue_script(
'wpforms-validation',
WPFORMS_PLUGIN_URL . 'assets/js/jquery.validate.min.js',
array( 'jquery' ),
'1.19.0',
true
);
// Load jQuery date/time libraries.
if (
$this->assets_global() ||
true === wpforms_has_field_type( 'date-time', $this->forms, true )
) {
wp_enqueue_script(
'wpforms-flatpickr',
WPFORMS_PLUGIN_URL . 'assets/js/flatpickr.min.js',
array( 'jquery' ),
'4.5.5',
true
);
wp_enqueue_script(
'wpforms-jquery-timepicker',
WPFORMS_PLUGIN_URL . 'assets/js/jquery.timepicker.min.js',
array( 'jquery' ),
'1.11.5',
true
);
}
// Load jQuery input mask library - https://github.com/RobinHerbots/jquery.inputmask.
if (
$this->assets_global() ||
true === wpforms_has_field_type( array( 'phone', 'address' ), $this->forms, true ) ||
true === wpforms_has_field_setting( 'input_mask', $this->forms, true )
) {
wp_enqueue_script(
'wpforms-maskedinput',
WPFORMS_PLUGIN_URL . 'assets/js/jquery.inputmask.bundle.min.js',
array( 'jquery' ),
'4.0.6',
true
);
}
// Load mailcheck library - https://github.com/mailcheck/mailcheck.
if (
$this->assets_global() ||
true === wpforms_has_field_type( array( 'email' ), $this->forms, true )
) {
wp_enqueue_script(
'wpforms-mailcheck',
WPFORMS_PLUGIN_URL . 'assets/js/mailcheck.min.js',
false,
'1.1.2',
true
);
}
// Load CC payment library - https://github.com/stripe/jquery.payment/.
if (
$this->assets_global() ||
true === wpforms_has_field_type( 'credit-card', $this->forms, true )
) {
wp_enqueue_script(
'wpforms-payment',
WPFORMS_PLUGIN_URL . 'assets/js/jquery.payment.min.js',
array( 'jquery' ),
WPFORMS_VERSION,
true
);
}
// Load base JS.
wp_enqueue_script(
'wpforms',
WPFORMS_PLUGIN_URL . 'assets/js/wpforms.js',
array( 'jquery' ),
WPFORMS_VERSION,
true
);
// Load reCAPTCHA support if form supports it.
$site_key = wpforms_setting( 'recaptcha-site-key' );
$secret_key = wpforms_setting( 'recaptcha-secret-key' );
$type = wpforms_setting( 'recaptcha-type', 'v2' );
if ( $site_key && $secret_key ) {
$recaptcha_api = apply_filters( 'wpforms_frontend_recaptcha_url', 'https://www.google.com/recaptcha/api.js?onload=wpformsRecaptchaLoad&render=explicit' );
wp_enqueue_script(
'wpforms-recaptcha',
$recaptcha_api,
array( 'jquery' ),
'2.0.0',
true
);
if ( 'invisible' === $type ) {
$recaptch_inline = 'var wpformsRecaptchaLoad = function(){jQuery(".g-recaptcha").each(function(index, el){var recaptchaID = grecaptcha.render(el,{callback:function(){wpformsRecaptchaCallback(el);}},true);jQuery(el).closest("form").find("button[type=submit]").get(0).recaptchaID = recaptchaID;});};';
$recaptch_inline .= 'var wpformsRecaptchaCallback = function(el){var $form = jQuery(el).closest("form");$form.find("button[type=submit]").get(0).recaptchaID = false;$form.submit();};';
} else {
$recaptch_inline = 'var wpformsRecaptchaLoad = function(){jQuery(".g-recaptcha").each(function(index, el){grecaptcha.render(el,{callback:function(){wpformsRecaptchaCallback(el);}},true);});};';
$recaptch_inline .= 'var wpformsRecaptchaCallback = function(el){jQuery(el).parent().find(".wpforms-recaptcha-hidden").val("1").trigger("change").valid();};';
}
wp_add_inline_script( 'wpforms-recaptcha', $recaptch_inline );
}
}
/**
* Load the necessary assets for the confirmation message.
*
* @since 1.1.2
*/
public function assets_confirmation() {
// Base CSS only.
if ( wpforms_setting( 'disable-css', '1' ) == '1' ) {
wp_enqueue_style(
'wpforms-full',
WPFORMS_PLUGIN_URL . 'assets/css/wpforms-full.css',
array(),
WPFORMS_VERSION
);
}
// Special confirmation JS.
wp_enqueue_script(
'wpforms-confirmation',
WPFORMS_PLUGIN_URL . 'assets/js/wpforms-confirmation.js',
array( 'jquery' ),
WPFORMS_VERSION,
true
);
do_action( 'wpforms_frontend_confirmation' );
}
/**
* Load the assets in footer if needed (archives, widgets, etc).
*
* @since 1.0.0
*/
public function assets_footer() {
if ( empty( $this->forms ) && ! $this->assets_global() ) {
return;
}
$this->assets_css();
$this->assets_js();
do_action( 'wpforms_wp_footer', $this->forms );
}
/**
* Hook at fires at a later priority in wp_footer
*
* @since 1.0.5
*/
public function footer_end() {
if ( empty( $this->forms ) && ! $this->assets_global() ) {
return;
}
/*
* Below we do our own implementation of wp_localize_script in an effort
* to be better compatible with caching plugins which were causing
* conflicts.
*/
// Define base strings.
$strings = array(
'val_required' => wpforms_setting( 'validation-required', esc_html__( 'This field is required.', 'wpforms-lite' ) ),
'val_url' => wpforms_setting( 'validation-url', esc_html__( 'Please enter a valid URL.', 'wpforms-lite' ) ),
'val_email' => wpforms_setting( 'validation-email', esc_html__( 'Please enter a valid email address.', 'wpforms-lite' ) ),
'val_email_suggestion' => wpforms_setting( 'validation-email-suggestion', esc_html__( 'Did you mean {suggestion}?', 'wpforms-lite' ) ),
'val_email_suggestion_title' => esc_attr__( 'Click to accept this suggestion.', 'wpforms-lite' ),
'val_number' => wpforms_setting( 'validation-number', esc_html__( 'Please enter a valid number.', 'wpforms-lite' ) ),
'val_confirm' => wpforms_setting( 'validation-confirm', esc_html__( 'Field values do not match.', 'wpforms-lite' ) ),
'val_fileextension' => wpforms_setting( 'validation-fileextension', esc_html__( 'File type is not allowed.', 'wpforms-lite' ) ),
'val_filesize' => wpforms_setting( 'validation-filesize', esc_html__( 'File exceeds max size allowed.', 'wpforms-lite' ) ),
'val_time12h' => wpforms_setting( 'validation-time12h', esc_html__( 'Please enter time in 12-hour AM/PM format (eg 8:45 AM).', 'wpforms-lite' ) ),
'val_time24h' => wpforms_setting( 'validation-time24h', esc_html__( 'Please enter time in 24-hour format (eg 22:45).', 'wpforms-lite' ) ),
'val_requiredpayment' => wpforms_setting( 'validation-requiredpayment', esc_html__( 'Payment is required.', 'wpforms-lite' ) ),
'val_creditcard' => wpforms_setting( 'validation-creditcard', esc_html__( 'Please enter a valid credit card number.', 'wpforms-lite' ) ),
'val_smart_phone' => wpforms_setting( 'validation-smart-phone', esc_html__( 'Please enter a valid phone number.', 'wpforms-lite' ) ),
'val_post_max_size' => wpforms_setting( 'validation-post_max_size', esc_html__( 'The total size of the selected files {totalSize} Mb exceeds the allowed limit {maxSize} Mb.', 'wpforms-lite' ) ),
'val_checklimit' => wpforms_setting( 'validation-check-limit', esc_html__( 'You have exceeded the number of allowed selections: {#}.', 'wpforms-lite' ) ),
'post_max_size' => wpforms_size_to_bytes( ini_get( 'post_max_size' ) ),
'uuid_cookie' => false,
'locale' => wpforms_get_language_code(),
'wpforms_plugin_url' => WPFORMS_PLUGIN_URL,
'gdpr' => wpforms_setting( 'gdpr' ),
);
// Include payment related strings if needed.
if ( function_exists( 'wpforms_get_currencies' ) ) {
$currency = wpforms_setting( 'currency', 'USD' );
$currencies = wpforms_get_currencies();
$strings['currency_code'] = $currency;
$strings['currency_thousands'] = $currencies[ $currency ]['thousands_separator'];
$strings['currency_decimal'] = $currencies[ $currency ]['decimal_separator'];
$strings['currency_symbol'] = $currencies[ $currency ]['symbol'];
$strings['currency_symbol_pos'] = $currencies[ $currency ]['symbol_pos'];
}
$strings = apply_filters( 'wpforms_frontend_strings', $strings );
foreach ( (array) $strings as $key => $value ) {
if ( ! is_scalar( $value ) ) {
continue;
}
$strings[ $key ] = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
}
echo "\n";
do_action( 'wpforms_wp_footer_end', $this->forms );
}
/**
* Google reCAPTCHA no-conflict mode.
*
* When enabled in the WPForms settings, forcefully remove all other
* reCAPTCHA enqueues to prevent conflicts. Filter can be used to target
* specific pages, etc.
*
* @since 1.4.5
*/
public function recaptcha_noconflict() {
$noconflict = wpforms_setting( 'recaptcha-noconflict' );
if ( empty( $noconflict ) ) {
return;
}
if ( ! apply_filters( 'wpforms_frontend_recaptcha_noconflict', true ) ) {
return;
}
global $wp_scripts;
$urls = array( 'google.com/recaptcha', 'gstatic.com/recaptcha' );
foreach ( $wp_scripts->queue as $handle ) {
if ( false !== strpos( $wp_scripts->registered[ $handle ]->handle, 'wpforms' ) ) {
return;
}
foreach ( $urls as $url ) {
if ( false !== strpos( $wp_scripts->registered[ $handle ]->src, $url ) ) {
wp_dequeue_script( $handle );
wp_deregister_script( $handle );
break;
}
}
}
}
/**
* Shortcode wrapper for the outputting a form.
*
* @since 1.0.0
*
* @param array $atts Shortcode attributes provided by a user.
*
* @return string
*/
public function shortcode( $atts ) {
$defaults = array(
'id' => false,
'title' => false,
'description' => false,
);
$atts = shortcode_atts( $defaults, shortcode_atts( $defaults, $atts, 'output' ), 'wpforms' );
// We need to stop shortcode processing in case we are on AMP page.
if ( wpforms_is_amp() ) {
$post_id = get_the_ID();
// Display our custom link to non-AMP only if we are on single post/page.
if ( ! empty( $post_id ) && ! empty( $atts['id'] ) ) {
/*
* We need this get param as one of the most popular ampforwp plugin has feature
* for mobile users being force-redirected to AMP version of a site.
* This `nonamp` GET param will ensure they will get to the actual page.
* Other plugins will ignore it.
*/
$link = trailingslashit( get_permalink( $post_id ) ) . '?nonamp=1#wpforms-' . absint( $atts['id'] );
$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(),
),
)
),
$link
)
);
return '
' . $text . '
';
}
// In case we are not on a post/page - return early with empty output.
return '';
}
ob_start();
$this->output( $atts['id'], $atts['title'], $atts['description'] );
return ob_get_clean();
}
}