pterotype/inc/outbox.php
2018-09-19 11:16:41 -04:00

166 lines
5.7 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', 'pterotype' ),
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', 'pterotype' ),
array( 'status' => 501 )
);
break;
case 'Remove':
return new \WP_Error(
'not_implemented',
__( 'The Remove activity has not been implemented', 'pterotype' ),
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', 'pterotype' ),
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 pterotype_activitypub_activities.activity
FROM pterotype_activitypub_outbox
JOIN pterotype_activitypub_actors
ON pterotype_activitypub_actors.id
= pterotype_activitypub_outbox.actor_id
JOIN pterotype_activitypub_activities
ON pterotype_activitypub_activities.id
= pterotype_activitypub_outbox.activity_id
WHERE pterotype_pterotype_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( 'pterotype_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 pterotype_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 pterotype_activitypub_activities(id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
"
);
}
?>