← All changes
|
backend/src/Services/Shortcode/ShortcodeService.php
+123
-260
0.4
→
1.5.1
View file →
| @@ -1,27 +1,19 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | /** |
| 4 | - * @copyright © TMS-Plugins. All rights reserved. | |
| 4 | + * @copyright © Melograno Venture Studio. All rights reserved. | |
| 5 | 5 | * @licence See LICENCE.md for license details. |
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | namespace IvyForms\Services\Shortcode; |
| 9 | 9 | |
| 10 | -// phpcs:disable PSR1.Files.SideEffects | |
| 11 | -if (!defined('ABSPATH')) { | |
| 12 | - exit; // Exit if accessed directly | |
| 13 | -} | |
| 14 | - | |
| 15 | 10 | use IvyForms\Common\Exceptions\ValidationException; |
| 16 | -use IvyForms\Factory\Field\FieldFactory; | |
| 17 | -use IvyForms\Routes\Routes; | |
| 11 | +use IvyForms\Common\Helpers\FieldHelper; | |
| 18 | 12 | use IvyForms\Services\API\IvyFormsAPI; |
| 19 | -use IvyForms\Services\Settings\SettingsService; | |
| 20 | -use IvyForms\Services\Translations\FrontendStrings; | |
| 21 | -use IvyForms\Factory\Security\SecurityServiceFactory; | |
| 22 | -use IvyForms\Services\Security\SecurityService; | |
| 23 | -use IvyForms\Repository\FieldOptions\FieldOptionsRepository; | |
| 13 | +use IvyForms\Services\Form\Style\FormStyleService; | |
| 14 | +use IvyForms\Services\Placeholder\PlaceholderLocalizationHelper; | |
| 15 | +use IvyForms\Services\Shortcode\Helpers\ShortcodeHelper; | |
| 24 | 16 | |
| 25 | 17 | /** |
| 26 | 18 | * Class ShortcodeService |
| 27 | 19 | * |
| @@ -30,9 +22,11 @@ | ||
| 30 | 22 | class ShortcodeService |
| 31 | 23 | { |
| 32 | 24 | public static int $counter = 0; |
| 33 | 25 | /** |
| 34 | - * @var mixed[] | |
| 26 | + * Array keyed by unique render counters (string) -> payload array | |
| 27 | + * | |
| 28 | + * @var array<string, array<string, mixed>> | |
| 35 | 29 | */ |
| 36 | 30 | public static array $formList = []; |
| 37 | 31 | |
| 38 | 32 | /** |
| @@ -40,14 +34,17 @@ | ||
| 40 | 34 | * |
| 41 | 35 | * @param mixed[] $atts |
| 42 | 36 | * |
| 43 | 37 | * @return string |
| 38 | + * @throws ValidationException | |
| 44 | 39 | */ |
| 45 | 40 | public static function shortcodeHandler(array $atts): string |
| 46 | 41 | { |
| 47 | 42 | $atts = shortcode_atts( |
| 48 | 43 | [ |
| 49 | - 'id' => '0' | |
| 44 | + 'id' => '0', | |
| 45 | + 'show_title' => null, | |
| 46 | + 'show_description' => null, | |
| 50 | 47 | ], |
| 51 | 48 | $atts |
| 52 | 49 | ); |
| 53 | 50 | |
| @@ -62,15 +59,38 @@ | ||
| 62 | 59 | if (is_wp_error($form) || !($form->isPublished())) { |
| 63 | 60 | return ''; |
| 64 | 61 | } |
| 65 | 62 | |
| 66 | - $counter = self::$counter++; | |
| 63 | + // Generate a stable unique identifier for this render | |
| 64 | + $counter = $formId . '_' . uniqid(); | |
| 67 | 65 | |
| 68 | - self::getFormList($formId, $counter); | |
| 66 | + // Build overrides array for show_title and show_description | |
| 67 | + $overrides = []; | |
| 68 | + if ($atts['show_title'] !== null) { | |
| 69 | + // Convert string 'true'/'false' or '1'/'0' to boolean | |
| 70 | + $overrides['showTitle'] = filter_var($atts['show_title'], FILTER_VALIDATE_BOOLEAN); | |
| 71 | + } | |
| 72 | + if ($atts['show_description'] !== null) { | |
| 73 | + $overrides['showDescription'] = filter_var($atts['show_description'], FILTER_VALIDATE_BOOLEAN); | |
| 74 | + } | |
| 69 | 75 | |
| 76 | + self::getFormList($formId, $counter, $overrides); | |
| 77 | + | |
| 70 | 78 | self::enqueueScripts(); |
| 71 | 79 | |
| 80 | + // Generate form-specific CSS | |
| 81 | + $formCSS = self::generateFormCSS($formId); | |
| 82 | + | |
| 72 | 83 | ob_start(); |
| 84 | + | |
| 85 | + // Output CSS inline before the form | |
| 86 | + if (!empty($formCSS)) { | |
| 87 | + $styleId = 'ivyforms-form-styles-' . sanitize_key( | |
| 88 | + str_replace('.', '-', $counter) | |
| 89 | + ); | |
| 90 | + echo '<style id="' . esc_attr($styleId) . '">' . "\n" . $formCSS . "\n" . '</style>' . "\n"; | |
| 91 | + } | |
| 92 | + | |
| 73 | 93 | include IVYFORMS_PATH . '/view/frontend/view.php'; |
| 74 | 94 | $html = ob_get_contents(); |
| 75 | 95 | ob_end_clean(); |
| 76 | 96 | |
| @@ -80,13 +100,14 @@ | ||
| 80 | 100 | /** |
| 81 | 101 | * Get form list |
| 82 | 102 | * |
| 83 | 103 | * @param int $formId |
| 84 | - * @param int $counter | |
| 104 | + * @param string $counter Unique counter identifier for this render | |
| 105 | + * @param array<string, mixed> $overrides Optional overrides for form settings (e.g., showTitle, showDescription) | |
| 85 | 106 | * |
| 86 | 107 | * @return void |
| 87 | 108 | */ |
| 88 | - public static function getFormList(int $formId, int $counter) | |
| 109 | + public static function getFormList(int $formId, string $counter, array $overrides = []) | |
| 89 | 110 | { |
| 90 | 111 | $form = IvyFormsAPI::getForm($formId); |
| 91 | 112 | |
| 92 | 113 | $fields = IvyFormsAPI::getFields($form->getId()); |
| @@ -106,8 +127,16 @@ | ||
| 106 | 127 | $form->setFields($fieldsMap); |
| 107 | 128 | |
| 108 | 129 | $formArr = $form->toArray(); |
| 109 | 130 | |
| 131 | + // Apply overrides for showTitle and showDescription (from Gutenberg block or shortcode) | |
| 132 | + if (array_key_exists('showTitle', $overrides)) { | |
| 133 | + $formArr['showTitle'] = $overrides['showTitle']; | |
| 134 | + } | |
| 135 | + if (array_key_exists('showDescription', $overrides)) { | |
| 136 | + $formArr['showDescription'] = $overrides['showDescription']; | |
| 137 | + } | |
| 138 | + | |
| 110 | 139 | self::$formList[$counter] = [ |
| 111 | 140 | 'form' => $formArr, |
| 112 | 141 | 'fields' => $fieldsMap, |
| 113 | 142 | 'id' => $form->getId(), |
| @@ -124,12 +153,17 @@ | ||
| 124 | 153 | * @return array<int, array<string, mixed>> |
| 125 | 154 | */ |
| 126 | 155 | private static function prepareFieldsMap(array $fields): array |
| 127 | 156 | { |
| 157 | + $generalData = PlaceholderLocalizationHelper::getGeneralData(); | |
| 128 | 158 | $fieldsMap = []; |
| 129 | 159 | foreach ($fields as $field) { |
| 130 | 160 | $fieldArr = $field->toArray(); |
| 131 | - if (isset($fieldArr['type']) && $fieldArr['type'] === 'radio') { | |
| 161 | + $fieldType = isset($fieldArr['type']) ? (string) $fieldArr['type'] : ''; | |
| 162 | + if ( | |
| 163 | + $fieldType !== '' | |
| 164 | + && (FieldHelper::fieldTypeHasOptions($fieldType) || $fieldType === 'rating') | |
| 165 | + ) { | |
| 132 | 166 | $fieldOptions = IvyFormsAPI::getFieldOptions($fieldArr['id']); |
| 133 | 167 | $fieldArr['fieldOptions'] = []; |
| 134 | 168 | foreach ($fieldOptions as $option) { |
| 135 | 169 | if (is_object($option) && method_exists($option, 'toArray')) { |
| @@ -136,216 +170,94 @@ | ||
| 136 | 170 | $fieldArr['fieldOptions'][] = $option->toArray(); |
| 137 | 171 | } |
| 138 | 172 | } |
| 139 | 173 | } |
| 140 | - $fieldsMap[] = $fieldArr; | |
| 174 | + $fieldsMap[] = self::resolveFieldPlaceholders($fieldArr, $generalData); | |
| 141 | 175 | } |
| 142 | 176 | return $fieldsMap; |
| 143 | 177 | } |
| 144 | 178 | |
| 145 | 179 | /** |
| 146 | - * Enqueue handler | |
| 180 | + * @param array<string, mixed> $fieldArr | |
| 181 | + * @param array<string, string|int> $generalData | |
| 182 | + * @return array<string, mixed> | |
| 147 | 183 | */ |
| 148 | - public static function enqueueScripts(): void | |
| 184 | + private static function resolveFieldPlaceholders(array $fieldArr, array $generalData): array | |
| 149 | 185 | { |
| 150 | - $scriptId = IVYFORMS_DEV ? 'ivyforms_scripts_dev_vite' : 'ivyforms_script_index'; // @phpstan-ignore-line | |
| 151 | - $scriptSrc = IVYFORMS_DEV ? // @phpstan-ignore-line | |
| 152 | - 'http://localhost:5173/src/assets/js/public/public.ts' : | |
| 153 | - IVYFORMS_URL . 'frontend/dist/public.js'; | |
| 154 | - | |
| 155 | - $styleSrc = IVYFORMS_DEV ? '' : IVYFORMS_URL . 'frontend/dist/index.css'; // @phpstan-ignore-line | |
| 156 | - $stylePublicSrc = IVYFORMS_DEV ? '' : IVYFORMS_URL . 'frontend/dist/public.css'; // @phpstan-ignore-line | |
| 157 | - $scriptVersion = IVYFORMS_DEV ? null : IVYFORMS_VERSION;// @phpstan-ignore-line | |
| 158 | - | |
| 159 | - wp_enqueue_script( | |
| 160 | - $scriptId, | |
| 161 | - $scriptSrc, | |
| 162 | - [], | |
| 163 | - $scriptVersion, | |
| 164 | - true | |
| 165 | - ); | |
| 166 | - | |
| 167 | - // TODO: Improve this with merging styles | |
| 168 | - if ($styleSrc) { // @phpstan-ignore-line | |
| 169 | - wp_enqueue_style( | |
| 170 | - 'ivyforms_style_index', | |
| 171 | - $styleSrc, | |
| 172 | - [], | |
| 173 | - $scriptVersion | |
| 174 | - ); | |
| 175 | - wp_enqueue_style( | |
| 176 | - 'ivyforms_style_public', | |
| 177 | - $stylePublicSrc, | |
| 178 | - [], | |
| 179 | - $scriptVersion | |
| 180 | - ); | |
| 181 | - | |
| 182 | - // TODO Create a helper service for adding styles | |
| 183 | - wp_add_inline_style( | |
| 184 | - 'ivyforms_style_index', | |
| 185 | - /** @lang CSS */ | |
| 186 | - '.ivyforms-skeleton { | |
| 187 | - width: 100%; | |
| 188 | - max-width: 720px; | |
| 189 | - margin: 0 auto; | |
| 186 | + foreach (['placeholder', 'defaultValue'] as $key) { | |
| 187 | + if (!isset($fieldArr[$key]) || !is_string($fieldArr[$key])) { | |
| 188 | + continue; | |
| 190 | 189 | } |
| 191 | - | |
| 192 | - .ivyforms-skeleton-title { | |
| 193 | - height: 28px; | |
| 194 | - width: 40%; | |
| 195 | - background: #dbdee3; | |
| 196 | - margin-bottom: 12px; | |
| 197 | - } | |
| 198 | - | |
| 199 | - .ivyforms-skeleton-field { | |
| 200 | - margin-bottom: 16px; | |
| 201 | - } | |
| 202 | - | |
| 203 | - .ivyforms-skeleton-label { | |
| 204 | - height: 22px; | |
| 205 | - width: 20%; | |
| 206 | - background: #dbdee3; | |
| 207 | - font-size: 14px; | |
| 208 | - border-radius: 4px; | |
| 209 | - margin-bottom: 4px; | |
| 210 | - } | |
| 211 | - | |
| 212 | - .ivyforms-skeleton-input { | |
| 213 | - line-height: 22px; | |
| 214 | - font-size: 16px; | |
| 215 | - background: #dbdee3; | |
| 216 | - border-radius: 8px; | |
| 217 | - width: 100%; | |
| 218 | - } | |
| 219 | - | |
| 220 | - .ivyforms-skeleton-email, | |
| 221 | - .ivyforms-skeleton-textarea, | |
| 222 | - .ivyforms-skeleton-text, | |
| 223 | - .ivyforms-skeleton-number, | |
| 224 | - .ivyforms-skeleton-phone { | |
| 225 | - height: 40px; | |
| 226 | - } | |
| 227 | - | |
| 228 | - .ivyforms-skeleton-textarea { | |
| 229 | - height: 60px; | |
| 230 | - } | |
| 231 | - | |
| 232 | - .ivyforms-skeleton-button { | |
| 233 | - height: 40px; | |
| 234 | - width: 100px; | |
| 235 | - background: #c0c5cb; | |
| 236 | - border-radius: 6px; | |
| 237 | - margin-top: 12px; | |
| 238 | - } | |
| 239 | - | |
| 240 | - .ivyforms-skeleton-animate { | |
| 241 | - animation: pulse 1.5s infinite ease-in-out; | |
| 242 | - } | |
| 243 | - | |
| 244 | - @keyframes pulse { | |
| 245 | - 0%, 100% { | |
| 246 | - opacity: 1; | |
| 247 | - } | |
| 248 | - 50% { | |
| 249 | - opacity: 0.4; | |
| 250 | - } | |
| 251 | - } | |
| 252 | - | |
| 253 | - .ivyforms-preview { | |
| 254 | - width: 100%; | |
| 255 | - max-width: 720px; | |
| 256 | - margin: 0 auto; | |
| 257 | - background: #fff; | |
| 258 | - padding: 40px; | |
| 259 | - } | |
| 260 | - | |
| 261 | - .ivyforms-preview h1 { | |
| 262 | - font-size: 50px !important; | |
| 263 | - font-weight: normal !important; | |
| 264 | - margin-top: 0 !important; | |
| 265 | - margin-left: 0 !important; | |
| 266 | - margin-bottom: 20px !important; | |
| 267 | - padding: 0 !important; | |
| 268 | - }' | |
| 190 | + $fieldArr[$key] = PlaceholderLocalizationHelper::resolveFieldString( | |
| 191 | + $fieldArr[$key], | |
| 192 | + $generalData | |
| 269 | 193 | ); |
| 270 | 194 | } |
| 271 | - self::enqueueRecaptchaScript($scriptId); | |
| 272 | 195 | |
| 273 | - wp_localize_script( | |
| 274 | - $scriptId, | |
| 275 | - 'wpIvyFormDataList', | |
| 276 | - self::$formList | |
| 277 | - ); | |
| 196 | + return $fieldArr; | |
| 197 | + } | |
| 278 | 198 | |
| 279 | - wp_localize_script( | |
| 280 | - $scriptId, | |
| 281 | - 'wpIvyLabels', | |
| 282 | - array_merge( | |
| 283 | - FrontendStrings::getFormRenderStrings(), | |
| 284 | - ) | |
| 285 | - ); | |
| 286 | - | |
| 287 | - wp_localize_script( | |
| 288 | - $scriptId, | |
| 289 | - 'wpIvyApiSettings', | |
| 290 | - [ | |
| 291 | - 'root' => esc_url_raw(rest_url()), | |
| 292 | - 'nonce' => wp_create_nonce('wp_rest'), | |
| 293 | - 'namespace' => Routes::$routeNamespace, | |
| 294 | - ] | |
| 295 | - ); | |
| 296 | - wp_localize_script( | |
| 297 | - $scriptId, | |
| 298 | - 'wpIvyDateFormat', | |
| 299 | - [ | |
| 300 | - 'dateFormat' => get_option('date_format'), | |
| 301 | - 'timeFormat' => get_option('time_format'), | |
| 302 | - 'dateTimeFormat' => get_option('date_format') . ' ' . get_option('time_format'), | |
| 303 | - 'locale' => get_locale(), | |
| 304 | - ] | |
| 305 | - ); | |
| 199 | + /** | |
| 200 | + * Enqueue handler | |
| 201 | + */ | |
| 202 | + public static function enqueueScripts(): void | |
| 203 | + { | |
| 204 | + ShortcodeHelper::registerFrontendAssets(self::$formList); | |
| 306 | 205 | } |
| 307 | 206 | |
| 308 | 207 | /** |
| 309 | - * Add script attribute for module type | |
| 208 | + * Generate CSS for a specific form | |
| 310 | 209 | * |
| 311 | - * @param array<string, mixed> $attributes | |
| 312 | - * | |
| 313 | - * @return array<string, mixed> | |
| 210 | + * @param int $formId | |
| 211 | + * @return string | |
| 314 | 212 | */ |
| 315 | - public static function addScriptAttribute(array $attributes): array | |
| 213 | + private static function generateFormCSS(int $formId): string | |
| 316 | 214 | { |
| 317 | - $scriptArray = [ | |
| 318 | - 'ivyforms_scripts_dev_main-js', | |
| 319 | - 'ivyforms_scripts_dev_vite-js', | |
| 320 | - 'ivyforms_script_index-js', | |
| 321 | - 'ivyforms_scripts_public-js', | |
| 322 | - ]; | |
| 215 | + foreach (self::$formList as $formData) { | |
| 216 | + if ($formData['id'] === $formId) { | |
| 217 | + $formPayload = $formData['form'] ?? null; | |
| 218 | + if (!is_array($formPayload)) { | |
| 219 | + return ''; | |
| 220 | + } | |
| 323 | 221 | |
| 324 | - if (isset($attributes['id']) && in_array($attributes['id'], $scriptArray, true)) { | |
| 325 | - $attributes['type'] = 'module'; | |
| 222 | + $styleSettings = $formPayload['styleSettings'] ?? null; | |
| 223 | + | |
| 224 | + if (!empty($styleSettings) && is_array($styleSettings)) { | |
| 225 | + return FormStyleService::generateFormCSS( | |
| 226 | + $formId, | |
| 227 | + $styleSettings, | |
| 228 | + isset($formPayload['formType']) ? (string)$formPayload['formType'] : null | |
| 229 | + ); | |
| 230 | + } | |
| 231 | + } | |
| 326 | 232 | } |
| 327 | 233 | |
| 328 | - return $attributes; | |
| 234 | + return ''; | |
| 329 | 235 | } |
| 330 | 236 | |
| 331 | 237 | /** |
| 332 | - * Enqueue reCAPTCHA script if configured | |
| 238 | + * Enqueue CAPTCHA scripts (reCAPTCHA, Turnstile, etc.) if configured | |
| 333 | 239 | * |
| 334 | 240 | * @param string $scriptId |
| 335 | - * @throws ValidationException | |
| 336 | 241 | */ |
| 337 | 242 | public static function enqueueRecaptchaScript(string $scriptId): void |
| 338 | 243 | { |
| 339 | - $securityService = self::getSecurityService(); | |
| 340 | - $formFields = self::getAllFormFields(); | |
| 244 | + // Delegate captcha discovery/enqueue to ShortcodeHelper while keeping BC wrapper here | |
| 245 | + $securityService = ShortcodeHelper::getSecurityService(); | |
| 246 | + $formFields = ShortcodeHelper::getAllFormFields(self::$formList); | |
| 341 | 247 | |
| 342 | - // Get security configuration using our enhanced service | |
| 343 | 248 | $securityConfig = $securityService->getFrontendSecurityConfig($formFields); |
| 344 | 249 | |
| 345 | - // Enqueue Google script if CAPTCHA is configured and has fields | |
| 346 | - if (self::shouldEnqueueCaptchaScript($securityConfig)) { | |
| 347 | - self::enqueueGoogleRecaptchaScript($securityConfig['captcha']); | |
| 250 | + if ($securityConfig['captcha']['enabled'] ?? false) { | |
| 251 | + if (isset($securityConfig['captcha']['recaptcha'])) { | |
| 252 | + ShortcodeHelper::enqueueGoogleRecaptchaScript($securityConfig['captcha']); | |
| 253 | + } | |
| 254 | + if (isset($securityConfig['captcha']['turnstile'])) { | |
| 255 | + ShortcodeHelper::enqueueTurnstileScript($securityConfig['captcha']); | |
| 256 | + } | |
| 257 | + if (isset($securityConfig['captcha']['hcaptcha'])) { | |
| 258 | + ShortcodeHelper::enqueueHCaptchaScript($securityConfig['captcha']); | |
| 259 | + } | |
| 348 | 260 | } |
| 349 | 261 | |
| 350 | 262 | wp_localize_script( |
| 351 | 263 | $scriptId, |
| @@ -351,78 +263,29 @@ | ||
| 351 | 263 | $scriptId, |
| 352 | 264 | 'wpIvyRecaptchaConfig', |
| 353 | 265 | $securityConfig['captcha'] ?? [] |
| 354 | 266 | ); |
| 355 | - } | |
| 356 | 267 | |
| 357 | - /** | |
| 358 | - * Get security service instance | |
| 359 | - * | |
| 360 | - * @return SecurityService | |
| 361 | - */ | |
| 362 | - private static function getSecurityService(): SecurityService | |
| 363 | - { | |
| 364 | - $settingsService = new SettingsService(); | |
| 365 | - $securityFactory = new SecurityServiceFactory($settingsService); | |
| 366 | - return new SecurityService($securityFactory); | |
| 367 | - } | |
| 268 | + wp_localize_script( | |
| 269 | + $scriptId, | |
| 270 | + 'wpIvyTurnstileConfig', | |
| 271 | + $securityConfig['captcha'] ?? [] | |
| 272 | + ); | |
| 368 | 273 | |
| 369 | - /** | |
| 370 | - * Get all form fields from currently loaded forms | |
| 371 | - * | |
| 372 | - * @return array<object> Array of form field objects | |
| 373 | - * @throws ValidationException | |
| 374 | - */ | |
| 375 | - private static function getAllFormFields(): array | |
| 376 | - { | |
| 377 | - $allFields = []; | |
| 378 | - | |
| 379 | - foreach (self::$formList as $formData) { | |
| 380 | - foreach ($formData['fields'] as $fieldData) { | |
| 381 | - // Create a simple object to match the expected interface | |
| 382 | - $field = FieldFactory::create($fieldData); | |
| 383 | - | |
| 384 | - $allFields[] = $field; | |
| 385 | - } | |
| 386 | - } | |
| 387 | - | |
| 388 | - return $allFields; | |
| 274 | + wp_localize_script( | |
| 275 | + $scriptId, | |
| 276 | + 'wpIvyHCaptchaConfig', | |
| 277 | + $securityConfig['captcha'] ?? [] | |
| 278 | + ); | |
| 389 | 279 | } |
| 390 | 280 | |
| 391 | 281 | /** |
| 392 | - * Check if CAPTCHA script should be enqueued | |
| 282 | + * Backwards-compatible wrapper for script attribute filter | |
| 393 | 283 | * |
| 394 | - * @param array<string, mixed> $securityConfig | |
| 395 | - * @return bool | |
| 284 | + * @param array<string, mixed> $attributes | |
| 285 | + * @return array<string, mixed> | |
| 396 | 286 | */ |
| 397 | - private static function shouldEnqueueCaptchaScript(array $securityConfig): bool | |
| 287 | + public static function addScriptAttribute(array $attributes): array | |
| 398 | 288 | { |
| 399 | - return isset($securityConfig['captcha']['recaptcha']) && $securityConfig['captcha']['enabled']; | |
| 400 | - } | |
| 401 | - | |
| 402 | - /** | |
| 403 | - * Enqueue Google reCAPTCHA script using security service | |
| 404 | - * | |
| 405 | - * @param array<string, mixed> $captchaConfig | |
| 406 | - */ | |
| 407 | - private static function enqueueGoogleRecaptchaScript(array $captchaConfig): void | |
| 408 | - { | |
| 409 | - if (!isset($captchaConfig['recaptcha'])) { | |
| 410 | - return; | |
| 411 | - } | |
| 412 | - | |
| 413 | - $recaptchaConfig = $captchaConfig['recaptcha']; | |
| 414 | - $scriptUrl = $recaptchaConfig['scriptUrl'] ?? ''; | |
| 415 | - | |
| 416 | - if (empty($scriptUrl)) { | |
| 417 | - return; | |
| 418 | - } | |
| 419 | - | |
| 420 | - wp_enqueue_script( | |
| 421 | - 'google-recaptcha', | |
| 422 | - $scriptUrl, | |
| 423 | - [], | |
| 424 | - null, // No version for external script | |
| 425 | - true // Load in footer | |
| 426 | - ); | |
| 289 | + return ShortcodeHelper::addScriptAttribute($attributes); | |
| 427 | 290 | } |
| 428 | 291 | } |