diff --git a/changelog.txt b/changelog.txt index 4cbac680fc..3cfe3c9ad0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,24 @@ Found a bug? Have a great feature idea? Get on GitHub and tell us about it and w Our GitHub has the full list of all prior releases of Pods: https://github.com/pods-framework/pods/releases += 3.2.7 - August 28th, 2024 = + +* Feature: New Pods Related Item List block that works like a Pods Item List block but uses the Pods Single Item block context where you specify a relationship field name to reference. (@sc0ttkclark) +* Feature: You can now link field value output from Pods Field Value block to any website field or just use `permalink` to link to the current item of the field. Works with single select relationship field as the link reference. (@sc0ttkclark) +* Feature: Add support for having multiple filters/pagination on the same page when using Pods shortcodes/blocks. (@sc0ttkclark) +* Feature: When a relationship field is using Taxonomy syncing, you can not choose to hide the Taxonomy UI from the Block Editor and Classic Editor. (@sc0ttkclark) +* Feature: New support for Query Monitor now shows Pods debug logs in a QM panel. (@sc0ttkclark) +* Tweak: Toggle add file button on single file field depending on whether a file is provided yet. #7315 (@heybran) +* Tweak: Added a `
` wrapper for the span-based pagination. (@sc0ttkclark) +* Removed: PHP support for Pod Templates and Pod Pages has been finally turned off by default (`PODS_DISABLE_EVAL` constant set to `false` can be used to re-enable it). It will be completely removed in Pods 3.3 after being deprecated in Pods 2.3. (@sc0ttkclark) +* Fixed: Improve REST authentication method to support other auth forms when registering fields. #7340 #7341 (@JoryHogeveen, @sc0ttkclark) +* Fixed: Fix invalid default value for REST API `write_all` option. #7339 (@JoryHogeveen) +* Fixed: Resolve issue with Taxonomy syncing for relationship fields. #7336 #7334 (@pdclark, @sc0ttkclark) +* Fixed: Add fallback for clipboard.writeText. #7314 (@heybran) +* Fixed: Reset items loop before running the fetch loop in `Pods::template()` and the Templates component. (@sc0ttkclark) +* Fixed: Resolve issues with cached queries in PodsData not having the correct corresponding total found for pagination. (@sc0ttkclark) +* Fixed: More phpstan/phpcs fixes across the codebase. (@sc0ttkclark) + = 3.2.6 - July 22nd, 2024 = * Fixed: Resolve issue with WordPress 6.5 and earlier compatibility by adding polyfill for `react-jsx-runtime` dependency that WP 6.6 related tooling now requires. (@sc0ttkclark) diff --git a/classes/Pods.php b/classes/Pods.php index 9f1afdf63d..dbcb023c35 100644 --- a/classes/Pods.php +++ b/classes/Pods.php @@ -22,7 +22,8 @@ * @property null|string $search Whether search is enabled. * @property null|string $search_var The query variable used for search. * @property null|string $search_mode The search mode to use. - * @property null|string $params The last find() params. + * @property null|string $filter_var The query variable used for filters. + * @property null|array $params The last find() params. * @property null|string $sql The last find() SQL query. */ class Pods implements Iterator { @@ -1349,11 +1350,19 @@ public function field( $name, $single = null, $raw = false ) { $item_data = array(); + // Debug purposes + if ( 1 == pods_v( 'pods_debug_params_all', 'get', 0 ) && pods_is_admin( array( 'pods' ) ) ) { + pods_debug( __METHOD__ . ':' . __LINE__ ); + pods_debug( $sql ); + } + if ( ! $related_obj || ! $related_obj->valid() ) { if ( ! is_object( $this->alt_data ) ) { $this->alt_data = pods_data(); } + pods_debug_log_data( [ 'field_name' => $params->name, 'sql' => $sql ], 'related-field-params', __METHOD__, __LINE__ ); + $item_data = $this->alt_data->select( $sql ); } else { // Support 'find' output ordering. @@ -1364,6 +1373,8 @@ public function field( $name, $single = null, $raw = false ) { $sql['orderby'] = 'FIELD( `t`.`' . $table['field_id'] . '`, ' . $order_ids . ' )'; } + pods_debug_log_data( [ 'field_name' => $params->name, 'sql' => $sql ], 'related-field-params', __METHOD__, __LINE__ ); + $related_obj->find( $sql ); // Support 'find' output. @@ -2391,6 +2402,7 @@ public function find( $params = null, $limit = 15, $where = null, $sql = null ) 'search_across_picks' => false, 'search_across_files' => false, // Advanced parameters. + 'filter_var' => $this->filter_var, 'filters' => $this->filters, 'sql' => $sql, // Caching parameters. @@ -2428,6 +2440,7 @@ public function find( $params = null, $limit = 15, $where = null, $sql = null ) $this->pagination = (boolean) $params->pagination; $this->search = (boolean) $params->search; $this->search_var = $params->search_var; + $this->filter_var = $params->filter_var; $params->join = (array) $params->join; if ( empty( $params->search_query ) ) { @@ -3367,28 +3380,29 @@ public function pagination( $params = null ) { $append = '&'; } - $defaults = array( - 'type' => 'advanced', - 'label' => __( 'Go to page:', 'pods' ), - 'show_label' => true, - 'first_text' => __( '« First', 'pods' ), - 'prev_text' => __( '‹ Previous', 'pods' ), - 'next_text' => __( 'Next ›', 'pods' ), - 'last_text' => __( 'Last »', 'pods' ), - 'prev_next' => true, - 'first_last' => true, - 'limit' => (int) $this->limit, - 'offset' => (int) $this->offset, - 'page' => max( 1, (int) $this->page ), - 'mid_size' => 2, - 'end_size' => 1, - 'total_found' => $this->total_found(), - 'page_var' => $this->page_var, - 'base' => "{$url}{$append}%_%", - 'format' => "{$this->page_var}=%#%", - 'class' => '', - 'link_class' => '', - ); + $defaults = [ + 'type' => 'advanced', + 'label' => __( 'Go to page:', 'pods' ), + 'show_label' => true, + 'first_text' => __( '« First', 'pods' ), + 'prev_text' => __( '‹ Previous', 'pods' ), + 'next_text' => __( 'Next ›', 'pods' ), + 'last_text' => __( 'Last »', 'pods' ), + 'prev_next' => true, + 'first_last' => true, + 'limit' => (int) $this->limit, + 'offset' => (int) $this->offset, + 'page' => max( 1, (int) $this->page ), + 'mid_size' => 2, + 'end_size' => 1, + 'total_found' => $this->total_found(), + 'page_var' => $this->page_var, + 'base' => "{$url}{$append}%_%", + 'format' => "{$this->page_var}=%#%", + 'class' => '', + 'link_class' => '', + 'wrap_pagination' => false, + ]; if ( is_object( $params ) ) { $params = get_object_vars( $params ); @@ -3408,6 +3422,8 @@ public function pagination( $params = null ) { $pagination = 'advanced'; } + $wrap_pagination = (bool) $params->wrap_pagination; + ob_start(); pods_view( PODS_DIR . 'ui/front/pagination/' . $pagination . '.php', compact( array_keys( get_defined_vars() ) ) ); @@ -3526,7 +3542,7 @@ public function filters( $params = null ) { $search = trim( $params['search'] ); if ( '' === $search ) { - $search = pods_v_sanitized( $pod->search_var, 'get', '' ); + $search = sanitize_text_field( pods_v( $pod->search_var, 'get', '' ) ); } ob_start(); @@ -3708,6 +3724,8 @@ public function template( $template_name, $code = null, $deprecated = false, $ch if ( ! empty( $code ) ) { // Only detail templates need $this->id. if ( empty( $this->id ) ) { + $this->reset(); + while ( $this->fetch() ) { $info['item_id'] = $this->id(); @@ -4630,6 +4648,7 @@ public function __get( $name ) { 'page_var', 'search', 'search_var', + 'filter_var', 'search_mode', 'api', 'row_number', @@ -4695,6 +4714,7 @@ public function __set( $name, $value ): void { 'page_var' => 'string', 'search' => 'boolean', 'search_var' => 'string', + 'filter_var' => 'string', 'search_mode' => 'string', 'id' => 'int', ); diff --git a/classes/PodsAPI.php b/classes/PodsAPI.php index f4937b272b..cb1212e930 100644 --- a/classes/PodsAPI.php +++ b/classes/PodsAPI.php @@ -1749,12 +1749,8 @@ public function save_pod( $params, $sanitized = false, $db = true ) { $pod = null; } - if ( $fail_on_load ) { - if ( is_wp_error( $pod ) ) { - return $pod; - } elseif ( empty( $pod ) ) { - return pods_error( __( 'Pod not found', 'pods' ), $this ); - } + if ( $fail_on_load && ! $pod instanceof Pod ) { + return pods_error( __( 'Pod not found', 'pods' ), $this ); } } } @@ -3285,7 +3281,7 @@ public function save_field( $params, $table_operation = true, $sanitized = false if ( $load_params ) { $field_obj = $this->load_field( $load_params ); - if ( $fail_on_load && ( empty( $field_obj ) || is_wp_error( $field_obj ) ) ) { + if ( $fail_on_load && ! $field_obj instanceof Field ) { return $field_obj; } } @@ -4830,11 +4826,12 @@ public function save_pod_item( $params ) { * * Use for globally setting field change tracking. * - * @param bool + * @param bool $track_changed_fields Whether to track changed fields or not. + * @param object $params The parameters passed to save_pod_item. * * @since 2.3.19 */ - $track_changed_fields = apply_filters( "pods_api_save_pod_item_track_changed_fields_{$pod_name}", (boolean) $params->track_changed_fields, $params ); + $track_changed_fields = (bool) apply_filters( "pods_api_save_pod_item_track_changed_fields_{$pod_name}", (bool) $params->track_changed_fields, $params ); $changed_fields = array(); @@ -5083,10 +5080,10 @@ public function save_pod_item( $params ) { * * @since 3.0 * - * @param bool $is_visible Whether the field is visible from conditional logic. - * @param Field $field The field object. - * @param array $field_values The field values referenced. - * @param object $params The save_pod_item parameters. + * @param bool $is_visible Whether the field is visible from conditional logic. + * @param Field|Value_Field $field The field object. + * @param array $field_values The field values referenced. + * @param object $params The save_pod_item parameters. */ $is_visible = (bool) apply_filters( 'pods_api_save_pod_item_conditional_logic_field_is_visible', @@ -6192,9 +6189,9 @@ public static function handle_changed_fields( $pod, $id, $mode = 'set' ) { return []; } - $changed_pods_cache = pods_static_cache_get( 'changed_pods_cache', __CLASS__ ) ?: []; - $old_fields_cache = pods_static_cache_get( 'old_fields_cache', __CLASS__ ) ?: []; - $changed_fields_cache = pods_static_cache_get( 'changed_fields_cache', __CLASS__ ) ?: []; + $changed_pods_cache = (array) ( pods_static_cache_get( 'changed_pods_cache', __CLASS__ ) ?: [] ); + $old_fields_cache = (array) ( pods_static_cache_get( 'old_fields_cache', __CLASS__ ) ?: [] ); + $changed_fields_cache = (array) ( pods_static_cache_get( 'changed_fields_cache', __CLASS__ ) ?: [] ); $cache_key = $pod . '|' . $id; @@ -6202,7 +6199,7 @@ public static function handle_changed_fields( $pod, $id, $mode = 'set' ) { 'depth' => 1, ); - if ( in_array( $mode, array( 'set', 'reset' ), true ) ) { + if ( in_array( $mode, [ 'set', 'reset' ], true ) ) { if ( isset( $changed_fields_cache[ $cache_key ] ) ) { unset( $changed_fields_cache[ $cache_key ] ); } @@ -7957,19 +7954,6 @@ public function delete_pod_item( $params, $wp = true ) { // Plugin hook $this->do_hook( 'pre_delete_pod_item', $params, $pod ); $this->do_hook( "pre_delete_pod_item_{$params->pod}", $params, $pod ); - - // Call any pre-save helpers (if not bypassed) - if ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) { - if ( ! empty( $pod ) ) { - $helpers = array( 'pre_delete_helpers', 'post_delete_helpers' ); - - foreach ( $helpers as $helper ) { - if ( isset( $pod[ $helper ] ) && ! empty( $pod[ $helper ] ) ) { - ${$helper} = explode( ',', $pod[ $helper ] ); - } - } - } - } } // Delete object from relationship fields @@ -8349,7 +8333,7 @@ public function get_pod_type_count( $type ) { * * @param bool $strict Makes sure the pod exists, throws an error if it doesn't work. * - * @return Pods\Whatsit\Pod|false Pod object or false if not found. + * @return Pods\Whatsit\Pod|false|WP_Error Pod object or false if not found. * * @throws Exception * @since 1.7.9 @@ -9785,7 +9769,7 @@ public function lookup_related_items( $field_id, $pod_id, $ids, $field = null, $ $related = get_comments( $comment_args ); - if ( ! is_wp_error( $related ) ) { + if ( $related ) { $related_ids = $related; } } elseif ( @@ -10456,13 +10440,13 @@ public function get_table_info( $object_type, $object, $name = null, $pod = null /** * Allow filtering the table information for an object. * - * @param array $info The table information. - * @param string $object_type The object type. - * @param string $object The object name. - * @param string $name The pod name. - * @param array|Pod $pod The pod config (if found). - * @param array|Field $field The field config (if found). - * @param self $obj The PodsAPI object. + * @param array $info The table information. + * @param string $object_type The object type. + * @param string $object The object name. + * @param string|null $name The pod name. + * @param array|Pod|null $pod The pod config (if found). + * @param array|Field|null $field The field config (if found). + * @param self $obj The PodsAPI object. */ return apply_filters( 'pods_api_get_table_info', $info, $object_type, $object, $name, $pod, $field, $this ); } else { @@ -10570,13 +10554,14 @@ public function get_table_info( $object_type, $object, $name = null, $pod = null * * Use to change "default" post status from publish to any other status or statuses. * - * @param array $post_status List of post statuses. Default is 'publish' or field setting (if available). - * @param string $post_type Post type of current object. - * @param array $info Array of information about the object. - * @param string $object Type of object. - * @param string $name Name of pod to load. - * @param array $pod Array with Pod information. Result of PodsAPI::load_pod(). - * @param array $field Array with field information. + * @param array $post_status List of post statuses. Default is 'publish' or field setting (if available). + * @param string $post_type Post type of current object. + * @param array $info Array of information about the object. + * @param string $object_type Type of object. + * @param string $object Object name if provided. + * @param string|null $name Name of pod to load. + * @param array|Pod|null $pod The pod config (if found). + * @param array|Field|null $field The field config (if found). * * @since unknown */ @@ -10993,10 +10978,6 @@ public function import( $import_data, $numeric_mode = false, $format = null ) { */ global $wpdb; - if ( null === $format && null !== $this->format ) { - $format = $this->format; - } - if ( 'csv' === $format && ! is_array( $import_data ) ) { $data = pods_migrate( 'sv', ',' )->parse( $import_data ); @@ -11016,8 +10997,6 @@ public function import( $import_data, $numeric_mode = false, $format = null ) { if ( ! empty( $this->pod_data ) ) { $pod = $this->pod_data; - } elseif ( ! empty( $this->pod ) ) { - $pod = $this->load_pod( [ 'name' => $this->pod ], false ); } if ( false === $pod ) { @@ -11183,8 +11162,6 @@ public function export( $pod = null, $params = null ) { if ( empty( $pod ) ) { if ( ! empty( $this->pod_data ) ) { $pod = $this->pod_data; - } elseif ( ! empty( $this->pod ) ) { - $pod = $this->load_pod( [ 'name' => $this->pod ], false ); } } @@ -11341,6 +11318,8 @@ public function cache_flush_pods( } else { // Do normal cache clear. pods_cache_clear( true ); + + wp_cache_flush(); } if ( $flush_rewrites ) { @@ -11681,7 +11660,7 @@ public function get_pods_object_from_wp_post( $post ) { $post = get_post( $post ); } - if ( ! $post || is_wp_error( $post ) ) { + if ( ! $post instanceof WP_Post ) { return false; } diff --git a/classes/PodsAdmin.php b/classes/PodsAdmin.php index 6521abee6a..1e7683a347 100644 --- a/classes/PodsAdmin.php +++ b/classes/PodsAdmin.php @@ -4375,21 +4375,21 @@ public function add_rest_settings_tab_fields( $options, $pod ) { } $options['rest-api'] = [ - 'rest_enable' => [ + 'rest_enable' => [ 'label' => __( 'Enable', 'pods' ), 'help' => __( 'Add REST API support for this Pod.', 'pods' ), 'type' => 'boolean', 'default' => '', 'dependency' => true, ], - 'rest_base' => [ + 'rest_base' => [ 'label' => __( 'REST Base (if any)', 'pods' ), 'help' => __( 'This will form the url for the route. Default / empty value here will use the pod name.', 'pods' ), 'type' => 'text', 'default' => '', 'depends-on' => [ 'rest_enable' => true ], ], - 'rest_namespace' => [ + 'rest_namespace' => [ 'label' => __( 'REST API namespace', 'pods' ), 'help' => __( 'This will change the namespace URL of the REST API route to a different one from the default one that all normal route endpoints use.', 'pods' ), 'type' => 'text', @@ -4397,7 +4397,7 @@ public function add_rest_settings_tab_fields( $options, $pod ) { 'placeholder' => 'wp/v2', 'depends-on' => [ 'rest_enable' => true ], ], - 'read_all' => [ + 'read_all' => [ 'label' => __( 'Show All Fields (read-only)', 'pods' ), 'help' => __( 'Show all fields in REST API. If unchecked fields must be enabled on a field by field basis.', 'pods' ), 'type' => 'boolean', @@ -4405,7 +4405,7 @@ public function add_rest_settings_tab_fields( $options, $pod ) { 'depends-on' => [ 'rest_enable' => true ], 'dependency' => true, ], - 'read_all_access' => [ + 'read_all_access' => [ 'label' => __( 'Read All Access', 'pods' ), 'help' => __( 'By default the REST API will allow the fields to be returned for everyone who has access to that endpoint/object. You can also restrict the access of your field based on whether the person is logged in.', 'pods' ), 'type' => 'boolean', @@ -4414,12 +4414,12 @@ public function add_rest_settings_tab_fields( $options, $pod ) { 'read_all' => true, ], ], - 'write_all' => [ - 'label' => __( 'Allow All Fields To Be Updated', 'pods' ), - 'help' => __( 'Allow all fields to be updated via the REST API. If unchecked fields must be enabled on a field by field basis.', 'pods' ), - 'type' => 'boolean', - 'default' => pods_v( 'name', $pod ), - 'depends-on' => [ 'rest_enable' => true, 'read_all' => true ], + 'write_all' => [ + 'label' => __( 'Allow All Fields To Be Updated', 'pods' ), + 'help' => __( 'Allow all fields to be updated via the REST API. If unchecked fields must be enabled on a field by field basis.', 'pods' ), + 'type' => 'boolean', + 'default' => '', + 'depends-on' => [ 'rest_enable' => true, 'read_all' => true ], ], /*'write_all_access' => [ 'label' => __( 'Write All Access', 'pods' ), @@ -4430,20 +4430,20 @@ public function add_rest_settings_tab_fields( $options, $pod ) { 'write_all' => true, ], ],*/ - 'rest_api_field_mode' => [ - 'label' => __( 'Field Mode', 'pods' ), - 'help' => __( 'Specify how you would like your values returned in the REST API responses. If you choose to show Both raw and rendered values then an object will be returned for each field that contains the value and rendered properties.', 'pods' ), - 'type' => 'pick', + 'rest_api_field_mode' => [ + 'label' => __( 'Field Mode', 'pods' ), + 'help' => __( 'Specify how you would like your values returned in the REST API responses. If you choose to show Both raw and rendered values then an object will be returned for each field that contains the value and rendered properties.', 'pods' ), + 'type' => 'pick', 'pick_format_single' => 'radio', - 'default' => 'value', - 'depends-on' => [ 'rest_enable' => true ], - 'data' => [ + 'default' => 'value', + 'depends-on' => [ 'rest_enable' => true ], + 'data' => [ 'value' => __( 'Raw values', 'pods' ), 'render' => __( 'Rendered values', 'pods' ), 'value_and_render' => __( 'Both raw and rendered values {value: raw_value, rendered: rendered_value}', 'pods' ), ], ], - 'rest_api_field_location' => [ + 'rest_api_field_location' => [ 'label' => __( 'Field Location', 'pods' ), 'help' => __( 'Specify where you would like your values returned in the REST API responses. To show in the "meta" object of the response, you must have Custom Fields enabled in the Post Type Supports features.', 'pods' ), 'type' => 'pick', diff --git a/classes/PodsComponents.php b/classes/PodsComponents.php index e296c82d71..5c6da93380 100644 --- a/classes/PodsComponents.php +++ b/classes/PodsComponents.php @@ -521,8 +521,13 @@ public function get_components() { pods_transient_set( 'pods_components', $components, WEEK_IN_SECONDS ); }//end if - if ( 1 === (int) pods_v( 'pods_debug_components', 'get', 0 ) && pods_is_admin( array( 'pods' ) ) ) { - pods_debug( $components ); + if ( is_admin() ) { + if ( 1 === (int) pods_v( 'pods_debug_components', 'get', 0 ) && pods_is_admin( [ 'pods' ] ) ) { + pods_debug( __METHOD__ . ':' . __LINE__ ); + pods_debug( $components ); + } + + pods_debug_log_data( $components, 'components', __METHOD__, __LINE__ ); } $this->components = $components; diff --git a/classes/PodsData.php b/classes/PodsData.php index c1b6872399..44e1638db6 100644 --- a/classes/PodsData.php +++ b/classes/PodsData.php @@ -153,6 +153,11 @@ class PodsData { */ public $search_var = 'search'; + /** + * @var string + */ + public $filter_var = 'filter'; + /** * int | text | text_like * @@ -639,24 +644,30 @@ public function delete( $table, $where, $where_format = null ) { /** * Select items, eventually building dynamic query * - * @param array $params + * @param array|object $params * * @return array|bool|mixed * @since 2.0.0 */ public function select( $params ) { + if ( is_array( $params ) ) { + $params = (object) $params; + } + global $wpdb; - $cache_key = false; - $results = false; + $cache_key = false; + $cache_mode = 'cache'; + $expires = 0; + $results = false; $instance = $this; /** * Filter select parameters before the query * - * @param array $params + * @param object $params * @param PodsData $instance The current PodsData class instance. * * @since unknown @@ -665,26 +676,84 @@ public function select( $params ) { // Debug purposes. if ( 1 === (int) pods_v( 'pods_debug_params', 'get', 0 ) && pods_is_admin( array( 'pods' ) ) ) { + pods_debug( __METHOD__ . ':' . __LINE__ ); pods_debug( $params ); } + pods_debug_log_data( $params, 'find-params', __METHOD__, __LINE__ ); + + $debug_sql = ( 1 === (int) pods_v( 'pods_debug_sql', 'get', 0 ) || 1 === (int) pods_v( 'pods_debug_sql_all', 'get', 0 ) ) && pods_is_admin( array( 'pods' ) ); + + $total_found_cached = false; + + $is_search = pods_v( $this->search_var ); + + // Disable caching for searches. + if ( null !== $is_search ) { + $params->expires = 0; + $params->cache_mode = null; + } + // Get from cache if enabled. - if ( null !== pods_v( 'expires', $params, null, true ) ) { - $cache_key = md5( (string) $this->pod . serialize( $params ) ); + if ( + ! $debug_sql + && null === $is_search + && null !== pods_v( 'expires', $params, null, true ) + ) { + $cache_key = md5( (string) $this->pod . serialize( $params ) ); + $cache_mode = pods_v( 'cache_mode', $params, 'cache', true ); + $expires = (int) pods_v( 'expires', $params, 0 ); - $results = pods_view_get( $cache_key, pods_v( 'cache_mode', $params, 'cache', true ), 'pods_data_select' ); + $results = pods_view_get( $cache_key, $cache_mode, 'pods_data_select' ); + $stats = pods_view_get( $cache_key, $cache_mode, 'pods_data_select_stats' ); if ( empty( $results ) ) { $results = false; + } elseif ( ! empty( $stats ) ) { + $this->total_found = $stats->total_found ?? null; + $this->limit = (int) ( $stats->limit ?? 15 ); + $this->page = (int) ( $stats->page ?? 1 ); + $this->offset = (int) ( $stats->offset ?? 0 ); + + if ( null !== $this->total_found ) { + $this->total_found_calculated = true; + + $total_found_cached = true; + } + } else { + $params->calc_rows = false; + + // Attempt to calculate total found. + $this->sql = $this->build( $params ); + + if ( $this->total_sql ) { + $this->calculate_totals(); + + // Cache if enabled. + if ( false !== $cache_key && $this->total_found_calculated ) { + $stats = (object) [ + 'total_found' => $this->total_found, + 'limit' => $this->limit, + 'page' => $this->page, + 'offset' => $this->offset, + ]; + + pods_view_set( $cache_key, $stats, $expires, $cache_mode, 'pods_data_select_stats' ); + } + } } } if ( empty( $results ) ) { + pods_debug_log_data( $params, 'sql-select-params', __METHOD__, __LINE__ ); + // Build. $this->sql = $this->build( $params ); // Debug purposes. - if ( ( 1 === (int) pods_v( 'pods_debug_sql', 'get', 0 ) || 1 === (int) pods_v( 'pods_debug_sql_all', 'get', 0 ) ) && pods_is_admin( array( 'pods' ) ) ) { + if ( $debug_sql ) { + pods_debug( __METHOD__ . ':' . __LINE__ ); + if ( function_exists( 'codecept_debug' ) ) { pods_debug( $this->get_sql() ); } else { @@ -692,6 +761,8 @@ public function select( $params ) { } } + pods_debug_log_data( $this->get_sql(), 'sql-select', __METHOD__, __LINE__ ); + if ( empty( $this->sql ) ) { return array(); } @@ -699,22 +770,28 @@ public function select( $params ) { // Get Data. $results = pods_query( $this->sql, $this ); + pods_debug_log_data( $results, 'sql-select-results', __METHOD__, __LINE__ ); + // Cache if enabled. if ( false !== $cache_key ) { - pods_view_set( $cache_key, $results, (int) pods_v( 'expires', $params, 0, false ), pods_v( 'cache_mode', $params, 'cache', true ), 'pods_data_select' ); + pods_view_set( $cache_key, $results, $expires, $cache_mode, 'pods_data_select' ); } }//end if + if ( is_object( $results ) ) { + $results = get_object_vars( $results ); + } + /** * Filter results of Pods Query * * @param array $results - * @param array $params + * @param object $params * @param PodsData $instance The current PodsData class instance. * * @since unknown */ - $results = apply_filters( 'pods_data_select', $results, $params, $instance ); + $results = (array) apply_filters( 'pods_data_select', $results, $params, $instance ); // Clean up data we don't want to work with. if ( @@ -740,7 +817,9 @@ public function select( $params ) { $this->row_number = - 1; $this->row = null; - $this->total_found_calculated = false; + if ( ! $total_found_cached ) { + $this->total_found_calculated = false; + } $this->total = 0; @@ -752,7 +831,7 @@ public function select( $params ) { * Filters whether the total_found should be calculated right away or not. * * @param boolean $auto_calculate_total_found Whether to auto calculate total_found. - * @param array $params Select parameters. + * @param object $params Select parameters. * @param PodsData $instance The current PodsData instance. * * @since 2.7.11 @@ -770,6 +849,10 @@ public function select( $params ) { */ public function calculate_totals() { + if ( $this->total_found_calculated ) { + return; + } + /** * @var $wpdb wpdb */ @@ -1079,9 +1162,12 @@ public function build( $params ) { $params->search = (boolean) $params->search; if ( 1 === (int) pods_v( 'pods_debug_params_all', 'get', 0 ) && pods_is_admin( array( 'pods' ) ) ) { + pods_debug( __METHOD__ . ':' . __LINE__ ); pods_debug( $params ); } + pods_debug_log_data( $params, 'find-params', __METHOD__, __LINE__ ); + $params->field_table_alias = 't'; // Get Aliases for future reference. @@ -1317,7 +1403,7 @@ public function build( $params ) { $filterfield = self::get_db_field( $db_field_params ); if ( 'pick' === $attributes['type'] ) { - $filter_value = pods_v( 'filter_' . $field ); + $filter_value = pods_v( $this->filter_var . '_' . $field ); if ( ! is_array( $filter_value ) ) { $filter_value = (array) $filter_value; @@ -1365,8 +1451,8 @@ public function build( $params ) { 'datetime', ), true ) ) { - $start_value = pods_v( 'filter_' . $field . '_start', 'get', false ); - $end_value = pods_v( 'filter_' . $field . '_end', 'get', false ); + $start_value = pods_v( $this->filter_var . '_' . $field . '_start', 'get', false ); + $end_value = pods_v( $this->filter_var . '_' . $field . '_end', 'get', false ); if ( empty( $start_value ) && empty( $end_value ) ) { continue; @@ -1404,7 +1490,7 @@ public function build( $params ) { } } } else { - $filter_value = (string) pods_v( 'filter_' . $field, 'get', '' ); + $filter_value = (string) pods_v( $this->filter_var . '_' . $field, 'get', '' ); if ( '' === $filter_value ) { continue; @@ -2448,7 +2534,7 @@ public static function query( $sql, $error = 'Database Error', $results_error = } } - if ( pods_is_admin() && 1 === (int) pods_v( 'pods_debug_backtrace' ) ) { + if ( 1 === (int) pods_v( 'pods_debug_backtrace' ) && pods_is_admin() ) { ob_start(); echo '
'; var_dump( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 11 ) ); @@ -2488,10 +2574,12 @@ public static function query( $sql, $error = 'Database Error', $results_error = } if ( 1 === (int) pods_v( 'pods_debug_sql_all', 'get', 0 ) && pods_is_admin( array( 'pods' ) ) ) { + pods_debug( __METHOD__ . ':' . __LINE__ ); echo ''; } + } - }//end if + pods_debug_log_data( pods_data()->get_sql( $params->sql ), 'sql-query', __METHOD__, __LINE__ ); $params->sql = trim( $params->sql ); @@ -2517,6 +2605,8 @@ public static function query( $sql, $error = 'Database Error', $results_error = $result = apply_filters( 'pods_data_query_result', $result, $params ); if ( false === $result && ! empty( $params->error ) && ! empty( $wpdb->last_error ) ) { + pods_debug_log_data( "{$params->error}; SQL: {$params->sql}; Response: {$wpdb->last_error}", 'sql-error', __METHOD__, __LINE__ ); + return pods_error( "{$params->error}; SQL: {$params->sql}; Response: {$wpdb->last_error}", $params->display_errors ); } @@ -2526,8 +2616,12 @@ public static function query( $sql, $error = 'Database Error', $results_error = $result = (array) $wpdb->last_result; if ( ! empty( $result ) && ! empty( $params->results_error ) ) { + pods_debug_log_data( "{$params->results_error}; SQL: {$params->sql}", 'sql-results-error', __METHOD__, __LINE__ ); + return pods_error( $params->results_error, $params->display_errors ); } elseif ( empty( $result ) && ! empty( $params->no_results_error ) ) { + pods_debug_log_data( "{$params->no_results_error}; SQL: {$params->sql}", 'sql-results-error', __METHOD__, __LINE__ ); + return pods_error( $params->no_results_error, $params->display_errors ); } } @@ -3182,11 +3276,11 @@ public function traverse_build( $fields = null, $params = null ) { $field = $data; } - if ( ! isset( $_GET[ 'filter_' . $field ] ) ) { + if ( ! isset( $_GET[ $this->filter_var . '_' . $field ] ) ) { continue; } - $field_value = pods_v( 'filter_' . $field, 'get', false, true ); + $field_value = pods_v( $this->filter_var . '_' . $field, 'get', false, true ); if ( ! empty( $field_value ) || ( is_string( $field_value ) && 0 < strlen( $field_value ) ) ) { $feed[ 'traverse_' . $field ] = array( $field ); @@ -3485,17 +3579,17 @@ public function traverse_recurse( $traverse_recurse ) { $rel_alias = 'rel_' . $field_joined; if ( pods_v( 'search', $traverse_recurse['params'], false ) && empty( $traverse_recurse['params']->filters ) ) { - if ( 0 < strlen( (string) pods_v( 'filter_' . $field_joined ) ) ) { - $val = absint( pods_v( 'filter_' . $field_joined ) ); + if ( 0 < strlen( (string) pods_v( $this->filter_var . '_' . $field_joined ) ) ) { + $val = absint( pods_v( $this->filter_var . '_' . $field_joined ) ); $search = "`{$field_joined}`.`{$table_info[ 'field_id' ]}` = {$val}"; if ( 'text' === $this->search_mode ) { - $val = pods_v_sanitized( 'filter_' . $field_joined ); + $val = pods_v_sanitized( $this->filter_var . '_' . $field_joined ); $search = "`{$field_joined}`.`{$traverse[ 'name' ]}` = '{$val}'"; } elseif ( 'text_like' === $this->search_mode ) { - $val = pods_sanitize( pods_sanitize_like( pods_v( 'filter_' . $field_joined ) ) ); + $val = pods_sanitize( pods_sanitize_like( pods_v( $this->filter_var . '_' . $field_joined ) ) ); $search = "`{$field_joined}`.`{$traverse[ 'name' ]}` LIKE '%{$val}%'"; } diff --git a/classes/PodsForm.php b/classes/PodsForm.php index 50e06fba62..4852e1922b 100644 --- a/classes/PodsForm.php +++ b/classes/PodsForm.php @@ -1,6 +1,7 @@ ' ) && ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) ) { - /** - * Input helpers are deprecated and not guaranteed to work properly. - * - * They will be entirely removed in Pods 3.0. - * - * @deprecated 2.7.0 - */ - eval( '?>' . $helper['code'] ); } elseif ( method_exists( static::class, 'field_' . $type ) ) { // @todo Move these custom field methods into real/faux field classes echo call_user_func( array( static::class, 'field_' . $type ), $name, $value, $options ); @@ -1510,7 +1502,8 @@ public static function field_loader( $field_type, $file = '' ) { * * @since unknown * - * @param string $file The file path to include for the field type. + * @param string $file The file path to include for the field type. + * @param string $field_type The field type. */ $file = apply_filters( 'pods_form_field_include', $file, $field_type ); diff --git a/classes/PodsInit.php b/classes/PodsInit.php index 55a45e1646..a657701910 100644 --- a/classes/PodsInit.php +++ b/classes/PodsInit.php @@ -246,6 +246,8 @@ public static function autoload_class( $class ) { * Load the plugin textdomain and set default constants */ public function plugins_loaded() { + // Set some default constants. + if ( ! defined( 'PODS_LIGHT' ) ) { define( 'PODS_LIGHT', false ); } @@ -254,6 +256,10 @@ public function plugins_loaded() { define( 'PODS_TABLELESS', false ); } + if ( ! defined( 'PODS_DISABLE_EVAL' ) ) { + define( 'PODS_DISABLE_EVAL', true ); + } + if ( ! defined( 'PODS_TEXTDOMAIN' ) || PODS_TEXTDOMAIN ) { load_plugin_textdomain( 'pods' ); } @@ -1032,6 +1038,8 @@ public function refresh_existing_content_types_cache( $force = false ) { pods_debug( [ __METHOD__, compact( 'existing_post_types_cached', 'existing_taxonomies_cached' ) ] ); } + pods_debug_log_data( compact( 'existing_post_types_cached', 'existing_taxonomies_cached' ), 'register-existing', __METHOD__, __LINE__ ); + return compact( 'existing_post_types_cached', 'existing_taxonomies_cached' ); } @@ -1650,6 +1658,8 @@ public function setup_content_types( $force = false ) { pods_debug( [ __METHOD__ . '/register_taxonomy', compact( 'taxonomy', 'ct_post_types', 'options' ) ] ); } + pods_debug_log_data( compact( 'taxonomy', 'ct_post_types', 'options' ), 'register-taxonomy', __METHOD__, __LINE__ ); + if ( 1 === (int) pods_v( 'pods_debug_register_export', 'get', 0 ) && pods_is_admin( array( 'pods' ) ) ) { echo '' . esc_html( $taxonomy ) . '
'; echo ''; @@ -1703,6 +1713,8 @@ public function setup_content_types( $force = false ) { pods_debug( [ __METHOD__ . '/register_post_type', compact( 'post_type', 'options' ) ] ); } + pods_debug_log_data( compact( 'post_type', 'options' ), 'register-post-type', __METHOD__, __LINE__ ); + if ( 1 === (int) pods_v( 'pods_debug_register_export', 'get', 0 ) && pods_is_admin( array( 'pods' ) ) ) { echo '' . esc_html( $post_type ) . '
'; echo ''; @@ -1793,7 +1805,7 @@ public function setup_content_types( $force = false ) { $rest_enabled = (boolean) pods_v( 'rest_enable', $pod, false ); if ( $rest_enabled ) { - new PodsRESTFields( $pod['name'] ); + new PodsRESTFields( $pod ); } } @@ -1803,7 +1815,7 @@ public function setup_content_types( $force = false ) { $rest_enabled = (boolean) pods_v( 'rest_enable', $pod, false ); if ( $rest_enabled ) { - new PodsRESTFields( $pod['name'] ); + new PodsRESTFields( $pod ); } } @@ -2479,9 +2491,6 @@ public function run() { // Compatibility with WP 5.4 privacy export. add_filter( 'wp_privacy_additional_user_profile_data', array( $this, 'filter_wp_privacy_additional_user_profile_data' ), 10, 3 ); - // Compatibility for Query Monitor conditionals - add_filter( 'query_monitor_conditionals', array( $this, 'filter_query_monitor_conditionals' ) ); - // Support for quick edit in WP 6.4+. add_filter( 'quick_edit_enabled_for_post_type', [ $this, 'quick_edit_enabled_for_post_type' ], 10, 2 ); add_filter( 'quick_edit_enabled_for_taxonomy', [ $this, 'quick_edit_enabled_for_taxonomy' ], 10, 2 ); @@ -2726,22 +2735,4 @@ public function filter_wp_privacy_additional_user_profile_data( $additional_user return $additional_user_profile_data; } - - /** - * Add Pods conditional functions to Query Monitor. - * - * @param array $conditionals - * @return array - */ - public function filter_query_monitor_conditionals( $conditionals ) { - $conditionals[] = 'pods_developer'; - $conditionals[] = 'pods_tableless'; - $conditionals[] = 'pods_light'; - $conditionals[] = 'pods_strict'; - $conditionals[] = 'pods_allow_deprecated'; - $conditionals[] = 'pods_api_cache'; - $conditionals[] = 'pods_shortcode_allow_evaluate_tags'; - $conditionals[] = 'pods_session_auto_start'; - return $conditionals; - } } diff --git a/classes/PodsMeta.php b/classes/PodsMeta.php index 3840b7862e..f0e6c32020 100644 --- a/classes/PodsMeta.php +++ b/classes/PodsMeta.php @@ -1052,10 +1052,9 @@ public function groups_get( $type, $name, $default_fields = null, $full_objects * * @since 2.6.6 * + * @param array $groups Array of groups * @param string $type The type of Pod * @param string $name Name of the Pod - * - * @param array $groups Array of groups */ $groups = apply_filters( 'pods_meta_groups_get', $groups, $type, $name ); @@ -1167,6 +1166,32 @@ public function meta_post_add( $post_type, $post = null ) { if ( $pods_field_found ) { // Only add the classes to forms that actually have pods fields add_action( 'post_edit_form_tag', array( $this, 'add_class_submittable' ) ); + + $pod = pods( $post_type, null, true ); + + if ( $pod ) { + // Check if we need to disable any specific taxonomies. + $taxonomy_sync_fields = $pod->pod_data->get_fields( [ + 'type' => 'pick', + 'args' => [ + 'pick_object' => 'taxonomy', + 'pick_sync_taxonomy' => 1, + 'pick_sync_taxonomy_hide_taxonomy_ui' => 1, + ], + ] ); + + foreach ( $taxonomy_sync_fields as $taxonomy_sync_field ) { + $taxonomy_name = $taxonomy_sync_field->get_related_object_name(); + + if ( $taxonomy_name ) { + if ( is_taxonomy_hierarchical( $taxonomy_name ) ) { + remove_meta_box( "{$taxonomy_name}div", $post_type, 'side' ); + } else { + remove_meta_box( "tagsdiv-{$taxonomy_name}", $post_type, 'side' ); + } + } + } + } } } @@ -2319,7 +2344,7 @@ public function save_user_track_changed_fields( $user_login ) { if ( ! $no_conflict ) { $user = get_user_by( 'login', $user_login ); - if ( $user && ! is_wp_error( $user ) ) { + if ( $user instanceof WP_User ) { $pod = 'user'; $id = $user->ID; @@ -4298,10 +4323,6 @@ public function update_meta( $object_type, $_null = null, $object_id = 0, $meta_ } $pod->data->row[ $meta_key ] = $meta_value; - - if ( isset( $meta_cache[ '_pods_' . $key ] ) && $field_object && in_array( $field_object['type'], $tableless_field_types, true ) ) { - unset( $meta_cache[ '_pods_' . $key ] ); - } } $pod->save( $meta_key, $meta_value, $object_id, array( @@ -4568,30 +4589,23 @@ public static function split_shared_term( $term_id, $new_term_id, $term_taxonomy /** * @param $id - * - * @return bool */ public function delete_user( $id ) { - return $this->delete_object( 'user', $id ); + $this->delete_object( 'user', $id ); } /** * @param $id - * - * @return bool */ public function delete_comment( $id ) { - return $this->delete_object( 'comment', $id ); + $this->delete_object( 'comment', $id ); } /** * @param $id - * - * @return bool */ public function delete_media( $id ) { - - return $this->delete_object( 'media', $id ); + $this->delete_object( 'media', $id ); } /** diff --git a/classes/PodsRESTFields.php b/classes/PodsRESTFields.php index e44ca0bdbc..66bad2ccd6 100644 --- a/classes/PodsRESTFields.php +++ b/classes/PodsRESTFields.php @@ -102,6 +102,28 @@ public function set_pod( $pod ) { $this->pod = $pod; } + /** + * Validates if a current user or application is logged in. + * + * @return bool + */ + public static function is_rest_authenticated(): bool { + $is_rest_authenticated = (bool) pods_static_cache_get( __FUNCTION__, __CLASS__ ); + + if ( $is_rest_authenticated ) { + return true; + } + + $is_rest_authenticated = ( + is_user_logged_in() + || wp_validate_application_password( get_current_user_id() ) + ); + + pods_static_cache_set( __FUNCTION__, (int) $is_rest_authenticated, __CLASS__ ); + + return $is_rest_authenticated; + } + /** * Add fields, based on options to REST read/write requests * @@ -225,12 +247,19 @@ public static function field_allowed_to_extend( $field, $pod, $mode ) { $pod_mode_arg = $mode . '_all'; - $all_fields_can_use_mode = filter_var( $pod->get_arg( $pod_mode_arg, false ), FILTER_VALIDATE_BOOLEAN ); + $pod_mode = $pod->get_arg( $pod_mode_arg, false ); + + // Backcompat for a previous bug in Pods < 3.2.7 where the default value was the pod name instead of '0'. + if ( $pod_mode === $pod->get_name() ) { + $pod_mode = 0; + } + + $all_fields_can_use_mode = filter_var( $pod_mode, FILTER_VALIDATE_BOOLEAN ); $all_fields_access = 'read' === $mode && filter_var( $pod->get_arg( 'read_all_access', false ), FILTER_VALIDATE_BOOLEAN ); // Check if user must be logged in to access all fields and override whether they can use it. if ( $all_fields_can_use_mode && $all_fields_access ) { - $all_fields_can_use_mode = is_user_logged_in(); + $all_fields_can_use_mode = self::is_rest_authenticated(); } // Maybe get the Field object from the Pod. @@ -260,7 +289,7 @@ public static function field_allowed_to_extend( $field, $pod, $mode ) { // Check if user must be logged in to access field and override whether they can use it. if ( $can_use_mode && $access ) { - $can_use_mode = is_user_logged_in(); + $can_use_mode = self::is_rest_authenticated(); } return $can_use_mode; diff --git a/classes/PodsRESTHandlers.php b/classes/PodsRESTHandlers.php index 3741344f80..0b4f8d12ba 100755 --- a/classes/PodsRESTHandlers.php +++ b/classes/PodsRESTHandlers.php @@ -17,29 +17,35 @@ class PodsRESTHandlers { * * @var Pods */ - private static $pod; + private static $pods = false; /** - * Get Pod object + * Get the Pods object. * * @since 2.5.6 * - * @param $pod_name - * @param $id + * @param string $pod_name The pod name. + * @param string|int $id The item ID. * - * @return bool|Pods + * @return bool|Pods The Pods object or false if not found. */ - protected static function get_pod( $pod_name, $id ) { + public static function get_pods_object( $pod_name, $id ) { - if ( ! self::$pod || self::$pod->pod !== $pod_name ) { - self::$pod = pods_get_instance( $pod_name, $id, true ); + if ( ! self::$pods || self::$pods->pod !== $pod_name ) { + self::$pods = pods_get_instance( $pod_name, $id, true ); } - if ( self::$pod && (int) self::$pod->id !== (int) $id ) { - self::$pod->fetch( $id ); + if ( self::$pods ) { + if ( (int) self::$pods->id !== (int) $id ) { + self::$pods->fetch( $id ); + } + + if ( ! self::$pods->exists() ) { + return false; + } } - return self::$pod; + return self::$pods; } @@ -81,15 +87,15 @@ public static function get_handler( $object, $field_name, $request, $object_type } /** - * Filter the pod name + * Filter the pod name for the REST API handler. * * @since 2.6.7 * - * @param array $pod_name Pod name - * @param Pods $object Rest object - * @param string $field_name Name of the field - * @param WP_REST_Request $request Current request - * @param string $object_type Rest Object type + * @param array $pod_name The Pod name. + * @param array $object The REST object. + * @param string $field_name The name of the field. + * @param WP_REST_Request $request The current request. + * @param string $object_type The REST object type. */ $pod_name = apply_filters( 'pods_rest_api_pod_name', $pod_name, $object, $field_name, $request, $object_type ); @@ -99,7 +105,7 @@ public static function get_handler( $object, $field_name, $request, $object_type $id = pods_v( 'ID', $object ); } - $pod = self::get_pod( $pod_name, $id ); + $pod = self::get_pods_object( $pod_name, $id ); $value = false; @@ -125,7 +131,7 @@ public static function get_handler( $object, $field_name, $request, $object_type * @param array $field_data The field data * @param object|Pods $pod The Pods object for Pod relationship is from. * @param int $id Current item ID - * @param object|WP_REST_Request Current request object. + * @param object|WP_REST_Request $request Current request object. */ $output_type = apply_filters( 'pods_rest_api_output_type_for_relationship_response', $output_type, $field_name, $field_data, $pod, $id, $request ); @@ -267,7 +273,7 @@ public static function save_handler( $object, $request, $creating ) { $pod_name = 'media'; } - $pod = self::get_pod( $pod_name, $id ); + $pod = self::get_pods_object( $pod_name, $id ); global $wp_rest_additional_fields; diff --git a/classes/PodsUI.php b/classes/PodsUI.php index 21216b6020..c9188920d7 100644 --- a/classes/PodsUI.php +++ b/classes/PodsUI.php @@ -56,6 +56,13 @@ class PodsUI { */ public $id = 0; + /** + * The inserted item ID. + * + * @var int + */ + public $insert_id = 0; + /** * The prefix used for all URL parameters used by PodsUI. * @@ -2352,11 +2359,13 @@ public function get_params( $params = null, $action = null ) { 'pagination' => true, 'limit' => (int) $limit, 'search' => $this->searchable, + 'search_var' => $this->num_prefix . 'search' . $this->num, 'search_query' => $this->search, 'search_across' => $this->search_across, 'search_across_picks' => $this->search_across_picks, - 'fields' => $this->fields['search'], + 'filter_var' => $this->num_prefix . 'filter' . $this->num, 'filters' => $this->filters, + 'fields' => $this->fields['search'], 'sql' => $sql, ); @@ -2371,8 +2380,10 @@ public function get_params( $params = null, $action = null ) { 'page' => (int) $this->page, 'pagination' => true, 'limit' => (int) $limit, + 'search_var' => $this->num_prefix . 'search' . $this->num, 'search' => $this->searchable, 'search_query' => $this->search, + 'filter_var' => $this->num_prefix . 'filter' . $this->num, 'fields' => $this->fields['search'], 'sql' => $sql, ); @@ -2401,7 +2412,7 @@ public function get_params( $params = null, $action = null ) { * * @param array $find_params Parameters used with Pods::find() * @param string $action Current action - * @param PodsUI $this PodsUI instance + * @param PodsUI $obj PodsUI instance * * @since 2.6.8 */ @@ -2409,9 +2420,12 @@ public function get_params( $params = null, $action = null ) { // Debug purposes if ( 1 == pods_v( 'pods_debug_params', 'get', 0 ) && pods_is_admin( array( 'pods' ) ) ) { + pods_debug( __METHOD__ . ':' . __LINE__ ); pods_debug( $find_params ); } + pods_debug_log_data( $find_params, 'pods-ui-find-params', __METHOD__, __LINE__ ); + return $find_params; } @@ -2634,9 +2648,9 @@ public function manage( $reorder = false ) { * @since 2.6.8 * * @param array $custom_container_classes List of custom classes to use. - * @param PodsUI $this PodsUI instance. + * @param PodsUI $obj PodsUI instance. */ - $custom_container_classes = apply_filters( 'pods_ui_manage_custom_container_classes', array() ); + $custom_container_classes = apply_filters( 'pods_ui_manage_custom_container_classes', array(), $this ); if ( is_admin() ) { array_unshift( $custom_container_classes, 'wrap' ); @@ -2756,18 +2770,18 @@ public function manage( $reorder = false ) { } if ( in_array( $filter_field['type'], array( 'date', 'datetime', 'time' ) ) ) { - if ( '' == pods_v( 'filter_' . $filter . '_start', 'get', '', true ) && '' == pods_v( 'filter_' . $filter . '_end', 'get', '', true ) ) { + if ( '' == pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_start', 'get', '', true ) && '' == pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end', 'get', '', true ) ) { unset( $filters[ $k ] ); continue; } - } elseif ( '' === pods_v( 'filter_' . $filter, 'get', '' ) ) { + } elseif ( '' === pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter, 'get', '' ) ) { unset( $filters[ $k ] ); continue; } - $excluded_filters[] = 'filter_' . $filter . '_start'; - $excluded_filters[] = 'filter_' . $filter . '_end'; - $excluded_filters[] = 'filter_' . $filter; + $excluded_filters[] = $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_start'; + $excluded_filters[] = $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end'; + $excluded_filters[] = $this->num_prefix . 'filter' . $this->num . '_' . $filter; }//end foreach $this->hidden_vars( $excluded_filters ); @@ -2814,8 +2828,8 @@ public function manage( $reorder = false ) { num_prefix . 'filter' . $this->num . '_' . $filter . '_start', 'get', pods_v( 'filter_default', $filter_field, '', true ), true ); + $end = pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end', 'get', pods_v( 'filter_ongoing_default', $filter_field, '', true ), true ); // override default value $filter_field['default_value'] = ''; @@ -2843,7 +2857,7 @@ public function manage( $reorder = false ) { '', ), - PodsForm::field( 'filter_' . $filter . '_start', $start, $filter_field['type'], $filter_field ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_start', $start, $filter_field['type'], $filter_field ) ); ?> @@ -2861,10 +2875,10 @@ public function manage( $reorder = false ) { '', ), - PodsForm::field( 'filter_' . $filter . '_end', $end, $filter_field['type'], $filter_field ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end', $end, $filter_field['type'], $filter_field ) ); } elseif ( 'pick' === $filter_field['type'] ) { - $value = pods_v( 'filter_' . $filter ); + $value = pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter ); if ( '' === $value ) { $value = pods_v( 'filter_default', $filter_field );} @@ -2895,10 +2909,10 @@ public function manage( $reorder = false ) { '', ), - PodsForm::field( 'filter_' . $filter, $value, 'pick', $options ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter, $value, 'pick', $options ) ); } elseif ( 'boolean' === $filter_field['type'] ) { - $value = pods_v( 'filter_' . $filter, 'get', '' ); + $value = pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter, 'get', '' ); if ( '' === $value ) { $value = pods_v( 'filter_default', $filter_field );} @@ -2935,10 +2949,10 @@ public function manage( $reorder = false ) { '', ), - PodsForm::field( 'filter_' . $filter, $value, 'pick', $options ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter, $value, 'pick', $options ) ); } else { - $value = pods_v( 'filter_' . $filter ); + $value = pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter ); if ( '' === $value ) { $value = pods_v( 'filter_default', $filter_field ); @@ -2965,7 +2979,7 @@ public function manage( $reorder = false ) { '', ), - PodsForm::field( 'filter_' . $filter, $value, 'text', $options ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter, $value, 'text', $options ) ); }//end if ?> @@ -2992,9 +3006,9 @@ public function manage( $reorder = false ) { ); foreach ( $this->filters as $filter ) { - $clear_filters[ 'filter_' . $filter . '_start' ] = false; - $clear_filters[ 'filter_' . $filter . '_end' ] = false; - $clear_filters[ 'filter_' . $filter ] = false; + $clear_filters[ $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_start' ] = false; + $clear_filters[ $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end' ] = false; + $clear_filters[ $this->num_prefix . 'filter' . $this->num . '_' . $filter ] = false; } ?>
@@ -3159,10 +3173,10 @@ public function filters() { } if ( isset( $filter_field ) && in_array( $filter_field['type'], array( 'date', 'datetime', 'time' ) ) ) { - if ( '' == pods_v( 'filter_' . $filter . '_start', 'get', '', true ) && '' == pods_v( 'filter_' . $filter . '_end', 'get', '', true ) ) { + if ( '' == pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_start', 'get', '', true ) && '' == pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end', 'get', '', true ) ) { unset( $filters[ $k ] ); } - } elseif ( '' === pods_v( 'filter_' . $filter, 'get', '' ) ) { + } elseif ( '' === pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter, 'get', '' ) ) { unset( $filters[ $k ] ); } } @@ -3226,9 +3240,9 @@ public function filters() { ); foreach ( $this->filters as $filter ) { - $clear_filters[ 'filter_' . $filter . '_start' ] = false; - $clear_filters[ 'filter_' . $filter . '_end' ] = false; - $clear_filters[ 'filter_' . $filter ] = false; + $clear_filters[ $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_start' ] = false; + $clear_filters[ $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end' ] = false; + $clear_filters[ $this->num_prefix . 'filter' . $this->num . '_' . $filter ] = false; } ?> "> num_prefix . 'filter' . $this->num . '_' . $filter . '_start', 'get', pods_v( 'filter_default', $filter_field, '', true ), true ); + $end = pods_v( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end', 'get', pods_v( 'filter_ongoing_default', $filter_field, '', true ), true ); // override default value $filter_field['default_value'] = ''; @@ -3527,7 +3541,7 @@ public function filters_popup() { '', ), - PodsForm::field( 'filter_' . $filter . '_start', $start, $filter_field['type'], $filter_field, $pod ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_start', $start, $filter_field['type'], $filter_field, $pod ) ); ?> @@ -3543,13 +3557,13 @@ public function filters_popup() { '', ), - PodsForm::field( 'filter_' . $filter . '_end', $end, $filter_field['type'], $filter_field, $pod ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter . '_end', $end, $filter_field['type'], $filter_field, $pod ) ); ?> num_prefix . 'filter' . $this->num . '_' . $filter, 'get', '', true ); if ( '' === $value ) { $value = pods_v( 'filter_default', $filter_field, '', true ); @@ -3586,13 +3600,13 @@ public function filters_popup() { '', ), - PodsForm::field( 'filter_' . $filter, $value, 'pick', $options, $pod ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter, $value, 'pick', $options, $pod ) ); ?> num_prefix . 'filter' . $this->num . '_' . $filter, 'get', '', true ); if ( '' === $value ) { $value = pods_v( 'filter_default', $filter_field, '', true ); @@ -3635,13 +3649,13 @@ public function filters_popup() { '', ), - PodsForm::field( 'filter_' . $filter, $value, 'pick', $options, $pod ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter, $value, 'pick', $options, $pod ) ); ?> num_prefix . 'filter' . $this->num . '_' . $filter, 'get', '', true ); if ( '' === $value ) { $value = pods_v( 'filter_default', $filter_field, '', true ); @@ -3674,7 +3688,7 @@ public function filters_popup() { '', ), - PodsForm::field( 'filter_' . $filter, $value, 'text', $options, $pod ) + PodsForm::field( $this->num_prefix . 'filter' . $this->num . '_' . $filter, $value, 'text', $options, $pod ) ); ?> @@ -4794,7 +4808,7 @@ public function pagination( $header = false ) { $this->num_prefix . 'orderby' . $this->num, $this->num_prefix . 'orderby_dir' . $this->num, $this->num_prefix . 'search' . $this->num, - 'filter_*', + $this->num_prefix . 'filter' . $this->num .'_*', $this->num_prefix . 'view' . $this->num, $this->num_prefix . 'pg' . $this->num, 'page', @@ -4939,7 +4953,7 @@ public function limit( $options = false ) { $this->num_prefix . 'orderby' . $this->num, $this->num_prefix . 'orderby_dir' . $this->num, $this->num_prefix . 'search' . $this->num, - 'filter_*', + $this->num_prefix . 'filter' . $this->num .'_*', 'page', ), $this->exclusion() ) diff --git a/classes/PodsView.php b/classes/PodsView.php index 12ccca14b0..c3e683569e 100644 --- a/classes/PodsView.php +++ b/classes/PodsView.php @@ -412,7 +412,7 @@ public static function get( $key, $cache_mode = 'cache', $group = '', $callback $pods_nocache = pods_v( 'pods_nocache' ); $nocache = array(); - if ( null !== $pods_nocache && pods_is_admin() ) { + if ( null !== $pods_nocache && 'static-cache' !== $cache_mode && pods_is_admin() ) { if ( is_string( $pods_nocache ) && 1 < strlen( $pods_nocache ) ) { $nocache = explode( ',', $pods_nocache ); $nocache = array_flip( $nocache ); diff --git a/classes/fields/pick.php b/classes/fields/pick.php index df980c2845..bc150f3895 100644 --- a/classes/fields/pick.php +++ b/classes/fields/pick.php @@ -449,7 +449,23 @@ public function options() { '^taxonomy-.*$', ], ], - ] + 'type' => 'boolean', + 'default' => 0, + ], + static::$type . '_sync_taxonomy_hide_taxonomy_ui' => [ + 'label' => __( 'Hide the associated taxonomy UI from the Editor', 'pods' ), + 'help' => __( 'This will hide the taxonomy meta box from the Classic Editor and disable the taxonomy panel in the Block Editor.', 'pods' ), + 'depends-on' => [ + static::$type . '_sync_taxonomy' => true, + ], + 'wildcard-on' => [ + static::$type . '_object' => [ + '^taxonomy-.*$', + ], + ], + 'type' => 'boolean', + 'default' => 0, + ], ]; $post_type_pick_objects = array(); @@ -1925,24 +1941,28 @@ public function save( $value, $id = null, $name = null, $options = null, $fields } elseif ( $options instanceof Field || $options instanceof Value_Field ) { $related_field = $options->get_bidirectional_field(); - if ( ! $related_field ) { - return; - } - - $related_pod = $related_field->get_parent_object(); - $related_pick_limit = $related_field->get_limit(); + if ( $related_field ) { + $related_pod = $related_field->get_parent_object(); + $related_pick_limit = $related_field->get_limit(); - if ( null === $current_ids ) { - $current_ids = self::$api->lookup_related_items( $options['id'], $pod['id'], $id, $options, $pod ); - } + if ( null === $current_ids ) { + $current_ids = self::$api->lookup_related_items( $options['id'], $pod['id'], $id, $options, $pod ); + } - // Get ids to remove. - if ( null === $remove_ids ) { - $remove_ids = array_diff( $current_ids, $value_ids ); + // Get ids to remove. + if ( null === $remove_ids ) { + $remove_ids = array_diff( $current_ids, $value_ids ); + } } } - if ( empty( $related_field ) || empty( $related_pod ) ) { + if ( + ( + empty( $related_field ) + || empty( $related_pod ) + ) + && empty( $options[ static::$type . '_sync_taxonomy' ] ) + ) { return; } @@ -2211,7 +2231,7 @@ public function data( $name, $value = null, $options = null, $pod = null, $id = * @param array|null $pod Pod information. * @param int|string|null $id Current item ID. */ - $always_show_default_select_text = (bool) apply_filters( 'pods_field_pick_always_show_default_select_text', false, $data, $name, $value, $options, $pod, $id ); + $always_show_default_select_text = (bool) apply_filters( 'pods_field_pick_always_show_default_select_text', (bool) pods_v( static::$type . '_select_text_always_show', $options, false ), $data, $name, $value, $options, $pod, $id ); if ( 'single' === pods_v( static::$type . '_format_type', $options, 'single' ) diff --git a/components/Pages.php b/components/Pages.php index 846f67c663..72cec141e5 100644 --- a/components/Pages.php +++ b/components/Pages.php @@ -632,7 +632,7 @@ public function clear_cache( $data, $pod = null, $id = null, $groups = null, $po * @param $text * @param $post * - * @return string|void + * @return string */ public function set_title_text( $text, $post ) { return __( 'Enter URL here', 'pods' ); @@ -991,7 +991,7 @@ public function page_check() { * * @param bool $pods_page * - * @return string + * @return string|false */ public static function content( $return = false, $pods_page = false ) { @@ -1026,11 +1026,17 @@ public static function content( $return = false, $pods_page = false ) { do_action( 'pods_content_pre', $pods_page, $content ); - if ( 0 < strlen( $content ) ) { - if ( false !== strpos( $content, '' ) && ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) ) { - pods_deprecated( 'Pod Page PHP code has been deprecated, please use WP Page Templates or hook into the pods_content filter instead of embedding PHP.', '2.1' ); + if ( $content && 0 < strlen( $content ) ) { + // @todo Remove this code in Pods 3.3 and completely ignore any $code that starts with in the string. + if ( false !== strpos( $content, '' ) ) { + _doing_it_wrong( 'Pods Pages', 'Pod Page Precode PHP code is no longer actively supported and will be completely removed in Pods 3.3', '3.0' ); - eval( "?>$content" ); + // Only use $content if eval is enabled. + if ( ! PODS_DISABLE_EVAL ) { + pods_deprecated( 'Pod Page PHP code has been deprecated, please use WP Page Templates or hook into the pods_content filter instead of embedding PHP.', '2.1' ); + + eval( "?>$content" ); + } } elseif ( is_object( $pods ) && ! empty( $pods->id ) ) { echo $pods->do_magic_tags( $content ); } else { @@ -1054,6 +1060,8 @@ public static function content( $return = false, $pods_page = false ) { } echo $content; + + return ''; } /** @@ -1078,8 +1086,8 @@ public function precode() { if ( $permission ) { $content = false; - if ( ! is_object( $pods ) && 404 != $pods && 0 < strlen( (string) pods_var( 'pod', self::$exists['options'] ) ) ) { - $slug = pods_var_raw( 'pod_slug', self::$exists['options'], null, null, true ); + if ( ! is_object( $pods ) && 404 != $pods && 0 < strlen( (string) pods_v( 'pod', self::$exists['options'] ) ) ) { + $slug = pods_v( 'pod_slug', self::$exists['options'], null, null, true ); $has_slug = 0 < strlen( $slug ); @@ -1088,7 +1096,7 @@ public function precode() { $slug = pods_evaluate_tags( $slug, true ); } - $pods = pods_get_instance( pods_var( 'pod', self::$exists['options'] ), $slug ); + $pods = pods_get_instance( pods_v_sanitized( 'pod', self::$exists['options'] ), $slug ); // Auto 404 handling if item doesn't exist if ( $has_slug && ( empty( $slug ) || ! $pods->exists() ) && apply_filters( 'pods_pages_auto_404', true, $slug, $pods, self::$exists ) ) { @@ -1097,13 +1105,18 @@ public function precode() { } if ( 0 < strlen( trim( self::$exists['precode'] ) ) ) { - $content = self::$exists['precode']; + $content = trim( self::$exists['precode'] ); } - if ( false !== $content && ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) ) { - pods_deprecated( 'Pod Page Precode has been deprecated, please use WP Page Templates or hook into the pods_content filter instead of embedding PHP.', '2.1' ); + // @todo Remove this code in Pods 3.3. + if ( $content && 0 < strlen( $content ) ) { + _doing_it_wrong( 'Pods Pages', 'Pod Page Precode PHP code is no longer actively supported and will be completely removed in Pods 3.3', '3.0' ); + + if ( ! PODS_DISABLE_EVAL ) { + pods_deprecated( 'Pod Page Precode has been deprecated, please use WP Page Templates or hook into the pods_content filter instead of embedding PHP.', '2.1' ); - eval( "?>$content" ); + eval( "?>$content" ); + } } do_action( 'pods_page_precode', self::$exists, $pods, $content ); diff --git a/components/Templates/Templates.php b/components/Templates/Templates.php index 0db5e5d524..1886b9d219 100644 --- a/components/Templates/Templates.php +++ b/components/Templates/Templates.php @@ -420,7 +420,7 @@ public function clear_cache( $data, $pod = null, $id = null, $groups = null, $po * @param $text * @param $post * - * @return string|void + * @return string */ public function set_title_text( $text, $post ) { return __( 'Enter template name here', 'pods' ); @@ -542,6 +542,8 @@ public static function template( $template_name, $code = null, $obj = null, $dep return ''; } + /** @var Pods $obj */ + $template = array( 'id' => 0, 'name' => $template_name, @@ -611,6 +613,8 @@ public static function template( $template_name, $code = null, $obj = null, $dep if ( ! empty( $code ) ) { // Only detail templates need $this->id if ( empty( $obj->id ) ) { + $obj->reset(); + while ( $obj->fetch() ) { $info['item_id'] = $obj->id(); @@ -705,25 +709,34 @@ public static function do_template( $code, $obj = null ) { $obj =& self::$obj; } - if ( empty( $obj ) || ! is_object( $obj ) ) { + if ( empty( $obj ) || ! is_object( $obj ) || ! is_string( $code ) ) { return ''; } - if ( false !== strpos( $code, '' ) && ( ! defined( 'PODS_DISABLE_EVAL' ) || ! PODS_DISABLE_EVAL ) ) { - pods_deprecated( 'Pod Template PHP code has been deprecated, please use WP Templates instead of embedding PHP.', '2.3' ); + $out = ''; + + if ( false !== strpos( $code, '' ) ) { + // @todo Remove this code in Pods 3.3 and completely ignore any $code that starts with in the string. + _doing_it_wrong( 'Pods Templates', 'Pod Template PHP code is no longer actively supported and will be completely removed in Pods 3.3', '3.0' ); - $code = str_replace( '$this->', '$obj->', $code ); + if ( ! PODS_DISABLE_EVAL ) { + pods_deprecated( 'Pod Template PHP code has been deprecated, please use WP Templates instead of embedding PHP.', '2.3' ); - ob_start(); + $code = str_replace( '$this->', '$obj->', $code ); - eval( "?>$code" ); + ob_start(); - $out = ob_get_clean(); + eval( "?>$code" ); + + $out = (string) ob_get_clean(); + } } else { $out = $code; } - $out = $obj->do_magic_tags( $out ); + if ( '' !== trim( $out ) ) { + $out = $obj->do_magic_tags( $out ); + } // Prevent blank whitespace from being output if nothing came through. if ( '' === trim( $out ) ) { diff --git a/includes/access.php b/includes/access.php index 09ce0e2336..76bb9d7287 100644 --- a/includes/access.php +++ b/includes/access.php @@ -176,7 +176,7 @@ function pods_user_can_access_object( array $args, ?int $user_id, string $access // Check if the user exists. $user = get_userdata( $user_id ); - if ( ! $user || is_wp_error( $user ) ) { + if ( ! $user instanceof WP_User ) { // If the user does not exist and it was not anonymous, do not allow access to an invalid user. if ( 0 < $user_id ) { return false; @@ -761,7 +761,6 @@ function pods_is_type_public( array $args, string $context = 'shortcode' ): bool * @type Pod|null $pod The Pod object (if built or provided). * } * @param string|null $context The context we are checking from (shortcode or null). - * @param Pod|null $pod The Pod object if set. */ return (bool) apply_filters( 'pods_is_type_public', @@ -1188,7 +1187,7 @@ function pods_get_access_admin_notice( array $args, bool $force_message = false ', - esc_html__( 'The content below is not public and may not be available to everyone else.', 'pods' ), + esc_html__( 'The content type or the content below is not public and may not be available to everyone else.', 'pods' ), esc_url( 'https://docs.pods.io/displaying-pods/access-rights-in-pods/' ), esc_html__( 'How access rights work with Pods (Documentation)', 'pods' ), esc_url( admin_url( 'admin.php?page=pods-settings#heading-security' ) ), diff --git a/includes/data.php b/includes/data.php index fa607db803..ea2f1d6b30 100644 --- a/includes/data.php +++ b/includes/data.php @@ -2643,6 +2643,42 @@ function pods_is_falsey( $value ) { return isset( $supported_strings[ $value ] ); } +/** + * Filter out the nulls from the array of data. + * + * @since 3.2.7 + * + * @param array $data The array of data to filter. + * + * @return array The array with nulls filtered out. + */ +function pods_array_filter_null( array $data ): array { + return array_filter( + $data, + static function( $value ) { + return null !== $value; + } + ); +} + +/** + * Filter out the nulls and empty strings from the array of data. + * + * @since 3.2.7 + * + * @param array $data The array of data to filter. + * + * @return array The array with nulls and empty strings filtered out. + */ +function pods_array_filter_null_and_empty_string( array $data ): array { + return array_filter( + $data, + static function( $value ) { + return null !== $value && '' !== $value; + } + ); +} + /** * Make replacements to a string using key=>value pairs. * diff --git a/includes/general.php b/includes/general.php index 699de402ac..34856f6b83 100644 --- a/includes/general.php +++ b/includes/general.php @@ -58,14 +58,12 @@ function pods_query( $sql, $error = 'Database Error', $results_error = null, $no if ( 1 === (int) pods_v( 'pods_debug_sql_all' ) && is_user_logged_in() && pods_is_admin( array( 'pods' ) ) ) { $debug_sql = $sql; - echo '