PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 3.2.7
Pods – Custom Content Types and Fields v3.2.7
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / includes / access.php
pods / includes Last commit date
compatibility 6 years ago access.php 1 year ago classes.php 2 years ago compatibility.php 2 years ago data.php 1 year ago forms.php 4 years ago general.php 1 year ago media.php 4 years ago
access.php
2370 lines
1 <?php
2 /**
3 * @package Pods\Global\Functions\Access
4 */
5
6 use Pods\Whatsit\Pod;
7
8 /**
9 * Normalize Pod information with a Pods object or object info.
10 *
11 * @since 3.1.0
12 *
13 * @param array $args {
14 * The arguments to use.
15 *
16 * @type string|null $object_type The object type.
17 * @type string|null $object_name The object name.
18 * @type int|string|null $item_id The item ID.
19 * @type Pods|null $pods The Pods object.
20 * @type Pod|null $pod The Pod object.
21 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
22 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
23 * }
24 *
25 * @return array {
26 * The arguments to use.
27 *
28 * @type string|null $object_type The object type (if set).
29 * @type string|null $object_name The object name (if set).
30 * @type int|string|null $item_id The item ID (if set).
31 * @type Pods|null $pods The Pods object (if built or provided).
32 * @type Pod|null $pod The Pod object (if built or provided).
33 * }
34 */
35 function pods_info_from_args( array $args ): array {
36 $info = [
37 'object_type' => null,
38 'object_name' => null,
39 'item_id' => null,
40 'pods' => null,
41 'pod' => null,
42 ];
43
44 $build_pods = false;
45 $build_pod = false;
46
47 if ( isset( $args['build_pods'] ) ) {
48 $build_pods = $args['build_pods'];
49
50 unset( $args['build_pods'] );
51 }
52
53 if ( isset( $args['build_pod'] ) ) {
54 $build_pod = $args['build_pod'];
55
56 unset( $args['build_pod'] );
57 }
58
59 // Merge in the args with the defaults.
60 $info = array_merge( $info, $args );
61
62 $object_type_set = null !== $info['object_type'];
63 $object_name_set = null !== $info['object_name'];
64
65 // Maybe auto-set the object name from the type if we can.
66 if (
67 $object_type_set
68 && ! $object_name_set
69 && in_array( $info['object_type'], [ 'comment', 'media', 'user' ], true )
70 ) {
71 $info['object_name'] = $info['object_type'];
72
73 $object_name_set = true;
74 }
75
76 // Normalize the Pods info to null if it's not valid.
77 if (
78 $info['pods'] instanceof Pods
79 && ! $info['pods']->is_valid()
80 ) {
81 $info['pods'] = null;
82 }
83
84 // Maybe build the Pods object from the info.
85 if (
86 $build_pods
87 && $object_name_set
88 && ! $info['pods'] instanceof Pods
89 ) {
90 $pods = pods_get_instance( $info['object_name'], $info['item_id'], true );
91
92 if (
93 $pods instanceof Pods
94 && $pods->is_valid()
95 && (
96 empty( $info['object_type'] )
97 || $info['object_type'] === $pods->pod_data->get_type()
98 )
99 ) {
100 $info['pods'] = $pods;
101
102 if ( ! $info['pod'] instanceof Pod ) {
103 $info['pod'] = clone $pods->pod_data;
104 }
105 }
106 } elseif (
107 $info['pods'] instanceof Pods
108 && $info['pods']->is_valid()
109 && ! $info['pod'] instanceof Pod
110 ) {
111 $info['pod'] = clone $info['pods']->pod_data;
112 }
113
114 // Maybe build the Pod object from the info.
115 if (
116 $build_pod
117 && $object_name_set
118 && ! $info['pod'] instanceof Pod
119 ) {
120 try {
121 $pod = pods_api()->load_pod( [
122 'name' => $info['object_name'],
123 ] );
124 } catch ( Exception $e ) {
125 $pod = null;
126 }
127
128 if (
129 $pod instanceof Pod
130 && (
131 empty( $info['object_type'] )
132 || $info['object_type'] === $pod->get_type()
133 )
134 ) {
135 $info['pod'] = $pod;
136 }
137 }
138
139 if ( $info['pod'] instanceof Pod ) {
140 $info['object_type'] = $info['pod']->get_type();
141 $info['object_name'] = $info['pod']->get_name();
142 }
143
144 return $info;
145 }
146
147 /**
148 * Determine whether the current user has access to an object.
149 *
150 * @since 3.1.0
151 *
152 * @param array $args {
153 * The arguments to use.
154 *
155 * @type string|null $object_type The object type.
156 * @type string|null $object_name The object name.
157 * @type int|string|null $item_id The item ID.
158 * @type Pods|null $pods The Pods object.
159 * @type Pod|null $pod The Pod object.
160 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
161 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
162 * }
163 * @param int|null $user_id The user ID to check against, set to 0 or null for anonymous access check.
164 * @param string $access_type The type of access to check for (read, add, edit, delete).
165 * @param string|null $context The unique slug that can be referenced by hooks for context.
166 *
167 * @return bool Whether the current user has access to an object.
168 */
169 function pods_user_can_access_object( array $args, ?int $user_id, string $access_type = 'edit', ?string $context = null ): bool {
170 $info = pods_info_from_args( $args );
171
172 if ( null === $user_id ) {
173 $user_id = 0;
174 }
175
176 // Check if the user exists.
177 $user = get_userdata( $user_id );
178
179 if ( ! $user instanceof WP_User ) {
180 // If the user does not exist and it was not anonymous, do not allow access to an invalid user.
181 if ( 0 < $user_id ) {
182 return false;
183 }
184
185 // If the user was 0 to begin with (anonymous) then set up a user object to work with.
186 $user = new WP_User();
187 }
188
189 // Determine if this is a user in WP that has full access.
190 if ( $user_id && pods_is_user_admin( $user_id ) ) {
191 return true;
192 }
193
194 if ( 'pod' === $info['object_type'] || 'table' === $info['object_type'] ) {
195 // If no object name is provided, we cannot check access.
196 if ( empty( $info['object_name'] ) ) {
197 return false;
198 }
199
200 // Determine if this user has full content access.
201 if ( $user->has_cap('pods_content' ) ) {
202 return true;
203 }
204 }
205
206 $capabilities = pods_access_map_capabilities( $info, $user_id );
207
208 // Unsupported capabilities returned.
209 if ( null === $capabilities ) {
210 return false;
211 }
212
213 /**
214 * Allow filtering the list of capabilities used for checking access against an object.
215 *
216 * @since 3.1.0
217 *
218 * @param array $capabilities The list of capabilities used for checking access against an object.
219 * @param int $user_id The user ID to check against.
220 * @param array $info {
221 * The normalized Pod information referenced.
222 *
223 * @type string|null $object_type The object type (if set).
224 * @type string|null $object_name The object name (if set).
225 * @type int|string|null $item_id The item ID (if set).
226 * @type Pods|null $pods The Pods object (if built or provided).
227 * @type Pod|null $pod The Pod object (if built or provided).
228 * }
229 * @param string $access_type The type of access to check for (read, add, edit, delete).
230 * @param string|null $context The unique slug that can be referenced by hooks for context.
231 */
232 $capabilities = (array) apply_filters(
233 'pods_user_can_access_object_get_capabilities',
234 $capabilities,
235 $user_id,
236 $info,
237 $access_type,
238 $context
239 );
240
241 // No capability mapped, do not allow access.
242 if ( ! array_key_exists( $access_type, $capabilities ) ) {
243 return false;
244 }
245
246 /**
247 * Allow filtering whether a user has access to an object before the normal capability check runs.
248 *
249 * @since 3.1.0
250 *
251 * @param null|bool $can_access Whether a user has access to an object (return null to run normal check).
252 * @param int $user_id The user ID to check against.
253 * @param array $info {
254 * The normalized Pod information referenced.
255 *
256 * @type string|null $object_type The object type (if set).
257 * @type string|null $object_name The object name (if set).
258 * @type int|string|null $item_id The item ID (if set).
259 * @type Pods|null $pods The Pods object (if built or provided).
260 * @type Pod|null $pod The Pod object (if built or provided).
261 * }
262 * @param string $access_type The type of access to check for (read, add, edit, delete).
263 * @param string|null $context The unique slug that can be referenced by hooks for context.
264 * @param array $capabilities The list of capabilities used for checking access against an object.
265 */
266 $can_access = apply_filters(
267 'pods_user_can_access_object_pre_check',
268 null,
269 $user_id,
270 $info,
271 $access_type,
272 $context,
273 $capabilities
274 );
275
276 // Check for access override and return that instead.
277 if ( null !== $can_access ) {
278 return $can_access;
279 }
280
281 // If we are allowing all access, null will be set for the capability.
282 if ( null === $capabilities[ $access_type ] ) {
283 $can_access = true;
284 } else {
285 // Support multiple capability checks ("OR" logic).
286 $capabilities[ $access_type ] = (array) $capabilities[ $access_type ];
287
288 $can_access = false;
289
290 foreach ( $capabilities[ $access_type ] as $capability ) {
291 if ( $info['item_id'] ) {
292 $can_access = $user->has_cap( $capability, $info['item_id'] );
293 } else {
294 $can_access = $user->has_cap( $capability );
295 }
296
297 if ( $can_access ) {
298 break;
299 }
300 }
301 }
302
303 $is_read_access = 'read' === $access_type;
304
305 // Check for password-protected post.
306 if (
307 $can_access
308 && 'post_type' === $info['object_type']
309 && $info['item_id']
310 && (
311 (
312 $is_read_access
313 && pods_access_bypass_post_with_password( $info )
314 )
315 || (
316 ! $is_read_access
317 && post_password_required( $info['item_id'] )
318 )
319 )
320 ) {
321 $can_access = false;
322 }
323
324 /**
325 * Allow filtering whether a user has access to an object after the normal capability check runs.
326 *
327 * @since 3.1.0
328 *
329 * @param bool $can_access Whether a user has access to an object.
330 * @param int $user_id The user ID to check against.
331 * @param array $info {
332 * The normalized Pod information referenced.
333 *
334 * @type string|null $object_type The object type (if set).
335 * @type string|null $object_name The object name (if set).
336 * @type int|string|null $item_id The item ID (if set).
337 * @type Pods|null $pods The Pods object (if built or provided).
338 * @type Pod|null $pod The Pod object (if built or provided).
339 * }
340 * @param string $access_type The type of access to check for (read, add, edit, delete).
341 * @param string|null $context The unique slug that can be referenced by hooks for context.
342 * @param array $capabilities The list of capabilities used for checking access against an object.
343 */
344 return (bool) apply_filters(
345 'pods_user_can_access_object',
346 $can_access,
347 $user_id,
348 $info,
349 $access_type,
350 $context,
351 $capabilities
352 );
353 }
354
355 /**
356 * Determine whether the current user has access to an object.
357 *
358 * @since 3.1.0
359 *
360 * @param array $args {
361 * The arguments to use.
362 *
363 * @type string|null $object_type The object type.
364 * @type string|null $object_name The object name.
365 * @type int|string|null $item_id The item ID.
366 * @type Pods|null $pods The Pods object.
367 * @type Pod|null $pod The Pod object.
368 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
369 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
370 * }
371 * @param string $access_type The type of access to check for (read, add, edit, delete).
372 * @param string|null $context The unique slug that can be referenced by hooks for context.
373 *
374 * @return bool Whether the current user has access to an object.
375 */
376 function pods_current_user_can_access_object( array $args, string $access_type = 'edit', ?string $context = null ): bool {
377 $user_id = null;
378
379 if ( is_user_logged_in() ) {
380 $user_id = get_current_user_id();
381 }
382
383 return pods_user_can_access_object( $args, $user_id, $access_type, $context );
384 }
385
386 /**
387 * Build and map the capabilities that a specific object type/name/ID have in relation to a user ID.
388 *
389 * @since 3.1.0
390 *
391 * @param array $args {
392 * The arguments to use.
393 *
394 * @type string|null $object_type The object type.
395 * @type string|null $object_name The object name.
396 * @type int|string|null $item_id The item ID.
397 * @type Pods|null $pods The Pods object.
398 * @type Pod|null $pod The Pod object.
399 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
400 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
401 * }
402 * @param int|null $user_id The user ID accessing the object.
403 * @param bool $strict Whether to strictly get the capabilities or have the 'read' capability evaluate to null if it's public (defaults to false).
404 *
405 * @return array|null The capabilities that a specific object type/name/ID have in relation to a user ID, or null if invalid.
406 */
407 function pods_access_map_capabilities( array $args, ?int $user_id = null, bool $strict = false ): ?array {
408 $args['build_pods'] = true;
409 $args['build_pod'] = true;
410
411 $info = pods_info_from_args( $args );
412
413 // If no object type or name, we cannot check access.
414 if ( empty( $info['object_type'] ) || empty( $info['object_name'] ) ) {
415 return null;
416 }
417
418 $wp_object = null;
419
420 $capabilities = [];
421
422 if ( 'post_type' === $info['object_type'] ) {
423 $info['item_id'] = (int) $info['item_id'];
424
425 if ( $info['item_id'] ) {
426 $capabilities['read'] = 'read_post';
427 $capabilities['edit'] = 'edit_post';
428 $capabilities['delete'] = 'delete_post';
429 } else {
430 $capabilities['read'] = 'read';
431 $capabilities['edit'] = 'edit_posts';
432 $capabilities['delete'] = 'delete_posts';
433 }
434
435 $capabilities['add'] = 'create_posts';
436 $capabilities['read_private'] = 'read_private_posts';
437 $capabilities['edit_others'] = 'edit_others_posts';
438 $capabilities['delete_others'] = 'delete_others_posts';
439 $capabilities['delete_published'] = 'delete_published_posts';
440 $capabilities['delete_private'] = 'delete_private_posts';
441
442 // Maybe map capabilities to the post type.
443 $wp_object = get_post_type_object( $info['object_name'] );
444
445 if ( $info['item_id'] ) {
446 $post = get_post( $info['item_id'] );
447
448 // If the post was found, do fine-grained access checks.
449 if ( $post instanceof WP_Post ) {
450 $status_obj = get_post_status_object( $post->post_status );
451
452 // Check if the person is allowed to read other posts.
453 if (
454 $user_id
455 && $post->post_author
456 && (int) $user_id === (int) $post->post_author
457 ) {
458 // This is their own post, they can have access.
459 $capabilities['read'] = 'read';
460 } elseif (
461 ! $status_obj
462 || $status_obj->private
463 ) {
464 // This is a private post, check private post capability.
465 $capabilities['read'] = $capabilities['read_private'];
466 }
467 }
468 }
469 } elseif ( 'taxonomy' === $info['object_type'] ) {
470 $info['item_id'] = (int) $info['item_id'];
471
472 $capabilities['read'] = 'read';
473 $capabilities['add'] = 'manage_terms';
474 $capabilities['edit'] = 'edit_terms';
475 $capabilities['delete'] = 'delete_terms';
476
477 // Maybe map capabilities to the post type.
478 $wp_object = get_taxonomy( $info['object_name'] );
479 } elseif ( 'user' === $info['object_type'] ) {
480 $info['item_id'] = (int) $info['item_id'];
481
482 $capabilities['read'] = 'list_users';
483 $capabilities['add'] = 'create_users';
484 $capabilities['edit'] = 'edit_users';
485 $capabilities['delete'] = 'delete_users';
486
487 // If an object ID is provided, check for access for that specific user.
488 if ( ! empty( $info['item_id'] ) ) {
489 $capabilities['edit'] = 'edit_user';
490 $capabilities['delete'] = 'delete_user';
491 }
492
493 // Fake the WP object for the logic below.
494 $wp_object = (object) [
495 'public' => false,
496 'cap' => (object) [],
497 ];
498 } elseif ( 'media' === $info['object_type'] ) {
499 $info['item_id'] = (int) $info['item_id'];
500
501 $capabilities['read'] = 'read';
502 $capabilities['add'] = 'upload_files';
503 $capabilities['edit'] = 'upload_files';
504 $capabilities['delete'] = 'upload_files';
505
506 // Fake the WP object for the logic below.
507 $wp_object = (object) [
508 'public' => false,
509 'cap' => (object) [],
510 ];
511 } elseif ( 'comment' === $info['object_type'] ) {
512 $info['item_id'] = (int) $info['item_id'];
513
514 $capabilities['read'] = 'read';
515 $capabilities['add'] = 1 === (int) get_option( 'comment_registration' ) ? 'read' : null;
516 $capabilities['edit'] = 'moderate_comments';
517 $capabilities['delete'] = 'moderate_comments';
518
519 // If an object ID is provided, check for access for that specific user.
520 if ( ! empty( $info['item_id'] ) ) {
521 $capabilities['edit'] = 'edit_comment';
522 }
523
524 // Fake the WP object for the logic below.
525 $wp_object = (object) [
526 'public' => true,
527 'cap' => (object) [],
528 ];
529 } elseif ( 'settings' === $info['object_type'] ) {
530 $capabilities['read'] = 'manage_options';
531 $capabilities['edit'] = 'manage_options';
532 $capabilities['delete'] = 'manage_options';
533
534 // Fake the WP object for the logic below.
535 $wp_object = (object) [
536 'public' => false,
537 'cap' => (object) [],
538 ];
539 } elseif ( 'pod' === $info['object_type'] || 'table' === $info['object_type'] ) {
540 $info['item_id'] = (int) $info['item_id'];
541
542 $capabilities['read'] = 'pods_read_' . $info['object_name'];
543 $capabilities['add'] = 'pods_add_' . $info['object_name'];
544 $capabilities['edit'] = 'pods_edit_' . $info['object_name'];
545 $capabilities['delete'] = 'pods_delete_' . $info['object_name'];
546 $capabilities['edit_others'] = 'pods_edit_others_' . $info['object_name'];
547 $capabilities['delete_others'] = 'pods_delete_others_' . $info['object_name'];
548
549 $is_public = false;
550
551 if ( $info['pods'] instanceof Pods && $info['pod'] instanceof Pod ) {
552 // If an object ID is provided, check for access for that specific item.
553 if ( $info['item_id'] && $info['pods']->exists() ) {
554 // Check for author field.
555 $author_field = $info['pod']->get_field( 'author' );
556
557 $author_user_id = $author_field ? (int) $info['pods']->field( $author_field->get_name() . '.ID' ) : null;
558
559 // If we have an author field, check if they are the author.
560 if ( $author_field ) {
561 if ( $user_id && $author_user_id === $user_id ) {
562 // This is their own post, they can also have access if have edit access.
563 $capabilities['read'] = [
564 $capabilities['read'],
565 'pods_edit_' . $info['object_name'],
566 ];
567 } else {
568 // This is not their post, check if they have access to others.
569 $capabilities['edit'] = 'pods_edit_others_' . $info['object_name'];
570 $capabilities['delete'] = 'pods_delete_others_' . $info['object_name'];
571 }
572 }
573 }
574
575 $is_public = $info['pod']->get_arg( 'public', '0', true );
576 $is_public = filter_var( $is_public, FILTER_VALIDATE_BOOLEAN );
577
578 // Fake the WP object for the logic below.
579 $wp_object = (object) [
580 'public' => $is_public,
581 'cap' => (object) [],
582 ];
583 }
584
585 if ( $is_public ) {
586 $capabilities['read'] = 'read';
587 }
588 }
589
590 // If no post type object is found, we cannot check access.
591 if ( ! $wp_object ) {
592 return null;
593 }
594
595 // Check if there are any capabilities mapped for this type object.
596 foreach ( $capabilities as $access_type => $capability ) {
597 if ( $capability ) {
598 if ( is_array( $capability ) ) {
599 foreach ( $capability as $k => $cap ) {
600 if ( isset( $wp_object->cap->{$cap} ) ) {
601 $capabilities[ $access_type ][ $k ] = $wp_object->cap->{$cap};
602 }
603 }
604 } elseif ( isset( $wp_object->cap->{$capability} ) ) {
605 $capabilities[ $access_type ] = $wp_object->cap->{$capability};
606 }
607 }
608 }
609
610 // If the object is public, allow read for anyone even logged out.
611 if ( ! $strict && $wp_object->public && 'read' === $capabilities['read'] && ! $user_id ) {
612 $capabilities['read'] = null;
613 }
614
615 /**
616 * Allow filtering the list of capabilities used for checking access against an object type or singular object.
617 *
618 * @since 3.1.0
619 *
620 * @param array $capabilities The list of capabilities used for checking access against an object type or singular object.
621 * @param int $user_id The user ID to check against.
622 * @param array $info {
623 * The normalized Pod information referenced.
624 *
625 * @type string|null $object_type The object type (if set).
626 * @type string|null $object_name The object name (if set).
627 * @type int|string|null $item_id The item ID (if set).
628 * @type Pods|null $pods The Pods object (if built or provided).
629 * @type Pod|null $pod The Pod object (if built or provided).
630 * }
631 */
632 return (array) apply_filters(
633 'pods_access_map_capabilities',
634 $capabilities,
635 $user_id,
636 $info
637 );
638 }
639
640 /**
641 * Determine whether the object type/name is public.
642 *
643 * @since 3.1.0
644 *
645 * @param array $args {
646 * The arguments to use.
647 *
648 * @type string|null $object_type The object type.
649 * @type string|null $object_name The object name.
650 * @type int|string|null $item_id The item ID.
651 * @type Pods|null $pods The Pods object.
652 * @type Pod|null $pod The Pod object.
653 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
654 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
655 * }
656 * @param string $context The context we are checking from (defaults to shortcode).
657 *
658 * @return bool Whether the object type/name is public.
659 */
660 function pods_is_type_public( array $args, string $context = 'shortcode' ): bool {
661 $args['build_pod'] = true;
662
663 $info = pods_info_from_args( $args );
664
665 $is_public = true;
666
667 $pod_has_public = null;
668
669 $is_post_type = 'post_type' === $info['object_type'];
670 $is_taxonomy = 'taxonomy' === $info['object_type'];
671 $is_pod = 'pod' === $info['object_type'];
672 $is_settings_pod = 'settings' === $info['object_type'];
673
674 $is_shortcode_context = 'shortcode' === $context;
675
676 if (
677 $info['pod'] instanceof Pod
678 && (
679 $is_post_type
680 || $is_taxonomy
681 || $is_pod
682 || $is_settings_pod
683 )
684 ) {
685 $is_extended = $info['pod']->is_extended();
686
687 if ( ! $is_extended ) {
688 $is_public = $info['pod']->get_arg( 'public', null, true );
689
690 if ( null !== $is_public ) {
691 $pod_has_public = true;
692
693 $is_public = filter_var( $is_public, FILTER_VALIDATE_BOOLEAN );
694
695 if ( $is_post_type || $is_taxonomy ) {
696 $is_public = $is_public && 1 === (int) $info['pod']->get_arg( 'publicly_queryable', $is_public, true );
697 }
698 }
699 }
700 }
701
702 // Maybe handle looking up the visibility based on the object type.
703 if ( null === $pod_has_public ) {
704 if ( $is_post_type ) {
705 // If no object name is provided, we cannot check if it is public.
706 if ( empty( $info['object_name'] ) ) {
707 $is_public = false;
708 } else {
709 $post_type_object = get_post_type_object( $info['object_name'] );
710
711 // Post type not found.
712 if ( ! $post_type_object ) {
713 $is_public = false;
714 } else {
715 $is_public = $post_type_object->public && $post_type_object->publicly_queryable;
716 }
717 }
718 } elseif ( $is_taxonomy ) {
719 // If no object name is provided, we cannot check if it is public.
720 if ( empty( $info['object_name'] ) ) {
721 $is_public = false;
722 } else {
723 $taxonomy_object = get_taxonomy( $info['object_name'] );
724
725 // Post type not found.
726 if ( ! $taxonomy_object ) {
727 $is_public = false;
728 } else {
729 $is_public = $taxonomy_object->public && $taxonomy_object->publicly_queryable;
730 }
731 }
732 } elseif ( 'user' === $info['object_type'] ) {
733 // Users are not public for shortcodes.
734 if ( $is_shortcode_context ) {
735 $is_public = false;
736 }
737 } elseif ( $is_pod || $is_settings_pod ) {
738 // Pods need special default handling for shortcodes.
739 if ( $is_shortcode_context ) {
740 $first_pods_version = get_option( 'pods_framework_version_first' );
741 $first_pods_version = '' === $first_pods_version ? PODS_VERSION : $first_pods_version;
742
743 $is_public = version_compare( $first_pods_version, '3.1.0-a-1', '<' ) ? true : false;
744 }
745 }
746 }
747
748 /**
749 * Allow filtering whether the object type/name is public.
750 *
751 * @since 3.1.0
752 *
753 * @param bool $is_public Whether the object type/name is public.
754 * @param array $info {
755 * The normalized Pod information referenced.
756 *
757 * @type string|null $object_type The object type (if set).
758 * @type string|null $object_name The object name (if set).
759 * @type int|string|null $item_id The item ID (if set).
760 * @type Pods|null $pods The Pods object (if built or provided).
761 * @type Pod|null $pod The Pod object (if built or provided).
762 * }
763 * @param string|null $context The context we are checking from (shortcode or null).
764 */
765 return (bool) apply_filters(
766 'pods_is_type_public',
767 $is_public,
768 $info,
769 $context
770 );
771 }
772
773 /**
774 * Determine whether a post should be bypassed because it it has a password.
775 *
776 * @since 3.1.0
777 *
778 * @param array $args {
779 * The arguments to use.
780 *
781 * @type string|null $object_type The object type.
782 * @type string|null $object_name The object name.
783 * @type int|string|null $item_id The item ID.
784 * @type Pods|null $pods The Pods object.
785 * @type Pod|null $pod The Pod object.
786 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
787 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
788 * }
789 *
790 * @return bool Whether a post should be bypassed because it it has a password.
791 */
792 function pods_access_bypass_post_with_password( array $args ): bool {
793 $info = pods_info_from_args( $args );
794
795 if ( 'post_type' !== $info['object_type'] || ! $info['item_id'] ) {
796 return false;
797 }
798
799 $post = get_post( (int) $info['item_id'] );
800
801 if ( ! $post instanceof WP_Post ) {
802 return false;
803 }
804
805 // Bypass posts that have a password required but not provided.
806 $bypass_post_with_password = post_password_required( $post );
807
808 /**
809 * Allow filtering whether a post should be bypassed because it it has a password.
810 *
811 * @since 3.1.0
812 *
813 * @param bool $bypass_post_with_password Whether a post should be bypassed because it it has a password.
814 * @param array $info {
815 * The normalized Pod information referenced.
816 *
817 * @type string|null $object_type The object type (if set).
818 * @type string|null $object_name The object name (if set).
819 * @type int|string|null $item_id The item ID (if set).
820 * @type Pods|null $pods The Pods object (if built or provided).
821 * @type Pod|null $pod The Pod object (if built or provided).
822 * }
823 */
824 return (bool) apply_filters(
825 'pods_access_bypass_post_with_password',
826 $bypass_post_with_password,
827 $info
828 );
829 }
830
831 /**
832 * Determine whether a post should be bypassed because it is private and capabilities are not met.
833 *
834 * @since 3.1.0
835 *
836 * @param array $args {
837 * The arguments to use.
838 *
839 * @type string|null $object_type The object type.
840 * @type string|null $object_name The object name.
841 * @type int|string|null $item_id The item ID.
842 * @type Pods|null $pods The Pods object.
843 * @type Pod|null $pod The Pod object.
844 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
845 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
846 * }
847 *
848 * @return bool Whether a post should be bypassed because it is private and capabilities are not met.
849 */
850 function pods_access_bypass_private_post( array $args ): bool {
851 $info = pods_info_from_args( $args );
852
853 if ( 'post_type' !== $info['object_type'] || ! $info['item_id'] ) {
854 return false;
855 }
856
857 $post = get_post( $info['item_id'] );
858
859 if ( ! $post instanceof WP_Post ) {
860 return false;
861 }
862
863 $bypass_private_post = false;
864
865 if ( ! is_post_publicly_viewable( $post ) ) {
866 $can_use_unrestricted = false;
867
868 // Check Pod dynamic features if the status is public.
869 if ( is_post_status_viewable( $post->post_status ) ) {
870 $can_use_unrestricted = pods_can_use_dynamic_feature_unrestricted( $info, 'display', 'read' );
871 }
872
873 if ( $can_use_unrestricted ) {
874 $bypass_private_post = false;
875 } else {
876 $bypass_private_post = ! pods_current_user_can_access_object( $info, 'read' );
877 }
878 }
879
880 /**
881 * Allow filtering whether a post should be bypassed because it is private.
882 *
883 * @since 3.1.0
884 *
885 * @param bool $bypass_private_post Whether a post should be bypassed because it is private.
886 * @param array $info {
887 * The normalized Pod information referenced.
888 *
889 * @type string|null $object_type The object type (if set).
890 * @type string|null $object_name The object name (if set).
891 * @type int|string|null $item_id The item ID (if set).
892 * @type Pods|null $pods The Pods object (if built or provided).
893 * @type Pod|null $pod The Pod object (if built or provided).
894 * }
895 */
896 return (bool) apply_filters(
897 'pods_access_bypass_private_post',
898 $bypass_private_post,
899 $info
900 );
901 }
902
903 /**
904 * Determine whether dynamic features can be used.
905 *
906 * @since 3.1.0
907 *
908 * @return bool Whether dynamic features can be used.
909 */
910 function pods_can_use_dynamic_features( ?Pod $pod = null ): bool {
911 // Check if the constant is defined and only override if no $pod is set or dynamic features are totally disabled.
912 if (
913 defined( 'PODS_DYNAMIC_FEATURES_ALLOW' )
914 && (
915 ! $pod
916 || ! PODS_DYNAMIC_FEATURES_ALLOW
917 )
918 ) {
919 return PODS_DYNAMIC_FEATURES_ALLOW;
920 }
921
922 $can_use_dynamic_features = apply_filters( 'pods_access_can_use_dynamic_features', null, $pod );
923
924 if ( is_bool( $can_use_dynamic_features ) ) {
925 return $can_use_dynamic_features;
926 }
927
928 // Check if all dynamic features are disabled.
929 $dynamic_features_allow = pods_get_setting( 'dynamic_features_allow', '1' );
930 $dynamic_features_allow = filter_var( $dynamic_features_allow, FILTER_VALIDATE_BOOLEAN );
931
932 if ( $dynamic_features_allow && $pod instanceof Pod ) {
933 // Check if all dynamic features are disabled for the Pod.
934 $dynamic_features_allow = $pod->get_arg( 'dynamic_features_allow', 'inherit' );
935
936 if ( 'inherit' === $dynamic_features_allow ) {
937 $dynamic_features_allow = pods_is_type_public(
938 [
939 'pod' => $pod,
940 ]
941 );
942 } else {
943 $dynamic_features_allow = filter_var( $dynamic_features_allow, FILTER_VALIDATE_BOOLEAN );
944 }
945 }
946
947 return $dynamic_features_allow;
948 }
949
950 /**
951 * Determine whether any or a specific dynamic feature can be used.
952 *
953 * @since 3.1.0
954 *
955 * @param string $type The dynamic feature type.
956 *
957 * @return bool Whether any or a specific dynamic feature can be used.
958 */
959 function pods_can_use_dynamic_feature( string $type ): bool {
960 if ( ! pods_can_use_dynamic_features() ) {
961 return false;
962 }
963
964 if ( empty( $type ) ) {
965 return false;
966 }
967
968 // Handle the constants.
969 if ( 'view' === $type && defined( 'PODS_SHORTCODE_ALLOW_VIEWS' ) && ! PODS_SHORTCODE_ALLOW_VIEWS ) {
970 return false;
971 }
972
973 $can_use_dynamic_feature = apply_filters( 'pods_access_can_use_dynamic_feature', null, $type );
974
975 if ( is_bool( $can_use_dynamic_feature ) ) {
976 return $can_use_dynamic_feature;
977 }
978
979 $dynamic_features_enabled = (array) pods_get_setting( 'dynamic_features_enabled', [
980 'display',
981 'form',
982 ] );
983 $dynamic_features_enabled = array_filter( $dynamic_features_enabled );
984
985 $constant_dynamic_features_enabled = defined( 'PODS_DYNAMIC_FEATURES_ENABLED' ) ? PODS_DYNAMIC_FEATURES_ENABLED : false;
986
987 if ( false !== $constant_dynamic_features_enabled && ! is_array( $constant_dynamic_features_enabled ) ) {
988 $constant_dynamic_features_enabled = explode( ',', $constant_dynamic_features_enabled );
989 $constant_dynamic_features_enabled = array_filter( $constant_dynamic_features_enabled );
990
991 $dynamic_features_enabled = $constant_dynamic_features_enabled;
992 }
993
994 if ( empty( $dynamic_features_enabled ) ) {
995 return false;
996 }
997
998 return in_array( $type, $dynamic_features_enabled, true );
999 }
1000
1001 /**
1002 * Determine whether specific dynamic feature is unrestricted.
1003 *
1004 * @since 3.1.0
1005 *
1006 * @param array $args {
1007 * The arguments to use.
1008 *
1009 * @type string|null $object_type The object type.
1010 * @type string|null $object_name The object name.
1011 * @type int|string|null $item_id The item ID.
1012 * @type Pods|null $pods The Pods object.
1013 * @type Pod|null $pod The Pod object.
1014 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
1015 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
1016 * }
1017 * @param string $type The dynamic feature type.
1018 * @param string $mode The dynamic feature mode (like "add" or "edit" for the form feature).
1019 *
1020 * @return bool Whether specific dynamic feature is unrestricted.
1021 */
1022 function pods_can_use_dynamic_feature_unrestricted( array $args, string $type, ?string $mode = null ): bool {
1023 if ( ! pods_can_use_dynamic_feature( $type ) ) {
1024 return false;
1025 }
1026
1027 if ( defined( 'PODS_DYNAMIC_FEATURES_RESTRICT' ) && ! PODS_DYNAMIC_FEATURES_RESTRICT ) {
1028 return true;
1029 }
1030
1031 $can_use_dynamic_features_unrestricted = apply_filters( 'pods_access_can_use_dynamic_features_unrestricted', null, $args, $type, $mode );
1032
1033 if ( is_bool( $can_use_dynamic_features_unrestricted ) ) {
1034 return $can_use_dynamic_features_unrestricted;
1035 }
1036
1037 $can_use_unrestricted = false;
1038
1039 $args['build_pod'] = true;
1040
1041 $info = pods_info_from_args( $args );
1042
1043 if ( ! $info['pod'] ) {
1044 $can_use_unrestricted = false;
1045 } else {
1046 $is_public_content_type = pods_is_type_public( $info );
1047
1048 $default_restricted_dynamic_features = [
1049 'form',
1050 ];
1051
1052 if ( ! $is_public_content_type ) {
1053 $default_restricted_dynamic_features[] = 'display';
1054 }
1055
1056 $default_restricted_dynamic_features_forms = [
1057 'edit',
1058 ];
1059
1060 if ( ! $is_public_content_type ) {
1061 $default_restricted_dynamic_features_forms[] = 'add';
1062 }
1063
1064 // Check if all dynamic features are unrestricted.
1065 $restrict_dynamic_features = $info['pod']->get_arg( 'restrict_dynamic_features', '1' );
1066 $restrict_dynamic_features = filter_var( $restrict_dynamic_features, FILTER_VALIDATE_BOOLEAN );
1067
1068 if ( ! $restrict_dynamic_features ) {
1069 $can_use_unrestricted = true;
1070 } elseif ( ! empty( $type ) ) {
1071 if ( defined( 'PODS_DYNAMIC_FEATURES_RESTRICTED' ) && false !== PODS_DYNAMIC_FEATURES_RESTRICTED ) {
1072 $constant_restricted_dynamic_features = PODS_DYNAMIC_FEATURES_RESTRICTED;
1073
1074 if ( ! is_array( $constant_restricted_dynamic_features ) ) {
1075 $constant_restricted_dynamic_features = explode( ',', $constant_restricted_dynamic_features );
1076 }
1077
1078 $restricted_dynamic_features = $constant_restricted_dynamic_features;
1079 } else {
1080 $restricted_dynamic_features = (array) $info['pod']->get_arg( 'restricted_dynamic_features', $default_restricted_dynamic_features );
1081 }
1082
1083 $restricted_dynamic_features = array_filter( $restricted_dynamic_features );
1084
1085 if ( empty( $restricted_dynamic_features ) ) {
1086 $can_use_unrestricted = true;
1087 } else {
1088 $can_use_unrestricted = ! in_array( $type, $restricted_dynamic_features, true );
1089 }
1090
1091 if ( ! $can_use_unrestricted && 'form' === $type && $mode ) {
1092 if ( defined( 'PODS_DYNAMIC_FEATURES_RESTRICTED_FORMS' ) && false !== PODS_DYNAMIC_FEATURES_RESTRICTED_FORMS ) {
1093 $constant_restricted_dynamic_features_forms = PODS_DYNAMIC_FEATURES_RESTRICTED_FORMS;
1094
1095 if ( ! is_array( $constant_restricted_dynamic_features_forms ) ) {
1096 $constant_restricted_dynamic_features_forms = explode( ',', $constant_restricted_dynamic_features_forms );
1097 }
1098
1099 $restricted_dynamic_features_forms = $constant_restricted_dynamic_features_forms;
1100 } else {
1101 $restricted_dynamic_features_forms = (array) $info['pod']->get_arg( 'restricted_dynamic_features_forms', $default_restricted_dynamic_features_forms );
1102 }
1103
1104 $restricted_dynamic_features_forms = array_filter( $restricted_dynamic_features_forms );
1105
1106 if ( empty( $restricted_dynamic_features_forms ) ) {
1107 $can_use_unrestricted = true;
1108 } else {
1109 $can_use_unrestricted = ! in_array( $mode, $restricted_dynamic_features_forms, true );
1110 }
1111 }
1112 }
1113 }
1114
1115 return $can_use_unrestricted;
1116 }
1117
1118 /**
1119 * Get the access notice for admin user based on object type and object name.
1120 *
1121 * @since 3.1.0
1122 *
1123 * @param array $args {
1124 * The arguments to use.
1125 *
1126 * @type string|null $object_type The object type.
1127 * @type string|null $object_name The object name.
1128 * @type int|string|null $item_id The item ID.
1129 * @type Pods|null $pods The Pods object.
1130 * @type Pod|null $pod The Pod object.
1131 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
1132 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
1133 * }
1134 * @param bool $force_message Whether to force the message to show even if messages are hidden by a setting.
1135 *
1136 * @return string The access notice for admin user based on object type and object name.
1137 */
1138 function pods_get_access_admin_notice( array $args, bool $force_message = false ): string {
1139 $args['build_pod'] = true;
1140
1141 $info = pods_info_from_args( $args );
1142
1143 $identifier_for_html = esc_html( json_encode( [
1144 'object_type' => $info['object_type'],
1145 'object_name' => $info['object_name'],
1146 'item_id' => $info['item_id'],
1147 ] ) );
1148
1149 // Check if constant is hiding all notices.
1150 if ( ! $force_message && defined( 'PODS_ACCESS_HIDE_NOTICES' ) && PODS_ACCESS_HIDE_NOTICES ) {
1151 return '<!-- pods:access-notices/admin/hidden-by-constant ' . $identifier_for_html . ' -->';
1152 }
1153
1154 // Check notice setting for the Pod itself.
1155 if ( $info['pod'] instanceof Pod ) {
1156 $show_access_admin_notices_for_pod = $info['pod']->get_arg( 'show_access_admin_notices', 'inherit' );
1157
1158 if ( 'inherit' !== $show_access_admin_notices_for_pod ) {
1159 $show_access_admin_notices_for_pod = filter_var( $show_access_admin_notices_for_pod, FILTER_VALIDATE_BOOLEAN );
1160
1161 // Check if all notices have been dismissed for the pod.
1162 if ( ! $force_message && ! $show_access_admin_notices_for_pod ) {
1163 return '<!-- pods:access-notices/admin/hidden-by-pod ' . $identifier_for_html . ' -->';
1164 }
1165 }
1166 }
1167
1168 // Show notice that this content may not be visible to others.
1169 $show_access_admin_notices = pods_get_setting( 'show_access_admin_notices', true );
1170 $show_access_admin_notices = filter_var( $show_access_admin_notices, FILTER_VALIDATE_BOOLEAN );
1171
1172 // Check if all notices have been dismissed.
1173 if ( ! $force_message && ! $show_access_admin_notices ) {
1174 return '<!-- pods:access-notices/admin/hidden-by-setting ' . $identifier_for_html . ' -->';
1175 }
1176
1177 $summary = esc_html__( 'Pods Access Rights: Admin-only Notice', 'pods' );
1178
1179 $content = sprintf(
1180 '
1181 <p>
1182 %1$s
1183 <br />
1184 <span class="pods-ui-notice-action-links">
1185 <a href="%2$s" target="_blank" rel="noopener noreferrer">%3$s</a>
1186 | <a href="%4$s" target="_blank" rel="noopener noreferrer">%5$s</a>
1187 </span>
1188 </p>
1189 ',
1190 esc_html__( 'The content type or the content below is not public and may not be available to everyone else.', 'pods' ),
1191 esc_url( 'https://docs.pods.io/displaying-pods/access-rights-in-pods/' ),
1192 esc_html__( 'How access rights work with Pods (Documentation)', 'pods' ),
1193 esc_url( admin_url( 'admin.php?page=pods-settings#heading-security' ) ),
1194 esc_html__( 'Edit other access right options', 'pods' )
1195 );
1196
1197 return '<!-- pods:access-notices/admin/message ' . $identifier_for_html . ' -->'
1198 . pods_message(
1199 sprintf(
1200 '
1201 <details open>
1202 <summary><strong>%1$s</strong></summary>
1203 %2$s
1204 </details>
1205 ',
1206 strip_tags( ! empty( $info['summary'] ) ? $info['summary'] : $summary ),
1207 ! empty( $info['content'] ) ? wpautop( $info['content'] ) : $content
1208 ),
1209 'notice',
1210 true
1211 );
1212 }
1213
1214 /**
1215 * Get the access notice for non-admin user based on object type and object name.
1216 *
1217 * @since 3.1.0
1218 *
1219 * @param array $args {
1220 * The arguments to use.
1221 *
1222 * @type string|null $object_type The object type.
1223 * @type string|null $object_name The object name.
1224 * @type int|string|null $item_id The item ID.
1225 * @type Pods|null $pods The Pods object.
1226 * @type Pod|null $pod The Pod object.
1227 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
1228 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
1229 * }
1230 * @param bool $force_message Whether to force the message to show even if messages are hidden by a setting.
1231 * @param string|null $message A custom message to use for the notice text.
1232 *
1233 * @return string The access notice for non-admin user based on object type and object name.
1234 */
1235 function pods_get_access_user_notice( array $args, bool $force_message = false, ?string $message = null ): string {
1236 $args['build_pod'] = true;
1237
1238 $info = pods_info_from_args( $args );
1239
1240 $identifier_for_html = esc_html( json_encode( [
1241 'object_type' => $info['object_type'],
1242 'object_name' => $info['object_name'],
1243 'item_id' => $info['item_id'],
1244 ] ) );
1245
1246 // Check for password-protected post.
1247 if ( $info['item_id'] && pods_access_bypass_post_with_password( $info ) ) {
1248 $message = get_the_password_form( $info['item_id'] );
1249
1250 return '<!-- pods:access-notices/user/protected/message ' . $identifier_for_html . ' -->'
1251 . pods_message(
1252 sprintf(
1253 '<p><strong>%1$s</strong></p> %2$s',
1254 esc_html__( 'Access Restricted', 'pods' ),
1255 $message
1256 ),
1257 'error',
1258 true
1259 );
1260 }
1261
1262 // Check if constant is hiding all notices.
1263 if ( ! $force_message && defined( 'PODS_ACCESS_HIDE_NOTICES' ) && PODS_ACCESS_HIDE_NOTICES ) {
1264 return '<!-- pods:access-notices/user/hidden-by-constant ' . $identifier_for_html . ' -->';
1265 }
1266
1267 // Check notice setting for the Pod itself.
1268 if ( $info['pod'] instanceof Pod ) {
1269 $show_access_restricted_messages_for_pod = $info['pod']->get_arg( 'show_access_restricted_messages', 'inherit' );
1270
1271 if ( 'inherit' !== $show_access_restricted_messages_for_pod ) {
1272 $show_access_restricted_messages_for_pod = filter_var( $show_access_restricted_messages_for_pod, FILTER_VALIDATE_BOOLEAN );
1273
1274 // Check if all notices have been dismissed for the pod.
1275 if ( ! $force_message && ! $show_access_restricted_messages_for_pod ) {
1276 return '<!-- pods:access-notices/user/hidden-by-pod ' . $identifier_for_html . ' -->';
1277 }
1278 }
1279 }
1280
1281 // Show notice that this content may not be visible to others.
1282 $show_access_restricted_messages = pods_get_setting( 'show_access_restricted_messages', false );
1283 $show_access_restricted_messages = filter_var( $show_access_restricted_messages, FILTER_VALIDATE_BOOLEAN );
1284
1285 // Check if all notices have been dismissed.
1286 if ( ! $force_message && ! $show_access_restricted_messages ) {
1287 return '<!-- pods:access-notices/user/hidden-by-setting ' . $identifier_for_html . ' -->';
1288 }
1289
1290 $message = $message ?? esc_html__( 'You do not have access to this embedded content.', 'pods' );
1291
1292 return '<!-- pods:access-notices/user/message ' . $identifier_for_html . ' -->'
1293 . pods_message(
1294 sprintf(
1295 '<p><strong>%1$s:</strong> %2$s</p>',
1296 esc_html__( 'Access Restricted', 'pods' ),
1297 $message
1298 ),
1299 'error',
1300 true
1301 );
1302 }
1303
1304 /**
1305 * Determine whether SQL clauses can be used with dynamic features.
1306 *
1307 * @since 3.1.0
1308 *
1309 * @param null|string $clause_type The clause type to check if allowed, if null used then it checks if any clauses are allowed.
1310 *
1311 * @return bool Whether SQL clauses can be used with dynamic features.
1312 */
1313 function pods_can_use_dynamic_feature_sql_clauses( ?string $clause_type = null ): bool {
1314 if ( defined( 'PODS_DISABLE_SHORTCODE_SQL' ) ) {
1315 // Negate the check since this is a "disable" constant.
1316 return ! PODS_DISABLE_SHORTCODE_SQL;
1317 }
1318
1319 if ( defined( 'PODS_DYNAMIC_FEATURES_ALLOW_SQL_CLAUSES' ) ) {
1320 $allow_sql_clauses = PODS_DYNAMIC_FEATURES_ALLOW_SQL_CLAUSES;
1321 } else {
1322 $first_pods_version = get_option( 'pods_framework_version_first' );
1323 $first_pods_version = '' === $first_pods_version ? PODS_VERSION : $first_pods_version;
1324
1325 $allow_sql_clauses = pods_get_setting( 'dynamic_features_allow_sql_clauses', version_compare( $first_pods_version, '3.1.0-a-1', '<' ) ? 'simple' : '0' );
1326 }
1327
1328 if (
1329 false === $allow_sql_clauses
1330 || '0' === $allow_sql_clauses
1331 ) {
1332 return false;
1333 }
1334
1335 if ( null === $clause_type ) {
1336 return true;
1337 }
1338
1339 if ( 'simple' === $clause_type && 'all' === $allow_sql_clauses ) {
1340 return true;
1341 }
1342
1343 return $clause_type === $allow_sql_clauses;
1344 }
1345
1346 /**
1347 * Determine whether a callback can be used.
1348 *
1349 * @since 3.1.0
1350 *
1351 * @param string|callable $callback The callback to check.
1352 * @param array $params Parameters used by Pods::helper() method.
1353 *
1354 * @return bool Whether the callback can be used.
1355 */
1356 function pods_access_callback_allowed( $callback, array $params = [] ): bool {
1357 // Real callables are allowed because they are done through PHP calls.
1358 if ( ! is_string( $callback ) ) {
1359 return true;
1360 }
1361
1362 if ( ! pods_can_use_dynamic_feature( 'display' ) ) {
1363 return false;
1364 }
1365
1366 if (
1367 defined( 'PODS_DISPLAY_CALLBACKS' )
1368 && ! PODS_DISPLAY_CALLBACKS
1369 ) {
1370 return false;
1371 }
1372
1373 /**
1374 * Allows changing whether callbacks are allowed to run.
1375 *
1376 * @param bool $allow_callbacks Whether callbacks are allowed to run.
1377 * @param array $params Parameters used by Pods::helper() method.
1378 *
1379 * @since 2.8.0
1380 */
1381 $allow_callbacks = (bool) apply_filters( 'pods_helper_allow_callbacks', true, $params );
1382
1383 if ( ! $allow_callbacks ) {
1384 return false;
1385 }
1386
1387 $disallowed = [
1388 // Regex related.
1389 'preg_replace',
1390 'preg_replace_array',
1391 'preg_replace_callback',
1392 'preg_replace_callback_array',
1393 'preg_match',
1394 'preg_match_all',
1395 // Shell/Eval related.
1396 'system',
1397 'exec',
1398 'passthru',
1399 'proc_close',
1400 'proc_get_status',
1401 'proc_nice',
1402 'proc_open',
1403 'proc_terminate',
1404 'shell_exec',
1405 'system',
1406 'eval',
1407 'create_function',
1408 // File related.
1409 'popen',
1410 'include',
1411 'include_once',
1412 'require',
1413 'require_once',
1414 'file_get_contents',
1415 'file_put_contents',
1416 'get_template_part',
1417 // Nonce related.
1418 'wp_nonce_url',
1419 'wp_nonce_field',
1420 'wp_create_nonce',
1421 'check_admin_referer',
1422 'check_ajax_referer',
1423 'wp_verify_nonce',
1424 // PHP related.
1425 'constant',
1426 'defined',
1427 'get_current_user',
1428 'get_defined_constants',
1429 'get_defined_functions',
1430 'get_defined_vars',
1431 'get_extension_funcs',
1432 'get_include_path',
1433 'get_included_files',
1434 'get_loaded_extensions',
1435 'get_required_files',
1436 'get_resources',
1437 'getenv',
1438 'getopt',
1439 'ini_alter',
1440 'ini_get',
1441 'ini_get_all',
1442 'ini_restore',
1443 'ini_set',
1444 'php_ini_loaded_file',
1445 'php_ini_scanned_files',
1446 'php_sapi_name',
1447 'php_uname',
1448 'phpinfo',
1449 'phpversion',
1450 'putenv',
1451 // WordPress related.
1452 'get_userdata',
1453 'get_currentuserinfo',
1454 'get_post',
1455 'get_term',
1456 'get_comment',
1457 ];
1458
1459 $allowed = [];
1460
1461 if ( defined( 'PODS_DISPLAY_CALLBACKS' ) ) {
1462 $display_callbacks = PODS_DISPLAY_CALLBACKS;
1463 } else {
1464 $first_pods_version = get_option( 'pods_framework_version_first' );
1465 $first_pods_version = '' === $first_pods_version ? PODS_VERSION : $first_pods_version;
1466
1467 $display_callbacks = pods_get_setting( 'display_callbacks', version_compare( $first_pods_version, '3.1.0-a-1', '<' ) ? 'restricted' : 'customized' );
1468 }
1469
1470 if ( '0' === $display_callbacks ) {
1471 return false;
1472 }
1473
1474 // Maybe specify the list of allowed callbacks.
1475 if ( 'customized' === $display_callbacks ) {
1476 if ( defined( 'PODS_DISPLAY_CALLBACKS_ALLOWED' ) ) {
1477 $display_callbacks_allowed = PODS_DISPLAY_CALLBACKS_ALLOWED;
1478 } else {
1479 // Maybe specify the list of allowed callbacks
1480 $display_callbacks_allowed = pods_get_setting( 'display_callbacks_allowed', 'esc_attr,esc_html' );
1481 }
1482
1483 if ( ! is_array( $display_callbacks_allowed ) ) {
1484 $display_callbacks_allowed = str_replace( "\n", ',', $display_callbacks_allowed );
1485 $display_callbacks_allowed = explode( ',', $display_callbacks_allowed );
1486 }
1487
1488 $display_callbacks_allowed = array_map( 'trim', $display_callbacks_allowed );
1489 $display_callbacks_allowed = array_filter( $display_callbacks_allowed );
1490
1491 if ( ! empty( $display_callbacks_allowed ) ) {
1492 $allowed = $display_callbacks_allowed;
1493 }
1494 }
1495
1496 /**
1497 * Allows adjusting the disallowed callbacks as needed.
1498 *
1499 * @param array $disallowed List of callbacks not allowed.
1500 * @param array $params Parameters used by Pods::helper() method.
1501 *
1502 * @since 2.7.0
1503 */
1504 $disallowed = apply_filters( 'pods_helper_disallowed_callbacks', $disallowed, $params );
1505
1506 /**
1507 * Allows adjusting the allowed callbacks as needed.
1508 *
1509 * @param array $allowed List of callbacks explicitly allowed.
1510 * @param array $params Parameters used by Pods::helper() method.
1511 *
1512 * @since 2.7.0
1513 */
1514 $allowed = apply_filters( 'pods_helper_allowed_callbacks', $allowed, $params );
1515
1516 // Clean up helper callback (if string).
1517 if ( is_string( $callback ) ) {
1518 $callback = strip_tags( str_replace( array( '`', chr( 96 ) ), "'", $callback ) );
1519 }
1520
1521 return (
1522 ! in_array( $callback, $disallowed, true )
1523 && (
1524 empty( $allowed )
1525 || in_array( $callback, $allowed, true )
1526 )
1527 );
1528 }
1529
1530 /**
1531 * Get the pod access tab options for a specific pod.
1532 *
1533 * @since 3.1.0
1534 *
1535 * @param string $pod_type The pod type.
1536 * @param string $pod_name The pod name.
1537 * @param null|Pod $pod The pod object.
1538 *
1539 * @return array The pod access tab options for a specific pod.
1540 */
1541 function pods_access_pod_options( string $pod_type, string $pod_name, ?Pod $pod = null ): array {
1542 $first_pods_version = get_option( 'pods_framework_version_first' );
1543 $first_pods_version = '' === $first_pods_version ? PODS_VERSION : $first_pods_version;
1544
1545 $options = [];
1546
1547 $options['security_access_rights_info'] = [
1548 'label' => __( 'How access rights work in Pods', 'pods' ),
1549 'type' => 'html',
1550 'html_content' => sprintf(
1551 '
1552 <p>%1$s</p>
1553 <p><a href="https://docs.pods.io/displaying-pods/access-rights-in-pods/" target="_blank" rel="noopener noreferrer">%2$s</a> <span class="dashicon dashicons dashicons-external"></span></p>
1554 ',
1555 __( 'Pods handles access rights similar to how WordPress itself works.', 'pods' ),
1556 __( 'Read more about how access rights work in Pods on our Documentation site', 'pods' )
1557 ),
1558 ];
1559
1560 if ( 'pod' === $pod_type ) {
1561 $options['public'] = [
1562 'label' => __( 'Public', 'pods' ),
1563 'help' => __( 'You can still embed Pods Content and Forms through PHP and make use of other features directly through code.', 'pods' ),
1564 'description' => __( 'When a content type is public, it can be viewed by anyone when it is embedded through Dynamic Features. Otherwise, a user will need to have the corresponding "read" capability for the content type.', 'pods' ),
1565 'type' => 'boolean',
1566 'default' => version_compare( $first_pods_version, '3.1.0-a-1', '<' ) ? true : false,
1567 'boolean_yes_label' => '',
1568 ];
1569 }
1570
1571 if ( pods_can_use_dynamic_features() ) {
1572 $options['dynamic_features_allow'] = [
1573 'label' => __( 'Dynamic Features', 'pods' ),
1574 'help' => [
1575 __( 'Enabling Dynamic Features will also enable the additional access rights checks for user access. This ensures that people viewing embedded content and forms have the required capabilties. Even when Dynamic Features are disabled, you can still embed Pods Content and Forms through PHP and make use of other features directly through code.', 'pods' ),
1576 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1577 ],
1578 'description' => __( 'Dynamic features include Pods Shortcodes, Blocks, and Widgets which let you embed content and forms on your site.', 'pods' ),
1579 'type' => 'pick',
1580 'default' => 'inherit',
1581 'pick_format_type' => 'single',
1582 'pick_format_single' => 'radio',
1583 'data' => [
1584 'inherit' => __( 'WP Default - If the content type is marked "Public" with WordPress then Dynamic Features will be enabled.', 'pods' ),
1585 '1' => __( 'Enable Dynamic Features including Pods Shortcodes, Blocks, and Widgets for this content type', 'pods' ),
1586 '0' => __( 'Disable All Dynamic Features in Pods for this content type', 'pods' ),
1587 ],
1588 'dependency' => true,
1589 ];
1590
1591 $is_public_content_type = pods_is_type_public(
1592 [
1593 'pod' => $pod,
1594 ]
1595 );
1596
1597 $options['restrict_dynamic_features'] = [
1598 'label' => __( 'Restrict Dynamic Features', 'pods' ),
1599 'help' => [
1600 __( 'This will check access rights for whether someone should have access to specific content before a they can view, modify, or interact with that content.', 'pods' ),
1601 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1602 ],
1603 'description' => sprintf(
1604 '<strong>%1$s</strong> %2$s',
1605 esc_html__( 'Warning:', 'pods' ),
1606 esc_html__( 'If you have authors/contributors on your site then disabling this would give them access to embedding content/forms without access checks for them or whoever views the embeds on the front of your site. Caution is always advised before giving access to other users you may not trust.', 'pods' )
1607 ),
1608 'type' => 'pick',
1609 'default' => '1',
1610 'pick_format_type' => 'single',
1611 'pick_format_single' => 'radio',
1612 'data' => [
1613 '0' => __( 'Unrestricted - Do not check for access rights for embedded content (only use this if you trust ALL users who have access to create content)', 'pods' ),
1614 '1' => __( 'Restricted - Check access rights for embedded content', 'pods' ),
1615 ],
1616 'excludes-on' => [ 'dynamic_features_allow' => '0' ],
1617 ];
1618
1619 $default_restricted_dynamic_features = [
1620 'form',
1621 ];
1622
1623 if ( ! $is_public_content_type ) {
1624 $default_restricted_dynamic_features[] = 'display';
1625 }
1626
1627 $options['restricted_dynamic_features'] = [
1628 'label' => __( 'Dynamic Features to Restrict', 'pods' ),
1629 'help' => [
1630 __( 'This will check access rights for the dynamic feature for whether someone should have access to specific content before a they can view, modify, or interact with that content.', 'pods' ),
1631 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1632 ],
1633 'type' => 'pick',
1634 'default' => $default_restricted_dynamic_features,
1635 'pick_format_type' => 'multi',
1636 'pick_format_multi' => 'checkbox',
1637 'data' => [
1638 'display' => __( 'Restricted Display - Shortcodes and Blocks that allow querying content from this Pod and displaying any field will check access rights.', 'pods' ),
1639 'form' => __( 'Restricted Forms - The Form Shortcode and Block submitting new content or editing existing content will check access rights.', 'pods' ),
1640 ],
1641 'depends-on' => [ 'restrict_dynamic_features' => '1' ],
1642 'excludes-on' => [ 'dynamic_features_allow' => '0' ],
1643 ];
1644
1645 $default_restricted_dynamic_features_forms = [
1646 'edit',
1647 ];
1648
1649 if ( ! $is_public_content_type ) {
1650 $default_restricted_dynamic_features_forms[] = 'add';
1651 }
1652
1653 $options['restricted_dynamic_features_forms'] = [
1654 'label' => __( 'Dynamic Features to Restrict for Forms', 'pods' ),
1655 'help' => [
1656 __( 'This will check access rights for whether someone should have access to specific content before a they can add or edit content.', 'pods' ),
1657 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1658 ],
1659 'type' => 'pick',
1660 'default' => $default_restricted_dynamic_features_forms,
1661 'pick_format_type' => 'multi',
1662 'pick_format_multi' => 'checkbox',
1663 'data' => [
1664 'add' => __( 'Restricted Add New Forms - Embedding the Form Shortcode and Block to allow for adding new content will check access rights.', 'pods' ),
1665 'edit' => __( 'Restricted Edit Forms - Embedding the Form Shortcode and Block to allow for editing existing content will check access rights.', 'pods' ),
1666 ],
1667 'depends-on-multi' => [ 'restricted_dynamic_features' => 'form' ],
1668 'excludes-on' => [ 'dynamic_features_allow' => '0' ],
1669 ];
1670
1671 $options['show_access_restricted_messages'] = [
1672 'label' => __( 'Access-related Restricted Messages', 'pods' ),
1673 'help' => [
1674 __( 'Access-related Restricted Messages will show to anyone who does not have access to add/edit/read a specific item from a content type.', 'pods' ),
1675 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1676 ],
1677 'type' => 'pick',
1678 'default' => 'inherit',
1679 'pick_format_type' => 'single',
1680 'pick_format_single' => 'radio',
1681 'data' => [
1682 '1' => __( 'Enable access-related restricted messages for forms/content displayed (instead of the form/content output)', 'pods' ),
1683 '0' => __( 'Disable access-related restricted messages for forms/content displayed (the form/content output will be blank)', 'pods' ),
1684 'inherit' => __( 'Default - Use the global Pods setting for this', 'pods' ),
1685 ],
1686 'depends-on' => [ 'restrict_dynamic_features' => '1' ],
1687 'excludes-on' => [ 'dynamic_features_allow' => '0' ],
1688 ];
1689
1690 $options['show_access_admin_notices'] = [
1691 'label' => __( 'Access-related Admin Notices', 'pods' ),
1692 'help' => [
1693 __( 'Access-related Admin Notices will only show to admins and will appear above content/forms that may not be entirely public.', 'pods' ),
1694 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1695 ],
1696 'type' => 'pick',
1697 'default' => 'inherit',
1698 'pick_format_type' => 'single',
1699 'pick_format_single' => 'radio',
1700 'data' => [
1701 '1' => __( 'Enable access-related admin notices above forms/content displayed', 'pods' ),
1702 '0' => __( 'Disable access-related admin notices above forms/content displayed', 'pods' ),
1703 'inherit' => __( 'Default - Use the global Pods setting for this', 'pods' ),
1704 ],
1705 'depends-on' => [ 'restrict_dynamic_features' => '1' ],
1706 'excludes-on' => [ 'dynamic_features_allow' => '0' ],
1707 ];
1708 }
1709
1710 $options['security_access_rights_preview'] = [
1711 'label' => __( 'Capabilities preview', 'pods' ),
1712 'type' => 'html',
1713 'html_content' => '
1714 <p>' . esc_html__( 'Below is a list of capabilities that a user will normally need for this content.' ) . '</p>
1715 ' . pods_access_get_capabilities_preview( $pod_type, $pod_name ),
1716 ];
1717
1718 return $options;
1719 }
1720
1721 /**
1722 * Get the list of dynamic features allow options.
1723 *
1724 * @since 3.1.0
1725 *
1726 * @return array The list of dynamic features allow options.
1727 */
1728 function pods_access_get_dynamic_features_allow_options(): array {
1729 return [
1730 'inherit' => __( 'WP Default (if content type is Public)', 'pods' ),
1731 '1' => __( 'Enabled', 'pods' ),
1732 '0' => '🔒 ' . __( 'Disabled', 'pods' ),
1733 ];
1734 }
1735
1736 /**
1737 * Get the list of restricted dynamic features options.
1738 *
1739 * @since 3.1.0
1740 *
1741 * @return array The list of restricted dynamic features options.
1742 */
1743 function pods_access_get_restricted_dynamic_features_options(): array {
1744 return [
1745 'display' => '🔒 ' . __( 'Display', 'pods' ),
1746 'form' => '🔒 ' . __( 'Form', 'pods' ),
1747 ];
1748 }
1749
1750 /**
1751 * Get the access rights capabilities preview HTML.
1752 *
1753 * @since 3.1.0
1754 *
1755 * @param string $pod_type The pod type.
1756 * @param string $pod_name The pod name.
1757 *
1758 * @return string The access rights capabilities preview HTML.
1759 */
1760 function pods_access_get_capabilities_preview( string $pod_type, string $pod_name ): string {
1761 $capabilities = pods_access_map_capabilities(
1762 [
1763 'object_type' => $pod_type,
1764 'object_name' => $pod_name,
1765 ],
1766 null,
1767 true
1768 );
1769
1770 if ( null === $capabilities ) {
1771 $capabilities = [
1772 'read' => null,
1773 'add' => null,
1774 'edit' => null,
1775 'delete' => null,
1776 ];
1777 }
1778
1779 $capabilities_preview = [
1780 'read' => esc_html__( 'Read capability', 'pods' ),
1781 'add' => esc_html__( 'Add New capability', 'pods' ),
1782 'edit' => esc_html__( 'Edit capability', 'pods' ),
1783 'delete' => esc_html__( 'Delete capability', 'pods' ),
1784 'read_private' => esc_html__( 'Read Private capability', 'pods' ),
1785 'edit_others' => esc_html__( 'Edit Others capability', 'pods' ),
1786 'delete_others' => esc_html__( 'Delete Others capability', 'pods' ),
1787 'delete_published' => esc_html__( 'Delete Published capability', 'pods' ),
1788 'delete_private' => esc_html__( 'Delete Private capability', 'pods' ),
1789 ];
1790
1791 $capabilities_preview_list = [
1792 '<strong>' . $capabilities_preview['read'] . ':</strong> ' . ( $capabilities['read'] ?: __( 'Not restricted', 'pods' ) ),
1793 ];
1794
1795 if ( 'settings' !== $pod_type ) {
1796 $capabilities_preview_list[] = '<strong>' . $capabilities_preview['add'] . ':</strong> ' . ( $capabilities['add'] ?: __( 'Not restricted', 'pods' ) );
1797 }
1798
1799 $capabilities_preview_list[] = '<strong>' . $capabilities_preview['edit'] . ':</strong> ' . ( $capabilities['edit'] ?: __( 'Not restricted', 'pods' ) );
1800
1801 if ( 'settings' !== $pod_type ) {
1802 $capabilities_preview_list[] = '<strong>' . $capabilities_preview['delete'] . ':</strong> ' . ( $capabilities['delete'] ?: __( 'Not restricted', 'pods' ) );
1803 }
1804
1805 if ( $capabilities && array_key_exists( 'read_private', $capabilities ) ) {
1806 $capabilities_preview_list[] = '<strong>' . $capabilities_preview['read_private'] . ':</strong> ' . ( $capabilities['read_private'] ?: __( 'Not restricted', 'pods' ) );
1807 }
1808
1809 if ( $capabilities && array_key_exists( 'edit_others', $capabilities ) ) {
1810 $capabilities_preview_list[] = '<strong>' . $capabilities_preview['edit_others'] . ':</strong> ' . ( $capabilities['edit_others'] ?: __( 'Not restricted', 'pods' ) );
1811 }
1812
1813 if ( $capabilities && array_key_exists( 'delete_others', $capabilities ) ) {
1814 $capabilities_preview_list[] = '<strong>' . $capabilities_preview['delete_others'] . ':</strong> ' . ( $capabilities['delete_others'] ?: __( 'Not restricted', 'pods' ) );
1815 }
1816
1817 if ( $capabilities && array_key_exists( 'delete_published', $capabilities ) ) {
1818 $capabilities_preview_list[] = '<strong>' . $capabilities_preview['delete_published'] . ':</strong> ' . ( $capabilities['delete_published'] ?: __( 'Not restricted', 'pods' ) );
1819 }
1820
1821 if ( $capabilities && array_key_exists( 'delete_private', $capabilities ) ) {
1822 $capabilities_preview_list[] = '<strong>' . $capabilities_preview['delete_private'] . ':</strong> ' . ( $capabilities['delete_private'] ?: __( 'Not restricted', 'pods' ) );
1823 }
1824
1825 return '
1826 <ul>
1827 <li>' . implode( '</li><li>', $capabilities_preview_list ) . '</li>
1828 </ul>
1829 ';
1830 }
1831
1832 /**
1833 * Get the pod settings config for access-related settings.
1834 *
1835 * @since 3.1.0
1836 *
1837 * @return array The pod settings config for access-related settings.
1838 */
1839 function pods_access_settings_config(): array {
1840 $first_pods_version = get_option( 'pods_framework_version_first' );
1841 $first_pods_version = '' === $first_pods_version ? PODS_VERSION : $first_pods_version;
1842
1843 $fields = [];
1844
1845 $fields['dynamic_features_allow'] = [
1846 'name' => 'dynamic_features_allow',
1847 'label' => __( 'Dynamic Features', 'pods' ),
1848 'help' => [
1849 __( 'Enabling Dynamic Features will also enable the additional access rights checks for user access. This ensures that people viewing embedded content and forms have the required capabilties. Even when Dynamic Features are disabled, you can still embed Pods Content and Forms through PHP and make use of other features directly through code.', 'pods' ),
1850 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1851 ],
1852 'description' => __( 'Dynamic features include Pods Shortcodes, Blocks, and Widgets which let you embed content and forms on your site.', 'pods' ),
1853 'type' => 'pick',
1854 'default' => '1',
1855 'pick_format_type' => 'single',
1856 'pick_format_single' => 'radio',
1857 'data' => [
1858 '1' => __( 'Enable Dynamic Features including Pods Shortcodes, Blocks, and Widgets', 'pods' ),
1859 '0' => __( 'Disable All Dynamic Features in Pods', 'pods' ),
1860 ],
1861 'site_health_data' => [
1862 '1' => __( 'Enable', 'pods' ),
1863 '0' => __( 'Disable', 'pods' ),
1864 ],
1865 'site_health_include_in_info' => true,
1866 ];
1867
1868 $fields['security_access_rights_info'] = [
1869 'name' => 'security_access_rights_info',
1870 'label' => __( 'How access rights work in Pods', 'pods' ),
1871 'type' => 'html',
1872 'html_content' => sprintf(
1873 '
1874 <p>%1$s</p>
1875 <p><a href="https://docs.pods.io/displaying-pods/access-rights-in-pods/" target="_blank" rel="noopener noreferrer">%2$s</a> <span class="dashicon dashicons dashicons-external"></span></p>
1876 ',
1877 __( 'Pods handles access rights similar to how WordPress itself works.', 'pods' ),
1878 __( 'Read more about how access rights work in Pods on our Documentation site', 'pods' )
1879 ),
1880 'depends-on' => [ 'dynamic_features_allow' => '1' ],
1881 ];
1882
1883 $fields['dynamic_features_enabled'] = [
1884 'name' => 'dynamic_features_enabled',
1885 'label' => __( 'Dynamic Features to Enable', 'pods' ),
1886 'help' => [
1887 __( 'You can choose one or more dynamic features to enable. By default, only Display and Form are enabled.', 'pods' ),
1888 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1889 ],
1890 'type' => 'pick',
1891 'default' => [
1892 'display',
1893 'form',
1894 ],
1895 'pick_format_type' => 'multi',
1896 'pick_format_multi' => 'checkbox',
1897 'data' => [
1898 'display' => __( 'Display - Shortcodes and Blocks that allow querying content from *any* Pod and displaying any field (WordPress access rights are still checked).', 'pods' ),
1899 'form' => __( 'Form - The Form Shortcode and Block that allows submitting new content or editing existing content from *any* Pod (WordPress access rights are still checked).', 'pods' ),
1900 'view' => __( 'View - The View Shortcode and Block that allows embedding *any* theme file on a page.', 'pods' ),
1901 ],
1902 'site_health_data' => [
1903 'display' => __( 'Display', 'pods' ),
1904 'form' => __( 'Form', 'pods' ),
1905 'view' => __( 'View', 'pods' ),
1906 ],
1907 'depends-on' => [ 'dynamic_features_allow' => '1' ],
1908 'site_health_include_in_info' => true,
1909 ];
1910
1911 $fields['show_access_restricted_messages'] = [
1912 'name' => 'show_access_restricted_messages',
1913 'label' => __( 'Access-related Restricted Messages', 'pods' ),
1914 'help' => [
1915 __( 'Access-related Restricted Messages will show to anyone who does not have access to add/edit/read a specific item from a content type.', 'pods' ),
1916 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1917 ],
1918 'type' => 'pick',
1919 'default' => '0',
1920 'pick_format_type' => 'single',
1921 'pick_format_single' => 'radio',
1922 'data' => [
1923 '1' => __( 'Enable access-related restricted messages for forms/content displayed (instead of the form/content output)', 'pods' ),
1924 '0' => __( 'Disable access-related restricted messages for forms/content displayed (the form/content output will be blank)', 'pods' ),
1925 ],
1926 'site_health_data' => [
1927 '1' => __( 'Enable', 'pods' ),
1928 '0' => __( 'Disable', 'pods' ),
1929 ],
1930 'site_health_include_in_info' => true,
1931 'depends-on' => [ 'dynamic_features_allow' => '1' ],
1932 ];
1933
1934 $fields['show_access_admin_notices'] = [
1935 'name' => 'show_access_admin_notices',
1936 'label' => __( 'Access-related Admin Notices', 'pods' ),
1937 'help' => [
1938 __( 'Access-related Admin Notices will only show to admins and will appear above content/forms that may not be entirely public.', 'pods' ),
1939 'https://docs.pods.io/displaying-pods/access-rights-in-pods/',
1940 ],
1941 'type' => 'pick',
1942 'default' => '1',
1943 'pick_format_type' => 'single',
1944 'pick_format_single' => 'radio',
1945 'data' => [
1946 '1' => __( 'Enable access-related admin notices above forms/content displayed', 'pods' ),
1947 '0' => __( 'Disable access-related admin notices above forms/content displayed', 'pods' ),
1948 ],
1949 'site_health_data' => [
1950 '1' => __( 'Enable', 'pods' ),
1951 '0' => __( 'Disable', 'pods' ),
1952 ],
1953 'site_health_include_in_info' => true,
1954 'depends-on' => [ 'dynamic_features_allow' => '1' ],
1955 ];
1956
1957 $fields['dynamic_features_allow_sql_clauses'] = [
1958 'name' => 'dynamic_features_allow_sql_clauses',
1959 'label' => __( 'Allow SQL clauses to be used in Dynamic Features', 'pods' ),
1960 'description' => __( 'SQL clauses in general should only be enabled for sites with trusted users. Since WordPress allows anyone to enter any shortcode or block in the editor, any person with the Contributor role or higher could have access to use this.', 'pods' ),
1961 'type' => 'pick',
1962 'default' => version_compare( $first_pods_version, '3.1.0-a-1', '<' ) ? 'simple' : '0',
1963 'pick_format_type' => 'single',
1964 'pick_format_single' => 'radio',
1965 'data' => [
1966 'all' => __( 'Unrestricted - Enable ALL SQL clause usage through dynamic features (only use this if you trust ALL users who have access to create content)', 'pods' ),
1967 'simple' => __( 'Restricted - Enable Simple SQL clause usage (only SELECT, WHERE, and ORDER BY) through dynamic features (only use this if you trust ALL users who have access to create content)', 'pods' ),
1968 '0' => __( 'Disable SQL clause usage through dynamic features', 'pods' ),
1969 ],
1970 'site_health_data' => [
1971 'all' => __( 'Unrestricted', 'pods' ),
1972 'simple' => __( 'Restricted', 'pods' ),
1973 '0' => __( 'Disable', 'pods' ),
1974 ],
1975 'depends-on' => [
1976 'dynamic_features_allow' => '1',
1977 ],
1978 'depends-on-multi' => [
1979 'dynamic_features_enabled' => 'display',
1980 ],
1981 'site_health_include_in_info' => true,
1982 ];
1983
1984 $fields['display_callbacks'] = [
1985 'name' => 'display_callbacks',
1986 'label' => __( 'Display callbacks', 'pods' ),
1987 'description' => __( 'Callbacks can be used when using Pods Templating syntax like {@my_field,my_callback} in your magic tags.', 'pods' ),
1988 'type' => 'pick',
1989 'default' => version_compare( $first_pods_version, '3.1.0-a-1', '<' ) ? 'restricted' : 'customized',
1990 'pick_format_type' => 'single',
1991 'pick_format_single' => 'radio',
1992 'data' => [
1993 'restricted' => __( 'Restricted - Certain system PHP functions are disallowed from being used for security reasons.', 'pods' ),
1994 'customized' => __( 'Customized - Only allow a list of specific PHP function callbacks.', 'pods' ),
1995 '0' => __( 'Disable display callbacks', 'pods' ),
1996 ],
1997 'site_health_data' => [
1998 'restricted' => __( 'Restricted', 'pods' ),
1999 'customized' => __( 'Customized', 'pods' ),
2000 '0' => __( 'Disable', 'pods' ),
2001 ],
2002 'depends-on' => [
2003 'dynamic_features_allow' => '1',
2004 ],
2005 'depends-on-multi' => [
2006 'dynamic_features_enabled' => 'display',
2007 ],
2008 'site_health_include_in_info' => true,
2009 ];
2010
2011 $fields['display_callbacks_allowed'] = [
2012 'name' => 'display_callbacks_allowed',
2013 'label' => __( 'Display callbacks allowed', 'pods' ),
2014 'description' => __( 'Please provide a comma-separated list of PHP function names to allow in callbacks.', 'pods' ),
2015 'type' => 'text',
2016 'default' => 'esc_attr,esc_html',
2017 'depends-on' => [
2018 'dynamic_features_allow' => '1',
2019 'display_callbacks' => 'customized',
2020 ],
2021 'depends-on-multi' => [
2022 'dynamic_features_enabled' => 'display',
2023 ],
2024 'site_health_include_in_info' => true,
2025 ];
2026
2027 return $fields;
2028 }
2029
2030 /**
2031 * Get the bleep placeholder text.
2032 *
2033 * @since 3.1.0
2034 *
2035 * @return string The bleep placeholder text.
2036 */
2037 function pods_access_bleep_placeholder(): string {
2038 return '****************';
2039 }
2040
2041 /**
2042 * Process the value and bleep it if it needs to be.
2043 *
2044 * @since 3.1.0
2045 *
2046 * @param string|mixed $value The value to be bleeped.
2047 *
2048 * @return string|mixed The bleeped text if not empty, otherwise the value as it was.
2049 */
2050 function pods_access_bleep_text( $value ) {
2051 $bleep_text = pods_access_bleep_placeholder();
2052
2053 if ( 0 < strlen( (string) $value ) ) {
2054 $value = $bleep_text;
2055 }
2056
2057 return $value;
2058 }
2059
2060 /**
2061 * Process the data and bleep anything that needs to be.
2062 *
2063 * @since 3.1.0
2064 *
2065 * @param array|object $data The data to be bleeped.
2066 * @param array $additional_bleep_properties The additional properties to be bleeped from objects and arrays.
2067 *
2068 * @return array|object The bleeped data.
2069 */
2070 function pods_access_bleep_data( $data, array $additional_bleep_properties = [] ) {
2071 $bleep_properties = [
2072 'user_pass',
2073 'user_activation_key',
2074 'post_password',
2075 ];
2076
2077 /**
2078 * Allow filtering the additional properties to be bleeped from objects and arrays.
2079 *
2080 * @since 3.1.0
2081 *
2082 * @param array $additional_bleep_properties The additional properties to be bleeped from objects and arrays.
2083 * @param array|object $data The data to be bleeped.
2084 */
2085 $additional_bleep_properties = apply_filters( 'pods_access_bleep_properties', $additional_bleep_properties, $data );
2086
2087 $bleep_properties = array_merge( $bleep_properties, $additional_bleep_properties );
2088
2089 $bleep_text = pods_access_bleep_placeholder();
2090
2091 if ( is_object( $data ) ) {
2092 foreach ( $bleep_properties as $bleep_property ) {
2093 if ( isset( $data->{$bleep_property} ) ) {
2094 $data->{$bleep_property} = 0 < strlen( (string) $data->{$bleep_property} ) ? $bleep_text : '';
2095 }
2096 }
2097 } elseif ( is_array( $data ) ) {
2098 foreach ( $bleep_properties as $bleep_property ) {
2099 if ( isset( $data[ $bleep_property ] ) ) {
2100 $data[ $bleep_property ] = 0 < strlen( (string) $data[ $bleep_property ] ) ? $bleep_text : '';
2101 }
2102 }
2103 }
2104
2105 return $data;
2106 }
2107
2108 /**
2109 * Process the data and bleep anything that needs to be.
2110 *
2111 * @since 3.1.0
2112 *
2113 * @param array $items The items to be bleeped.
2114 * @param array $additional_bleep_properties The additional properties to be bleeped from objects and arrays.
2115 *
2116 * @return array|object The bleeped data.
2117 */
2118 function pods_access_bleep_items( array $items, array $additional_bleep_properties = [] ) {
2119 // Call the pods_access_bleep_data() function for all items in the $items array.
2120 return array_map(
2121 static function ( $item ) use ( $additional_bleep_properties ) {
2122 return pods_access_bleep_data( $item, $additional_bleep_properties );
2123 },
2124 $items
2125 );
2126 }
2127
2128 /**
2129 * Determine whether the SQL fragment is allowed to be used.
2130 *
2131 * @since 3.1.0
2132 *
2133 * @param string $sql The SQL fragment to check.
2134 * @param string $context The SQL fragment context.
2135 * @param array $args {
2136 * The arguments to use.
2137 *
2138 * @type string|null $object_type The object type.
2139 * @type string|null $object_name The object name.
2140 * @type int|string|null $item_id The item ID.
2141 * @type Pods|null $pods The Pods object.
2142 * @type Pod|null $pod The Pod object.
2143 * @type bool $build_pods Whether to try to build a Pods object from the object type/name/ID (false by default).
2144 * @type bool $build_pod Whether to try to build a Pod object from the object type/name (false by default).
2145 * }
2146 *
2147 * @return bool Whether the SQL fragment is allowed to be used.
2148 */
2149 function pods_access_sql_fragment_is_allowed( string $sql, string $context, array $args = [] ): bool {
2150 $context = strtoupper( $context );
2151
2152 $info = pods_info_from_args( $args );
2153
2154 /**
2155 * Allows filtering whether the SQL fragment is allowed to be used.
2156 *
2157 * @since 3.1.0
2158 *
2159 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2160 * @param string $sql The SQL fragment to check.
2161 * @param string $context The SQL fragment context.
2162 * @param array $info Pod information.
2163 */
2164 return (bool) apply_filters( 'pods_access_sql_fragment_is_allowed', true, $sql, $context, $info );
2165 }
2166
2167 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_mismatch_parenthesis', 10, 2 );
2168 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_unsafe_functions', 10, 2 );
2169 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_unsafe_tables', 10, 2 );
2170 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_double_hyphens', 10, 2 );
2171 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_subqueries', 10, 2 );
2172 add_filter( 'pods_access_sql_fragment_is_allowed', 'pods_access_sql_fragment_disallow_post_status', 10, 4 );
2173
2174 /**
2175 * Disallow mismatched parenthesis from being used in SQL fragments.
2176 *
2177 * @since 3.1.0
2178 *
2179 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2180 * @param string $sql The SQL fragment to check.
2181 *
2182 * @return bool Whether the SQL fragment is allowed to be used.
2183 */
2184 function pods_access_sql_fragment_disallow_mismatch_parenthesis( bool $allowed, string $sql ): bool {
2185 return (
2186 $allowed
2187 && substr_count( $sql, '(' ) === substr_count( $sql, ')' )
2188 );
2189 }
2190
2191 /**
2192 * Disallow unsafe functions from being used in SQL fragments.
2193 *
2194 * @since 3.1.0
2195 *
2196 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2197 * @param string $sql The SQL fragment to check.
2198 *
2199 * @return bool Whether the SQL fragment is allowed to be used.
2200 */
2201 function pods_access_sql_fragment_disallow_unsafe_functions( bool $allowed, string $sql ): bool {
2202 if ( ! $allowed ) {
2203 return $allowed;
2204 }
2205
2206 $unsafe_functions = [
2207 'USER',
2208 'DATABASE',
2209 'VERSION',
2210 'FROM_BASE64',
2211 'TO_BASE64',
2212 'SLEEP',
2213 'WAIT_FOR_EXECUTED_GTID_SET',
2214 'WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS',
2215 'MASTER_POS_WAIT',
2216 'SOURCE_POS_WAIT',
2217 'LOAD_FILE',
2218 ];
2219
2220 /**
2221 * Allow filtering the list of unsafe functions to disallow.
2222 *
2223 * @since 3.1.0
2224 *
2225 * @param array $unsafe_functions The list of unsafe functions to disallow.
2226 * @param string $sql The SQL fragment to check.
2227 */
2228 $unsafe_functions = (array) apply_filters( 'pods_access_sql_fragment_disallow_unsafe_functions', $unsafe_functions, $sql );
2229
2230 $unsafe_functions = array_filter( $unsafe_functions );
2231
2232 foreach ( $unsafe_functions as $unsafe_function ) {
2233 if ( 1 === (int) preg_match( '/\s*' . preg_quote( $unsafe_function, '/' ) . '\s*\(/i', $sql ) ) {
2234 return false;
2235 }
2236 }
2237
2238 return $allowed;
2239 }
2240
2241 /**
2242 * Disallow unsafe tables from being used in SQL fragments.
2243 *
2244 * @since 3.1.0
2245 *
2246 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2247 * @param string $sql The SQL fragment to check.
2248 *
2249 * @return bool Whether the SQL fragment is allowed to be used.
2250 */
2251 function pods_access_sql_fragment_disallow_unsafe_tables( bool $allowed, string $sql ): bool {
2252 if ( ! $allowed ) {
2253 return $allowed;
2254 }
2255
2256 $unsafe_tables = [
2257 'mysql.',
2258 'information_schema.',
2259 'performance_schema.',
2260 'sys.',
2261 ];
2262
2263 /**
2264 * Allow filtering the list of unsafe tables to disallow.
2265 *
2266 * @since 3.1.0
2267 *
2268 * @param array $unsafe_tables The list of unsafe tables to disallow.
2269 * @param string $sql The SQL fragment to check.
2270 */
2271 $unsafe_tables = (array) apply_filters( 'pods_access_sql_fragment_disallow_unsafe_tables', $unsafe_tables, $sql );
2272
2273 $unsafe_tables = array_filter( $unsafe_tables );
2274
2275 foreach ( $unsafe_tables as $unsafe_table ) {
2276 if ( 1 === (int) preg_match( '/\s*' . preg_quote( $unsafe_table, '/' ) . '/i', $sql ) ) {
2277 return false;
2278 }
2279 }
2280
2281 return $allowed;
2282 }
2283
2284 /**
2285 * Disallow double hyphens from being used in SQL fragments.
2286 *
2287 * @since 3.1.0
2288 *
2289 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2290 * @param string $sql The SQL fragment to check.
2291 *
2292 * @return bool Whether the SQL fragment is allowed to be used.
2293 */
2294 function pods_access_sql_fragment_disallow_double_hyphens( bool $allowed, string $sql ): bool {
2295 return (
2296 $allowed
2297 && false === strpos( $sql, '--' )
2298 );
2299 }
2300
2301 /**
2302 * Disallow subqueries from being used in SQL fragments.
2303 *
2304 * @since 3.1.0
2305 *
2306 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2307 * @param string $sql The SQL fragment to check.
2308 *
2309 * @return bool Whether the SQL fragment is allowed to be used.
2310 */
2311 function pods_access_sql_fragment_disallow_subqueries( bool $allowed, string $sql ): bool {
2312 return (
2313 $allowed
2314 && 0 === (int) preg_match( '/\s*SELECT(\s|\()+/i', $sql )
2315 );
2316 }
2317
2318 /**
2319 * Disallow post_status from being used in the WHERE/HAVING SQL fragment unless they have admin access.
2320 *
2321 * @since 3.1.0
2322 *
2323 * @param bool $allowed Whether the SQL fragment is allowed to be used.
2324 * @param string $sql The SQL fragment to check.
2325 * @param string $context The SQL fragment context.
2326 * @param array $info Pod information.
2327 *
2328 * @return bool Whether the SQL fragment is allowed to be used.
2329 */
2330 function pods_access_sql_fragment_disallow_post_status( bool $allowed, string $sql, string $context, array $info ): bool {
2331 if ( 'WHERE' !== $context && 'HAVING' !== $context ) {
2332 return $allowed;
2333 }
2334
2335 return (
2336 $allowed
2337 && (
2338 false === stripos( $sql, 'post_status' )
2339 || pods_is_admin( 'edit_posts' )
2340 )
2341 );
2342 }
2343
2344 /**
2345 * Safely unserialize data if it's PHP serialized.
2346 *
2347 * @since 3.1.0
2348 *
2349 * @param string|mixed $data The data to unserialize.
2350 *
2351 * @return array|string|mixed The unserialized data if it was PHP serialized, otherwise the data as it was.
2352 */
2353 function pods_maybe_safely_unserialize( $data ) {
2354 // The $options parameter of unserialize() requires PHP 7.0+.
2355 if ( version_compare( PHP_VERSION, '7.0', '<' ) ) {
2356 // Fall back to normal WP function.
2357 return maybe_unserialize( $data );
2358 }
2359
2360 // Check if the data is serialized.
2361 if ( is_serialized( $data ) ) {
2362 $data = trim( $data );
2363
2364 // Unserialize the data but exclude classes.
2365 return @unserialize( $data, [ 'allowed_classes' => false ] );
2366 }
2367
2368 return $data;
2369 }
2370