diff --git a/CHANGELOG.md b/CHANGELOG.md index 0579de6af..1f8c3318a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added a filter to make custom comment types manageable in WP.com Calypso +### Changed + +* Hide ActivityPub post meta keys from the custom Fields UI + ### Fixed * Undefined array key warnings in various places +* Image captions not being included in the ActivityPub representation when the image is attached to the post ### Changed * Print `_activityPubOptions` in the `wp_footer` action on the frontend. diff --git a/includes/class-activitypub.php b/includes/class-activitypub.php index bdd6641d0..bf94e303c 100644 --- a/includes/class-activitypub.php +++ b/includes/class-activitypub.php @@ -339,7 +339,7 @@ public static function get_avatar_url( $comment ) { public static function trash_post( $post_id ) { \add_post_meta( $post_id, - 'activitypub_canonical_url', + '_activitypub_canonical_url', \get_permalink( $post_id ), true ); @@ -351,7 +351,7 @@ public static function trash_post( $post_id ) { * @param string $post_id The Post ID. */ public static function untrash_post( $post_id ) { - \delete_post_meta( $post_id, 'activitypub_canonical_url' ); + \delete_post_meta( $post_id, '_activitypub_canonical_url' ); } /** @@ -482,7 +482,7 @@ private static function register_post_types() { \register_post_meta( Followers::POST_TYPE, - 'activitypub_inbox', + '_activitypub_inbox', array( 'type' => 'string', 'single' => true, @@ -492,7 +492,7 @@ private static function register_post_types() { \register_post_meta( Followers::POST_TYPE, - 'activitypub_errors', + '_activitypub_errors', array( 'type' => 'string', 'single' => false, @@ -508,7 +508,7 @@ private static function register_post_types() { \register_post_meta( Followers::POST_TYPE, - 'activitypub_user_id', + '_activitypub_user_id', array( 'type' => 'string', 'single' => false, @@ -520,7 +520,7 @@ private static function register_post_types() { \register_post_meta( Followers::POST_TYPE, - 'activitypub_actor_json', + '_activitypub_actor_json', array( 'type' => 'string', 'single' => true, diff --git a/includes/class-migration.php b/includes/class-migration.php index fe352b478..0ad7ccf83 100644 --- a/includes/class-migration.php +++ b/includes/class-migration.php @@ -161,6 +161,9 @@ public static function maybe_migrate() { if ( \version_compare( $version_from_db, '4.5.0', '<' ) ) { \wp_schedule_single_event( \time() + MINUTE_IN_SECONDS, 'activitypub_update_comment_counts' ); } + if ( \version_compare( $version_from_db, '4.6.0', '<' ) ) { + self::migrate_to_4_6_0(); + } /** * Fires when the system has to be migrated. @@ -387,6 +390,26 @@ public static function migrate_to_4_1_0() { ); } + /** + * Updates post meta keys to be prefixed with an underscore. + */ + public static function migrate_to_4_6_0() { + global $wpdb; + + $meta_keys = array( + 'activitypub_actor_json', + 'activitypub_canonical_url', + 'activitypub_errors', + 'activitypub_inbox', + 'activitypub_user_id', + ); + + foreach ( $meta_keys as $meta_key ) { + // phpcs:ignore WordPress.DB + $wpdb->update( $wpdb->postmeta, array( 'meta_key' => '_' . $meta_key ), array( 'meta_key' => $meta_key ) ); + } + } + /** * Update comment counts for posts in batches. * diff --git a/includes/collection/class-actors.php b/includes/collection/class-actors.php index 9e862e028..88ad59462 100644 --- a/includes/collection/class-actors.php +++ b/includes/collection/class-actors.php @@ -100,7 +100,7 @@ public static function get_by_username( $username ) { 'meta_query' => array( 'relation' => 'OR', array( - 'key' => 'activitypub_user_identifier', + 'key' => '_activitypub_user_identifier', 'value' => $username, 'compare' => 'LIKE', ), diff --git a/includes/collection/class-followers.php b/includes/collection/class-followers.php index dc4e31507..d6f8b0727 100644 --- a/includes/collection/class-followers.php +++ b/includes/collection/class-followers.php @@ -52,11 +52,11 @@ public static function add_follower( $user_id, $actor ) { return $id; } - $post_meta = get_post_meta( $id, 'activitypub_user_id', false ); + $post_meta = get_post_meta( $id, '_activitypub_user_id', false ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict if ( is_array( $post_meta ) && ! in_array( $user_id, $post_meta ) ) { - add_post_meta( $id, 'activitypub_user_id', $user_id ); + add_post_meta( $id, '_activitypub_user_id', $user_id ); wp_cache_delete( sprintf( self::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); } @@ -89,7 +89,7 @@ public static function remove_follower( $user_id, $actor ) { */ do_action( 'activitypub_followers_pre_remove_follower', $follower, $user_id, $actor ); - return delete_post_meta( $follower->get__id(), 'activitypub_user_id', $user_id ); + return delete_post_meta( $follower->get__id(), '_activitypub_user_id', $user_id ); } /** @@ -106,7 +106,7 @@ public static function get_follower( $user_id, $actor ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery $post_id = $wpdb->get_var( $wpdb->prepare( - "SELECT DISTINCT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = %s AND pm.meta_key = 'activitypub_user_id' AND pm.meta_value = %d AND p.guid = %s", + "SELECT DISTINCT p.ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = %s AND pm.meta_key = '_activitypub_user_id' AND pm.meta_value = %d AND p.guid = %s", array( esc_sql( self::POST_TYPE ), esc_sql( $user_id ), @@ -188,7 +188,7 @@ public static function get_followers_with_count( $user_id, $number = -1, $page = // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'meta_query' => array( array( - 'key' => 'activitypub_user_id', + 'key' => '_activitypub_user_id', 'value' => $user_id, ), ), @@ -219,11 +219,11 @@ public static function get_all_followers() { 'meta_query' => array( 'relation' => 'AND', array( - 'key' => 'activitypub_inbox', + 'key' => '_activitypub_inbox', 'compare' => 'EXISTS', ), array( - 'key' => 'activitypub_actor_json', + 'key' => '_activitypub_actor_json', 'compare' => 'EXISTS', ), ), @@ -247,15 +247,15 @@ public static function count_followers( $user_id ) { 'meta_query' => array( 'relation' => 'AND', array( - 'key' => 'activitypub_user_id', + 'key' => '_activitypub_user_id', 'value' => $user_id, ), array( - 'key' => 'activitypub_inbox', + 'key' => '_activitypub_inbox', 'compare' => 'EXISTS', ), array( - 'key' => 'activitypub_actor_json', + 'key' => '_activitypub_actor_json', 'compare' => 'EXISTS', ), ), @@ -290,15 +290,15 @@ public static function get_inboxes( $user_id ) { 'meta_query' => array( 'relation' => 'AND', array( - 'key' => 'activitypub_inbox', + 'key' => '_activitypub_inbox', 'compare' => 'EXISTS', ), array( - 'key' => 'activitypub_user_id', + 'key' => '_activitypub_user_id', 'value' => $user_id, ), array( - 'key' => 'activitypub_inbox', + 'key' => '_activitypub_inbox', 'value' => '', 'compare' => '!=', ), @@ -318,7 +318,7 @@ public static function get_inboxes( $user_id ) { $wpdb->prepare( "SELECT DISTINCT meta_value FROM {$wpdb->postmeta} WHERE post_id IN (" . implode( ', ', array_fill( 0, count( $posts ), '%d' ) ) . ") - AND meta_key = 'activitypub_inbox' + AND meta_key = '_activitypub_inbox' AND meta_value IS NOT NULL", $posts ) @@ -378,24 +378,24 @@ public static function get_faulty_followers( $number = 20 ) { 'meta_query' => array( 'relation' => 'OR', array( - 'key' => 'activitypub_errors', + 'key' => '_activitypub_errors', 'compare' => 'EXISTS', ), array( - 'key' => 'activitypub_inbox', + 'key' => '_activitypub_inbox', 'compare' => 'NOT EXISTS', ), array( - 'key' => 'activitypub_actor_json', + 'key' => '_activitypub_actor_json', 'compare' => 'NOT EXISTS', ), array( - 'key' => 'activitypub_inbox', + 'key' => '_activitypub_inbox', 'value' => '', 'compare' => '=', ), array( - 'key' => 'activitypub_actor_json', + 'key' => '_activitypub_actor_json', 'value' => '', 'compare' => '=', ), @@ -437,7 +437,7 @@ public static function add_error( $post_id, $error ) { return add_post_meta( $post_id, - 'activitypub_errors', + '_activitypub_errors', $error_message ); } diff --git a/includes/functions.php b/includes/functions.php index a06a09b7f..19d1af2c1 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -620,7 +620,7 @@ function is_json( $data ) { } /** - * Check whther a blog is public based on the `blog_public` option. + * Check whether a blog is public based on the `blog_public` option. * * @return bool True if public, false if not */ @@ -1071,7 +1071,7 @@ function ( $enclosure ) { * * @param int|\WP_Comment $comment Comment ID or comment object. * - * @return \WP_Comment[] Array of ancestor comments or empty array if there are none. + * @return int[] Array of ancestor IDs. */ function get_comment_ancestors( $comment ) { $comment = \get_comment( $comment ); diff --git a/includes/model/class-follower.php b/includes/model/class-follower.php index 45ef6157b..bcf5d39dd 100644 --- a/includes/model/class-follower.php +++ b/includes/model/class-follower.php @@ -36,7 +36,7 @@ class Follower extends Actor { * @return mixed */ public function get_errors() { - return get_post_meta( $this->_id, 'activitypub_errors', false ); + return get_post_meta( $this->_id, '_activitypub_errors', false ); } /** @@ -72,7 +72,7 @@ public function get_url() { * Reset (delete) all errors. */ public function reset_errors() { - delete_post_meta( $this->_id, 'activitypub_errors' ); + delete_post_meta( $this->_id, '_activitypub_errors' ); } /** @@ -216,9 +216,9 @@ public function delete() { * Update the post meta. */ protected function get_post_meta_input() { - $meta_input = array(); - $meta_input['activitypub_inbox'] = $this->get_shared_inbox(); - $meta_input['activitypub_actor_json'] = $this->to_json(); + $meta_input = array(); + $meta_input['_activitypub_inbox'] = $this->get_shared_inbox(); + $meta_input['_activitypub_actor_json'] = $this->to_json(); return $meta_input; } @@ -334,7 +334,7 @@ public function get_shared_inbox() { * @return \Activitypub\Activity\Base_Object|WP_Error */ public static function init_from_cpt( $post ) { - $actor_json = get_post_meta( $post->ID, 'activitypub_actor_json', true ); + $actor_json = get_post_meta( $post->ID, '_activitypub_actor_json', true ); $object = self::init_from_json( $actor_json ); $object->set__id( $post->ID ); $object->set_id( $post->guid ); diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index d01c121c4..f3cbcf960 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -152,7 +152,7 @@ public function get_url() { switch ( \get_post_status( $post ) ) { case 'trash': - $permalink = \get_post_meta( $post->ID, 'activitypub_canonical_url', true ); + $permalink = \get_post_meta( $post->ID, '_activitypub_canonical_url', true ); break; case 'draft': // Get_sample_permalink is in wp-admin, not always loaded. @@ -454,10 +454,21 @@ protected function get_media_from_blocks( $blocks, $media ) { $alt = $match[2]; } - $media['image'][] = array( - 'id' => $block['attrs']['id'], - 'alt' => $alt, - ); + $found = false; + foreach ( $media['image'] as $i => $image ) { + if ( $image['id'] === $block['attrs']['id'] ) { + $media['image'][ $i ]['alt'] = $alt; + $found = true; + break; + } + } + + if ( ! $found ) { + $media['image'][] = array( + 'id' => $block['attrs']['id'], + 'alt' => $alt, + ); + } } break; case 'core/audio': diff --git a/integration/class-jetpack.php b/integration/class-jetpack.php index f4cf31e40..5586cf79e 100644 --- a/integration/class-jetpack.php +++ b/integration/class-jetpack.php @@ -35,9 +35,9 @@ public static function add_sync_meta( $allow_list ) { return $allow_list; } $activitypub_meta_keys = array( - 'activitypub_user_id', - 'activitypub_inbox', - 'activitypub_actor_json', + '_activitypub_user_id', + '_activitypub_inbox', + '_activitypub_actor_json', ); return \array_merge( $allow_list, $activitypub_meta_keys ); } diff --git a/readme.txt b/readme.txt index 40500b814..5b69986c2 100644 --- a/readme.txt +++ b/readme.txt @@ -135,7 +135,9 @@ For reasons of data protection, it is not possible to see the followers of other = Unreleased = * Added: A filter to make custom comment types manageable in WP.com Calypso +* Changed: Hide ActivityPub post meta keys from the custom Fields UI * Fixed: Undefined array key warnings in various places +* Fixed: Image captions not being included in the ActivityPub representation when the image is attached to the post * Changed: Print `_activityPubOptions` in the `wp_footer` action on the frontend. = 4.6.0 = diff --git a/tests/includes/class-test-migration.php b/tests/includes/class-test-migration.php index 73384c240..00589fe2f 100644 --- a/tests/includes/class-test-migration.php +++ b/tests/includes/class-test-migration.php @@ -186,6 +186,79 @@ public function test_migrate_to_4_1_0() { $this->assertFalse( $content_type ); } + /** + * Test migrate to 4.6.0. + * + * @covers ::migrate_to_4_6_0 + */ + public function test_migrate_to_4_6_0() { + $post1 = \wp_insert_post( + array( + 'post_author' => 1, + 'post_content' => 'Test post 1', + ) + ); + + $post2 = \wp_insert_post( + array( + 'post_author' => 1, + 'post_content' => 'Test post 2', + ) + ); + + // Set up test meta data. + $meta_data = array( + 'activitypub_actor_json' => '{"type":"Person"}', + 'activitypub_canonical_url' => 'https://example.com/post-1', + 'activitypub_errors' => 'Test error', + 'activitypub_inbox' => 'https://example.com/inbox', + 'activitypub_user_id' => '123', + 'unrelated_meta' => 'should not change', + ); + + foreach ( $meta_data as $key => $value ) { + \update_post_meta( $post1, $key, $value ); + \update_post_meta( $post2, $key, $value . '-2' ); + } + + // Run migration. + Migration::migrate_to_4_6_0(); + + // Clean post cache to ensure fresh meta data. + \clean_post_cache( $post1 ); + \clean_post_cache( $post2 ); + + // Check post 1 meta. + $this->assertEmpty( \get_post_meta( $post1, 'activitypub_actor_json', true ), 'Old actor_json meta should be empty' ); + $this->assertEmpty( \get_post_meta( $post1, 'activitypub_canonical_url', true ), 'Old canonical_url meta should be empty' ); + $this->assertEmpty( \get_post_meta( $post1, 'activitypub_errors', true ), 'Old errors meta should be empty' ); + $this->assertEmpty( \get_post_meta( $post1, 'activitypub_inbox', true ), 'Old inbox meta should be empty' ); + $this->assertEmpty( \get_post_meta( $post1, 'activitypub_user_id', true ), 'Old user_id meta should be empty' ); + + $this->assertEquals( '{"type":"Person"}', \get_post_meta( $post1, '_activitypub_actor_json', true ), 'New actor_json meta should match' ); + $this->assertEquals( 'https://example.com/post-1', \get_post_meta( $post1, '_activitypub_canonical_url', true ), 'New canonical_url meta should match' ); + $this->assertEquals( 'Test error', \get_post_meta( $post1, '_activitypub_errors', true ), 'New errors meta should match' ); + $this->assertEquals( 'https://example.com/inbox', \get_post_meta( $post1, '_activitypub_inbox', true ), 'New inbox meta should match' ); + $this->assertEquals( '123', \get_post_meta( $post1, '_activitypub_user_id', true ), 'New user_id meta should match' ); + + // Check post 2 meta. + $this->assertEmpty( \get_post_meta( $post2, 'activitypub_actor_json', true ), 'Old actor_json meta should be empty' ); + $this->assertEmpty( \get_post_meta( $post2, 'activitypub_canonical_url', true ), 'Old canonical_url meta should be empty' ); + $this->assertEmpty( \get_post_meta( $post2, 'activitypub_errors', true ), 'Old errors meta should be empty' ); + $this->assertEmpty( \get_post_meta( $post2, 'activitypub_inbox', true ), 'Old inbox meta should be empty' ); + $this->assertEmpty( \get_post_meta( $post2, 'activitypub_user_id', true ), 'Old user_id meta should be empty' ); + + $this->assertEquals( '{"type":"Person"}-2', \get_post_meta( $post2, '_activitypub_actor_json', true ), 'New actor_json meta should match' ); + $this->assertEquals( 'https://example.com/post-1-2', \get_post_meta( $post2, '_activitypub_canonical_url', true ), 'New canonical_url meta should match' ); + $this->assertEquals( 'Test error-2', \get_post_meta( $post2, '_activitypub_errors', true ), 'New errors meta should match' ); + $this->assertEquals( 'https://example.com/inbox-2', \get_post_meta( $post2, '_activitypub_inbox', true ), 'New inbox meta should match' ); + $this->assertEquals( '123-2', \get_post_meta( $post2, '_activitypub_user_id', true ), 'New user_id meta should match' ); + + // Verify unrelated meta is unchanged. + $this->assertEquals( 'should not change', \get_post_meta( $post1, 'unrelated_meta', true ), 'Unrelated meta should not change' ); + $this->assertEquals( 'should not change-2', \get_post_meta( $post2, 'unrelated_meta', true ), 'Unrelated meta should not change' ); + } + /** * Tests that a new migration lock can be successfully acquired when no lock exists. * diff --git a/tests/includes/collection/class-test-followers.php b/tests/includes/collection/class-test-followers.php index e19dca592..cd54412ed 100644 --- a/tests/includes/collection/class-test-followers.php +++ b/tests/includes/collection/class-test-followers.php @@ -291,7 +291,7 @@ public function test_get_faulty_followers() { $follower = Followers::get_follower( 1, 'http://sally.example.org' ); for ( $i = 1; $i <= 15; $i++ ) { - add_post_meta( $follower->get__id(), 'activitypub_errors', 'error ' . $i ); + add_post_meta( $follower->get__id(), '_activitypub_errors', 'error ' . $i ); } $follower = Followers::get_follower( 1, 'http://sally.example.org' ); @@ -332,7 +332,7 @@ public function test_add_duplicate_follower() { $this->assertContains( $follower, $db_followers ); $follower = current( $db_followers ); - $meta = get_post_meta( $follower->get__id(), 'activitypub_user_id', false ); + $meta = get_post_meta( $follower->get__id(), '_activitypub_user_id', false ); $this->assertCount( 1, $meta ); } @@ -477,7 +477,7 @@ public function test_get_inboxes() { $id = $follower->upsert(); - add_post_meta( $id, 'activitypub_user_id', 1 ); + add_post_meta( $id, '_activitypub_user_id', 1 ); } $inboxes = Followers::get_inboxes( 1 ); @@ -503,7 +503,7 @@ public function test_get_inboxes() { $id = $follower->upsert(); - add_post_meta( $id, 'activitypub_user_id', 1 ); + add_post_meta( $id, '_activitypub_user_id', 1 ); } $inboxes2 = Followers::get_inboxes( 1 ); @@ -533,7 +533,7 @@ public function test_get_all_followers() { $id = $follower->upsert(); - add_post_meta( $id, 'activitypub_user_id', 1 ); + add_post_meta( $id, '_activitypub_user_id', 1 ); } $followers = Followers::get_all_followers(); diff --git a/tests/includes/transformer/class-test-post.php b/tests/includes/transformer/class-test-post.php index 63cb574cf..a6a07202c 100644 --- a/tests/includes/transformer/class-test-post.php +++ b/tests/includes/transformer/class-test-post.php @@ -286,7 +286,7 @@ public function test_to_object() { $this->assertEquals( $permalink, $activitypub_post->get_id() ); - $cached = \get_post_meta( $post, 'activitypub_canonical_url', true ); + $cached = \get_post_meta( $post, '_activitypub_canonical_url', true ); $this->assertEquals( $cached, $activitypub_post->get_id() ); } @@ -387,56 +387,105 @@ public function test_block_attachments_with_fallback() { } /** - * Saves an attachment. - * - * @param string $file The file name to create attachment object for. - * @param int $parent_id ID of the post to attach the file to. + * Test get_media_from_blocks adds alt text to existing images. * - * @return int|WP_Error The attachment ID on success. The value 0 or WP_Error on failure. + * @covers ::get_media_from_blocks */ - public function create_upload_object( $file, $parent_id = 0 ) { - if ( ! class_exists( 'WP_Filesystem_Direct' ) ) { - require ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; - require ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; - } + public function test_get_media_from_blocks_adds_alt_text_to_existing_images() { + $post_id = self::factory()->post->create( + array( + 'post_content' => '
Test alt text
', + ) + ); + $post = get_post( $post_id ); - $dest = dirname( $file ) . DIRECTORY_SEPARATOR . 'test-temp.jpg'; - $fs = new \WP_Filesystem_Direct( array() ); - $fs->copy( $file, $dest ); + $transformer = new Post( $post ); + $media = array( + 'image' => array( + array( + 'id' => 123, + 'alt' => '', + ), + ), + 'audio' => array(), + 'video' => array(), + ); - $file = $dest; + $reflection = new \ReflectionClass( Post::class ); + $method = $reflection->getMethod( 'get_media_from_blocks' ); + $method->setAccessible( true ); - $file_array = array( - 'name' => wp_basename( $file ), - 'tmp_name' => $file, + $blocks = parse_blocks( $post->post_content ); + $result = $method->invoke( $transformer, $blocks, $media ); + + $this->assertSame( 'Test alt text', $result['image'][0]['alt'] ); + $this->assertSame( 123, $result['image'][0]['id'] ); + } + + /** + * Test get_media_from_blocks adds new image when none exist. + * + * @covers ::get_media_from_blocks + */ + public function test_get_media_from_blocks_adds_new_image() { + $post_id = self::factory()->post->create( + array( + 'post_content' => '
Test alt text
', + ) ); + $post = get_post( $post_id ); - $upload = wp_handle_sideload( $file_array, array( 'test_form' => false ) ); + $transformer = new Post( $post ); + $media = array( + 'image' => array(), + 'audio' => array(), + 'video' => array(), + ); - $type = ''; - if ( ! empty( $upload['type'] ) ) { - $type = $upload['type']; - } else { - $mime = wp_check_filetype( $upload['file'] ); - if ( $mime ) { - $type = $mime['type']; - } - } + $reflection = new \ReflectionClass( Post::class ); + $method = $reflection->getMethod( 'get_media_from_blocks' ); + $method->setAccessible( true ); - $attachment = array( - 'post_title' => wp_basename( $upload['file'] ), - 'post_content' => '', - 'post_type' => 'attachment', - 'post_parent' => $parent_id, - 'post_mime_type' => $type, - 'guid' => $upload['url'], + $blocks = parse_blocks( $post->post_content ); + $result = $method->invoke( $transformer, $blocks, $media ); + + $this->assertCount( 1, $result['image'] ); + $this->assertSame( 123, $result['image'][0]['id'] ); + $this->assertSame( 'Test alt text', $result['image'][0]['alt'] ); + } + + /** + * Test get_media_from_blocks handles multiple blocks correctly. + * + * @covers ::get_media_from_blocks + */ + public function test_get_media_from_blocks_handles_multiple_blocks() { + $post_id = self::factory()->post->create( + array( + 'post_content' => '
Test alt 1
Test alt 2
', + ) ); + $post = get_post( $post_id ); - // Save the data. - $id = wp_insert_attachment( $attachment, $upload['file'], $parent_id ); - wp_update_attachment_metadata( $id, @wp_generate_attachment_metadata( $id, $upload['file'] ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged + $transformer = new Post( $post ); + $media = array( + 'image' => array(), + 'audio' => array(), + 'video' => array(), + ); - return $id; + $reflection = new \ReflectionClass( Post::class ); + $method = $reflection->getMethod( 'get_media_from_blocks' ); + $method->setAccessible( true ); + + $blocks = parse_blocks( $post->post_content ); + $result = $method->invoke( $transformer, $blocks, $media ); + + $this->assertCount( 2, $result['image'] ); + $this->assertSame( 123, $result['image'][0]['id'] ); + $this->assertSame( 'Test alt 1', $result['image'][0]['alt'] ); + $this->assertSame( 456, $result['image'][1]['id'] ); + $this->assertSame( 'Test alt 2', $result['image'][1]['alt'] ); } /** @@ -457,7 +506,7 @@ public function test_get_icon() { $attachment_id = $this->create_upload_object( dirname( __DIR__, 2 ) . '/assets/test.jpg' ); // Set up reflection method. - $reflection = new ReflectionClass( Post::class ); + $reflection = new \ReflectionClass( Post::class ); $method = $reflection->getMethod( 'get_icon' ); $method->setAccessible( true ); @@ -513,4 +562,56 @@ public function test_get_icon() { wp_delete_post( $post_id, true ); wp_delete_attachment( $attachment_id, true ); } + + /** + * Saves an attachment. + * + * @param string $file The file name to create attachment object for. + * @param int $parent_id ID of the post to attach the file to. + * @return int|\WP_Error The attachment ID on success. The value 0 or WP_Error on failure. + */ + public function create_upload_object( $file, $parent_id = 0 ) { + if ( ! class_exists( 'WP_Filesystem_Direct' ) ) { + require ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; + require ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; + } + + $dest = dirname( $file ) . DIRECTORY_SEPARATOR . 'test-temp.jpg'; + $fs = new \WP_Filesystem_Direct( array() ); + $fs->copy( $file, $dest ); + + $file = $dest; + + $file_array = array( + 'name' => wp_basename( $file ), + 'tmp_name' => $file, + ); + + $upload = wp_handle_sideload( $file_array, array( 'test_form' => false ) ); + + $type = ''; + if ( ! empty( $upload['type'] ) ) { + $type = $upload['type']; + } else { + $mime = wp_check_filetype( $upload['file'] ); + if ( $mime ) { + $type = $mime['type']; + } + } + + $attachment = array( + 'post_title' => wp_basename( $upload['file'] ), + 'post_content' => '', + 'post_type' => 'attachment', + 'post_parent' => $parent_id, + 'post_mime_type' => $type, + 'guid' => $upload['url'], + ); + + // Save the data. + $id = wp_insert_attachment( $attachment, $upload['file'], $parent_id ); + wp_update_attachment_metadata( $id, @wp_generate_attachment_metadata( $id, $upload['file'] ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged + + return $id; + } }