Make a number of changes after understanding the spec better
These include: - enable querying activities and objects by ActivityPub id - GET the outbox
This commit is contained in:
parent
fdbe64c136
commit
1e808f3eca
@ -12,7 +12,20 @@ function get_activity( $id ) {
|
||||
);
|
||||
}
|
||||
$activity = json_decode( $activity_json, true );
|
||||
$activity['id'] = get_activity_url( $id );
|
||||
return $activity;
|
||||
}
|
||||
|
||||
function get_activity_by_activitypub_id( $activitypub_id ) {
|
||||
global $wpdb;
|
||||
$activity_json = $wpdb->get_var( $wpdb->prepare(
|
||||
'SELECT activity FROM activitypub_activities WHERE id = %s', $activitypub_id
|
||||
) );
|
||||
if ( is_null( $activity_json ) ) {
|
||||
return new \WP_Error(
|
||||
'not_found', __( 'Activity not found', 'activitypub' ), array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
$activity = json_decode( $activity_json, true );
|
||||
return $activity;
|
||||
}
|
||||
|
||||
@ -28,26 +41,37 @@ function strip_private_fields( $activity ) {
|
||||
|
||||
function persist_activity( $activity ) {
|
||||
global $wpdb;
|
||||
$wpdb->insert(
|
||||
'activitypub_activities', array( 'activity' => wp_json_encode( $activity ) )
|
||||
);
|
||||
$activity["id"] = get_activity_url( $wpdb->insert_id );
|
||||
if ( !array_key_exists( 'id', $activity ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_activity',
|
||||
__( 'Activity must have an "id" field', 'activitypub' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$activitypub_id = $activity['id'];
|
||||
$wpdb->insert( 'activitypub_activities', array(
|
||||
'activitypub_id' => $activitypub_id,
|
||||
'activity' => wp_json_encode( $activity )
|
||||
) );
|
||||
return $activity;
|
||||
}
|
||||
|
||||
function get_activity_url( $id ) {
|
||||
return get_rest_url( null, sprintf( '/activitypub/v1/activity/%d', $id ) );
|
||||
}
|
||||
|
||||
function create_activities_table() {
|
||||
global $wpdb;
|
||||
$wpdb->query(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS activitypub_activities (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
activitypub_id TEXT UNIQUE NOT NULL,
|
||||
activity TEXT NOT NULL
|
||||
);
|
||||
"
|
||||
);
|
||||
$wpdb->query(
|
||||
"
|
||||
CREATE UNIQUE INDEX ACTIVITYPUB_ID_INDEX
|
||||
ON activitypub_activities (activitypub_id);
|
||||
"
|
||||
);
|
||||
}
|
||||
?>
|
||||
|
@ -29,10 +29,10 @@ function handle_outbox( $actor, $activity ) {
|
||||
);
|
||||
}
|
||||
$object = $activity['object'];
|
||||
$actor_id = $activity['actor'];
|
||||
$object['attributedTo'] = $actor_id;
|
||||
$attributed_actor = $activity['actor'];
|
||||
$object['attributedTo'] = $attributed_actor;
|
||||
reconcile_receivers( $object, $activity );
|
||||
scrub_object( $object );
|
||||
$object = scrub_object( $object );
|
||||
$object = \objects\create_object( $object );
|
||||
if ( is_wp_error( $object ) ) {
|
||||
return $object;
|
||||
@ -68,8 +68,9 @@ function copy_field_value( $field, $from, &$to ) {
|
||||
}
|
||||
}
|
||||
|
||||
function scrub_object( &$object ) {
|
||||
function scrub_object( $object ) {
|
||||
unset( $object['bcc'] );
|
||||
unset( $object['bto'] );
|
||||
return $object;
|
||||
}
|
||||
?>
|
||||
|
@ -26,7 +26,7 @@ function handle_outbox( $actor, $activity ) {
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$existing_object = \objects\get_object_from_url( $update_object['id'] );
|
||||
$existing_object = \objects\get_object_by_actvitypub_id( $update_object['id'] );
|
||||
if ( is_wp_error( $existing_object ) ) {
|
||||
return $existing_object;
|
||||
}
|
||||
|
17
inc/api.php
17
inc/api.php
@ -11,10 +11,15 @@ function get_actor( $request ) {
|
||||
return \actors\get_actor_by_slug( $actor );
|
||||
}
|
||||
|
||||
function handle_outbox( $request ) {
|
||||
$actor = $request['actor'];
|
||||
function post_to_outbox( $request ) {
|
||||
$actor_slug = $request['actor'];
|
||||
$activity = json_decode( $request->get_body(), true );
|
||||
return \outbox\handle_activity( $actor, $activity );
|
||||
return \outbox\handle_activity( $actor_slug, $activity );
|
||||
}
|
||||
|
||||
function get_outbox( $request ) {
|
||||
$actor_slug = $request['actor'];
|
||||
return \outbox\get_outbox( $actor_slug );
|
||||
}
|
||||
|
||||
function get_object( $request ) {
|
||||
@ -30,7 +35,11 @@ function get_activity( $request ) {
|
||||
function register_routes() {
|
||||
register_rest_route( 'activitypub/v1', '/actor/(?P<actor>[a-zA-Z0-9-]+)/outbox', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => __NAMESPACE__ . '\handle_outbox',
|
||||
'callback' => __NAMESPACE__ . '\post_to_outbox',
|
||||
) );
|
||||
register_rest_route( 'activitypub/v1', '/actor/(?P<actor>[a-zA-Z0-9-]+/outbox', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => __NAMESPACE__ . '\get_outbox',
|
||||
) );
|
||||
register_rest_route( 'activitypub/v1', '/actor/(?P<actor>[a-zA-Z0-9-]+)', array(
|
||||
'methods' => 'GET',
|
||||
|
12
inc/collections.php
Normal file
12
inc/collections.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace collections;
|
||||
|
||||
function make_ordered_collection( $objects ) {
|
||||
$ordered_collection = array(
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'type' => 'OrderedCollection',
|
||||
'totalItems' => count( $objects ),
|
||||
'orderedItems' => $objects
|
||||
);
|
||||
}
|
||||
?>
|
@ -57,6 +57,7 @@ function forward_activity( $activity ) {
|
||||
}
|
||||
|
||||
function persist_activity( $activity ) {
|
||||
global $wpdb;
|
||||
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,22 @@ namespace objects;
|
||||
|
||||
function create_object( $object ) {
|
||||
global $wpdb;
|
||||
$res = $wpdb->insert(
|
||||
'activitypub_objects', array( 'object' => wp_json_encode( $object ) )
|
||||
);
|
||||
if ( !array_key_exists( 'id', $object ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_object',
|
||||
__( 'Object must have an "id" field', 'activitypub' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$res = $wpdb->insert( 'activitypub_objects', array(
|
||||
'activitypub_id' => $object['id'],
|
||||
'object' => wp_json_encode( $object )
|
||||
) );
|
||||
if ( !$res ) {
|
||||
return new \WP_Error(
|
||||
'db_error', __( 'Failed to insert object row', 'activitypub' )
|
||||
);
|
||||
}
|
||||
$object['id'] = get_object_url( $wpdb->insert_id );
|
||||
return $object;
|
||||
}
|
||||
|
||||
@ -23,11 +30,10 @@ function update_object( $object ) {
|
||||
if ( !array_key_exists( 'id', $object ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_object',
|
||||
__( 'Object must have an "id" parameter', 'activitypub' ),
|
||||
__( 'Object must have an "id" field', 'activitypub' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$id = get_id_from_url( $object['id'] );
|
||||
$object_json = wp_json_encode( $object );
|
||||
$res = $wpdb->update(
|
||||
'activitypub_object',
|
||||
@ -52,9 +58,20 @@ function get_object( $id ) {
|
||||
'not_found', __( 'Object not found', 'activitypub' ), array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
$object = json_decode( $object_json, true );
|
||||
$object['id'] = get_object_url( $id );
|
||||
return $object;
|
||||
return json_decode( $object_json, true );
|
||||
}
|
||||
|
||||
function get_object_by_activitypub_id( $activitypub_id ) {
|
||||
global $wpdb;
|
||||
$object_json = $wpdb->get_var( $wpdb->prepare(
|
||||
'SELECT object FROM activitypub_objects WHERE activitypub_id = %s', $activitypub_id
|
||||
) );
|
||||
if ( is_null( $object_json ) ) {
|
||||
return new \WP_Error(
|
||||
'not_found', __( 'Object not found', 'activitypub' ), array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
return json_decode( $object_json, true );
|
||||
}
|
||||
|
||||
function delete_object( $object ) {
|
||||
@ -62,51 +79,34 @@ function delete_object( $object ) {
|
||||
if ( !array_key_exists( 'id', $object ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_object',
|
||||
__( 'Object must have an "id" parameter', 'activitypub' ),
|
||||
__( 'Object must have an "id" field', 'activitypub' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$id = get_id_from_url( $object['id'] );
|
||||
$res = $wpdb->delete( 'activitypub_objects', array( 'id' => $id ), '%d' );
|
||||
$activitypub_id = $object['id'];
|
||||
$res = $wpdb->delete( 'activitypub_objects', array( 'activitypub_id' => $id ), '%s' );
|
||||
if ( !$res ) {
|
||||
return new \WP_Error( 'db_error', __( 'Error deleting object', 'activitypub' ) );
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
function get_id_from_url( $url ) {
|
||||
global $wpdb;
|
||||
$matches = array();
|
||||
$found = preg_match(
|
||||
get_rest_url( null, '/activitypub/v1/object/(.+)' ), $url, $matches );
|
||||
if ( $found === 0 || count( $matches ) != 2 ) {
|
||||
return new \WP_Error(
|
||||
'invalid_url',
|
||||
sprintf( '%s %s', $url, __( 'is not a valid object url', 'activitypub' ) ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$id = $matches[1];
|
||||
return $id;
|
||||
}
|
||||
|
||||
function get_object_from_url( $url ) {
|
||||
return get_object( get_id_from_url( $url ) );
|
||||
}
|
||||
|
||||
function get_object_url( $id ) {
|
||||
return get_rest_url( null, sprintf( '/activitypub/v1/object/%d', $id ) );
|
||||
}
|
||||
|
||||
function create_object_table() {
|
||||
global $wpdb;
|
||||
$wpdb->query(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS activitypub_objects (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
activitypub_id TEXT UNIQUE NOT NULL,
|
||||
object TEXT NOT NULL
|
||||
);
|
||||
"
|
||||
);
|
||||
$wpdb->query(
|
||||
"
|
||||
CREATE UNIQUE INDEX ACTIVITYPUB_ID_INDEX
|
||||
ON activitypub_objects (activitypub_id);
|
||||
"
|
||||
);
|
||||
}
|
||||
?>
|
||||
|
@ -13,6 +13,7 @@ When an Activity is received (i.e. POSTed) to an Actor's outbox, the server must
|
||||
namespace outbox;
|
||||
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/actors.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/deliver.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/create.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/update.php';
|
||||
@ -21,7 +22,7 @@ require_once plugin_dir_path( __FILE__ ) . '/activities/like.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/follow.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/block.php';
|
||||
|
||||
function handle_activity( $actor, $activity ) {
|
||||
function handle_activity( $actor_slug, $activity ) {
|
||||
// TODO handle authentication/authorization
|
||||
if ( !array_key_exists( 'type', $activity ) ) {
|
||||
return new \WP_Error(
|
||||
@ -32,16 +33,16 @@ function handle_activity( $actor, $activity ) {
|
||||
}
|
||||
switch ( $activity['type'] ) {
|
||||
case 'Create':
|
||||
$activity = \activities\create\handle_outbox( $actor, $activity );
|
||||
$activity = \activities\create\handle_outbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Update':
|
||||
$activity = \activities\update\handle_outbox( $actor, $activity );
|
||||
$activity = \activities\update\handle_outbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Delete':
|
||||
$activity = \activities\delete\handle_outbox( $actor, $activity );
|
||||
$activity = \activities\delete\handle_outbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Follow':
|
||||
$activity = \activities\follow\handle_outbox( $actor, $activity );
|
||||
$activity = \activities\follow\handle_outbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Add':
|
||||
return new \WP_Error(
|
||||
@ -58,10 +59,10 @@ function handle_activity( $actor, $activity ) {
|
||||
);
|
||||
break;
|
||||
case 'Like':
|
||||
$activity = \activities\like\handle_outbox( $actor, $activity );
|
||||
$activity = \activities\like\handle_outbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Block':
|
||||
$activity = \activities\block\handle_outbox( $actor, $activity );
|
||||
$activity = \activities\block\handle_outbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Undo':
|
||||
return new \WP_Error(
|
||||
@ -75,24 +76,37 @@ function handle_activity( $actor, $activity ) {
|
||||
if ( is_wp_error( $create_activity ) ) {
|
||||
return $create_activity;
|
||||
}
|
||||
$activity = \activities\create\handle_outbox( $actor, $create_activity );
|
||||
$activity = \activities\create\handle_outbox( $actor_slug, $create_activity );
|
||||
break;
|
||||
}
|
||||
if ( is_wp_error( $activity ) ) {
|
||||
return $activity;
|
||||
}
|
||||
$activity = deliver_activity( $activity );
|
||||
return persist_activity( $actor, $activity );
|
||||
return persist_activity( $actor_slug, $activity );
|
||||
}
|
||||
|
||||
function get_outbox( $actor_id ) {
|
||||
function get_outbox( $actor_slug ) {
|
||||
global $wpdb;
|
||||
$activities = $wpdb->get_results( $wpdb->prepare(
|
||||
"
|
||||
SELECT * FROM activitypub_outbox WHERE
|
||||
// TODO what sort of joins should these be?
|
||||
$results = $wpdb->get_results( $wpdb->prepare(
|
||||
"
|
||||
SELECT activitypub_activities.activity FROM activitypub_outbox
|
||||
JOIN activitypub_actors
|
||||
ON activitypub_actors.id = activitypub_outbox.actor_id
|
||||
JOIN activitypub_activities
|
||||
ON activitypub_activities.id = activitypub_outbox.activity_id
|
||||
WHERE activitypub_outbox.actor_id = %d
|
||||
",
|
||||
$actor_id
|
||||
) );
|
||||
// TODO return PagedCollection if $activites is too big
|
||||
return \collections\make_ordered_collection( array_map(
|
||||
function ( $result) {
|
||||
return json_decode( $result->activity, true);
|
||||
},
|
||||
$results
|
||||
) );
|
||||
// $wpdb->num_rows will hold the number of results, once this implements paging
|
||||
}
|
||||
|
||||
function deliver_activity( $activity ) {
|
||||
@ -101,15 +115,15 @@ function deliver_activity( $activity ) {
|
||||
return $activity;
|
||||
}
|
||||
|
||||
function persist_activity( $actor, $activity ) {
|
||||
function persist_activity( $actor_slug, $activity ) {
|
||||
global $wpdb;
|
||||
$activity = \activities\persist_activity( $activity );
|
||||
$activity_id = $wpdb->insert_id;
|
||||
$wpdb->insert( 'activitypub_outbox',
|
||||
array(
|
||||
'actor' => $actor,
|
||||
'activity_id' => $activity_id,
|
||||
) );
|
||||
$actor_id = \actors\get_actor_id( $actor_slug );
|
||||
$wpdb->insert( 'activitypub_outbox', array(
|
||||
'actor_id' => $actor_id,
|
||||
'activity_id' => $activity_id,
|
||||
) );
|
||||
$response = new \WP_REST_Response();
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', $activity['id'] );
|
||||
|
Loading…
Reference in New Issue
Block a user