From a08424b2e046694e87da835da3ad05626e01aa39 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 13 Apr 2026 14:52:22 +0100 Subject: [PATCH 1/5] Add support for Mastodon FeaturedCollection import Add FeaturedItem handling to object_to_uri() so the Starter Kit importer can process both the Pixelfed starter-kit format and the Mastodon FEP FeaturedCollection format transparently. Also validates collection type on import, resolves images via object_to_uri() for format-agnostic URL extraction, and shows actor display names when available in the import UI. --- includes/functions-activity.php | 4 ++ .../wp-admin/import/class-starter-kit.php | 37 +++++++++++++++---- .../class-test-functions-activity.php | 14 +++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/includes/functions-activity.php b/includes/functions-activity.php index fbb2ceaab6..6d9a37034c 100644 --- a/includes/functions-activity.php +++ b/includes/functions-activity.php @@ -212,6 +212,10 @@ function object_to_uri( $data ) { $data = $data['href']; break; + case 'FeaturedItem': // See https://github.com/mastodon/featured_collections/pull/1. + $data = object_to_uri( $data['featuredObject'] ?? null ); + break; + default: $data = $data['id']; break; diff --git a/includes/wp-admin/import/class-starter-kit.php b/includes/wp-admin/import/class-starter-kit.php index b965cbb68f..3a3881e252 100644 --- a/includes/wp-admin/import/class-starter-kit.php +++ b/includes/wp-admin/import/class-starter-kit.php @@ -378,12 +378,16 @@ private static function render_starter_kit_info() { echo '

' . \esc_html( $name ) . '

'; - if ( ! empty( self::$starter_kit['image']['url'] ) ) { - \printf( - '%s', - \esc_url( self::$starter_kit['image']['url'] ), - \esc_attr( self::$starter_kit['image']['summary'] ?? '' ) - ); + if ( ! empty( self::$starter_kit['image'] ) ) { + $image_url = object_to_uri( self::$starter_kit['image'] ); + + if ( $image_url ) { + \printf( + '%s', + \esc_url( $image_url ), + \esc_attr( self::$starter_kit['name'] ?? '' ) + ); + } } if ( ! empty( self::$starter_kit['summary'] ) ) { @@ -440,11 +444,18 @@ private static function render_actor_selection( $actors ) { if ( ! self::is_valid_actor( $actor_uri ) ) { continue; } + + $actor_name = \is_array( $actor ) ? ( $actor['name'] ?? '' ) : ''; ?>
  • @@ -612,6 +623,18 @@ private static function get_actor_list() { return new \WP_Error( 'invalid_json', \esc_html__( 'Invalid JSON format in the uploaded file.', 'activitypub' ) ); } + /* + * Validate that the type is a Collection-like type. + * FeaturedCollection is from the Mastodon FEP draft: + * https://github.com/mastodon/featured_collections/pull/1 + */ + $type = (array) ( self::$starter_kit['type'] ?? array() ); + $valid_types = array( 'Collection', 'OrderedCollection', 'FeaturedCollection' ); + + if ( ! \array_intersect( $type, $valid_types ) ) { + return new \WP_Error( 'invalid_type', \esc_html__( 'The file does not contain a valid Starter Kit Collection.', 'activitypub' ) ); + } + $actors = self::$starter_kit['items'] ?? self::$starter_kit['orderedItems'] ?? array(); // Limit list to 150 actors. diff --git a/tests/phpunit/tests/includes/class-test-functions-activity.php b/tests/phpunit/tests/includes/class-test-functions-activity.php index 93bb8b10f0..88daf4e470 100644 --- a/tests/phpunit/tests/includes/class-test-functions-activity.php +++ b/tests/phpunit/tests/includes/class-test-functions-activity.php @@ -93,6 +93,20 @@ public function object_to_uri_provider() { ), 'https://example.com', ), + array( + array( + 'type' => 'FeaturedItem', + 'featuredObject' => 'https://example.com/users/alice', + 'featureAuthorization' => 'https://example.com/users/alice/stamps/1', + ), + 'https://example.com/users/alice', + ), + array( + array( + 'type' => 'FeaturedItem', + ), + null, + ), ); } From c63ff31eee47f0f33fdd2a257b4f51cd8200974f Mon Sep 17 00:00:00 2001 From: Automattic Bot Date: Mon, 13 Apr 2026 15:53:22 +0200 Subject: [PATCH 2/5] Add changelog --- .github/changelog/3168-from-description | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/changelog/3168-from-description diff --git a/.github/changelog/3168-from-description b/.github/changelog/3168-from-description new file mode 100644 index 0000000000..5976605a48 --- /dev/null +++ b/.github/changelog/3168-from-description @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Add support for importing Starter Packs in both the Pixelfed and Mastodon formats. From ec12137b642119e8710ae25f34995beb5f6cfaa5 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 13 Apr 2026 15:03:40 +0100 Subject: [PATCH 3/5] Use pre-computed $name variable for image alt text --- includes/wp-admin/import/class-starter-kit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wp-admin/import/class-starter-kit.php b/includes/wp-admin/import/class-starter-kit.php index 3a3881e252..792925e807 100644 --- a/includes/wp-admin/import/class-starter-kit.php +++ b/includes/wp-admin/import/class-starter-kit.php @@ -385,7 +385,7 @@ private static function render_starter_kit_info() { \printf( '%s', \esc_url( $image_url ), - \esc_attr( self::$starter_kit['name'] ?? '' ) + \esc_attr( $name ) ); } } From 153d466152d6b62f57394db073a4a183714f7f18 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 13 Apr 2026 15:27:47 +0100 Subject: [PATCH 4/5] Make object_to_uri() default branch resilient to missing id Fall back to url, then href, then null when an object has no id property. This prevents PHP notices when processing objects like images that may only have a url or href. --- includes/functions-activity.php | 10 +++++++- .../class-test-functions-activity.php | 23 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/includes/functions-activity.php b/includes/functions-activity.php index 6d9a37034c..68f453c349 100644 --- a/includes/functions-activity.php +++ b/includes/functions-activity.php @@ -217,7 +217,15 @@ function object_to_uri( $data ) { break; default: - $data = $data['id']; + if ( isset( $data['id'] ) ) { + $data = $data['id']; + } elseif ( isset( $data['url'] ) ) { + $data = object_to_uri( $data['url'] ); + } elseif ( isset( $data['href'] ) ) { + $data = $data['href']; + } else { + $data = null; + } break; } diff --git a/tests/phpunit/tests/includes/class-test-functions-activity.php b/tests/phpunit/tests/includes/class-test-functions-activity.php index 88daf4e470..1eca01a2c9 100644 --- a/tests/phpunit/tests/includes/class-test-functions-activity.php +++ b/tests/phpunit/tests/includes/class-test-functions-activity.php @@ -107,6 +107,29 @@ public function object_to_uri_provider() { ), null, ), + // Default fallback: object with url but no id. + array( + array( + 'type' => 'Unknown', + 'url' => 'https://example.com/image.jpg', + ), + 'https://example.com/image.jpg', + ), + // Default fallback: object with href but no id or url. + array( + array( + 'type' => 'Unknown', + 'href' => 'https://example.com/link', + ), + 'https://example.com/link', + ), + // Default fallback: object with no id, url, or href. + array( + array( + 'type' => 'Unknown', + ), + null, + ), ); } From e56c2c7e9e6573dd0b5de5d91edd5ad4006c4006 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 13 Apr 2026 15:41:25 +0100 Subject: [PATCH 5/5] Update moderation test for graceful missing-id handling The test expected a PHP notice when actor has no id field. Now that object_to_uri() returns null gracefully instead of triggering a notice, the test asserts the correct behavior: not blocked. --- .../tests/includes/class-test-moderation.php | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/tests/phpunit/tests/includes/class-test-moderation.php b/tests/phpunit/tests/includes/class-test-moderation.php index 0dc290ad2a..c9ac2bcbd8 100644 --- a/tests/phpunit/tests/includes/class-test-moderation.php +++ b/tests/phpunit/tests/includes/class-test-moderation.php @@ -507,23 +507,8 @@ public function test_activity_blocking_edge_cases() { ) ); - // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler - \set_error_handler( - static function ( $errno, $errstr ) { - throw new \Exception( \esc_html( $errstr ), \esc_html( $errno ) ); - }, - E_NOTICE | E_WARNING - ); - - // PHP 7.x uses "Undefined index", PHP 8+ uses "Undefined array key". - if ( version_compare( PHP_VERSION, '8.0.0', '>=' ) ) { - $this->expectExceptionMessage( 'Undefined array key "id"' ); - } else { - $this->expectExceptionMessage( 'Undefined index: id' ); - } + // A malformed actor with no 'id' should gracefully return false, not trigger a PHP notice. $this->assertFalse( Moderation::activity_is_blocked( $activity ) ); - - \restore_error_handler(); } /**