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/EditorShortcodeParser.php +239 -79 3.6.60 → 6.2.15 View file →
@@ -1,16 +1,16 @@
1 1 <?php
2 2
3 3 namespace FluentForm\App\Services\FormBuilder;
4 4
5 +use FluentForm\App\Helpers\Helper;
5 6 use FluentForm\App\Services\Browser\Browser;
6 -use FluentForm\Framework\Helpers\ArrayHelper;
7 -use FluentForm\Request;
8 7
9 8 class EditorShortcodeParser
10 9 {
11 10 /**
12 11 * Available dynamic short codes
12 + *
13 13 * @var null
14 14 */
15 15 private static $dynamicShortcodes = null;
16 16
@@ -15,8 +15,9 @@
15 15 private static $dynamicShortcodes = null;
16 16
17 17 /**
18 18 * mappings of methods to parse the shortcode
19 + *
19 20 * @var array
20 21 */
21 22 private static $handlers = [
22 23 'ip' => 'parseIp',
@@ -41,24 +42,26 @@
41 42
42 43 'browser.name' => 'parseBrowserProperties',
43 44 'browser.platform' => 'parseBrowserProperties',
44 45
45 - 'get.param_name' => 'parseQueryParam'
46 + 'get.param_name' => 'parseRequestParam',
47 + 'random_string.param_name' => 'parseRandomString',
46 48 ];
47 49
48 50 /**
49 51 * Filter dynamic shortcodes in input value
50 - * @param string $value
52 + *
53 + * @param string $value
54 + *
51 55 * @return string
52 56 */
53 57 public static function filter($value, $form)
54 58 {
55 - if(strpos($value, '{ ') === 0) {
59 + if (0 === strpos($value, '{ ')) {
56 60 // it's the css
57 61 return $value;
58 62 }
59 63
60 -
61 64 if (is_null(static::$dynamicShortcodes)) {
62 65 static::$dynamicShortcodes = fluentFormEditorShortCodes();
63 66 }
64 67
@@ -69,65 +72,88 @@
69 72 return call_user_func_array(
70 73 [__CLASS__, static::$handlers[$handler]],
71 74 ['{' . $handler . '}', $form]
72 75 );
73 - } elseif (strpos($handler, 'get.') !== false) {
74 - return static::parseQueryParam($handler);
75 - } else if (strpos($handler, 'user.meta.') !== false) {
76 - $key = substr(str_replace(['{', '}'], '', $value), 10);
77 - $user = wp_get_current_user();
78 - if ($user) {
79 - $value = get_post_meta($user->ID, $key, true);
80 - if (!is_array($value) && !is_object($value)) {
81 - return $value;
82 - }
83 - }
84 - return '';
85 - } else if (strpos($handler, 'user.') !== false) {
86 - $value = self::parseUserProperties($handler);
87 - if (is_array($value) || is_object($value)) {
76 + }
77 +
78 + if (false !== strpos($handler, 'get.')) {
79 + return static::parseRequestParam($handler);
80 + }
81 + if (false !== strpos($handler, 'random_string.')) {
82 + return static::parseRandomString($handler);
83 + }
84 +
85 + if (false !== strpos($handler, 'user.')) {
86 + $parsedValue = self::parseUserProperties($handler);
87 + if (is_array($parsedValue) || is_object($parsedValue)) {
88 88 return '';
89 89 }
90 - return $value;
91 - } else if (strpos($handler, 'date.') !== false) {
92 - return self::parseDate($handler);
93 - } else if (strpos($handler, 'embed_post.meta.') !== false) {
90 + return esc_html($parsedValue);
91 + }
92 +
93 + if (false !== strpos($handler, 'date.')) {
94 + return esc_html(self::parseDate($handler));
95 + }
96 +
97 + if (false !== strpos($handler, 'embed_post.meta.')) {
94 98 $key = substr(str_replace(['{', '}'], '', $value), 16);
95 99 global $post;
96 100 if ($post) {
97 - $value = get_post_meta($post->ID, $key, true);
98 - if (!is_array($value) && !is_object($value)) {
99 - return $value;
101 + $metaValue = get_post_meta($post->ID, $key, true);
102 + if (!is_array($metaValue) && !is_object($metaValue)) {
103 + return esc_html($metaValue);
100 104 }
101 105 }
102 106 return '';
103 - } else if (strpos($handler, 'embed_post.') !== false) {
107 + }
108 +
109 + if (false !== strpos($handler, 'embed_post.')) {
104 110 return self::parsePostProperties($handler, $form);
105 - } else if (strpos($handler, 'cookie.') !== false) {
111 + }
112 +
113 + if (false !== strpos($handler, 'cookie.')) {
106 114 $scookieProperty = substr($handler, strlen('cookie.'));
107 - return ArrayHelper::get($_COOKIE, $scookieProperty);
108 - } else if (strpos($handler, 'dynamic.') !== false) {
115 + $cookieValue = array_key_exists($scookieProperty, $_COOKIE) ? sanitize_text_field(wp_unslash($_COOKIE[$scookieProperty])) : '';
116 +
117 + return static::escapeReflectedValue($cookieValue);
118 + }
119 +
120 + if (false !== strpos($handler, 'dynamic.')) {
109 121 $dynamicKey = substr($handler, strlen('dynamic.'));
110 122 // maybe has fallback value
111 123 $dynamicKey = explode('|', $dynamicKey);
112 124 $fallBack = '';
113 125 $ref = '';
114 - if(count($dynamicKey) > 1) {
126 + if (count($dynamicKey) > 1) {
115 127 $fallBack = $dynamicKey[1];
116 128 }
117 - $ref = $dynamicKey[0];
129 + if (isset($dynamicKey[0])) {
130 + $ref = $dynamicKey[0];
131 + }
118 132
119 - if($ref == 'payment_summary') {
120 - return '<div class="ff_dynamic_value ff_dynamic_payment_summary" data-ref="payment_summary"><div class="ff_payment_summary"></div><div class="ff_payment_summary_fallback">'.$fallBack.'</div></div>';
133 + if ('payment_summary' == $ref) {
134 + return fluentform_sanitize_html('<div class="ff_dynamic_value ff_dynamic_payment_summary" data-ref="payment_summary"><div class="ff_payment_summary"></div><div class="ff_payment_summary_fallback">' . $fallBack . '</div></div>');
121 135 }
122 136
123 - return '<span class="ff_dynamic_value" data-ref="'.$ref.'" data-fallback="'.$fallBack.'">'.$fallBack.'</span>';
124 - } else {
125 - // This can be the css
126 - $handlerValue = apply_filters('fluentform_editor_shortcode_callback_' . $handler, '{' . $handler . '}', $form);
127 - // In not found then return the original please
128 - $filteredValue = $handlerValue;
137 + return fluentform_sanitize_html('<span class="ff_dynamic_value" data-ref="' . $ref . '" data-fallback="' . $fallBack . '">' . $fallBack . '</span>');
129 138 }
139 +
140 + // if it's multi line then just return
141 + if (false !== strpos($handler, PHP_EOL)) { // most probably it's a css
142 + return '{' . $handler . '}';
143 + }
144 +
145 + $handlerArray = explode('.', $handler);
146 +
147 + if (count($handlerArray) > 1) {
148 + // it's a grouped handler
149 + $group = array_shift($handlerArray);
150 + $parsedValue = apply_filters('fluentform_editor_shortcode_callback_group_' . $group, '{' . $handler . '}', $form, $handlerArray);
151 + return apply_filters('fluentform/editor_shortcode_callback_group_' . $group, $parsedValue, $form, $handlerArray);
152 + }
153 +
154 + $parsedValue = apply_filters('fluentform_editor_shortcode_callback_' . $handler, '{' . $handler . '}', $form);
155 + return apply_filters('fluentform/editor_shortcode_callback_' . $handler, $parsedValue, $form);
130 156 }
131 157
132 158 return $filteredValue;
133 159 }
@@ -132,10 +158,60 @@
132 158 return $filteredValue;
133 159 }
134 160
135 161 /**
162 + * Parse request query param.
163 + *
164 + * @param string $value
165 + * @param \stdClass $form
166 + *
167 + * @return string
168 + */
169 + public static function parseRequestParam($value)
170 + {
171 + $exploded = explode('.', $value);
172 + $param = array_pop($exploded);
173 + $value = wpFluentForm('request')->get($param);
174 +
175 + if (null === $value || '' === $value) {
176 + return '';
177 + }
178 +
179 + return static::escapeReflectedValue(Helper::flattenRequestValue($value));
180 + }
181 +
182 + /**
183 + * Escape a visitor-supplied value ({get.x}, {cookie.x}) for the assembled form HTML.
184 + *
185 + * Smartcodes are substituted after Custom HTML was sanitized, so the value can land in
186 + * any attribute, including an iframe src or anchor href. esc_attr() leaves a javascript:
187 + * scheme intact and keeps existing entities (?p=java&#9;script:...), so encode every
188 + * ampersand and drop values that would resolve to a script-capable URL.
189 + *
190 + * @param string $value
191 + *
192 + * @return string
193 + */
194 + public static function escapeReflectedValue($value)
195 + {
196 + $value = wp_check_invalid_utf8((string) $value);
197 +
198 + // Browsers strip control chars and whitespace from URLs before reading the scheme.
199 + $scheme = strtolower(preg_replace('/[\x00-\x20]+/', '', $value));
200 +
201 + if (preg_match('/^(javascript|vbscript|data):/', $scheme)) {
202 + return '';
203 + }
204 +
205 + // Encode braces too, so the value cannot plant a smartcode for a later replacement pass.
206 + return str_replace(['{', '}'], ['&#123;', '&#125;'], htmlspecialchars($value, ENT_QUOTES, 'UTF-8', true));
207 + }
208 +
209 + /**
136 210 * Parse the curly braced shortcode into array
137 - * @param string $value
211 + *
212 + * @param string $value
213 + *
138 214 * @return mixed
139 215 */
140 216 public static function parseValue($value)
141 217 {
@@ -142,9 +218,9 @@
142 218 if (!is_array($value)) {
143 219 return preg_split(
144 220 '/{(.*?)}/',
145 221 $value,
146 - null,
222 + -1,
147 223 PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
148 224 );
149 225 }
150 226
@@ -156,9 +232,11 @@
156 232 */
157 233
158 234 /**
159 235 * Parse loggedin user properties
160 - * @param string $value
236 + *
237 + * @param string $value
238 + *
161 239 * @return string
162 240 */
163 241 private static function parseUserProperties($value, $form = null)
164 242 {
@@ -163,9 +241,29 @@
163 241 private static function parseUserProperties($value, $form = null)
164 242 {
165 243 if ($user = wp_get_current_user()) {
166 244 $prop = substr(str_replace(['{', '}'], '', $value), 5);
167 - return $user->{$prop};
245 +
246 + if (false !== strpos($prop, 'meta.')) {
247 + $metaKey = substr($prop, strlen('meta.'));
248 + $metaKey = sanitize_text_field($metaKey);
249 + if (empty($metaKey) || ShortCodeParser::isDeniedUserProperty($metaKey)) {
250 + return '';
251 + }
252 + $userId = $user->ID;
253 + $data = get_user_meta($userId, $metaKey, true);
254 + $data = Helper::safeUnserialize($data);
255 + if (!is_array($data)) {
256 + return esc_html($data);
257 + }
258 + return esc_html(implode(',', $data));
259 + }
260 +
261 + if (ShortCodeParser::isDeniedUserProperty($prop)) {
262 + return '';
263 + }
264 +
265 + return esc_html($user->{$prop});
168 266 }
169 267
170 268 return '';
171 269 }
@@ -170,10 +268,12 @@
170 268 return '';
171 269 }
172 270
173 271 /**
174 - * Parse loggedin user properties
175 - * @param string $value
272 + * Parse embedded post properties
273 + *
274 + * @param string $value
275 + *
176 276 * @return string
177 277 */
178 278 private static function parsePostProperties($value, $form = null)
179 279 {
@@ -181,37 +281,70 @@
181 281 if (!$post) {
182 282 return '';
183 283 }
184 284
185 - $prop = substr(str_replace(['{', '}'], '', $value), 11);
186 - if ($prop == 'permalink') {
187 - return htmlspecialchars(site_url(wp_unslash($_SERVER['REQUEST_URI'])));
285 + $key = $prop = substr(str_replace(['{', '}'], '', $value), 11);
286 +
287 + if (false !== strpos($key, 'author.')) {
288 + $authorProperty = substr($key, strlen('author.'));
289 + $authorId = $post->post_author;
290 + if ($authorId && !ShortCodeParser::isDeniedUserProperty($authorProperty)) {
291 + $data = get_the_author_meta($authorProperty, $authorId);
292 + if (!is_array($data)) {
293 + return esc_html($data);
294 + }
295 + }
296 + return '';
297 + } elseif (false !== strpos($key, 'meta.')) {
298 + $metaKey = substr($key, strlen('meta.'));
299 + $postId = $post->ID;
300 + $data = get_post_meta($postId, $metaKey, true);
301 + if (!is_array($data)) {
302 + return esc_html($data);
303 + }
304 + return '';
305 + } elseif (false !== strpos($key, 'acf.')) {
306 + $metaKey = substr($key, strlen('acf.'));
307 + $postId = $post->ID;
308 + if (function_exists('get_field')) {
309 + $data = get_field($metaKey, $postId, true);
310 + if (!is_array($data)) {
311 + return esc_html($data);
312 + }
313 + return '';
314 + }
188 315 }
189 - if (property_exists($post, $prop)) {
190 - return $post->{$prop};
316 +
317 + if ('permalink' == $prop) {
318 + return site_url(esc_attr(urldecode(wpFluentForm('request')->server('REQUEST_URI'))));
191 319 }
320 +
321 + if ('post_password' !== $prop && property_exists($post, $prop)) {
322 + return esc_html($post->{$prop});
323 + }
192 324 return '';
193 325 }
194 326
195 -
196 327 /**
197 328 * Parse WP Properties
198 - * @param string $value
329 + *
330 + * @param string $value
331 + *
199 332 * @return string
200 333 */
201 334 private static function parseWPProperties($value, $form = null)
202 335 {
203 - if ($value == '{wp.admin_email}') {
204 - return get_option('admin_email');
336 + if ('{wp.admin_email}' == $value) {
337 + return esc_html(get_option('admin_email'));
205 338 }
206 - if ($value == '{wp.site_url}') {
207 - return site_url();
339 + if ('{wp.site_url}' == $value) {
340 + return esc_url(site_url());
208 341 }
209 - if ($value == '{wp.site_title}') {
210 - return get_option('blogname');
342 + if ('{wp.site_title}' == $value) {
343 + return esc_html(get_option('blogname'));
211 344 }
212 - if ($value == '{http_referer}') {
213 - return wp_get_referer();
345 + if ('{http_referer}' == $value) {
346 + return esc_url(wp_get_referer());
214 347 }
215 348
216 349 return '';
217 350 }
@@ -217,18 +350,20 @@
217 350 }
218 351
219 352 /**
220 353 * Parse browser/user-agent properties
221 - * @param string $value
354 + *
355 + * @param string $value
356 + *
222 357 * @return string
223 358 */
224 359 private static function parseBrowserProperties($value, $form = null)
225 360 {
226 - $browser = new Browser;
227 - if ($value == '{browser.name}') {
228 - return $browser->getBrowser();
229 - } elseif ($value == '{browser.platform}') {
230 - return $browser->getPlatform();
361 + $browser = new Browser();
362 + if ('{browser.name}' == $value) {
363 + return esc_html($browser->getBrowser());
364 + } elseif ('{browser.platform}' == $value) {
365 + return esc_html($browser->getPlatform());
231 366 }
232 367
233 368 return '';
234 369 }
@@ -234,20 +369,25 @@
234 369 }
235 370
236 371 /**
237 372 * Parse ip shortcode
238 - * @param string $value
373 + *
374 + * @param string $value
375 + *
239 376 * @return string
240 377 */
241 378 private static function parseIp($value, $form = null)
242 379 {
243 - $ip = Request::getIp();
244 - return $ip ? $ip : $value;
380 + $rawIp = wpFluentForm('request')->getIp();
381 + $ip = sanitize_text_field($rawIp);
382 + return $ip ? esc_html($ip) : $value;
245 383 }
246 384
247 385 /**
248 386 * Parse date shortcode
249 - * @param string $value
387 + *
388 + * @param string $value
389 + *
250 390 * @return string
251 391 */
252 392 private static function parseDate($value, $form = null)
253 393 {
@@ -252,16 +392,17 @@
252 392 private static function parseDate($value, $form = null)
253 393 {
254 394 $format = substr(str_replace(['}', '{'], '', $value), 5);
255 395 $date = date($format, strtotime(current_time('mysql')));
256 - return $date ? $date : '';
396 + return $date ? esc_html($date) : '';
257 397 }
258 398
259 399 /**
260 400 * Parse request query param.
261 401 *
262 - * @param string $value
263 - * @param \stdClass $form
402 + * @param string $value
403 + * @param \stdClass $form
404 + *
264 405 * @return string
265 406 */
266 407 public static function parseQueryParam($value)
267 408 {
@@ -266,14 +407,33 @@
266 407 public static function parseQueryParam($value)
267 408 {
268 409 $exploded = explode('.', $value);
269 410 $param = array_pop($exploded);
270 - if(!isset($_REQUEST[$param])) {
411 + $value = wpFluentForm('request')->get($param);
412 +
413 + if (!$value) {
271 414 return '';
272 415 }
273 - $value = $_REQUEST[$param];
274 - if(is_array($value)) {
416 +
417 + if (is_array($value)) {
275 418 return sanitize_textarea_field(implode(', ', $value));
276 419 }
420 +
277 421 return sanitize_textarea_field($value);
422 + }
423 +
424 + /**
425 + * Generate random a string with prefix
426 + *
427 + * @param $value
428 + *
429 + * @return string
430 + */
431 + public static function parseRandomString($value)
432 + {
433 + $exploded = explode('.', $value);
434 + $prefix = array_pop($exploded);
435 + $value = $prefix . uniqid();
436 +
437 + return esc_html(apply_filters('fluentform/shortcode_parser_callback_random_string', $value, $prefix, new static()));
278 438 }
279 439 }