diff --git a/inc/activities/accept.php b/inc/activities/accept.php index 852541d..4cf425e 100644 --- a/inc/activities/accept.php +++ b/inc/activities/accept.php @@ -2,6 +2,7 @@ namespace activities\accept; require_once plugin_dir_path( __FILE__ ) . '/../following.php'; +require_once plugin_dir_path( __FILE__ ) . '/../followers.php'; require_once plugin_dir_path( __FILE__ ) . '/../objects.php'; require_once plugin_dir_path( __FILE__ ) . '/../actors.php'; @@ -37,4 +38,27 @@ function handle_inbox( $actor_slug, $activity ) { } return $activity; } + +function handle_outbox( $actor_slug, $activity ) { + if ( !array_key_exists( 'object', $activity ) ) { + return new \WP_Error( + 'invalid_activity', + __( 'Activity must have an "object" field', 'pterotype' ), + array( 'status' => 400 ) + ); + } + $object = $activity['object']; + if ( array_key_exists( 'type', $object ) ) { + switch ( $object['type'] ) { + case 'Follow': + if ( !array_key_exists( 'actor', $object ) ) { + break; + } + $follower = $object['actor']; + \followers\add_follower( $actor_slug, $follower ); + break; + } + } + return $activity; +} ?> diff --git a/inc/activities/follow.php b/inc/activities/follow.php index 42a08ee..2ab9495 100644 --- a/inc/activities/follow.php +++ b/inc/activities/follow.php @@ -28,17 +28,51 @@ function handle_inbox( $actor_slug, $activity ) { // For now, always Accept follow requests // in the future, implement a UI to either accept or reject // (or automatically accept if the user chooses to enable that setting) - $accept = make_accept( $actor_slug, $activity ); - if ( is_wp_error( $accept ) ) { - return $accept; - } - $res = \outbox\handle_activity( $actor_slug, $accept ); - if ( is_wp_error( $res ) ) { - return $res; + if ( actor_is_object( $actor_slug, $activity ) ) { + if ( !array_key_exists( 'actor', $activity ) ) { + return new \WP_Error( + 'invalid_activity', + __( 'Activity must have an "actor" field', 'pterotype' ), + array( 'status' => 400 ) + ); + } + $follower = $activity['actor']; + \objects\upsert_object( $follower ); + $accept = make_accept( $actor_slug, $activity ); + if ( is_wp_error( $accept ) ) { + return $accept; + } + $res = \outbox\handle_activity( $actor_slug, $accept ); + if ( is_wp_error( $res ) ) { + return $res; + } } return $activity; } +/* +Return true if the actor denoted by $actor_slug is the object of $activity +*/ +function actor_is_object( $actor_slug, $activity ) { + if ( !array_key_exists( 'object', $activity ) ) { + return false; + } + $actor = \actors\get_actor_by_slug( $actor_slug ); + if ( is_wp_error( $actor ) ) { + return false; + } + $object = $activity['object']; + if ( !array_key_exists( 'type', $object ) ) { + return false; + } + switch ( $object['type'] ) { + case 'Link': + return array_key_exists( 'href', $object ) && $object['href'] === $actor['id']; + default: + return array_key_exists( 'id', $object ) && $object['id'] === $actor['id']; + } +} + function make_accept( $actor_slug, $follow ) { if ( !array_key_exists( 'actor', $follow ) ) { return new \WP_Error( diff --git a/inc/actors.php b/inc/actors.php index c23c8fb..d25eff5 100644 --- a/inc/actors.php +++ b/inc/actors.php @@ -25,6 +25,11 @@ function get_actor_id( $slug ) { } function get_actor_from_row( $row ) { + if ( !$row ) { + return new \WP_Error( + 'not_found', __( 'Actor not found', 'pterotype' ), array( 'status' => 404 ) + ); + } switch ( $row->type ) { case "user": $user = get_user_by( 'slug', $row->slug ); diff --git a/inc/followers.php b/inc/followers.php new file mode 100644 index 0000000..f825fda --- /dev/null +++ b/inc/followers.php @@ -0,0 +1,37 @@ + 404 ) + ); + } + if ( !array_key_exists( 'id', $follower ) ) { + return new \WP_Error( + 'invalid_object', + __( 'Object must have an "id" field', 'pterotype' ), + array( 'status' => 400 ) + ); + } + $object_id = \objects\get_object_id( $follower['id'] ); + if ( !$object_id ) { + $row = \objects\upsert_object( $follower ); + $object_id = $row->id; + } + $wpdb->insert( + 'pterotype_followers', + array( + 'actor_id' => $actor_id, + 'object_id' = $object_id, + ); + ); +} +?> diff --git a/inc/migrations.php b/inc/migrations.php index 18c8a71..924c6b5 100644 --- a/inc/migrations.php +++ b/inc/migrations.php @@ -22,6 +22,7 @@ function run_migrations() { } apply_migration( '0.0.1', 'migration_0_0_1' ); apply_migration( '0.0.2', 'migration_0_0_2' ); + apply_migration( '0.0.3', 'migration_0_0_3' ); update_option( 'pterotype_previously_migrated_version', PTEROTYPE_VERSION ); } @@ -159,4 +160,22 @@ function migration_0_0_2() { " ); } + +function migration_0_0_3() { + global $wpdb; + $wpdb->query( + " + CREATE TABLE pterotype_followers( + actor_id INT UNSIGNED NOT NULL, + object_id INT UNSIGNED NOT NULL, + PRIMARY KEY (actor_id, object_id), + FOREIGN KEY following_actor_fk(actor_id) + REFERENCES pterotype_actors(id), + FOREIGN KEY following_object_fk(object_id) + REFERENCES pterotype_objects(id) + ) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + " + ); +} ?> diff --git a/inc/objects.php b/inc/objects.php index 8d27372..aae7f19 100644 --- a/inc/objects.php +++ b/inc/objects.php @@ -170,6 +170,13 @@ function get_object_by_activitypub_id( $activitypub_id ) { return json_decode( $object_json, true ); } +function get_object_id( $activitypub_id ) { + global $wpdb; + return $wpdb->get_var( $wpdb->prepare( + 'SELECT id FROM pterotype_objects WHERE activitypub_id = %s', $activitypub_id + ) ); +} + function delete_object( $object ) { global $wpdb; if ( !array_key_exists( 'id', $object ) ) { diff --git a/inc/outbox.php b/inc/outbox.php index 9a2384b..5c065dc 100644 --- a/inc/outbox.php +++ b/inc/outbox.php @@ -71,6 +71,9 @@ function handle_activity( $actor_slug, $activity ) { array( 'status' => 501 ) ); break; + case 'Accept': + $activity = \activities\accept\handle_inbox( $actor_slug, $activity ); + break; default: $create_activity = wrap_object_in_create( $activity ); if ( is_wp_error( $create_activity ) ) { diff --git a/pterotype.php b/pterotype.php index c4e911e..82187f7 100644 --- a/pterotype.php +++ b/pterotype.php @@ -4,7 +4,7 @@ Plugin Name: Pterotype */ require_once plugin_dir_path( __FILE__ ) . 'inc/init.php'; -define( 'PTEROTYPE_VERSION', '0.0.2' ); +define( 'PTEROTYPE_VERSION', '0.0.3' ); function pterotype_init() { do_action( 'pterotype_init' );