2023-12-25 22:35:04 +01:00
|
|
|
<?php
|
|
|
|
namespace Activitypub\Model;
|
|
|
|
|
|
|
|
use WP_Error;
|
|
|
|
|
|
|
|
use Activitypub\Activity\Base_Object;
|
2023-12-27 15:39:53 +01:00
|
|
|
use Activitypub\Collection\Followers;
|
2023-12-25 22:35:04 +01:00
|
|
|
use Activitypub\Collection\Users;
|
|
|
|
use Activitypub\Model\Follower;
|
2023-12-27 15:39:53 +01:00
|
|
|
|
2023-12-25 22:35:04 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ActivityPub Follow Class
|
|
|
|
*
|
|
|
|
* This Object represents a Follow object.
|
|
|
|
* There is no direct reference to a WordPress User here.
|
|
|
|
*
|
|
|
|
* @author André Menrath
|
|
|
|
*
|
|
|
|
* @see https://www.w3.org/TR/activitypub/#follow-activity-inbox
|
|
|
|
*/
|
|
|
|
class Follow_Request extends Base_Object {
|
|
|
|
const FOLLOW_REQUEST_POST_TYPE = 'ap_follow_request';
|
2023-12-27 15:39:53 +01:00
|
|
|
const FOLLOW_REQUEST_STATUS_APPROVED = 'approved';
|
|
|
|
const FOLLOW_REQUEST_STATUS_REJECTED = 'rejected';
|
2023-12-25 22:35:04 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Stores theinternal WordPress post id of the post of type ap_follow_request
|
2023-12-26 13:50:54 +01:00
|
|
|
*
|
2023-12-25 22:35:04 +01:00
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $_id;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The id/URI of the follower
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $actor;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The internal WordPress post id of the follower
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $_actor;
|
|
|
|
|
2024-01-04 16:02:14 +01:00
|
|
|
/**
|
|
|
|
* The status: 'approved', 'pending', 'rejected'<div class=""></div>
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $_status;
|
|
|
|
|
2023-12-25 22:35:04 +01:00
|
|
|
/**
|
|
|
|
* @param int $id
|
|
|
|
* @return Follow_Request $follow_request
|
|
|
|
*/
|
|
|
|
public static function get_from_array( $array ) {
|
|
|
|
$follow_request = self::init_from_array( $array );
|
|
|
|
if ( ! self::is_valid( $follow_request ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( ! $follow_request->get__id() ) {
|
|
|
|
$follow_request->set__id( self::get_follow_request_id_by_uri( $follow_request->get_id() ) );
|
|
|
|
}
|
|
|
|
return $follow_request;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the WordPress post id of the follow request by its id (URI)
|
|
|
|
*/
|
|
|
|
public static function get_follow_request_id_by_uri( $uri ) {
|
|
|
|
global $wpdb;
|
2023-12-26 13:50:54 +01:00
|
|
|
return $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE guid=%s", esc_sql( $uri ) ) );
|
2023-12-25 22:35:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the follow request is valid which means it fits to the already stored data.
|
2023-12-26 13:50:54 +01:00
|
|
|
*
|
2023-12-25 22:35:04 +01:00
|
|
|
* @param Follow_Request $follow_request The follow request to be checked.
|
|
|
|
* @return bool Whether the follow request is valid.
|
|
|
|
*/
|
|
|
|
public static function is_valid( $follow_request ) {
|
|
|
|
if ( self::class != get_class( $follow_request ) ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ( 'Follow' != $follow_request->get_type() ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-26 13:50:54 +01:00
|
|
|
$id = self::get_follow_request_id_by_uri( $follow_request->get_id() );
|
2023-12-25 22:35:04 +01:00
|
|
|
if ( ! $id || is_wp_error( $id ) ) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-12-26 13:50:54 +01:00
|
|
|
if ( self::FOLLOW_REQUEST_POST_TYPE != get_post_type( $id ) ) {
|
2023-12-25 22:35:04 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$post = get_post( $id );
|
|
|
|
$follower = get_post_parent( $id );
|
|
|
|
if ( $follower->guid != $follow_request->get_actor() ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param int $id
|
|
|
|
* @return Follow_Request $follow_request
|
|
|
|
*/
|
|
|
|
public static function from_wp_id( $id ) {
|
2023-12-26 13:50:54 +01:00
|
|
|
if ( self::FOLLOW_REQUEST_POST_TYPE != get_post_type( $id ) ) {
|
2023-12-25 22:35:04 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
$post = get_post( $id );
|
|
|
|
|
2023-12-26 13:50:54 +01:00
|
|
|
$follow_request = new static();
|
2023-12-25 22:35:04 +01:00
|
|
|
$follow_request->set_id( $post->guid );
|
|
|
|
$follow_request->set__id( $post->ID );
|
2024-01-04 16:02:14 +01:00
|
|
|
$follow_request->set__status( $post->post_status );
|
2023-12-25 22:35:04 +01:00
|
|
|
$follow_request->set_type( 'Follow' );
|
|
|
|
|
|
|
|
return $follow_request;
|
|
|
|
}
|
|
|
|
|
2023-12-27 15:39:53 +01:00
|
|
|
/**
|
|
|
|
* Get the internal ID of the follower who issues the follow request.
|
|
|
|
*/
|
|
|
|
private function get_follower_id() {
|
|
|
|
return wp_get_post_parent_id( $this->get__id() );
|
|
|
|
}
|
|
|
|
|
2023-12-25 22:35:04 +01:00
|
|
|
/**
|
|
|
|
* Save the current Follower-Object.
|
|
|
|
*
|
|
|
|
* @param Follower $follower
|
|
|
|
*
|
|
|
|
* @return Follow_Request|WP_Error The Follow_Request or an WP_Error.
|
|
|
|
*/
|
2023-12-27 16:45:23 +01:00
|
|
|
public static function save( $follower_id, $user_id, $activity_id ) {
|
2023-12-25 22:35:04 +01:00
|
|
|
$meta_input = array(
|
|
|
|
'activitypub_user_id' => $user_id,
|
|
|
|
);
|
|
|
|
|
|
|
|
$args = array(
|
|
|
|
'guid' => $activity_id,
|
|
|
|
'post_author' => 0,
|
|
|
|
'post_type' => self::FOLLOW_REQUEST_POST_TYPE,
|
|
|
|
'post_status' => 'pending',
|
|
|
|
'post_parent' => $follower_id,
|
|
|
|
'meta_input' => $meta_input,
|
2023-12-26 13:50:54 +01:00
|
|
|
'mime_type' => 'text/plain',
|
2023-12-25 22:35:04 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
$post_id = wp_insert_post( $args );
|
|
|
|
|
|
|
|
return self::from_wp_id( $post_id );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the user is allowed to handle this follow request.
|
2023-12-26 13:50:54 +01:00
|
|
|
*
|
2023-12-25 22:35:04 +01:00
|
|
|
* Usually needed for the ajax functions.
|
|
|
|
* @return bool Whether the user is allowed.
|
|
|
|
*/
|
|
|
|
public function can_handle_follow_request() {
|
2023-12-26 13:50:54 +01:00
|
|
|
$target_actor = get_post_meta( $this->get__id(), 'activitypub_user_id' );
|
|
|
|
if ( get_current_user_id() == $target_actor || current_user_can( 'manage_options' ) ) {
|
2023-12-25 22:35:04 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-12-27 15:39:53 +01:00
|
|
|
* Get the URL/URI of the Follower that issued the follow request.
|
|
|
|
*
|
|
|
|
* @return string The url/uri of the follower.
|
2023-12-25 22:35:04 +01:00
|
|
|
*/
|
2023-12-27 15:39:53 +01:00
|
|
|
private function get_follower_actor() {
|
|
|
|
$follower_id = wp_get_post_parent_id( $this->get__id() );
|
|
|
|
$follower = Follower::init_from_cpt( get_post( $follower_id ) );
|
|
|
|
return $follower->get_id();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save the status of the follow request.
|
|
|
|
*
|
|
|
|
* @param string $status The new status of the follow request.
|
|
|
|
*/
|
|
|
|
private function save_follow_request_status( $status ) {
|
2023-12-26 13:50:54 +01:00
|
|
|
wp_update_post(
|
2023-12-25 22:35:04 +01:00
|
|
|
array(
|
|
|
|
'ID' => $this->get__id(),
|
2023-12-27 15:39:53 +01:00
|
|
|
'post_status' => $status,
|
2023-12-25 22:35:04 +01:00
|
|
|
)
|
|
|
|
);
|
2023-12-27 15:39:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reject the follow request.
|
|
|
|
*/
|
|
|
|
public function reject() {
|
|
|
|
$user_id = get_post_meta( $this->get__id(), 'activitypub_user_id', true );
|
|
|
|
$actor = $this->get_follower_actor();
|
|
|
|
|
|
|
|
Followers::remove_follow_relationship( $user_id, $actor );
|
|
|
|
|
|
|
|
$this->save_follow_request_status( self::FOLLOW_REQUEST_STATUS_REJECTED );
|
|
|
|
|
2023-12-27 22:41:32 +01:00
|
|
|
$this->send_response( 'Reject', $user_id );
|
2023-12-27 15:39:53 +01:00
|
|
|
|
2023-12-25 22:35:04 +01:00
|
|
|
$this->delete();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-12-27 15:39:53 +01:00
|
|
|
* Approve the follow request.
|
2023-12-25 22:35:04 +01:00
|
|
|
*/
|
|
|
|
public function approve() {
|
2023-12-27 15:39:53 +01:00
|
|
|
$user_id = get_post_meta( $this->get__id(), 'activitypub_user_id', true );
|
|
|
|
$follower_id = $this->get_follower_id();
|
|
|
|
|
|
|
|
Followers::add_follow_relationship( $user_id, $follower_id );
|
|
|
|
|
|
|
|
$this->save_follow_request_status( self::FOLLOW_REQUEST_STATUS_APPROVED );
|
|
|
|
|
2023-12-27 22:41:32 +01:00
|
|
|
$this->send_response( 'Accept', $user_id, $follower_id );
|
2023-12-25 22:35:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-12-27 15:39:53 +01:00
|
|
|
* Delete the follow request.
|
2023-12-26 13:50:54 +01:00
|
|
|
*
|
2023-12-25 22:35:04 +01:00
|
|
|
* This should only be called after it has been rejected.
|
|
|
|
*/
|
|
|
|
public function delete() {
|
|
|
|
wp_delete_post( $this->get__id() );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepere the sending of the follow request response and hand it over to the sending handler.
|
2023-12-27 22:41:32 +01:00
|
|
|
*
|
|
|
|
* @param string $type The Activity type of the response: 'Accept', or 'Reject'.
|
|
|
|
* @param int|string $user_id The user id of who gets followed.
|
|
|
|
* @param int $follwer_id The internal follower id.
|
2023-12-25 22:35:04 +01:00
|
|
|
*/
|
2023-12-27 22:41:32 +01:00
|
|
|
public function send_response( $type, $user_id = null, $follower_id = null ) {
|
|
|
|
if ( ! $user_id ) {
|
|
|
|
$user_id = get_post_meta( $this->get__id(), 'activitypub_user_id', true );
|
|
|
|
}
|
2023-12-25 22:35:04 +01:00
|
|
|
|
2023-12-27 22:41:32 +01:00
|
|
|
if ( ! $follower_id ) {
|
|
|
|
$follower_id = $this->get_follower_id();
|
|
|
|
}
|
2023-12-26 13:50:54 +01:00
|
|
|
|
2023-12-27 15:39:53 +01:00
|
|
|
$follower_inbox = get_post_field( 'post_content_filtered', $follower_id );
|
2023-12-25 22:35:04 +01:00
|
|
|
|
2023-12-27 15:39:53 +01:00
|
|
|
// Reconstruct the follow_object.
|
|
|
|
$follow_object = array(
|
2023-12-25 22:35:04 +01:00
|
|
|
'id' => $this->get_id(),
|
|
|
|
'type' => $this->get_type(),
|
2024-01-04 16:02:14 +01:00
|
|
|
'actor' => get_post_field( 'guid', $follower_id ),
|
2023-12-25 22:35:04 +01:00
|
|
|
);
|
|
|
|
|
2023-12-27 22:41:32 +01:00
|
|
|
do_action( 'activitypub_send_follow_response', $user_id, $follower_inbox, $follow_object, $type );
|
2023-12-25 22:35:04 +01:00
|
|
|
}
|
|
|
|
}
|