Merge branch 'master' into add/follow-me-block
Some checks failed
PHP_CodeSniffer / phpcs (push) Failing after 2s
Unit Testing / phpunit (5.6, 6.2) (push) Failing after 2s
Unit Testing / phpunit (7.0) (push) Failing after 3s
Unit Testing / phpunit (7.2) (push) Failing after 2s
Unit Testing / phpunit (7.3) (push) Failing after 2s
Unit Testing / phpunit (7.4) (push) Failing after 3s
Unit Testing / phpunit (8.0) (push) Failing after 2s
Unit Testing / phpunit (8.1) (push) Failing after 2s
Unit Testing / phpunit (8.2) (push) Failing after 2s
Unit Testing / phpunit (latest) (push) Failing after 2s
Some checks failed
PHP_CodeSniffer / phpcs (push) Failing after 2s
Unit Testing / phpunit (5.6, 6.2) (push) Failing after 2s
Unit Testing / phpunit (7.0) (push) Failing after 3s
Unit Testing / phpunit (7.2) (push) Failing after 2s
Unit Testing / phpunit (7.3) (push) Failing after 2s
Unit Testing / phpunit (7.4) (push) Failing after 3s
Unit Testing / phpunit (8.0) (push) Failing after 2s
Unit Testing / phpunit (8.1) (push) Failing after 2s
Unit Testing / phpunit (8.2) (push) Failing after 2s
Unit Testing / phpunit (latest) (push) Failing after 2s
This commit is contained in:
commit
a5c25a1e5d
10 changed files with 233 additions and 50 deletions
|
@ -75,11 +75,10 @@ Implemented:
|
|||
* follow (accept follows)
|
||||
* share posts
|
||||
* receive comments/reactions
|
||||
* signature verification
|
||||
|
||||
To implement:
|
||||
|
||||
* signature verification
|
||||
* better WordPress integration
|
||||
* better configuration possibilities
|
||||
* threaded comments support
|
||||
|
||||
|
@ -120,6 +119,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu
|
|||
* Add: Signature Verification: https://docs.joinmastodon.org/spec/security/ .
|
||||
* Add: a Followers Block.
|
||||
* Add: Simple caching
|
||||
* Add: Collection endpoints for Featured Tags and Featured Posts
|
||||
* Update: Complete rewrite of the Follower-System based on Custom Post Types.
|
||||
* Update: Improved linter (PHPCS)
|
||||
* Compatibility: Add a new conditional, `\Activitypub\is_activitypub_request()`, to allow third-party plugins to detect ActivityPub requests.
|
||||
|
@ -127,6 +127,8 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu
|
|||
* Compatibility: Indicate that the plugin is compatible and has been tested with the latest version of WordPress, 6.3.
|
||||
* Compatibility: Avoid PHP notice on sites using PHP 8.2.
|
||||
* Fixed: Load the plugin later in the WordPress code lifecycle to avoid errors in some requests.
|
||||
* Fixed: Updating posts
|
||||
* Fixed: Hashtag now support CamelCase and UTF-8
|
||||
|
||||
### 0.17.0 ###
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class Http {
|
|||
$response = \wp_safe_remote_post( $url, $args );
|
||||
$code = \wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( 400 <= $code && 500 >= $code ) {
|
||||
if ( $code >= 400 ) {
|
||||
$response = new WP_Error( $code, __( 'Failed HTTP Request', 'activitypub' ) );
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ class Http {
|
|||
$response = \wp_safe_remote_get( $url, $args );
|
||||
$code = \wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( 400 <= $code && 500 >= $code ) {
|
||||
if ( $code >= 400 ) {
|
||||
$response = new WP_Error( $code, __( 'Failed HTTP Request', 'activitypub' ) );
|
||||
}
|
||||
|
||||
|
|
|
@ -59,8 +59,7 @@ class Mention {
|
|||
);
|
||||
|
||||
$the_content = \preg_replace_callback( '/@' . ACTIVITYPUB_USERNAME_REGEXP . '/', array( self::class, 'replace_with_links' ), $the_content );
|
||||
|
||||
$the_content = str_replace( array_reverse( array_keys( $protected_tags ) ), array_reverse( array_values( $protected_tags ) ), $the_content );
|
||||
$the_content = \str_replace( array_reverse( array_keys( $protected_tags ) ), array_reverse( array_values( $protected_tags ) ), $the_content );
|
||||
|
||||
return $the_content;
|
||||
}
|
||||
|
@ -74,6 +73,7 @@ class Mention {
|
|||
*/
|
||||
public static function replace_with_links( $result ) {
|
||||
$metadata = get_remote_metadata_by_actor( $result[0] );
|
||||
|
||||
if ( ! is_wp_error( $metadata ) && ! empty( $metadata['url'] ) ) {
|
||||
$username = ltrim( $result[0], '@' );
|
||||
if ( ! empty( $metadata['name'] ) ) {
|
||||
|
|
|
@ -62,7 +62,7 @@ class Webfinger {
|
|||
$response = \wp_remote_get(
|
||||
$url,
|
||||
array(
|
||||
'headers' => array( 'Accept' => 'application/activity+json' ),
|
||||
'headers' => array( 'Accept' => 'application/jrd+json' ),
|
||||
'redirection' => 0,
|
||||
'timeout' => 2,
|
||||
)
|
||||
|
@ -94,4 +94,110 @@ class Webfinger {
|
|||
\set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a URI string to an identifier and its host.
|
||||
* Automatically adds acct: if it's missing.
|
||||
*
|
||||
* @param string $url The URI (acct:, mailto:, http:, https:)
|
||||
*
|
||||
* @return WP_Error|array Error reaction or array with
|
||||
* identifier and host as values
|
||||
*/
|
||||
public static function get_identifier_and_host( $url ) {
|
||||
// remove leading @
|
||||
$url = ltrim( $url, '@' );
|
||||
|
||||
if ( ! preg_match( '/^([a-zA-Z+]+):/', $url, $match ) ) {
|
||||
$identifier = 'acct:' . $url;
|
||||
$scheme = 'acct';
|
||||
} else {
|
||||
$identifier = $url;
|
||||
$scheme = $match[1];
|
||||
}
|
||||
|
||||
$host = null;
|
||||
|
||||
switch ( $scheme ) {
|
||||
case 'acct':
|
||||
case 'mailto':
|
||||
case 'xmpp':
|
||||
if ( strpos( $identifier, '@' ) !== false ) {
|
||||
$host = substr( $identifier, strpos( $identifier, '@' ) + 1 );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$host = wp_parse_url( $identifier, PHP_URL_HOST );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( empty( $host ) ) {
|
||||
return new WP_Error( 'invalid_identifier', __( 'Invalid Identifier', 'activitypub' ) );
|
||||
}
|
||||
|
||||
return array( $identifier, $host );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WebFinger data for a given URI
|
||||
*
|
||||
* @param string $identifier The Identifier: <identifier>@<host>
|
||||
* @param string $host The Host: <identifier>@<host>
|
||||
*
|
||||
* @return WP_Error|array Error reaction or array with
|
||||
* identifier and host as values
|
||||
*/
|
||||
public static function get_data( $identifier, $host ) {
|
||||
$webfinger_url = 'https://' . $host . '/.well-known/webfinger?resource=' . rawurlencode( $identifier );
|
||||
|
||||
$response = wp_safe_remote_get(
|
||||
$webfinger_url,
|
||||
array(
|
||||
'headers' => array( 'Accept' => 'application/jrd+json' ),
|
||||
'redirection' => 0,
|
||||
'timeout' => 2,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return new WP_Error( 'webfinger_url_not_accessible', null, $webfinger_url );
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
|
||||
return json_decode( $body, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function get_remote_follow_endpoint( $uri ) {
|
||||
$identifier_and_host = self::get_identifier_and_host( $uri );
|
||||
|
||||
if ( is_wp_error( $identifier_and_host ) ) {
|
||||
return $identifier_and_host;
|
||||
}
|
||||
|
||||
list( $identifier, $host ) = $identifier_and_host;
|
||||
|
||||
$data = self::get_data( $identifier, $host );
|
||||
|
||||
if ( is_wp_error( $data ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( empty( $data['links'] ) ) {
|
||||
return new WP_Error( 'webfinger_url_invalid_response', null, $data );
|
||||
}
|
||||
|
||||
foreach ( $data['links'] as $link ) {
|
||||
if ( 'http://ostatus.org/schema/1.0/subscribe' === $link['rel'] ) {
|
||||
return $link['template'];
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error( 'webfinger_remote_follow_endpoint_invalid', null, $data );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,11 +265,18 @@ class Followers {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( isset( $object['user_id'] ) ) {
|
||||
unset( $object['user_id'] );
|
||||
}
|
||||
|
||||
unset( $object['@context'] );
|
||||
// only send minimal data
|
||||
$object = array_intersect_key(
|
||||
$object,
|
||||
array_flip(
|
||||
array(
|
||||
'id',
|
||||
'type',
|
||||
'actor',
|
||||
'object',
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$user = Users::get_by_id( $user_id );
|
||||
|
||||
|
@ -282,7 +289,7 @@ class Followers {
|
|||
$activity->set_object( $object );
|
||||
$activity->set_actor( $user->get_id() );
|
||||
$activity->set_to( $actor );
|
||||
$activity->set_id( $user->get_id() . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) );
|
||||
$activity->set_id( $user->get_id() . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) . '-' . \time() );
|
||||
|
||||
$activity = $activity->to_json();
|
||||
|
||||
|
|
|
@ -305,8 +305,9 @@ function is_activitypub_request() {
|
|||
* and return true when the header includes at least one of the following:
|
||||
* - application/activity+json
|
||||
* - application/ld+json
|
||||
* - application/json
|
||||
*/
|
||||
if ( preg_match( '/(application\/(ld\+json|activity\+json))/', $accept ) ) {
|
||||
if ( preg_match( '/(application\/(ld\+json|activity\+json|json))/i', $accept ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,11 @@ namespace Activitypub\Rest;
|
|||
use WP_REST_Server;
|
||||
use WP_REST_Response;
|
||||
use Activitypub\Transformer\Post;
|
||||
use Activitypub\Activity\Activity;
|
||||
use Activitypub\Collection\Users as User_Collection;
|
||||
|
||||
use function Activitypub\esc_hashtag;
|
||||
use function Activitypub\is_single_user;
|
||||
use function Activitypub\get_rest_url_by_path;
|
||||
|
||||
/**
|
||||
|
@ -64,7 +67,13 @@ class Collection {
|
|||
*/
|
||||
public static function tags_get( $request ) {
|
||||
$user_id = $request->get_param( 'user_id' );
|
||||
$number = 4;
|
||||
$user = User_Collection::get_by_various( $user_id );
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
$number = 4;
|
||||
|
||||
$tags = \get_terms(
|
||||
array(
|
||||
|
@ -80,13 +89,9 @@ class Collection {
|
|||
}
|
||||
|
||||
$response = array(
|
||||
'@context' => array(
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
array(
|
||||
'Hashtag' => 'as:Hashtag',
|
||||
),
|
||||
),
|
||||
'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $user_id ) ),
|
||||
'@context' => Activity::CONTEXT,
|
||||
'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $user->get__id() ) ),
|
||||
'type' => 'Collection',
|
||||
'totalItems' => count( $tags ),
|
||||
'items' => array(),
|
||||
);
|
||||
|
@ -111,36 +116,43 @@ class Collection {
|
|||
*/
|
||||
public static function featured_get( $request ) {
|
||||
$user_id = $request->get_param( 'user_id' );
|
||||
$user = User_Collection::get_by_various( $user_id );
|
||||
|
||||
$args = array(
|
||||
'post__in' => \get_option( 'sticky_posts' ),
|
||||
'ignore_sticky_posts' => 1,
|
||||
);
|
||||
|
||||
if ( $user_id > 0 ) {
|
||||
$args['author'] = $user_id;
|
||||
if ( is_wp_error( $user ) ) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
$posts = \get_posts( $args );
|
||||
$sticky_posts = \get_option( 'sticky_posts' );
|
||||
|
||||
if ( ! is_single_user() && User_Collection::BLOG_USER_ID === $user->get__id() ) {
|
||||
$posts = array();
|
||||
} elseif ( $sticky_posts ) {
|
||||
$args = array(
|
||||
'post__in' => $sticky_posts,
|
||||
'ignore_sticky_posts' => 1,
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
);
|
||||
|
||||
if ( $user->get__id() > 0 ) {
|
||||
$args['author'] = $user->get__id();
|
||||
}
|
||||
|
||||
$posts = \get_posts( $args );
|
||||
} else {
|
||||
$posts = array();
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
array(
|
||||
'ostatus' => 'http://ostatus.org#',
|
||||
'atomUri' => 'ostatus:atomUri',
|
||||
'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri',
|
||||
'conversation' => 'ostatus:conversation',
|
||||
'sensitive' => 'as:sensitive',
|
||||
'toot' => 'http://joinmastodon.org/ns#',
|
||||
'votersCount' => 'toot:votersCount',
|
||||
),
|
||||
'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $user_id ) ),
|
||||
'totalItems' => count( $posts ),
|
||||
'items' => array(),
|
||||
'@context' => Activity::CONTEXT,
|
||||
'id' => get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $user_id ) ),
|
||||
'type' => 'OrderedCollection',
|
||||
'totalItems' => count( $posts ),
|
||||
'orderedItems' => array(),
|
||||
);
|
||||
|
||||
foreach ( $posts as $post ) {
|
||||
$response['items'][] = Post::transform( $post )->to_object()->to_array();
|
||||
$response['orderedItems'][] = Post::transform( $post )->to_object()->to_array();
|
||||
}
|
||||
|
||||
return new WP_REST_Response( $response, 200 );
|
||||
|
|
|
@ -3,7 +3,9 @@ namespace Activitypub\Rest;
|
|||
|
||||
use WP_Error;
|
||||
use WP_REST_Server;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
use Activitypub\Webfinger;
|
||||
use Activitypub\Activity\Activity;
|
||||
use Activitypub\Collection\Users as User_Collection;
|
||||
|
||||
|
@ -40,6 +42,25 @@ class Users {
|
|||
),
|
||||
)
|
||||
);
|
||||
|
||||
\register_rest_route(
|
||||
ACTIVITYPUB_REST_NAMESPACE,
|
||||
'/users/(?P<user_id>[\w\-\.]+)/remote-follow',
|
||||
array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( self::class, 'remote_follow_get' ),
|
||||
|
||||
'args' => array(
|
||||
'resource' => array(
|
||||
'required' => true,
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
'permission_callback' => '__return_true',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,6 +101,38 @@ class Users {
|
|||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Endpoint for remote follow UI/Block
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
*
|
||||
* @return void|string The URL to the remote follow page
|
||||
*/
|
||||
public static function remote_follow_get( WP_REST_Request $request ) {
|
||||
$resource = $request->get_param( 'resource' );
|
||||
$user_id = $request->get_param( 'user_id' );
|
||||
$user = User_Collection::get_by_various( $user_id );
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
$template = Webfinger::get_remote_follow_endpoint( $resource );
|
||||
|
||||
if ( is_wp_error( $template ) ) {
|
||||
return $template;
|
||||
}
|
||||
|
||||
$resource = $user->get_resource();
|
||||
$url = str_replace( '{uri}', $resource, $template );
|
||||
|
||||
return new WP_REST_Response(
|
||||
array( 'url' => $url ),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The supported parameters
|
||||
*
|
||||
|
|
|
@ -75,11 +75,10 @@ Implemented:
|
|||
* follow (accept follows)
|
||||
* share posts
|
||||
* receive comments/reactions
|
||||
* signature verification
|
||||
|
||||
To implement:
|
||||
|
||||
* signature verification
|
||||
* better WordPress integration
|
||||
* better configuration possibilities
|
||||
* threaded comments support
|
||||
|
||||
|
@ -120,6 +119,7 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu
|
|||
* Add: Signature Verification: https://docs.joinmastodon.org/spec/security/ .
|
||||
* Add: a Followers Block.
|
||||
* Add: Simple caching
|
||||
* Add: Collection endpoints for Featured Tags and Featured Posts
|
||||
* Update: Complete rewrite of the Follower-System based on Custom Post Types.
|
||||
* Update: Improved linter (PHPCS)
|
||||
* Compatibility: Add a new conditional, `\Activitypub\is_activitypub_request()`, to allow third-party plugins to detect ActivityPub requests.
|
||||
|
@ -127,6 +127,8 @@ Project maintained on GitHub at [automattic/wordpress-activitypub](https://githu
|
|||
* Compatibility: Indicate that the plugin is compatible and has been tested with the latest version of WordPress, 6.3.
|
||||
* Compatibility: Avoid PHP notice on sites using PHP 8.2.
|
||||
* Fixed: Load the plugin later in the WordPress code lifecycle to avoid errors in some requests.
|
||||
* Fixed: Updating posts
|
||||
* Fixed: Hashtag now support CamelCase and UTF-8
|
||||
|
||||
= 0.17.0 =
|
||||
|
||||
|
|
|
@ -30,13 +30,13 @@
|
|||
<label for="activitypub-blog-identifier"><?php \esc_html_e( 'Username', 'activitypub' ); ?></label>
|
||||
</p>
|
||||
<p>
|
||||
<input type="text" class="regular-text" id="activitypub-blog-identifier" value="<?php echo \esc_attr( $blog_user->get_resource() ); ?>" />
|
||||
<input type="text" class="regular-text" id="activitypub-blog-identifier" value="<?php echo \esc_attr( $blog_user->get_resource() ); ?>" readonly />
|
||||
</p>
|
||||
<p>
|
||||
<label for="activitypub-blog-url"><?php \esc_html_e( 'Profile-URL', 'activitypub' ); ?></label>
|
||||
</p>
|
||||
<p>
|
||||
<input type="text" class="regular-text" id="activitypub-blog-url" value="<?php echo \esc_attr( $blog_user->get_url() ); ?>" />
|
||||
<input type="text" class="regular-text" id="activitypub-blog-url" value="<?php echo \esc_attr( $blog_user->get_url() ); ?>" readonly />
|
||||
</p>
|
||||
<p>
|
||||
<?php \esc_html_e( 'This Blog-User will federate all posts written on your Blog, regardless of the User who posted it.', 'activitypub' ); ?>
|
||||
|
@ -62,13 +62,13 @@
|
|||
<label for="activitypub-user-identifier"><?php \esc_html_e( 'Username', 'activitypub' ); ?></label>
|
||||
</p>
|
||||
<p>
|
||||
<input type="text" class="regular-text" id="activitypub-user-identifier" value="<?php echo \esc_attr( $user->get_resource() ); ?>" />
|
||||
<input type="text" class="regular-text" id="activitypub-user-identifier" value="<?php echo \esc_attr( $user->get_resource() ); ?>" readonly />
|
||||
</p>
|
||||
<p>
|
||||
<label for="activitypub-user-url"><?php \esc_html_e( 'Profile-URL', 'activitypub' ); ?></label>
|
||||
</p>
|
||||
<p>
|
||||
<input type="text" class="regular-text" id="activitypub-user-url" value="<?php echo \esc_attr( $user->get_url() ); ?>" />
|
||||
<input type="text" class="regular-text" id="activitypub-user-url" value="<?php echo \esc_attr( $user->get_url() ); ?>" readonly />
|
||||
</p>
|
||||
<p>
|
||||
<?php \esc_html_e( 'Users who can not access this settings page will find their username on the "Edit Profile" page.', 'activitypub' ); ?>
|
||||
|
|
Loading…
Reference in a new issue