Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
aaa4f5d
Optimize and align validate_object methods across handlers
obenland Oct 2, 2025
fcafb53
Add undo support for Like and Announce activities
pfefferle Oct 6, 2025
a47d8e5
Improve error handling in handle_undo method
pfefferle Oct 6, 2025
49b6b9b
Merge branch 'trunk' into add/inbox-undo
pfefferle Oct 6, 2025
7d887e2
Update includes/collection/class-inbox.php
pfefferle Oct 6, 2025
2ad1a01
Update includes/collection/class-inbox.php
pfefferle Oct 6, 2025
101feac
Refactor Follow activity handling in Inbox
pfefferle Oct 6, 2025
167c27a
Add validation for required object attributes in Undo
pfefferle Oct 6, 2025
c08f808
Bump @wordpress/editor from 14.31.0 to 14.32.0 (#2283)
dependabot[bot] Oct 3, 2025
62a27de
Bump @wordpress/env from 10.31.0 to 10.32.0 (#2280)
dependabot[bot] Oct 3, 2025
8e7c78e
Bump @wordpress/edit-post from 8.31.0 to 8.32.0 (#2282)
dependabot[bot] Oct 3, 2025
7fa5d3c
Fix Prettify workflow for pull requests from forks (#2277)
obenland Oct 3, 2025
140daa8
Tests: Avoid using `runInSeparateProcess` where possible (#2275)
obenland Oct 3, 2025
6a1755a
Update builds (again) (#2285)
pfefferle Oct 3, 2025
83d5cbf
Reorganize test directory structure (#2289)
obenland Oct 6, 2025
1cd87cd
Fix: issue #2286 – typo, improved quoting explanation, add missing i1…
Jiwoon-Kim Oct 6, 2025
85922f3
Use `bp_members_get_user_url` if available for author URLs (#2292)
pfefferle Oct 6, 2025
5d0a1cb
Exclude build asset files from PHP GitHub Actions triggers (#2293)
pfefferle Oct 6, 2025
6da01bc
Merge branch 'trunk' into optimize-validate-object-functions
pfefferle Oct 6, 2025
dbb7e59
Merge branch 'optimize-validate-object-functions' into add/inbox-undo
pfefferle Oct 6, 2025
79d43fa
Relax validation for Undo activity object
pfefferle Oct 6, 2025
7d5b074
Fix handling of missing post in Inbox::undo and update test
pfefferle Oct 6, 2025
46e6e76
Merge branch 'trunk' into add/inbox-undo
pfefferle Oct 6, 2025
57ff2aa
Merge branch 'trunk' into add/inbox-undo
pfefferle Oct 6, 2025
51d0165
Refactor and expand Undo activity handling and tests
pfefferle Oct 6, 2025
8ffd025
Add changelog
matticbot Oct 6, 2025
9528537
Update includes/collection/class-inbox.php
pfefferle Oct 7, 2025
9f7d359
Refactor and simplify Undo handler tests
pfefferle Oct 7, 2025
c17271e
Merge branch 'trunk' into add/inbox-undo
pfefferle Oct 7, 2025
9abbea6
Refactor Undo handler tests for activity-based flows
pfefferle Oct 7, 2025
174a10e
Improve error message for unsupported Undo activities
pfefferle Oct 7, 2025
62fbb39
Add spacing after return statements in Inbox class
pfefferle Oct 7, 2025
b50f690
Refactor variable name in Inbox::undo method
pfefferle Oct 7, 2025
da2df7f
Refactor variable names in Follow case for clarity
pfefferle Oct 7, 2025
9a1022f
Update includes/collection/class-inbox.php
pfefferle Oct 7, 2025
a52f132
Add missing docblock spacing in trait-collection.php
pfefferle Oct 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/changelog/2295-from-description
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: changed

Extended inbox support for undoing Like, Create, and Announce activities, with refactored undo logic and improved activity persistence.
91 changes: 91 additions & 0 deletions includes/collection/class-inbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Activitypub\Activity\Activity;
use Activitypub\Activity\Base_Object;
use Activitypub\Comment;

use function Activitypub\is_activity_public;
use function Activitypub\object_to_uri;
Expand Down Expand Up @@ -140,4 +141,94 @@ public static function get( $guid, $user_id ) {

return \get_post( $post_id );
}

/**
* Get an inbox item by its GUID.
*
* @param string $guid The GUID of the inbox item.
*
* @return \WP_Post|\WP_Error The inbox item or WP_Error.
*/
public static function get_by_guid( $guid ) {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$post_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE guid=%s AND post_type=%s",
\esc_url( $guid ),
self::POST_TYPE
)
);

if ( ! $post_id ) {
return new \WP_Error(
'activitypub_inbox_item_not_found',
\__( 'Inbox item not found', 'activitypub' ),
array( 'status' => 404 )
);
}

return \get_post( $post_id );
}

/**
* Undo a received activity.
*
* @param string $id The ID of the inbox item to be removed.
*
* @return bool|\WP_Error True on success, WP_Error on failure.
*/
public static function undo( $id ) {
$inbox_item = self::get_by_guid( $id );

if ( \is_wp_error( $inbox_item ) ) {
// If inbox entry not found, return the error.
return $inbox_item;
}

$type = \get_post_meta( $inbox_item->ID, '_activitypub_activity_type', true );

switch ( $type ) {
case 'Follow':
$actor = \get_post_meta( $inbox_item->ID, '_activitypub_activity_remote_actor', true );
$remote_actor = Remote_Actors::get_by_uri( $actor );

if ( \is_wp_error( $remote_actor ) ) {
return $remote_actor;
}

return Followers::remove( $remote_actor, $inbox_item->post_author );

case 'Like':
case 'Create':
case 'Announce':
if ( ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS ) {
return new \WP_Error(
'activitypub_inbox_undo_interactions_disabled',
\__( 'Undo is not possible because incoming interactions are disabled.', 'activitypub' ),
array( 'status' => 403 )
);
}

$result = Comment::object_id_to_comment( esc_url_raw( $inbox_item->guid ) );

if ( empty( $result ) ) {
return new \WP_Error(
'activitypub_inbox_undo_comment_not_found',
\__( 'Undo is not possible because the comment was not found.', 'activitypub' ),
array( 'status' => 404 )
);
}

return \wp_delete_comment( $result, true );

default:
return new \WP_Error(
'activitypub_inbox_undo_unsupported',
// Translators: %s is the activity type.
\sprintf( \__( 'Undo is not supported for %s activities.', 'activitypub' ), $type ),
array( 'status' => 400 )
);
}
}
}
2 changes: 1 addition & 1 deletion includes/handler/class-inbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static function handle_inbox_requests( $data, $user_id, $type, $activity
*
* @param array $activity_types The activity types to persist in the inbox.
*/
$activity_types = \apply_filters( 'activitypub_persist_inbox_activity_types', array( 'Create', 'Update', 'Follow' ) );
$activity_types = \apply_filters( 'activitypub_persist_inbox_activity_types', array( 'Create', 'Update', 'Follow', 'Like', 'Announce' ) );
$activity_types = \array_map( 'Activitypub\camel_to_snake_case', $activity_types );

if ( ! \in_array( \strtolower( $type ), $activity_types, true ) ) {
Expand Down
39 changes: 6 additions & 33 deletions includes/handler/class-undo.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@

namespace Activitypub\Handler;

use Activitypub\Collection\Actors;
use Activitypub\Collection\Followers;
use Activitypub\Collection\Remote_Actors;
use Activitypub\Comment;
use Activitypub\Collection\Inbox as Inbox_Collection;

use function Activitypub\object_to_uri;

Expand All @@ -33,35 +30,11 @@ public static function init() {
* @param int|null $user_id The ID of the user who initiated the "Undo" activity.
*/
public static function handle_undo( $activity, $user_id ) {
$type = $activity['object']['type'];
$success = false;
$result = null;
$result = Inbox_Collection::undo( object_to_uri( $activity['object'] ) );

// Handle "Unfollow" requests.
if ( 'Follow' === $type ) {
$user_id = Actors::get_id_by_resource( object_to_uri( $activity['object']['object'] ) );

if ( ! \is_wp_error( $user_id ) ) {
$post = Remote_Actors::get_by_uri( object_to_uri( $activity['actor'] ) );

if ( ! \is_wp_error( $post ) ) {
$success = Followers::remove( $post, $user_id );
}
}
}

// Handle "Undo" requests for "Like" and "Create" activities.
if ( in_array( $type, array( 'Like', 'Create', 'Announce' ), true ) ) {
if ( ! ACTIVITYPUB_DISABLE_INCOMING_INTERACTIONS ) {
$object_id = object_to_uri( $activity['object'] );
$result = Comment::object_id_to_comment( esc_url_raw( $object_id ) );

if ( empty( $result ) ) {
$success = false;
} else {
$success = \wp_delete_comment( $result, true );
}
}
if ( $result && ! \is_wp_error( $result ) ) {
$success = true;
}

/**
Expand Down Expand Up @@ -99,11 +72,11 @@ public static function validate_object( $valid, $param, $request ) {
return false;
}

if ( ! \is_array( $activity['object'] ) ) {
if ( ! \is_array( $activity['object'] ) && ! \is_string( $activity['object'] ) ) {
return false;
}

if ( ! isset( $activity['object']['id'], $activity['object']['type'], $activity['object']['actor'], $activity['object']['object'] ) ) {
if ( \is_array( $activity['object'] ) && ! isset( $activity['object']['id'] ) ) {
return false;
}

Expand Down
2 changes: 2 additions & 0 deletions includes/rest/trait-collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ trait Collection {
*
* @param array $response The collection response array.
* @param \WP_REST_Request $request The request object.
*
* @return array|\WP_Error The response array with navigation links or WP_Error on invalid page.
*/
public function prepare_collection_response( $response, $request ) {
Expand Down Expand Up @@ -76,6 +77,7 @@ public function prepare_collection_response( $response, $request ) {
* that controllers can use to compose their full schema by passing in their item schema.
*
* @param array $item_schema Optional. The schema for the items in the collection. Default empty array.
*
* @return array The collection schema.
*/
public function get_collection_schema( $item_schema = array() ) {
Expand Down
Loading