PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.1
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.1
2.12.8 2.12.7 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 All 98 releases
sureforms / inc / frontend-assets.php

frontend-assets.php in SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz 2.12.1, at inc/frontend-assets.php

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