Skip to content

Commit

Permalink
This adds Icon support for Audio and Video attachments (#1053)
Browse files Browse the repository at this point in the history
  • Loading branch information
pfefferle authored Dec 11, 2024
1 parent dad75d3 commit 6d5c76f
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* `icon` support for `Audio` and `Video` attachments
* Send "new follower" emails

### Improved
Expand Down
3 changes: 3 additions & 0 deletions includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,9 @@ function object_to_uri( $data ) {

// Return part of Object that makes most sense.
switch ( $type ) {
case 'Image':
$data = $data['url'];
break;
case 'Link':
$data = $data['href'];
break;
Expand Down
87 changes: 74 additions & 13 deletions includes/transformer/class-post.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Activitypub\Collection\Actors;

use function Activitypub\esc_hashtag;
use function Activitypub\object_to_uri;
use function Activitypub\is_single_user;
use function Activitypub\get_enclosures;
use function Activitypub\get_upload_baseurl;
Expand Down Expand Up @@ -208,7 +209,63 @@ protected function get_image() {
*/
$thumbnail = apply_filters(
'activitypub_get_image',
self::get_wordpress_attachment( $id, $image_size ),
$this->get_wordpress_attachment( $id, $image_size ),
$id,
$image_size
);

if ( ! $thumbnail ) {
return null;
}

$mime_type = \get_post_mime_type( $id );

$image = array(
'type' => 'Image',
'url' => \esc_url( $thumbnail[0] ),
'mediaType' => \esc_attr( $mime_type ),
);

$alt = \get_post_meta( $id, '_wp_attachment_image_alt', true );
if ( $alt ) {
$image['name'] = \wp_strip_all_tags( \html_entity_decode( $alt ) );
}

return $image;
}

/**
* Returns an Icon, based on the Featured Image with a fallback to the site-icon.
*
* @return array|null The Icon or null if no icon is available.
*/
protected function get_icon() {
$post_id = $this->wp_object->ID;

// List post thumbnail first if this post has one.
if ( \has_post_thumbnail( $post_id ) ) {
$id = \get_post_thumbnail_id( $post_id );
} else {
// Try site_logo, falling back to site_icon, first.
$id = get_option( 'site_icon' );
}

if ( ! $id ) {
return null;
}

$image_size = 'thumbnail';

/**
* Filter the image URL returned for each post.
*
* @param array|false $thumbnail The image URL, or false if no image is available.
* @param int $id The attachment ID.
* @param string $image_size The image size to retrieve. Set to 'large' by default.
*/
$thumbnail = apply_filters(
'activitypub_get_image',
$this->get_wordpress_attachment( $id, $image_size ),
$id,
$image_size
);
Expand Down Expand Up @@ -273,7 +330,7 @@ protected function get_attachment() {
$media = $this->get_classic_editor_images( $media, $max_media );
}

$media = self::filter_media_by_object_type( $media, \get_post_format( $this->wp_object ), $this->wp_object );
$media = $this->filter_media_by_object_type( $media, \get_post_format( $this->wp_object ), $this->wp_object );
$unique_ids = \array_unique( \array_column( $media, 'id' ) );
$media = \array_intersect_key( $media, $unique_ids );
$media = \array_slice( $media, 0, $max_media );
Expand All @@ -288,7 +345,7 @@ protected function get_attachment() {
*/
$media = \apply_filters( 'activitypub_attachment_ids', $media, $this->wp_object );

$attachments = \array_filter( \array_map( array( self::class, 'wp_attachment_to_activity_attachment' ), $media ) );
$attachments = \array_filter( \array_map( array( $this, 'wp_attachment_to_activity_attachment' ), $media ) );

/**
* Filter the attachments for a post.
Expand Down Expand Up @@ -361,7 +418,7 @@ protected function get_block_attachments( $media, $max_media ) {

$blocks = \parse_blocks( $this->wp_object->post_content );

return self::get_media_from_blocks( $blocks, $media );
return $this->get_media_from_blocks( $blocks, $media );
}

/**
Expand All @@ -372,11 +429,11 @@ protected function get_block_attachments( $media, $max_media ) {
*
* @return array The image IDs.
*/
protected static function get_media_from_blocks( $blocks, $media ) {
protected function get_media_from_blocks( $blocks, $media ) {
foreach ( $blocks as $block ) {
// Recurse into inner blocks.
if ( ! empty( $block['innerBlocks'] ) ) {
$media = self::get_media_from_blocks( $block['innerBlocks'], $media );
$media = $this->get_media_from_blocks( $block['innerBlocks'], $media );
}

switch ( $block['blockName'] ) {
Expand Down Expand Up @@ -587,7 +644,7 @@ protected function get_classic_editor_image_attachments( $max_images ) {
*
* @return array The filtered media IDs.
*/
protected static function filter_media_by_object_type( $media, $type, $wp_object ) {
protected function filter_media_by_object_type( $media, $type, $wp_object ) {
/**
* Filter the object type for media attachments.
*
Expand All @@ -612,7 +669,7 @@ protected static function filter_media_by_object_type( $media, $type, $wp_object
*
* @return array The ActivityPub Attachment.
*/
public static function wp_attachment_to_activity_attachment( $media ) {
public function wp_attachment_to_activity_attachment( $media ) {
if ( ! isset( $media['id'] ) ) {
return $media;
}
Expand All @@ -635,7 +692,7 @@ public static function wp_attachment_to_activity_attachment( $media ) {
*/
$thumbnail = apply_filters(
'activitypub_get_image',
self::get_wordpress_attachment( $id, $image_size ),
$this->get_wordpress_attachment( $id, $image_size ),
$id,
$image_size
);
Expand Down Expand Up @@ -674,7 +731,11 @@ public static function wp_attachment_to_activity_attachment( $media ) {
$attachment['width'] = \esc_attr( $meta['width'] );
$attachment['height'] = \esc_attr( $meta['height'] );
}
// @todo: add `icon` support for audio/video attachments. Maybe use post thumbnail?

if ( $this->get_icon() ) {
$attachment['icon'] = object_to_uri( $this->get_icon() );
}

break;
}

Expand All @@ -697,7 +758,7 @@ public static function wp_attachment_to_activity_attachment( $media ) {
*
* @return array|false Array of image data, or boolean false if no image is available.
*/
protected static function get_wordpress_attachment( $id, $image_size = 'large' ) {
protected function get_wordpress_attachment( $id, $image_size = 'large' ) {
/**
* Hook into the image retrieval process. Before image retrieval.
*
Expand Down Expand Up @@ -920,7 +981,7 @@ protected function get_content() {
*/
do_action( 'activitypub_before_get_content', $post );

add_filter( 'render_block_core/embed', array( self::class, 'revert_embed_links' ), 10, 2 );
add_filter( 'render_block_core/embed', array( $this, 'revert_embed_links' ), 10, 2 );

// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$post = $this->wp_object;
Expand Down Expand Up @@ -1119,7 +1180,7 @@ public function get_summary_map() {
*
* @return string A block level link
*/
public static function revert_embed_links( $block_content, $block ) {
public function revert_embed_links( $block_content, $block ) {
if ( ! isset( $block['attrs']['url'] ) ) {
return $block_content;
}
Expand Down
11 changes: 8 additions & 3 deletions integration/class-seriously-simple-podcasting.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Activitypub\Transformer\Post;

use function Activitypub\object_to_uri;
use function Activitypub\generate_post_summary;

/**
Expand All @@ -35,9 +36,13 @@ public function get_attachment() {
'name' => \esc_attr( \get_the_title( $post->ID ) ?? '' ),
);

$cover = \get_post_meta( $post->ID, 'cover_image', true );
if ( $cover ) {
$attachment['icon'] = \esc_url( $cover );
$icon = \get_post_meta( $post->ID, 'cover_image', true );
if ( ! $icon ) {
$icon = $this->get_icon();
}

if ( $icon ) {
$attachment['icon'] = \esc_url( object_to_uri( $icon ) );
}

return array( $attachment );
Expand Down
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ For reasons of data protection, it is not possible to see the followers of other

= Unreleased =

* Added: `icon` support for `Audio` and `Video` attachments
* Added: Send "new follower" emails
* Improved: Email templates for Likes and Reposts
* Improved: Interactions moderation
Expand Down
75 changes: 75 additions & 0 deletions tests/includes/transformer/class-test-post.php
Original file line number Diff line number Diff line change
Expand Up @@ -438,4 +438,79 @@ public function create_upload_object( $file, $parent_id = 0 ) {

return $id;
}

/**
* Test get_icon method.
*
* @covers ::get_icon
*/
public function test_get_icon() {
$post_id = $this->factory->post->create(
array(
'post_title' => 'Test Post',
'post_content' => 'Test content',
)
);
$post = get_post( $post_id );

// Create test image.
$attachment_id = $this->create_upload_object( dirname( __DIR__, 2 ) . '/assets/test.jpg' );

// Set up reflection method.
$reflection = new ReflectionClass( Post::class );
$method = $reflection->getMethod( 'get_icon' );
$method->setAccessible( true );

// Test with featured image.
set_post_thumbnail( $post_id, $attachment_id );

$transformer = new Post( $post );
$icon = $method->invoke( $transformer );

$this->assertIsArray( $icon );
$this->assertEquals( 'Image', $icon['type'] );
$this->assertArrayHasKey( 'url', $icon );
$this->assertArrayHasKey( 'mediaType', $icon );
$this->assertEquals( get_post_mime_type( $attachment_id ), $icon['mediaType'] );

// Test with site icon.
delete_post_thumbnail( $post_id );
update_option( 'site_icon', $attachment_id );

$icon = $method->invoke( $transformer );

$this->assertIsArray( $icon );
$this->assertEquals( 'Image', $icon['type'] );
$this->assertArrayHasKey( 'url', $icon );
$this->assertArrayHasKey( 'mediaType', $icon );
$this->assertEquals( get_post_mime_type( $attachment_id ), $icon['mediaType'] );

// Test with alt text.
$alt_text = 'Test Alt Text';
update_post_meta( $attachment_id, '_wp_attachment_image_alt', $alt_text );

$icon = $method->invoke( $transformer );

$this->assertIsArray( $icon );
$this->assertEquals( 'Image', $icon['type'] );
$this->assertArrayHasKey( 'name', $icon );
$this->assertEquals( $alt_text, $icon['name'] );

// Test without any images.
delete_post_thumbnail( $post_id );
delete_option( 'site_icon' );
delete_post_meta( $attachment_id, '_wp_attachment_image_alt' );

$icon = $method->invoke( $transformer );
$this->assertNull( $icon );

// Test with invalid image.
set_post_thumbnail( $post_id, 99999 );
$icon = $method->invoke( $transformer );
$this->assertNull( $icon );

// Cleanup.
wp_delete_post( $post_id, true );
wp_delete_attachment( $attachment_id, true );
}
}

0 comments on commit 6d5c76f

Please sign in to comment.