WIP: Add Event Sources Logic (ActivityPub follows) #86

Draft
linos wants to merge 36 commits from event_sources into main
6 changed files with 178 additions and 83 deletions
Showing only changes of commit d77cdf1430 - Show all commits

View file

@ -92,6 +92,16 @@ class Event_Sources {
)
);
\register_post_meta(
self::POST_TYPE,
'activitypub_inbox',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => 'sanitize_url',
)
);
\register_post_meta(
self::POST_TYPE,
'event_source_utilize_announces',
@ -229,4 +239,140 @@ class Event_Sources {
$post_id = Event_Source::get_wp_post_from_activitypub_actor_id( $event_source );
return wp_delete_post( $post_id, true );
}
/**
* Queue a hook to run async.
*
* @param string $hook The hook name.
* @param array $args The arguments to pass to the hook.
* @param string $unqueue_hook Optional a hook to unschedule before queuing.
* @return void|bool Whether the hook was queued.
*/
public static function queue( $hook, $args, $unqueue_hook = null ) {
if ( $unqueue_hook ) {
$hook_timestamp = wp_next_scheduled( $unqueue_hook, $args );
if ( $hook_timestamp ) {
wp_unschedule_event( $hook_timestamp, $unqueue_hook, $args );
}
}
if ( wp_next_scheduled( $hook, $args ) ) {
return;
}
return \wp_schedule_single_event( \time(), $hook, $args );
}
/**
* Prepare to follow an ActivityPub actor via a scheduled event.
*
* @param string $actor The ActivityPub actor.
*
* @return bool|WP_Error Whether the event was queued.
*/
public static function queue_follow_actor( $actor ) {
$queued = self::queue(
'event_bridge_for_activitypub_follow',
$actor,
'event_bridge_for_activitypub_unfollow'
);
return $queued;
}
/**
* Follow an ActivityPub actor via the Application user.
*
* @param string $actor_id The ID/URL of the Actor.
*/
public static function activitypub_follow_actor( $actor_id ) {
$post_id = Event_Source::get_wp_post_from_activitypub_actor_id( $actor_id );
if ( ! $post_id ) {
return;
}
$actor = Event_Source::init_from_cpt( get_post( $post_id ) );
if ( is_wp_error( $actor ) ) {
return $actor;
}
$inbox = $actor->get_shared_inbox();
$to = $actor->get_id();
$application = new \Activitypub\Model\Application();
$activity = new \Activitypub\Activity\Activity();
$activity->set_type( 'Follow' );
$activity->set_to( null );
$activity->set_cc( null );
$activity->set_actor( $application->get_id() );
$activity->set_object( $to );
$activity->set_id( $actor . '#follow-' . \preg_replace( '~^https?://~', '', $to ) );
$activity = $activity->to_json();
\Activitypub\safe_remote_post( $inbox, $activity, \Activitypub\Collection\Actors::APPLICATION_USER_ID );
}
/**
* Prepare to unfollow an actor via a scheduled event.
*
* @param string $actor The ActivityPub actor ID.
*
* @return bool|WP_Error Whether the event was queued.
*/
public static function queue_unfollow_actor( $actor ) {
$queued = self::queue(
'event_bridge_for_activitypub_unfollow',
$actor,
'event_bridge_for_activitypub_follow'
);
return $queued;
}
/**
* Unfollow an ActivityPub actor.
*
* @param string $actor_id The ActivityPub actor ID.
*/
public static function activitypub_unfollow_actor( $actor_id ) {
$post_id = Event_Source::get_wp_post_from_activitypub_actor_id( $actor_id );
if ( ! $post_id ) {
return;
}
$actor = Event_Source::init_from_cpt( get_post( $post_id ) );
if ( is_wp_error( $actor ) ) {
return $actor;
}
$inbox = $actor->get_shared_inbox();
$to = $actor->get_id();
$application = new \Activitypub\Model\Application();
if ( is_wp_error( $inbox ) ) {
return $inbox;
}
$activity = new \Activitypub\Activity\Activity();
$activity->set_type( 'Undo' );
$activity->set_to( null );
$activity->set_cc( null );
$activity->set_actor( $application->get_id() );
$activity->set_object(
array(
'type' => 'Follow',
'actor' => $actor,
'object' => $to,
'id' => $to,
)
);
$activity->set_id( $actor . '#unfollow-' . \preg_replace( '~^https?://~', '', $to ) );
$activity = $activity->to_json();
\Activitypub\safe_remote_post( $inbox, $activity, \Activitypub\Collection\Actors::APPLICATION_USER_ID );
}
}

View file

@ -9,7 +9,6 @@
namespace Event_Bridge_For_ActivityPub\ActivityPub\Model;
use Activitypub\Activity\Actor;
use Activitypub\Webfinger;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources;
use WP_Error;
@ -118,6 +117,32 @@ class Event_Source extends Actor {
return true;
}
/**
* Update the post meta.
*/
protected function get_post_meta_input() {
$meta_input = array();
$meta_input['activitypub_inbox'] = $this->get_shared_inbox();
$meta_input['activitypub_actor_json'] = $this->to_json();
return $meta_input;
}
/**
* Get the shared inbox, with a fallback to the inbox.
*
* @return string|null The URL to the shared inbox, the inbox or null.
*/
public function get_shared_inbox() {
if ( ! empty( $this->get_endpoints()['sharedInbox'] ) ) {
return $this->get_endpoints()['sharedInbox'];
} elseif ( ! empty( $this->get_inbox() ) ) {
return $this->get_inbox();
}
return null;
}
/**
* Save the current Event Source object to Database within custom post type.
*

View file

@ -9,11 +9,8 @@ namespace Event_Bridge_For_ActivityPub;
use Activitypub\Activity\Extended_Object\Event;
use Activitypub\Collection\Actors;
use Activitypub\Http;
use Exception;
use function Activitypub\get_remote_metadata_by_actor;
use function register_post_type;
/**
* Class for handling and saving the ActivityPub event sources (i.e. follows).
@ -30,83 +27,9 @@ class Event_Sources {
* Constructor.
*/
public function __construct() {
\add_action( 'init', array( $this, 'register_post_meta' ) );
\add_action( 'activitypub_inbox', array( $this, 'handle_activitypub_inbox' ), 15, 3 );
}
/**
* Register the post type used to store the external event sources (i.e., followed ActivityPub actors).
*/
public static function register_post_type() {
register_post_type(
self::POST_TYPE,
array(
'labels' => array(
'name' => _x( 'Event Sources', 'post_type plural name', 'activitypub' ),
'singular_name' => _x( 'Event Source', 'post_type single name', 'activitypub' ),
),
'public' => false,
'hierarchical' => false,
'rewrite' => false,
'query_var' => false,
'delete_with_user' => false,
'can_export' => true,
'supports' => array(),
)
);
\register_post_meta(
self::POST_TYPE,
'activitypub_inbox',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => 'sanitize_url',
)
);
\register_post_meta(
self::POST_TYPE,
'activitypub_errors',
array(
'type' => 'string',
'single' => false,
'sanitize_callback' => function ( $value ) {
if ( ! is_string( $value ) ) {
throw new Exception( 'Error message is no valid string' );
}
return esc_sql( $value );
},
)
);
\register_post_meta(
self::POST_TYPE,
'activitypub_user_id',
array(
'type' => 'string',
'single' => false,
'sanitize_callback' => function ( $value ) {
return esc_sql( $value );
},
)
);
\register_post_meta(
self::POST_TYPE,
'activitypub_actor_json',
array(
'type' => 'string',
'single' => true,
'sanitize_callback' => function ( $value ) {
return sanitize_text_field( $value );
},
)
);
}
/**
* Handle the ActivityPub Inbox.
*

View file

@ -20,6 +20,7 @@ use Event_Bridge_For_ActivityPub\Admin\General_Admin_Notices;
use Event_Bridge_For_ActivityPub\Admin\Health_Check;
use Event_Bridge_For_ActivityPub\Admin\Settings_Page;
use Event_Bridge_For_ActivityPub\Integrations\Event_Plugin;
use Event_Bridge_For_ActivityPub\ActivityPub\Collection\Event_Sources as Event_Sources_Collection;
require_once ABSPATH . 'wp-admin/includes/plugin.php';
@ -183,7 +184,7 @@ class Setup {
}
add_action( 'init', array( Health_Check::class, 'init' ) );
add_action( 'init', array( Event_Sources::class, 'register_post_type' ) );
add_action( 'init', array( Event_Sources_Collection::class, 'register_post_type' ) );
// Check if the minimum required version of the ActivityPub plugin is installed.
if ( ! version_compare( $this->activitypub_plugin_version, EVENT_BRIDGE_FOR_ACTIVITYPUB_ACTIVITYPUB_PLUGIN_MIN_VERSION ) ) {

View file

@ -95,7 +95,7 @@ class Event_Sources extends WP_List_Table {
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$event_sources = Event_Sources_Collection::get_event_sources_with_count($per_page, $page_num, $args );
$event_sources = Event_Sources_Collection::get_event_sources_with_count( $per_page, $page_num, $args );
$actors = $event_sources['actors'];
$counter = $event_sources['total'];

View file

@ -28,7 +28,7 @@ $table = new \Event_Bridge_For_ActivityPub\Table\Event_Sources();
<!-- Button that triggers ThickBox -->
<a href="#TB_inline?width=600&height=400&inlineId=Event_Bridge_For_ActivityPub_add_new_source" class="thickbox button button-primary">
<?php esc_html_e( 'Add new', 'event-bridge-for-activitypub' ); ?>
<?php esc_html_e( 'Add Event Source', 'event-bridge-for-activitypub' ); ?>
</a>
<!-- ThickBox content (hidden initially) -->
@ -37,8 +37,8 @@ $table = new \Event_Bridge_For_ActivityPub\Table\Event_Sources();
<p> <?php esc_html_e( 'Here you can enter either a Fediverse handle (@username@example.social), URL of an ActivityPub Account (https://example.social/user/username) or instance URL.', 'event-bridge-for-activitypub' ); ?> </p>
<form method="post" action="options.php">
<?php \settings_fields( 'event-bridge-for-activitypub-event-sources' ); ?>
<input type="text" name="event_bridge_for_activitypub_event_source" id="event_bridge_for_activitypub_event_source" value="test">
<?php \submit_button(); ?>
<input type="text" name="event_bridge_for_activitypub_event_source" id="event_bridge_for_activitypub_event_source" value="">
<?php \submit_button( __( 'Add Event Source', 'event-bridge-for-activitypub' ) ); ?>
</form>
</div>
</div>