PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.8.2
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.8.2
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
496 lines 15.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\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 wp_localize_script(
277 SRFM_SLUG . '-textarea',
278 'srfm_quill_i18n',
279 [
280 'normal' => _x( 'Normal', 'Quill heading picker: default paragraph style', 'sureforms' ),
281 'heading_1' => __( 'Heading 1', 'sureforms' ),
282 'heading_2' => __( 'Heading 2', 'sureforms' ),
283 'heading_3' => __( 'Heading 3', 'sureforms' ),
284 'heading_4' => __( 'Heading 4', 'sureforms' ),
285 'heading_5' => __( 'Heading 5', 'sureforms' ),
286 'heading_6' => __( 'Heading 6', 'sureforms' ),
287 'visit_url' => _x( 'Visit URL:', 'Quill link tooltip label', 'sureforms' ),
288 'enter_link' => _x( 'Enter link:', 'Quill link tooltip label', 'sureforms' ),
289 'edit' => _x( 'Edit', 'Quill link tooltip action', 'sureforms' ),
290 'save' => _x( 'Save', 'Quill link tooltip action', 'sureforms' ),
291 'remove' => _x( 'Remove', 'Quill link tooltip action', 'sureforms' ),
292 ]
293 );
294 }
295 }
296 /**
297 * Enqueueing the input mask JS for input and date-picker blocks.
298 * This is a workaround for the input mask JS to work with the date-picker block.
299 * Not adding in the above existing condition because code only runs when free block are added in the form.
300 * Aim is to reduce redundant code and library file duplication.
301 */
302 if ( 'date-picker' === $block_name ) {
303 // Input mask JS.
304 wp_enqueue_script( SRFM_SLUG . '-inputmask', SRFM_URL . 'assets/js/minified/deps/inputmask.min.js', [], SRFM_VER, true );
305 }
306
307 if ( 'payment' === $block_name ) {
308 // Register Stripe.js library from CDN.
309 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Stripe CDN ignores version; version param is included to keep linter happy.
310 wp_enqueue_script(
311 'stripe-js',
312 'https://js.stripe.com/v3/',
313 [],
314 SRFM_VER,
315 true
316 );
317
318 wp_enqueue_script(
319 SRFM_SLUG . '-stripe-payment',
320 SRFM_URL . 'assets/js/stripe-payment.js',
321 [ 'stripe-js' ],
322 SRFM_VER,
323 true
324 );
325
326 // Enqueue Payment Manager for payment method switching.
327 wp_enqueue_script(
328 SRFM_SLUG . '-payment-manager',
329 SRFM_URL . 'assets/js/payment-manager.js',
330 [ SRFM_SLUG . '-stripe-payment' ],
331 SRFM_VER,
332 true
333 );
334
335 // Localize script for Stripe payment functionality.
336 wp_localize_script(
337 SRFM_SLUG . '-stripe-payment',
338 'srfm_ajax',
339 [
340 'ajax_url' => admin_url( 'admin-ajax.php' ),
341 ]
342 );
343
344 // Localize Stripe payment data for frontend.
345 wp_localize_script(
346 SRFM_SLUG . '-stripe-payment',
347 'srfmStripe',
348 [
349 'zeroDecimalCurrencies' => Payment_Helper::get_zero_decimal_currencies(),
350 'currenciesData' => Payment_Helper::get_all_currencies_data(),
351 'strings' => Payment_Helper::get_payment_strings(),
352 'currencySignPosition' => Payment_Helper::get_currency_sign_position(),
353 ]
354 );
355 }
356
357 // Trigger custom action hook to allow third-party plugins or add-ons
358 // to enqueue additional scripts/styles for specific blocks (e.g., payment providers).
359 do_action(
360 'srfm_enqueue_block_scripts',
361 [
362 'block_name' => $block_name,
363 'attr' => $attr,
364 ]
365 );
366 }
367
368 /**
369 * Maps WordPress locale to intl-tel-input language code.
370 *
371 * Converts WordPress locale format (e.g., fr_FR, de_DE, pt_BR)
372 * to intl-tel-input language codes (e.g., fr, de, pt).
373 * Returns null if the language is not supported by intl-tel-input.
374 *
375 * @since 2.5.0
376 * @param string|null $locale WordPress locale string. If null, uses get_locale().
377 * @return string|null Language code if supported, null if not supported or English.
378 */
379 public static function get_intl_tel_input_locale( $locale = null ) {
380 // Get WordPress locale if not provided.
381 if ( null === $locale ) {
382 $locale = get_locale();
383 }
384
385 // Extract language code from WordPress locale (e.g., fr_FR -> fr, pt_BR -> pt).
386 $lang_code = substr( $locale, 0, 2 );
387
388 // List of supported languages - available translation files in /assets/js/minified/deps/intl/i18n/.
389 // Reference: https://unpkg.com/browse/intl-tel-input@24.5.1/build/js/i18n/.
390 $supported_languages = [
391 'de', // German - Deutsch.
392 'es', // Spanish - Español.
393 'fr', // French - Français.
394 'it', // Italian - Italiano.
395 'nl', // Dutch - Nederlands.
396 'pl', // Polish - Polski.
397 'pt', // Portuguese - Português.
398 ];
399
400 // Return language code only if supported and not English (English is default).
401 if ( in_array( $lang_code, $supported_languages, true ) && 'en' !== $lang_code ) {
402 return $lang_code;
403 }
404
405 // Return null for unsupported languages or English (uses default English).
406 return null;
407 }
408
409 /**
410 * Enqueues intl-tel-input i18n script for the phone field.
411 *
412 * This method handles conditional loading of language files based on WordPress locale.
413 * Only enqueues if a supported non-English language is detected and the file exists.
414 *
415 * @since 2.5.0
416 * @param string $handle Script handle to enqueue.
417 * @param string $dependencies Optional. Script handle that this i18n depends on. Default empty.
418 * @return bool True if i18n was enqueued, false otherwise.
419 */
420 public static function enqueue_intl_tel_input_i18n( $handle, $dependencies = '' ) {
421 $intl_locale = self::get_intl_tel_input_locale();
422
423 // Return early if no locale or English.
424 if ( empty( $intl_locale ) ) {
425 return false;
426 }
427
428 $i18n_file_path = SRFM_DIR . "assets/js/minified/deps/intl/i18n/{$intl_locale}/index.min.js";
429
430 // Only enqueue if the language file exists.
431 if ( ! file_exists( $i18n_file_path ) ) {
432 return false;
433 }
434
435 $deps = ! empty( $dependencies ) ? [ $dependencies ] : [];
436
437 wp_enqueue_script(
438 $handle,
439 SRFM_URL . "assets/js/minified/deps/intl/i18n/{$intl_locale}/index.min.js",
440 $deps,
441 SRFM_VER,
442 true
443 );
444
445 return true;
446 }
447
448 /**
449 * Render function.
450 *
451 * @param string $block_content Entire Block Content.
452 * @param array<string> $block Block Properties As An Array.
453 * @return string
454 */
455 public function generate_render_script( $block_content, $block ) {
456
457 if ( isset( $block['attrs']['isEditing'] ) ) {
458 // Only load block assets on the frontend.
459 return $block_content;
460 }
461
462 if ( isset( $block['blockName'] ) ) {
463 $attr = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : [];
464
465 self::enqueue_srfm_script( $block['blockName'], $attr );
466 }
467 return $block_content;
468 }
469
470 /**
471 * Form Template filter.
472 *
473 * @param string $template Template.
474 * @return string Template.
475 * @since 0.0.1
476 */
477 public function page_template( $template ) {
478
479 if ( ! is_singular( SRFM_FORMS_POST_TYPE ) ) {
480 // Bail if not SureForms post type.
481 return $template;
482 }
483
484 $file_name = 'single-form.php';
485 $template = locate_template( $file_name );
486
487 /**
488 * Hook: srfm_form_template filter.
489 *
490 * @since 0.0.1
491 */
492 return apply_filters( 'srfm_form_template', $template ? $template : SRFM_DIR . '/templates/' . $file_name );
493 }
494
495 }
496