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.
diff --git a/includes/functions-activity.php b/includes/functions-activity.php
index fbb2ceaab6..68f453c349 100644
--- a/includes/functions-activity.php
+++ b/includes/functions-activity.php
@@ -212,8 +212,20 @@ 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'];
+ 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/includes/wp-admin/import/class-starter-kit.php b/includes/wp-admin/import/class-starter-kit.php
index b965cbb68f..792925e807 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(
- '
',
- \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(
+ '
',
+ \esc_url( $image_url ),
+ \esc_attr( $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..1eca01a2c9 100644
--- a/tests/phpunit/tests/includes/class-test-functions-activity.php
+++ b/tests/phpunit/tests/includes/class-test-functions-activity.php
@@ -93,6 +93,43 @@ 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,
+ ),
+ // 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,
+ ),
);
}
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();
}
/**