From 93b2f1ee7d1d740ff9f0821deca0a69664cbf928 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 5 Dec 2023 18:59:00 +0100 Subject: [PATCH] Normalize attributes that can have mixed value types (#586) * fix #571 * support empty values * fix phpcs issues * test for `null` * use `object_to_uri` on followers list --- includes/class-blocks.php | 6 ++- includes/compat.php | 29 +++++++++++++ includes/functions.php | 37 ++++++++++++++++ includes/rest/class-inbox.php | 15 ++----- includes/table/class-followers.php | 4 +- tests/test-functions.php | 69 ++++++++++++++++++++++++++++++ 6 files changed, 145 insertions(+), 15 deletions(-) diff --git a/includes/class-blocks.php b/includes/class-blocks.php index 02f9659..267ebee 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -3,7 +3,9 @@ namespace Activitypub; use Activitypub\Collection\Followers; use Activitypub\Collection\Users as User_Collection; -use Activitypub\is_user_type_disabled; + +use function Activitypub\object_to_uri; +use function Activitypub\is_user_type_disabled; class Blocks { public static function init() { @@ -140,7 +142,7 @@ class Blocks { return sprintf( $template, - esc_url( $data['url'] ), + esc_url( object_to_uri( $data['url'] ) ), esc_attr( $data['name'] ), esc_attr( $data['icon']['url'] ), esc_html( $data['name'] ), diff --git a/includes/compat.php b/includes/compat.php index c0996af..d1047df 100644 --- a/includes/compat.php +++ b/includes/compat.php @@ -47,3 +47,32 @@ if ( ! function_exists( 'is_countable' ) ) { return is_array( $value ) || $value instanceof \Countable; } } + +/** + * Polyfill for `array_is_list()` function added in PHP 7.3. + * + * @param array $array The array to check. + * + * @return bool True if `$array` is a list, otherwise false. + */ +if ( ! function_exists( 'array_is_list' ) ) { + function array_is_list( $array ) { + if ( ! is_array( $array ) ) { + return false; + } + + if ( array_values( $array ) === $array ) { + return true; + } + + $next_key = -1; + + foreach ( $array as $k => $v ) { + if ( ++$next_key !== $k ) { + return false; + } + } + + return true; + } +} diff --git a/includes/functions.php b/includes/functions.php index 9b2c64d..1140724 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -698,3 +698,40 @@ function url_to_commentid( $url ) { return null; } + +/** + * Get the URI of an ActivityPub object + * + * @param array $object The ActivityPub object + * + * @return string The URI of the ActivityPub object + */ +function object_to_uri( $object ) { + // check if it is already simple + if ( ! $object || is_string( $object ) ) { + return $object; + } + + // check if it is a list, then take first item + // this plugin does not support collections + if ( array_is_list( $object ) ) { + $object = $object[0]; + } + + // check if it is simplified now + if ( is_string( $object ) ) { + return $object; + } + + // return part of Object that makes most sense + switch ( $object['type'] ) { + case 'Link': + $object = $object['href']; + break; + default: + $object = $object['id']; + break; + } + + return $object; +} diff --git a/includes/rest/class-inbox.php b/includes/rest/class-inbox.php index d38ffb5..bdca0f4 100644 --- a/includes/rest/class-inbox.php +++ b/includes/rest/class-inbox.php @@ -8,6 +8,7 @@ use Activitypub\Activity\Activity; use Activitypub\Collection\Users as User_Collection; use function Activitypub\get_context; +use function Activitypub\object_to_uri; use function Activitypub\url_to_authorid; use function Activitypub\get_rest_url_by_path; use function Activitypub\get_remote_metadata_by_actor; @@ -237,14 +238,7 @@ class Inbox { $params['actor'] = array( 'required' => true, 'sanitize_callback' => function( $param, $request, $key ) { - if ( \is_array( $param ) ) { - if ( isset( $param['id'] ) ) { - $param = $param['id']; - } else { - $param = $param['url']; - } - } - return \esc_url_raw( $param ); + return object_to_uri( $param ); }, ); @@ -286,10 +280,7 @@ class Inbox { 'required' => true, //'type' => array( 'object', 'string' ), 'sanitize_callback' => function( $param, $request, $key ) { - if ( ! \is_string( $param ) ) { - $param = $param['id']; - } - return \esc_url_raw( $param ); + return object_to_uri( $param ); }, ); diff --git a/includes/table/class-followers.php b/includes/table/class-followers.php index 3045ddd..df9747b 100644 --- a/includes/table/class-followers.php +++ b/includes/table/class-followers.php @@ -5,6 +5,8 @@ use WP_List_Table; use Activitypub\Collection\Users; use Activitypub\Collection\Followers as FollowerCollection; +use function Activitypub\object_to_uri; + if ( ! \class_exists( '\WP_List_Table' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; } @@ -101,7 +103,7 @@ class Followers extends WP_List_Table { 'icon' => esc_attr( $follower->get_icon_url() ), 'post_title' => esc_attr( $follower->get_name() ), 'username' => esc_attr( $follower->get_preferred_username() ), - 'url' => esc_attr( $follower->get_url() ), + 'url' => esc_attr( object_to_uri( $follower->get_url() ) ), 'identifier' => esc_attr( $follower->get_id() ), 'published' => esc_attr( $follower->get_published() ), 'modified' => esc_attr( $follower->get_updated() ), diff --git a/tests/test-functions.php b/tests/test-functions.php index da85ef8..4ee049c 100644 --- a/tests/test-functions.php +++ b/tests/test-functions.php @@ -77,4 +77,73 @@ class Test_Functions extends ActivityPub_TestCase_Cache_HTTP { $query_result = \Activitypub\object_id_to_comment( $duplicate_comment_source_id ); $this->assertFalse( $query_result ); } + + /** + * @dataProvider object_to_uri_provider + */ + public function test_object_to_uri( $input, $output ) { + $this->assertEquals( $output, \Activitypub\object_to_uri( $input ) ); + } + + public function object_to_uri_provider() { + return array( + array( null, null ), + array( 'https://example.com', 'https://example.com' ), + array( array( 'https://example.com' ), 'https://example.com' ), + array( + array( + 'https://example.com', + 'https://example.org', + ), + 'https://example.com', + ), + array( + array( + 'type' => 'Link', + 'href' => 'https://example.com', + ), + 'https://example.com', + ), + array( + array( + array( + 'type' => 'Link', + 'href' => 'https://example.com', + ), + array( + 'type' => 'Link', + 'href' => 'https://example.org', + ), + ), + 'https://example.com', + ), + array( + array( + 'type' => 'Actor', + 'id' => 'https://example.com', + ), + 'https://example.com', + ), + array( + array( + array( + 'type' => 'Actor', + 'id' => 'https://example.com', + ), + array( + 'type' => 'Actor', + 'id' => 'https://example.org', + ), + ), + 'https://example.com', + ), + array( + array( + 'type' => 'Activity', + 'id' => 'https://example.com', + ), + 'https://example.com', + ), + ); + } }