Skip to content

Commit

Permalink
Merge branch 'master' into add/content-warning-metabox
Browse files Browse the repository at this point in the history
  • Loading branch information
pfefferle authored Sep 25, 2024
2 parents 18b6a1a + 7370e97 commit 87f31f7
Show file tree
Hide file tree
Showing 12 changed files with 446 additions and 32 deletions.
2 changes: 1 addition & 1 deletion activitypub.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
\defined( 'ACTIVITYPUB_AUTHORIZED_FETCH' ) || \define( 'ACTIVITYPUB_AUTHORIZED_FETCH', false );
\defined( 'ACTIVITYPUB_DISABLE_REWRITES' ) || \define( 'ACTIVITYPUB_DISABLE_REWRITES', false );
\defined( 'ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS' ) || \define( 'ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS', false );
// Disable reactions like `Like` and `Accounce` by default
// Disable reactions like `Like` and `Announce` by default
\defined( 'ACTIVITYPUB_DISABLE_REACTIONS' ) || \define( 'ACTIVITYPUB_DISABLE_REACTIONS', true );
\defined( 'ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS' ) || \define( 'ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS', false );
\defined( 'ACTIVITYPUB_SHARED_INBOX_FEATURE' ) || \define( 'ACTIVITYPUB_SHARED_INBOX_FEATURE', false );
Expand Down
15 changes: 15 additions & 0 deletions includes/activity/class-activity.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,21 @@ class Activity extends Base_Object {
*/
protected $result;

/**
* Identifies a Collection containing objects considered to be responses
* to this object.
* WordPress has a strong core system of approving replies. We only include
* approved replies here.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies
*
* @var array
* | ObjectType
* | Link
* | null
*/
protected $replies;

/**
* An indirect object of the activity from which the
* activity is directed.
Expand Down
61 changes: 47 additions & 14 deletions includes/class-comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,46 @@ public static function comment_class( $classes, $css_class, $comment_id ) {
return $classes;
}

/**
* Gets the public comment id via the WordPress comments meta.
*
* @param int $wp_comment_id The internal WordPress comment ID.
* @param bool $fallback Whether the code should fall back to `source_url` if `source_id` is not set.
*
* @return string|null The ActivityPub id/url of the comment.
*/
public static function get_source_id( $wp_comment_id, $fallback = true ) {
$comment_meta = \get_comment_meta( $wp_comment_id );

if ( ! empty( $comment_meta['source_id'][0] ) ) {
return $comment_meta['source_id'][0];
} elseif ( ! empty( $comment_meta['source_url'][0] && $fallback ) ) {
return $comment_meta['source_url'][0];
}

return null;
}

/**
* Gets the public comment url via the WordPress comments meta.
*
* @param int $wp_comment_id The internal WordPress comment ID.
* @param bool $fallback Whether the code should fall back to `source_id` if `source_url` is not set.
*
* @return string|null The ActivityPub id/url of the comment.
*/
public static function get_source_url( $wp_comment_id, $fallback = true ) {
$comment_meta = \get_comment_meta( $wp_comment_id );

if ( ! empty( $comment_meta['source_url'][0] ) ) {
return $comment_meta['source_url'][0];
} elseif ( ! empty( $comment_meta['source_id'][0] && $fallback ) ) {
return $comment_meta['source_id'][0];
}

return null;
}

/**
* Link remote comments to source url.
*
Expand All @@ -353,15 +393,9 @@ public static function remote_comment_link( $comment_link, $comment ) {
return $comment_link;
}

$comment_meta = \get_comment_meta( $comment->comment_ID );

if ( ! empty( $comment_meta['source_url'][0] ) ) {
return $comment_meta['source_url'][0];
} elseif ( ! empty( $comment_meta['source_id'][0] ) ) {
return $comment_meta['source_id'][0];
}
$public_comment_link = self::get_source_url( $comment->comment_ID );

return $comment_link;
return $public_comment_link ?? $comment_link;
}


Expand All @@ -373,14 +407,13 @@ public static function remote_comment_link( $comment_link, $comment ) {
* @return string ActivityPub URI for comment
*/
public static function generate_id( $comment ) {
$comment = \get_comment( $comment );
$comment_meta = \get_comment_meta( $comment->comment_ID );
$comment = \get_comment( $comment );

// show external comment ID if it exists
if ( ! empty( $comment_meta['source_id'][0] ) ) {
return $comment_meta['source_id'][0];
} elseif ( ! empty( $comment_meta['source_url'][0] ) ) {
return $comment_meta['source_url'][0];
$public_comment_link = self::get_source_id( $comment->comment_ID );

if ( $public_comment_link ) {
return $public_comment_link;
}

// generate URI based on comment ID
Expand Down
2 changes: 1 addition & 1 deletion includes/collection/class-followers.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public static function get_follower( $user_id, $actor ) {
}

/**
* Get a Follower by Actor indepenent from the User.
* Get a Follower by Actor independent from the User.
*
* @param string $actor The Actor URL.
*
Expand Down
176 changes: 176 additions & 0 deletions includes/collection/class-replies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php
namespace Activitypub\Collection;

use WP_Post;
use WP_Comment;
use WP_Error;

use Activitypub\Comment;

use function Activitypub\is_local_comment;
use function Activitypub\get_rest_url_by_path;

/**
* Class containing code for getting replies Collections and CollectionPages of posts and comments.
*/
class Replies {
/**
* Build base arguments for fetching the comments of either a WordPress post or comment.
*
* @param WP_Post|WP_Comment $wp_object
*/
private static function build_args( $wp_object ) {
$args = array(
'status' => 'approve',
'orderby' => 'comment_date_gmt',
'order' => 'ASC',
);

if ( $wp_object instanceof WP_Post ) {
$args['parent'] = 0; // TODO: maybe this is unnecessary.
$args['post_id'] = $wp_object->ID;
} elseif ( $wp_object instanceof WP_Comment ) {
$args['parent'] = $wp_object->comment_ID;
} else {
return new WP_Error();
}

return $args;
}

/**
* Adds pagination args comments query.
*
* @param array $args Query args built by self::build_args.
* @param int $page The current pagination page.
* @param int $comments_per_page The number of comments per page.
*/
private static function add_pagination_args( $args, $page, $comments_per_page ) {
$args['number'] = $comments_per_page;

$offset = intval( $page ) * $comments_per_page;
$args['offset'] = $offset;

return $args;
}


/**
* Get the replies collections ID.
*
* @param WP_Post|WP_Comment $wp_object
*
* @return string The rest URL of the replies collection.
*/
private static function get_id( $wp_object ) {
if ( $wp_object instanceof WP_Post ) {
return get_rest_url_by_path( sprintf( 'posts/%d/replies', $wp_object->ID ) );
} elseif ( $wp_object instanceof WP_Comment ) {
return get_rest_url_by_path( sprintf( 'comments/%d/replies', $wp_object->comment_ID ) );
} else {
return new WP_Error();
}
}

/**
* Get the replies collection.
*
* @param WP_Post|WP_Comment $wp_object
* @param int $page
*
* @return array An associative array containing the replies collection without JSON-LD context.
*/
public static function get_collection( $wp_object ) {
$id = self::get_id( $wp_object );

if ( ! $id ) {
return null;
}

$replies = array(
'id' => $id,
'type' => 'Collection',
);

$replies['first'] = self::get_collection_page( $wp_object, 0, $replies['id'] );

return $replies;
}

/**
* Get the ActivityPub ID's from a list of comments.
*
* It takes only federated/non-local comments into account, others also do not have an
* ActivityPub ID available.
*
* @param WP_Comment[] $comments The comments to retrieve the ActivityPub ids from.
*
* @return string[] A list of the ActivityPub ID's.
*/
private static function get_reply_ids( $comments ) {
$comment_ids = array();
// Only add external comments from the fediverse.
// Maybe use the Comment class more and the function is_local_comment etc.
foreach ( $comments as $comment ) {
if ( is_local_comment( $comment ) ) {
continue;
}

$public_comment_id = Comment::get_source_id( $comment->comment_ID );
if ( $public_comment_id ) {
$comment_ids[] = $public_comment_id;
}
}
return $comment_ids;
}

/**
* Returns a replies collection page as an associative array.
*
* @link https://www.w3.org/TR/activitystreams-vocabulary/#dfn-collectionpage
*
* @param WP_Post|WP_Comment $wp_object The post of comment the replies are for.
* @param int $page The current pagination page.
* @param string $part_of The collection id/url the returned CollectionPage belongs to.
*
* @return array A CollectionPage as an associative array.
*/
public static function get_collection_page( $wp_object, $page, $part_of = null ) {
// Build initial arguments for fetching approved comments.
$args = self::build_args( $wp_object );

// Retrieve the partOf if not already given.
$part_of = $part_of ?? self::get_id( $wp_object );

// If the collection page does not exist.
if ( is_wp_error( $args ) || is_wp_error( $part_of ) ) {
return null;
}

// Get to total replies count.
$total_replies = \get_comments( array_merge( $args, array( 'count' => true ) ) );

// Modify query args to retrieve paginated results.
$comments_per_page = \get_option( 'comments_per_page' );

// Fetch internal and external comments for current page.
$comments = get_comments( self::add_pagination_args( $args, $page, $comments_per_page ) );

// Get the ActivityPub ID's of the comments, without out local-only comments.
$comment_ids = self::get_reply_ids( $comments );

// Build the associative CollectionPage array.
$collection_page = array(
'id' => \add_query_arg( 'page', $page, $part_of ),
'type' => 'CollectionPage',
'partOf' => $part_of,
'items' => $comment_ids,
);

if ( $total_replies / $comments_per_page > $page + 1 ) {
$collection_page['next'] = \add_query_arg( 'page', $page + 1, $part_of );
}

return $collection_page;
}
}
Loading

0 comments on commit 87f31f7

Please sign in to comment.