Replies Collection, settings, other fixes

This commit is contained in:
Django Doucet 2022-09-27 15:28:45 -06:00
parent 51643142aa
commit 8e1c9ff6bb
11 changed files with 246 additions and 173 deletions

View file

@ -86,6 +86,19 @@ function init() {
} }
\add_action( 'plugins_loaded', '\Activitypub\init' ); \add_action( 'plugins_loaded', '\Activitypub\init' );
/**
* Add plugin settings link
*/
function plugin_settings_link( $links ) {
$settings_link[] = \sprintf( '<a href="%1s">%2s</a>',
\menu_page_url( 'activitypub', false ),
\__( 'Settings', 'activitypub' )
);
return \array_merge( $settings_link, $links );
}
\add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), '\Activitypub\plugin_settings_link' );
/** /**
* Add rewrite rules * Add rewrite rules
*/ */

View file

@ -15,7 +15,7 @@ class Activity_Dispatcher {
public static function init() { public static function init() {
\add_action( 'activitypub_send_post_activity', array( '\Activitypub\Activity_Dispatcher', 'send_post_activity' ) ); \add_action( 'activitypub_send_post_activity', array( '\Activitypub\Activity_Dispatcher', 'send_post_activity' ) );
\add_action( 'activitypub_send_update_activity', array( '\Activitypub\Activity_Dispatcher', 'send_update_activity' ) ); \add_action( 'activitypub_send_update_activity', array( '\Activitypub\Activity_Dispatcher', 'send_update_activity' ) );
\add_action( 'activitypub_send_delete_activity', array( '\Activitypub\Activity_Dispatcher', 'send_delete_activity' ) ); \add_action( 'activitypub_send_delete_activity', array( '\Activitypub\Activity_Dispatcher', 'send_delete_activity', 2 ) );
\add_action( 'activitypub_send_comment_activity', array( '\Activitypub\Activity_Dispatcher', 'send_comment_activity' ) ); \add_action( 'activitypub_send_comment_activity', array( '\Activitypub\Activity_Dispatcher', 'send_comment_activity' ) );
\add_action( 'activitypub_send_update_comment_activity', array( '\Activitypub\Activity_Dispatcher', 'send_update_comment_activity' ) ); \add_action( 'activitypub_send_update_comment_activity', array( '\Activitypub\Activity_Dispatcher', 'send_update_comment_activity' ) );
@ -49,11 +49,6 @@ class Activity_Dispatcher {
* @param \Activitypub\Model\Post $activitypub_post * @param \Activitypub\Model\Post $activitypub_post
*/ */
public static function send_update_activity( $activitypub_post ) { public static function send_update_activity( $activitypub_post ) {
// save permalink for delete
$post_id = \url_to_postid( $activitypub_post->get_id() );
//shouldn't this go in schedule_*_activity? yeah
\update_post_meta( $post_id, '_ap_deleted_slug', $activitypub_post->get_id() );
// get latest version of post // get latest version of post
$user_id = $activitypub_post->get_post_author(); $user_id = $activitypub_post->get_post_author();
$updated = \wp_date( 'Y-m-d\TH:i:s\Z', \strtotime( $activitypub_post->get_updated() ) ); $updated = \wp_date( 'Y-m-d\TH:i:s\Z', \strtotime( $activitypub_post->get_updated() ) );
@ -74,10 +69,13 @@ class Activity_Dispatcher {
* *
* @param \Activitypub\Model\Post $activitypub_post * @param \Activitypub\Model\Post $activitypub_post
*/ */
public static function send_delete_activity( $activitypub_post ) { public static function send_delete_activity( $activitypub_post, $permalink = null ) {
// get latest version of post // get latest version of post
$user_id = $activitypub_post->get_post_author(); $user_id = $activitypub_post->get_post_author();
$deleted = \current_time( 'Y-m-d\TH:i:s\Z', true ); $deleted = \current_time( 'Y-m-d\TH:i:s\Z', true );
if ( $permalink ) {
$activitypub_post->set_id( $permalink );
}
$activitypub_post->set_deleted( $deleted ); $activitypub_post->set_deleted( $deleted );
$activitypub_activity = new \Activitypub\Model\Activity( 'Delete', \Activitypub\Model\Activity::TYPE_FULL ); $activitypub_activity = new \Activitypub\Model\Activity( 'Delete', \Activitypub\Model\Activity::TYPE_FULL );
@ -101,27 +99,27 @@ class Activity_Dispatcher {
//ONLY FOR LOCAL USERS ? //ONLY FOR LOCAL USERS ?
$activitypub_comment = \get_comment( $activitypub_comment_id ); $activitypub_comment = \get_comment( $activitypub_comment_id );
$user_id = $activitypub_comment->user_id; $user_id = $activitypub_comment->user_id;
$replyto = get_comment_meta( $activitypub_comment->comment_parent, 'comment_author_url', true );// must include in replyto $mentions[] = \get_comment_meta( $activitypub_comment_id, 'mentions', true );// mention[href, name]
$mentions = get_comment_meta( $activitypub_comment_id, 'mentions', true );//might be tagged
$activitypub_comment = new \Activitypub\Model\Comment( $activitypub_comment ); $activitypub_comment = new \Activitypub\Model\Comment( $activitypub_comment );
$activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_FULL ); $activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_FULL );
$activitypub_activity->from_comment( $activitypub_comment->to_array() ); $activitypub_activity->from_comment( $activitypub_comment->to_array() );
$mentioned_actors = array();
foreach ( \Activitypub\get_mentioned_inboxes( $mentions ) as $inbox => $to ) { foreach ( \Activitypub\get_mentioned_inboxes( $mentions ) as $inbox => $to ) {
$activitypub_activity->set_to( $to );//all users at shared inbox $activitypub_activity->set_to( $to );//all users at shared inbox
$activity = $activitypub_activity->to_json(); // phpcs:ignore $activity = $activitypub_activity->to_json(); // phpcs:ignore
\Activitypub\safe_remote_post( $inbox, $activity, $user_id ); \Activitypub\safe_remote_post( $inbox, $activity, $user_id );
$mentioned_actors[] = $to;
} }
//will this reset the activities?
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $cc ) { foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $cc ) {
$activitypub_activity->set_cc( $cc );//set_cc $activitypub_activity->set_cc( $cc );//set_cc
$activity = $activitypub_activity->to_json(); // phpcs:ignore $activity = $activitypub_activity->to_json(); // phpcs:ignore
// Send reply to followers, skip if replying to followers (avoid duplicate replies) // Send reply to followers, skip if mentioned followers (avoid duplicate replies)
if ( in_array( $cc, $replyto ) || in_array( $cc, $mentions ) ) { if ( in_array( $cc, $mentioned_actors ) ) {
continue; continue;
} }
\Activitypub\safe_remote_post( $inbox, $activity, $user_id ); \Activitypub\safe_remote_post( $inbox, $activity, $user_id );
@ -136,12 +134,11 @@ class Activity_Dispatcher {
public static function inbox_forward_activity( $activitypub_comment_id ) { public static function inbox_forward_activity( $activitypub_comment_id ) {
$activitypub_comment = \get_comment( $activitypub_comment_id ); $activitypub_comment = \get_comment( $activitypub_comment_id );
//original author should NOT recieve a copy of ther own post //original author should NOT recieve a copy of their own post
$replyto[] = $activitypub_comment->comment_author_url; $replyto[] = $activitypub_comment->comment_author_url;
$activitypub_activity = unserialize( get_comment_meta( $activitypub_comment->comment_ID, 'ap_object', true ) ); $activitypub_activity = unserialize( get_comment_meta( $activitypub_comment->comment_ID, 'ap_object', true ) );
//will be forwarded to the parent_comment->author or post_author followers collection //will be forwarded to the parent_comment->author or post_author followers collection
//TODO verify that ... what?
$parent_comment = \get_comment( $activitypub_comment->comment_parent ); $parent_comment = \get_comment( $activitypub_comment->comment_parent );
if ( ! is_null( $parent_comment ) ) { if ( ! is_null( $parent_comment ) ) {
$user_id = $parent_comment->user_id; $user_id = $parent_comment->user_id;
@ -150,18 +147,16 @@ class Activity_Dispatcher {
$user_id = $original_post->post_author; $user_id = $original_post->post_author;
} }
//remove user_id from $activitypub_comment unset( $activitypub_activity['user_id'] ); // remove user_id from $activitypub_comment
unset( $activitypub_activity['user_id'] );
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $cc ) {
//Forward reply to followers, skip sender //Forward reply to followers, skip sender
if ( in_array( $to, $replyto ) || ( $replyto == $to ) ) { if ( in_array( $cc, $replyto ) ) {
continue; continue;
} }
$activitypub_activity['object']['to'] = $to; $activitypub_activity['object']['cc'] = $cc;
$activitypub_activity['to'] = $to; $activitypub_activity['cc'] = $cc;
$activity = \wp_json_encode( $activitypub_activity, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_QUOT ); $activity = \wp_json_encode( $activitypub_activity, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_QUOT );
\Activitypub\forward_remote_post( $inbox, $activity, $user_id ); \Activitypub\forward_remote_post( $inbox, $activity, $user_id );

View file

@ -42,7 +42,7 @@ class Activitypub {
* @return string The new path to the JSON template. * @return string The new path to the JSON template.
*/ */
public static function render_json_template( $template ) { public static function render_json_template( $template ) {
if ( ! \is_author() && ! \is_singular() && ! \is_home() && ! \Activitypub\is_ap_comment() ) { if ( ! \is_author() && ! \is_singular() && ! \is_home() && ! \Activitypub\is_ap_comment() && ! \Activitypub\is_ap_replies() ) {
return $template; return $template;
} }
@ -169,12 +169,11 @@ class Activitypub {
if ( $commentdata['comment_type'] === 'activitypub' ) { if ( $commentdata['comment_type'] === 'activitypub' ) {
if ( ( $comment_approved === 1 ) && if ( ( $comment_approved === 1 ) &&
! empty( $commentdata['user_id'] ) && ! empty( $commentdata['user_id'] ) &&
( $user = get_userdata( $commentdata['user_id'] ) ) && // get the user data ( $user = \get_userdata( $commentdata['user_id'] ) ) && // get the user data
in_array( 'administrator', $user->roles ) // check the roles \in_array( 'administrator', $user->roles ) // check the roles
) { ) {
// Only for Admins? // Only for Admins
$mentions = \get_comment_meta( $comment_id, 'mentions', true ); $mentions = \get_comment_meta( $comment_id, 'mentions', true );
//\ActivityPub\Activity_Dispatcher::send_comment_activity( $comment_id ); // performance > followers collection
\wp_schedule_single_event( \time(), 'activitypub_send_comment_activity', array( $comment_id ) ); \wp_schedule_single_event( \time(), 'activitypub_send_comment_activity', array( $comment_id ) );
} else { } else {
@ -193,7 +192,6 @@ class Activitypub {
* Fires immediately before comment status transition hooks are fired. (useful only for admin) * Fires immediately before comment status transition hooks are fired. (useful only for admin)
*/ */
public static function edit_comment( $comment_ID, $data ) { public static function edit_comment( $comment_ID, $data ) {
// advantage of ap_published is it would be set once, (does preprocess fire on edit?)
if ( ! is_null( $data['user_id'] ) ) { if ( ! is_null( $data['user_id'] ) ) {
\wp_schedule_single_event( \time(), 'activitypub_send_update_comment_activity', array( $comment_ID ) ); \wp_schedule_single_event( \time(), 'activitypub_send_update_comment_activity', array( $comment_ID ) );
} }

View file

@ -164,44 +164,31 @@ class Admin {
// Public Reply // Public Reply
$reply_button = '<button type="button" data-comment-id="%d" data-post-id="%d" data-action="%s" class="%s button-link" aria-expanded="false" aria-label="%s" data-recipients="%s" data-summary="%s">%s</button>'; $reply_button = '<button type="button" data-comment-id="%d" data-post-id="%d" data-action="%s" class="%s button-link" aria-expanded="false" aria-label="%s" data-recipients="%s" data-summary="%s">%s</button>';
$actions['reply'] = sprintf( $actions['reply'] = sprintf(
$reply_button, $reply_button,
$comment->comment_ID, $comment->comment_ID,
$comment->comment_post_ID, $comment->comment_post_ID,
'replyto', 'replyto',
'vim-r comment-inline', 'vim-r comment-inline',
esc_attr__( 'Reply to this comment' ), esc_attr__( 'Reply to this comment' ),
$recipients, $recipients,
$summary, $summary,
__( 'Reply', 'activitypub' ) __( 'Reply', 'activitypub' )
); );
// Private
// $actions['private_reply'] = sprintf(
// $format,
// $comment->comment_ID,
// $comment->comment_post_ID,
// 'private_replyto',
// 'vim-r comment-inline',
// esc_attr__( 'Reply in private to this comment' ),
// $recipients,
// $summary,
// __( 'Private reply', 'activitypub' )
// );
return $actions; return $actions;
} }
public static function scripts_reply_comments( $hook ) { public static function scripts_reply_comments( $hook ) {
if ('edit-comments.php' !== $hook) { if ( 'edit-comments.php' !== $hook ) {
return; return;
} }
wp_enqueue_script( 'activitypub_client', wp_enqueue_script(
plugin_dir_url(__FILE__) . '/activitypub.js', 'activitypub_client',
array('jquery'), plugin_dir_url( __FILE__ ) . '/activitypub.js',
filemtime( plugin_dir_path(__FILE__) . '/activitypub.js' ), array( 'jquery' ),
true filemtime( plugin_dir_path( __FILE__ ) . '/activitypub.js' ),
true
); );
} }
} }

View file

@ -251,6 +251,12 @@ function get_follower_inboxes( $user_id ) {
return $inboxes; return $inboxes;
} }
/**
*
* @param $mentions array of mentioned actors, each mention is an array of actor URI (href), and webfinger (name)
*
* @return array of (shared) inboxes
*/
function get_mentioned_inboxes( $mentions ) { function get_mentioned_inboxes( $mentions ) {
$inboxes = array(); $inboxes = array();
@ -259,7 +265,7 @@ function get_mentioned_inboxes( $mentions ) {
if ( ! $inbox || \is_wp_error( $inbox ) ) { if ( ! $inbox || \is_wp_error( $inbox ) ) {
continue; continue;
} }
// init array if empty
if ( ! isset( $inboxes[ $inbox ] ) ) { if ( ! isset( $inboxes[ $inbox ] ) ) {
$inboxes[ $inbox ] = array(); $inboxes[ $inbox ] = array();
} }
@ -355,7 +361,10 @@ function url_to_authorid( $url ) {
/** /**
* Verify if in_replyto_url is a local comment, * Verify if in_replyto_url is a local comment,
* Or if it is a previously received remote comment * Or if it is a previously received remote comment
* return int comment_id * (For threading comments locally)
*
* @param string activitypub object id URI
* @return int comment_id
*/ */
function url_to_commentid( $in_replyto_url ) { function url_to_commentid( $in_replyto_url ) {
if ( empty( $in_replyto_url ) ) { if ( empty( $in_replyto_url ) ) {
@ -364,16 +373,12 @@ function url_to_commentid( $in_replyto_url ) {
//rewrite for activitypub object id simplification //rewrite for activitypub object id simplification
$url_maybe_id = \wp_parse_url( $in_replyto_url ); $url_maybe_id = \wp_parse_url( $in_replyto_url );
if ( site_url() === $url_maybe_id['scheme'] . '://' . $url_maybe_id['host'] && !empty( $url_maybe_id['query'] )) {
if ( site_url() === $url_maybe_id['scheme'] . '://' . $url_maybe_id['host'] ) {
//is local post or comment //is local post or comment
\parse_str( $url_maybe_id['query'], $reply_query ); \parse_str( $url_maybe_id['query'], $reply_query );
if ( isset( $reply_query['ap_comment_id'] ) && is_int( $reply_query['ap_comment_id'] ) ) { if ( isset( $reply_query['ap_comment_id'] ) ) {
//is local comment //is local comment
return $reply_query['ap_comment_id']; return $reply_query['ap_comment_id'];
} else {
//not a comment
return null;
} }
} else { } else {
//is remote url //is remote url
@ -396,14 +401,14 @@ function url_to_commentid( $in_replyto_url ) {
} }
return $found_comment_ids[0]; return $found_comment_ids[0];
} }
return null;
} }
return null;
} }
/** /**
* Verify if url is a wp_ap_comment, * Verify if url is a wp_ap_comment,
* Or if it is a previously received remote comment * Or if it is a previously received remote comment
* return int comment_id * @return int comment_id
*/ */
function is_ap_comment() { function is_ap_comment() {
$comment_id = get_query_var( 'ap_comment_id', null ); $comment_id = get_query_var( 'ap_comment_id', null );
@ -418,11 +423,10 @@ function is_ap_comment() {
} }
/** /**
* Verify if url is a /replies endoint, * Verify if url has a replies query,
* return int true * @return bool
*/ */
function is_ap_replies() { function is_ap_replies() {
global $wp;
$replies = get_query_var( 'replies' ); $replies = get_query_var( 'replies' );
if ( $replies ) { if ( $replies ) {
return $replies; return $replies;
@ -476,8 +480,10 @@ function get_summary( $comment_id ) {
} }
/** /**
* parse content for tags to transform * Parse content for tags to transform
*
* @param string $content to search * @param string $content to search
* @return array content, mentions (for storage in post_meta)
*/ */
function transform_tags( $content ) { function transform_tags( $content ) {
//#tags //#tags
@ -544,21 +550,8 @@ function url_to_webfinger( $user_url ) {
} }
/** /**
* Transform comment url, replace #fragment with ?query * @param $comment or $comment_id
* * @return ActivityPub URI of comment
* AP Object ID must be unique
*
* https://www.w3.org/TR/activitypub/#obj-id
* https://github.com/tootsuite/mastodon/issues/13879
*/
function normalize_comment_url( $comment ) {
$comment_id = explode( '#comment-', \get_comment_link( $comment ) );
$comment_id = $comment_id[0] . '?ap_comment_id=' . $comment_id[1];
return $comment_id;
}
/**
* Set ap_comment_id
* *
* AP Object ID must be unique * AP Object ID must be unique
* *
@ -566,20 +559,16 @@ function normalize_comment_url( $comment ) {
* https://github.com/tootsuite/mastodon/issues/13879 * https://github.com/tootsuite/mastodon/issues/13879
*/ */
function set_ap_comment_id( $comment ) { function set_ap_comment_id( $comment ) {
$comment = \get_comment( $comment );
$ap_comment_id = add_query_arg( $ap_comment_id = add_query_arg(
array( array(
'p' => $comment->comment_post_ID, 'p' => $comment->comment_post_ID,
'ap_comment_id' => $comment->comment_ID, //should probably rename to ap_comment or something 'ap_comment_id' => $comment->comment_ID,
), ),
trailingslashit( site_url() ) trailingslashit( site_url() )
); );
return $ap_comment_id; return $ap_comment_id;
} }
/* comment_id_to_url( $comment_id ) {
//get remote from post_id from comment meta
//get local normalized comment_link
}
*/
/** /**
* Determine AP audience of incoming object * Determine AP audience of incoming object
@ -591,7 +580,7 @@ function get_audience( $object ) {
return 'public'; return 'public';
} }
if ( in_array( AS_PUBLIC, $object['cc'] ) ) { if ( in_array( AS_PUBLIC, $object['cc'] ) ) {
return 'unlisted';//is unlisted even relevant? return 'unlisted';
} }
if ( ! in_array( AS_PUBLIC, $object['to'] ) && ! in_array( AS_PUBLIC, $object['cc'] ) ) { if ( ! in_array( AS_PUBLIC, $object['to'] ) && ! in_array( AS_PUBLIC, $object['cc'] ) ) {
$author_post_url = get_author_posts_url( $object['user_id'] ); $author_post_url = get_author_posts_url( $object['user_id'] );

View file

@ -23,7 +23,7 @@ class Comment {
$this->contentWarning = $this->generate_content_warning(); $this->contentWarning = $this->generate_content_warning();
$this->permalink = $this->generate_permalink(); $this->permalink = $this->generate_permalink();
$this->context = $this->generate_context(); $this->context = $this->generate_context();
$this->cc_recipients = $this->generate_recipients(); $this->to_recipients = $this->generate_mention_recipients();
$this->tags = $this->generate_tags(); $this->tags = $this->generate_tags();
$this->update = $this->generate_update(); $this->update = $this->generate_update();
$this->deleted = $this->generate_trash(); $this->deleted = $this->generate_trash();
@ -59,9 +59,10 @@ class Comment {
'context' => $this->context, 'context' => $this->context,
//'source' => \get_comment_link( $comment ), //non-conforming, see https://www.w3.org/TR/activitypub/#source-property //'source' => \get_comment_link( $comment ), //non-conforming, see https://www.w3.org/TR/activitypub/#source-property
'url' => \get_comment_link( $comment ), //link for mastodon 'url' => \get_comment_link( $comment ), //link for mastodon
'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ), //audience logic 'to' => $this->to_recipients,
'cc' => $this->cc_recipients, 'cc' => array( 'https://www.w3.org/ns/activitystreams#Public' ),
'tag' => $this->tags, 'tag' => $this->tags,
'replies' => $this->replies,
); );
if ( $this->replies ) { if ( $this->replies ) {
$array['replies'] = $this->replies; $array['replies'] = $this->replies;
@ -85,7 +86,7 @@ class Comment {
} }
public function generate_comment_id() { public function generate_comment_id() {
return \Activitypub\set_ap_comment_id( $this->comment ); return \Activitypub\set_ap_comment_id( $this->comment->comment_ID );
} }
public function generate_permalink() { public function generate_permalink() {
@ -96,7 +97,7 @@ class Comment {
} }
/** /**
* What is status is being replied to * What is status being replied to
* Comment ID or Post ID * Comment ID or Post ID
*/ */
public function generate_parent_url() { public function generate_parent_url() {
@ -114,31 +115,43 @@ class Comment {
trailingslashit( site_url() ) trailingslashit( site_url() )
); );
} }
} else { } else { //parent is_post
$inReplyTo = add_query_arg( // Backwards compatibility
array( $pretty_permalink = \get_post_meta( $comment->comment_post_ID, '_activitypub_permalink_compat', true ); // TODO finalize meta
'p' => $comment->comment_post_ID, if ( $pretty_permalink ) {
), $inReplyTo = $pretty_permalink;
trailingslashit( site_url() ) } else {
); $inReplyTo = add_query_arg(
array(
'p' => $comment->comment_post_ID,
),
trailingslashit( site_url() )
);
}
} }
return $inReplyTo; return $inReplyTo;
} }
public function generate_context() { public function generate_context() {
$comment = $this->comment; $comment = $this->comment;
$inReplyTo = add_query_arg( // support pretty_permalinks
array( $pretty_permalink = \get_post_meta( $comment->comment_post_ID, '_activitypub_permalink_compat', true );
'p' => $comment->comment_post_ID, if ( $pretty_permalink ) {
), $context = $pretty_permalink;
trailingslashit( site_url() ) } else {
); $context = add_query_arg(
return $inReplyTo; array(
'p' => $comment->comment_post_ID,
),
trailingslashit( site_url() )
);
}
return $context;
} }
/** /**
* Generate courtesy Content Warning * Generate courtesy Content Warning
* If peer used CW let's just copy it * If parent status used CW let's just copy it
* TODO: Move to preprocess_comment / row_actions * TODO: Move to preprocess_comment / row_actions
* Add option for wrapping CW in Details/Summary markup * Add option for wrapping CW in Details/Summary markup
* Figure out some CW syntax: [shortcode-style], {brackets-style}? * Figure out some CW syntax: [shortcode-style], {brackets-style}?
@ -148,7 +161,7 @@ class Comment {
$comment = $this->comment; $comment = $this->comment;
$contentWarning = null; $contentWarning = null;
// TODO Replace auto CW, with Title field or CW shortcode // Temporarily generate Summary from parent
$parent_comment = \get_comment( $comment->comment_parent ); $parent_comment = \get_comment( $comment->comment_parent );
if ( $parent_comment ) { if ( $parent_comment ) {
//get (received) comment //get (received) comment
@ -157,9 +170,10 @@ class Comment {
$contentWarning = $ap_object['object']['summary']; $contentWarning = $ap_object['object']['summary'];
} }
} }
// TODO Replace auto generate with Summary shortcode
/*summary = \get_comment_meta( $this->comment->comment_ID, 'summary', true ) ; /*summary = \get_comment_meta( $this->comment->comment_ID, 'summary', true ) ;
if ( !empty( $summary ) ) { if ( !empty( $summary ) ) {
$contentWarning = \Activitypub\add_summary( $summary ); //TODO $contentWarning = \Activitypub\add_summary( $summary );
} */ } */
return $contentWarning; return $contentWarning;
} }
@ -167,9 +181,7 @@ class Comment {
/** /**
* Who is being replied to * Who is being replied to
*/ */
public function generate_recipients() { public function generate_mention_recipients() {
//TODO Add audience logic get parent audience
//TODO shouldn't mentions go in 'to'?
$recipients = array( AS_PUBLIC ); $recipients = array( AS_PUBLIC );
$mentions = \get_comment_meta( $this->comment->comment_ID, 'mentions', true ); $mentions = \get_comment_meta( $this->comment->comment_ID, 'mentions', true );
if ( ! empty( $mentions ) ) { if ( ! empty( $mentions ) ) {
@ -226,40 +238,41 @@ class Comment {
*/ */
public function generate_replies() { public function generate_replies() {
$comment = $this->comment; $comment = $this->comment;
$replies = [];
$args = array( $args = array(
'post_id' => $comment->comment_post_ID, 'post_id' => $comment->comment_post_ID,
'parent' => $comment->comment_ID, 'parent' => $comment->comment_ID,
'author__in' => $comment->user_id,
'status' => 'approve', 'status' => 'approve',
'hierarchical' => false, 'hierarchical' => false,
); );
$children = \get_comments( $args ); $comments_list = \get_comments( $args );
$replies = null;
if ( $children ) { if ( $comments_list ) {
$items = array(); $items = array();
foreach ( $children as $child_comment ) { foreach ( $comments_list as $comment ) {
$comment_url = \add_query_arg( // remote replies
array( $source_url = \get_comment_meta( $comment->comment_ID, 'source_url', true );
'p' => $child_comment->comment_post_ID, if ( ! empty( $source_url ) ){
'ap_comment_id' => $child_comment->comment_ID, $items[] = $source_url;
), } else {
trailingslashit( site_url() ) // local replies
); $comment_url = \add_query_arg( //
$items[] = $comment_url; array(
'p' => $comment->comment_post_ID,
'ap_comment_id' => $comment->comment_ID,
),
trailingslashit( site_url() )
);
$items[] = $comment_url;
}
} }
$replies = (object) array( $replies = (object) array(
'type' => 'Collection', 'type' => 'Collection',
'id' => \add_query_arg( array( 'replies' => '' ), $this->id ), 'id' => \add_query_arg( array( 'replies' => '' ), $this->id ),
'first' => (object) array( 'first' => (object) array(
'type' => 'CollectionPage', 'type' => 'CollectionPage',
'partOf' => \add_query_arg( array( 'replies' => '' ), $this->id ), 'partOf' => \add_query_arg( array( 'replies' => '' ), $this->id ),
'next' => \add_query_arg(
array(
'replies' => '',
'page' => 1,
),
$this->id
),
'items' => $items, 'items' => $items,
), ),
); );

View file

@ -30,9 +30,7 @@ class Post {
$this->tags = $this->generate_tags(); $this->tags = $this->generate_tags();
$this->object_type = $this->generate_object_type(); $this->object_type = $this->generate_object_type();
$this->replies = $this->generate_replies(); $this->replies = $this->generate_replies();
//$this->updated = $this->generate_updated(); $this->updated = $this->generate_updated();
$this->slug = \get_permalink( $this->id );
$this->updated = null;
$this->delete = $this->get_deleted(); $this->delete = $this->get_deleted();
} }
@ -73,9 +71,10 @@ class Post {
} }
if ( $this->deleted ) { if ( $this->deleted ) {
$array['deleted'] = \date( 'Y-m-d\TH:i:s\Z', \strtotime( $post->post_modified_gmt ) ); $array['deleted'] = \date( 'Y-m-d\TH:i:s\Z', \strtotime( $post->post_modified_gmt ) );
// TODO if using slugs instead of ids _ap_deleted_slug $deleted_post_slug = \get_post_meta( $post->ID, '_activitypub_permalink_compat', true );
//$deleted_post_slug = \get_post_meta( $post->ID, '_ap_deleted_slug', true ); if ( $deleted_post_slug ) {
//$array['id'] = $deleted_post_slug; $array['id'] = $deleted_post_slug;
}
} }
if ( $this->updated ) { if ( $this->updated ) {
$array['updated'] = \date( 'Y-m-d\TH:i:s\Z', \strtotime( $post->post_modified_gmt ) ); $array['updated'] = \date( 'Y-m-d\TH:i:s\Z', \strtotime( $post->post_modified_gmt ) );
@ -89,15 +88,19 @@ class Post {
public function generate_id() { public function generate_id() {
$post = $this->post; $post = $this->post;
$permalink = \add_query_arg( // $pretty_permalink = \get_post_meta( $post->ID, '_activitypub_permalink_compat', true );
array(
'p' => $post->ID,
),
trailingslashit( site_url() )
);
// replace 'trashed' for delete activity if( ! empty( $pretty_permalink ) ) {
return \str_replace( '__trashed', '', $permalink ); $object_id = $pretty_permalink;
} else {
$object_id = \add_query_arg( //
array(
'p' => $post->ID,
),
trailingslashit( site_url() )
);
}
return $object_id;
} }
public function generate_attachments() { public function generate_attachments() {
@ -180,10 +183,9 @@ class Post {
public function generate_replies() { public function generate_replies() {
$replies = null; $replies = null;
//\error_log( 'generate_replies: $post' . print_r( $this->post, true ) );
if ( $this->post->comment_count > 0 ) { if ( $this->post->comment_count > 0 ) {
$args = array( $args = array(
'post_id' => $this->post->ID, // Use post_id, not post_ID 'post_id' => $this->post->ID,
'hierarchical' => false, 'hierarchical' => false,
'status' => 'approve', 'status' => 'approve',
); );
@ -193,7 +195,6 @@ class Post {
foreach ( $comments as $comment ) { foreach ( $comments as $comment ) {
// include self replies // include self replies
if ( $this->post->post_author === $comment->user_id ) { if ( $this->post->post_author === $comment->user_id ) {
//$comment_url = $comment->comment_ID;
$comment_url = \add_query_arg( // $comment_url = \add_query_arg( //
array( array(
'p' => $this->post->ID, 'p' => $this->post->ID,
@ -201,24 +202,23 @@ class Post {
), ),
trailingslashit( site_url() ) trailingslashit( site_url() )
); );
//\error_log( 'generate_replies: $comment' . print_r( $comment, true ) );
$items[] = $comment_url; $items[] = $comment_url;
} else {
$ap_object = \unserialize(\get_comment_meta( $comment->comment_ID, 'ap_object', true ));
$comment_url = \get_comment_meta( $comment->comment_ID, 'source_url', true );
if ( ! empty( $comment_url ) ){
$items[] = \get_comment_meta( $comment->comment_ID, 'source_url', true );
}
} }
} }
//\error_log( 'generate_replies: $comments' . print_r( $comments, true ) );
$replies = (object) array( $replies = (object) array(
'type' => 'Collection', 'type' => 'Collection',
'id' => \add_query_arg( array( 'replies' => '' ), $this->id ), 'id' => \add_query_arg( array( 'replies' => '' ), $this->id ),
'first' => (object) array( 'first' => (object) array(
'type' => 'CollectionPage', 'type' => 'CollectionPage',
'partOf' => \add_query_arg( array( 'replies' => '' ), $this->id ), 'partOf' => \add_query_arg( array( 'replies' => '' ), $this->id ),
'next' => \add_query_arg(
array(
'replies' => '',
'page' => 1,
),
$this->id
),
'items' => $items, 'items' => $items,
), ),
); );

View file

@ -427,13 +427,6 @@ class Inbox {
} }
} }
//not all implementaions use url
if ( isset( $object['object']['id'] ) ) {
$source_url = \esc_url_raw( $object['object']['id'] );
} else {
$source_url = \esc_url_raw( $object['object']['url'] );
}
// if no name is set use peer username // if no name is set use peer username
if ( ! empty( $meta['name'] ) ) { if ( ! empty( $meta['name'] ) ) {
$name = \esc_attr( $meta['name'] ); $name = \esc_attr( $meta['name'] );
@ -446,10 +439,10 @@ class Inbox {
} }
//Only create WP_Comment for public replies to local posts //Only create WP_Comment for public replies to local posts
if ( ( in_array( AS_PUBLIC, $object['to'] ) if ( ( 'public' === $audience )
|| in_array( AS_PUBLIC, $object['cc'] ) ) || ( 'unlisted' === $audience )
&& ( ! empty( $comment_post_ID ) && ( ! empty( $comment_post_ID )
|| ! empty( $comment_parent ) || ! empty( $comment_parent_ID )
) ) { ) ) {
$commentdata = array( $commentdata = array(
@ -462,7 +455,7 @@ class Inbox {
'comment_parent' => $comment_parent_ID, 'comment_parent' => $comment_parent_ID,
'comment_meta' => array( 'comment_meta' => array(
'ap_object' => \serialize( $object ), 'ap_object' => \serialize( $object ),
'source_url' => $source_url, 'source_url' => \esc_url_raw( $object['object']['id'] ),
'avatar_url' => $avatar_url, 'avatar_url' => $avatar_url,
'protocol' => 'activitypub', 'protocol' => 'activitypub',
), ),

View file

@ -126,6 +126,7 @@ class Webfinger {
* WebFinger Lookup to find user uri * WebFinger Lookup to find user uri
* *
* @param string $resource the WebFinger resource * @param string $resource the WebFinger resource
* @return array ['href', 'name']. ex: href=https://domain.tld/user/webfinger, name=webfinger@domain.tld
*/ */
public static function webfinger_lookup( $webfinger ) { public static function webfinger_lookup( $webfinger ) {
$activity_profile = null; $activity_profile = null;

View file

@ -1,5 +1,5 @@
<?php <?php
$comment = \get_comment( get_query_var('ap_comment_id') ); $comment = \get_comment( \get_query_var( 'ap_comment_id' ) );
$activitypub_comment = new \Activitypub\Model\Comment( $comment ); $activitypub_comment = new \Activitypub\Model\Comment( $comment );
$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $activitypub_comment->to_array() ); $json = \array_merge( array( '@context' => \Activitypub\get_context() ), $activitypub_comment->to_array() );

View file

@ -0,0 +1,84 @@
<?php
$post = \get_post();
$page = \get_query_var( 'page' );
$args = array(
'status' => 'approve',
'number' => '10',
'offset' => $page,
'type' => 'activitypub',
'post_id' => $post->ID,
'order' => 'ASC',
);
$comments = get_comments( $args );
$replies_request = \add_query_arg( $_SERVER['QUERY_STRING'], '', $post->guid );
$collection_id = \remove_query_arg(['page', 'ap_comment_id'], $replies_request );
$json = new \stdClass();
$json->{'@context'} = 'https://www.w3.org/ns/activitystreams';
$json->id = $collection_id;
$collectionPage = new \stdClass();
$collectionPage->type = 'CollectionPage';
$collectionPage->partOf = $collection_id;
$collectionPage->totalItems = \count( $comments ); // phpcs:ignore
if ( $page && ( ( \ceil ( $collectionPage->totalItems / 10 ) ) > $page ) ) { // phpcs:ignore
$collectionPage->first = \add_query_arg( 'page', 1, $collectionPage->partOf ); // phpcs:ignore TODO
$collectionPage->next = \add_query_arg( 'page', $page + 1, $collectionPage->partOf ); // phpcs:ignore
$collectionPage->last = \add_query_arg( 'page', \ceil ( $collectionPage->totalItems / 10 ), $collectionPage->partOf ); // phpcs:ignore
}
foreach ( $comments as $comment ) {
$remote_url = \get_comment_meta( $comment->comment_ID, 'source_url', true );
if ( $remote_url ) { //
$collectionPage->items[] = $remote_url;
} else {
$activitypub_comment = new \Activitypub\Model\Comment( $comment );
$activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_NONE );
$activitypub_activity->from_post( $activitypub_comment->to_array() );
$collectionPage->items[] = $activitypub_activity->to_array(); // phpcs:ignore
}
}
if ( ! \get_query_var( 'collection_page' ) ) {
$json->type = 'Collection';
//if +10, embed first
$json->first = $collectionPage;
} else {
$json = $collectionPage;
}
// filter output
$json = \apply_filters( 'activitypub_json_replies_array', $json );
/*
* Action triggerd prior to the ActivityPub profile being created and sent to the client
*/
\do_action( 'activitypub_json_replies_pre' );
$options = 0;
// JSON_PRETTY_PRINT added in PHP 5.4
if ( \get_query_var( 'pretty' ) ) {
$options |= \JSON_PRETTY_PRINT; // phpcs:ignore
}
$options |= \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT;
/*
* Options to be passed to json_encode()
*
* @param int $options The current options flags
*/
$options = \apply_filters( 'activitypub_json_replies_options', $options );
\header( 'Content-Type: application/activity+json' );
echo \wp_json_encode( $json, $options );
/*
* Action triggerd after the ActivityPub profile has been created and sent to the client
*/
\do_action( 'activitypub_json_replies_comment' );