diff --git a/Gruntfile.js b/Gruntfile.js index 2769468..773f183 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -62,7 +62,7 @@ module.exports = function( grunt ) { '*.php', 'css/*', 'js/*', - 'php/*', + 'php/**', 'readme.txt' ], dest: 'build', diff --git a/php/class-wp-customize-posts-preview.php b/php/class-wp-customize-posts-preview.php index 4be9d8a..19a9beb 100644 --- a/php/class-wp-customize-posts-preview.php +++ b/php/class-wp-customize-posts-preview.php @@ -234,6 +234,8 @@ public function filter_the_posts_to_preview_settings( array $posts ) { /** * Get current posts being previewed which should be included in the given query. * + * @todo The $published flag is likely a vestigate of when this was specifically for post_status. Refactoring needed. + * * @access public * * @param \WP_Query $query The query. @@ -257,45 +259,140 @@ public function get_previewed_posts_for_query( WP_Query $query, $published = tru $post_ids = array(); $settings = $this->component->manager->unsanitized_post_values(); - if ( ! empty( $settings ) ) { - foreach ( (array) $settings as $id => $post_data ) { - if ( ! preg_match( WP_Customize_Post_Setting::SETTING_ID_PATTERN, $id, $matches ) ) { - continue; - } - $post_id = intval( $matches['post_id'] ); - $statuses = $query_vars['post_status']; - + foreach ( $settings as $id => $setting_value ) { + $is_match = ( + preg_match( WP_Customize_Postmeta_Setting::SETTING_ID_PATTERN, $id, $matches ) + || + preg_match( WP_Customize_Post_Setting::SETTING_ID_PATTERN, $id, $matches ) + ); + if ( ! $is_match ) { + continue; + } + $statuses = $query_vars['post_status']; + $setting_post_id = intval( $matches['post_id'] ); + $setting_post_type = $matches['post_type']; + $setting_post_meta_key = isset( $matches['meta_key'] ) ? $matches['meta_key'] : null; + $setting_type = $setting_post_meta_key ? 'postmeta' : 'post'; + + if ( 'post' === $setting_type ) { + // Post type match. $post_type_match = ( empty( $query_vars['post_type'] ) || - in_array( $matches['post_type'], $query_vars['post_type'], true ) + in_array( $setting_post_type, $query_vars['post_type'], true ) || ( in_array( 'any', $query_vars['post_type'], true ) && - in_array( $matches['post_type'], get_post_types( array( 'exclude_from_search' => false ) ), true ) + in_array( $setting_post_type, get_post_types( array( 'exclude_from_search' => false ) ), true ) ) ); - $post_type_obj = get_post_type_object( $matches['post_type'] ); - if ( $post_type_obj && current_user_can( $post_type_obj->cap->read_private_posts, $post_id ) ) { + // Post status match. + $post_type_obj = get_post_type_object( $setting_post_type ); + if ( $post_type_obj && current_user_can( $post_type_obj->cap->read_private_posts, $setting_post_id ) ) { $statuses[] = 'private'; } - if ( empty( $query_vars['post_status'] ) ) { $post_status_match = true; } elseif ( false === $published ) { - $post_status_match = ! in_array( $post_data['post_status'], array( 'publish', 'private' ), true ); + $post_status_match = ! in_array( $setting_value['post_status'], array( 'publish', 'private' ), true ); } else { - $post_status_match = in_array( $post_data['post_status'], $statuses, true ); + $post_status_match = in_array( $setting_value['post_status'], $statuses, true ); } - $post__in_match = empty( $query_vars['post__in'] ) || in_array( $post_id, $query_vars['post__in'], true ); + // Post IN match. + $post__in_match = empty( $query_vars['post__in'] ) || in_array( $setting_post_id, $query_vars['post__in'], true ); if ( $post_status_match && $post_type_match && $post__in_match ) { - $post_ids[] = $post_id; + $post_ids[] = $setting_post_id; + } + } elseif ( ! empty( $query->meta_query ) && ! empty( $query->meta_query->queries ) ) { // @todo The $published flag is probably an indication of something awry. + $meta_queries = $query->meta_query->queries; + $relation = $meta_queries['relation']; + unset( $meta_queries['relation'] ); + $matched_queries = array(); + + foreach ( $meta_queries as $meta_query ) { + if ( empty( $meta_query['key'] ) || $meta_query['key'] !== $setting_post_meta_key ) { + continue; + } + + if ( ! isset( $meta_query['value'] ) || '' === $meta_query['value'] ) { + $matched_queries[] = true; + continue; + } + + if ( is_array( $meta_query['value'] ) && ! empty( $meta_query['compare'] ) && 'IN' !== $meta_query['compare'] ) { + continue; + } + + if ( empty( $meta_query['compare'] ) ) { + if ( is_array( $meta_query['value'] ) ) { + $meta_query['compare'] = 'IN'; + } else { + $meta_query['compare'] = '='; + } + } + $is_setting_value_array = is_array( $setting_value ); + if ( '=' === $meta_query['compare'] ) { + $values = $is_setting_value_array ? $setting_value : array( $setting_value ); + $compared_flag = in_array( (string) $meta_query['value'], array_map( 'strval', $values ), true ); + } elseif ( '>=' === $meta_query['compare'] && ! $is_setting_value_array ) { + $compared_flag = ( (string) $setting_value >= (string) $meta_query['value'] ); + } elseif ( '<=' === $meta_query['compare'] && ! $is_setting_value_array ) { + $compared_flag = ( (string) $setting_value <= (string) $meta_query['value'] ); + } elseif ( '>' === $meta_query['compare'] && ! $is_setting_value_array ) { + $compared_flag = ( (string) (string) $setting_value > $meta_query['value'] ); + } elseif ( '<' === $meta_query['compare'] && ! $is_setting_value_array ) { + $compared_flag = ( (string) $setting_value < (string) $meta_query['value'] ); + } elseif ( 'IN' === $meta_query['compare'] && is_array( $meta_query['value'] ) ) { + $values = $is_setting_value_array ? $setting_value : array( $setting_value ); + $common_values = array_intersect( array_map( 'strval', $values ), array_map( 'strval', $meta_query['value'] ) ); + $compared_flag = empty( $common_values ) ? false : true; + } + + if ( isset( $compared_flag ) ) { + // Check should we include this in IN query or NOT IN query. + if ( true === $published ) { + $matched_queries[] = $compared_flag; + } else { + $matched_queries[] = ! $compared_flag; + } + } else { + $matched_queries[] = false; + } + } + + if ( 'AND' === $relation ) { + if ( in_array( false, $matched_queries, true ) ) { + $matched_queries = array(); + } + } else { + $matched_queries = array_filter( $matched_queries ); + } + + if ( ! empty( $matched_queries ) ) { + $post_ids[] = $setting_post_id; } } } + /** + * Filter customize preview posts. + * + * @param array $post_ids Post ids being filtered. + * @param array array { + * Args. + * + * @type \WP_Query $query WP_Query obj. + * @type array $settings Field Values in snapshot. + * @type bool $publish IN query or NOT IN query. + * } + */ + $post_ids = apply_filters( 'customize_previewed_posts_for_query', $post_ids, array( + 'query' => $query, + 'settings' => $settings, + 'publish' => $published, + ) ); return $post_ids; } @@ -313,6 +410,7 @@ public function get_previewed_posts_for_query( WP_Query $query, $published = tru public function filter_posts_where_to_include_previewed_posts( $where, $query ) { global $wpdb; + // @todo There are undoubtedly hundreds of possible conditions that are not being accounted for in regards to all of the possible query vars a WP_Query can take. if ( ! $query->is_singular() ) { $post__not_in = implode( ',', array_map( 'absint', $this->get_previewed_posts_for_query( $query, false ) ) ); if ( ! empty( $post__not_in ) ) { diff --git a/readme.md b/readme.md index ced0a35..f5bbfa0 100644 --- a/readme.md +++ b/readme.md @@ -10,7 +10,6 @@ Edit posts and postmeta in the Customizer. Stop editing your posts/postmeta blin **Tested up to:** 4.6-alpha **Stable tag:** 0.6.1 **License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html) -**Text Domain:** customize-posts [![Build Status](https://travis-ci.org/xwp/wp-customize-posts.svg?branch=master)](https://travis-ci.org/xwp/wp-customize-posts) [![Coverage Status](https://coveralls.io/repos/xwp/wp-customize-posts/badge.svg?branch=master)](https://coveralls.io/github/xwp/wp-customize-posts) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.svg)](http://gruntjs.com) [![devDependency Status](https://david-dm.org/xwp/wp-customize-posts/dev-status.svg)](https://david-dm.org/xwp/wp-customize-posts#info=devDependencies) @@ -75,6 +74,7 @@ The following are listed in reverse chronological order. The first, more recent * Add support for focusing on controls for setting properties when those properties are invalid * Prevent `customized-posts` messages sent via `selective-refresh` from effecting `post-navigation` state * Improve feature detection for including customize-controls patched for trac-36521 +* Included plugin-support and theme-support PHP files that were inadvertantly omitted from the 0.6.0 build. See full commit log: [`0.6.0...0.6.1`](https://github.com/xwp/wp-customize-posts/compare/0.6.0...0.6.1) diff --git a/readme.txt b/readme.txt index 2843185..824cad0 100644 --- a/readme.txt +++ b/readme.txt @@ -6,7 +6,6 @@ Tested up to: 4.6-alpha Stable tag: 0.6.1 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html -Text Domain: customize-posts Edit posts and postmeta in the Customizer. Stop editing your posts/postmeta blind! @@ -73,6 +72,7 @@ The following are listed in reverse chronological order. The first, more recent * Add support for focusing on controls for setting properties when those properties are invalid * Prevent `customized-posts` messages sent via `selective-refresh` from effecting `post-navigation` state * Improve feature detection for including customize-controls patched for trac-36521 +* Included plugin-support and theme-support PHP files that were inadvertantly omitted from the 0.6.0 build. See full commit log: [`0.6.0...0.6.1`](https://github.com/xwp/wp-customize-posts/compare/0.6.0...0.6.1) diff --git a/tests/php/test-class-wp-customize-posts-preview.php b/tests/php/test-class-wp-customize-posts-preview.php index 09453e6..145f357 100644 --- a/tests/php/test-class-wp-customize-posts-preview.php +++ b/tests/php/test-class-wp-customize-posts-preview.php @@ -281,7 +281,7 @@ public function filter_the_posts_to_add_dynamic_post_settings_and_sections() { } /** - * Test get_previewed_drafts method. + * Test get_previewed_posts_for_query method. * * @see WP_Customize_Posts_Preview::get_previewed_posts_for_query() */ @@ -318,6 +318,138 @@ public function test_get_previewed_posts_for_query() { $this->assertEquals( array( $post->ID, $page->ID ), $this->posts_component->preview->get_previewed_posts_for_query( $query ) ); } + /** + * Test querying posts based on meta queries. + * + * @see WP_Customize_Posts_Preview::get_previewed_posts_for_query() + * @see WP_Customize_Posts_Preview::filter_posts_where_to_include_previewed_posts() + */ + public function test_get_previewed_post_for_meta_query() { + $meta_key = 'index'; + $post_type = 'post'; + $this->posts_component->register_post_type_meta( $post_type, $meta_key ); + + $post_data = array(); + foreach ( array( 'foo', 'bar', 'baz', 'qux', 'multi' ) as $i => $name ) { + $post_id = $this->factory()->post->create( array( 'post_title' => $name ) ); + $post = get_post( $post_id ); + $postmeta_setting_id = WP_Customize_Postmeta_Setting::get_post_meta_setting_id( $post, $meta_key ); + if ( 'qux' === $name ) { + $i = 2; + } + if ( 'multi' === $name ) { + $this->wp_customize->set_post_value( $postmeta_setting_id, array( '10', '11', '12' ) ); + } else { + $this->wp_customize->set_post_value( $postmeta_setting_id, (string) $i ); + } + list( $postmeta_setting ) = $this->wp_customize->add_dynamic_settings( array( $postmeta_setting_id ) ); + $this->assertEquals( $postmeta_setting_id, $postmeta_setting->id ); + if ( 'multi' === $name ) { + $post_data[ $name ] = array( + 'post' => $post, + 'postmeta_setting' => $postmeta_setting, + 'index' => array( '10', '11', '12' ), + ); + } else { + $post_data[ $name ] = array( + 'post' => $post, + 'postmeta_setting' => $postmeta_setting, + 'index' => (string) $i, + ); + } + if ( 'qux' === $name ) { + add_post_meta( $post_id, $meta_key, '0', true ); + } + $postmeta_setting->preview(); + if ( 'multi' === $name ) { + $d = get_post_meta( $post_id, $meta_key ); + $this->assertEquals( $post_data[ $name ][ $meta_key ], array_shift( $d ) ); + } else { + $this->assertEquals( $post_data[ $name ][ $meta_key ], get_post_meta( $post_id, $meta_key, true ) ); + } + } + + $query_post_with_index_meta = new WP_Query( array( + 'post_type' => $post_type, + 'meta_key' => $meta_key, + ) ); + $this->assertCount( count( $post_data ), $query_post_with_index_meta->posts ); + + $query_post_with_index_1 = new WP_Query( array( + 'post_type' => $post_type, + 'meta_key' => $meta_key, + 'meta_value' => '1', + ) ); + $this->assertCount( 1, $query_post_with_index_1->posts ); + + $query_post_with_index_gte_1 = new WP_Query( array( + 'post_type' => $post_type, + 'meta_key' => $meta_key, + 'meta_value' => '1', + 'meta_compare' => '>=' + ) ); + $this->assertCount( 4, $query_post_with_index_gte_1->posts ); + + $query_post_with_compound_meta_query = new WP_Query( array( + 'post_type' => $post_type, + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => $meta_key, + 'value' => '0', + 'compare' => '>', + ), + array( + 'key' => $meta_key, + 'value' => '2', + 'compare' => '<', + ) + ), + ) ); + $this->assertCount( 1, $query_post_with_compound_meta_query->posts ); + + $query_post_with_in_query = new WP_Query( array( + 'post_type' => $post_type, + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => $meta_key, + 'value' => array( '11', '1', '2' ), + 'compare' => 'IN', + ), + ), + ) ); + + $this->assertCount( 4, $query_post_with_in_query->posts ); + + $query_post_where_actual_meta_and_snapshot_with_zero = new WP_Query( array( + 'post_type' => $post_type, + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => $meta_key, + 'value' => '0', + ), + ), + ) ); + + $this->assertCount( 1, $query_post_where_actual_meta_and_snapshot_with_zero->posts ); + + $query_post_with_meta_value_as_array_compare_equals = new WP_Query( array( + 'post_type' => $post_type, + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => $meta_key, + 'value' => '11', + 'compare' => '=', + ), + ), + ) ); + + $this->assertCount( 1, $query_post_with_meta_value_as_array_compare_equals->posts ); + } + /** * Test filter_nav_menu_item_to_set_url(). *