PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.7.1
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.7.1
2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 All 96 releases
sureforms / inc / frontend-assets.php
frontend-assets.php
477 lines 14.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * SureForms Public Class.
4 *
5 * Class file for public functions.
6 *
7 * @package SureForms
8 */
9
10 namespace SRFM\Inc;
11
12 use SRFM\Inc\Traits\Get_Instance;
13 use SRFM\Inc\Payments\Payment_Helper;
14 use SRFM\Inc\Payments\Stripe\Stripe_Helper;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // Exit if accessed directly.
18 }
19
20 /**
21 * Public Class
22 *
23 * @since 0.0.1
24 */
25 class Frontend_Assets {
26 use Get_Instance;
27
28 /**
29 * JS Assets.
30 *
31 * @since 0.0.11
32 * @var array<string>
33 */
34 public static $js_assets = [
35 'form-submit' => 'formSubmit',
36 'frontend' => 'frontend',
37 ];
38
39 /**
40 * CSS Assets.
41 *
42 * @since 0.0.11
43 * @var array<string>
44 */
45 public static $css_assets = [
46 'frontend-default' => 'blocks/default/frontend',
47 'common' => 'common',
48 'form' => 'frontend/form',
49 'single' => 'single',
50 ];
51
52 /**
53 * External CSS Assets.
54 *
55 * @since 0.0.11
56 * @var array<string>
57 */
58 public static $css_external_assets = [
59 'tom-select' => 'tom-select',
60 'intl-tel-input' => 'intl/intlTelInput.min',
61 ];
62
63 /**
64 * Constructor
65 *
66 * @since 0.0.1
67 */
68 public function __construct() {
69 add_filter( 'template_include', [ $this, 'page_template' ], PHP_INT_MAX );
70 add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ] );
71 add_filter( 'render_block', [ $this, 'generate_render_script' ], 10, 2 );
72 }
73
74 /**
75 * Enqueue Script.
76 *
77 * @return void
78 * @since 0.0.1
79 */
80 public function register_scripts() {
81 $file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
82 $dir_name = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
83 $js_uri = SRFM_URL . 'assets/js/' . $dir_name . '/';
84 $css_uri = SRFM_URL . 'assets/css/' . $dir_name . '/';
85 $css_vendor = SRFM_URL . 'assets/css/minified/deps/';
86 $is_rtl = is_rtl();
87 $rtl = $is_rtl ? '-rtl' : '';
88
89 $security_setting_options = get_option( 'srfm_security_settings_options' );
90 $is_set_v2_site_key = false;
91 if ( is_array( $security_setting_options ) && isset( $security_setting_options['srfm_v2_invisible_site_key'] ) && ! empty( $security_setting_options['srfm_v2_invisible_site_key'] ) ) {
92 $is_set_v2_site_key = true;
93 }
94
95 // Styles based on meta style.
96 foreach ( self::$css_assets as $handle => $path ) {
97 wp_register_style( SRFM_SLUG . '-' . $handle, $css_uri . $path . $file_prefix . $rtl . '.css', [], SRFM_VER );
98 }
99
100 // External styles.
101 foreach ( self::$css_external_assets as $handle => $path ) {
102 wp_register_style( SRFM_SLUG . '-' . $handle, $css_vendor . $path . '.css', [], SRFM_VER );
103 }
104
105 // Scripts.
106 foreach ( self::$js_assets as $handle => $name ) {
107 if ( 'form-submit' === $handle ) {
108 wp_register_script(
109 SRFM_SLUG . '-' . $handle,
110 SRFM_URL . 'assets/build/' . $name . '.js',
111 [ 'wp-api-fetch' ],
112 SRFM_VER,
113 true
114 );
115 } else {
116 wp_register_script(
117 SRFM_SLUG . '-' . $handle,
118 $js_uri . $name . $file_prefix . '.js',
119 [],
120 SRFM_VER,
121 true
122 );
123 }
124 }
125
126 wp_localize_script(
127 SRFM_SLUG . '-form-submit',
128 SRFM_SLUG . '_submit',
129 [
130 'site_url' => site_url(),
131 'nonce' => wp_create_nonce( 'wp_rest' ),
132 'messages' => array_merge(
133 Translatable::get_frontend_validation_messages(),
134 [
135 'srfm_turnstile_error_message' => __( 'Turnstile sitekey verification failed. Please contact your site administrator.', 'sureforms' ),
136 'srfm_google_captcha_error_message' => __( 'Google Captcha sitekey verification failed. Please contact your site administrator.', 'sureforms' ),
137 'srfm_captcha_h_error_message' => __( 'HCaptcha sitekey verification failed. Please contact your site administrator.', 'sureforms' ),
138 ]
139 ),
140 'is_rtl' => $is_rtl,
141 ]
142 );
143
144 $current_post = get_post();
145
146 // Let's conditionally load form assets if current requested page has our forms.
147 if ( $current_post instanceof \WP_Post ) {
148 // Handles condition for Instant Form, Block Embedded, and Shortcode Embedded forms.
149 $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' ) ) );
150
151 if ( $load_assets ) {
152 // Load needed styles in head tag if current requested page has SureForms form.
153 self::enqueue_scripts_and_styles();
154 }
155 }
156 }
157
158 /**
159 * Enqueue scripts and styles.
160 *
161 * @return void
162 * @since 0.0.11
163 */
164 public static function enqueue_scripts_and_styles() {
165 // Load the styles.
166 foreach ( self::$css_assets as $handle => $path ) {
167
168 // Skip single form styles if not on single form page.
169 if ( 'single' === $handle && ! is_singular( SRFM_FORMS_POST_TYPE ) ) {
170 continue;
171 }
172
173 wp_enqueue_style( SRFM_SLUG . '-' . $handle );
174 }
175
176 // Load the external styles. Like Phone and Tom Select.
177 foreach ( self::$css_external_assets as $handle => $path ) {
178 wp_enqueue_style( SRFM_SLUG . '-' . $handle );
179 }
180
181 // Load the scripts.
182 foreach ( self::$js_assets as $handle => $path ) {
183 wp_enqueue_script( SRFM_SLUG . '-' . $handle );
184 }
185 }
186
187 /**
188 * Enqueue block scripts
189 *
190 * @param string $block_type block name.
191 * @param array<string, mixed> $attr Array of block attributes.
192 * @since 0.0.1
193 * @return void
194 */
195 public function enqueue_srfm_script( $block_type, $attr ) {
196 $block_name = str_replace( 'srfm/', '', $block_type );
197 // associative array to keep the count of block that requires scripts to work.
198 $script_dep_blocks = [
199 'dropdown' => 0,
200 'multi-choice' => 0,
201 'number' => 0,
202 'textarea' => 0,
203 'url' => 0,
204 'phone' => 0,
205 'input' => 0,
206 ];
207
208 $file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
209 $dir_name = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
210
211 // Check if block is in the array and check if block is already enqueued.
212 if (
213 in_array( $block_name, array_keys( $script_dep_blocks ), true ) &&
214 0 === $script_dep_blocks[ $block_name ]
215 ) {
216 $script_dep_blocks[ $block_name ] += 1;
217 $js_uri = SRFM_URL . 'assets/js/' . $dir_name . '/blocks/';
218 $js_vendor_uri = SRFM_URL . 'assets/js/minified/deps/';
219 $css_vendor_uri = SRFM_URL . 'assets/css/minified/deps/';
220 if ( 'phone' === $block_name ) {
221 // Enqueue main intl-tel-input library.
222 wp_enqueue_script( SRFM_SLUG . "-{$block_name}-intl-input-deps", $js_vendor_uri . 'intl/intTelInputWithUtils.min.js', [], SRFM_VER, true );
223
224 // Enqueue i18n translations if available for current locale.
225 self::enqueue_intl_tel_input_i18n(
226 SRFM_SLUG . "-{$block_name}-intl-i18n",
227 SRFM_SLUG . "-{$block_name}-intl-input-deps"
228 );
229 }
230
231 if ( 'dropdown' === $block_name ) {
232 // 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.
233 wp_dequeue_script( SRFM_SLUG . '-form-submit' );
234 wp_enqueue_script( SRFM_SLUG . '-dropdown', $js_uri . 'dropdown' . $file_prefix . '.js', [ 'wp-a11y' ], SRFM_VER, true );
235 wp_enqueue_script( SRFM_SLUG . '-tom-select', $js_vendor_uri . 'tom-select.min.js', [], SRFM_VER, true );
236 // frontend utils using dropdown dependency.
237 wp_enqueue_script(
238 SRFM_SLUG . '-form-submit',
239 SRFM_URL . 'assets/build/formSubmit.js',
240 [
241 'srfm-tom-select',
242 'srfm-dropdown',
243 'wp-api-fetch',
244 ],
245 SRFM_VER,
246 true
247 );
248 }
249
250 $is_not_dropdown = 'dropdown' !== $block_name;
251 $is_not_textarea = 'textarea' !== $block_name;
252
253 if ( $is_not_dropdown && $is_not_textarea ) {
254 // Set dependencies for phone block to ensure intl-tel-input loads first.
255 $block_dependencies = [];
256 if ( 'phone' === $block_name ) {
257 // Phone.js depends on intl-tel-input library (and i18n if loaded).
258 $block_dependencies = [ SRFM_SLUG . "-{$block_name}-intl-input-deps" ];
259 }
260 wp_enqueue_script( SRFM_SLUG . "-{$block_name}", $js_uri . $block_name . $file_prefix . '.js', $block_dependencies, SRFM_VER, true );
261 }
262
263 if ( 'input' === $block_name && isset( $attr['inputMask'] ) && 'none' !== $attr['inputMask'] ) {
264 // Input mask JS - only load when inputMask is configured.
265 wp_enqueue_script( SRFM_SLUG . '-inputmask', $js_vendor_uri . 'inputmask.min.js', [], SRFM_VER, true );
266 }
267
268 // Adding js for the input textarea block.
269 if ( 'textarea' === $block_name && ! empty( $attr['isRichText'] ) ) {
270 wp_enqueue_script( SRFM_SLUG . '-quill-editor', $js_vendor_uri . '/quill.min.js', [], SRFM_VER, true );
271
272 wp_enqueue_style( SRFM_SLUG . '-quill-editor', $css_vendor_uri . 'quill/quill.snow.css', [], SRFM_VER );
273
274 wp_enqueue_script( SRFM_SLUG . '-textarea', $js_uri . 'textarea' . $file_prefix . '.js', [], SRFM_VER, true );
275 }
276 }
277 /**
278 * Enqueueing the input mask JS for input and date-picker blocks.
279 * This is a workaround for the input mask JS to work with the date-picker block.
280 * Not adding in the above existing condition because code only runs when free block are added in the form.
281 * Aim is to reduce redundant code and library file duplication.
282 */
283 if ( 'date-picker' === $block_name ) {
284 // Input mask JS.
285 wp_enqueue_script( SRFM_SLUG . '-inputmask', SRFM_URL . 'assets/js/minified/deps/inputmask.min.js', [], SRFM_VER, true );
286 }
287
288 if ( 'payment' === $block_name ) {
289 // Register Stripe.js library from CDN.
290 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Stripe CDN ignores version; version param is included to keep linter happy.
291 wp_enqueue_script(
292 'stripe-js',
293 'https://js.stripe.com/v3/',
294 [],
295 SRFM_VER,
296 true
297 );
298
299 wp_enqueue_script(
300 SRFM_SLUG . '-stripe-payment',
301 SRFM_URL . 'assets/js/stripe-payment.js',
302 [ 'stripe-js' ],
303 SRFM_VER,
304 true
305 );
306
307 // Enqueue Payment Manager for payment method switching.
308 wp_enqueue_script(
309 SRFM_SLUG . '-payment-manager',
310 SRFM_URL . 'assets/js/payment-manager.js',
311 [ SRFM_SLUG . '-stripe-payment' ],
312 SRFM_VER,
313 true
314 );
315
316 // Localize script for Stripe payment functionality.
317 wp_localize_script(
318 SRFM_SLUG . '-stripe-payment',
319 'srfm_ajax',
320 [
321 'ajax_url' => admin_url( 'admin-ajax.php' ),
322 ]
323 );
324
325 // Localize Stripe payment data for frontend.
326 wp_localize_script(
327 SRFM_SLUG . '-stripe-payment',
328 'srfmStripe',
329 [
330 'zeroDecimalCurrencies' => Payment_Helper::get_zero_decimal_currencies(),
331 'currenciesData' => Payment_Helper::get_all_currencies_data(),
332 'strings' => Payment_Helper::get_payment_strings(),
333 'currencySignPosition' => Payment_Helper::get_currency_sign_position(),
334 ]
335 );
336 }
337
338 // Trigger custom action hook to allow third-party plugins or add-ons
339 // to enqueue additional scripts/styles for specific blocks (e.g., payment providers).
340 do_action(
341 'srfm_enqueue_block_scripts',
342 [
343 'block_name' => $block_name,
344 'attr' => $attr,
345 ]
346 );
347 }
348
349 /**
350 * Maps WordPress locale to intl-tel-input language code.
351 *
352 * Converts WordPress locale format (e.g., fr_FR, de_DE, pt_BR)
353 * to intl-tel-input language codes (e.g., fr, de, pt).
354 * Returns null if the language is not supported by intl-tel-input.
355 *
356 * @since 2.5.0
357 * @param string|null $locale WordPress locale string. If null, uses get_locale().
358 * @return string|null Language code if supported, null if not supported or English.
359 */
360 public static function get_intl_tel_input_locale( $locale = null ) {
361 // Get WordPress locale if not provided.
362 if ( null === $locale ) {
363 $locale = get_locale();
364 }
365
366 // Extract language code from WordPress locale (e.g., fr_FR -> fr, pt_BR -> pt).
367 $lang_code = substr( $locale, 0, 2 );
368
369 // List of supported languages - available translation files in /assets/js/minified/deps/intl/i18n/.
370 // Reference: https://unpkg.com/browse/intl-tel-input@24.5.1/build/js/i18n/.
371 $supported_languages = [
372 'de', // German - Deutsch.
373 'es', // Spanish - Español.
374 'fr', // French - Français.
375 'it', // Italian - Italiano.
376 'nl', // Dutch - Nederlands.
377 'pl', // Polish - Polski.
378 'pt', // Portuguese - Português.
379 ];
380
381 // Return language code only if supported and not English (English is default).
382 if ( in_array( $lang_code, $supported_languages, true ) && 'en' !== $lang_code ) {
383 return $lang_code;
384 }
385
386 // Return null for unsupported languages or English (uses default English).
387 return null;
388 }
389
390 /**
391 * Enqueues intl-tel-input i18n script for the phone field.
392 *
393 * This method handles conditional loading of language files based on WordPress locale.
394 * Only enqueues if a supported non-English language is detected and the file exists.
395 *
396 * @since 2.5.0
397 * @param string $handle Script handle to enqueue.
398 * @param string $dependencies Optional. Script handle that this i18n depends on. Default empty.
399 * @return bool True if i18n was enqueued, false otherwise.
400 */
401 public static function enqueue_intl_tel_input_i18n( $handle, $dependencies = '' ) {
402 $intl_locale = self::get_intl_tel_input_locale();
403
404 // Return early if no locale or English.
405 if ( empty( $intl_locale ) ) {
406 return false;
407 }
408
409 $i18n_file_path = SRFM_DIR . "assets/js/minified/deps/intl/i18n/{$intl_locale}/index.min.js";
410
411 // Only enqueue if the language file exists.
412 if ( ! file_exists( $i18n_file_path ) ) {
413 return false;
414 }
415
416 $deps = ! empty( $dependencies ) ? [ $dependencies ] : [];
417
418 wp_enqueue_script(
419 $handle,
420 SRFM_URL . "assets/js/minified/deps/intl/i18n/{$intl_locale}/index.min.js",
421 $deps,
422 SRFM_VER,
423 true
424 );
425
426 return true;
427 }
428
429 /**
430 * Render function.
431 *
432 * @param string $block_content Entire Block Content.
433 * @param array<string> $block Block Properties As An Array.
434 * @return string
435 */
436 public function generate_render_script( $block_content, $block ) {
437
438 if ( isset( $block['attrs']['isEditing'] ) ) {
439 // Only load block assets on the frontend.
440 return $block_content;
441 }
442
443 if ( isset( $block['blockName'] ) ) {
444 $attr = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : [];
445
446 self::enqueue_srfm_script( $block['blockName'], $attr );
447 }
448 return $block_content;
449 }
450
451 /**
452 * Form Template filter.
453 *
454 * @param string $template Template.
455 * @return string Template.
456 * @since 0.0.1
457 */
458 public function page_template( $template ) {
459
460 if ( ! is_singular( SRFM_FORMS_POST_TYPE ) ) {
461 // Bail if not SureForms post type.
462 return $template;
463 }
464
465 $file_name = 'single-form.php';
466 $template = locate_template( $file_name );
467
468 /**
469 * Hook: srfm_form_template filter.
470 *
471 * @since 0.0.1
472 */
473 return apply_filters( 'srfm_form_template', $template ? $template : SRFM_DIR . '/templates/' . $file_name );
474 }
475
476 }
477