PluginProbe
The Innovative Form Builder – IvyForms / 1.5
The Innovative Form Builder – IvyForms v1.5
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Services / Shortcode / Helpers / ShortcodeHelper.php

ShortcodeHelper.php in The Innovative Form Builder – IvyForms 1.5, at backend/src/Services/Shortcode/Helpers/ShortcodeHelper.php

396 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace IvyForms\Services\Shortcode\Helpers;
4
5 use IvyForms\Factory\Field\FieldFactory;
6 use IvyForms\Factory\Security\SecurityServiceFactory;
7 use IvyForms\Services\Payment\SquareEnvironment;
8 use IvyForms\Services\Settings\SettingsService;
9 use IvyForms\Services\Security\SecurityService;
10 use IvyForms\Services\Translations\FrontendStrings;
11 use IvyForms\Routes\Routes;
12 use IvyForms\Services\Shortcode\InlineStyleHelper;
13
14 // phpcs:disable PSR1.Files.SideEffects
15 if (!defined('ABSPATH')) {
16 exit; // Exit if accessed directly
17 }
18
19 /**
20 * Helper utilities used by shortcode-related services.
21 * Consolidates asset registration and small helpers so that ShortcodeService
22 * can stay focused on rendering and orchestration.
23 */
24 class ShortcodeHelper
25 {
26 /**
27 * Register frontend style handles early so external integrations can safely depend on them.
28 *
29 * @return void
30 */
31 public static function registerFrontendStyleHandles(): void
32 {
33 [, , $styleSrc, $stylePublicSrc, $scriptVersion] = self::resolveAssetPaths();
34
35 if (!wp_style_is('ivyforms_style_index', 'registered')) {
36 wp_register_style('ivyforms_style_index', $styleSrc ?: false, [], $scriptVersion);
37 }
38
39 if (!wp_style_is('ivyforms_style_public', 'registered')) {
40 wp_register_style('ivyforms_style_public', $stylePublicSrc ?: false, [], $scriptVersion);
41 }
42 }
43
44 /**
45 * Build SecurityService instance
46 *
47 * @return SecurityService
48 */
49 public static function getSecurityService(): SecurityService
50 {
51 $settingsService = new SettingsService();
52 $securityFactory = new SecurityServiceFactory($settingsService);
53 return new SecurityService($securityFactory);
54 }
55
56 /**
57 * Convert form list to runtime Field objects expected by the security service
58 *
59 * @param array<string, array<string, mixed>> $formList
60 * @return array<object>
61 */
62 public static function getAllFormFields(array $formList): array
63 {
64 $allFields = [];
65
66 foreach ($formList as $formData) {
67 foreach ($formData['fields'] as $fieldData) {
68 $field = FieldFactory::create($fieldData);
69 $allFields[] = $field;
70 }
71 }
72
73 return $allFields;
74 }
75
76 /**
77 * Enqueue provider script if configured
78 *
79 * @param string $handle
80 * @param array<string, mixed> $providerConfig
81 * @return void
82 */
83 public static function enqueueProviderScript(string $handle, array $providerConfig): void
84 {
85 $scriptUrl = $providerConfig['scriptUrl'] ?? '';
86 if (empty($scriptUrl)) {
87 return;
88 }
89
90 wp_enqueue_script(
91 $handle,
92 $scriptUrl,
93 [],
94 null,
95 true
96 );
97 }
98
99 /**
100 * Enqueue Google reCAPTCHA script if present in captcha configuration
101 *
102 * @param array<string, mixed> $captchaConfig Top-level captcha configuration array (may contain 'recaptcha')
103 * @return void
104 */
105 public static function enqueueGoogleRecaptchaScript(array $captchaConfig): void
106 {
107 if (!isset($captchaConfig['recaptcha'])) {
108 return;
109 }
110
111 self::enqueueProviderScript('google-recaptcha', $captchaConfig['recaptcha']);
112 }
113
114 /**
115 * Enqueue Cloudflare Turnstile script if present in captcha configuration
116 *
117 * @param array<string, mixed> $captchaConfig Top-level captcha configuration array (may contain 'turnstile')
118 * @return void
119 */
120 public static function enqueueTurnstileScript(array $captchaConfig): void
121 {
122 if (!isset($captchaConfig['turnstile'])) {
123 return;
124 }
125
126 self::enqueueProviderScript('cloudflare-turnstile', $captchaConfig['turnstile']);
127 }
128
129 /**
130 * Enqueue hCaptcha script if present in captcha configuration
131 *
132 * @param array<string, mixed> $captchaConfig Top-level captcha configuration array (may contain 'hcaptcha')
133 * @return void
134 */
135 public static function enqueueHCaptchaScript(array $captchaConfig): void
136 {
137 if (!isset($captchaConfig['hcaptcha'])) {
138 return;
139 }
140
141 self::enqueueProviderScript('hcaptcha', $captchaConfig['hcaptcha']);
142 }
143
144 /**
145 * Register frontend assets (script, styles) and localize data for IvyForms shortcode.
146 * Intended to be called from ShortcodeService::enqueueScripts()
147 *
148 * @param array<string, array<string, mixed>> $formList
149 * @return void
150 */
151 public static function registerFrontendAssets(array $formList): void
152 {
153 // Resolve paths and handles
154 [$scriptId, $scriptSrc, $styleSrc, $stylePublicSrc, $scriptVersion] = self::resolveAssetPaths();
155
156 // Ensure style handles are available for dependencies before enqueuing.
157 self::registerFrontendStyleHandles();
158
159 $mainScriptDeps = [];
160 if (self::formHasPaymentField($formList)) {
161 wp_enqueue_script(
162 'square-web-payments-sdk',
163 SquareEnvironment::getWebPaymentsSdkUrl(),
164 [],
165 null,
166 true
167 );
168 $mainScriptDeps[] = 'square-web-payments-sdk';
169 }
170
171 // Enqueue main script and styles
172 self::enqueueMainScript($scriptId, $scriptSrc, $scriptVersion, $mainScriptDeps);
173
174 if ($styleSrc) { // @phpstan-ignore-line
175 self::enqueueStyles($styleSrc, $stylePublicSrc, $scriptVersion);
176 }
177
178 // Enqueue captcha scripts and localize captcha config
179 self::enqueueCaptchaAndLocalize($scriptId, $formList);
180
181 SquareFrontendConfigHelper::localizeConfigIfNeeded($scriptId, $formList);
182
183 // Localize common frontend data (forms, labels, urls, api settings, date formats)
184 self::localizeFormListAndLabels($scriptId, $formList);
185
186 do_action('ivyforms/shortcode/enqueue_scripts', $scriptId);
187 }
188
189 /**
190 * Resolve script/style handles and URLs
191 *
192 * @return array{0:string,1:string,2:string,3:string,4:mixed}
193 */
194 private static function resolveAssetPaths(): array
195 {
196 $scriptId = IVYFORMS_DEV ? 'ivyforms_scripts_dev_vite' : 'ivyforms_script_index'; // @phpstan-ignore-line
197 $scriptSrc = IVYFORMS_DEV ? // @phpstan-ignore-line
198 'http://localhost:5173/src/assets/js/public/public.ts' :
199 IVYFORMS_URL . 'frontend/dist/public.js';
200
201 $styleSrc = IVYFORMS_DEV ? '' : IVYFORMS_URL . 'frontend/dist/index.css'; // @phpstan-ignore-line
202 $stylePublicSrc = IVYFORMS_DEV ? '' : IVYFORMS_URL . 'frontend/dist/public.css'; // @phpstan-ignore-line
203 $scriptVersion = IVYFORMS_DEV ? null : IVYFORMS_VERSION;// @phpstan-ignore-line
204
205 return [$scriptId, $scriptSrc, $styleSrc, $stylePublicSrc, $scriptVersion];
206 }
207
208 /**
209 * Enqueue the main frontend script
210 *
211 * @param array<string> $deps
212 */
213 private static function enqueueMainScript(
214 string $scriptId,
215 string $scriptSrc,
216 ?string $scriptVersion,
217 array $deps = []
218 ): void {
219 wp_enqueue_script(
220 $scriptId,
221 $scriptSrc,
222 $deps,
223 $scriptVersion,
224 true
225 );
226 }
227
228 /**
229 * Enqueue styles and inline css
230 *
231 * @param string $styleSrc
232 * @param string $stylePublicSrc
233 * @param mixed $scriptVersion
234 * @return void
235 */
236 private static function enqueueStyles(string $styleSrc, string $stylePublicSrc, $scriptVersion): void
237 {
238 wp_enqueue_style('ivyforms_style_index', $styleSrc, [], $scriptVersion);
239 wp_enqueue_style('ivyforms_style_public', $stylePublicSrc, [], $scriptVersion);
240
241 // Use helper for inline styles
242 wp_add_inline_style(
243 'ivyforms_style_index',
244 InlineStyleHelper::getFrontendInlineCss()
245 );
246 }
247
248 /**
249 * Enqueue captcha scripts and localize captcha configs for consumers
250 *
251 * @param string $scriptId
252 * @param array<string, array<string, mixed>> $formList
253 * @return void
254 */
255 private static function enqueueCaptchaAndLocalize(string $scriptId, array $formList): void
256 {
257 $securityService = self::getSecurityService();
258 $formFields = self::getAllFormFields($formList);
259
260 $securityConfig = $securityService->getFrontendSecurityConfig($formFields);
261
262 if ($securityConfig['captcha']['enabled'] ?? false) {
263 if (isset($securityConfig['captcha']['recaptcha'])) {
264 self::enqueueGoogleRecaptchaScript($securityConfig['captcha']);
265 }
266 if (isset($securityConfig['captcha']['turnstile'])) {
267 self::enqueueTurnstileScript($securityConfig['captcha']);
268 }
269 if (isset($securityConfig['captcha']['hcaptcha'])) {
270 self::enqueueHCaptchaScript($securityConfig['captcha']);
271 }
272 }
273
274 wp_localize_script(
275 $scriptId,
276 'wpIvyRecaptchaConfig',
277 $securityConfig['captcha'] ?? []
278 );
279
280 wp_localize_script(
281 $scriptId,
282 'wpIvyTurnstileConfig',
283 $securityConfig['captcha'] ?? []
284 );
285
286 wp_localize_script(
287 $scriptId,
288 'wpIvyHCaptchaConfig',
289 $securityConfig['captcha'] ?? []
290 );
291 }
292
293 /**
294 * Localize form list, labels, urls, api settings and date format
295 *
296 * @param string $scriptId
297 * @param array<string, array<string, mixed>> $formList
298 * @return void
299 */
300 /**
301 * @param array<string, array<string, mixed>> $formList
302 */
303 private static function formHasPaymentField(array $formList): bool
304 {
305 foreach ($formList as $formData) {
306 foreach ($formData['fields'] ?? [] as $field) {
307 if (is_array($field) && ($field['type'] ?? '') === 'payment-method') {
308 return true;
309 }
310 }
311 }
312
313 return false;
314 }
315
316 /**
317 * @param array<string, array<string, mixed>> $formList
318 */
319 private static function localizeFormListAndLabels(string $scriptId, array $formList): void
320 {
321 wp_localize_script(
322 $scriptId,
323 'wpIvyFormDataList',
324 $formList
325 );
326
327 $frontendLabels = array_merge(
328 FrontendStrings::getFormRenderStrings()
329 );
330
331 /**
332 * Filter frontend labels before localizing
333 * Allows Pro version to merge additional strings
334 *
335 * @since 0.1.0
336 * @param array $frontendLabels Array of frontend label strings
337 */
338 $frontendLabels = apply_filters('ivyforms/shortcode/frontend_labels', $frontendLabels);
339
340 wp_localize_script($scriptId, 'wpIvyLabels', $frontendLabels);
341
342 wp_localize_script(
343 $scriptId,
344 'wpIvyUrls',
345 [
346 'pluginURL' => IVYFORMS_URL,
347 'siteURL' => esc_url_raw(site_url()),
348 ]
349 );
350
351 wp_localize_script(
352 $scriptId,
353 'wpIvyApiSettings',
354 [
355 'root' => esc_url_raw(rest_url()),
356 'nonce' => wp_create_nonce('wp_rest'),
357 'namespace' => Routes::$routeNamespace,
358 ]
359 );
360
361 wp_localize_script(
362 $scriptId,
363 'wpIvyDateFormat',
364 [
365 'dateFormat' => get_option('date_format'),
366 'timeFormat' => get_option('time_format'),
367 'dateTimeFormat' => get_option('date_format') . ' ' . get_option('time_format'),
368 'locale' => get_locale(),
369 ]
370 );
371 }
372
373 /**
374 * Add script attribute for module type
375 *
376 * @param array<string, mixed> $attributes
377 *
378 * @return array<string, mixed>
379 */
380 public static function addScriptAttribute(array $attributes): array
381 {
382 $scriptArray = [
383 'ivyforms_scripts_dev_main-js',
384 'ivyforms_scripts_dev_vite-js',
385 'ivyforms_script_index-js',
386 'ivyforms_scripts_public-js',
387 ];
388
389 if (isset($attributes['id']) && in_array($attributes['id'], $scriptArray, true)) {
390 $attributes['type'] = 'module';
391 }
392
393 return $attributes;
394 }
395 }
396