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 +472 -95 3.6.416.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 +use FluentForm\App\Modules\Form\FormDataParser;
7 +use FluentForm\App\Modules\Form\FormFieldsParser;
6 8 use FluentForm\App\Services\Browser\Browser;
7 9 use FluentForm\Framework\Helpers\ArrayHelper;
8 -use FluentForm\App\Modules\Form\FormDataParser;
9 -use FluentForm\App\Modules\Form\FormFieldsParser;
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,32 +20,32 @@
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 - 'inputs' => null,
27 + 'inputs' => null,
23 28 'original_inputs' => null,
24 - 'user' => null,
25 - 'post' => null,
26 - 'other' => null,
27 - 'submission' => null
29 + 'user' => null,
30 + 'post' => null,
31 + 'other' => null,
32 + 'submission' => null,
28 33 ];
29 34
30 - public static function parse($parsable, $entryId, $data = [], $form = null, $isUrl = false)
35 + public static function parse($parsable, $entryId, $data = [], $form = null, $isUrl = false, $providerOrIsHTML = false, $htmlSanitized = false)
31 36 {
32 37 try {
33 - $entryId = (int)$entryId;
38 + static::setDependencies($entryId, $data, $form, $providerOrIsHTML);
34 39
35 - static::setDependencies($entryId, $data, $form);
36 -
37 40 if (is_array($parsable)) {
38 - return static::parseShortCodeFromArray($parsable, $isUrl);
41 + return static::parseShortCodeFromArray($parsable, $isUrl, $providerOrIsHTML, $htmlSanitized);
39 42 }
40 43
41 - return static::parseShortCodeFromString($parsable, $isUrl);
42 -
44 + return static::parseShortCodeFromString($parsable, $isUrl, $providerOrIsHTML, $htmlSanitized);
43 45 } catch (\Exception $e) {
44 - if(defined('WP_DEBUG') && WP_DEBUG) {
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
45 48 error_log($e->getTraceAsString());
46 49 }
47 50 return '';
48 51 }
@@ -47,13 +50,14 @@
47 50 return '';
48 51 }
49 52 }
50 53
51 - protected static function setDependencies($entry, $data, $form)
54 + protected static function setDependencies($entry, $data, $form, $provider)
52 55 {
53 56 static::setEntry($entry);
54 57 static::setData($data);
55 58 static::setForm($form);
59 + static::$provider = $provider;
56 60 }
57 61
58 62 protected static function setEntry($entry)
59 63 {
@@ -80,15 +84,30 @@
80 84 static::$form = static::getEntry()->form_id;
81 85 }
82 86 }
83 87
84 - protected static function parseShortCodeFromArray($parsable, $isUrl = false)
88 + protected static function parseShortCodeFromArray($parsable, $isUrl = false, $provider = false, $htmlSanitized = false)
85 89 {
86 90 foreach ($parsable as $key => $value) {
87 91 if (is_array($value)) {
88 - $parsable[$key] = static::parseShortCodeFromArray($value, $isUrl);
92 + $parsable[$key] = static::parseShortCodeFromArray($value, $isUrl, $provider, $htmlSanitized);
89 93 } else {
90 - $parsable[$key] = static::parseShortCodeFromString($value, $isUrl);
94 + $isHtml = false;
95 + if ($provider) {
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);
108 + }
109 + $parsable[$key] = static::parseShortCodeFromString($value, $isUrl, $isHtml, $htmlSanitized);
91 110 }
92 111 }
93 112
94 113 return $parsable;
@@ -93,108 +112,225 @@
93 112
94 113 return $parsable;
95 114 }
96 115
97 - protected static function parseShortCodeFromString($parsable, $isUrl = false)
116 + protected static function parseShortCodeFromString($parsable, $isUrl = false, $isHtml = false, $htmlSanitized = false)
98 117 {
99 - if(!$parsable) {
118 + if ('0' === $parsable) {
119 + return $parsable;
120 + }
121 +
122 + if (!$parsable) {
100 123 return '';
101 124 }
102 - return preg_replace_callback('/{+(.*?)}/', function ($matches) use ($isUrl) {
125 + return preg_replace_callback('/{+(.*?)}/', function ($matches) use ($isUrl, $isHtml, $htmlSanitized) {
103 126 $value = '';
104 - if (strpos($matches[1], 'inputs.') !== false) {
127 + if (false !== strpos($matches[1], 'inputs.')) {
105 128 $formProperty = substr($matches[1], strlen('inputs.'));
106 - $value = static::getFormData($formProperty);
107 - } elseif (strpos($matches[1], 'user.') !== false) {
129 + $value = static::getFormData($formProperty, $isHtml);
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.')) {
108 134 $userProperty = substr($matches[1], strlen('user.'));
109 135 $value = static::getUserData($userProperty);
110 - } elseif (strpos($matches[1], 'embed_post.') !== false) {
136 + } elseif (false !== strpos($matches[1], 'embed_post.')) {
111 137 $postProperty = substr($matches[1], strlen('embed_post.'));
112 138 $value = static::getPostData($postProperty);
113 - } elseif (strpos($matches[1], 'wp.') !== false) {
139 + } elseif (false !== strpos($matches[1], 'wp.')) {
114 140 $wpProperty = substr($matches[1], strlen('wp.'));
115 141 $value = static::getWPData($wpProperty);
116 - } elseif (strpos($matches[1], 'submission.') !== false) {
142 + } elseif (false !== strpos($matches[1], 'submission.')) {
117 143 $submissionProperty = substr($matches[1], strlen('submission.'));
118 144 $value = static::getSubmissionData($submissionProperty);
119 - } elseif (strpos($matches[1], 'cookie.') !== false) {
145 + } elseif (false !== strpos($matches[1], 'cookie.')) {
120 146 $scookieProperty = substr($matches[1], strlen('cookie.'));
121 - $value = ArrayHelper::get($_COOKIE, $scookieProperty);
122 - } 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.')) {
123 149 $property = substr($matches[1], strlen('payment.'));
124 - $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());
125 162 } else {
126 163 $value = static::getOtherData($matches[1]);
127 164 }
128 165
129 - if(is_array($value)) {
166 + if (is_array($value)) {
130 167 $value = fluentImplodeRecursive(', ', $value);
131 168 }
132 169
133 - if($isUrl) {
134 - $value = urlencode($value);
170 + if ($isUrl) {
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);
135 177 }
136 178
137 179 return $value;
138 -
139 180 }, $parsable);
140 181 }
141 182
142 - protected static function getFormData($key)
183 + protected static function getFormData($key, $isHtml = false)
143 184 {
144 - if(strpos($key, '.label')) {
185 + if (strpos($key, '.label')) {
145 186 $key = str_replace('.label', '', $key);
146 - $value = ArrayHelper::get(static::$store['original_inputs'], $key);
147 - $field = ArrayHelper::get(static::$formFields, $key, '');
148 - return static::$store['inputs'][$key] = apply_filters(
149 - 'fluentform_response_render_' . $field['element'],
150 - static::$store['inputs'][$key],
151 - $field,
152 - static::getForm()->id,
153 - true
154 - );
187 + $isHtml = true;
155 188 }
156 189
190 + if (strpos($key, '.value')) {
191 + $key = str_replace('.value', '', $key);
192 + return ArrayHelper::get(static::$store['original_inputs'], $key);
193 + }
194 +
157 195 if (strpos($key, '.') && !isset(static::$store['inputs'][$key])) {
158 - if (!isset(static::$store['inputs'][$key])) {
159 - static::$store['inputs'][$key] = ArrayHelper::get(
160 - static::$store['original_inputs'], $key, ''
161 - );
162 - }
196 + return ArrayHelper::get(
197 + static::$store['original_inputs'],
198 + $key,
199 + ''
200 + );
163 201 }
164 202
165 203 if (!isset(static::$store['inputs'][$key])) {
166 204 static::$store['inputs'][$key] = ArrayHelper::get(
167 - static::$store['inputs'], $key, ''
205 + static::$store['inputs'],
206 + $key,
207 + ''
168 208 );
169 209 }
170 210
171 211 if (is_null(static::$formFields)) {
172 212 static::$formFields = FormFieldsParser::getShortCodeInputs(
173 - static::getForm(), ['admin_label', 'attributes', 'options', 'raw']
213 + static::getForm(),
214 + ['admin_label', 'attributes', 'options', 'raw']
174 215 );
175 216 }
176 217
177 218 $field = ArrayHelper::get(static::$formFields, $key, '');
178 -
179 219
180 - if (!$field) return '';
181 -
220 + if (!$field) {
221 + return '';
222 + }
223 +
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 + );
238 + return apply_filters(
239 + 'fluentform/response_render_' . $field['element'],
240 + $originalInput,
241 + $field,
242 + static::getForm()->id,
243 + $isHtml
244 + );
245 + }
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 +
182 260 return static::$store['inputs'][$key] = apply_filters(
183 - 'fluentform_response_render_' . $field['element'],
261 + 'fluentform/response_render_' . $field['element'],
184 262 static::$store['inputs'][$key],
185 263 $field,
186 264 static::getForm()->id,
187 - false
265 + $isHtml
188 266 );
189 267 }
190 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 +
191 302 protected static function getUserData($key)
192 303 {
193 304 if (is_null(static::$store['user'])) {
194 305 static::$store['user'] = wp_get_current_user();
195 306 }
196 - 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 '';
197 333 }
198 334
199 335 protected static function getPostData($key)
200 336 {
@@ -200,35 +336,38 @@
200 336 {
201 337 if (is_null(static::$store['post'])) {
202 338 $postId = static::$store['inputs']['__fluent_form_embded_post_id'];
203 339 static::$store['post'] = get_post($postId);
340 + if (is_null(static::$store['post'])) {
341 + return '';
342 + }
204 343 static::$store['post']->permalink = get_the_permalink(static::$store['post']);
205 344 }
206 345
207 - if (strpos($key, 'author.') !== false) {
346 + if (false !== strpos($key, 'author.')) {
208 347 $authorProperty = substr($key, strlen('author.'));
209 348 $authorId = static::$store['post']->post_author;
210 - if ($authorId) {
211 - $data = get_the_author_meta($authorProperty, $authorId);
212 - if(!is_array($data)) {
349 + if ($authorId && !static::isDeniedUserProperty($authorProperty)) {
350 + $data = get_the_author_meta($authorProperty, $authorId);
351 + if (!is_array($data)) {
213 352 return $data;
214 353 }
215 354 }
216 355 return '';
217 - } else if(strpos($key, 'meta.') !== false) {
356 + } elseif (false !== strpos($key, 'meta.')) {
218 357 $metaKey = substr($key, strlen('meta.'));
219 358 $postId = static::$store['post']->ID;
220 359 $data = get_post_meta($postId, $metaKey, true);
221 - if(!is_array($data)) {
360 + if (!is_array($data)) {
222 361 return $data;
223 362 }
224 363 return '';
225 - } else if(strpos($key, 'acf.') !== false) {
364 + } elseif (false !== strpos($key, 'acf.')) {
226 365 $metaKey = substr($key, strlen('acf.'));
227 366 $postId = static::$store['post']->ID;
228 - if(function_exists('get_field')) {
229 - $data = get_field($metaKey, $postId, true);
230 - if(!is_array($data)) {
367 + if (function_exists('get_field')) {
368 + $data = get_field($metaKey, $postId, true);
369 + if (!is_array($data)) {
231 370 return $data;
232 371 }
233 372 return '';
234 373 }
@@ -233,20 +372,39 @@
233 372 return '';
234 373 }
235 374 }
236 375
376 + if ('post_password' === $key) {
377 + return '';
378 + }
379 +
237 380 return static::$store['post']->{$key};
238 381 }
239 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 +
240 398 protected static function getWPData($key)
241 399 {
242 - if ($key == 'admin_email') {
400 + if ('admin_email' == $key) {
243 401 return get_option('admin_email');
244 402 }
245 - if ($key == 'site_url') {
403 + if ('site_url' == $key) {
246 404 return site_url();
247 405 }
248 - if ($key == 'site_title') {
406 + if ('site_title' == $key) {
249 407 return get_option('blogname');
250 408 }
251 409 return $key;
252 410 }
@@ -253,20 +411,34 @@
253 411
254 412 protected static function getSubmissionData($key)
255 413 {
256 414 $entry = static::getEntry();
257 - if (property_exists($entry, $key)) {
258 - 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) {
259 424 return round($entry->{$key} / 100, 2);
260 425 }
426 + if ('payment_method' == $key && 'test' == $entry->{$key}) {
427 + return __('Offline', 'fluentform');
428 + }
261 429 return $entry->{$key};
262 430 }
263 - if($key == 'admin_view_url') {
264 - return admin_url('admin.php?page=fluent_forms&route=entries&form_id='.$entry->form_id.'#/entries/'.$entry->id);
265 - } else if(strpos($key, 'meta.') !== false) {
431 + if ('admin_view_url' == $key) {
432 + return admin_url('admin.php?page=fluent_forms&route=entries&form_id=' . $entry->form_id . '#/entries/' . $entry->id);
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.')) {
266 438 $metaKey = substr($key, strlen('meta.'));
267 - $data = App\Helpers\Helper::getSubmissionMeta($entry->id, $metaKey);
268 - if(!is_array($data)) {
439 + $data = Helper::getSubmissionMeta($entry->id, $metaKey);
440 + if (!is_array($data)) {
269 441 return $data;
270 442 }
271 443 return '';
272 444 }
@@ -275,43 +447,179 @@
275 447 }
276 448
277 449 protected static function getOtherData($key)
278 450 {
279 - if (strpos($key, 'date.') === 0) {
451 + if (0 === strpos($key, 'date.')) {
280 452 $format = str_replace('date.', '', $key);
281 453 return date($format, strtotime(current_time('mysql')));
282 - } elseif ($key == 'admin_email') {
454 + } elseif ('admin_email' == $key) {
283 455 return get_option('admin_email', false);
284 - } elseif ($key == 'ip') {
456 + } elseif ('ip' == $key) {
285 457 return static::getRequest()->getIp();
286 - } elseif ($key == 'browser.platform') {
458 + } elseif ('browser.platform' == $key) {
287 459 return static::getUserAgent()->getPlatform();
288 - } elseif ($key == 'browser.name') {
460 + } elseif ('browser.name' == $key) {
289 461 return static::getUserAgent()->getBrowser();
290 - } elseif ($key == 'all_data') {
462 + } elseif (in_array($key, ['all_data', 'all_data_without_hidden_fields'])) {
291 463 $formFields = FormFieldsParser::getEntryInputs(static::getForm());
292 464 $inputLabels = FormFieldsParser::getAdminLabels(static::getForm(), $formFields);
293 465 $response = FormDataParser::parseFormSubmission(static::getEntry(), static::getForm(), $formFields, true);
294 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 +
295 506 $html = '<table class="ff_all_data" width="600" cellpadding="0" cellspacing="0"><tbody>';
296 - foreach ($inputLabels as $key => $label) {
297 - if (array_key_exists($key, $response->user_inputs) && ArrayHelper::get($response->user_inputs, $key)) {
298 - $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);
299 510 if (is_array($data) || is_object($data)) {
300 511 continue;
301 512 }
513 + // $label is admin-set, $data is already sanitized via fluentFormSanitizer() on submission insert
302 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>';
303 515 }
304 516 }
517 +
305 518 $html .= '</tbody></table>';
306 - 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 '';
307 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 +
308 604 // This fallback actually
309 - $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 + );
310 615
616 + $handlerValue = apply_filters('fluentform/shortcode_parser_callback_' . $key, $handlerValue, static::getInstance());
617 +
311 618 if ($handlerValue) {
312 619 return $handlerValue;
313 620 }
621 +
314 622 return '';
315 623 }
316 624
317 625 public static function getForm()
@@ -322,8 +630,13 @@
322 630
323 631 return static::$form;
324 632 }
325 633
634 + public static function getProvider()
635 + {
636 + return static::$provider;
637 + }
638 +
326 639 public static function getEntry()
327 640 {
328 641 if (!is_object(static::$entry)) {
329 642 static::$entry = wpFluent()->table('fluentform_submissions')->find(static::$entry);
@@ -333,9 +646,9 @@
333 646 }
334 647
335 648 protected static function getRequest()
336 649 {
337 - return App::make('request');
650 + return wpFluentForm('request');
338 651 }
339 652
340 653 protected static function getUserAgent()
341 654 {
@@ -347,12 +660,76 @@
347 660
348 661 public static function getInstance()
349 662 {
350 663 static $instance;
351 - if($instance) {
664 + if ($instance) {
352 665 return $instance;
353 666 }
354 667 $instance = new static();
355 668 return $instance;
356 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 + }
357 735 }
358 -