PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.15
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.15
6.2.15 6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 All 197 releases
← All changes | app/Services/FormBuilder/ShortCodeParser.php +431 -79 3.6.60 → 6.2.15 View file →
@@ -1,16 +1,19 @@
1 1 <?php
2 2
3 3 namespace FluentForm\App\Services\FormBuilder;
4 4
5 -use FluentForm\App;
5 +use FluentForm\App\Models\SubmissionMeta;
6 6 use FluentForm\App\Modules\Form\FormDataParser;
7 7 use FluentForm\App\Modules\Form\FormFieldsParser;
8 8 use FluentForm\App\Services\Browser\Browser;
9 9 use FluentForm\Framework\Helpers\ArrayHelper;
10 +use FluentForm\App\Helpers\Helper;
10 11
11 12 class ShortCodeParser
12 13 {
14 + const USER_SECRET_PROPERTIES = ['user_pass', 'user_activation_key', 'session_tokens', 'data'];
15 +
13 16 protected static $form = null;
14 17
15 18 protected static $entry = null;
16 19
@@ -17,8 +20,10 @@
17 20 protected static $browser = null;
18 21
19 22 protected static $formFields = null;
20 23
24 + protected static $provider = null;
25 +
21 26 protected static $store = [
22 27 'inputs' => null,
23 28 'original_inputs' => null,
24 29 'user' => null,
@@ -23,24 +28,24 @@
23 28 'original_inputs' => null,
24 29 'user' => null,
25 30 'post' => null,
26 31 'other' => null,
27 - 'submission' => null
32 + 'submission' => null,
28 33 ];
29 34
30 - public static function parse($parsable, $entryId, $data = [], $form = null, $isUrl = false, $provider = false)
35 + public static function parse($parsable, $entryId, $data = [], $form = null, $isUrl = false, $providerOrIsHTML = false, $htmlSanitized = false)
31 36 {
32 37 try {
33 - static::setDependencies($entryId, $data, $form);
38 + static::setDependencies($entryId, $data, $form, $providerOrIsHTML);
34 39
35 40 if (is_array($parsable)) {
36 - return static::parseShortCodeFromArray($parsable, $isUrl, $provider);
41 + return static::parseShortCodeFromArray($parsable, $isUrl, $providerOrIsHTML, $htmlSanitized);
37 42 }
38 43
39 - return static::parseShortCodeFromString($parsable, $isUrl, false);
40 -
44 + return static::parseShortCodeFromString($parsable, $isUrl, $providerOrIsHTML, $htmlSanitized);
41 45 } catch (\Exception $e) {
42 46 if (defined('WP_DEBUG') && WP_DEBUG) {
47 + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging only when WP_DEBUG is enabled, helps developers troubleshoot shortcode parsing issues
43 48 error_log($e->getTraceAsString());
44 49 }
45 50 return '';
46 51 }
@@ -45,13 +50,14 @@
45 50 return '';
46 51 }
47 52 }
48 53
49 - protected static function setDependencies($entry, $data, $form)
54 + protected static function setDependencies($entry, $data, $form, $provider)
50 55 {
51 56 static::setEntry($entry);
52 57 static::setData($data);
53 58 static::setForm($form);
59 + static::$provider = $provider;
54 60 }
55 61
56 62 protected static function setEntry($entry)
57 63 {
@@ -78,19 +84,30 @@
78 84 static::$form = static::getEntry()->form_id;
79 85 }
80 86 }
81 87
82 - protected static function parseShortCodeFromArray($parsable, $isUrl = false, $provider = false)
88 + protected static function parseShortCodeFromArray($parsable, $isUrl = false, $provider = false, $htmlSanitized = false)
83 89 {
84 90 foreach ($parsable as $key => $value) {
85 91 if (is_array($value)) {
86 - $parsable[$key] = static::parseShortCodeFromArray($value, $isUrl, $provider);
92 + $parsable[$key] = static::parseShortCodeFromArray($value, $isUrl, $provider, $htmlSanitized);
87 93 } else {
88 94 $isHtml = false;
89 95 if ($provider) {
90 - $isHtml = apply_filters('ff_will_return_html', false, $provider, $key);
96 + $isHtml = apply_filters_deprecated(
97 + 'ff_will_return_html',
98 + [
99 + false,
100 + $provider,
101 + $key,
102 + ],
103 + FLUENTFORM_FRAMEWORK_UPGRADE,
104 + 'fluentform/will_return_html',
105 + 'Use fluentform/will_return_html instead of ff_will_return_html.'
106 + );
107 + $isHtml = apply_filters('fluentform/will_return_html', $isHtml, $provider, $key);
91 108 }
92 - $parsable[$key] = static::parseShortCodeFromString($value, $isUrl, $isHtml);
109 + $parsable[$key] = static::parseShortCodeFromString($value, $isUrl, $isHtml, $htmlSanitized);
93 110 }
94 111 }
95 112
96 113 return $parsable;
@@ -95,36 +112,54 @@
95 112
96 113 return $parsable;
97 114 }
98 115
99 - protected static function parseShortCodeFromString($parsable, $isUrl = false, $isHtml = false)
116 + protected static function parseShortCodeFromString($parsable, $isUrl = false, $isHtml = false, $htmlSanitized = false)
100 117 {
118 + if ('0' === $parsable) {
119 + return $parsable;
120 + }
121 +
101 122 if (!$parsable) {
102 123 return '';
103 124 }
104 - return preg_replace_callback('/{+(.*?)}/', function ($matches) use ($isUrl, $isHtml) {
125 + return preg_replace_callback('/{+(.*?)}/', function ($matches) use ($isUrl, $isHtml, $htmlSanitized) {
105 126 $value = '';
106 - if (strpos($matches[1], 'inputs.') !== false) {
127 + if (false !== strpos($matches[1], 'inputs.')) {
107 128 $formProperty = substr($matches[1], strlen('inputs.'));
108 129 $value = static::getFormData($formProperty, $isHtml);
109 - } elseif (strpos($matches[1], 'user.') !== false) {
130 + } elseif (false !== strpos($matches[1], 'labels.')) {
131 + $formLabelProperty = substr($matches[1], strlen('labels.'));
132 + $value = static::getFormLabelData($formLabelProperty);
133 + } elseif (false !== strpos($matches[1], 'user.')) {
110 134 $userProperty = substr($matches[1], strlen('user.'));
111 135 $value = static::getUserData($userProperty);
112 - } elseif (strpos($matches[1], 'embed_post.') !== false) {
136 + } elseif (false !== strpos($matches[1], 'embed_post.')) {
113 137 $postProperty = substr($matches[1], strlen('embed_post.'));
114 138 $value = static::getPostData($postProperty);
115 - } elseif (strpos($matches[1], 'wp.') !== false) {
139 + } elseif (false !== strpos($matches[1], 'wp.')) {
116 140 $wpProperty = substr($matches[1], strlen('wp.'));
117 141 $value = static::getWPData($wpProperty);
118 - } elseif (strpos($matches[1], 'submission.') !== false) {
142 + } elseif (false !== strpos($matches[1], 'submission.')) {
119 143 $submissionProperty = substr($matches[1], strlen('submission.'));
120 144 $value = static::getSubmissionData($submissionProperty);
121 - } elseif (strpos($matches[1], 'cookie.') !== false) {
145 + } elseif (false !== strpos($matches[1], 'cookie.')) {
122 146 $scookieProperty = substr($matches[1], strlen('cookie.'));
123 - $value = ArrayHelper::get($_COOKIE, $scookieProperty);
124 - } elseif (strpos($matches[1], 'payment.') !== false) {
147 + $value = array_key_exists($scookieProperty, $_COOKIE) ? sanitize_text_field(wp_unslash($_COOKIE[$scookieProperty])) : '';
148 + } elseif (false !== strpos($matches[1], 'payment.')) {
125 149 $property = substr($matches[1], strlen('payment.'));
126 - $value = apply_filters('fluentform_payment_smartcode', '', $property, self::getInstance());
150 + $deprecatedValue = apply_filters_deprecated(
151 + 'fluentform_payment_smartcode', [
152 + '',
153 + $property,
154 + self::getInstance(),
155 + ],
156 + FLUENTFORM_FRAMEWORK_UPGRADE,
157 + 'fluentform/payment_smartcode',
158 + 'Use fluentform/payment_smartcode instead of fluentform_payment_smartcode.'
159 + );
160 +
161 + $value = apply_filters('fluentform/payment_smartcode', $deprecatedValue, $property, self::getInstance());
127 162 } else {
128 163 $value = static::getOtherData($matches[1]);
129 164 }
130 165
@@ -132,13 +167,17 @@
132 167 $value = fluentImplodeRecursive(', ', $value);
133 168 }
134 169
135 170 if ($isUrl) {
136 - $value = urlencode($value);
171 + // Don't encode values that are already complete URLs like {wp.site_url}
172 + if (!preg_match('#^https?://#i', (string) $value)) {
173 + $value = rawurlencode($value);
174 + }
175 + } elseif ($htmlSanitized) {
176 + $value = fluentform_sanitize_html($value);
137 177 }
138 178
139 179 return $value;
140 -
141 180 }, $parsable);
142 181 }
143 182
144 183 protected static function getFormData($key, $isHtml = false)
@@ -144,22 +183,9 @@
144 183 protected static function getFormData($key, $isHtml = false)
145 184 {
146 185 if (strpos($key, '.label')) {
147 186 $key = str_replace('.label', '', $key);
148 - $field = ArrayHelper::get(static::$formFields, $key, '');
149 - if (empty($field['element'])) {
150 - return '';
151 - }
152 -
153 - $value = ArrayHelper::get(static::$store['original_inputs'], $key);
154 -
155 - return apply_filters(
156 - 'fluentform_response_render_' . $field['element'],
157 - $value,
158 - $field,
159 - static::getForm()->id,
160 - true
161 - );
187 + $isHtml = true;
162 188 }
163 189
164 190 if (strpos($key, '.value')) {
165 191 $key = str_replace('.value', '', $key);
@@ -166,36 +192,53 @@
166 192 return ArrayHelper::get(static::$store['original_inputs'], $key);
167 193 }
168 194
169 195 if (strpos($key, '.') && !isset(static::$store['inputs'][$key])) {
170 - if (!isset(static::$store['inputs'][$key])) {
171 - static::$store['inputs'][$key] = ArrayHelper::get(
172 - static::$store['original_inputs'], $key, ''
173 - );
174 - }
196 + return ArrayHelper::get(
197 + static::$store['original_inputs'],
198 + $key,
199 + ''
200 + );
175 201 }
176 202
177 203 if (!isset(static::$store['inputs'][$key])) {
178 204 static::$store['inputs'][$key] = ArrayHelper::get(
179 - static::$store['inputs'], $key, ''
205 + static::$store['inputs'],
206 + $key,
207 + ''
180 208 );
181 209 }
182 210
183 211 if (is_null(static::$formFields)) {
184 212 static::$formFields = FormFieldsParser::getShortCodeInputs(
185 - static::getForm(), ['admin_label', 'attributes', 'options', 'raw']
213 + static::getForm(),
214 + ['admin_label', 'attributes', 'options', 'raw']
186 215 );
187 216 }
188 217
189 218 $field = ArrayHelper::get(static::$formFields, $key, '');
190 219
220 + if (!$field) {
221 + return '';
222 + }
191 223
192 - if (!$field) return '';
193 -
194 - if($isHtml) {
224 + if ($isHtml) {
225 + $originalInput = ArrayHelper::get(static::$store['original_inputs'], $key, '');
226 + $originalInput = apply_filters_deprecated(
227 + 'fluentform_response_render_' . $field['element'],
228 + [
229 + $originalInput,
230 + $field,
231 + static::getForm()->id,
232 + $isHtml,
233 + ],
234 + FLUENTFORM_FRAMEWORK_UPGRADE,
235 + 'fluentform/response_render_' . $field['element'],
236 + 'Use fluentform/response_render_' . $field['element'] . ' instead of fluentform_response_render_' . $field['element']
237 + );
195 238 return apply_filters(
196 - 'fluentform_response_render_' . $field['element'],
197 - static::$store['original_inputs'][$key],
239 + 'fluentform/response_render_' . $field['element'],
240 + $originalInput,
198 241 $field,
199 242 static::getForm()->id,
200 243 $isHtml
201 244 );
@@ -200,10 +243,23 @@
200 243 $isHtml
201 244 );
202 245 }
203 246
247 + static::$store['inputs'][$key] = apply_filters_deprecated(
248 + 'fluentform_response_render_' . $field['element'],
249 + [
250 + static::$store['inputs'][$key],
251 + $field,
252 + static::getForm()->id,
253 + $isHtml,
254 + ],
255 + FLUENTFORM_FRAMEWORK_UPGRADE,
256 + 'fluentform/response_render_' . $field['element'],
257 + 'Use fluentform/response_render_' . $field['element'] . ' instead of fluentform_response_render_' . $field['element']
258 + );
259 +
204 260 return static::$store['inputs'][$key] = apply_filters(
205 - 'fluentform_response_render_' . $field['element'],
261 + 'fluentform/response_render_' . $field['element'],
206 262 static::$store['inputs'][$key],
207 263 $field,
208 264 static::getForm()->id,
209 265 $isHtml
@@ -209,14 +265,72 @@
209 265 $isHtml
210 266 );
211 267 }
212 268
269 + protected static function getFormLabelData($key)
270 + {
271 + if (is_null(static::$formFields)) {
272 + static::$formFields = FormFieldsParser::getShortCodeInputs(
273 + static::getForm(),
274 + ['admin_label', 'attributes', 'options', 'raw', 'label']
275 + );
276 + }
277 +
278 + // Resolve global validation messages {labels.current_field} shortcode.
279 + // Current field name attribute was setted as inputs data key 'current_field'.
280 + if ('current_field' === $key && $currentFieldName = ArrayHelper::get(static::$store['inputs'], $key)) {
281 + $currentFieldName = str_replace(['[', ']'], ['.', ''], $currentFieldName);
282 + $key = $currentFieldName;
283 + }
284 + $inputLabel = ArrayHelper::get(ArrayHelper::get(static::$formFields, $key, []), 'label', '');
285 + $inputLabel = str_replace(['[', ']'], '', $inputLabel);
286 + $keys = explode('.', $key);
287 + if (count($keys) > 1) {
288 + $parentKey = array_shift($keys);
289 + $inputLabel = str_replace($parentKey, '', $inputLabel);
290 + }
291 + if (empty($inputLabel)) {
292 + $inputLabel = ArrayHelper::get(ArrayHelper::get(static::$formFields, $key, []), 'admin_label', '');
293 + }
294 + if (empty($inputLabel) && isset($parentKey) && $parentKey) {
295 + $inputLabel = ArrayHelper::get(ArrayHelper::get(static::$formFields, $parentKey, []), 'label', '');
296 + $key = $parentKey;
297 + }
298 +
299 + return apply_filters('fluentform/input_label_shortcode', $inputLabel, $key, static::getForm());
300 + }
301 +
213 302 protected static function getUserData($key)
214 303 {
215 304 if (is_null(static::$store['user'])) {
216 305 static::$store['user'] = wp_get_current_user();
217 306 }
218 - return static::$store['user']->{$key};
307 +
308 + $user = static::$store['user'];
309 +
310 + // SECURITY (FINDING-11): `$user->{$key}` reads straight from the wp_users row via
311 + // WP_User::__get, so an author-controlled {user.user_pass} (or {user.user_activation_key})
312 + // would exfiltrate the *submitting* user's password hash / reset token in a notification.
313 + // Allow only a fixed set of safe profile fields; resolve anything else from user meta,
314 + // which never contains the sensitive wp_users columns.
315 + $allowed = [
316 + 'ID', 'id', 'display_name', 'first_name', 'last_name', 'user_email',
317 + 'user_login', 'user_nicename', 'nickname', 'user_url', 'description', 'roles',
318 + 'user_registered', // non-sensitive wp_users column; keep {user.user_registered} working
319 + ];
320 + if (static::isDeniedUserProperty($key)) {
321 + return '';
322 + }
323 + if (in_array($key, $allowed, true)) {
324 + return $user->{$key};
325 + }
326 +
327 + $key = (string) $key;
328 + if ($user->ID && '' !== $key) {
329 + return get_user_meta($user->ID, $key, true);
330 + }
331 +
332 + return '';
219 333 }
220 334
221 335 protected static function getPostData($key)
222 336 {
@@ -222,15 +336,18 @@
222 336 {
223 337 if (is_null(static::$store['post'])) {
224 338 $postId = static::$store['inputs']['__fluent_form_embded_post_id'];
225 339 static::$store['post'] = get_post($postId);
340 + if (is_null(static::$store['post'])) {
341 + return '';
342 + }
226 343 static::$store['post']->permalink = get_the_permalink(static::$store['post']);
227 344 }
228 345
229 - if (strpos($key, 'author.') !== false) {
346 + if (false !== strpos($key, 'author.')) {
230 347 $authorProperty = substr($key, strlen('author.'));
231 348 $authorId = static::$store['post']->post_author;
232 - if ($authorId) {
349 + if ($authorId && !static::isDeniedUserProperty($authorProperty)) {
233 350 $data = get_the_author_meta($authorProperty, $authorId);
234 351 if (!is_array($data)) {
235 352 return $data;
236 353 }
@@ -235,9 +352,9 @@
235 352 return $data;
236 353 }
237 354 }
238 355 return '';
239 - } else if (strpos($key, 'meta.') !== false) {
356 + } elseif (false !== strpos($key, 'meta.')) {
240 357 $metaKey = substr($key, strlen('meta.'));
241 358 $postId = static::$store['post']->ID;
242 359 $data = get_post_meta($postId, $metaKey, true);
243 360 if (!is_array($data)) {
@@ -243,9 +360,9 @@
243 360 if (!is_array($data)) {
244 361 return $data;
245 362 }
246 363 return '';
247 - } else if (strpos($key, 'acf.') !== false) {
364 + } elseif (false !== strpos($key, 'acf.')) {
248 365 $metaKey = substr($key, strlen('acf.'));
249 366 $postId = static::$store['post']->ID;
250 367 if (function_exists('get_field')) {
251 368 $data = get_field($metaKey, $postId, true);
@@ -255,20 +372,39 @@
255 372 return '';
256 373 }
257 374 }
258 375
376 + if ('post_password' === $key) {
377 + return '';
378 + }
379 +
259 380 return static::$store['post']->{$key};
260 381 }
261 382
383 + // Shared by {user.*} and {embed_post.author.*} in both parsers. get_the_author_meta() and
384 + // WP_User fall through to any user meta, where plugins keep 2FA secrets and tokens under
385 + // protected (underscore) keys, so the secret columns alone are not enough to deny.
386 + public static function isDeniedUserProperty($property)
387 + {
388 + // Same aliases get_the_author_meta() accepts: 'pass' means user_pass
389 + if (in_array($property, ['login', 'pass', 'nicename', 'email', 'url', 'registered', 'activation_key', 'status'], true)) {
390 + $property = 'user_' . $property;
391 + }
392 +
393 + $denied = (array) apply_filters('fluentform/smartcode_user_denied_properties', self::USER_SECRET_PROPERTIES);
394 +
395 + return in_array($property, $denied, true) || is_protected_meta($property, 'user');
396 + }
397 +
262 398 protected static function getWPData($key)
263 399 {
264 - if ($key == 'admin_email') {
400 + if ('admin_email' == $key) {
265 401 return get_option('admin_email');
266 402 }
267 - if ($key == 'site_url') {
403 + if ('site_url' == $key) {
268 404 return site_url();
269 405 }
270 - if ($key == 'site_title') {
406 + if ('site_title' == $key) {
271 407 return get_option('blogname');
272 408 }
273 409 return $key;
274 410 }
@@ -275,22 +411,33 @@
275 411
276 412 protected static function getSubmissionData($key)
277 413 {
278 414 $entry = static::getEntry();
279 - if (property_exists($entry, $key)) {
280 - if ($key == 'total_paid' || $key == 'payment_total') {
415 +
416 + if (empty($entry->id)) {
417 + return '';
418 + }
419 +
420 + $columns = Helper::getEntryColumns($entry);
421 +
422 + if (array_key_exists($key, $columns)) {
423 + if ('total_paid' == $key || 'payment_total' == $key) {
281 424 return round($entry->{$key} / 100, 2);
282 425 }
283 - if ($key == 'payment_method' && $key == 'test') {
426 + if ('payment_method' == $key && 'test' == $entry->{$key}) {
284 427 return __('Offline', 'fluentform');
285 428 }
286 429 return $entry->{$key};
287 430 }
288 - if ($key == 'admin_view_url') {
431 + if ('admin_view_url' == $key) {
289 432 return admin_url('admin.php?page=fluent_forms&route=entries&form_id=' . $entry->form_id . '#/entries/' . $entry->id);
290 - } else if (strpos($key, 'meta.') !== false) {
433 + } elseif ('entry_uid' == $key) {
434 + return static::getShortEntryUid($entry);
435 + } elseif ('entry_uid_link' == $key) {
436 + return static::getEntryUidLink($entry);
437 + } elseif (false !== strpos($key, 'meta.')) {
291 438 $metaKey = substr($key, strlen('meta.'));
292 - $data = App\Helpers\Helper::getSubmissionMeta($entry->id, $metaKey);
439 + $data = Helper::getSubmissionMeta($entry->id, $metaKey);
293 440 if (!is_array($data)) {
294 441 return $data;
295 442 }
296 443 return '';
@@ -300,43 +447,179 @@
300 447 }
301 448
302 449 protected static function getOtherData($key)
303 450 {
304 - if (strpos($key, 'date.') === 0) {
451 + if (0 === strpos($key, 'date.')) {
305 452 $format = str_replace('date.', '', $key);
306 453 return date($format, strtotime(current_time('mysql')));
307 - } elseif ($key == 'admin_email') {
454 + } elseif ('admin_email' == $key) {
308 455 return get_option('admin_email', false);
309 - } elseif ($key == 'ip') {
456 + } elseif ('ip' == $key) {
310 457 return static::getRequest()->getIp();
311 - } elseif ($key == 'browser.platform') {
458 + } elseif ('browser.platform' == $key) {
312 459 return static::getUserAgent()->getPlatform();
313 - } elseif ($key == 'browser.name') {
460 + } elseif ('browser.name' == $key) {
314 461 return static::getUserAgent()->getBrowser();
315 - } elseif ($key == 'all_data') {
462 + } elseif (in_array($key, ['all_data', 'all_data_without_hidden_fields'])) {
316 463 $formFields = FormFieldsParser::getEntryInputs(static::getForm());
317 464 $inputLabels = FormFieldsParser::getAdminLabels(static::getForm(), $formFields);
318 465 $response = FormDataParser::parseFormSubmission(static::getEntry(), static::getForm(), $formFields, true);
319 466
467 + $status = apply_filters_deprecated(
468 + 'fluentform_all_data_skip_password_field',
469 + [
470 + __return_true(),
471 + ],
472 + FLUENTFORM_FRAMEWORK_UPGRADE,
473 + 'fluentform/all_data_skip_password_field',
474 + 'Use fluentform/all_data_skip_password_field instead of fluentform_all_data_skip_password_field.'
475 + );
476 +
477 + if (apply_filters('fluentform/all_data_skip_password_field', $status)) {
478 + $passwords = FormFieldsParser::getInputsByElementTypes(static::getForm(), ['input_password']);
479 + if (is_array($passwords) && !empty($passwords)) {
480 + $user_inputs = $response->user_inputs;
481 + ArrayHelper::forget($user_inputs, array_keys($passwords));
482 + $response->user_inputs = $user_inputs;
483 + }
484 + }
485 +
486 + $hideHiddenField = true;
487 + $hideHiddenField = apply_filters_deprecated(
488 + 'fluentform_all_data_without_hidden_fields',
489 + [
490 + $hideHiddenField,
491 + ],
492 + FLUENTFORM_FRAMEWORK_UPGRADE,
493 + 'fluentform/all_data_without_hidden_fields',
494 + 'Use fluentform/all_data_without_hidden_fields instead of fluentform_all_data_without_hidden_fields.'
495 + );
496 + $skipHiddenFields = ('all_data_without_hidden_fields' == $key) &&
497 + apply_filters('fluentform/all_data_without_hidden_fields', $hideHiddenField);
498 +
499 + if ($skipHiddenFields) {
500 + $hiddenFields = FormFieldsParser::getInputsByElementTypes(static::getForm(), ['input_hidden']);
501 + if (is_array($hiddenFields) && !empty($hiddenFields)) {
502 + ArrayHelper::forget($response->user_inputs, array_keys($hiddenFields));
503 + }
504 + }
505 +
320 506 $html = '<table class="ff_all_data" width="600" cellpadding="0" cellspacing="0"><tbody>';
321 - foreach ($inputLabels as $key => $label) {
322 - if (array_key_exists($key, $response->user_inputs) && ArrayHelper::get($response->user_inputs, $key)) {
323 - $data = ArrayHelper::get($response->user_inputs, $key);
507 + foreach ($inputLabels as $inputKey => $label) {
508 + if (array_key_exists($inputKey, $response->user_inputs) && '' !== ArrayHelper::get($response->user_inputs, $inputKey)) {
509 + $data = ArrayHelper::get($response->user_inputs, $inputKey);
324 510 if (is_array($data) || is_object($data)) {
325 511 continue;
326 512 }
513 + // $label is admin-set, $data is already sanitized via fluentFormSanitizer() on submission insert
327 514 $html .= '<tr class="field-label"><th style="padding: 6px 12px; background-color: #f8f8f8; text-align: left;"><strong>' . $label . '</strong></th></tr><tr class="field-value"><td style="padding: 6px 12px 12px 12px;">' . $data . '</td></tr>';
328 515 }
329 516 }
517 +
330 518 $html .= '</tbody></table>';
331 - return $html;
519 + $html = apply_filters_deprecated(
520 + 'fluentform_all_data_shortcode_html',
521 + [
522 + $html,
523 + $formFields,
524 + $inputLabels,
525 + $response,
526 + ],
527 + FLUENTFORM_FRAMEWORK_UPGRADE,
528 + 'fluentform/all_data_shortcode_html',
529 + 'Use fluentform/all_data_shortcode_html instead of fluentform_all_data_shortcode_html.'
530 + );
531 + return apply_filters('fluentform/all_data_shortcode_html', $html, $formFields, $inputLabels, $response);
532 + } elseif ('http_referer' === $key) {
533 + return wp_get_referer();
534 + } elseif (0 === strpos($key, 'pdf.download_link.')) {
535 + $key = apply_filters_deprecated(
536 + 'fluentform_shortcode_parser_callback_pdf.download_link.public',
537 + [
538 + $key, static::getInstance(),
539 + ],
540 + FLUENTFORM_FRAMEWORK_UPGRADE,
541 + 'fluentform/shortcode_parser_callback_pdf.download_link.public',
542 + 'Use fluentform/shortcode_parser_callback_pdf.download_link.public instead of fluentform_shortcode_parser_callback_pdf.download_link.public.'
543 + );
544 + return apply_filters('fluentform/shortcode_parser_callback_pdf.download_link.public', $key, static::getInstance());
545 + } elseif (false !== strpos($key, 'random_string.')) {
546 + $exploded = explode('.', $key);
547 + $prefix = array_pop($exploded);
548 + $value = $prefix . uniqid();
549 +
550 + return apply_filters('fluentform/shortcode_parser_callback_random_string', $value, $prefix, static::getInstance());
551 + } elseif ('form_title' == $key) {
552 + return static::getForm()->title;
553 + } elseif (false !== strpos($key, 'chat_gpt_response.')) {
554 + if (defined('FLUENTFORMPRO') && class_exists('\FluentFormPro\classes\Chat\ChatFieldController')) {
555 + $exploded = explode('.', $key);
556 + $prefix = array_pop($exploded);
557 + if (!$prefix) {
558 + return '';
559 + }
560 + $exploded = explode('_', $prefix);
561 + $formId = reset($exploded);
562 + $feedId = end($exploded);
563 + $chatGPT = new \FluentFormPro\classes\Chat\ChatFieldController(wpFluentForm());
564 + if ($chatGPT->api->isApiEnabled()) {
565 + $entry = static::getEntry();
566 + $lastResponse = SubmissionMeta::retrieve("chat_gpt_response_{$feedId}", $entry->id);
567 + if (!$lastResponse) {
568 + $response = $chatGPT->chatGPTSubmissionMessageHandler($formId, $feedId, static::getInstance());
569 + SubmissionMeta::persist($entry->id, "chat_gpt_response_{$feedId}", $response, $formId);
570 + return $response;
571 + }
572 + return $lastResponse;
573 + }
574 + }
575 + return '';
332 576 }
577 +
578 + // if it's multi line then just return
579 + if (false !== strpos($key, PHP_EOL)) { // most probably it's a css
580 + return '{' . $key . '}';
581 + }
582 +
583 + $groups = explode('.', $key);
584 + if (count($groups) > 1) {
585 + $group = array_shift($groups);
586 + $property = implode('.', $groups);
587 + $handlerValue = apply_filters_deprecated(
588 + 'fluentform_smartcode_group_' . $group,
589 + [
590 + $property,
591 + static::getInstance(),
592 + ],
593 + FLUENTFORM_FRAMEWORK_UPGRADE,
594 + 'fluentform/smartcode_group_' . $group,
595 + 'Use fluentform/smartcode_group_' . $group . ' instead of fluentform_smartcode_group_' . $group
596 + );
597 +
598 + $handlerValue = apply_filters('fluentform/smartcode_group_' . $group, $handlerValue, static::getInstance());
599 + if ($handlerValue != $property) {
600 + return $handlerValue;
601 + }
602 + }
603 +
333 604 // This fallback actually
334 - $handlerValue = apply_filters('fluentform_shortcode_parser_callback_' . $key, '{' . $key . '}', self::getInstance());
605 + $handlerValue = apply_filters_deprecated(
606 + 'fluentform_shortcode_parser_callback_' . $key,
607 + [
608 + '{' . $key . '}',
609 + static::getInstance(),
610 + ],
611 + FLUENTFORM_FRAMEWORK_UPGRADE,
612 + 'fluentform/shortcode_parser_callback_' . $key,
613 + 'Use fluentform/shortcode_parser_callback_' . $key . ' instead of fluentform_shortcode_parser_callback_' . $key
614 + );
335 615
616 + $handlerValue = apply_filters('fluentform/shortcode_parser_callback_' . $key, $handlerValue, static::getInstance());
617 +
336 618 if ($handlerValue) {
337 619 return $handlerValue;
338 620 }
621 +
339 622 return '';
340 623 }
341 624
342 625 public static function getForm()
@@ -347,8 +630,13 @@
347 630
348 631 return static::$form;
349 632 }
350 633
634 + public static function getProvider()
635 + {
636 + return static::$provider;
637 + }
638 +
351 639 public static function getEntry()
352 640 {
353 641 if (!is_object(static::$entry)) {
354 642 static::$entry = wpFluent()->table('fluentform_submissions')->find(static::$entry);
@@ -358,9 +646,9 @@
358 646 }
359 647
360 648 protected static function getRequest()
361 649 {
362 - return App::make('request');
650 + return wpFluentForm('request');
363 651 }
364 652
365 653 protected static function getUserAgent()
366 654 {
@@ -378,6 +666,70 @@
378 666 }
379 667 $instance = new static();
380 668 return $instance;
381 669 }
670 +
671 + public static function getInputs()
672 + {
673 + return static::$store['original_inputs'];
674 + }
675 +
676 + /**
677 + * Get the entry UID link for a submission
678 + *
679 + * @param object $entry
680 + * @return string
681 + */
682 + protected static function getEntryUidLink($entry)
683 + {
684 + // Check if entry already has the entry_uid_link property
685 + if (isset($entry->entry_uid_link)) {
686 + return $entry->entry_uid_link;
687 + }
688 +
689 + // Check if front-end entry view is enabled for this form
690 + $frontEndSettings = Helper::getFormMeta($entry->form_id, 'front_end_entry_view', []);
691 + if (ArrayHelper::get($frontEndSettings, 'status') !== 'yes') {
692 + return '';
693 + }
694 +
695 + // Get the UID hash from submission meta
696 + $meta = wpFluent()->table('fluentform_submission_meta')
697 + ->where('response_id', $entry->id)
698 + ->where('meta_key', '_entry_uid_hash')
699 + ->first();
700 +
701 + if (!$meta || !$meta->value) {
702 + return '';
703 + }
704 +
705 + // Generate the link
706 + return site_url('?ff_entry=1&hash=' . $meta->value);
707 + }
708 +
709 + protected static function getShortEntryUid($entry)
710 + {
711 + if (empty($entry->id)) {
712 + return '';
713 + }
714 +
715 + $entryId = strtoupper(base_convert((string) absint($entry->id), 10, 36));
716 + $entryHash = SubmissionMeta::retrieve('_entry_uid_hash', $entry->id);
717 +
718 + if (!$entryHash) {
719 + return $entryId;
720 + }
721 +
722 + return $entryId . '-' . strtoupper(substr($entryHash, 0, 4));
723 + }
724 +
725 + public static function resetData()
726 + {
727 + self::$form = null;
728 + self::$entry = null;
729 + self::$browser = null;
730 + self::$formFields = null;
731 +
732 + FormFieldsParser::resetData();
733 + FormDataParser::resetData();
734 + }
382 735 }
383 -