diff --git a/inc/activities/like.php b/inc/activities/like.php index 0aedccc..21fa2ba 100644 --- a/inc/activities/like.php +++ b/inc/activities/like.php @@ -9,17 +9,81 @@ function handle_outbox( $actor, $activity ) { if ( !array_key_exists( 'object', $activity ) ) { return new \WP_Error( 'invalid_activity', - __( 'Expected an object', 'activitypub' ), + __( 'Expected an object', 'pterotype' ), array( 'status' => 400 ) ); } $object = $activity['object']; + if ( !array_key_exists( 'id', $object ) ) { + return new \WP_Error( + 'invalid_object', + __( 'Expected an id', 'pterotype' ), + array( 'status' => 400 ) + ); + } $object_row = \objects\upsert_object( $object ); $actor_id = \actors\get_actor_id( $actor ); - $res = \likes\create_like( $actor_id, $object_row->id ); + $res = \likes\create_local_actor_like( $actor_id, $object_row->id ); if ( is_wp_error( $res ) ) { return $res; } + if ( \objects\is_local_object( $object ) ) { + $activity_id = \activities\get_activity_id( $activity['id'] ); + if ( !$activity_id ) { + return new \WP_Error( + 'not_found', + __( 'Activity not found', 'pterotype' ), + array( 'status' => 404 ) + ); + } + $object_id = $object_row->id; + \likes\record_like( $object_id, $activity_id ); + } + return $activity; +} + +function handle_inbox( $actor, $activity ) { + if ( !array_key_exists( 'id', $activity ) ) { + return new \WP_Error( + 'invalid_activity', + __( 'Expected an id', 'pterotype' ), + array( 'status' => 400 ) + ); + } + if ( !array_key_exists( 'object', $activity ) ) { + return new \WP_Error( + 'invalid_activity', + __( 'Expected an object', 'pterotype' ), + array( 'status' => 400 ) + ); + } + $object = $activity['object']; + if ( !array_key_exists( 'id', $object ) ) { + return new \WP_Error( + 'invalid_object', + __( 'Expected an id', 'pterotype' ), + array( 'status' => 400 ) + ); + } + if ( \objects\is_local_object( $object ) ) { + $activity_id = \activities\get_activity_id( $activity['id'] ); + if ( !$activity_id ) { + return new \WP_Error( + 'not_found', + __( 'Activity not found', 'pterotype' ), + array( 'status' => 404 ) + ); + } + $object_id = \objects\get_object_id( $object['id'] ); + if ( !$object_id ) { + return new \WP_Error( + 'not_found', + __( 'Object not found', 'pterotype' ), + array( 'status' => 404 ) + ); + } + \likes\record_like( $object_id, $activity_id ); + } return $activity; } ?> diff --git a/inc/api.php b/inc/api.php index d4678ff..47fe28a 100644 --- a/inc/api.php +++ b/inc/api.php @@ -6,6 +6,8 @@ require_once plugin_dir_path( __FILE__ ) . '/outbox.php'; require_once plugin_dir_path( __FILE__ ) . '/objects.php'; require_once plugin_dir_path( __FILE__ ) . '/activities.php'; require_once plugin_dir_path( __FILE__ ) . '/following.php'; +require_once plugin_dir_path( __FILE__ ) . '/likes.php'; +require_once plugin_dir_path( __FILE__ ) . '/shares.php'; function get_actor( $request ) { $actor = $request['actor']; @@ -38,6 +40,16 @@ function get_following( $request ) { return \following\get_following_collection( $actor_slug ); } +function get_likes( $request ) { + $object_id = $request['object']; + return \likes\get_likes_collection( $object_id ); +} + +function get_shares( $request ) { + $object_id = $request['object']; + return \shares\get_shares_collection( $object_id ); +} + function register_routes() { register_rest_route( 'pterotype/v1', '/actor/(?P[a-zA-Z0-9-]+)/outbox', array( 'methods' => 'POST', @@ -59,9 +71,17 @@ function register_routes() { 'methods' => 'GET', 'callback' => __NAMESPACE__ . '\get_activity', ) ); - register_rest_route( 'pterotype/v1', '/actor/(?P[a-zA-Z0-9-]+)', array( + register_rest_route( 'pterotype/v1', '/actor/(?P[a-zA-Z0-9-]+)/following', array( 'methods' => 'GET', 'callback' => __NAMESPACE__ . '\get_following', ) ); + register_rest_route( 'pterotype/v1', '/object/(?P[0-9]+)/likes', array( + 'methods' => 'GET', + 'callback' => __NAMESPACE__ . '\get_likes', + ) ); + register_rest_route( 'pterotype/v1', '/object/(?P[0-9]+)/shares', array( + 'methods' => 'GET', + 'callback' => __NAMESPACE__ . '\get_shares', + ) ); } ?> diff --git a/inc/following.php b/inc/following.php index 7d8cd9b..3a1d9ef 100644 --- a/inc/following.php +++ b/inc/following.php @@ -56,7 +56,7 @@ function get_following_collection( $actor_slug ) { ), ARRAY_A ); - if ( !$object ) { + if ( !$objects ) { $objects = array(); } $collection = \collections\make_ordered_collection( $objects ); diff --git a/inc/likes.php b/inc/likes.php index 607f7d0..e56adee 100644 --- a/inc/likes.php +++ b/inc/likes.php @@ -1,13 +1,49 @@ insert( - 'pterotype_likes', array( 'actor_id' => $actor_id, 'object_id' => $object_id ) + '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( + '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( + $wpdb->prepare( + ' + SELECT activity FROM pterotype_object_likes + JOIN pterotype_activities ON like_id = pterotype_activities.id + WHERE object_id = %d + ', + $object_id + ), + ARRAY_A + ); + if ( !$likes ) { + $likes = array(); + } + $collection = \collections\make_ordered_collection( $likes ); + $collection['id'] = get_rest_url( null, sprintf( + '/pterotype/v1/object/%d/likes', $object_id + ) ); + return $collection; +} ?> diff --git a/inc/migrations.php b/inc/migrations.php index b1e35d3..32ba251 100644 --- a/inc/migrations.php +++ b/inc/migrations.php @@ -23,6 +23,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' ); + apply_migration( '0.0.4', 'migration_0_0_4' ); update_option( 'pterotype_previously_migrated_version', PTEROTYPE_VERSION ); } @@ -185,7 +186,36 @@ function migration_0_0_3() { PRIMARY KEY (object_id, shared_by), FOREIGN KEY shares_object_fk(object_id) REFERENCES pterotype_objects(id), - FOREIGN KEY shares_activity_fk(object_id) + FOREIGN KEY shares_activity_fk(announce_id) + REFERENCES pterotype_activities(id) + ) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + " + ); +} + +function migration_0_0_4() { + global $wpdb; + /* + pterotype_actor_likes stores things that local actors have liked + */ + $wpdb->query( + " + ALTER TABLE pterotype_likes RENAME pterotype_actor_likes; + " + ); + /* + pterotype_object_likes stores likes about local objects + */ + $wpdb->query( + " + CREATE TABLE pterotype_object_likes( + object_id INT UNSIGNED NOT NULL, + like_id INT UNSIGNED NOT NULL, + PRIMARY KEY (object_id, like_id), + FOREIGN KEY o_likes_object_fk(object_id) + REFERENCES pterotype_objects(id), + FOREIGN KEY o_likes_activity_fk(like_id) REFERENCES pterotype_activities(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/inc/outbox.php b/inc/outbox.php index 7ba1d6f..b74273e 100644 --- a/inc/outbox.php +++ b/inc/outbox.php @@ -31,6 +31,10 @@ function handle_activity( $actor_slug, $activity ) { array( 'status' => 400 ) ); } + $res = persist_activity( $actor_slug, $activity ); + if ( is_wp_error( $res ) ) { + return $res; + } switch ( $activity['type'] ) { case 'Create': $activity = \activities\create\handle_outbox( $actor_slug, $activity ); @@ -102,8 +106,8 @@ function handle_activity( $actor_slug, $activity ) { if ( is_wp_error( $activity ) ) { return $activity; } - $activity = deliver_activity( $activity ); - return persist_activity( $actor_slug, $activity ); + deliver_activity( $activity ); + return $res; } function get_outbox( $actor_slug ) { @@ -137,6 +141,7 @@ function deliver_activity( $activity ) { function persist_activity( $actor_slug, $activity ) { global $wpdb; + $activity = \activities\strip_private_fields( $activity ); $activity = \activities\create_local_activity( $activity ); $activity_id = $wpdb->insert_id; $actor_id = \actors\get_actor_id( $actor_slug ); diff --git a/inc/shares.php b/inc/shares.php index fce2530..0f160fb 100644 --- a/inc/shares.php +++ b/inc/shares.php @@ -1,6 +1,8 @@ insert( @@ -12,4 +14,27 @@ function add_share( $object_id, $activity_id ) { '%d' ); } + +function get_shares_collection( $object_id ) { + global $wpdb; + $shares = $wpdb->get_results( + $wpdb->prepare( + ' + SELECT activity FROM pterotype_shares + JOIN pterotype_activities ON announce_id = pterotype_activities.id + WHERE object_id = %d + ', + $object_id + ), + ARRAY_A + ); + if ( !$shares ) { + $shares = array(); + } + $collection = \collections\make_ordered_collection( $shares ); + $collection['id'] = get_rest_url( null, sprintf( + '/pterotype/v1/object/%d/shares', $object_id + ) ); + return $collection; +} ?> diff --git a/pterotype.php b/pterotype.php index 82187f7..5db8d84 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.3' ); +define( 'PTEROTYPE_VERSION', '0.0.4' ); function pterotype_init() { do_action( 'pterotype_init' );