Implement Undo activity
This commit is contained in:
parent
024e1557b7
commit
fd3afcbb16
201
inc/activities/undo.php
Normal file
201
inc/activities/undo.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
namespace activities\undo;
|
||||
|
||||
require_once plugin_dir_path( __FILE__ ) . '/../util.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/../activities.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/../actors.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/../objects.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/../likes.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/../following.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/../followers.php';
|
||||
|
||||
function handle_outbox( $actor_slug, $activity ) {
|
||||
$object = validate_undo( $activity );
|
||||
if ( is_wp_error( $object ) ) {
|
||||
return $object;
|
||||
}
|
||||
$actor_id = \actors\get_actor_id( $actor_slug );
|
||||
if ( !$actor_id ) {
|
||||
return new \WP_Error(
|
||||
'not_found',
|
||||
__( 'Actor not found', 'pterotype' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
switch ( $object['type'] ) {
|
||||
case 'Like':
|
||||
if ( !array_key_exists( 'object', $object ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_activity',
|
||||
__( 'Expected an "object" field', 'pterotype' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$liked_object_url = \util\get_id( $object['object'] );
|
||||
if ( !$liked_object_url ) {
|
||||
break;
|
||||
}
|
||||
$liked_object_id = \objects\get_object_id( $liked_object_url );
|
||||
if ( !$liked_object_id ) {
|
||||
break;
|
||||
}
|
||||
\likes\delete_local_actor_like( $actor_id, $liked_object_id );
|
||||
$like_id = \activities\get_activity_id( $object['id'] );
|
||||
if ( !$like_id ) {
|
||||
break;
|
||||
}
|
||||
\likes\delete_object_like( $liked_object_id, $like_id );
|
||||
break;
|
||||
case 'Block':
|
||||
if ( !array_key_exists( 'object', $object ) ) {
|
||||
break;
|
||||
}
|
||||
$blocked_object_url = \util\get_id( $object['object'] );
|
||||
if ( !$blocked_object_url ) {
|
||||
break;
|
||||
}
|
||||
$res = \blocks\delete_block( $actor_id, $blocked_object_url );
|
||||
if ( is_wp_error( $res ) ) {
|
||||
return $res;
|
||||
}
|
||||
break;
|
||||
case 'Follow':
|
||||
if ( !array_key_exists( 'object', $object ) ) {
|
||||
break;
|
||||
}
|
||||
$follow_object_url = \util\get_id( $object['object'] );
|
||||
if ( !$follow_object_url ) {
|
||||
break;
|
||||
}
|
||||
$follow_object_id = \objects\get_object_id( $follow_object_url );
|
||||
if ( !$follow_object_id ) {
|
||||
break;
|
||||
}
|
||||
\following\reject_follow( $actor_id, $follow_object_id );
|
||||
break;
|
||||
// TODO I should support Undoing these as well
|
||||
case 'Add':
|
||||
case 'Remove':
|
||||
case 'Accept':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return $activity;
|
||||
}
|
||||
|
||||
function handle_inbox( $actor_slug, $activity ) {
|
||||
$object = validate_undo( $activity );
|
||||
if ( is_wp_error( $object ) ) {
|
||||
return $object;
|
||||
}
|
||||
$actor_id = \actors\get_actor_id( $actor_slug );
|
||||
if ( !$actor_id ) {
|
||||
return new \WP_Error(
|
||||
'not_found',
|
||||
__( 'Actor not found', 'pterotype' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
switch( $object['type'] ) {
|
||||
case 'Like':
|
||||
if ( !array_key_exists( 'object', $object ) ) {
|
||||
break;
|
||||
}
|
||||
if ( \objects\is_local_object( $object['object'] ) ) {
|
||||
$object_url = \objects\get_object_id( $object['object'] );
|
||||
if ( !$object_url ) {
|
||||
break;
|
||||
}
|
||||
$object_id = \objects\get_object_id( $object_url );
|
||||
$like_id = \activities\get_activity_id( $object['id'] );
|
||||
if ( !$like_id ) {
|
||||
break;
|
||||
}
|
||||
\likes\delete_object_like( $object_id, $like_id );
|
||||
}
|
||||
break;
|
||||
case 'Follow':
|
||||
if ( !array_key_exists( 'actor', $object ) ) {
|
||||
break;
|
||||
}
|
||||
$follower = $object['actor'];
|
||||
\followers\remove_follower( $actor_slug, $follower );
|
||||
break;
|
||||
case 'Accept':
|
||||
if ( !array_key_exists( 'object', $object ) ) {
|
||||
break;
|
||||
}
|
||||
$accept_object = \util\dereference_object( $object['object'] );
|
||||
if ( is_wp_error( $object ) ) {
|
||||
break;
|
||||
}
|
||||
if ( array_key_exists( 'type', $accept_object ) && $accept_object['type'] === 'Follow' ) {
|
||||
if ( !array_key_exists( 'object', $accept_object ) ) {
|
||||
break;
|
||||
}
|
||||
$followed_object_url = \util\get_id( $accept_object['object'] );
|
||||
$followed_object_id = \objects\get_object_id( $followed_object_url );
|
||||
if ( !$followed_object_id ) {
|
||||
break;
|
||||
}
|
||||
// Put the follow request back into the PENDING state
|
||||
\following\request_follow( $actor_id, $followed_object_id );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return $activity;
|
||||
}
|
||||
|
||||
function validate_undo( $activity ) {
|
||||
if ( !array_key_exists( 'actor', $activity ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_activity',
|
||||
__( 'Expected an "actor" field', 'pterotype' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
if ( !array_key_exists( 'object', $activity ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_activity',
|
||||
__( 'Expected an "object" field', 'pterotype' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
$object = \util\dereference_object( $activity['object'] );
|
||||
if ( is_wp_error( $object ) ) {
|
||||
return $object;
|
||||
}
|
||||
if ( !array_key_exists( 'actor', $object ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_activity',
|
||||
__( 'Expected a "actor" field', 'pterotype' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
if ( !array_key_exists( 'id', $object ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_activity',
|
||||
__( 'Expected an "id" field', 'pterotype' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
if ( !\util\is_same_object( $activity['actor'], $object['actor'] ) ) {
|
||||
return new \WP_Error(
|
||||
'unauthorized',
|
||||
__( 'Unauthorzed Undo activity', 'pterotype' ),
|
||||
array( 'status' => 403 )
|
||||
);
|
||||
}
|
||||
if ( !array_key_exists( 'type', $object ) ) {
|
||||
return new \WP_Error(
|
||||
'invalid_activity',
|
||||
__( 'Expected a "type" field', 'pterotype' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
?>
|
@ -17,4 +17,15 @@ function create_block( $actor_id, $blocked_actor_url ) {
|
||||
return new \WP_Error( 'db_error', __( 'Error inserting block row', 'pterotype' ) );
|
||||
}
|
||||
}
|
||||
|
||||
function delete_block( $actor_id, $blocked_actor_url ) {
|
||||
global $wpdb;
|
||||
$res = $wpdb->delete(
|
||||
'pterotype_blocks',
|
||||
array( 'actor_id' => $actor_id, 'blocked_actor_url' => $blocked_actor_url )
|
||||
);
|
||||
if ( !$res ) {
|
||||
return new \WP_Error( 'db_error', __( 'Error inserting block row', 'pterotype' ) );
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -35,6 +35,40 @@ function add_follower( $actor_slug, $follower ) {
|
||||
);
|
||||
}
|
||||
|
||||
function remove_follower( $actor_slug, $follower ) {
|
||||
global $wpdb;
|
||||
$actor_id = \actors\get_actor_id( $actor_slug );
|
||||
if ( !$actor_id ) {
|
||||
return new \WP_Error(
|
||||
'not_found',
|
||||
__( 'Actor not found', 'pterotype' ),
|
||||
array( 'status' => 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 ) {
|
||||
return new \WP_Error(
|
||||
'not_found',
|
||||
__( 'Object not found', 'pterotype' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
$wpdb->delete(
|
||||
'pterotype_followers',
|
||||
array(
|
||||
'actor_id' => $actor_id,
|
||||
'object_id' = $object_id,
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
function get_followers_collection( $actor_slug ) {
|
||||
global $wpdb;
|
||||
$actor_id = \actors\get_actor_id( $actor_slug );
|
||||
|
@ -8,11 +8,12 @@ define( 'PTEROTYPE_FOLLOW_FOLLOWING', 'FOLLOWING' );
|
||||
|
||||
function request_follow( $actor_id, $object_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->insert(
|
||||
return $wpdb->replace(
|
||||
'pterotype_following',
|
||||
array( 'actor_id' => $actor_id,
|
||||
'object_id' => $object_id,
|
||||
'state' => PTEROTYPE_FOLLOW_PENDING
|
||||
array(
|
||||
'actor_id' => $actor_id,
|
||||
'object_id' => $object_id,
|
||||
'state' => PTEROTYPE_FOLLOW_PENDING
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ require_once plugin_dir_path( __FILE__ ) . '/activities/follow.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/accept.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/reject.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/announce.php';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/undo.php';
|
||||
|
||||
function handle_activity( $actor_slug, $activity ) {
|
||||
if ( !array_key_exists( 'type', $activity ) ) {
|
||||
@ -52,17 +53,11 @@ function handle_activity( $actor_slug, $activity ) {
|
||||
case 'Reject':
|
||||
$activity = \activities\reject\handle_inbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Add':
|
||||
// TODO not yet implemented
|
||||
break;
|
||||
case 'Remove':
|
||||
// TODO not yet implemented
|
||||
break;
|
||||
case 'Announce':
|
||||
$activity = \activities\announce\handle_inbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Undo':
|
||||
// TODO
|
||||
$activity = \activities\undo\handle_inbox( $actor_slug, $activity );
|
||||
break;
|
||||
}
|
||||
if ( is_wp_error( $activity ) ) {
|
||||
|
@ -12,6 +12,15 @@ function create_local_actor_like( $actor_id, $object_id ) {
|
||||
);
|
||||
}
|
||||
|
||||
function delete_local_actor_like( $actor_id, $object_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->delete(
|
||||
'pterotype_actor_likes',
|
||||
array( 'actor_id' => $actor_id, 'object_id' => $object_id ),
|
||||
'%d'
|
||||
);
|
||||
}
|
||||
|
||||
function record_like ( $object_id, $like_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->insert(
|
||||
@ -24,6 +33,18 @@ function record_like ( $object_id, $like_id ) {
|
||||
);
|
||||
}
|
||||
|
||||
function delete_object_like( $object_id, $like_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->delete(
|
||||
'pterotype_object_likes',
|
||||
array(
|
||||
'object_id' => $object_id,
|
||||
'like_id' => $like_id
|
||||
),
|
||||
'%d'
|
||||
);
|
||||
}
|
||||
|
||||
function get_likes_collection( $object_id ) {
|
||||
global $wpdb;
|
||||
$likes = $wpdb->get_results(
|
||||
|
@ -240,8 +240,18 @@ function make_tombstone( $object ) {
|
||||
}
|
||||
|
||||
function is_local_object( $object ) {
|
||||
if ( array_key_exists( 'id', $object ) ) {
|
||||
$parsed = parse_url( $object['id'] );
|
||||
if ( is_array( $object ) ) {
|
||||
if ( array_key_exists( 'id', $object ) ) {
|
||||
$parsed = parse_url( $object['id'] );
|
||||
if ( $parsed ) {
|
||||
$site_host = parse_url( get_site_url() )['host'];
|
||||
return $parsed['host'] === $site_host;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$parsed = parse_url( $object );
|
||||
if ( $parsed ) {
|
||||
$site_host = parse_url( get_site_url() )['host'];
|
||||
return $parsed['host'] === $site_host;
|
||||
|
@ -21,6 +21,7 @@ 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';
|
||||
require_once plugin_dir_path( __FILE__ ) . '/activities/undo.php';
|
||||
|
||||
function handle_activity( $actor_slug, $activity ) {
|
||||
// TODO handle authentication/authorization
|
||||
@ -69,7 +70,7 @@ function handle_activity( $actor_slug, $activity ) {
|
||||
$activity = \activities\block\handle_outbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Undo':
|
||||
// TODO
|
||||
$activity = \activities\undo\handle_outbox( $actor_slug, $activity );
|
||||
break;
|
||||
case 'Accept':
|
||||
$activity = \activities\accept\handle_inbox( $actor_slug, $activity );
|
||||
|
47
inc/util.php
Normal file
47
inc/util.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace util;
|
||||
|
||||
// TODO audit places throughout the repo where I need to dereference objects
|
||||
// (this is anywhere I access a field on an object, basically)
|
||||
|
||||
function dereference_object( $object ) {
|
||||
if ( is_array( $object ) ) {
|
||||
return $object;
|
||||
} else if ( filter_var( $object, FILTER_VALIDATE_URL ) ) {
|
||||
$response = wp_remote_get( $object );
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
if ( empty( $body ) ) {
|
||||
return new \WP_Error(
|
||||
'not_found',
|
||||
__( 'The object did not dereference to a valid object', 'pterotype' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
$body_array = json_decode( $body, true );
|
||||
return $body_array;
|
||||
} else {
|
||||
return new \WP_Error(
|
||||
'invalid_object',
|
||||
__( 'Not a valid ActivityPub object or reference', 'pterotype' ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function is_same_object( $object1, $object2 ) {
|
||||
return get_id( $object1 ) === get_id( $object2 );
|
||||
}
|
||||
|
||||
function get_id( $object ) {
|
||||
if ( is_array( $object ) ) {
|
||||
return array_key_exists( 'id', $object ) ?
|
||||
$object['id'] :
|
||||
null;
|
||||
} else {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
?>
|
Loading…
Reference in New Issue
Block a user