Comments update
This commit is contained in:
parent
163f2ed013
commit
8938c67073
8 changed files with 546 additions and 140 deletions
|
@ -18,6 +18,7 @@ class Activity_Dispatcher {
|
||||||
\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' ) );
|
||||||
|
|
||||||
\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_inbox_forward_activity', array( '\Activitypub\Activity_Dispatcher', 'inbox_forward_activity' ) );
|
\add_action( 'activitypub_inbox_forward_activity', array( '\Activitypub\Activity_Dispatcher', 'inbox_forward_activity' ) );
|
||||||
\add_action( 'activitypub_send_delete_comment_activity', array( '\Activitypub\Activity_Dispatcher', 'send_delete_comment_activity' ) );
|
\add_action( 'activitypub_send_delete_comment_activity', array( '\Activitypub\Activity_Dispatcher', 'send_delete_comment_activity' ) );
|
||||||
}
|
}
|
||||||
|
@ -48,8 +49,14 @@ 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() ) );
|
||||||
|
|
||||||
$activitypub_activity = new \Activitypub\Model\Activity( 'Update', \Activitypub\Model\Activity::TYPE_FULL );
|
$activitypub_activity = new \Activitypub\Model\Activity( 'Update', \Activitypub\Model\Activity::TYPE_FULL );
|
||||||
$activitypub_activity->from_post( $activitypub_post->to_array() );
|
$activitypub_activity->from_post( $activitypub_post->to_array() );
|
||||||
|
@ -70,14 +77,17 @@ class Activity_Dispatcher {
|
||||||
public static function send_delete_activity( $activitypub_post ) {
|
public static function send_delete_activity( $activitypub_post ) {
|
||||||
// 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 );
|
||||||
|
$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 );
|
||||||
$activitypub_activity->from_post( $activitypub_post->to_array() );
|
$activitypub_activity->from_post( $activitypub_post->to_array() );
|
||||||
|
$activitypub_activity->set_deleted( $deleted );
|
||||||
|
|
||||||
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
||||||
$activitypub_activity->set_to( $to );
|
$activitypub_activity->set_to( $to );
|
||||||
$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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,43 +101,31 @@ 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 );//
|
$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 );//
|
$mentions = get_comment_meta( $activitypub_comment_id, 'mentions', true );//might be tagged
|
||||||
//error_log( 'dispatcher:send_comment:$activitypub_comment: ' . print_r( $activitypub_comment, true ) );
|
|
||||||
|
|
||||||
$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() );
|
||||||
|
|
||||||
\error_log( 'Activity_Dispatcher::send_comment_activity: ' . print_r($activitypub_activity, true));
|
foreach ( \Activitypub\get_mentioned_inboxes( $mentions ) as $inbox => $to ) {
|
||||||
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
$activitypub_activity->set_to( $to );//all users at shared inbox
|
||||||
\error_log( '$user_id: ' . $user_id . ', $inbox: '. $inbox . ', $to: '. print_r($to, true ) );
|
$activity = $activitypub_activity->to_json(); // phpcs:ignore
|
||||||
$activitypub_activity->set_to( $to[0] );
|
|
||||||
|
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
|
||||||
|
}
|
||||||
|
//will this reset the activities?
|
||||||
|
|
||||||
|
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $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 replying to followers (avoid duplicate replies)
|
||||||
// if( in_array( $to, $replyto ) || ( $replyto == $to ) ) {
|
if( in_array( $cc, $replyto ) || in_array( $cc, $mentions ) ) {
|
||||||
// break;
|
continue;
|
||||||
// }
|
}
|
||||||
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
|
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
|
||||||
}
|
}
|
||||||
// TODO: Reply (to followers and non-followers)
|
|
||||||
// if( is_array( $replyto ) && count( $replyto ) > 1 ) {
|
|
||||||
// foreach ( $replyto as $to ) {
|
|
||||||
// $inbox = \Activitypub\get_inbox_by_actor( $to );
|
|
||||||
// $activitypub_activity->set_to( $to );
|
|
||||||
// $activity = $activitypub_activity->to_json(); // phpcs:ignore
|
|
||||||
// error_log( 'dispatches->replyto: ' . $to );
|
|
||||||
// \Activitypub\safe_remote_post( $inbox, $activity, $user_id );
|
|
||||||
// }
|
|
||||||
// } elseif ( !is_array( $replyto ) ) {
|
|
||||||
// $inbox = \Activitypub\get_inbox_by_actor( $to );
|
|
||||||
// $activitypub_activity->set_to( $replyto );
|
|
||||||
// $activity = $activitypub_activity->to_json(); // phpcs:ignore
|
|
||||||
// error_log( 'dispatch->replyto: ' . $replyto );
|
|
||||||
// \Activitypub\safe_remote_post( $inbox, $activity, $user_id );
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -136,7 +134,6 @@ class Activity_Dispatcher {
|
||||||
* @param \Activitypub\Model\Comment $activitypub_comment
|
* @param \Activitypub\Model\Comment $activitypub_comment
|
||||||
*/
|
*/
|
||||||
public static function inbox_forward_activity( $activitypub_comment_id ) {
|
public static function inbox_forward_activity( $activitypub_comment_id ) {
|
||||||
//\error_log( 'Activity_Dispatcher::inbox_forward_activity' . print_r( $activitypub_comment, true ) );
|
|
||||||
$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 ther own post
|
||||||
|
@ -157,28 +154,17 @@ class Activity_Dispatcher {
|
||||||
unset($activitypub_activity['user_id']);
|
unset($activitypub_activity['user_id']);
|
||||||
|
|
||||||
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
||||||
\error_log( '$user_id: ' . $user_id . ', $inbox: '. $inbox . ', $to: '. print_r($to, true ) );
|
|
||||||
|
|
||||||
//Forward reply to followers, skip sender
|
//Forward reply to followers, skip sender
|
||||||
if( in_array( $to, $replyto ) || ( $replyto == $to ) ) {
|
if( in_array( $to, $replyto ) || ( $replyto == $to ) ) {
|
||||||
error_log( 'dispatch:forward: nope:' . print_r( $to, true ) );
|
continue;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$activitypub_activity['object']['to'] = $to;
|
$activitypub_activity['object']['to'] = $to;
|
||||||
$activitypub_activity['to'] = $to;
|
$activitypub_activity['to'] = $to;
|
||||||
|
|
||||||
//$activitypub_activity
|
|
||||||
//$activitypub_activity->set_to( $to );
|
|
||||||
//$activity = $activitypub_activity->to_json(); // phpcs:ignore
|
|
||||||
|
|
||||||
$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 );
|
||||||
error_log( 'dispatch:forward:activity:' . print_r( $activity, true ) );
|
|
||||||
\Activitypub\forward_remote_post( $inbox, $activity, $user_id );
|
\Activitypub\forward_remote_post( $inbox, $activity, $user_id );
|
||||||
|
|
||||||
//reset //unnecessary
|
|
||||||
//array_pop( $activitypub_activity->object->to[] );
|
|
||||||
//array_pop( $activitypub_activity->to[] );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,16 +176,23 @@ class Activity_Dispatcher {
|
||||||
public static function send_delete_comment_activity( $activitypub_comment_id ) {
|
public static function send_delete_comment_activity( $activitypub_comment_id ) {
|
||||||
// get comment
|
// get comment
|
||||||
$activitypub_comment = \get_comment( $activitypub_comment_id );
|
$activitypub_comment = \get_comment( $activitypub_comment_id );
|
||||||
$user_id = $activitypub_comment->post_author;
|
$user_id = $activitypub_comment->user_id;
|
||||||
|
// Prevent sending received/anonymous comments
|
||||||
|
if ( ! $user_id ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleted = \wp_date( 'Y-m-d\TH:i:s\Z', \strtotime( $activitypub_comment->comment_date_gmt ) );
|
||||||
|
|
||||||
$activitypub_comment = new \Activitypub\Model\Comment( $activitypub_comment );
|
$activitypub_comment = new \Activitypub\Model\Comment( $activitypub_comment );
|
||||||
|
$activitypub_comment->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 );
|
||||||
$activitypub_activity->from_comment( $activitypub_comment->to_array() );
|
$activitypub_activity->from_comment( $activitypub_comment->to_array() );
|
||||||
|
$activitypub_activity->set_deleted( $deleted );
|
||||||
|
|
||||||
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
||||||
$activitypub_activity->set_to( $to );
|
$activitypub_activity->set_to( $to );
|
||||||
$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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,10 @@ class Activitypub {
|
||||||
|
|
||||||
\add_filter( 'preprocess_comment' , array( '\Activitypub\Activitypub', 'preprocess_comment' ) );
|
\add_filter( 'preprocess_comment' , array( '\Activitypub\Activitypub', 'preprocess_comment' ) );
|
||||||
\add_filter( 'comment_post' , array( '\Activitypub\Activitypub', 'postprocess_comment' ), 10, 3 );
|
\add_filter( 'comment_post' , array( '\Activitypub\Activitypub', 'postprocess_comment' ), 10, 3 );
|
||||||
|
\add_filter( 'wp_update_comment_data', array( '\Activitypub\Activitypub', 'comment_updated_published' ), 20, 3 );
|
||||||
\add_action( 'transition_comment_status', array( '\Activitypub\Activitypub', 'schedule_comment_activity' ), 20, 3 );
|
\add_action( 'transition_comment_status', array( '\Activitypub\Activitypub', 'schedule_comment_activity' ), 20, 3 );
|
||||||
|
\add_action( 'edit_comment', array( '\Activitypub\Activitypub', 'edit_comment' ), 20, 2 );//schedule_admin_comment_activity
|
||||||
|
\add_filter( 'get_comment_text', array( '\Activitypub\Activitypub', 'comment_append_edit_datetime' ), 10, 3 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,12 +42,16 @@ 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() ) {
|
if ( ! \is_author() && ! \is_singular() && ! \is_home() && ! \Activitypub\is_ap_comment() ) {
|
||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( \is_author() ) {
|
if ( \is_author() ) {
|
||||||
$json_template = \dirname( __FILE__ ) . '/../templates/author-json.php';
|
$json_template = \dirname( __FILE__ ) . '/../templates/author-json.php';
|
||||||
|
} elseif ( \Activitypub\is_ap_replies() ) {
|
||||||
|
$json_template = \dirname( __FILE__ ) . '/../templates/replies-json.php';
|
||||||
|
} elseif ( \Activitypub\is_ap_comment() ) {
|
||||||
|
$json_template = \dirname( __FILE__ ) . '/../templates/comment-json.php';
|
||||||
} elseif ( \is_singular() ) {
|
} elseif ( \is_singular() ) {
|
||||||
$json_template = \dirname( __FILE__ ) . '/../templates/post-json.php';
|
$json_template = \dirname( __FILE__ ) . '/../templates/post-json.php';
|
||||||
} elseif ( \is_home() ) {
|
} elseif ( \is_home() ) {
|
||||||
|
@ -90,6 +97,12 @@ class Activitypub {
|
||||||
*/
|
*/
|
||||||
public static function add_query_vars( $vars ) {
|
public static function add_query_vars( $vars ) {
|
||||||
$vars[] = 'activitypub';
|
$vars[] = 'activitypub';
|
||||||
|
$vars[] = 'ap_comment_id';//comment_id doesn't work, 'c' is probably too short and prone to collisions
|
||||||
|
|
||||||
|
//Collections review
|
||||||
|
$vars[] = 'replies';
|
||||||
|
$vars[] = 'collection_page';
|
||||||
|
$vars[] = 'only_other_accounts';
|
||||||
|
|
||||||
return $vars;
|
return $vars;
|
||||||
}
|
}
|
||||||
|
@ -124,44 +137,35 @@ class Activitypub {
|
||||||
|
|
||||||
if ( 'publish' === $new_status && 'publish' !== $old_status ) {
|
if ( 'publish' === $new_status && 'publish' !== $old_status ) {
|
||||||
\wp_schedule_single_event( \time(), 'activitypub_send_post_activity', array( $activitypub_post ) );
|
\wp_schedule_single_event( \time(), 'activitypub_send_post_activity', array( $activitypub_post ) );
|
||||||
} elseif ( 'publish' === $new_status ) {
|
} elseif ( 'publish' === $new_status ) { //this triggers when restored post from trash, which may not be desired
|
||||||
\wp_schedule_single_event( \time(), 'activitypub_send_update_activity', array( $activitypub_post ) );
|
\wp_schedule_single_event( \time(), 'activitypub_send_update_activity', array( $activitypub_post ) );
|
||||||
} elseif ( 'trash' === $new_status ) {
|
} elseif ( 'trash' === $new_status ) {
|
||||||
\wp_schedule_single_event( \time(), 'activitypub_send_delete_activity', array( $activitypub_post ) );
|
\wp_schedule_single_event( \time(), 'activitypub_send_delete_activity', array( $activitypub_post ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* preprocess local comments for federated replies
|
* preprocess local comments for federated replies
|
||||||
*/
|
*/
|
||||||
public static function preprocess_comment( $commentdata ) {
|
public static function preprocess_comment( $commentdata ) {
|
||||||
|
// only process replies from local actors
|
||||||
//must only process replies from local actors
|
|
||||||
if ( !empty( $commentdata['user_id'] ) ) {
|
if ( !empty( $commentdata['user_id'] ) ) {
|
||||||
//\error_log( 'is_local user' );//TODO Test
|
$commentdata['comment_type'] = 'activitypub';
|
||||||
//TODO TEST
|
// transform webfinger mentions to links and add @mentions to cc
|
||||||
$post_type = \get_object_subtype( 'post', $commentdata['comment_post_ID'] );
|
$tagged_content = \Activitypub\transform_tags( $commentdata['comment_content'] );
|
||||||
$ap_post_types = \get_option( 'activitypub_support_post_types' );
|
$commentdata['comment_content'] = $tagged_content['content'];
|
||||||
if ( !\is_null( $ap_post_types ) ) {
|
$commentdata['comment_meta']['mentions'] = $tagged_content['mentions'];
|
||||||
if ( in_array( $post_type, $ap_post_types ) ) {
|
|
||||||
$commentdata['comment_type'] = 'activitypub';
|
|
||||||
// transform webfinger mentions to links and add @mentions to cc
|
|
||||||
$tagged_content = \Activitypub\transform_tags( $commentdata['comment_content'] );
|
|
||||||
$commentdata['comment_content'] = $tagged_content['content'];
|
|
||||||
$commentdata['comment_meta']['mentions'] = $tagged_content['mentions'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return $commentdata;
|
return $commentdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* comment_post()
|
||||||
* postprocess_comment for federating replies and inbox-forwarding
|
* postprocess_comment for federating replies and inbox-forwarding
|
||||||
*/
|
*/
|
||||||
public static function postprocess_comment( $comment_id, $comment_approved, $commentdata ) {
|
public static function postprocess_comment( $comment_id, $comment_approved, $commentdata ) {
|
||||||
//Admin users comments bypass transition_comment_status (auto approved)
|
//Admin users comments bypass transition_comment_status (auto approved)
|
||||||
|
|
||||||
//\error_log( 'postprocess_comment_handler: comment_status: ' . $comment_approved );
|
|
||||||
if ( $commentdata['comment_type'] === 'activitypub' ) {
|
if ( $commentdata['comment_type'] === 'activitypub' ) {
|
||||||
if (
|
if (
|
||||||
( $comment_approved === 1 ) &&
|
( $comment_approved === 1 ) &&
|
||||||
|
@ -176,7 +180,7 @@ class Activitypub {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// TODO check that this is unused
|
// TODO check that this is unused
|
||||||
// TODO comment test as anon
|
// TODO comment test as anon / no auth_url, no fetchable status?
|
||||||
// TODO comment test as registered
|
// TODO comment test as registered
|
||||||
// TODO comment test as anyother site settings
|
// TODO comment test as anyother site settings
|
||||||
|
|
||||||
|
@ -197,14 +201,27 @@ class Activitypub {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* edit_comment()
|
||||||
|
*
|
||||||
|
* Fires immediately after a comment is updated in the database.
|
||||||
|
* Fires immediately before comment status transition hooks are fired. (useful only for admin)
|
||||||
|
*/
|
||||||
|
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'] ) ) {
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_update_comment_activity', array( $comment_ID ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule Activities
|
* Schedule Activities
|
||||||
*
|
*
|
||||||
|
* transition_comment_status()
|
||||||
* @param int $comment
|
* @param int $comment
|
||||||
*/
|
*/
|
||||||
public static function schedule_comment_activity( $new_status, $old_status, $activitypub_comment ) {
|
public static function schedule_comment_activity( $new_status, $old_status, $activitypub_comment ) {
|
||||||
|
|
||||||
// TODO format $activitypub_comment = new \Activitypub\Model\Comment( $comment );
|
// TODO format $activitypub_comment = new \Activitypub\Model\Comment( $comment );
|
||||||
if ( 'approved' === $new_status && 'approved' !== $old_status ) {
|
if ( 'approved' === $new_status && 'approved' !== $old_status ) {
|
||||||
//should only federate replies from local actors
|
//should only federate replies from local actors
|
||||||
|
@ -222,17 +239,35 @@ class Activitypub {
|
||||||
|| in_array( $local_user, $ap_object['tag'] )
|
|| in_array( $local_user, $ap_object['tag'] )
|
||||||
) {
|
) {
|
||||||
//if inReplyTo, object, target and/or tag are (local-wp) objects
|
//if inReplyTo, object, target and/or tag are (local-wp) objects
|
||||||
//\ActivityPub\Activity_Dispatcher::inbox_forward_activity( $activitypub_comment );
|
|
||||||
\wp_schedule_single_event( \time(), 'activitypub_inbox_forward_activity', array( $activitypub_comment->comment_ID ) );
|
\wp_schedule_single_event( \time(), 'activitypub_inbox_forward_activity', array( $activitypub_comment->comment_ID ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ( 'trash' === $new_status ) {
|
} elseif ( 'trash' === $new_status ) {
|
||||||
\wp_schedule_single_event( \time(), 'activitypub_send_delete_comment_activity', array( $activitypub_comment ) );
|
\wp_schedule_single_event( \time(), 'activitypub_send_delete_comment_activity', array( $activitypub_comment ) );
|
||||||
|
} elseif ( $old_status === $new_status ) {
|
||||||
|
//TODO Test with non-admin user
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_update_comment_activity', array( $activitypub_comment->comment_ID ) );
|
||||||
} else {
|
} else {
|
||||||
|
//error_log( 'schedule_update_comment_activity: else?:' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_comment_text( $comment )
|
||||||
|
*
|
||||||
|
* Filters the comment content before it is updated in the database.
|
||||||
|
*/
|
||||||
|
public static function comment_append_edit_datetime( $comment_text, $comment, $args ) {
|
||||||
|
if ( 'activitypub' === $comment->comment_type ) {
|
||||||
|
$updated = \wp_date( 'Y-m-d H:i:s', \strtotime( \get_comment_meta( $comment->comment_ID, 'ap_last_modified', true ) ) );
|
||||||
|
if( $updated ) {
|
||||||
|
$append_updated = "<div>(Last edited on <time class='modified' datetime='{$updated}'>$updated</time>)</div>";
|
||||||
|
$comment_text .= $append_updated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $comment_text;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the default avatar.
|
* Replaces the default avatar.
|
||||||
|
|
|
@ -58,14 +58,15 @@ function safe_remote_post( $url, $body, $user_id ) {
|
||||||
|
|
||||||
$response = \wp_safe_remote_post( $url, $args );
|
$response = \wp_safe_remote_post( $url, $args );
|
||||||
|
|
||||||
\do_action( 'activitypub_safe_remote_post_response', $response, $url, $body, $user_id );
|
//\do_action( 'activitypub_safe_remote_post_response', $response, $url, $body, $user_id );
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
function forward_remote_post( $url, $body, $user_id ) {
|
function forward_remote_post( $url, $body, $user_id ) {
|
||||||
$date = \gmdate( 'D, d M Y H:i:s T' );
|
$date = \gmdate( 'D, d M Y H:i:s T' );
|
||||||
$signature = \Activitypub\Signature::generate_signature( $user_id, $url, $date );
|
$digest = \Activitypub\Signature::generate_digest( $body );
|
||||||
|
$signature = \Activitypub\Signature::generate_signature( 1, $url, $date );
|
||||||
|
|
||||||
$wp_version = \get_bloginfo( 'version' );
|
$wp_version = \get_bloginfo( 'version' );
|
||||||
$user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
|
$user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
|
||||||
|
@ -250,6 +251,24 @@ function get_follower_inboxes( $user_id ) {
|
||||||
return $inboxes;
|
return $inboxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_mentioned_inboxes( $mentions ) {
|
||||||
|
$inboxes = array();
|
||||||
|
|
||||||
|
foreach ( $mentions as $mention ) {
|
||||||
|
$inbox = \Activitypub\get_inbox_by_actor( $mention['href'] );
|
||||||
|
if ( ! $inbox || \is_wp_error( $inbox ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// init array if empty
|
||||||
|
if ( ! isset( $inboxes[ $inbox ] ) ) {
|
||||||
|
$inboxes[ $inbox ] = array();
|
||||||
|
}
|
||||||
|
$inboxes[ $inbox ][] = $mention;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $inboxes;
|
||||||
|
}
|
||||||
|
|
||||||
function get_identifier_settings( $user_id ) {
|
function get_identifier_settings( $user_id ) {
|
||||||
?>
|
?>
|
||||||
<table class="form-table">
|
<table class="form-table">
|
||||||
|
@ -332,36 +351,40 @@ function url_to_authorid( $url ) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify if 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
|
* return int comment_id
|
||||||
*/
|
*/
|
||||||
function url_to_commentid( $comment_url ) {
|
function url_to_commentid( $in_replyto_url ) {
|
||||||
if ( empty( $comment_url ) ) {
|
if ( empty( $in_replyto_url ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$post_url = \url_to_postid( $comment_url );
|
|
||||||
|
|
||||||
if ( $post_url ) {
|
//rewrite for activitypub object id simplification
|
||||||
//for local comment parent
|
$url_maybe_id = \wp_parse_url( $in_replyto_url );
|
||||||
$comment_id = explode( '#comment-', $comment_url );
|
|
||||||
if ( isset( $comment_id[1] ) ){
|
if ( $url_maybe_id['scheme'] . '://' . $url_maybe_id['host'] == site_url() ) {
|
||||||
return $comment_id[1];
|
//is local post or comment
|
||||||
|
\parse_str( $url_maybe_id['query'], $reply_query );
|
||||||
|
if (isset( $reply_query['ap_comment_id'] ) && is_int( $reply_query['ap_comment_id'] ) ){
|
||||||
|
//is local comment
|
||||||
|
return $reply_query['ap_comment_id'];
|
||||||
} else {
|
} else {
|
||||||
|
//not a comment
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//remote comment parent, assuming the parent was also recieved
|
//is remote url
|
||||||
//Compare inReplyTo with source_url from meta, to determine if local comment_id exists for peer replied object
|
//verify if in_replyto_url corresponds to a previously received comment
|
||||||
$comment_args = array(
|
$comment_args = array(
|
||||||
'type' => 'activitypub',
|
'type' => 'activitypub',
|
||||||
'meta_query' => array(
|
'meta_query' => array(
|
||||||
array(
|
array(
|
||||||
'key' => 'source_url',
|
'key' => 'source_url',//$object['object']['id']
|
||||||
'value' => $comment_url,
|
'value' => $in_replyto_url,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -369,13 +392,46 @@ function url_to_commentid( $comment_url ) {
|
||||||
$comments = $comments_query->query( $comment_args );
|
$comments = $comments_query->query( $comment_args );
|
||||||
$found_comment_ids = array();
|
$found_comment_ids = array();
|
||||||
if ( $comments ) {
|
if ( $comments ) {
|
||||||
foreach ( $comments as $comment ) {
|
foreach ( $comments as $comment ) {
|
||||||
$found_comment_ids[] = $comment->comment_ID;
|
$found_comment_ids[] = $comment->comment_ID;
|
||||||
}
|
}
|
||||||
return $found_comment_ids[0];
|
return $found_comment_ids[0];
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify if url is a wp_ap_comment,
|
||||||
|
* Or if it is a previously received remote comment
|
||||||
|
*
|
||||||
|
* return int comment_id
|
||||||
|
*/
|
||||||
|
function is_ap_comment() {
|
||||||
|
$comment_id = get_query_var( 'ap_comment_id', null );
|
||||||
|
if( ! is_null( $comment_id ) ) {
|
||||||
|
$comment = \get_comment( $comment_id );
|
||||||
|
// Only return local origin comments
|
||||||
|
if( $comment->user_id ) {
|
||||||
|
return $comment_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify if url is a /replies endoint,
|
||||||
|
*
|
||||||
|
* return int true
|
||||||
|
*/
|
||||||
|
function is_ap_replies() {
|
||||||
|
global $wp;
|
||||||
|
$replies = get_query_var( 'replies' );
|
||||||
|
//$page = get_query_var( 'collection_page' );
|
||||||
|
if( ( $replies ) ) {
|
||||||
|
return $replies;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -502,10 +558,34 @@ function url_to_webfinger( $user_url ) {
|
||||||
*/
|
*/
|
||||||
function normalize_comment_url( $comment ) {
|
function normalize_comment_url( $comment ) {
|
||||||
$comment_id = explode( '#comment-', \get_comment_link( $comment ) );
|
$comment_id = explode( '#comment-', \get_comment_link( $comment ) );
|
||||||
$comment_id = $comment_id[0] . '?comment-' . $comment_id[1];
|
$comment_id = $comment_id[0] . '?ap_comment_id=' . $comment_id[1];
|
||||||
return $comment_id;
|
return $comment_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set ap_comment_id
|
||||||
|
*
|
||||||
|
* AP Object ID must be unique
|
||||||
|
*
|
||||||
|
* https://www.w3.org/TR/activitypub/#obj-id
|
||||||
|
* https://github.com/tootsuite/mastodon/issues/13879
|
||||||
|
*/
|
||||||
|
function set_ap_comment_id( $comment ) {
|
||||||
|
$ap_comment_id = add_query_arg(
|
||||||
|
array(
|
||||||
|
'p' => $comment->comment_post_ID,
|
||||||
|
'ap_comment_id' => $comment->comment_ID,//should probably rename to ap_comment or something
|
||||||
|
),
|
||||||
|
trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
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
|
||||||
* @param string $object
|
* @param string $object
|
||||||
|
@ -527,4 +607,4 @@ function get_audience( $object ) {
|
||||||
return 'private';
|
return 'private';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Activity {
|
||||||
private $to = array( 'https://www.w3.org/ns/activitystreams#Public' );
|
private $to = array( 'https://www.w3.org/ns/activitystreams#Public' );
|
||||||
private $cc = array( 'https://www.w3.org/ns/activitystreams#Public' );
|
private $cc = array( 'https://www.w3.org/ns/activitystreams#Public' );
|
||||||
private $object = null;
|
private $object = null;
|
||||||
|
private $tag = null;
|
||||||
|
|
||||||
const TYPE_SIMPLE = 'simple';
|
const TYPE_SIMPLE = 'simple';
|
||||||
const TYPE_FULL = 'full';
|
const TYPE_FULL = 'full';
|
||||||
|
@ -71,8 +72,13 @@ class Activity {
|
||||||
$this->tag = $object['tag'];
|
$this->tag = $object['tag'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function to_comment() {
|
public function to_comment( $timestamp ) {
|
||||||
|
if ( $this->trash ) {
|
||||||
|
$this->deleted = $timestamp['deleted'];
|
||||||
|
}
|
||||||
|
if ( $this->updated) {
|
||||||
|
$this->updated = $timestamp['updated'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function from_remote_array( $array ) {
|
public function from_remote_array( $array ) {
|
||||||
|
|
|
@ -8,20 +8,26 @@ namespace Activitypub\Model;
|
||||||
*/
|
*/
|
||||||
class Comment {
|
class Comment {
|
||||||
private $comment;
|
private $comment;
|
||||||
|
private $updated;
|
||||||
|
private $deleted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the class
|
* Initialize the class
|
||||||
*/
|
*/
|
||||||
public function __construct( $comment = null ) {
|
public function __construct( $comment = null ) {
|
||||||
$this->comment = $comment;
|
$this->comment = $comment;
|
||||||
|
$this->id = $this->generate_comment_id();
|
||||||
$this->comment_author_url = \get_author_posts_url( $this->comment->user_id );
|
$this->comment_author_url = \get_author_posts_url( $this->comment->user_id );
|
||||||
$this->safe_comment_id = $this->generate_comment_id();
|
$this->safe_comment_id = $this->generate_comment_id();
|
||||||
$this->inReplyTo = $this->generate_parent_url();
|
$this->inReplyTo = $this->generate_parent_url();
|
||||||
$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->cc_recipients = $this->generate_recipients();
|
$this->cc_recipients = $this->generate_recipients();
|
||||||
$this->tags = $this->generate_tags();
|
$this->tags = $this->generate_tags();
|
||||||
|
$this->update = $this->generate_update();
|
||||||
|
$this->deleted = $this->generate_trash();
|
||||||
|
$this->replies = $this->generate_replies();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __call( $method, $params ) {
|
public function __call( $method, $params ) {
|
||||||
|
@ -40,7 +46,7 @@ class Comment {
|
||||||
$comment = $this->comment;
|
$comment = $this->comment;
|
||||||
|
|
||||||
$array = array(
|
$array = array(
|
||||||
'id' => \Activitypub\Model\Comment::normalize_comment_id( $comment ),
|
'id' => $this->safe_comment_id,
|
||||||
'type' => 'Note',
|
'type' => 'Note',
|
||||||
'published' => \date( 'Y-m-d\TH:i:s\Z', \strtotime( $comment->comment_date_gmt ) ),
|
'published' => \date( 'Y-m-d\TH:i:s\Z', \strtotime( $comment->comment_date_gmt ) ),
|
||||||
'attributedTo' => $this->comment_author_url,
|
'attributedTo' => $this->comment_author_url,
|
||||||
|
@ -50,12 +56,22 @@ class Comment {
|
||||||
'contentMap' => array(
|
'contentMap' => array(
|
||||||
\strstr( \get_locale(), '_', true ) => $comment->comment_content,
|
\strstr( \get_locale(), '_', true ) => $comment->comment_content,
|
||||||
),
|
),
|
||||||
'source' => \get_comment_link( $comment ),
|
'context' => $this->context,
|
||||||
|
//'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' => array( 'https://www.w3.org/ns/activitystreams#Public' ),//audience logic
|
||||||
'cc' => $this->cc_recipients,
|
'cc' => $this->cc_recipients,
|
||||||
'tag' => $this->tags,
|
'tag' => $this->tags,
|
||||||
);
|
);
|
||||||
|
if ( $this->replies ) {
|
||||||
|
$array['replies'] = $this->replies;
|
||||||
|
}
|
||||||
|
if ( $this->update ) {
|
||||||
|
$array['updated'] = $this->update;
|
||||||
|
}
|
||||||
|
if ( $this->deleted ) {
|
||||||
|
$array['deleted'] = $this->deleted;
|
||||||
|
}
|
||||||
|
|
||||||
return \apply_filters( 'activitypub_comment', $array );
|
return \apply_filters( 'activitypub_comment', $array );
|
||||||
}
|
}
|
||||||
|
@ -68,10 +84,13 @@ class Comment {
|
||||||
return \get_author_posts_url( $this->comment->comment_author );
|
return \get_author_posts_url( $this->comment->comment_author );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generate_comment_id() {
|
||||||
|
return \Activitypub\set_ap_comment_id( $this->comment );
|
||||||
|
}
|
||||||
|
|
||||||
public function generate_permalink() {
|
public function generate_permalink() {
|
||||||
$comment = $this->comment;
|
$comment = $this->comment;
|
||||||
$permalink = \get_comment_link( $comment );
|
$permalink = \get_comment_link( $comment );
|
||||||
|
|
||||||
// replace 'trashed' for delete activity
|
// replace 'trashed' for delete activity
|
||||||
return \str_replace( '__trashed', '', $permalink );
|
return \str_replace( '__trashed', '', $permalink );
|
||||||
}
|
}
|
||||||
|
@ -83,18 +102,42 @@ class Comment {
|
||||||
public function generate_parent_url() {
|
public function generate_parent_url() {
|
||||||
$comment = $this->comment;
|
$comment = $this->comment;
|
||||||
$parent_comment = \get_comment( $comment->comment_parent );
|
$parent_comment = \get_comment( $comment->comment_parent );
|
||||||
if ( $parent_comment ) {
|
if ( $comment->comment_parent ) {
|
||||||
//reply to local (received) comment
|
//is parent remote?
|
||||||
$inReplyTo = \get_comment_meta( $comment->comment_parent, 'source_url', true );
|
$inReplyTo = \get_comment_meta( $comment->comment_parent, 'source_url', true );
|
||||||
|
if ( ! $inReplyTo ) {
|
||||||
|
$inReplyTo = add_query_arg(
|
||||||
|
array(
|
||||||
|
'p' => $comment->comment_post_ID,
|
||||||
|
'ap_comment_id' => $comment->comment_parent,
|
||||||
|
),
|
||||||
|
trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//reply to local post
|
$inReplyTo = add_query_arg(
|
||||||
$inReplyTo = \get_permalink( $comment->comment_post_ID );
|
array(
|
||||||
|
'p' => $comment->comment_post_ID,
|
||||||
|
),
|
||||||
|
trailingslashit( site_url() )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return $inReplyTo;
|
return $inReplyTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generate_context() {
|
||||||
|
$comment = $this->comment;
|
||||||
|
$inReplyTo = add_query_arg(
|
||||||
|
array(
|
||||||
|
'p' => $comment->comment_post_ID,
|
||||||
|
),
|
||||||
|
trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
return $inReplyTo;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate Content Warning from peer
|
* Generate courtesy Content Warning
|
||||||
* If peer used CW let's just copy it
|
* If peer 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
|
||||||
|
@ -104,6 +147,8 @@ class Comment {
|
||||||
public function generate_content_warning() {
|
public function generate_content_warning() {
|
||||||
$comment = $this->comment;
|
$comment = $this->comment;
|
||||||
$contentWarning = null;
|
$contentWarning = null;
|
||||||
|
|
||||||
|
// TODO Replace auto CW, with Title field or CW shortcode
|
||||||
$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
|
||||||
|
@ -112,9 +157,9 @@ class Comment {
|
||||||
$contentWarning = $ap_object['object']['summary'];
|
$contentWarning = $ap_object['object']['summary'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*$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 );
|
$contentWarning = \Activitypub\add_summary( $summary ); //TODO
|
||||||
} */
|
} */
|
||||||
return $contentWarning;
|
return $contentWarning;
|
||||||
}
|
}
|
||||||
|
@ -124,6 +169,7 @@ class Comment {
|
||||||
*/
|
*/
|
||||||
public function generate_recipients() {
|
public function generate_recipients() {
|
||||||
//TODO Add audience logic get parent audience
|
//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 ) ) {
|
||||||
|
@ -152,16 +198,66 @@ class Comment {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform comment url, replace #fragment with ?query
|
* Generate updated datetime
|
||||||
*
|
|
||||||
* AP Object ID must be unique
|
|
||||||
*
|
|
||||||
* https://www.w3.org/TR/activitypub/#obj-id
|
|
||||||
* https://github.com/tootsuite/mastodon/issues/13879
|
|
||||||
*/
|
*/
|
||||||
public function normalize_comment_id( $comment ) {
|
public function generate_update() {
|
||||||
$comment_id = explode( '#comment-', \get_comment_link( $comment ) );
|
$comment = $this->comment;
|
||||||
$comment_id = $comment_id[0] . '?comment-' . $comment_id[1];
|
$updated = null;
|
||||||
return $comment_id;
|
if ( \get_comment_meta( $comment->comment_ID, 'ap_last_modified', true ) ) {
|
||||||
|
$updated = \wp_date( 'Y-m-d\TH:i:s\Z', \get_comment_meta( $comment->comment_ID, 'ap_last_modified', true ) );
|
||||||
|
}
|
||||||
|
return $updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate deleted datetime
|
||||||
|
*/
|
||||||
|
public function generate_trash() {
|
||||||
|
$comment = $this->comment;
|
||||||
|
$deleted = null;
|
||||||
|
if ( 'trash' == $comment->status ) {
|
||||||
|
$deleted = \date( 'Y-m-d\TH:i:s\Z', \strtotime( $comment->comment_date_gmt ) );
|
||||||
|
}
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate replies collections
|
||||||
|
*/
|
||||||
|
public function generate_replies() {
|
||||||
|
$comment = $this->comment;
|
||||||
|
$args = array(
|
||||||
|
'post_id' => $comment->comment_post_ID,
|
||||||
|
'parent' => $comment->comment_ID,
|
||||||
|
'author__in' => $comment->user_id,
|
||||||
|
'status' => 'approve',
|
||||||
|
'hierarchical' => false,
|
||||||
|
);
|
||||||
|
$children = \get_comments( $args );
|
||||||
|
$replies = null;
|
||||||
|
if ( $children ) {
|
||||||
|
$items = [];
|
||||||
|
foreach ( $children as $child_comment ){
|
||||||
|
$comment_url = \add_query_arg(
|
||||||
|
array(
|
||||||
|
'p' => $child_comment->comment_post_ID,
|
||||||
|
'ap_comment_id' => $child_comment->comment_ID
|
||||||
|
),
|
||||||
|
trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
$items[] = $comment_url;
|
||||||
|
}
|
||||||
|
$replies = (object) [
|
||||||
|
'type' => 'Collection',
|
||||||
|
'id' => \add_query_arg( array( 'replies' => '' ), $this->id ),
|
||||||
|
'first' => (object) [
|
||||||
|
'type' => 'CollectionPage',
|
||||||
|
'partOf'=> \add_query_arg( array( 'replies' => '' ), $this->id ),
|
||||||
|
'next' => \add_query_arg( array( 'replies' => '', 'page' => 1 ), $this->id ),
|
||||||
|
'items' => $items
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $replies;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,6 +15,9 @@ class Post {
|
||||||
private $attachments;
|
private $attachments;
|
||||||
private $tags;
|
private $tags;
|
||||||
private $object_type;
|
private $object_type;
|
||||||
|
private $deleted;
|
||||||
|
private $updated;
|
||||||
|
private $slug;
|
||||||
|
|
||||||
public function __construct( $post = null ) {
|
public function __construct( $post = null ) {
|
||||||
$this->post = \get_post( $post );
|
$this->post = \get_post( $post );
|
||||||
|
@ -26,6 +29,11 @@ class Post {
|
||||||
$this->attachments = $this->generate_attachments();
|
$this->attachments = $this->generate_attachments();
|
||||||
$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->updated = $this->generate_updated();
|
||||||
|
$this->slug = \get_permalink( $this->id );
|
||||||
|
$this->updated = null;
|
||||||
|
$this->delete = $this->get_deleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __call( $method, $params ) {
|
public function __call( $method, $params ) {
|
||||||
|
@ -50,6 +58,7 @@ class Post {
|
||||||
'attributedTo' => \get_author_posts_url( $post->post_author ),
|
'attributedTo' => \get_author_posts_url( $post->post_author ),
|
||||||
'summary' => $this->summary,
|
'summary' => $this->summary,
|
||||||
'inReplyTo' => null,
|
'inReplyTo' => null,
|
||||||
|
'url' => \get_permalink( $post->ID ),
|
||||||
'content' => $this->content,
|
'content' => $this->content,
|
||||||
'contentMap' => array(
|
'contentMap' => array(
|
||||||
\strstr( \get_locale(), '_', true ) => $this->content,
|
\strstr( \get_locale(), '_', true ) => $this->content,
|
||||||
|
@ -59,7 +68,18 @@ class Post {
|
||||||
'attachment' => $this->attachments,
|
'attachment' => $this->attachments,
|
||||||
'tag' => $this->tags,
|
'tag' => $this->tags,
|
||||||
);
|
);
|
||||||
|
if ( $this->replies ) {
|
||||||
|
$array['replies'] = $this->replies;
|
||||||
|
}
|
||||||
|
if ( $this->deleted ) {
|
||||||
|
$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, '_ap_deleted_slug', true );
|
||||||
|
//$array['id'] = $deleted_post_slug;
|
||||||
|
}
|
||||||
|
if ( $this->updated ) {
|
||||||
|
$array['updated'] = \date( 'Y-m-d\TH:i:s\Z', \strtotime( $post->post_modified_gmt ) );
|
||||||
|
}
|
||||||
return \apply_filters( 'activitypub_post', $array );
|
return \apply_filters( 'activitypub_post', $array );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +89,12 @@ class Post {
|
||||||
|
|
||||||
public function generate_id() {
|
public function generate_id() {
|
||||||
$post = $this->post;
|
$post = $this->post;
|
||||||
$permalink = \get_permalink( $post );
|
$permalink = \add_query_arg( //
|
||||||
|
array(
|
||||||
|
'p' => $post->ID,
|
||||||
|
),
|
||||||
|
trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
|
||||||
// replace 'trashed' for delete activity
|
// replace 'trashed' for delete activity
|
||||||
return \str_replace( '__trashed', '', $permalink );
|
return \str_replace( '__trashed', '', $permalink );
|
||||||
|
@ -152,6 +177,48 @@ class Post {
|
||||||
|
|
||||||
return $tags;
|
return $tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generate_replies() {
|
||||||
|
$replies = null;
|
||||||
|
//\error_log( 'generate_replies: $post' . print_r( $this->post, true ) );
|
||||||
|
if ( $this->post->comment_count > 0 ) {
|
||||||
|
$args = array(
|
||||||
|
'post_id' => $this->post->ID, // Use post_id, not post_ID
|
||||||
|
'hierarchical' => false,
|
||||||
|
'status' => 'approve',
|
||||||
|
);
|
||||||
|
$comments = \get_comments( $args );
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
foreach ( $comments as $comment ){
|
||||||
|
// include self replies
|
||||||
|
if ( $this->post->post_author === $comment->user_id ) {
|
||||||
|
//$comment_url = $comment->comment_ID;
|
||||||
|
$comment_url = \add_query_arg( //
|
||||||
|
array(
|
||||||
|
'p' => $this->post->ID,
|
||||||
|
'ap_comment_id' => $comment->comment_ID
|
||||||
|
),
|
||||||
|
trailingslashit( site_url() )
|
||||||
|
);
|
||||||
|
//\error_log( 'generate_replies: $comment' . print_r( $comment, true ) );
|
||||||
|
$items[] = $comment_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//\error_log( 'generate_replies: $comments' . print_r( $comments, true ) );
|
||||||
|
$replies = (object) [
|
||||||
|
'type' => 'Collection',
|
||||||
|
'id' => \add_query_arg( array( 'replies' => '' ), $this->id ),
|
||||||
|
'first' => (object) [
|
||||||
|
'type' => 'CollectionPage',
|
||||||
|
'partOf'=> \add_query_arg( array( 'replies' => '' ), $this->id ),
|
||||||
|
'next' => \add_query_arg( array( 'replies' => '', 'page' => 1 ), $this->id ),
|
||||||
|
'items' => $items
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $replies;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the as2 object-type for a given post
|
* Returns the as2 object-type for a given post
|
||||||
|
@ -357,4 +424,18 @@ class Post {
|
||||||
|
|
||||||
return \implode( ' ', $hash_tags );
|
return \implode( ' ', $hash_tags );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get deleted datetime
|
||||||
|
*/
|
||||||
|
public function get_deleted() {
|
||||||
|
$post = $this->post;
|
||||||
|
$deleted = null;
|
||||||
|
if ( 'trash' == $post->post_status ) {
|
||||||
|
|
||||||
|
$deleted = \date( 'Y-m-d\TH:i:s\Z', \strtotime( $post->post_modified_gmt ) );
|
||||||
|
\error_log( 'trash: ' . print_r( $deleted, true ) );
|
||||||
|
}
|
||||||
|
return $deleted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ class Inbox {
|
||||||
//\add_action( 'activitypub_inbox_like', array( '\Activitypub\Rest\Inbox', 'handle_reaction' ), 10, 2 );
|
//\add_action( 'activitypub_inbox_like', array( '\Activitypub\Rest\Inbox', 'handle_reaction' ), 10, 2 );
|
||||||
//\add_action( 'activitypub_inbox_announce', array( '\Activitypub\Rest\Inbox', 'handle_reaction' ), 10, 2 );
|
//\add_action( 'activitypub_inbox_announce', array( '\Activitypub\Rest\Inbox', 'handle_reaction' ), 10, 2 );
|
||||||
\add_action( 'activitypub_inbox_create', array( '\Activitypub\Rest\Inbox', 'handle_create' ), 10, 2 );
|
\add_action( 'activitypub_inbox_create', array( '\Activitypub\Rest\Inbox', 'handle_create' ), 10, 2 );
|
||||||
|
\add_action( 'activitypub_inbox_update', array( '\Activitypub\Rest\Inbox', 'handle_update' ), 10, 2 );
|
||||||
|
\add_action( 'activitypub_inbox_delete', array( '\Activitypub\Rest\Inbox', 'handle_delete' ), 10, 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -409,28 +411,27 @@ class Inbox {
|
||||||
$avatar_url = null;
|
$avatar_url = null;
|
||||||
$audience = \Activitypub\get_audience( $object );
|
$audience = \Activitypub\get_audience( $object );
|
||||||
|
|
||||||
//Determine parent post and/or parent comment
|
//Determine comment_post_ID and/or comment_parent
|
||||||
$comment_post_ID = $object_parent = $object_parent_ID = 0;
|
$comment_post_ID = $comment_parent = $comment_parent_ID = 0;
|
||||||
if ( isset( $object['object']['inReplyTo'] ) ) {
|
if ( isset( $object['object']['inReplyTo'] ) ) {
|
||||||
$comment_post_ID = \url_to_postid( $object['object']['inReplyTo'] );
|
|
||||||
//if not a direct reply to a post, remote post parent
|
$comment_parent_ID = \Activitypub\url_to_commentid( \esc_url_raw( $object['object']['inReplyTo'] ) );
|
||||||
if ( $comment_post_ID === 0 ) {
|
|
||||||
//verify if reply to a local or remote received comment
|
if ( !is_null( $comment_parent_ID ) ) {
|
||||||
$object_parent_ID = \Activitypub\url_to_commentid( \esc_url_raw( $object['object']['inReplyTo'] ) );
|
//inReplyTo a known local comment
|
||||||
if ( !is_null( $object_parent_ID ) ) {
|
$comment_parent = \get_comment( $comment_parent_ID );
|
||||||
//replied to a local comment (which has a post_ID)
|
$comment_post_ID = $comment_parent->comment_post_ID;
|
||||||
$object_parent = get_comment( $object_parent_ID );
|
} else {
|
||||||
$comment_post_ID = $object_parent->comment_post_ID;
|
//inReplyTo a known post
|
||||||
}
|
$comment_post_ID = \url_to_postid( $object['object']['inReplyTo'] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//not all implementaions use url
|
//not all implementaions use url
|
||||||
if ( isset( $object['object']['url'] ) ) {
|
if ( isset( $object['object']['id'] ) ) {
|
||||||
$source_url = \esc_url_raw( $object['object']['url'] );
|
|
||||||
} else {
|
|
||||||
//could also try $object['object']['source']?
|
|
||||||
$source_url = \esc_url_raw( $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
|
||||||
|
@ -448,7 +449,7 @@ class Inbox {
|
||||||
if ( ( in_array( AS_PUBLIC, $object['to'] )
|
if ( ( in_array( AS_PUBLIC, $object['to'] )
|
||||||
|| in_array( AS_PUBLIC, $object['cc'] ) )
|
|| in_array( AS_PUBLIC, $object['cc'] ) )
|
||||||
&& ( !empty( $comment_post_ID )
|
&& ( !empty( $comment_post_ID )
|
||||||
|| !empty ( $object_parent )
|
|| !empty ( $comment_parent )
|
||||||
) ) {
|
) ) {
|
||||||
|
|
||||||
$commentdata = array(
|
$commentdata = array(
|
||||||
|
@ -458,7 +459,7 @@ class Inbox {
|
||||||
'comment_content' => \wp_filter_kses( $object['object']['content'] ),
|
'comment_content' => \wp_filter_kses( $object['object']['content'] ),
|
||||||
'comment_type' => 'activitypub',
|
'comment_type' => 'activitypub',
|
||||||
'comment_author_email' => '',
|
'comment_author_email' => '',
|
||||||
'comment_parent' => $object_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' => $source_url,
|
||||||
|
@ -468,7 +469,7 @@ class Inbox {
|
||||||
);
|
);
|
||||||
|
|
||||||
// disable flood control
|
// disable flood control
|
||||||
\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
|
//\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
|
||||||
|
|
||||||
// do not require email for AP entries
|
// do not require email for AP entries
|
||||||
\add_filter( 'pre_option_require_name_email', '__return_false' );
|
\add_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
@ -478,11 +479,89 @@ class Inbox {
|
||||||
\remove_filter( 'pre_option_require_name_email', '__return_false' );
|
\remove_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
// re-add flood control
|
// re-add flood control
|
||||||
\add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
|
//\add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles "Update" requests
|
||||||
|
*
|
||||||
|
* @param array $object The activity-object
|
||||||
|
* @param int $user_id The id of the local blog-user
|
||||||
|
*/
|
||||||
|
public static function handle_update( $object, $user_id ) {
|
||||||
|
$meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] );
|
||||||
|
$avatar_url = null;
|
||||||
|
$audience = \Activitypub\get_audience( $object );
|
||||||
|
|
||||||
|
//Determine comment_ID
|
||||||
|
$object_comment_ID = \Activitypub\url_to_commentid( \esc_url_raw( $object['object']['id'] ) );
|
||||||
|
if ( !is_null( $object_comment_ID ) ) {
|
||||||
|
|
||||||
|
//found a local comment id
|
||||||
|
$commentdata = \get_comment( $object_comment_ID, ARRAY_A );
|
||||||
|
|
||||||
|
//$commentdata['comment_ID'] = \esc_url_raw( $object_comment_ID );
|
||||||
|
$commentdata['comment_content'] = \wp_filter_kses( $object['object']['content'] );
|
||||||
|
$commentdata['comment_meta']['ap_published'] = \wp_date( 'Y-m-d H:i:s', strtotime( $object['object']['published'] ) );
|
||||||
|
$commentdata['comment_meta']['ap_last_modified'] = $object['object']['updated'];
|
||||||
|
$commentdata['comment_meta']['ap_object'] = \serialize( $object );
|
||||||
|
|
||||||
|
//apply_filters( 'wp_update_comment_data', $data, $comment, $commentarr );
|
||||||
|
|
||||||
|
// disable flood control
|
||||||
|
\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
|
||||||
|
|
||||||
|
// do not require email for AP entries
|
||||||
|
\add_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
|
$state = \wp_update_comment( $commentdata, true );
|
||||||
|
|
||||||
|
\remove_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
|
// re-add flood control
|
||||||
|
\add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles "Delete" requests
|
||||||
|
*
|
||||||
|
* @param array $object The activity-object
|
||||||
|
* @param int $user_id The id of the local blog-user
|
||||||
|
*/
|
||||||
|
public static function handle_delete( $object, $user_id ) {
|
||||||
|
$meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] );
|
||||||
|
$avatar_url = null;
|
||||||
|
$audience = \Activitypub\get_audience( $object );
|
||||||
|
|
||||||
|
if ( ! isset( $object['object']['id'] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Determine comment_ID
|
||||||
|
$object_comment_ID = \Activitypub\url_to_commentid( \esc_url_raw( $object['object']['id'] ) );
|
||||||
|
if ( !is_null( $object_comment_ID ) ) {
|
||||||
|
|
||||||
|
//found a local comment id
|
||||||
|
$commentdata = \get_comment( $object_comment_ID, ARRAY_A );
|
||||||
|
|
||||||
|
// disable flood control
|
||||||
|
\remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
|
||||||
|
|
||||||
|
// do not require email for AP entries
|
||||||
|
\add_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
|
// Should we trash or send back to moderation
|
||||||
|
$state = \wp_trash_comment( $commentdata['comment_ID'], true );
|
||||||
|
|
||||||
|
//\remove_filter( 'pre_option_require_name_email', '__return_false' );
|
||||||
|
|
||||||
|
// re-add flood control
|
||||||
|
\add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function extract_recipients( $data ) {
|
public static function extract_recipients( $data ) {
|
||||||
$recipients = array();
|
$recipients = array();
|
||||||
$users = array();
|
$users = array();
|
||||||
|
|
36
templates/comment-json.php
Normal file
36
templates/comment-json.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
$comment = \get_comment( get_query_var('ap_comment_id') );
|
||||||
|
|
||||||
|
$activitypub_comment = new \Activitypub\Model\Comment( $comment );
|
||||||
|
$json = \array_merge( array( '@context' => \Activitypub\get_context() ), $activitypub_comment->to_array() );
|
||||||
|
|
||||||
|
// filter output
|
||||||
|
$json = \apply_filters( 'activitypub_json_comment_array', $json );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Action triggerd prior to the ActivityPub profile being created and sent to the client
|
||||||
|
*/
|
||||||
|
\do_action( 'activitypub_json_comment_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_comment_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_comment_comment' );
|
Loading…
Reference in a new issue