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
← All changes | backend/src/Services/Shortcode/ShortcodeService.php +72 -239 0.9 → 1.5 View file →
@@ -6,22 +6,14 @@
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 *
@@ -84,9 +76,21 @@
84 76 self::getFormList($formId, $counter, $overrides);
85 77
86 78 self::enqueueScripts();
87 79
80 + // Generate form-specific CSS
81 + $formCSS = self::generateFormCSS($formId);
82 +
88 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 +
89 93 include IVYFORMS_PATH . '/view/frontend/view.php';
90 94 $html = ob_get_contents();
91 95 ob_end_clean();
92 96
@@ -149,17 +153,16 @@
149 153 * @return array<int, array<string, mixed>>
150 154 */
151 155 private static function prepareFieldsMap(array $fields): array
152 156 {
157 + $generalData = PlaceholderLocalizationHelper::getGeneralData();
153 158 $fieldsMap = [];
154 159 foreach ($fields as $field) {
155 160 $fieldArr = $field->toArray();
161 + $fieldType = isset($fieldArr['type']) ? (string) $fieldArr['type'] : '';
156 162 if (
157 - isset($fieldArr['type']) && in_array(
158 - $fieldArr['type'],
159 - ['radio', 'checkbox', 'select', 'multi-select', 'rating'],
160 - true
161 - )
163 + $fieldType !== ''
164 + && (FieldHelper::fieldTypeHasOptions($fieldType) || $fieldType === 'rating')
162 165 ) {
163 166 $fieldOptions = IvyFormsAPI::getFieldOptions($fieldArr['id']);
164 167 $fieldArr['fieldOptions'] = [];
165 168 foreach ($fieldOptions as $option) {
@@ -167,130 +170,69 @@
167 170 $fieldArr['fieldOptions'][] = $option->toArray();
168 171 }
169 172 }
170 173 }
171 - $fieldsMap[] = $fieldArr;
174 + $fieldsMap[] = self::resolveFieldPlaceholders($fieldArr, $generalData);
172 175 }
173 176 return $fieldsMap;
174 177 }
175 178
176 179 /**
177 - * Enqueue handler
178 - * @throws ValidationException
180 + * @param array<string, mixed> $fieldArr
181 + * @param array<string, string|int> $generalData
182 + * @return array<string, mixed>
179 183 */
180 - public static function enqueueScripts(): void
184 + private static function resolveFieldPlaceholders(array $fieldArr, array $generalData): array
181 185 {
182 - $scriptId = IVYFORMS_DEV ? 'ivyforms_scripts_dev_vite' : 'ivyforms_script_index'; // @phpstan-ignore-line
183 - $scriptSrc = IVYFORMS_DEV ? // @phpstan-ignore-line
184 - 'http://localhost:5173/src/assets/js/public/public.ts' :
185 - IVYFORMS_URL . 'frontend/dist/public.js';
186 -
187 - $styleSrc = IVYFORMS_DEV ? '' : IVYFORMS_URL . 'frontend/dist/index.css'; // @phpstan-ignore-line
188 - $stylePublicSrc = IVYFORMS_DEV ? '' : IVYFORMS_URL . 'frontend/dist/public.css'; // @phpstan-ignore-line
189 - $scriptVersion = IVYFORMS_DEV ? null : IVYFORMS_VERSION;// @phpstan-ignore-line
190 -
191 - wp_enqueue_script(
192 - $scriptId,
193 - $scriptSrc,
194 - [],
195 - $scriptVersion,
196 - true
197 - );
198 -
199 - // TODO: Improve this with merging styles
200 - if ($styleSrc) { // @phpstan-ignore-line
201 - wp_enqueue_style('ivyforms_style_index', $styleSrc, [], $scriptVersion);
202 - wp_enqueue_style('ivyforms_style_public', $stylePublicSrc, [], $scriptVersion);
203 -
204 - // Use helper for inline styles
205 - wp_add_inline_style(
206 - 'ivyforms_style_index',
207 - InlineStyleHelper::getFrontendInlineCss()
186 + foreach (['placeholder', 'defaultValue'] as $key) {
187 + if (!isset($fieldArr[$key]) || !is_string($fieldArr[$key])) {
188 + continue;
189 + }
190 + $fieldArr[$key] = PlaceholderLocalizationHelper::resolveFieldString(
191 + $fieldArr[$key],
192 + $generalData
208 193 );
209 194 }
210 - self::enqueueRecaptchaScript($scriptId);
211 195
212 - wp_localize_script(
213 - $scriptId,
214 - 'wpIvyFormDataList',
215 - self::$formList
216 - );
196 + return $fieldArr;
197 + }
217 198
218 - $frontendLabels = array_merge(
219 - FrontendStrings::getFormRenderStrings()
220 - );
221 -
222 - /**
223 - * Filter frontend labels before localizing
224 - * Allows Pro version to merge additional strings
225 - *
226 - * @since 0.1.0
227 - * @param array $frontendLabels Array of frontend label strings
228 - */
229 - $frontendLabels = apply_filters('ivyforms/shortcode/frontend_labels', $frontendLabels);
230 -
231 - wp_localize_script($scriptId, 'wpIvyLabels', $frontendLabels);
232 -
233 - wp_localize_script(
234 - $scriptId,
235 - 'wpIvyUrls',
236 - [
237 - 'pluginURL' => IVYFORMS_URL,
238 - 'siteURL' => esc_url_raw(site_url()),
239 - ]
240 - );
241 -
242 - wp_localize_script(
243 - $scriptId,
244 - 'wpIvyApiSettings',
245 - [
246 - 'root' => esc_url_raw(rest_url()),
247 - 'nonce' => wp_create_nonce('wp_rest'),
248 - 'namespace' => Routes::$routeNamespace,
249 - ]
250 - );
251 - wp_localize_script(
252 - $scriptId,
253 - 'wpIvyDateFormat',
254 - [
255 - 'dateFormat' => get_option('date_format'),
256 - 'timeFormat' => get_option('time_format'),
257 - 'dateTimeFormat' => get_option('date_format') . ' ' . get_option('time_format'),
258 - 'locale' => get_locale(),
259 - ]
260 - );
261 -
262 - /**
263 - * Allow external plugins/components to enqueue additional scripts/styles.
264 - * @since 0.1.0
265 - *
266 - * Args:
267 - * - string $scriptId The main script handle for the frontend (public) script
268 - */
269 - do_action('ivyforms/shortcode/enqueue_scripts', $scriptId);
199 + /**
200 + * Enqueue handler
201 + */
202 + public static function enqueueScripts(): void
203 + {
204 + ShortcodeHelper::registerFrontendAssets(self::$formList);
270 205 }
271 206
272 207 /**
273 - * Add script attribute for module type
208 + * Generate CSS for a specific form
274 209 *
275 - * @param array<string, mixed> $attributes
276 - *
277 - * @return array<string, mixed>
210 + * @param int $formId
211 + * @return string
278 212 */
279 - public static function addScriptAttribute(array $attributes): array
213 + private static function generateFormCSS(int $formId): string
280 214 {
281 - $scriptArray = [
282 - 'ivyforms_scripts_dev_main-js',
283 - 'ivyforms_scripts_dev_vite-js',
284 - 'ivyforms_script_index-js',
285 - 'ivyforms_scripts_public-js',
286 - ];
215 + foreach (self::$formList as $formData) {
216 + if ($formData['id'] === $formId) {
217 + $formPayload = $formData['form'] ?? null;
218 + if (!is_array($formPayload)) {
219 + return '';
220 + }
287 221
288 - if (isset($attributes['id']) && in_array($attributes['id'], $scriptArray, true)) {
289 - $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 + }
290 232 }
291 233
292 - return $attributes;
234 + return '';
293 235 }
294 236
295 237 /**
296 238 * Enqueue CAPTCHA scripts (reCAPTCHA, Turnstile, etc.) if configured
@@ -295,32 +237,29 @@
295 237 /**
296 238 * Enqueue CAPTCHA scripts (reCAPTCHA, Turnstile, etc.) if configured
297 239 *
298 240 * @param string $scriptId
299 - * @throws ValidationException
300 241 */
301 242 public static function enqueueRecaptchaScript(string $scriptId): void
302 243 {
303 - $securityService = self::getSecurityService();
304 - $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);
305 247
306 - // Get security configuration using our enhanced service
307 248 $securityConfig = $securityService->getFrontendSecurityConfig($formFields);
308 249
309 - // Enqueue scripts if CAPTCHA is configured and has fields
310 250 if ($securityConfig['captcha']['enabled'] ?? false) {
311 251 if (isset($securityConfig['captcha']['recaptcha'])) {
312 - self::enqueueGoogleRecaptchaScript($securityConfig['captcha']);
252 + ShortcodeHelper::enqueueGoogleRecaptchaScript($securityConfig['captcha']);
313 253 }
314 254 if (isset($securityConfig['captcha']['turnstile'])) {
315 - self::enqueueTurnstileScript($securityConfig['captcha']);
255 + ShortcodeHelper::enqueueTurnstileScript($securityConfig['captcha']);
316 256 }
317 257 if (isset($securityConfig['captcha']['hcaptcha'])) {
318 - self::enqueueHCaptchaScript($securityConfig['captcha']);
258 + ShortcodeHelper::enqueueHCaptchaScript($securityConfig['captcha']);
319 259 }
320 260 }
321 261
322 - // Localize reCAPTCHA config
323 262 wp_localize_script(
324 263 $scriptId,
325 264 'wpIvyRecaptchaConfig',
326 265 $securityConfig['captcha'] ?? []
@@ -325,9 +264,8 @@
325 264 'wpIvyRecaptchaConfig',
326 265 $securityConfig['captcha'] ?? []
327 266 );
328 267
329 - // Localize Turnstile config
330 268 wp_localize_script(
331 269 $scriptId,
332 270 'wpIvyTurnstileConfig',
333 271 $securityConfig['captcha'] ?? []
@@ -332,9 +270,8 @@
332 270 'wpIvyTurnstileConfig',
333 271 $securityConfig['captcha'] ?? []
334 272 );
335 273
336 - // Localize hCaptcha config
337 274 wp_localize_script(
338 275 $scriptId,
339 276 'wpIvyHCaptchaConfig',
340 277 $securityConfig['captcha'] ?? []
@@ -341,118 +278,14 @@
341 278 );
342 279 }
343 280
344 281 /**
345 - * Get security service instance
282 + * Backwards-compatible wrapper for script attribute filter
346 283 *
347 - * @return SecurityService
284 + * @param array<string, mixed> $attributes
285 + * @return array<string, mixed>
348 286 */
349 - private static function getSecurityService(): SecurityService
287 + public static function addScriptAttribute(array $attributes): array
350 288 {
351 - $settingsService = new SettingsService();
352 - $securityFactory = new SecurityServiceFactory($settingsService);
353 - return new SecurityService($securityFactory);
354 - }
355 -
356 - /**
357 - * Get all form fields from currently loaded forms
358 - *
359 - * @return array<object> Array of form field objects
360 - * @throws ValidationException
361 - */
362 - private static function getAllFormFields(): array
363 - {
364 - $allFields = [];
365 -
366 - foreach (self::$formList as $formData) {
367 - foreach ($formData['fields'] as $fieldData) {
368 - // Create a simple object to match the expected interface
369 - $field = FieldFactory::create($fieldData);
370 -
371 - $allFields[] = $field;
372 - }
373 - }
374 -
375 - return $allFields;
376 - }
377 -
378 - /**
379 - * Enqueue Google reCAPTCHA script using security service
380 - *
381 - * @param array<string, mixed> $captchaConfig
382 - */
383 - private static function enqueueGoogleRecaptchaScript(array $captchaConfig): void
384 - {
385 - if (!isset($captchaConfig['recaptcha'])) {
386 - return;
387 - }
388 -
389 - $recaptchaConfig = $captchaConfig['recaptcha'];
390 - $scriptUrl = $recaptchaConfig['scriptUrl'] ?? '';
391 -
392 - if (empty($scriptUrl)) {
393 - return;
394 - }
395 -
396 - wp_enqueue_script(
397 - 'google-recaptcha',
398 - $scriptUrl,
399 - [],
400 - null, // No version for external script
401 - true // Load in footer
402 - );
403 - }
404 -
405 - /**
406 - * Enqueue Cloudflare Turnstile script
407 - *
408 - * @param array<string, mixed> $captchaConfig
409 - */
410 - private static function enqueueTurnstileScript(array $captchaConfig): void
411 - {
412 - if (!isset($captchaConfig['turnstile'])) {
413 - return;
414 - }
415 -
416 - $turnstileConfig = $captchaConfig['turnstile'];
417 - $scriptUrl = $turnstileConfig['scriptUrl'] ?? '';
418 -
419 - if (empty($scriptUrl)) {
420 - return;
421 - }
422 -
423 - wp_enqueue_script(
424 - 'cloudflare-turnstile',
425 - $scriptUrl,
426 - [],
427 - null, // No version for external script
428 - true // Load in footer
429 - );
430 - }
431 -
432 - /**
433 - * Enqueue hCaptcha script
434 - *
435 - * @param array<string, mixed> $captchaConfig
436 - */
437 - private static function enqueueHCaptchaScript(array $captchaConfig): void
438 - {
439 - if (!isset($captchaConfig['hcaptcha'])) {
440 - return;
441 - }
442 -
443 - $hcaptchaConfig = $captchaConfig['hcaptcha'];
444 - $scriptUrl = $hcaptchaConfig['scriptUrl'] ?? '';
445 -
446 - if (empty($scriptUrl)) {
447 - return;
448 - }
449 -
450 - wp_enqueue_script(
451 - 'hcaptcha',
452 - $scriptUrl,
453 - [],
454 - null, // No version for external script
455 - true // Load in footer
456 - );
289 + return ShortcodeHelper::addScriptAttribute($attributes);
457 290 }
458 291 }