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 +354 -59 2.03.3.1 View file →
@@ -1,85 +1,166 @@
1 1 <?php
2 2
3 3 namespace BitCode\BitForm\Core\Util;
4 4
5 -final class FrontendHelpers {
6 - public static $pageBuilderList = [
7 - 'action' => 'elementor', // elementor
8 - 'vcv-action' => 'frontend', // visual composer
9 - 'fl_builder' => '', // beaver builder
10 - 'action' => 'in-front-editor', // brizy
11 - 'ct_builder' => 'true', // oxygen
12 - 'breakdance' => 'builder', // breakdance
13 - 'lc_action_launch_editing' => '1', // live canvas
14 - 'vc_action' => 'vc_inline', // wp bakery
15 - 'et_fb' => '1', // divi
5 +use BitCode\BitForm\Core\Form\FormManager;
6 +use WP_Rewrite;
7 +
8 +final class FrontendHelpers
9 +{
10 + public static $isPageBuilder = false;
11 + public static $bfFrontendFormIds = [];
12 + public static $bfFrontendViewIds = [];
13 + public static $bfFormIdsFromPost = [];
14 + public static $bfViewIdsFromPost = [];
15 + private static $formsPermissions = [];
16 +
17 + public static $pageBuilderQueryParamsList = [
16 18 'et_pb_preview' => 'true', // divi
17 - 'bricks' => 'run', // bricks
18 - 'action' => 'architect', // thrive architect
19 - 'page' => 'livecomposer_editor', // live composer
19 + 'vc_editable' => 'true', // wp bakery
20 + 'action' => 'ct_render_shortcode' // oxygen
20 21 ];
21 22
22 - public static function getFormIdsFromPost() {
23 + public static $pageBuilderURLParamsList = [
24 + 'wp-json/bricks/v1/render', // bricks
25 + ];
26 +
27 + public static $pageBuilderRefererQueryParamsList = [
28 + 'breakdance' => 'builder', // breakdance
29 + ];
30 +
31 + public static function getFormIdsFromPost()
32 + {
23 33 global $post;
24 34 global $wpdb;
25 - global $bfUniqFormIds;
26 -
27 35 if (empty($post)) {
28 - $bfUniqFormIds = [];
29 - return;
30 - };
36 + self::$bfFormIdsFromPost = [];
37 + return [];
38 + }
31 39 $postId = $post->ID;
32 - $formsIds = [];
33 - // $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");
34 - $bfMetaValues = $wpdb->get_results('SELECT meta_value FROM `' . $wpdb->postmeta . "` WHERE `post_id`={$post->ID} AND meta_value LIKE '%[bitform id=%'");
40 + $shortcodeFormIds = [];
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 + );
35 48 $postContent = $post->post_content;
36 49 $bfMetaValues[] = (object) ['meta_value' => $postContent];
37 50 foreach ($bfMetaValues as $bfShortcut) {
38 - $meta_value = $bfShortcut->meta_value;
39 - $meta_value = str_replace('\\', '', $meta_value);
40 - \preg_match_all("/\[bitform\s+id\s*=\s*('|\")\s*(\d+)\s*('|\")\]/", $meta_value, $shortCode);
41 - $shortCodes = $shortCode[2];
42 - if (!empty($shortCodes)) {
43 - $formsIds = array_merge($formsIds, $shortCodes);
51 + $meta_value = (is_string($bfShortcut->meta_value) && !empty($bfShortcut->meta_value)) ? $bfShortcut->meta_value : '';
52 + $shortcodeIds = self::getShortCodeIds($meta_value);
53 + $shortcodeFormIds = array_merge($shortcodeFormIds, $shortcodeIds);
54 + }
55 +
56 + self::$bfFormIdsFromPost = $shortcodeFormIds;
57 + return $shortcodeFormIds;
58 + }
59 +
60 + public static function getShortCodeIds($content = '')
61 + {
62 + $pattern = '/' . get_shortcode_regex(['bitform']) . '/';
63 + \preg_match_all($pattern, $content, $short);
64 +
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'];
44 70 }
45 71 }
46 72
47 - $bfUniqFormIds = array_unique($formsIds);
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];
48 79
49 - return $bfUniqFormIds;
80 + return $formIds;
50 81 }
51 82
52 - public static function isPageBuilder() {
53 - global $bfMultipleFormsExists;
54 - global $isPageBuilder;
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;
55 96
56 - $current_url = $_SERVER['REQUEST_URI'];
57 - $isAdminSide = false !== strpos($current_url, '/wp-admin/');
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 + }
58 103
59 - $isPageBuilder = get_transient('is_page_builder');
60 - $isPageBuilderSessionExists = false !== $isPageBuilder;
61 - if ($isAdminSide) {
62 - $isPageBuilder = true;
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 + }
63 120 }
64 121
65 - if ($isPageBuilder) {
66 - $bfMultipleFormsExists = true;
122 + // \preg_match_all('/\[bitform-view\s+\b[^\]]*\bid\s*=\s*["\']?(\d+)["\']?[^\]]*\]/', $content, $shortCode);
123 + // $ids = $shortCode[1];
124 + return $viewIds;
125 + }
126 +
127 + public static function checkIsPageBuilder($srvr)
128 + {
129 + if (is_admin()) {
130 + self::$isPageBuilder = true;
67 131 return true;
68 132 }
133 + $current_url = $srvr['REQUEST_URI'];
134 + $queryParams = self::parseQueryParams($current_url);
135 + foreach (self::$pageBuilderQueryParamsList as $key => $value) {
136 + if (isset($queryParams[$key]) && $queryParams[$key] === $value) {
137 + self::$isPageBuilder = true;
138 + return true;
139 + }
140 + }
141 + foreach (self::$pageBuilderURLParamsList as $value) {
142 + if (false !== strpos($current_url, $value)) {
143 + self::$isPageBuilder = true;
144 + return true;
145 + }
146 + }
69 147
70 - if ($isPageBuilderSessionExists) {
71 - set_transient('is_page_builder', $isPageBuilder);
148 + $referrer = isset($srvr['HTTP_REFERER']) ? $srvr['HTTP_REFERER'] : '';
149 + $referrerQueryParams = self::parseQueryParams($referrer);
150 + foreach (self::$pageBuilderRefererQueryParamsList as $key => $value) {
151 + if (isset($referrerQueryParams[$key]) && $referrerQueryParams[$key] === $value) {
152 + self::$isPageBuilder = true;
153 + return true;
154 + }
72 155 }
73 156
74 - self::getFormIdsFromPost();
75 - global $bfUniqFormIds;
76 - $bfMultipleFormsExists = $isPageBuilder ? true : count($bfUniqFormIds) > 1;
77 - return $isPageBuilder;
157 + return self::$isPageBuilder;
78 158 }
79 159
80 - public static function parseQueryParams($url) {
81 - $url_components = parse_url($url);
160 + public static function parseQueryParams($url)
161 + {
162 + $url_components = wp_parse_url($url);
82 163 if (isset($url_components['query'])) {
83 164 parse_str($url_components['query'], $queryParams);
84 165 return $queryParams;
85 166 }
@@ -85,17 +166,231 @@
85 166 }
86 167 return [];
87 168 }
88 169
89 - public static function handleBfFormIdsSession($formId = null) {
90 - global $bfFrontendFormIds;
91 - $bfFrontendFormIds = get_transient('bf_frontend_form_ids');
92 - if (empty($bfFrontendFormIds)) {
93 - $bfFrontendFormIds = [];
170 + public static function isRestRequest()
171 + {
172 + $prefix = rest_get_url_prefix();
173 + // Read-only check to detect REST requests for routing; no state change performed here.
174 + if (defined('REST_REQUEST') && REST_REQUEST
175 + || (isset($_GET['rest_route'])
176 + && 0 === strpos(trim(sanitize_text_field(wp_unslash($_GET['rest_route'])), '\\/'), $prefix, 0))) {
177 + return true;
94 178 }
95 - if (is_null($formId)) {
96 - return $bfFrontendFormIds;
179 + global $wp_rewrite;
180 + if (null === $wp_rewrite) {
181 + $wp_rewrite = new WP_Rewrite();
97 182 }
98 - $bfFrontendFormIds[] = $formId;
99 - set_transient('bf_frontend_form_ids', $bfFrontendFormIds);
183 + $rest_url = wp_parse_url(trailingslashit(rest_url()));
184 + $current_url = wp_parse_url(add_query_arg([]));
185 + return 0 === strpos($current_url['path'], $rest_url['path'], 0);
186 + }
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 +
211 + public static function isAdminRequest()
212 + {
213 + $current_url = home_url(add_query_arg(null, null));
214 + $admin_url = strtolower(admin_url());
215 + $referrer = strtolower(wp_get_referer());
216 +
217 + $requestFromBackend = self::isRestRequest() && strpos($admin_url, '/wp-admin/') > 0 && !strpos($admin_url, '/wp-admin/admin-ajax.php');
218 +
219 + if ($requestFromBackend) {
220 + return true;
221 + }
222 +
223 + if (0 === strpos($current_url, $admin_url)) {
224 + if (0 === strpos($referrer, $admin_url)) {
225 + return true;
226 + } else {
227 + if (function_exists('wp_doing_ajax')) {
228 + return !wp_doing_ajax();
229 + } else {
230 + return !(defined('DOING_AJAX') && DOING_AJAX);
231 + }
232 + }
233 + } else {
234 + return false;
235 + }
236 + }
237 +
238 + public static function parseUrlParams($url)
239 + {
240 + $url_components = wp_parse_url($url);
241 + if (isset($url_components['path'])) {
242 + $urlParams = explode('/', $url_components['path']);
243 + return $urlParams;
244 + }
245 +
246 + return [];
247 + }
248 +
249 + public static function setBfFrontendFormIds($formId)
250 + {
251 + self::$bfFrontendFormIds[] = $formId;
252 + }
253 +
254 + public static function getAllFormIdsInPage()
255 + {
256 + $bfFrontendFormIds = self::$bfFrontendFormIds;
257 + $bfFormIdsFromPost = self::getFormIdsFromPost();
258 + $allFormIds = array_merge($bfFrontendFormIds, $bfFormIdsFromPost);
259 + return $allFormIds;
260 + }
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 +
270 + public static function getAllUniqFormIdsInPage()
271 + {
272 + return array_unique(self::getAllFormIdsInPage());
273 + }
274 +
275 + public static function hasMultipleForms()
276 + {
277 + $bfUniqFormIds = self::getAllFormIdsInPage();
278 + self::checkIsPageBuilder($_SERVER);
279 + $isPageBuilder = self::$isPageBuilder;
280 + $bfMultipleFormsExists = $isPageBuilder ? true : count($bfUniqFormIds) > 1;
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);
100 395 }
101 396 }