From 03cd8d5b7013b5c2ec655e528035c6765c3b1d6e Mon Sep 17 00:00:00 2001 From: Boone Gorges Date: Sun, 18 Jun 2017 10:39:12 +0000 Subject: [PATCH] Add term meta support to XML-RPC `addTerm` and `editTerm` endpoints. This changeset also includes the new function `has_term_meta()`, a counterpart to `has_meta()` (for posts). Props enrico.sorcinelli. Fixes #35991. git-svn-id: https://develop.svn.wordpress.org/trunk@40916 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-xmlrpc-server.php | 75 ++++++++++++++++++++++ src/wp-includes/taxonomy.php | 21 ++++++ tests/phpunit/tests/term/meta.php | 34 ++++++++++ tests/phpunit/tests/xmlrpc/wp/editTerm.php | 70 ++++++++++++++++++++ tests/phpunit/tests/xmlrpc/wp/getTerm.php | 26 ++++++++ tests/phpunit/tests/xmlrpc/wp/getTerms.php | 3 + tests/phpunit/tests/xmlrpc/wp/newTerm.php | 24 +++++++ 7 files changed, 253 insertions(+) diff --git a/src/wp-includes/class-wp-xmlrpc-server.php b/src/wp-includes/class-wp-xmlrpc-server.php index 9b80f0dfe1..a71ddfd44a 100644 --- a/src/wp-includes/class-wp-xmlrpc-server.php +++ b/src/wp-includes/class-wp-xmlrpc-server.php @@ -403,6 +403,68 @@ class wp_xmlrpc_server extends IXR_Server { } } + /** + * Retrieve custom fields for a term. + * + * @since 4.9.0 + * + * @param int $post_id Post ID. + * @return array Array of custom fields, if they exist. + */ + public function get_term_custom_fields( $term_id ) { + $term_id = (int) $term_id; + + $custom_fields = array(); + + foreach ( (array) has_term_meta( $term_id ) as $meta ) { + + if ( ! current_user_can( 'edit_term_meta', $term_id ) ) { + continue; + } + + $custom_fields[] = array( + 'id' => $meta['meta_id'], + 'key' => $meta['meta_key'], + 'value' => $meta['meta_value'], + ); + } + + return $custom_fields; + } + + /** + * Set custom fields for a term. + * + * @since 4.9.0 + * + * @param int $post_id Post ID. + * @param array $fields Custom fields. + */ + public function set_term_custom_fields( $term_id, $fields ) { + $term_id = (int) $term_id; + + foreach ( (array) $fields as $meta ) { + if ( isset( $meta['id'] ) ) { + $meta['id'] = (int) $meta['id']; + $pmeta = get_metadata_by_mid( 'term', $meta['id'] ); + if ( isset( $meta['key'] ) ) { + $meta['key'] = wp_unslash( $meta['key'] ); + if ( $meta['key'] !== $pmeta->meta_key ) { + continue; + } + $meta['value'] = wp_unslash( $meta['value'] ); + if ( current_user_can( 'edit_term_meta', $term_id ) ) { + update_metadata_by_mid( 'term', $meta['id'], $meta['value'] ); + } + } elseif ( current_user_can( 'delete_term_meta', $term_id ) ) { + delete_metadata_by_mid( 'term', $meta['id'] ); + } + } elseif ( current_user_can( 'add_term_meta', $term_id ) ) { + add_term_meta( $term_id, $meta['key'], $meta['value'] ); + } + } + } + /** * Set up blog options property. * @@ -742,6 +804,9 @@ class wp_xmlrpc_server extends IXR_Server { // Count we are happy to return as an integer because people really shouldn't use terms that much. $_term['count'] = intval( $_term['count'] ); + // Get term meta. + $_term['custom_fields'] = $this->get_term_custom_fields( $_term['term_id'] ); + /** * Filters XML-RPC-prepared data for the given term. * @@ -1943,6 +2008,11 @@ class wp_xmlrpc_server extends IXR_Server { if ( ! $term ) return new IXR_Error( 500, __( 'Sorry, your term could not be created.' ) ); + // Add term meta. + if ( isset( $content_struct['custom_fields'] ) ) { + $this->set_term_custom_fields( $term['term_id'], $content_struct['custom_fields'] ); + } + return strval( $term['term_id'] ); } @@ -2042,6 +2112,11 @@ class wp_xmlrpc_server extends IXR_Server { if ( ! $term ) return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) ); + // Update term meta. + if ( isset( $content_struct['custom_fields'] ) ) { + $this->set_term_custom_fields( $term_id, $content_struct['custom_fields'] ); + } + return true; } diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index f1c1bce307..0c4012242c 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -1240,6 +1240,27 @@ function update_termmeta_cache( $term_ids ) { return update_meta_cache( 'term', $term_ids ); } +/** + * Get all meta data, including meta IDs, for the given term ID. + * + * @since 4.9.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $term_id + * @return array|false + */ +function has_term_meta( $term_id ) { + // Bail if term meta table is not installed. + if ( get_option( 'db_version' ) < 34370 ) { + return false; + } + + global $wpdb; + + return $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value, meta_id, term_id FROM $wpdb->termmeta WHERE term_id = %d ORDER BY meta_key,meta_id", $term_id ), ARRAY_A ); +} + /** * Check if Term exists. * diff --git a/tests/phpunit/tests/term/meta.php b/tests/phpunit/tests/term/meta.php index ac309dc622..0676c82bc0 100644 --- a/tests/phpunit/tests/term/meta.php +++ b/tests/phpunit/tests/term/meta.php @@ -404,6 +404,40 @@ class Tests_Term_Meta extends WP_UnitTestCase { $this->assertSame( '', get_term_meta( $t, 'foo1', true ) ); } + /** + * @ticket 35991 + */ + public function test_has_term_meta() { + $t = self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + + $term_meta_id = add_term_meta( $t, 'foo', 'bar' ); + $meta = has_term_meta( $t ); + + $this->assertSame( 1, count( $meta ) ); + + $expected = array( + 'meta_key' => 'foo', + 'meta_value' => 'bar', + 'meta_id' => $term_meta_id, + 'term_id' => $t, + ); + + $found = $meta[0]; + + $this->assertEquals( $expected, $found ); + } + + /** + * @ticket 35991 + */ + public function test_has_term_meta_empty_results() { + $t = self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + + $meta = has_term_meta( $t ); + + $this->assertSame( array(), $meta ); + } + public static function set_cache_results( $q ) { $q->set( 'cache_results', true ); } diff --git a/tests/phpunit/tests/xmlrpc/wp/editTerm.php b/tests/phpunit/tests/xmlrpc/wp/editTerm.php index 1e62de9d1f..89af608d3e 100644 --- a/tests/phpunit/tests/xmlrpc/wp/editTerm.php +++ b/tests/phpunit/tests/xmlrpc/wp/editTerm.php @@ -145,4 +145,74 @@ class Tests_XMLRPC_wp_editTerm extends WP_XMLRPC_UnitTestCase { $this->assertNotIXRError( $result ); $this->assertInternalType( 'boolean', $result ); } + + /** + * @ticket 35991 + */ + public function test_update_term_meta() { + register_taxonomy( 'wptests_tax', 'post' ); + + $t = self::factory()->term->create( array( + 'taxonomy' => 'wptests_tax', + ) ); + $meta_id = add_term_meta( $t, 'foo', 'bar' ); + + $this->make_user_by_role( 'editor' ); + + $result = $this->myxmlrpcserver->wp_editTerm( array( + 1, + 'editor', + 'editor', + $t, + array( + 'taxonomy' => 'wptests_tax', + 'custom_fields' => array( + array( + 'id' => $meta_id, + 'key' => 'foo', + 'value' => 'baz', + ), + ), + ), + ) ); + + $this->assertNotIXRError( $result ); + + $found = get_term_meta( $t, 'foo', true ); + $this->assertSame( 'baz', $found ); + } + + /** + * @ticket 35991 + */ + public function test_delete_term_meta() { + register_taxonomy( 'wptests_tax', 'post' ); + + $t = self::factory()->term->create( array( + 'taxonomy' => 'wptests_tax', + ) ); + $meta_id = add_term_meta( $t, 'foo', 'bar' ); + + $this->make_user_by_role( 'editor' ); + + $result = $this->myxmlrpcserver->wp_editTerm( array( + 1, + 'editor', + 'editor', + $t, + array( + 'taxonomy' => 'wptests_tax', + 'custom_fields' => array( + array( + 'id' => $meta_id, + ), + ), + ), + ) ); + + $this->assertNotIXRError( $result ); + + $found = get_term_meta( $t, 'foo' ); + $this->assertSame( array(), $found ); + } } diff --git a/tests/phpunit/tests/xmlrpc/wp/getTerm.php b/tests/phpunit/tests/xmlrpc/wp/getTerm.php index fca71a7a95..df4f95053c 100644 --- a/tests/phpunit/tests/xmlrpc/wp/getTerm.php +++ b/tests/phpunit/tests/xmlrpc/wp/getTerm.php @@ -69,6 +69,7 @@ class Tests_XMLRPC_wp_getTerm extends WP_XMLRPC_UnitTestCase { $this->make_user_by_role( 'editor' ); $term = get_term( self::$term_id, 'category', ARRAY_A ); + $term['custom_fields'] = array(); $result = $this->myxmlrpcserver->wp_getTerm( array( 1, 'editor', 'editor', 'category', self::$term_id ) ); @@ -95,4 +96,29 @@ class Tests_XMLRPC_wp_getTerm extends WP_XMLRPC_UnitTestCase { $this->assertEquals( 'category', $result['taxonomy'] ); $this->assertEquals( $term['description'], $result['description'] ); } + + /** + * @ticket 35991 + */ + public function test_get_term_meta() { + $this->make_user_by_role( 'editor' ); + + // Add term meta to test wp.getTerm. + add_term_meta( self::$term_id, 'foo', 'bar' ); + + $term = get_term( self::$term_id, 'category', ARRAY_A ); + + $result = $this->myxmlrpcserver->wp_getTerm( array( + 1, + 'editor', + 'editor', + 'category', + self::$term_id, + ) ); + $this->assertNotIXRError( $result ); + + $this->assertInternalType( 'array', $result['custom_fields'] ); + $term_meta = get_term_meta( self::$term_id, '', true ); + $this->assertEquals( $term_meta['foo'][0], $result['custom_fields'][0]['value'] ); + } } diff --git a/tests/phpunit/tests/xmlrpc/wp/getTerms.php b/tests/phpunit/tests/xmlrpc/wp/getTerms.php index b67cbde140..dfb65d4e46 100644 --- a/tests/phpunit/tests/xmlrpc/wp/getTerms.php +++ b/tests/phpunit/tests/xmlrpc/wp/getTerms.php @@ -50,6 +50,9 @@ class Tests_XMLRPC_wp_getTerms extends WP_XMLRPC_UnitTestCase { foreach( $results as $term ) { $this->assertInternalType( 'int', $term['count'] ); + // Check custom term meta + $this->assertInternalType( 'array', $term['custom_fields'] ); + // We expect all other IDs to be strings not integers so we don't return something larger than an XMLRPC integer can describe. $this->assertStringMatchesFormat( '%d', $term['term_id'] ); $this->assertStringMatchesFormat( '%d', $term['term_group'] ); diff --git a/tests/phpunit/tests/xmlrpc/wp/newTerm.php b/tests/phpunit/tests/xmlrpc/wp/newTerm.php index f5f012f3eb..9df8473471 100644 --- a/tests/phpunit/tests/xmlrpc/wp/newTerm.php +++ b/tests/phpunit/tests/xmlrpc/wp/newTerm.php @@ -106,4 +106,28 @@ class Tests_XMLRPC_wp_newTerm extends WP_XMLRPC_UnitTestCase { $this->assertNotIXRError( $result ); $this->assertStringMatchesFormat( '%d', $result ); } + + /** + * @ticket 35991 + */ + public function test_add_term_meta() { + $this->make_user_by_role( 'editor' ); + $result = $this->myxmlrpcserver->wp_newTerm( array( + 1, + 'editor', + 'editor', + array( + 'taxonomy' => 'category', + 'name' => 'Test meta', + 'custom_fields' => array( + array( + 'key' => 'key1', + 'value' => 'value1', + ), + ), + ), + ) ); + $this->assertNotIXRError( $result ); + $this->assertStringMatchesFormat( '%d', $result ); + } }