nly on singular pages if WPForms shortcode or editor block is present. * However, if a form is added as a sidebar widget, in a template or somewhere else outside the Loop, * we will discover that too late for assets to be included in the header. * In this case, we will include all required assets in the footer instead. * This may lead to a brief FOUC (Flash Of Unstyled Content). * * Returning `true` from this filter on a particular page that matches your criteria is useful * if you need to load assets in the header on archive pages or any other pages that you know have a form. * It may be as a sidebar widget, dynamically inserted on form preview page, on category pages, etc. * * @since 1.8.1 * * @param bool $force_load Force loading assets in the header, default `false`. */ $force_load_css = (bool) apply_filters( 'wpforms_frontend_assets_header_force_load', false ); if ( $force_load_css ) { $this->assets_css(); return; } if ( ! is_singular() ) { return; } global $post; if ( has_shortcode( $post->post_content, 'wpforms' ) || ( function_exists( 'has_block' ) && has_block( 'wpforms/form-selector' ) ) ) { $this->assets_css(); } } /** * Load the CSS assets for frontend output. * * @since 1.0.0 */ public function assets_css() { /** * Fires before enqueueing frontend CSS. * * @since 1.0.0 * * @param array $forms Array of forms on the page. */ do_action( 'wpforms_frontend_css', $this->forms ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName $min = wpforms_get_min_suffix(); $disable_css = (int) wpforms_setting( 'disable-css', '1' ); if ( $disable_css === 3 ) { wp_enqueue_style( 'wpforms-no-styles', WPFORMS_PLUGIN_URL . "assets/css/frontend/wpforms-no-styles{$min}.css", [], WPFORMS_VERSION ); return; } $style_name = $disable_css === 1 ? 'full' : 'base'; $handle = "wpforms-{$this->render_engine}-{$style_name}"; wp_enqueue_style( $handle, WPFORMS_PLUGIN_URL . "assets/css/frontend/{$this->render_engine}/wpforms-{$style_name}{$min}.css", [], WPFORMS_VERSION ); // Add CSS variables for the Modern Markup mode for full styles. if ( empty( $this->css_vars_obj ) || $this->render_engine !== 'modern' || $style_name !== 'full' ) { return; } wp_add_inline_style( $handle, $this->css_vars_obj->get_root_vars_css() ); } /** * Load the JS assets for frontend output. * * @since 1.0.0 */ public function assets_js() { if ( $this->amp_obj->is_amp() ) { return; } /** * Fire before frontend JS assets are loaded. * * @since 1.0.0 * * @param array $forms Forms on the current page. */ do_action( 'wpforms_frontend_js', $this->forms ); $min = wpforms_get_min_suffix(); $in_footer = $this->load_script_in_footer(); // Load the jQuery validation library - https://jqueryvalidation.org/. wp_enqueue_script( 'wpforms-validation', WPFORMS_PLUGIN_URL . 'assets/lib/jquery.validate.min.js', [ 'jquery' ], '1.21.0', $in_footer ); // Load jQuery input mask library - https://github.com/RobinHerbots/jquery.inputmask. if ( $this->assets_global() || wpforms_has_field_type( [ 'phone', 'address' ], $this->forms, true ) || wpforms_has_field_setting( 'input_mask', $this->forms, true ) ) { wp_enqueue_script( 'wpforms-maskedinput', WPFORMS_PLUGIN_URL . 'assets/lib/jquery.inputmask.min.js', [ 'jquery' ], '5.0.9', $in_footer ); } // Load mailcheck and punycode libraries. if ( $this->assets_global() || wpforms_has_field_type( [ 'email' ], $this->forms, true ) ) { wp_enqueue_script( 'wpforms-mailcheck', WPFORMS_PLUGIN_URL . 'assets/lib/mailcheck.min.js', [], '1.1.2', $in_footer ); wp_enqueue_script( 'wpforms-punycode', WPFORMS_PLUGIN_URL . 'assets/lib/punycode.min.js', [], '1.0.0', $in_footer ); } wp_enqueue_script( 'wpforms-generic-utils', WPFORMS_PLUGIN_URL . "assets/js/share/utils{$min}.js", [ 'jquery' ], WPFORMS_VERSION, $in_footer ); // Load base JS. wp_enqueue_script( 'wpforms', WPFORMS_PLUGIN_URL . "assets/js/frontend/wpforms{$min}.js", [ 'jquery' ], WPFORMS_VERSION, $in_footer ); // Load JS additions needed in the Modern Markup mode. if ( $this->render_engine === 'modern' ) { wp_enqueue_script( 'wpforms-modern', WPFORMS_PLUGIN_URL . "assets/js/frontend/wpforms-modern{$min}.js", [ 'wpforms' ], WPFORMS_VERSION, $in_footer ); } } /** * Cloudflare Turnstile captcha requires defer attribute. * * @since 1.8.0 * * @param string $tag HTML for the script tag. * @param string $handle Handle of a script. * @param string $src Src of a script. * * @return string * @noinspection PhpMissingParamTypeInspection * @noinspection PhpUnusedParameterInspection */ public function set_defer_attribute( $tag, $handle, $src ): string { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed $captcha_settings = wpforms_get_captcha_settings(); if ( $captcha_settings['provider'] !== 'turnstile' ) { return $tag; } if ( $handle !== 'wpforms-recaptcha' ) { return $tag; } return str_replace( ' src', ' defer src', $tag ); } /** * Load the necessary assets for the confirmation message. * * @since 1.1.2 * @since 1.7.9 Added $form_data argument. * * @param array $form_data Form data and settings. */ public function assets_confirmation( $form_data = [] ): void { $form_data = (array) $form_data; $min = wpforms_get_min_suffix(); $in_footer = $this->load_script_in_footer(); // Base CSS only. if ( (int) wpforms_setting( 'disable-css', '1' ) === 1 ) { wp_enqueue_style( 'wpforms-full', WPFORMS_PLUGIN_URL . "assets/css/frontend/{$this->render_engine}/wpforms-full{$min}.css", [], WPFORMS_VERSION ); } // Special confirmation JS. if ( ! $this->amp_obj->is_amp() ) { wp_enqueue_script( 'wpforms-confirmation', WPFORMS_PLUGIN_URL . "assets/js/frontend/wpforms-confirmation{$min}.js", [ 'jquery' ], WPFORMS_VERSION, $in_footer ); } /** * Fires after enqueueing assets on the confirmation page have been enqueued. * * @since 1.1.2 * @since 1.7.9 Added $form_data argument. * * @param array $form_data Form data and settings. */ do_action( 'wpforms_frontend_confirmation', $form_data ); } /** * Load the assets in the footer if needed (archives, widgets, etc.). * * @since 1.0.0 */ public function assets_footer(): void { if ( empty( $this->forms ) && ! $this->assets_global() ) { return; } $this->assets_css(); $this->assets_js(); /** * Fires after enqueueing footer assets. * * @since 1.0.0 * * @param array $forms Forms being shown. */ do_action( 'wpforms_wp_footer', $this->forms ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName } /** * Get strings to localize. * * @since 1.6.0 * * @return array Array of strings to localize. */ public function get_strings(): array { // Define base strings. $strings = [ 'val_required' => wpforms_setting( 'validation-required', esc_html__( 'This field is required.', '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', sprintf( /* translators: %s - suggested email address. */ esc_html__( 'Did you mean %s?', 'wpforms-lite' ), '{suggestion}' ) ), 'val_email_suggestion_title' => esc_attr__( 'Click to accept this suggestion.', 'wpforms-lite' ), 'val_email_restricted' => wpforms_setting( 'validation-email-restricted', esc_html__( 'This email address is not allowed.', 'wpforms-lite' ) ), 'val_number' => wpforms_setting( 'validation-number', esc_html__( 'Please enter a valid number.', 'wpforms-lite' ) ), 'val_number_positive' => wpforms_setting( 'validation-number-positive', esc_html__( 'Please enter a valid positive number.', 'wpforms-lite' ) ), 'val_minimum_price' => wpforms_setting( 'validation-minimum-price', esc_html__( 'Amount entered is less than the required minimum.', 'wpforms-lite' ) ), 'val_confirm' => wpforms_setting( 'validation-confirm', esc_html__( 'Field values do not match.', 'wpforms-lite' ) ), 'val_checklimit' => wpforms_setting( 'validation-check-limit', esc_html__( 'You have exceeded the number of allowed selections: {#}.', 'wpforms-lite' ) ), 'val_limit_characters' => wpforms_setting( 'validation-character-limit', sprintf( /* translators: %1$s - character count, %2$s - character limit. */ esc_html__( '%1$s of %2$s max characters.', 'wpforms-lite' ), '{count}', '{limit}' ) ), 'val_limit_words' => wpforms_setting( 'validation-word-limit', sprintf( /* translators: %1$s - word count, %2$s - word limit. */ esc_html__( '%1$s of %2$s max words.', 'wpforms-lite' ), '{count}', '{limit}' ) ), 'val_min' => wpforms_setting( 'validation-min', esc_html__( 'Please enter a value greater than or equal to {0}.', 'wpforms-lite' ) ), 'val_max' => wpforms_setting( 'validation-max', esc_html__( 'Please enter a value less than or equal to {0}.', 'wpforms-lite' ) ), 'val_recaptcha_fail_msg' => wpforms_setting( 'recaptcha-fail-msg', esc_html__( 'Google reCAPTCHA verification failed, please try again later.', 'wpforms-lite' ) ), 'val_turnstile_fail_msg' => wpforms_setting( 'turnstile-fail-msg', esc_html__( 'Cloudflare Turnstile verification failed, please try again later.', 'wpforms-lite' ) ), 'val_inputmask_incomplete' => wpforms_setting( 'validation-inputmask-incomplete', esc_html__( 'Please fill out the field in required format.', 'wpforms-lite' ) ), 'uuid_cookie' => false, 'locale' => wpforms_get_language_code(), /** * Filters the user's country code. * * Leave empty for most cases, it will be auto-detected. * If set, it will make country recognition in wpforms.js frontend skipped. * Allows testing Phone Smart field with different countries. * * @since 1.9.0 * * @param string|false $country Country code. */ 'country' => apply_filters( 'wpforms_frontend_get_user_country_code', false ), 'country_list_label' => esc_html__( 'Country list', 'wpforms-lite' ), 'wpforms_plugin_url' => WPFORMS_PLUGIN_URL, 'gdpr' => wpforms_setting( 'gdpr' ), 'ajaxurl' => admin_url( 'admin-ajax.php' ), /** * Filters mail check enabled flag. * * @since 1.5.4.2 * * @param bool $flag Enabled flag. */ 'mailcheck_enabled' => (bool) apply_filters( 'wpforms_mailcheck_enabled', true ), // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName /** * Filters mail check domains. * * @since 1.5.4.2 * * @param array $domains Domains to check. */ 'mailcheck_domains' => array_map( 'sanitize_text_field', (array) apply_filters( 'wpforms_mailcheck_domains', [] ) ), // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName /** * Filters toplevel domains for mail check. * * @since 1.5.4.2 * * @param array $toplevel_domains Toplevel domains to check. */ 'mailcheck_toplevel_domains' => array_map( 'sanitize_text_field', (array) apply_filters( 'wpforms_mailcheck_toplevel_domains', [ 'dev' ] ) ), // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName 'is_ssl' => is_ssl(), ]; // Include payment-related strings if needed. $strings = $this->get_payment_strings( $strings ); // Include CSS variables list. $strings = $this->get_css_vars_strings( $strings ); /** * Filters frontend strings. * * @since 1.3.7.3 * * @param array $strings Frontend strings. */ $strings = (array) apply_filters( 'wpforms_frontend_strings', $strings ); foreach ( $strings as $key => $value ) { if ( ! is_scalar( $value ) ) { continue; } $strings[ $key ] = esc_html( html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' ) ); } return $strings; } /** * Get payment strings. * * @since 1.8.1 * * @param array $strings Strings. * * @return array */ private function get_payment_strings( array $strings ): array { if ( function_exists( 'wpforms_get_currencies' ) ) { $currency = wpforms_get_currency(); $currencies = wpforms_get_currencies(); $strings['currency_code'] = $currency; $strings['currency_thousands'] = $currencies[ $currency ]['thousands_separator'] ?? ','; $strings['currency_decimals'] = wpforms_get_currency_decimals( $currencies[ $currency ] ); $strings['currency_decimal'] = $currencies[ $currency ]['decimal_separator'] ?? '.'; $strings['currency_symbol'] = $currencies[ $currency ]['symbol'] ?? '$'; $strings['currency_symbol_pos'] = $currencies[ $currency ]['symbol_pos'] ?? 'left'; } $strings['val_requiredpayment'] = wpforms_setting( 'validation-requiredpayment', esc_html__( 'Payment is required.', 'wpforms-lite' ) ); $strings['val_creditcard'] = wpforms_setting( 'validation-creditcard', esc_html__( 'Please enter a valid credit card number.', 'wpforms-lite' ) ); return $strings; } /** * Get CSS variables data. * * @since 1.8.1 * * @param array $strings Strings. * * @return array */ private function get_css_vars_strings( array $strings ): array { if ( wpforms_get_render_engine() !== 'modern' ) { return $strings; } if ( empty( $this->css_vars_obj ) ) { return $strings; } $strings['css_vars'] = array_keys( $this->css_vars_obj->get_vars() ); return $strings; } /** * Hook at fires at a later priority in wp_footer. * * @since 1.0.5 * @since 1.7.0 Load wpforms_settings on the confirmation page for a non-ajax form. */ public function footer_end(): void { if ( ( empty( $this->forms ) && empty( $_POST['wpforms'] ) && ! $this->assets_global() ) || // phpcs:ignore WordPress.Security.NonceVerification.Missing $this->amp_obj->is_amp() ) { return; } $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 * conflicts. */ echo "\n"; /** * Fires after the end of the footer. * * @since 1.0.6 * * @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. * * @since 1.0.0 * * @param array|mixed $atts Shortcode attributes provided by a user. * * @return string */ public function shortcode( $atts ): string { $atts = (array) $atts; $defaults = [ 'id' => false, 'title' => false, 'description' => false, ]; $atts = shortcode_atts( $defaults, shortcode_atts( $defaults, $atts, 'output' ), 'wpforms' ); ob_start(); $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. * * @since 1.6.4.1 */ public function missing_assets_error_js(): void { /** * Disable missing assets error js checking. * * @since 1.6.6 * * @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() ) { return; } if ( empty( $this->forms ) && ! $this->assets_global() ) { return; } if ( $this->amp_obj->is_amp() ) { return; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped printf( $this->get_missing_assets_error_script(), $this->get_missing_assets_error_message() ); } /** * Get missing assets error script. * * @since 1.6.4.1 * * @return string */ private function get_missing_assets_error_script(): string { return ""; } /** * Get a missing assets error message. * * @since 1.6.4.1 * * @return string * @noinspection HtmlUnknownTarget */ private function get_missing_assets_error_message(): string { $message = sprintf( 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 troubleshooting guide to learn more or contact support.', 'wpforms-lite' ), [ 'a' => [ 'href' => [], 'target' => [], 'rel' => [], ], ] ), 'https://wpforms.com/docs/getting-support-wpforms/' ); $message .= '

'; $message .= esc_html__( 'This message is only displayed to site administrators.', 'wpforms-lite' ); $message .= '

'; return $message; } /** * Render the single field. * * @since 1.7.7 * * @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']}" ) ) { return; } /** * Modify Field before render. * * @since 1.4.0 * * @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 if ( empty( $field ) ) { return; } $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: * Priority / Description * 5 Field opening container markup. * 15 Field label. * 20 Field description (depending on position). * * @since 1.3.7 * * @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 * field form elements. * See `field_display` methods in /includes/fields. * * @since 1.3.7 * * @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: * Priority / Description * 3 Field error messages. * 5 Field description (depending on position). * 15 Field closing container markup. * 20 Pagebreak markups (close previous page, open next). * * @since 1.3.7 * * @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. * * @since 1.9.0 * * @return bool */ protected function load_script_in_footer(): bool { return ! wpforms_is_frontend_js_header_force_load(); } } Amcrest – Faithx