Comments 1
This commit is contained in:
parent
6795d707c8
commit
17c386cb9d
10 changed files with 811 additions and 29 deletions
|
@ -30,6 +30,7 @@ function init() {
|
||||||
|
|
||||||
require_once \dirname( __FILE__ ) . '/includes/model/class-activity.php';
|
require_once \dirname( __FILE__ ) . '/includes/model/class-activity.php';
|
||||||
require_once \dirname( __FILE__ ) . '/includes/model/class-post.php';
|
require_once \dirname( __FILE__ ) . '/includes/model/class-post.php';
|
||||||
|
require_once \dirname( __FILE__ ) . '/includes/model/class-comment.php';
|
||||||
|
|
||||||
require_once \dirname( __FILE__ ) . '/includes/class-activity-dispatcher.php';
|
require_once \dirname( __FILE__ ) . '/includes/class-activity-dispatcher.php';
|
||||||
\Activitypub\Activity_Dispatcher::init();
|
\Activitypub\Activity_Dispatcher::init();
|
||||||
|
|
22
includes/activitypub.js
Normal file
22
includes/activitypub.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
(function($) {
|
||||||
|
/**
|
||||||
|
* Reply Comment-edit screen
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Insert Mentions into comment content on reply
|
||||||
|
$('.comment-inline.button-link').on('click', function( event){
|
||||||
|
// Summary/ContentWarning Syntax [CW]
|
||||||
|
var summary = $(this).attr('data-summary') ? '[' + $(this).attr('data-summary') + '] ' : '';
|
||||||
|
var recipients = $(this).attr('data-recipients') ? $(this).attr('data-recipients') + ' ' : '';
|
||||||
|
setTimeout(function() {
|
||||||
|
if ( summary || recipients ){
|
||||||
|
$('#replycontent').val( summary + recipients )
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
})
|
||||||
|
//Clear Mentions from content on cancel
|
||||||
|
$('.cancel.button').on('click', function(){
|
||||||
|
$('#replycontent').val('');
|
||||||
|
});
|
||||||
|
|
||||||
|
})( jQuery );
|
|
@ -16,6 +16,10 @@ class Activity_Dispatcher {
|
||||||
\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' ) );
|
||||||
|
|
||||||
|
\add_action( 'activitypub_send_comment_activity', array( '\Activitypub\Activity_Dispatcher', 'send_comment_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' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,4 +81,126 @@ class Activity_Dispatcher {
|
||||||
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
|
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send "create" activities for comments
|
||||||
|
*
|
||||||
|
* @param \Activitypub\Model\Comment $activitypub_comment
|
||||||
|
*/
|
||||||
|
public static function send_comment_activity( $activitypub_comment_id ) {
|
||||||
|
//ONLY FOR LOCAL USERS ?
|
||||||
|
$activitypub_comment = \get_comment( $activitypub_comment_id );
|
||||||
|
$user_id = $activitypub_comment->user_id;
|
||||||
|
$replyto = get_comment_meta( $activitypub_comment->comment_parent, 'comment_author_url', true );//
|
||||||
|
$mentions = get_comment_meta( $activitypub_comment_id, 'mentions', true );//
|
||||||
|
//error_log( 'dispatcher:send_comment:$activitypub_comment: ' . print_r( $activitypub_comment, true ) );
|
||||||
|
|
||||||
|
$activitypub_comment = new \Activitypub\Model\Comment( $activitypub_comment );
|
||||||
|
$activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_FULL );
|
||||||
|
$activitypub_activity->from_comment( $activitypub_comment->to_array() );
|
||||||
|
|
||||||
|
\error_log( 'Activity_Dispatcher::send_comment_activity: ' . print_r($activitypub_activity, true));
|
||||||
|
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
||||||
|
\error_log( '$user_id: ' . $user_id . ', $inbox: '. $inbox . ', $to: '. print_r($to, true ) );
|
||||||
|
$activitypub_activity->set_to( $to[0] );
|
||||||
|
$activity = $activitypub_activity->to_json(); // phpcs:ignore
|
||||||
|
|
||||||
|
// Send reply to followers, skip if replying to followers (avoid duplicate replies)
|
||||||
|
// if( in_array( $to, $replyto ) || ( $replyto == $to ) ) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
\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 );
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forward replies to followers
|
||||||
|
*
|
||||||
|
* @param \Activitypub\Model\Comment $activitypub_comment
|
||||||
|
*/
|
||||||
|
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 );
|
||||||
|
|
||||||
|
//original author should NOT recieve a copy of ther own post
|
||||||
|
$replyto[] = $activitypub_comment->comment_author_url;
|
||||||
|
$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
|
||||||
|
//TODO verify that ... what?
|
||||||
|
$parent_comment = \get_comment( $activitypub_comment->comment_parent );
|
||||||
|
if ( !is_null( $parent_comment ) ) {
|
||||||
|
$user_id = $parent_comment->user_id;
|
||||||
|
} else {
|
||||||
|
$original_post = \get_post( $activitypub_comment->comment_post_ID );
|
||||||
|
$user_id = $original_post->post_author;
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove user_id from $activitypub_comment
|
||||||
|
unset($activitypub_activity['user_id']);
|
||||||
|
|
||||||
|
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
|
||||||
|
if( in_array( $to, $replyto ) || ( $replyto == $to ) ) {
|
||||||
|
error_log( 'dispatch:forward: nope:' . print_r( $to, true ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$activitypub_activity['object']['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 );
|
||||||
|
error_log( 'dispatch:forward:activity:' . print_r( $activity, true ) );
|
||||||
|
\Activitypub\forward_remote_post( $inbox, $activity, $user_id );
|
||||||
|
|
||||||
|
//reset //unnecessary
|
||||||
|
//array_pop( $activitypub_activity->object->to[] );
|
||||||
|
//array_pop( $activitypub_activity->to[] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send "delete" activities.
|
||||||
|
*
|
||||||
|
* @param \Activitypub\Model\Comment $activitypub_comment
|
||||||
|
*/
|
||||||
|
public static function send_delete_comment_activity( $activitypub_comment_id ) {
|
||||||
|
// get comment
|
||||||
|
$activitypub_comment = \get_comment( $activitypub_comment_id );
|
||||||
|
$user_id = $activitypub_comment->post_author;
|
||||||
|
|
||||||
|
$activitypub_comment = new \Activitypub\Model\Comment( $activitypub_comment );
|
||||||
|
$activitypub_activity = new \Activitypub\Model\Activity( 'Delete', \Activitypub\Model\Activity::TYPE_FULL );
|
||||||
|
$activitypub_activity->from_comment( $activitypub_comment->to_array() );
|
||||||
|
|
||||||
|
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
|
||||||
|
$activitypub_activity->set_to( $to );
|
||||||
|
$activity = $activitypub_activity->to_json(); // phpcs:ignore
|
||||||
|
|
||||||
|
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,11 @@ class Activitypub {
|
||||||
}
|
}
|
||||||
|
|
||||||
\add_action( 'transition_post_status', array( '\Activitypub\Activitypub', 'schedule_post_activity' ), 10, 3 );
|
\add_action( 'transition_post_status', array( '\Activitypub\Activitypub', 'schedule_post_activity' ), 10, 3 );
|
||||||
|
|
||||||
|
\add_filter( 'preprocess_comment' , array( '\Activitypub\Activitypub', 'preprocess_comment' ) );
|
||||||
|
\add_filter( 'comment_post' , array( '\Activitypub\Activitypub', 'postprocess_comment' ), 10, 3 );
|
||||||
|
\add_action( 'transition_comment_status', array( '\Activitypub\Activitypub', 'schedule_comment_activity' ), 20, 3 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,6 +131,109 @@ class Activitypub {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* preprocess local comments for federated replies
|
||||||
|
*/
|
||||||
|
public static function preprocess_comment( $commentdata ) {
|
||||||
|
|
||||||
|
//must only process replies from local actors
|
||||||
|
if ( !empty( $commentdata['user_id'] ) ) {
|
||||||
|
//\error_log( 'is_local user' );//TODO Test
|
||||||
|
//TODO TEST
|
||||||
|
$post_type = \get_object_subtype( 'post', $commentdata['comment_post_ID'] );
|
||||||
|
$ap_post_types = \get_option( 'activitypub_support_post_types' );
|
||||||
|
if ( !\is_null( $ap_post_types ) ) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* postprocess_comment for federating replies and inbox-forwarding
|
||||||
|
*/
|
||||||
|
public static function postprocess_comment( $comment_id, $comment_approved, $commentdata ) {
|
||||||
|
//Admin users comments bypass transition_comment_status (auto approved)
|
||||||
|
|
||||||
|
//\error_log( 'postprocess_comment_handler: comment_status: ' . $comment_approved );
|
||||||
|
if ( $commentdata['comment_type'] === 'activitypub' ) {
|
||||||
|
if (
|
||||||
|
( $comment_approved === 1 ) &&
|
||||||
|
! empty( $commentdata['user_id'] ) &&
|
||||||
|
( $user = get_userdata( $commentdata['user_id'] ) ) && // get the user data
|
||||||
|
in_array( 'administrator', $user->roles ) // check the roles
|
||||||
|
) {
|
||||||
|
// Only for Admins?
|
||||||
|
$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 ) );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// TODO check that this is unused
|
||||||
|
// TODO comment test as anon
|
||||||
|
// TODO comment test as registered
|
||||||
|
// TODO comment test as anyother site settings
|
||||||
|
|
||||||
|
|
||||||
|
// $replyto = get_comment_meta( $comment_id, 'replyto', true );
|
||||||
|
|
||||||
|
//inbox forward prep
|
||||||
|
// if ( !empty( $ap_object ) ) {
|
||||||
|
// //if is remote user (has ap_object)
|
||||||
|
// //error_log( print_r( $ap_object, true ) );
|
||||||
|
// // TODO verify that deduplication check happens at object create.
|
||||||
|
|
||||||
|
// //if to/cc/audience contains local followers collection
|
||||||
|
// //$local_user = \get_comment_author_url( $comment_id );
|
||||||
|
// //$is_local_user = \Activitypub\url_to_authorid( $commentdata['comment_author_url'] );
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule Activities
|
||||||
|
*
|
||||||
|
* @param int $comment
|
||||||
|
*/
|
||||||
|
public static function schedule_comment_activity( $new_status, $old_status, $activitypub_comment ) {
|
||||||
|
|
||||||
|
// TODO format $activitypub_comment = new \Activitypub\Model\Comment( $comment );
|
||||||
|
if ( 'approved' === $new_status && 'approved' !== $old_status ) {
|
||||||
|
//should only federate replies from local actors
|
||||||
|
//should only federate replies to federated actors
|
||||||
|
|
||||||
|
$ap_object = unserialize( \get_comment_meta( $activitypub_comment->comment_ID, 'ap_object', true ) );
|
||||||
|
if ( empty( $ap_object ) ) {
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_comment_activity', array( $activitypub_comment->comment_ID ) );
|
||||||
|
} else {
|
||||||
|
$local_user = \get_author_posts_url( $ap_object['user_id'] );
|
||||||
|
if ( !is_null( $local_user ) ) {
|
||||||
|
if ( in_array( $local_user, $ap_object['to'] )
|
||||||
|
|| in_array( $local_user, $ap_object['cc'] )
|
||||||
|
|| in_array( $local_user, $ap_object['audience'] )
|
||||||
|
|| in_array( $local_user, $ap_object['tag'] )
|
||||||
|
) {
|
||||||
|
//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 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ( 'trash' === $new_status ) {
|
||||||
|
\wp_schedule_single_event( \time(), 'activitypub_send_delete_comment_activity', array( $activitypub_comment ) );
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the default avatar.
|
* Replaces the default avatar.
|
||||||
*
|
*
|
||||||
|
@ -143,7 +251,7 @@ class Activitypub {
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
$allowed_comment_types = \apply_filters( 'get_avatar_comment_types', array( 'comment' ) );
|
$allowed_comment_types = \apply_filters( 'get_avatar_comment_types', array( 'comment', 'activitypub' ) );
|
||||||
if ( ! empty( $id_or_email->comment_type ) && ! \in_array( $id_or_email->comment_type, (array) $allowed_comment_types, true ) ) {
|
if ( ! empty( $id_or_email->comment_type ) && ! \in_array( $id_or_email->comment_type, (array) $allowed_comment_types, true ) ) {
|
||||||
$args['url'] = false;
|
$args['url'] = false;
|
||||||
/** This filter is documented in wp-includes/link-template.php */
|
/** This filter is documented in wp-includes/link-template.php */
|
||||||
|
|
|
@ -14,6 +14,9 @@ class Admin {
|
||||||
\add_action( 'admin_menu', array( '\Activitypub\Admin', 'admin_menu' ) );
|
\add_action( 'admin_menu', array( '\Activitypub\Admin', 'admin_menu' ) );
|
||||||
\add_action( 'admin_init', array( '\Activitypub\Admin', 'register_settings' ) );
|
\add_action( 'admin_init', array( '\Activitypub\Admin', 'register_settings' ) );
|
||||||
\add_action( 'show_user_profile', array( '\Activitypub\Admin', 'add_fediverse_profile' ) );
|
\add_action( 'show_user_profile', array( '\Activitypub\Admin', 'add_fediverse_profile' ) );
|
||||||
|
\add_action( 'admin_enqueue_scripts', array( '\Activitypub\Admin', 'scripts_reply_comments' ), 10, 2 );
|
||||||
|
\add_filter( 'comment_row_actions', array( '\Activitypub\Admin', 'reply_comments_actions' ), 10, 2 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,4 +142,54 @@ class Admin {
|
||||||
<?php
|
<?php
|
||||||
\Activitypub\get_identifier_settings( $user->ID );
|
\Activitypub\get_identifier_settings( $user->ID );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function reply_comments_actions( $actions, $comment ) {
|
||||||
|
//unset( $actions['reply'] );
|
||||||
|
$recipients = \Activitypub\get_recipients( $comment->comment_ID );
|
||||||
|
$summary = \Activitypub\get_summary( $comment->comment_ID );
|
||||||
|
|
||||||
|
//TODO revise for non-js reply action
|
||||||
|
// 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>';
|
||||||
|
$actions['reply'] = sprintf(
|
||||||
|
$reply_button,
|
||||||
|
$comment->comment_ID,
|
||||||
|
$comment->comment_post_ID,
|
||||||
|
'replyto',
|
||||||
|
'vim-r comment-inline',
|
||||||
|
esc_attr__( 'Reply to this comment' ),
|
||||||
|
$recipients,
|
||||||
|
$summary,
|
||||||
|
__( '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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function scripts_reply_comments( $hook ) {
|
||||||
|
if ('edit-comments.php' !== $hook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wp_enqueue_script( 'activitypub_client',
|
||||||
|
plugin_dir_url(__FILE__) . '/activitypub.js',
|
||||||
|
array('jquery'),
|
||||||
|
filemtime( plugin_dir_path(__FILE__) . '/activitypub.js' ),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Activitypub;
|
namespace Activitypub;
|
||||||
|
|
||||||
|
define('AS_PUBLIC', 'https://www.w3.org/ns/activitystreams#Public');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ActivityPub default JSON-context
|
* Returns the ActivityPub default JSON-context
|
||||||
*
|
*
|
||||||
|
@ -61,6 +63,34 @@ function safe_remote_post( $url, $body, $user_id ) {
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function forward_remote_post( $url, $body, $user_id ) {
|
||||||
|
$date = \gmdate( 'D, d M Y H:i:s T' );
|
||||||
|
$signature = \Activitypub\Signature::generate_signature( $user_id, $url, $date );
|
||||||
|
|
||||||
|
$wp_version = \get_bloginfo( 'version' );
|
||||||
|
$user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
|
||||||
|
$args = array(
|
||||||
|
'timeout' => 100,
|
||||||
|
'limit_response_size' => 1048576,
|
||||||
|
'redirection' => 3,
|
||||||
|
'user-agent' => "$user_agent; ActivityPub",
|
||||||
|
'headers' => array(
|
||||||
|
'Accept' => 'application/activity+json',
|
||||||
|
'Content-Type' => 'application/activity+json',
|
||||||
|
'Digest' => "SHA-256=$digest",
|
||||||
|
'Signature' => $signature,
|
||||||
|
'Date' => $date,
|
||||||
|
),
|
||||||
|
'body' => $body,
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = \wp_safe_remote_post( $url, $args );
|
||||||
|
|
||||||
|
\do_action( 'activitypub_forward_remote_post_response', $response, $url, $body, $user_id );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
function safe_remote_get( $url, $user_id ) {
|
function safe_remote_get( $url, $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 );
|
$signature = \Activitypub\Signature::generate_signature( $user_id, $url, $date );
|
||||||
|
@ -300,3 +330,199 @@ function url_to_authorid( $url ) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Verify if url is a local comment,
|
||||||
|
* Or if it is a previously received remote comment
|
||||||
|
*
|
||||||
|
* return int comment_id
|
||||||
|
*/
|
||||||
|
function url_to_commentid( $comment_url ) {
|
||||||
|
if ( empty( $comment_url ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$post_url = \url_to_postid( $comment_url );
|
||||||
|
|
||||||
|
if ( $post_url ) {
|
||||||
|
//for local comment parent
|
||||||
|
$comment_id = explode( '#comment-', $comment_url );
|
||||||
|
if ( isset( $comment_id[1] ) ){
|
||||||
|
return $comment_id[1];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//remote comment parent, assuming the parent was also recieved
|
||||||
|
//Compare inReplyTo with source_url from meta, to determine if local comment_id exists for peer replied object
|
||||||
|
$comment_args = array(
|
||||||
|
'type' => 'activitypub',
|
||||||
|
'meta_query' => array(
|
||||||
|
array(
|
||||||
|
'key' => 'source_url',
|
||||||
|
'value' => $comment_url,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$comments_query = new \WP_Comment_Query;
|
||||||
|
$comments = $comments_query->query( $comment_args );
|
||||||
|
$found_comment_ids = array();
|
||||||
|
if ( $comments ) {
|
||||||
|
foreach ( $comments as $comment ) {
|
||||||
|
$found_comment_ids[] = $comment->comment_ID;
|
||||||
|
}
|
||||||
|
return $found_comment_ids[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tagged users from received AP object meta
|
||||||
|
* @param string $object_id a comment_id to search
|
||||||
|
* @param boolean $post defaults to searching a comment_id
|
||||||
|
*
|
||||||
|
* @return array of tagged users
|
||||||
|
*/
|
||||||
|
function get_recipients( $object_id, $post = null ) {
|
||||||
|
$tagged_users_name = null;
|
||||||
|
if ( $post ) {
|
||||||
|
//post
|
||||||
|
$ap_object = \unserialize( \get_post_meta( $object_id, 'ap_object', true ) );
|
||||||
|
} else {
|
||||||
|
//comment
|
||||||
|
$ap_object = \unserialize( \get_comment_meta( $object_id, 'ap_object', true ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !empty( $ap_object ) ) {
|
||||||
|
$tagged_users_name[] = \Activitypub\url_to_webfinger( $ap_object['actor'] );
|
||||||
|
if ( !empty( $ap_object['object']['tag'] ) ) {
|
||||||
|
$author_post_url = \get_author_posts_url( $ap_object['user_id'] );
|
||||||
|
foreach ( $ap_object['object']['tag'] as $tag ) {
|
||||||
|
if ( $author_post_url == $tag['href'] ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( in_array( 'Mention', $tag ) ) {
|
||||||
|
$tagged_users_name[] = $tag['name'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode( ' ', $tagged_users_name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add summary to reply
|
||||||
|
*/
|
||||||
|
function get_summary( $comment_id ) {
|
||||||
|
$ap_object = \unserialize( \get_comment_meta( $comment_id, 'ap_object', true ) );
|
||||||
|
if ( !empty( $ap_object ) ) {
|
||||||
|
if ( !empty( $ap_object['object']['summary'] ) ) {
|
||||||
|
return \esc_attr( $ap_object['object']['summary'] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse content for tags to transform
|
||||||
|
* @param string $content to search
|
||||||
|
*/
|
||||||
|
function transform_tags( $content ) {
|
||||||
|
//#tags
|
||||||
|
|
||||||
|
//@Mentions
|
||||||
|
$mentions = null;
|
||||||
|
$webfinger_tags = \Activitypub\webfinger_extract( $content, true );
|
||||||
|
if ( !empty( $webfinger_tags) ) {
|
||||||
|
foreach ( $webfinger_tags[0] as $webfinger_tag ) {
|
||||||
|
$ap_profile = \Activitypub\Rest\Webfinger::webfinger_lookup( $webfinger_tag );
|
||||||
|
if ( ! empty( $ap_profile ) ) {
|
||||||
|
$short_tag = \Activitypub\webfinger_short_tag( $webfinger_tag );
|
||||||
|
$webfinger_link = "<span class='h-card'><a href=\"{$ap_profile['href']}\" class='u-url mention' rel='noopener noreferer' target='_blank'>{$short_tag}</a></span>";
|
||||||
|
$content = str_replace( $webfinger_tag, $webfinger_link, $content );
|
||||||
|
$mentions[] = $ap_profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return mentions separately to attach to comment/post meta
|
||||||
|
$content_mentions['mentions'] = $mentions;
|
||||||
|
$content_mentions['content'] = $content;
|
||||||
|
return $content_mentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tag_user( $recipient ) {
|
||||||
|
$tagged_user = array(
|
||||||
|
'type' => 'Mention',
|
||||||
|
'href' => $recipient,
|
||||||
|
'name' => \Activitypub\url_to_webfinger( $recipient ),
|
||||||
|
);
|
||||||
|
$tag[] = $tagged_user;
|
||||||
|
return $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $content
|
||||||
|
* @return array of all matched webfinger
|
||||||
|
*/
|
||||||
|
function webfinger_extract( $string ) {
|
||||||
|
preg_match_all("/@[\._a-zA-Z0-9-]+@[\._a-zA-Z0-9-]+/i", $string, $matches);
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string full $webfinger
|
||||||
|
* @return string short @webfinger
|
||||||
|
*/
|
||||||
|
function webfinger_short_tag( $webfinger ) {
|
||||||
|
$short_tag = explode( '@', $webfinger );
|
||||||
|
return '@' . $short_tag[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $user_url
|
||||||
|
* @return string $webfinger
|
||||||
|
*/
|
||||||
|
function url_to_webfinger( $user_url ) {
|
||||||
|
$user_url = \untrailingslashit( $user_url );
|
||||||
|
$user_url_array = explode( '/', $user_url );
|
||||||
|
$user_name = end( $user_url_array );
|
||||||
|
$url_host = parse_url( $user_url , PHP_URL_HOST );
|
||||||
|
$webfinger = '@' . $user_name . '@' . $url_host;
|
||||||
|
return $webfinger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform comment url, replace #fragment with ?query
|
||||||
|
*
|
||||||
|
* 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] . '?comment-' . $comment_id[1];
|
||||||
|
return $comment_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine AP audience of incoming object
|
||||||
|
* @param string $object
|
||||||
|
* @return string audience
|
||||||
|
*/
|
||||||
|
function get_audience( $object ) {
|
||||||
|
if ( in_array( AS_PUBLIC, $object['to'] ) ) {
|
||||||
|
return 'public';
|
||||||
|
}
|
||||||
|
if ( in_array( AS_PUBLIC, $object['cc'] ) ) {
|
||||||
|
return 'unlisted';//is unlisted even relevant?
|
||||||
|
}
|
||||||
|
if ( !in_array( AS_PUBLIC, $object['to'] ) && !in_array( AS_PUBLIC, $object['cc'] ) ) {
|
||||||
|
$author_post_url = get_author_posts_url( $object['user_id'] );
|
||||||
|
if ( in_array( $author_post_url, $object['cc'] ) ) {
|
||||||
|
return 'followers_only';
|
||||||
|
}
|
||||||
|
if ( in_array( $author_post_url, $object['to'] ) ) {
|
||||||
|
return 'private';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -61,7 +61,12 @@ class Activity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function from_comment( $object ) {
|
public function from_comment( $object ) {
|
||||||
|
$this->object = $object;
|
||||||
|
$this->published = $object['published'];
|
||||||
|
$this->actor = $object['attributedTo'];
|
||||||
|
$this->id = $object['id'] . '-activity';
|
||||||
|
$this->cc = $object['cc'];
|
||||||
|
$this->tag = $object['tag'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function to_comment() {
|
public function to_comment() {
|
||||||
|
|
167
includes/model/class-comment.php
Normal file
167
includes/model/class-comment.php
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
<?php
|
||||||
|
namespace Activitypub\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityPub Comment Class
|
||||||
|
*
|
||||||
|
* @author Django Doucet
|
||||||
|
*/
|
||||||
|
class Comment {
|
||||||
|
private $comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the class
|
||||||
|
*/
|
||||||
|
public function __construct( $comment = null ) {
|
||||||
|
$this->comment = $comment;
|
||||||
|
|
||||||
|
$this->comment_author_url = \get_author_posts_url( $this->comment->user_id );
|
||||||
|
$this->safe_comment_id = $this->generate_comment_id();
|
||||||
|
$this->inReplyTo = $this->generate_parent_url();
|
||||||
|
$this->contentWarning = $this->generate_content_warning();
|
||||||
|
$this->permalink = $this->generate_permalink();
|
||||||
|
$this->cc_recipients = $this->generate_recipients();
|
||||||
|
$this->tags = $this->generate_tags();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call( $method, $params ) {
|
||||||
|
$var = \strtolower( \substr( $method, 4 ) );
|
||||||
|
|
||||||
|
if ( \strncasecmp( $method, 'get', 3 ) === 0 ) {
|
||||||
|
return $this->$var;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( \strncasecmp( $method, 'set', 3 ) === 0 ) {
|
||||||
|
$this->$var = $params[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function to_array() {
|
||||||
|
$comment = $this->comment;
|
||||||
|
|
||||||
|
$array = array(
|
||||||
|
'id' => \Activitypub\Model\Comment::normalize_comment_id( $comment ),
|
||||||
|
'type' => 'Note',
|
||||||
|
'published' => \date( 'Y-m-d\TH:i:s\Z', \strtotime( $comment->comment_date_gmt ) ),
|
||||||
|
'attributedTo' => $this->comment_author_url,
|
||||||
|
'summary' => $this->contentWarning,
|
||||||
|
'inReplyTo' => $this->inReplyTo,
|
||||||
|
'content' => $comment->comment_content,
|
||||||
|
'contentMap' => array(
|
||||||
|
\strstr( \get_locale(), '_', true ) => $comment->comment_content,
|
||||||
|
),
|
||||||
|
'source' => \get_comment_link( $comment ),
|
||||||
|
'url' => \get_comment_link( $comment ),//link for mastodon
|
||||||
|
'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ),//audience logic
|
||||||
|
'cc' => $this->cc_recipients,
|
||||||
|
'tag' => $this->tags,
|
||||||
|
);
|
||||||
|
|
||||||
|
return \apply_filters( 'activitypub_comment', $array );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function to_json() {
|
||||||
|
return \wp_json_encode( $this->to_array(), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_QUOT );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate_comment_author_link() {
|
||||||
|
return \get_author_posts_url( $this->comment->comment_author );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate_permalink() {
|
||||||
|
$comment = $this->comment;
|
||||||
|
$permalink = \get_comment_link( $comment );
|
||||||
|
|
||||||
|
// replace 'trashed' for delete activity
|
||||||
|
return \str_replace( '__trashed', '', $permalink );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What is status is being replied to
|
||||||
|
* Comment ID or Post ID
|
||||||
|
*/
|
||||||
|
public function generate_parent_url() {
|
||||||
|
$comment = $this->comment;
|
||||||
|
$parent_comment = \get_comment( $comment->comment_parent );
|
||||||
|
if ( $parent_comment ) {
|
||||||
|
//reply to local (received) comment
|
||||||
|
$inReplyTo = \get_comment_meta( $comment->comment_parent, 'source_url', true );
|
||||||
|
} else {
|
||||||
|
//reply to local post
|
||||||
|
$inReplyTo = \get_permalink( $comment->comment_post_ID );
|
||||||
|
}
|
||||||
|
return $inReplyTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate Content Warning from peer
|
||||||
|
* If peer used CW let's just copy it
|
||||||
|
* TODO: Move to preprocess_comment / row_actions
|
||||||
|
* Add option for wrapping CW in Details/Summary markup
|
||||||
|
* Figure out some CW syntax: [shortcode-style], {brackets-style}?
|
||||||
|
* So it can be inserted into reply textbox, and removed or modified at will
|
||||||
|
*/
|
||||||
|
public function generate_content_warning() {
|
||||||
|
$comment = $this->comment;
|
||||||
|
$contentWarning = null;
|
||||||
|
$parent_comment = \get_comment( $comment->comment_parent );
|
||||||
|
if ( $parent_comment ) {
|
||||||
|
//get (received) comment
|
||||||
|
$ap_object = \unserialize( \get_comment_meta( $comment->comment_parent, 'ap_object', true ) );
|
||||||
|
if ( isset( $ap_object['object']['summary'] ) ) {
|
||||||
|
$contentWarning = $ap_object['object']['summary'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*$summary = \get_comment_meta( $this->comment->comment_ID, 'summary', true ) ;
|
||||||
|
if ( !empty( $summary ) ) {
|
||||||
|
$contentWarning = \Activitypub\add_summary( $summary );
|
||||||
|
} */
|
||||||
|
return $contentWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Who is being replied to
|
||||||
|
*/
|
||||||
|
public function generate_recipients() {
|
||||||
|
//TODO Add audience logic get parent audience
|
||||||
|
$recipients = array( AS_PUBLIC );
|
||||||
|
$mentions = \get_comment_meta( $this->comment->comment_ID, 'mentions', true ) ;
|
||||||
|
if ( !empty( $mentions ) ) {
|
||||||
|
foreach ($mentions as $mention) {
|
||||||
|
$recipients[] = $mention['href'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mention user being replied to
|
||||||
|
*/
|
||||||
|
public function generate_tags() {
|
||||||
|
$mentions = \get_comment_meta( $this->comment->comment_ID, 'mentions', true ) ;
|
||||||
|
if ( !empty( $mentions ) ) {
|
||||||
|
foreach ($mentions as $mention) {
|
||||||
|
$mention_tags[] = array(
|
||||||
|
'type' => 'Mention',
|
||||||
|
'href' => $mention['href'],
|
||||||
|
'name' => '@' . $mention['name'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $mention_tags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform comment url, replace #fragment with ?query
|
||||||
|
*
|
||||||
|
* 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 ) {
|
||||||
|
$comment_id = explode( '#comment-', \get_comment_link( $comment ) );
|
||||||
|
$comment_id = $comment_id[0] . '?comment-' . $comment_id[1];
|
||||||
|
return $comment_id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -395,25 +395,63 @@ class Inbox {
|
||||||
*/
|
*/
|
||||||
public static function handle_create( $object, $user_id ) {
|
public static function handle_create( $object, $user_id ) {
|
||||||
$meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] );
|
$meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] );
|
||||||
|
$avatar_url = null;
|
||||||
|
$audience = \Activitypub\get_audience( $object );
|
||||||
|
|
||||||
$comment_post_id = \url_to_postid( $object['object']['inReplyTo'] );
|
//Determine parent post and/or parent comment
|
||||||
|
$comment_post_ID = $object_parent = $object_parent_ID = 0;
|
||||||
// save only replys and reactions
|
if ( isset( $object['object']['inReplyTo'] ) ) {
|
||||||
if ( ! $comment_post_id ) {
|
$comment_post_ID = \url_to_postid( $object['object']['inReplyTo'] );
|
||||||
return false;
|
//if not a direct reply to a post, remote post parent
|
||||||
|
if ( $comment_post_ID === 0 ) {
|
||||||
|
//verify if reply to a local or remote received comment
|
||||||
|
$object_parent_ID = \Activitypub\url_to_commentid( \esc_url_raw( $object['object']['inReplyTo'] ) );
|
||||||
|
if ( !is_null( $object_parent_ID ) ) {
|
||||||
|
//replied to a local comment (which has a post_ID)
|
||||||
|
$object_parent = get_comment( $object_parent_ID );
|
||||||
|
$comment_post_ID = $object_parent->comment_post_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//not all implementaions use url
|
||||||
|
if ( isset( $object['object']['url'] ) ) {
|
||||||
|
$source_url = \esc_url_raw( $object['object']['url'] );
|
||||||
|
} else {
|
||||||
|
//could also try $object['object']['source']?
|
||||||
|
$source_url = \esc_url_raw( $object['object']['id'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no name is set use peer username
|
||||||
|
if ( !empty( $meta['name'] ) ) {
|
||||||
|
$name = \esc_attr( $meta['name'] );
|
||||||
|
} else {
|
||||||
|
$name = \esc_attr( $meta['preferredUsername'] );
|
||||||
|
}
|
||||||
|
// if avatar is set
|
||||||
|
if ( !empty( $meta['icon']['url'] ) ) {
|
||||||
|
$avatar_url = \esc_attr( $meta['icon']['url'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
//Only create WP_Comment for public replies to local posts
|
||||||
|
if ( ( in_array( AS_PUBLIC, $object['to'] )
|
||||||
|
|| in_array( AS_PUBLIC, $object['cc'] ) )
|
||||||
|
&& ( !empty( $comment_post_ID )
|
||||||
|
|| !empty ( $object_parent )
|
||||||
|
) ) {
|
||||||
|
|
||||||
$commentdata = array(
|
$commentdata = array(
|
||||||
'comment_post_ID' => $comment_post_id,
|
'comment_post_ID' => $comment_post_ID,
|
||||||
'comment_author' => \esc_attr( $meta['name'] ),
|
'comment_author' => $name,
|
||||||
'comment_author_url' => \esc_url_raw( $object['actor'] ),
|
'comment_author_url' => \esc_url_raw( $object['actor'] ),
|
||||||
'comment_content' => \wp_filter_kses( $object['object']['content'] ),
|
'comment_content' => \wp_filter_kses( $object['object']['content'] ),
|
||||||
'comment_type' => '',
|
'comment_type' => 'activitypub',
|
||||||
'comment_author_email' => '',
|
'comment_author_email' => '',
|
||||||
'comment_parent' => 0,
|
'comment_parent' => $object_parent_ID,
|
||||||
'comment_meta' => array(
|
'comment_meta' => array(
|
||||||
'source_url' => \esc_url_raw( $object['object']['url'] ),
|
'ap_object' => \serialize( $object ),
|
||||||
'avatar_url' => \esc_url_raw( $meta['icon']['url'] ),
|
'source_url' => $source_url,
|
||||||
|
'avatar_url' => $avatar_url,
|
||||||
'protocol' => 'activitypub',
|
'protocol' => 'activitypub',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -430,6 +468,8 @@ class Inbox {
|
||||||
|
|
||||||
// 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 );
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function extract_recipients( $data ) {
|
public static function extract_recipients( $data ) {
|
||||||
|
|
|
@ -15,6 +15,8 @@ class Webfinger {
|
||||||
public static function init() {
|
public static function init() {
|
||||||
\add_action( 'rest_api_init', array( '\Activitypub\Rest\Webfinger', 'register_routes' ) );
|
\add_action( 'rest_api_init', array( '\Activitypub\Rest\Webfinger', 'register_routes' ) );
|
||||||
\add_action( 'webfinger_user_data', array( '\Activitypub\Rest\Webfinger', 'add_webfinger_discovery' ), 10, 3 );
|
\add_action( 'webfinger_user_data', array( '\Activitypub\Rest\Webfinger', 'add_webfinger_discovery' ), 10, 3 );
|
||||||
|
\add_action( 'webfinger_lookup', array( '\Activitypub\Rest\Webfinger', 'webfinger_lookup' ), 10, 3 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,4 +119,36 @@ class Webfinger {
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebFinger Lookup to find user uri
|
||||||
|
*
|
||||||
|
* @param string $resource the WebFinger resource
|
||||||
|
*/
|
||||||
|
public static function webfinger_lookup( $webfinger ) {
|
||||||
|
$activity_profile = null;
|
||||||
|
if ( \substr($webfinger, 0, 1) === '@' ) {
|
||||||
|
$webfinger = substr( $webfinger, 1 );
|
||||||
|
}
|
||||||
|
$url_host = \explode( '@', $webfinger );
|
||||||
|
$webfinger_query = 'https://' . \end( $url_host ) . '/.well-known/webfinger?resource=acct%3A' . \urlencode( $webfinger );
|
||||||
|
|
||||||
|
$response = \wp_safe_remote_get( $webfinger_query );
|
||||||
|
if ( ! is_wp_error( $response ) ) {
|
||||||
|
$ap_link = json_decode( $response['body'] );
|
||||||
|
if ( isset( $ap_link->links ) ) {
|
||||||
|
foreach ( $ap_link->links as $link ) {
|
||||||
|
if ( !property_exists( $link, 'type' ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( isset( $link->type ) && $link->type === 'application/activity+json' ) {
|
||||||
|
$activity_profile['href'] = $link->href;
|
||||||
|
$activity_profile['name'] = $webfinger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $activity_profile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue