$strings['css_vars'] = array_keys( $this->css_vars_obj->get_vars() );
* Hook at fires at a later priority in wp_footer.
* @since 1.7.0 Load wpforms_settings on the confirmation page for a non-ajax form.
public function footer_end(): void {
( empty( $this->forms ) && empty( $_POST['wpforms'] ) && ! $this->assets_global() ) || // phpcs:ignore WordPress.Security.NonceVerification.Missing
$strings = $this->get_strings();
* Below we do our own implementation of wp_localize_script in an effort
* to be better compatible with caching plugins which were causing
echo "<script type='text/javascript'>\n";
echo "/* <![CDATA[ */\n";
echo 'var wpforms_settings = ' . wp_json_encode( $strings ) . "\n";
* Fires after the end of the footer.
* @param array $forms Forms being shown.
do_action( 'wpforms_wp_footer_end', $this->forms ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
* Shortcode wrapper for the outputting a form.
* @param array|mixed $atts Shortcode attributes provided by a user.
public function shortcode( $atts ): string {
$atts = shortcode_atts( $defaults, shortcode_atts( $defaults, $atts, 'output' ), 'wpforms' );
$this->css_vars_obj->output_css_vars_for_shortcode( $atts );
$this->output( $atts['id'], $atts['title'], $atts['description'] );
return (string) ob_get_clean();
* Inline a script to check if our main js is loaded and display a warning message otherwise.
public function missing_assets_error_js(): void {
* Disable missing assets error js checking.
* @param bool $skip False by default, set to True to disable checking.
$skip = (bool) apply_filters( 'wpforms_frontend_missing_assets_error_js_disable', false );
if ( $skip || ! wpforms_current_user_can() ) {
if ( empty( $this->forms ) && ! $this->assets_global() ) {
if ( $this->amp_obj->is_amp() ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
printf( $this->get_missing_assets_error_script(), $this->get_missing_assets_error_message() );
* Get missing assets error script.
private function get_missing_assets_error_script(): string {
function wpforms_js_error_loading() {
if ( typeof window.wpforms !== 'undefined' ) {
const forms = document.querySelectorAll( '.wpforms-form' );
const error = document.createElement( 'div' );
error.classList.add( 'wpforms-error-container' );
error.setAttribute( 'role', 'alert' );
forms.forEach( function( form ) {
if ( form.querySelector( '.wpforms-error-container' ) ) {
const formError = error.cloneNode( true ),
formErrorId = form.id + '-error';
formError.setAttribute( 'id', formErrorId );
form.insertBefore( formError, form.firstChild );
form.setAttribute( 'aria-invalid', 'true' );
form.setAttribute( 'aria-errormessage', formErrorId );
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', wpforms_js_error_loading );
wpforms_js_error_loading();
* Get a missing assets error message.
* @noinspection HtmlUnknownTarget
private function get_missing_assets_error_message(): string {
wp_kses( /* translators: %s - URL to the troubleshooting guide. */
__( 'Heads up! WPForms has detected an issue with JavaScript on this page. JavaScript is required for this form to work properly, so this form may not work as expected. See our <a href="%s" target="_blank" rel="noopener noreferrer">troubleshooting guide</a> to learn more or contact support.', 'wpforms-lite' ),
'https://wpforms.com/docs/getting-support-wpforms/'
$message .= esc_html__( 'This message is only displayed to site administrators.', 'wpforms-lite' );
* Render the single field.
* @param array $form_data Form data.
* @param array $field Field data.
public function render_field( array $form_data, array $field ): void {
if ( ! has_action( "wpforms_display_field_{$field['type']}" ) ) {
* Modify Field before render.
* @param array $field Current field.
* @param array $form_data Form data and settings.
$field = (array) apply_filters( 'wpforms_field_data', $field, $form_data ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
$this->rendered_fields[] = $field['id'];
// 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:
* 5 Field opening container markup.
* 20 Field description (depending on position).
* @param array $field Field.
* @param array $form_data Form data.
do_action( 'wpforms_display_field_before', $field, $form_data ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
* Individual field classes use this hook to display the actual
* See `field_display` methods in /includes/fields.
* @param array $field Field.
* @param array $attributes Field attributes.
* @param array $form_data Form data.
do_action( "wpforms_display_field_{$field['type']}", $field, $attributes, $form_data ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
* Core actions on this hook:
* 3 Field error messages.
* 5 Field description (depending on position).
* 15 Field closing container markup.
* 20 Pagebreak markups (close previous page, open next).
* @param array $field Field.
* @param array $form_data Form data.
do_action( 'wpforms_display_field_after', $field, $form_data ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
* Whether to print the script in the footer.
protected function load_script_in_footer(): bool {
return ! wpforms_is_frontend_js_header_force_load();