PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.14
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.14
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 3.6.65 All 196 releases
← All changes | app/Services/FormBuilder/ShortCodeParser.php +428 -74 3.6.616.2.14 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);
@@ -167,33 +193,52 @@
167 193 }
168 194
169 195 if (strpos($key, '.') && !isset(static::$store['inputs'][$key])) {
170 196 return ArrayHelper::get(
171 - static::$store['original_inputs'], $key, ''
197 + static::$store['original_inputs'],
198 + $key,
199 + ''
172 200 );
173 201 }
174 202
175 203 if (!isset(static::$store['inputs'][$key])) {
176 204 static::$store['inputs'][$key] = ArrayHelper::get(
177 - static::$store['inputs'], $key, ''
205 + static::$store['inputs'],
206 + $key,
207 + ''
178 208 );
179 209 }
180 210
181 211 if (is_null(static::$formFields)) {
182 212 static::$formFields = FormFieldsParser::getShortCodeInputs(
183 - static::getForm(), ['admin_label', 'attributes', 'options', 'raw']
213 + static::getForm(),
214 + ['admin_label', 'attributes', 'options', 'raw']
184 215 );
185 216 }
186 217
187 218 $field = ArrayHelper::get(static::$formFields, $key, '');
188 219
220 + if (!$field) {
221 + return '';
222 + }
189 223
190 - if (!$field) return '';
191 -
192 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 + );
193 238 return apply_filters(
194 - 'fluentform_response_render_' . $field['element'],
195 - static::$store['original_inputs'][$key],
239 + 'fluentform/response_render_' . $field['element'],
240 + $originalInput,
196 241 $field,
197 242 static::getForm()->id,
198 243 $isHtml
199 244 );
@@ -198,10 +243,23 @@
198 243 $isHtml
199 244 );
200 245 }
201 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 +
202 260 return static::$store['inputs'][$key] = apply_filters(
203 - 'fluentform_response_render_' . $field['element'],
261 + 'fluentform/response_render_' . $field['element'],
204 262 static::$store['inputs'][$key],
205 263 $field,
206 264 static::getForm()->id,
207 265 $isHtml
@@ -207,14 +265,72 @@
207 265 $isHtml
208 266 );
209 267 }
210 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 +
211 302 protected static function getUserData($key)
212 303 {
213 304 if (is_null(static::$store['user'])) {
214 305 static::$store['user'] = wp_get_current_user();
215 306 }
216 - 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 '';
217 333 }
218 334
219 335 protected static function getPostData($key)
220 336 {
@@ -220,15 +336,18 @@
220 336 {
221 337 if (is_null(static::$store['post'])) {
222 338 $postId = static::$store['inputs']['__fluent_form_embded_post_id'];
223 339 static::$store['post'] = get_post($postId);
340 + if (is_null(static::$store['post'])) {
341 + return '';
342 + }
224 343 static::$store['post']->permalink = get_the_permalink(static::$store['post']);
225 344 }
226 345
227 - if (strpos($key, 'author.') !== false) {
346 + if (false !== strpos($key, 'author.')) {
228 347 $authorProperty = substr($key, strlen('author.'));
229 348 $authorId = static::$store['post']->post_author;
230 - if ($authorId) {
349 + if ($authorId && !static::isDeniedUserProperty($authorProperty)) {
231 350 $data = get_the_author_meta($authorProperty, $authorId);
232 351 if (!is_array($data)) {
233 352 return $data;
234 353 }
@@ -233,9 +352,9 @@
233 352 return $data;
234 353 }
235 354 }
236 355 return '';
237 - } else if (strpos($key, 'meta.') !== false) {
356 + } elseif (false !== strpos($key, 'meta.')) {
238 357 $metaKey = substr($key, strlen('meta.'));
239 358 $postId = static::$store['post']->ID;
240 359 $data = get_post_meta($postId, $metaKey, true);
241 360 if (!is_array($data)) {
@@ -241,9 +360,9 @@
241 360 if (!is_array($data)) {
242 361 return $data;
243 362 }
244 363 return '';
245 - } else if (strpos($key, 'acf.') !== false) {
364 + } elseif (false !== strpos($key, 'acf.')) {
246 365 $metaKey = substr($key, strlen('acf.'));
247 366 $postId = static::$store['post']->ID;
248 367 if (function_exists('get_field')) {
249 368 $data = get_field($metaKey, $postId, true);
@@ -253,20 +372,39 @@
253 372 return '';
254 373 }
255 374 }
256 375
376 + if ('post_password' === $key) {
377 + return '';
378 + }
379 +
257 380 return static::$store['post']->{$key};
258 381 }
259 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 +
260 398 protected static function getWPData($key)
261 399 {
262 - if ($key == 'admin_email') {
400 + if ('admin_email' == $key) {
263 401 return get_option('admin_email');
264 402 }
265 - if ($key == 'site_url') {
403 + if ('site_url' == $key) {
266 404 return site_url();
267 405 }
268 - if ($key == 'site_title') {
406 + if ('site_title' == $key) {
269 407 return get_option('blogname');
270 408 }
271 409 return $key;
272 410 }
@@ -273,22 +411,33 @@
273 411
274 412 protected static function getSubmissionData($key)
275 413 {
276 414 $entry = static::getEntry();
277 - if (property_exists($entry, $key)) {
278 - 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) {
279 424 return round($entry->{$key} / 100, 2);
280 425 }
281 - if ($key == 'payment_method' && $key == 'test') {
426 + if ('payment_method' == $key && 'test' == $entry->{$key}) {
282 427 return __('Offline', 'fluentform');
283 428 }
284 429 return $entry->{$key};
285 430 }
286 - if ($key == 'admin_view_url') {
431 + if ('admin_view_url' == $key) {
287 432 return admin_url('admin.php?page=fluent_forms&route=entries&form_id=' . $entry->form_id . '#/entries/' . $entry->id);
288 - } 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.')) {
289 438 $metaKey = substr($key, strlen('meta.'));
290 - $data = App\Helpers\Helper::getSubmissionMeta($entry->id, $metaKey);
439 + $data = Helper::getSubmissionMeta($entry->id, $metaKey);
291 440 if (!is_array($data)) {
292 441 return $data;
293 442 }
294 443 return '';
@@ -298,43 +447,179 @@
298 447 }
299 448
300 449 protected static function getOtherData($key)
301 450 {
302 - if (strpos($key, 'date.') === 0) {
451 + if (0 === strpos($key, 'date.')) {
303 452 $format = str_replace('date.', '', $key);
304 453 return date($format, strtotime(current_time('mysql')));
305 - } elseif ($key == 'admin_email') {
454 + } elseif ('admin_email' == $key) {
306 455 return get_option('admin_email', false);
307 - } elseif ($key == 'ip') {
456 + } elseif ('ip' == $key) {
308 457 return static::getRequest()->getIp();
309 - } elseif ($key == 'browser.platform') {
458 + } elseif ('browser.platform' == $key) {
310 459 return static::getUserAgent()->getPlatform();
311 - } elseif ($key == 'browser.name') {
460 + } elseif ('browser.name' == $key) {
312 461 return static::getUserAgent()->getBrowser();
313 - } elseif ($key == 'all_data') {
462 + } elseif (in_array($key, ['all_data', 'all_data_without_hidden_fields'])) {
314 463 $formFields = FormFieldsParser::getEntryInputs(static::getForm());
315 464 $inputLabels = FormFieldsParser::getAdminLabels(static::getForm(), $formFields);
316 465 $response = FormDataParser::parseFormSubmission(static::getEntry(), static::getForm(), $formFields, true);
317 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 +
318 506 $html = '<table class="ff_all_data" width="600" cellpadding="0" cellspacing="0"><tbody>';
319 - foreach ($inputLabels as $key => $label) {
320 - if (array_key_exists($key, $response->user_inputs) && ArrayHelper::get($response->user_inputs, $key)) {
321 - $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);
322 510 if (is_array($data) || is_object($data)) {
323 511 continue;
324 512 }
513 + // $label is admin-set, $data is already sanitized via fluentFormSanitizer() on submission insert
325 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>';
326 515 }
327 516 }
517 +
328 518 $html .= '</tbody></table>';
329 - 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 '';
330 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 +
331 604 // This fallback actually
332 - $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 + );
333 615
616 + $handlerValue = apply_filters('fluentform/shortcode_parser_callback_' . $key, $handlerValue, static::getInstance());
617 +
334 618 if ($handlerValue) {
335 619 return $handlerValue;
336 620 }
621 +
337 622 return '';
338 623 }
339 624
340 625 public static function getForm()
@@ -345,8 +630,13 @@
345 630
346 631 return static::$form;
347 632 }
348 633
634 + public static function getProvider()
635 + {
636 + return static::$provider;
637 + }
638 +
349 639 public static function getEntry()
350 640 {
351 641 if (!is_object(static::$entry)) {
352 642 static::$entry = wpFluent()->table('fluentform_submissions')->find(static::$entry);
@@ -356,9 +646,9 @@
356 646 }
357 647
358 648 protected static function getRequest()
359 649 {
360 - return App::make('request');
650 + return wpFluentForm('request');
361 651 }
362 652
363 653 protected static function getUserAgent()
364 654 {
@@ -376,6 +666,70 @@
376 666 }
377 667 $instance = new static();
378 668 return $instance;
379 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 + }
380 735 }
381 -