dvadf
<?php
/**
* SureForms Public Class.
*
* Class file for public functions.
*
* @package SureForms
*/
namespace SRFM\Inc;
use SRFM\Inc\Traits\Get_Instance;
use SRFM\Inc\Payments\Payment_Helper;
use SRFM\Inc\Payments\Stripe\Stripe_Helper;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Public Class
*
* @since 0.0.1
*/
class Frontend_Assets {
use Get_Instance;
/**
* JS Assets.
*
* @since 0.0.11
* @var array<string>
*/
public static $js_assets = [
'form-submit' => 'formSubmit',
'frontend' => 'frontend',
];
/**
* CSS Assets.
*
* @since 0.0.11
* @var array<string>
*/
public static $css_assets = [
'frontend-default' => 'blocks/default/frontend',
'common' => 'common',
'form' => 'frontend/form',
'single' => 'single',
];
/**
* External CSS Assets.
*
* @since 0.0.11
* @var array<string>
*/
public static $css_external_assets = [
'tom-select' => 'tom-select',
'intl-tel-input' => 'intl/intlTelInput.min',
];
/**
* Constructor
*
* @since 0.0.1
*/
public function __construct() {
add_filter( 'template_include', [ $this, 'page_template' ], PHP_INT_MAX );
add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ] );
add_filter( 'render_block', [ $this, 'generate_render_script' ], 10, 2 );
}
/**
* Enqueue Script.
*
* @return void
* @since 0.0.1
*/
public function register_scripts() {
$file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
$dir_name = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
$js_uri = SRFM_URL . 'assets/js/' . $dir_name . '/';
$css_uri = SRFM_URL . 'assets/css/' . $dir_name . '/';
$css_vendor = SRFM_URL . 'assets/css/minified/deps/';
$is_rtl = is_rtl();
$rtl = $is_rtl ? '-rtl' : '';
$security_setting_options = get_option( 'srfm_security_settings_options' );
$is_set_v2_site_key = false;
if ( is_array( $security_setting_options ) && isset( $security_setting_options['srfm_v2_invisible_site_key'] ) && ! empty( $security_setting_options['srfm_v2_invisible_site_key'] ) ) {
$is_set_v2_site_key = true;
}
// Styles based on meta style.
foreach ( self::$css_assets as $handle => $path ) {
wp_register_style( SRFM_SLUG . '-' . $handle, $css_uri . $path . $file_prefix . $rtl . '.css', [], SRFM_VER );
}
// External styles.
foreach ( self::$css_external_assets as $handle => $path ) {
wp_register_style( SRFM_SLUG . '-' . $handle, $css_vendor . $path . '.css', [], SRFM_VER );
}
// Scripts.
foreach ( self::$js_assets as $handle => $name ) {
if ( 'form-submit' === $handle ) {
wp_register_script(
SRFM_SLUG . '-' . $handle,
SRFM_URL . 'assets/build/' . $name . '.js',
[ 'wp-api-fetch' ],
SRFM_VER,
true
);
} else {
wp_register_script(
SRFM_SLUG . '-' . $handle,
$js_uri . $name . $file_prefix . '.js',
[],
SRFM_VER,
true
);
}
}
wp_localize_script(
SRFM_SLUG . '-form-submit',
SRFM_SLUG . '_submit',
[
'site_url' => site_url(),
'nonce' => wp_create_nonce( 'wp_rest' ),
'messages' => array_merge(
Translatable::get_frontend_validation_messages(),
[
'srfm_turnstile_error_message' => __( 'Turnstile sitekey verification failed. Please contact your site administrator.', 'sureforms' ),
'srfm_google_captcha_error_message' => __( 'Google Captcha sitekey verification failed. Please contact your site administrator.', 'sureforms' ),
'srfm_captcha_h_error_message' => __( 'HCaptcha sitekey verification failed. Please contact your site administrator.', 'sureforms' ),
]
),
'is_rtl' => $is_rtl,
]
);
$current_post = get_post();
// Let's conditionally load form assets if current requested page has our forms.
if ( $current_post instanceof \WP_Post ) {
// Handles condition for Instant Form, Block Embedded, and Shortcode Embedded forms.
$load_assets = ( SRFM_FORMS_POST_TYPE === $current_post->post_type || ( false !== strpos( $current_post->post_content, 'wp:srfm/form' ) || has_shortcode( $current_post->post_content, 'sureforms' ) ) );
if ( $load_assets ) {
// Load needed styles in head tag if current requested page has SureForms form.
self::enqueue_scripts_and_styles();
}
}
}
/**
* Enqueue scripts and styles.
*
* @return void
* @since 0.0.11
*/
public static function enqueue_scripts_and_styles() {
// Load the styles.
foreach ( self::$css_assets as $handle => $path ) {
// Skip single form styles if not on single form page.
if ( 'single' === $handle && ! is_singular( SRFM_FORMS_POST_TYPE ) ) {
continue;
}
wp_enqueue_style( SRFM_SLUG . '-' . $handle );
}
// Load the external styles. Like Phone and Tom Select.
foreach ( self::$css_external_assets as $handle => $path ) {
wp_enqueue_style( SRFM_SLUG . '-' . $handle );
}
// Load the scripts.
foreach ( self::$js_assets as $handle => $path ) {
wp_enqueue_script( SRFM_SLUG . '-' . $handle );
}
}
/**
* Enqueue block scripts
*
* @param string $block_type block name.
* @param array<string, mixed> $attr Array of block attributes.
* @since 0.0.1
* @return void
*/
public function enqueue_srfm_script( $block_type, $attr ) {
$block_name = str_replace( 'srfm/', '', $block_type );
// associative array to keep the count of block that requires scripts to work.
$script_dep_blocks = [
'dropdown' => 0,
'multi-choice' => 0,
'number' => 0,
'textarea' => 0,
'url' => 0,
'phone' => 0,
'input' => 0,
];
$file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
$dir_name = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
// Check if block is in the array and check if block is already enqueued.
if (
in_array( $block_name, array_keys( $script_dep_blocks ), true ) &&
0 === $script_dep_blocks[ $block_name ]
) {
$script_dep_blocks[ $block_name ] += 1;
$js_uri = SRFM_URL . 'assets/js/' . $dir_name . '/blocks/';
$js_vendor_uri = SRFM_URL . 'assets/js/minified/deps/';
$css_vendor_uri = SRFM_URL . 'assets/css/minified/deps/';
if ( 'phone' === $block_name ) {
// Enqueue main intl-tel-input library.
wp_enqueue_script( SRFM_SLUG . "-{$block_name}-intl-input-deps", $js_vendor_uri . 'intl/intTelInputWithUtils.min.js', [], SRFM_VER, true );
// Enqueue i18n translations if available for current locale.
self::enqueue_intl_tel_input_i18n(
SRFM_SLUG . "-{$block_name}-intl-i18n",
SRFM_SLUG . "-{$block_name}-intl-input-deps"
);
}
if ( 'dropdown' === $block_name ) {
// if the dropdown / address-compact block is after any other block, then we need to dequeue the srfm-form-submit script and enqueue it again and load it with tom-select dependency.
wp_dequeue_script( SRFM_SLUG . '-form-submit' );
wp_enqueue_script( SRFM_SLUG . '-dropdown', $js_uri . 'dropdown' . $file_prefix . '.js', [ 'wp-a11y' ], SRFM_VER, true );
wp_enqueue_script( SRFM_SLUG . '-tom-select', $js_vendor_uri . 'tom-select.min.js', [], SRFM_VER, true );
// frontend utils using dropdown dependency.
wp_enqueue_script(
SRFM_SLUG . '-form-submit',
SRFM_URL . 'assets/build/formSubmit.js',
[
'srfm-tom-select',
'srfm-dropdown',
'wp-api-fetch',
],
SRFM_VER,
true
);
}
$is_not_dropdown = 'dropdown' !== $block_name;
$is_not_textarea = 'textarea' !== $block_name;
if ( $is_not_dropdown && $is_not_textarea ) {
// Set dependencies for phone block to ensure intl-tel-input loads first.
$block_dependencies = [];
if ( 'phone' === $block_name ) {
// Phone.js depends on intl-tel-input library (and i18n if loaded).
$block_dependencies = [ SRFM_SLUG . "-{$block_name}-intl-input-deps" ];
}
wp_enqueue_script( SRFM_SLUG . "-{$block_name}", $js_uri . $block_name . $file_prefix . '.js', $block_dependencies, SRFM_VER, true );
}
if ( 'input' === $block_name && isset( $attr['inputMask'] ) && 'none' !== $attr['inputMask'] ) {
// Input mask JS - only load when inputMask is configured.
wp_enqueue_script( SRFM_SLUG . '-inputmask', $js_vendor_uri . 'inputmask.min.js', [], SRFM_VER, true );
}
// Adding js for the input textarea block.
if ( 'textarea' === $block_name && ! empty( $attr['isRichText'] ) ) {
wp_enqueue_script( SRFM_SLUG . '-quill-editor', $js_vendor_uri . '/quill.min.js', [], SRFM_VER, true );
wp_enqueue_style( SRFM_SLUG . '-quill-editor', $css_vendor_uri . 'quill/quill.snow.css', [], SRFM_VER );
wp_enqueue_script( SRFM_SLUG . '-textarea', $js_uri . 'textarea' . $file_prefix . '.js', [], SRFM_VER, true );
}
}
/**
* Enqueueing the input mask JS for input and date-picker blocks.
* This is a workaround for the input mask JS to work with the date-picker block.
* Not adding in the above existing condition because code only runs when free block are added in the form.
* Aim is to reduce redundant code and library file duplication.
*/
if ( 'date-picker' === $block_name ) {
// Input mask JS.
wp_enqueue_script( SRFM_SLUG . '-inputmask', SRFM_URL . 'assets/js/minified/deps/inputmask.min.js', [], SRFM_VER, true );
}
if ( 'payment' === $block_name ) {
// Register Stripe.js library from CDN.
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Stripe CDN ignores version; version param is included to keep linter happy.
wp_enqueue_script(
'stripe-js',
'https://js.stripe.com/v3/',
[],
SRFM_VER,
true
);
wp_enqueue_script(
SRFM_SLUG . '-stripe-payment',
SRFM_URL . 'assets/js/stripe-payment.js',
[ 'stripe-js' ],
SRFM_VER,
true
);
// Enqueue Payment Manager for payment method switching.
wp_enqueue_script(
SRFM_SLUG . '-payment-manager',
SRFM_URL . 'assets/js/payment-manager.js',
[ SRFM_SLUG . '-stripe-payment' ],
SRFM_VER,
true
);
// Localize script for Stripe payment functionality.
wp_localize_script(
SRFM_SLUG . '-stripe-payment',
'srfm_ajax',
[
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'srfm_stripe_payment_nonce' ),
]
);
// Localize Stripe payment data for frontend.
wp_localize_script(
SRFM_SLUG . '-stripe-payment',
'srfmStripe',
[
'zeroDecimalCurrencies' => Payment_Helper::get_zero_decimal_currencies(),
'currenciesData' => Payment_Helper::get_all_currencies_data(),
'strings' => Payment_Helper::get_payment_strings(),
]
);
}
// Trigger custom action hook to allow third-party plugins or add-ons
// to enqueue additional scripts/styles for specific blocks (e.g., payment providers).
do_action(
'srfm_enqueue_block_scripts',
[
'block_name' => $block_name,
'attr' => $attr,
]
);
}
/**
* Maps WordPress locale to intl-tel-input language code.
*
* Converts WordPress locale format (e.g., fr_FR, de_DE, pt_BR)
* to intl-tel-input language codes (e.g., fr, de, pt).
* Returns null if the language is not supported by intl-tel-input.
*
* @since 2.5.0
* @param string|null $locale WordPress locale string. If null, uses get_locale().
* @return string|null Language code if supported, null if not supported or English.
*/
public static function get_intl_tel_input_locale( $locale = null ) {
// Get WordPress locale if not provided.
if ( null === $locale ) {
$locale = get_locale();
}
// Extract language code from WordPress locale (e.g., fr_FR -> fr, pt_BR -> pt).
$lang_code = substr( $locale, 0, 2 );
// List of supported languages - available translation files in /assets/js/minified/deps/intl/i18n/.
// Reference: https://unpkg.com/browse/intl-tel-input@24.5.1/build/js/i18n/.
$supported_languages = [
'de', // German - Deutsch.
'es', // Spanish - Español.
'fr', // French - Français.
'it', // Italian - Italiano.
'nl', // Dutch - Nederlands.
'pl', // Polish - Polski.
'pt', // Portuguese - Português.
];
// Return language code only if supported and not English (English is default).
if ( in_array( $lang_code, $supported_languages, true ) && 'en' !== $lang_code ) {
return $lang_code;
}
// Return null for unsupported languages or English (uses default English).
return null;
}
/**
* Enqueues intl-tel-input i18n script for the phone field.
*
* This method handles conditional loading of language files based on WordPress locale.
* Only enqueues if a supported non-English language is detected and the file exists.
*
* @since 2.5.0
* @param string $handle Script handle to enqueue.
* @param string $dependencies Optional. Script handle that this i18n depends on. Default empty.
* @return bool True if i18n was enqueued, false otherwise.
*/
public static function enqueue_intl_tel_input_i18n( $handle, $dependencies = '' ) {
$intl_locale = self::get_intl_tel_input_locale();
// Return early if no locale or English.
if ( empty( $intl_locale ) ) {
return false;
}
$i18n_file_path = SRFM_DIR . "assets/js/minified/deps/intl/i18n/{$intl_locale}/index.min.js";
// Only enqueue if the language file exists.
if ( ! file_exists( $i18n_file_path ) ) {
return false;
}
$deps = ! empty( $dependencies ) ? [ $dependencies ] : [];
wp_enqueue_script(
$handle,
SRFM_URL . "assets/js/minified/deps/intl/i18n/{$intl_locale}/index.min.js",
$deps,
SRFM_VER,
true
);
return true;
}
/**
* Render function.
*
* @param string $block_content Entire Block Content.
* @param array<string> $block Block Properties As An Array.
* @return string
*/
public function generate_render_script( $block_content, $block ) {
if ( isset( $block['attrs']['isEditing'] ) ) {
// Only load block assets on the frontend.
return $block_content;
}
if ( isset( $block['blockName'] ) ) {
$attr = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : [];
self::enqueue_srfm_script( $block['blockName'], $attr );
}
return $block_content;
}
/**
* Form Template filter.
*
* @param string $template Template.
* @return string Template.
* @since 0.0.1
*/
public function page_template( $template ) {
if ( ! is_singular( SRFM_FORMS_POST_TYPE ) ) {
// Bail if not SureForms post type.
return $template;
}
$file_name = 'single-form.php';
$template = locate_template( $file_name );
/**
* Hook: srfm_form_template filter.
*
* @since 0.0.1
*/
return apply_filters( 'srfm_form_template', $template ? $template : SRFM_DIR . '/templates/' . $file_name );
}
}