pterotype/inc/outbox.php
2018-09-19 08:54:07 -04:00

163 lines
5.5 KiB
PHP

<?php
/*
When an Activity is received (i.e. POSTed) to an Actor's outbox, the server must:
0. Make sure the request is authenticated
1. Add the Activity to the Actor's outbox collection in the DB
2. Deliver the Activity to the appropriate inboxes based on the received Activity
This involves discovering all the inboxes, including nested ones if the target
is a collection, deduplicating inboxes, and the POSTing the Activity to each
target inbox.
3. Perform side effects as necessary
*/
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';
require_once plugin_dir_path( __FILE__ ) . '/activities/delete.php';
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_slug, $activity ) {
// TODO handle authentication/authorization
if ( !array_key_exists( 'type', $activity ) ) {
return new \WP_Error(
'invalid_activity',
__( 'Invalid activity', 'activitypub' ),
array( 'status' => 400 )
);
}
switch ( $activity['type'] ) {
case 'Create':
$activity = \activities\create\handle_outbox( $actor_slug, $activity );
break;
case 'Update':
$activity = \activities\update\handle_outbox( $actor_slug, $activity );
break;
case 'Delete':
$activity = \activities\delete\handle_outbox( $actor_slug, $activity );
break;
case 'Follow':
$activity = \activities\follow\handle_outbox( $actor_slug, $activity );
break;
case 'Add':
return new \WP_Error(
'not_implemented',
__( 'The Add activity has not been implemented', 'activitypub' ),
array( 'status' => 501 )
);
break;
case 'Remove':
return new \WP_Error(
'not_implemented',
__( 'The Remove activity has not been implemented', 'activitypub' ),
array( 'status' => 501 )
);
break;
case 'Like':
$activity = \activities\like\handle_outbox( $actor_slug, $activity );
break;
case 'Block':
$activity = \activities\block\handle_outbox( $actor_slug, $activity );
break;
case 'Undo':
return new \WP_Error(
'not_implemented',
__( 'The Undo activity has not been implemented', 'activitypub' ),
array( 'status' => 501 )
);
break;
default:
$create_activity = wrap_object_in_create( $activity );
if ( is_wp_error( $create_activity ) ) {
return $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_slug, $activity );
}
function get_outbox( $actor_slug ) {
global $wpdb;
// 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
) );
}
function deliver_activity( $activity ) {
\deliver\deliver_activity( $activity );
$activity = \activities\strip_private_fields( $activity );
return $activity;
}
function persist_activity( $actor_slug, $activity ) {
global $wpdb;
$activity = \activities\create_local_activity( $activity );
$activity_id = $wpdb->insert_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'] );
return $response;
}
function wrap_object_in_create( $actor_slug, $object ) {
$actor = \actors\get_actor_by_slug( $actor_slug );
if ( is_wp_error( $actor ) ) {
return $actor;
}
$activity = array(
'@context' => 'https://www.w3.org/ns/activitystreams',
'type' => 'Create',
'actor' => $actor,
'object' => $object
);
return $activity;
}
function create_outbox_table() {
global $wpdb;
$wpdb->query(
"
CREATE TABLE IF NOT EXISTS activitypub_outbox (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
actor_id INT UNSIGNED NOT NULL,
activity_id INT UNSIGNED NOT NULL,
FOREIGN KEY outbox_activity_fk(activity_id)
REFERENCES activitypub_activities(id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
"
);
}
?>