diff --git a/includes/functions.php b/includes/functions.php index 616391ad8..7065edb01 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -1471,3 +1471,19 @@ function get_attribution_domains() { return $domains; } + +/** + * Get the base URL for uploads. + * + * @return string The upload base URL. + */ +function get_upload_baseurl() { + $upload_dir = \wp_get_upload_dir(); + + /** + * Filters the upload base URL. + * + * @param string \wp_get_upload_dir()['baseurl'] The upload base URL. + */ + return apply_filters( 'activitypub_get_upload_baseurl', $upload_dir['baseurl'] ); +} diff --git a/includes/transformer/class-post.php b/includes/transformer/class-post.php index 1272b5bc2..3d0326c36 100644 --- a/includes/transformer/class-post.php +++ b/includes/transformer/class-post.php @@ -15,6 +15,7 @@ use function Activitypub\esc_hashtag; use function Activitypub\is_single_user; use function Activitypub\get_enclosures; +use function Activitypub\get_upload_baseurl; use function Activitypub\get_content_warning; use function Activitypub\site_supports_blocks; use function Activitypub\generate_post_summary; @@ -477,14 +478,22 @@ protected function get_classic_editor_image_embeds( $max_images ) { } $images = array(); - $base = \wp_get_upload_dir()['baseurl']; + $base = get_upload_baseurl(); $content = \get_post_field( 'post_content', $this->wp_object ); $tags = new \WP_HTML_Tag_Processor( $content ); // This linter warning is a false positive - we have to re-count each time here as we modify $images. // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found while ( $tags->next_tag( 'img' ) && ( \count( $images ) <= $max_images ) ) { - $src = $tags->get_attribute( 'src' ); + /** + * Filter the image source URL. + * + * This can be used to modify the image source URL before it is used to + * determine the attachment ID. + * + * @param string $src The image source URL. + */ + $src = \apply_filters( 'activitypub_image_src', $tags->get_attribute( 'src' ) ); /* * If the img source is in our uploads dir, get the @@ -499,16 +508,22 @@ protected function get_classic_editor_image_embeds( $max_images ) { if ( null !== $src && \str_starts_with( $src, $base ) ) { $img_id = \attachment_url_to_postid( $src ); + if ( 0 === $img_id ) { + $count = 0; + $src = \strtok( $src, '?' ); + $img_id = \attachment_url_to_postid( $src ); + } + if ( 0 === $img_id ) { $count = 0; - $src = preg_replace( '/-(?:\d+x\d+)(\.[a-zA-Z]+)$/', '$1', $src, 1, $count ); + $src = \preg_replace( '/-(?:\d+x\d+)(\.[a-zA-Z]+)$/', '$1', $src, 1, $count ); if ( $count > 0 ) { $img_id = \attachment_url_to_postid( $src ); } } if ( 0 === $img_id ) { - $src = preg_replace( '/(\.[a-zA-Z]+)$/', '-scaled$1', $src ); + $src = \preg_replace( '/(\.[a-zA-Z]+)$/', '-scaled$1', $src ); $img_id = \attachment_url_to_postid( $src ); } diff --git a/tests/assets/test.jpg b/tests/assets/test.jpg new file mode 100644 index 000000000..c21946f64 Binary files /dev/null and b/tests/assets/test.jpg differ diff --git a/tests/class-test-activitypub-post.php b/tests/class-test-activitypub-post.php index d05e9c0e6..de47694e0 100644 --- a/tests/class-test-activitypub-post.php +++ b/tests/class-test-activitypub-post.php @@ -74,4 +74,107 @@ public function test_content_visibility() { $this->assertEquals( array(), $object->get_to() ); $this->assertEquals( array(), $object->get_cc() ); } + + /** + * Test different variations of Attachment parsing. + */ + public function test_block_attachments_with_fallback() { + $attachment_id = $this->create_upload_object( __DIR__ . '/assets/test.jpg' ); + $attachment_src = \wp_get_attachment_image_src( $attachment_id ); + + $post_id = \wp_insert_post( + array( + 'post_author' => 1, + 'post_content' => sprintf( + '
', + $attachment_id, + $attachment_src[0] + ), + 'post_status' => 'publish', + ) + ); + + $object = \Activitypub\Transformer\Post::transform( get_post( $post_id ) )->to_object(); + + $this->assertEquals( + array( + array( + 'type' => 'Image', + 'url' => $attachment_src[0], + 'mediaType' => 'image/jpeg', + ), + ), + $object->get_attachment() + ); + + $post_id = \wp_insert_post( + array( + 'post_author' => 1, + 'post_content' => sprintf( + '

this is a photo

', + $attachment_id, + $attachment_src[0] + ), + 'post_status' => 'publish', + ) + ); + + $object = \Activitypub\Transformer\Post::transform( get_post( $post_id ) )->to_object(); + + $this->assertEquals( + array( + array( + 'type' => 'Image', + 'url' => $attachment_src[0], + 'mediaType' => 'image/jpeg', + ), + ), + $object->get_attachment() + ); + + \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 ) { + $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 ); + // phpcs:ignore + @wp_update_attachment_metadata( $id, @wp_generate_attachment_metadata( $id, $upload['file'] ) ); + + return $id; + } }