PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
← All changes | includes/Core/Util/FrontendHelpers.php +221 -7 2.10.03.3.1 View file →
@@ -1,8 +1,9 @@
1 1 <?php
2 2
3 3 namespace BitCode\BitForm\Core\Util;
4 4
5 +use BitCode\BitForm\Core\Form\FormManager;
5 6 use WP_Rewrite;
6 7
7 8 final class FrontendHelpers
8 9 {
@@ -7,9 +8,12 @@
7 8 final class FrontendHelpers
8 9 {
9 10 public static $isPageBuilder = false;
10 11 public static $bfFrontendFormIds = [];
12 + public static $bfFrontendViewIds = [];
11 13 public static $bfFormIdsFromPost = [];
14 + public static $bfViewIdsFromPost = [];
15 + private static $formsPermissions = [];
12 16
13 17 public static $pageBuilderQueryParamsList = [
14 18 'et_pb_preview' => 'true', // divi
15 19 'vc_editable' => 'true', // wp bakery
@@ -33,10 +37,15 @@
33 37 return [];
34 38 }
35 39 $postId = $post->ID;
36 40 $shortcodeFormIds = [];
37 - // $bfMetaValues = $wpdb->get_results("SELECT pmt1.meta_value FROM wp_postmeta pmt1 LEFT OUTER JOIN wp_postmeta pmt2 ON (pmt1.meta_id < pmt2.meta_id AND pmt1.meta_key = pmt2.meta_key) WHERE pmt2.meta_id IS NULL AND pmt1.post_id = {$postId} AND pmt1.meta_value LIKE '%[bitform%' ORDER BY pmt1.meta_id DESC");
38 - $bfMetaValues = $wpdb->get_results('SELECT meta_value FROM `' . $wpdb->postmeta . "` WHERE `post_id`={$postId}");
41 + // postmeta table name from $wpdb->postmeta (WordPress-managed, not user input). post_id parameterized via %d.
42 + $bfMetaValues = $wpdb->get_results(
43 + $wpdb->prepare(
44 + 'SELECT meta_value FROM `' . $wpdb->postmeta . '` WHERE `post_id`=%d',
45 + $postId
46 + )
47 + );
39 48 $postContent = $post->post_content;
40 49 $bfMetaValues[] = (object) ['meta_value' => $postContent];
41 50 foreach ($bfMetaValues as $bfShortcut) {
42 51 $meta_value = (is_string($bfShortcut->meta_value) && !empty($bfShortcut->meta_value)) ? $bfShortcut->meta_value : '';
@@ -49,14 +58,73 @@
49 58 }
50 59
51 60 public static function getShortCodeIds($content = '')
52 61 {
53 - \preg_match_all("/\[bitform\s+id\s*=\s*('|\")\s*(\d+)\s*('|\")\]/", $content, $shortCode);
54 - $ids = $shortCode[2];
62 + $pattern = '/' . get_shortcode_regex(['bitform']) . '/';
63 + \preg_match_all($pattern, $content, $short);
55 64
56 - return $ids;
65 + $formIds = [];
66 + foreach ($short[3] as $attr_string) {
67 + $attr = shortcode_parse_atts($attr_string);
68 + if (!empty($attr['id'])) {
69 + $formIds[] = $attr['id'];
70 + }
71 + }
72 +
73 + // Regex handles:
74 + // 1. [bitform ... id=... ]
75 + // 2. id="123" or id='123' or id=123
76 + // 3. Escaped quotes id=\"123\" or id=\'123\' (common in builder meta)
77 + // \preg_match_all('/\[bitform\s+\b[^\]]*\bid\s*=\s*(?:\\\\?[\'"])?(\d+)(?:\\\\?[\'"])?[^\]]*\]/', $content, $shortCode);
78 + // $ids = $shortCode[1];
79 +
80 + return $formIds;
57 81 }
58 82
83 + public static function getViewIdsFromPost()
84 + {
85 + global $post;
86 + global $wpdb;
87 + if (empty($post)) {
88 + self::$bfViewIdsFromPost = [];
89 + return [];
90 + }
91 + $postId = $post->ID;
92 + $shortcodeViewIds = [];
93 + // postmeta table name from $wpdb->postmeta (WordPress-managed, not user input). post_id parameterized via %d.
94 + $bfMetaValues = $wpdb->get_results($wpdb->prepare('SELECT meta_value FROM `' . $wpdb->postmeta . '` WHERE `post_id` = %d', $postId));
95 + $postContent = $post->post_content;
96 +
97 + $bfMetaValues[] = (object) ['meta_value' => $postContent];
98 + foreach ($bfMetaValues as $bfShortcut) {
99 + $meta_value = (is_string($bfShortcut->meta_value) && !empty($bfShortcut->meta_value)) ? $bfShortcut->meta_value : '';
100 + $shortcodeIds = self::getViewShortCodeIds($meta_value);
101 + $shortcodeViewIds = array_merge($shortcodeViewIds, $shortcodeIds);
102 + }
103 +
104 + self::$bfViewIdsFromPost = $shortcodeViewIds;
105 + return $shortcodeViewIds;
106 + }
107 +
108 + public static function getViewShortCodeIds($content = '')
109 + {
110 + $pattern = '/' . get_shortcode_regex(['bitform-view']) . '/';
111 + \preg_match_all($pattern, $content, $short);
112 +
113 + $viewIds = [];
114 +
115 + foreach ($short[3] as $attr_string) {
116 + $attr = shortcode_parse_atts($attr_string);
117 + if (!empty($attr['id'])) {
118 + $viewIds[] = $attr['id'];
119 + }
120 + }
121 +
122 + // \preg_match_all('/\[bitform-view\s+\b[^\]]*\bid\s*=\s*["\']?(\d+)["\']?[^\]]*\]/', $content, $shortCode);
123 + // $ids = $shortCode[1];
124 + return $viewIds;
125 + }
126 +
59 127 public static function checkIsPageBuilder($srvr)
60 128 {
61 129 if (is_admin()) {
62 130 self::$isPageBuilder = true;
@@ -101,11 +169,12 @@
101 169
102 170 public static function isRestRequest()
103 171 {
104 172 $prefix = rest_get_url_prefix();
173 + // Read-only check to detect REST requests for routing; no state change performed here.
105 174 if (defined('REST_REQUEST') && REST_REQUEST
106 - || isset($_GET['rest_route'])
107 - && 0 === strpos(trim($_GET['rest_route'], '\\/'), $prefix, 0)) {
175 + || (isset($_GET['rest_route'])
176 + && 0 === strpos(trim(sanitize_text_field(wp_unslash($_GET['rest_route'])), '\\/'), $prefix, 0))) {
108 177 return true;
109 178 }
110 179 global $wp_rewrite;
111 180 if (null === $wp_rewrite) {
@@ -115,8 +184,31 @@
115 184 $current_url = wp_parse_url(add_query_arg([]));
116 185 return 0 === strpos($current_url['path'], $rest_url['path'], 0);
117 186 }
118 187
188 + public static function isAjaxRequest()
189 + {
190 + if (function_exists('wp_doing_ajax') && wp_doing_ajax()) {
191 + return true;
192 + }
193 + if (self::isRestRequest()) {
194 + return true;
195 + }
196 +
197 + if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'xmlhttprequest' === strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_REQUESTED_WITH'])))) {
198 + return true;
199 + }
200 + if (isset($_SERVER['HTTP_SEC_FETCH_MODE'], $_SERVER['HTTP_SEC_FETCH_DEST'])) {
201 + $destination = strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_SEC_FETCH_DEST'])));
202 + $mode = strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_SEC_FETCH_MODE'])));
203 + if (('empty' === $destination && in_array($mode, ['cors', 'same-origin'], true))) {
204 + return true;
205 + }
206 + }
207 +
208 + return false;
209 + }
210 +
119 211 public static function isAdminRequest()
120 212 {
121 213 $current_url = home_url(add_query_arg(null, null));
122 214 $admin_url = strtolower(admin_url());
@@ -166,8 +258,16 @@
166 258 $allFormIds = array_merge($bfFrontendFormIds, $bfFormIdsFromPost);
167 259 return $allFormIds;
168 260 }
169 261
262 + public static function getAllViewIdsInPage()
263 + {
264 + $bfFrontendViewIds = self::$bfFrontendViewIds;
265 + $bfViewIdsFromPost = self::getViewIdsFromPost();
266 + $allViewIds = array_merge($bfFrontendViewIds, $bfViewIdsFromPost);
267 + return $allViewIds;
268 + }
269 +
170 270 public static function getAllUniqFormIdsInPage()
171 271 {
172 272 return array_unique(self::getAllFormIdsInPage());
173 273 }
@@ -174,9 +274,123 @@
174 274
175 275 public static function hasMultipleForms()
176 276 {
177 277 $bfUniqFormIds = self::getAllFormIdsInPage();
278 + self::checkIsPageBuilder($_SERVER);
178 279 $isPageBuilder = self::$isPageBuilder;
179 280 $bfMultipleFormsExists = $isPageBuilder ? true : count($bfUniqFormIds) > 1;
180 281 return $bfMultipleFormsExists;
282 + }
283 +
284 + /**
285 + * Field keys the browser reported as hidden at submit time.
286 + *
287 + * Comma joined, and must be compared key by key: a substring test lets a hidden `b1-175`
288 + * also match `b1-17`.
289 + *
290 + * @param mixed $rawHiddenFields the posted `hidden_fields` value (string or array)
291 + *
292 + * @return string[]
293 + */
294 + public static function parseHiddenFieldKeys($rawHiddenFields)
295 + {
296 + if (is_array($rawHiddenFields)) {
297 + $keys = $rawHiddenFields;
298 + } elseif (is_string($rawHiddenFields)) {
299 + $keys = explode(',', $rawHiddenFields);
300 + } else {
301 + return [];
302 + }
303 +
304 + $keys = array_map(function ($key) {
305 + return is_string($key) || is_numeric($key) ? trim((string) $key) : '';
306 + }, $keys);
307 +
308 + return array_values(array_unique(array_filter($keys, function ($key) {
309 + return '' !== $key;
310 + })));
311 + }
312 +
313 + /**
314 + * @param mixed $hiddenFieldKeys parsed key list; anything else is treated as "nothing hidden"
315 + * @param string $fieldKey
316 + *
317 + * @return bool
318 + */
319 + public static function isFieldHidden($hiddenFieldKeys, $fieldKey)
320 + {
321 + return is_array($hiddenFieldKeys) && in_array($fieldKey, $hiddenFieldKeys, true);
322 + }
323 +
324 + public static function getFormPermissions($formId)
325 + {
326 + if (!isset(self::$formsPermissions[$formId])) {
327 + $formManager = FormManager::getInstance($formId);
328 + self::$formsPermissions[$formId] = $formManager->getFormPermission();
329 + }
330 +
331 + return self::$formsPermissions[$formId];
332 + }
333 +
334 + public static function is_current_user_can_access($formId, $action = 'entryViewAccess', $scope = '', $entryUserId = '')
335 + {
336 + $formPermissions = self::getFormPermissions($formId);
337 + $accessPermission = isset($formPermissions->{$action}) ? $formPermissions->{$action} : null;
338 + if (empty($accessPermission)) {
339 + return false;
340 + }
341 + if ('entryViewAccess' === $action && (!isset($accessPermission->preventPublicAccess) || !$accessPermission->preventPublicAccess)) {
342 + return true;
343 + }
344 + if (is_user_logged_in()) {
345 + $user = wp_get_current_user();
346 + $userId = (string) $user->ID;
347 + if (in_array('administrator', $user->roles) || current_user_can('manage_bitform')) {
348 + return true;
349 + }
350 + if ('entryEditAccess' === $action && !(isset($accessPermission->allowEntriesEdit) && $accessPermission->allowEntriesEdit)) {
351 + return false;
352 + }
353 + if (!empty($scope) && !empty($accessPermission->{$scope}) && is_string($accessPermission->{$scope})) {
354 + $accessRolesArray = explode(',', $accessPermission->{$scope});
355 + if (self::has_access_for_roles($user, $accessRolesArray) && empty($entryUserId)) {
356 + return true;
357 + }
358 + if (!empty($entryUserId) && (('ownEntries' === $scope && $userId === $entryUserId) || ('othersEntries' === $scope && $userId !== $entryUserId))) {
359 + return true;
360 + }
361 + }
362 +
363 + if (empty($scope) && isset($accessPermission->ownEntries) && !empty($accessPermission->ownEntries) && is_string($accessPermission->ownEntries)) {
364 + $accessRolesArray = explode(',', $accessPermission->ownEntries);
365 + if (self::has_access_for_roles($user, $accessRolesArray) && !empty($entryUserId) && $userId === $entryUserId) {
366 + return true;
367 + }
368 + if (self::has_access_for_roles($user, $accessRolesArray) && empty($entryUserId)) {
369 + return true;
370 + }
371 + }
372 +
373 + if (empty($scope) && isset($accessPermission->othersEntries) && !empty($accessPermission->othersEntries) && is_string($accessPermission->othersEntries)) {
374 + $accessRolesArray = explode(',', $accessPermission->othersEntries);
375 + if (self::has_access_for_roles($user, $accessRolesArray) && !empty($entryUserId) && $userId !== $entryUserId) {
376 + return true;
377 + }
378 + if (self::has_access_for_roles($user, $accessRolesArray) && empty($entryUserId)) {
379 + return true;
380 + }
381 + }
382 + }
383 + return false;
384 + }
385 +
386 + private static function has_access_for_roles($user, $accessRoles)
387 + {
388 + // If "all_logged_in_users" is in the allowed roles, grant access
389 + if (in_array('all_logged_in_users', $accessRoles)) {
390 + return true;
391 + }
392 + // Check if any of the user's roles match the allowed roles
393 + $userRoles = array_intersect($user->roles, $accessRoles);
394 + return !empty($userRoles);
181 395 }
182 396 }