PluginProbe
The Innovative Form Builder – IvyForms / 1.1.1
The Innovative Form Builder – IvyForms v1.1.1
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 / ShortcodeService.php

ShortcodeService.php in The Innovative Form Builder – IvyForms 1.1.1, at backend/src/Services/Shortcode/ShortcodeService.php

267 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @copyright © Melograno Venture Studio. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace IvyForms\Services\Shortcode;
9
10 use IvyForms\Common\Exceptions\ValidationException;
11 use IvyForms\Services\API\IvyFormsAPI;
12 use IvyForms\Services\Form\Style\FormStyleService;
13 use IvyForms\Services\Shortcode\Helpers\ShortcodeHelper;
14
15 /**
16 * Class ShortcodeService
17 *
18 * @package IvyForms\Services\Shortcode
19 */
20 class ShortcodeService
21 {
22 public static int $counter = 0;
23 /**
24 * Array keyed by unique render counters (string) -> payload array
25 *
26 * @var array<string, array<string, mixed>>
27 */
28 public static array $formList = [];
29
30 /**
31 * Shortcode handler
32 *
33 * @param mixed[] $atts
34 *
35 * @return string
36 * @throws ValidationException
37 */
38 public static function shortcodeHandler(array $atts): string
39 {
40 $atts = shortcode_atts(
41 [
42 'id' => '0',
43 'show_title' => null,
44 'show_description' => null,
45 ],
46 $atts
47 );
48
49 if (empty($atts['id'])) {
50 return '';
51 }
52
53 $formId = (int)$atts['id'];
54 $form = IvyFormsAPI::getForm($formId);
55
56 // Check if the form is published
57 if (is_wp_error($form) || !($form->isPublished())) {
58 return '';
59 }
60
61 // Generate a stable unique identifier for this render
62 $counter = $formId . '_' . uniqid();
63
64 // Build overrides array for show_title and show_description
65 $overrides = [];
66 if ($atts['show_title'] !== null) {
67 // Convert string 'true'/'false' or '1'/'0' to boolean
68 $overrides['showTitle'] = filter_var($atts['show_title'], FILTER_VALIDATE_BOOLEAN);
69 }
70 if ($atts['show_description'] !== null) {
71 $overrides['showDescription'] = filter_var($atts['show_description'], FILTER_VALIDATE_BOOLEAN);
72 }
73
74 self::getFormList($formId, $counter, $overrides);
75
76 self::enqueueScripts();
77
78 // Generate form-specific CSS
79 $formCSS = self::generateFormCSS($formId);
80
81 ob_start();
82
83 // Output CSS inline before the form
84 if (!empty($formCSS)) {
85 $styleId = 'ivyforms-form-styles-' . sanitize_key(
86 str_replace('.', '-', $counter)
87 );
88 echo '<style id="' . esc_attr($styleId) . '">' . "\n" . $formCSS . "\n" . '</style>' . "\n";
89 }
90
91 include IVYFORMS_PATH . '/view/frontend/view.php';
92 $html = ob_get_contents();
93 ob_end_clean();
94
95 return $html;
96 }
97
98 /**
99 * Get form list
100 *
101 * @param int $formId
102 * @param string $counter Unique counter identifier for this render
103 * @param array<string, mixed> $overrides Optional overrides for form settings (e.g., showTitle, showDescription)
104 *
105 * @return void
106 */
107 public static function getFormList(int $formId, string $counter, array $overrides = [])
108 {
109 $form = IvyFormsAPI::getForm($formId);
110
111 $fields = IvyFormsAPI::getFields($form->getId());
112
113 $confirmations = IvyFormsAPI::getConfirmations($form->getId());
114
115 $fieldsMap = self::prepareFieldsMap($fields);
116
117 $confirmationsMap = [];
118
119 foreach ($confirmations as $confirmation) {
120 $confirmationsMap[] = is_object($confirmation) && method_exists($confirmation, 'toArray')
121 ? $confirmation->toArray()
122 : $confirmation;
123 }
124
125 $form->setFields($fieldsMap);
126
127 $formArr = $form->toArray();
128
129 // Apply overrides for showTitle and showDescription (from Gutenberg block or shortcode)
130 if (array_key_exists('showTitle', $overrides)) {
131 $formArr['showTitle'] = $overrides['showTitle'];
132 }
133 if (array_key_exists('showDescription', $overrides)) {
134 $formArr['showDescription'] = $overrides['showDescription'];
135 }
136
137 self::$formList[$counter] = [
138 'form' => $formArr,
139 'fields' => $fieldsMap,
140 'id' => $form->getId(),
141 'counter' => $counter,
142 'nonce' => wp_create_nonce('ivyformsFrontSubmissionNonce_' . $form->getId()),
143 'confirmations' => $confirmationsMap,
144 ];
145 }
146
147 /**
148 * Prepare fields map for a form
149 *
150 * @param array<int, object> $fields
151 * @return array<int, array<string, mixed>>
152 */
153 private static function prepareFieldsMap(array $fields): array
154 {
155 $fieldsMap = [];
156 foreach ($fields as $field) {
157 $fieldArr = $field->toArray();
158 if (
159 isset($fieldArr['type']) && in_array(
160 $fieldArr['type'],
161 ['radio', 'checkbox', 'select', 'multi-select', 'rating'],
162 true
163 )
164 ) {
165 $fieldOptions = IvyFormsAPI::getFieldOptions($fieldArr['id']);
166 $fieldArr['fieldOptions'] = [];
167 foreach ($fieldOptions as $option) {
168 if (is_object($option) && method_exists($option, 'toArray')) {
169 $fieldArr['fieldOptions'][] = $option->toArray();
170 }
171 }
172 }
173 $fieldsMap[] = $fieldArr;
174 }
175 return $fieldsMap;
176 }
177
178 /**
179 * Enqueue handler
180 */
181 public static function enqueueScripts(): void
182 {
183 ShortcodeHelper::registerFrontendAssets(self::$formList);
184 }
185
186 /**
187 * Generate CSS for a specific form
188 *
189 * @param int $formId
190 * @return string
191 */
192 private static function generateFormCSS(int $formId): string
193 {
194 foreach (self::$formList as $formData) {
195 if ($formData['id'] === $formId) {
196 $formPayload = $formData['form'] ?? null;
197 if (!is_array($formPayload)) {
198 return '';
199 }
200
201 $styleSettings = $formPayload['styleSettings'] ?? null;
202
203 if (!empty($styleSettings) && is_array($styleSettings)) {
204 return FormStyleService::generateFormCSS($formId, $styleSettings);
205 }
206 }
207 }
208
209 return '';
210 }
211
212 /**
213 * Enqueue CAPTCHA scripts (reCAPTCHA, Turnstile, etc.) if configured
214 *
215 * @param string $scriptId
216 */
217 public static function enqueueRecaptchaScript(string $scriptId): void
218 {
219 // Delegate captcha discovery/enqueue to ShortcodeHelper while keeping BC wrapper here
220 $securityService = ShortcodeHelper::getSecurityService();
221 $formFields = ShortcodeHelper::getAllFormFields(self::$formList);
222
223 $securityConfig = $securityService->getFrontendSecurityConfig($formFields);
224
225 if ($securityConfig['captcha']['enabled'] ?? false) {
226 if (isset($securityConfig['captcha']['recaptcha'])) {
227 ShortcodeHelper::enqueueGoogleRecaptchaScript($securityConfig['captcha']);
228 }
229 if (isset($securityConfig['captcha']['turnstile'])) {
230 ShortcodeHelper::enqueueTurnstileScript($securityConfig['captcha']);
231 }
232 if (isset($securityConfig['captcha']['hcaptcha'])) {
233 ShortcodeHelper::enqueueHCaptchaScript($securityConfig['captcha']);
234 }
235 }
236
237 wp_localize_script(
238 $scriptId,
239 'wpIvyRecaptchaConfig',
240 $securityConfig['captcha'] ?? []
241 );
242
243 wp_localize_script(
244 $scriptId,
245 'wpIvyTurnstileConfig',
246 $securityConfig['captcha'] ?? []
247 );
248
249 wp_localize_script(
250 $scriptId,
251 'wpIvyHCaptchaConfig',
252 $securityConfig['captcha'] ?? []
253 );
254 }
255
256 /**
257 * Backwards-compatible wrapper for script attribute filter
258 *
259 * @param array<string, mixed> $attributes
260 * @return array<string, mixed>
261 */
262 public static function addScriptAttribute(array $attributes): array
263 {
264 return ShortcodeHelper::addScriptAttribute($attributes);
265 }
266 }
267