From 1ffdfdaea7d511acc0e79257ef20d189d740b720 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Sat, 19 Sep 2020 01:20:48 +0000 Subject: [PATCH] Posts, Post Types: Ensure default terms are added by `wp_publish_post()`. Transitioning posts from `auto-draft` to `publish` via `wp_publish_post()` could result in published posts without the default category or custom taxonomy default terms. Props frank-klein, TimothyBlynJacobs, peterwilsoncc. Fixes #51292. git-svn-id: https://develop.svn.wordpress.org/trunk@49000 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/post.php | 27 +++ tests/phpunit/tests/post/wpPublishPosts.php | 153 ++++++++++++++++ tests/phpunit/tests/term/termCounts.php | 186 ++++++++++++++++++++ 3 files changed, 366 insertions(+) create mode 100644 tests/phpunit/tests/post/wpPublishPosts.php create mode 100644 tests/phpunit/tests/term/termCounts.php diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 1088d4ffb5..9456bec763 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -4375,6 +4375,33 @@ function wp_publish_post( $post ) { return; } + // Ensure at least one term is applied for taxonomies with a default term. + foreach ( get_object_taxonomies( $post->post_type, 'object' ) as $taxonomy => $tax_object ) { + // Skip taxonomy if no default term is set. + if ( + 'category' !== $taxonomy && + empty( $tax_object->default_term ) + ) { + continue; + } + + // Do not modify previously set terms. + if ( ! empty( get_the_terms( $post, $taxonomy ) ) ) { + continue; + } + + if ( 'category' === $taxonomy ) { + $default_term_id = (int) get_option( 'default_category', 0 ); + } else { + $default_term_id = (int) get_option( 'default_term_' . $taxonomy, 0 ); + } + + if ( ! $default_term_id ) { + continue; + } + wp_set_post_terms( $post->ID, array( $default_term_id ), $taxonomy ); + } + $wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'ID' => $post->ID ) ); clean_post_cache( $post->ID ); diff --git a/tests/phpunit/tests/post/wpPublishPosts.php b/tests/phpunit/tests/post/wpPublishPosts.php new file mode 100644 index 0000000000..42e3884d94 --- /dev/null +++ b/tests/phpunit/tests/post/wpPublishPosts.php @@ -0,0 +1,153 @@ +post->create( array( 'post_status' => 'auto-draft' ) ); + } + + /** + * Ensure wp_publish_post does not add default category in error. + * + * @ticket 51292 + */ + function test_wp_publish_post_respects_current_categories() { + $post_id = self::$auto_draft_id; + $category_id = $this->factory->term->create( array( 'taxonomy' => 'category' ) ); + wp_set_post_categories( $post_id, $category_id ); + wp_publish_post( $post_id ); + + $post_categories = get_the_category( $post_id ); + $this->assertCount( 1, $post_categories ); + $this->assertSame( + $category_id, + $post_categories[0]->term_id, + 'wp_publish_post replaced set category.' + ); + } + + /** + * Ensure wp_publish_post adds default category. + * + * @covers wp_publish_post + * @ticket 51292 + */ + function test_wp_publish_post_adds_default_category() { + $post_id = self::$auto_draft_id; + + wp_publish_post( $post_id ); + + $post_categories = get_the_category( $post_id ); + $this->assertCount( 1, $post_categories ); + $this->assertSame( + (int) get_option( 'default_category' ), + $post_categories[0]->term_id, + 'wp_publish_post failed to add default category.' + ); + } + + /** + * Ensure wp_publish_post adds default category when tagged. + * + * @covers wp_publish_post + * @ticket 51292 + */ + function test_wp_publish_post_adds_default_category_when_tagged() { + $post_id = self::$auto_draft_id; + $tag_id = $this->factory->term->create( array( 'taxonomy' => 'post_tag' ) ); + wp_set_post_tags( $post_id, array( $tag_id ) ); + wp_publish_post( $post_id ); + + $post_categories = get_the_category( $post_id ); + $this->assertCount( 1, $post_categories ); + $this->assertSame( + (int) get_option( 'default_category' ), + $post_categories[0]->term_id, + 'wp_publish_post failed to add default category.' + ); + } + + /** + * Ensure wp_publish_post does not add default term in error. + * + * @covers wp_publish_post + * @ticket 51292 + */ + function test_wp_publish_post_respects_current_terms() { + // Create custom taxonomy to test with. + register_taxonomy( + 'tax_51292', + 'post', + array( + 'hierarchical' => true, + 'public' => true, + 'default_term' => array( + 'name' => 'Default 51292', + 'slug' => 'default-51292', + ), + ) + ); + + $post_id = self::$auto_draft_id; + $term_id = $this->factory->term->create( array( 'taxonomy' => 'tax_51292' ) ); + wp_set_object_terms( $post_id, array( $term_id ), 'tax_51292' ); + wp_publish_post( $post_id ); + + $post_terms = get_the_terms( $post_id, 'tax_51292' ); + $this->assertCount( 1, $post_terms ); + $this->assertSame( + $term_id, + $post_terms[0]->term_id, + 'wp_publish_post replaced set term for custom taxonomy.' + ); + } + + /** + * Ensure wp_publish_post adds default term. + * + * @covers wp_publish_post + * @ticket 51292 + */ + function test_wp_publish_post_adds_default_term() { + // Create custom taxonomy to test with. + register_taxonomy( + 'tax_51292', + 'post', + array( + 'hierarchical' => true, + 'public' => true, + 'default_term' => array( + 'name' => 'Default 51292', + 'slug' => 'default-51292', + ), + ) + ); + + $post_id = self::$auto_draft_id; + + wp_publish_post( $post_id ); + + $post_terms = get_the_terms( $post_id, 'tax_51292' ); + $this->assertCount( 1, $post_terms ); + $this->assertSame( + get_term_by( 'slug', 'default-51292', 'tax_51292' )->term_id, + $post_terms[0]->term_id, + 'wp_publish_post failed to add default term for custom taxonomy.' + ); + } +} diff --git a/tests/phpunit/tests/term/termCounts.php b/tests/phpunit/tests/term/termCounts.php new file mode 100644 index 0000000000..284d5f90ea --- /dev/null +++ b/tests/phpunit/tests/term/termCounts.php @@ -0,0 +1,186 @@ +post->create( array( 'post_status' => $status ) ); + } + + register_taxonomy( 'wp_test_tax_counts', array( 'post', 'attachment' ) ); + self::$attachment_term = $factory->term->create( array( 'taxonomy' => 'wp_test_tax_counts' ) ); + } + + public function setUp() { + parent::setUp(); + + register_taxonomy( 'wp_test_tax_counts', array( 'post', 'attachment' ) ); + } + + /** + * Term counts increments correctly when post status becomes published. + * + * @covers wp_publish_post + * @covers wp_count_terms + * @dataProvider data_term_counts_incremented_on_publish + * @ticket 40351 + * @ticket 51292 + * + * @param string $original_post_status Post status prior to change to publish. + * @param int $change Expected change upon publish. + */ + public function test_term_counts_incremented_on_publish( $original_post_status, $change ) { + $post_id = self::$post_ids[ $original_post_status ]; + $term_count = get_term( get_option( 'default_category' ) )->count; + + wp_publish_post( $post_id ); + + $expected = $term_count + $change; + $this->assertSame( $expected, get_term( get_option( 'default_category' ) )->count ); + } + + /** + * Data provider for test_term_count_changes_for_post_statuses. + * + * @return array[] { + * @type string $original_post_status Post status prior to change to publish. + * @type int $change Expected change upon publish. + * } + */ + function data_term_counts_incremented_on_publish() { + return array( + // 0. Published post + array( 'publish', 0 ), + // 1. Auto draft + array( 'auto-draft', 1 ), + // 2. Draft + array( 'draft', 1 ), + // 3. Private post + array( 'private', 1 ), + ); + } + + /** + * Term counts increments correctly when post status becomes published. + * + * @covers wp_publish_post + * @dataProvider data_term_counts_incremented_on_publish_with_attachments + * @ticket 40351 + * @ticket 51292 + * + * @param string $original_post_status Post status prior to change to publish. + * @param int $change Expected change upon publish. + */ + public function test_term_counts_incremented_on_publish_with_attachments( $original_post_status, $change ) { + $post_id = self::$post_ids[ $original_post_status ]; + wp_add_object_terms( $post_id, self::$attachment_term, 'wp_test_tax_counts' ); + $attachment_id = self::factory()->attachment->create_object( + array( + 'file' => 'image.jpg', + 'post_parent' => $post_id, + 'post_status' => 'inherit', + ) + ); + wp_add_object_terms( $attachment_id, self::$attachment_term, 'wp_test_tax_counts' ); + $term_count = get_term( self::$attachment_term )->count; + + wp_publish_post( $post_id ); + + $expected = $term_count + $change; + $this->assertSame( $expected, get_term( self::$attachment_term )->count ); + } + + /** + * Data provider for test_term_count_changes_for_post_statuses_with_attachments. + * + * @return array[] { + * @type string $original_post_status Post status prior to change to publish. + * @type int $change Expected change upon publish. + * } + */ + function data_term_counts_incremented_on_publish_with_attachments() { + return array( + // 0. Published post + array( 'publish', 0 ), + // 1. Auto draft + array( 'auto-draft', 2 ), + // 2. Draft + array( 'draft', 2 ), + // 3. Private post + array( 'private', 2 ), + ); + } + + /** + * Term counts increments correctly when post status becomes published. + * + * @covers wp_publish_post + * @dataProvider data_term_counts_incremented_on_publish_with_untermed_attachments + * @ticket 40351 + * @ticket 51292 + * + * @param string $original_post_status Post status prior to change to publish. + * @param int $change Expected change upon publish. + */ + public function test_term_counts_incremented_on_publish_with_untermed_attachments( $original_post_status, $change ) { + $post_id = self::$post_ids[ $original_post_status ]; + wp_add_object_terms( $post_id, self::$attachment_term, 'wp_test_tax_counts' ); + $attachment_id = self::factory()->attachment->create_object( + array( + 'file' => 'image.jpg', + 'post_parent' => $post_id, + 'post_status' => 'inherit', + ) + ); + $term_count = get_term( self::$attachment_term )->count; + + wp_publish_post( $post_id ); + + $expected = $term_count + $change; + $this->assertSame( $expected, get_term( self::$attachment_term )->count ); + } + + /** + * Data provider for test_term_count_changes_for_post_statuses_with_untermed_attachments. + * + * @return array[] { + * @type string $original_post_status Post status prior to change to publish. + * @type int $change Expected change upon publish. + * } + */ + function data_term_counts_incremented_on_publish_with_untermed_attachments() { + return array( + // 0. Published post + array( 'publish', 0 ), + // 1. Auto draft + array( 'auto-draft', 1 ), + // 2. Draft + array( 'draft', 1 ), + // 3. Private post + array( 'private', 1 ), + ); + } +}