Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Outbox Collection #593

Draft
wants to merge 35 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5565c26
init
pfefferle Dec 8, 2023
7448cd6
outbox should not be public!
pfefferle Dec 8, 2023
6ac167f
add basic `to_activity` function
pfefferle Dec 11, 2023
704030c
Merge branch 'master' into add/outbox-collection
pfefferle Dec 11, 2023
89ece40
do not allow to instance post transformer
pfefferle Dec 11, 2023
0ba6d7b
Merge branch 'master' into add/outbox-collection
pfefferle Dec 12, 2023
efda4a5
fix tests
pfefferle Dec 12, 2023
1558551
Merge branch 'master' into add/outbox-collection
pfefferle Dec 13, 2023
6edc4b5
Merge branch 'master' into add/outbox-collection
pfefferle Dec 21, 2023
a38c565
Merge branch 'master' into add/outbox-collection
pfefferle Dec 22, 2023
2a3a6c3
Merge branch 'trunk' into add/outbox-collection
pfefferle Sep 27, 2024
5d66494
fix phpcs
pfefferle Sep 27, 2024
b1d260d
revert change
pfefferle Sep 27, 2024
81b0b88
remove unused `use` declarations
pfefferle Sep 27, 2024
8c0c3fa
the handler should not handle outgoing stuff
pfefferle Sep 27, 2024
3eb8ca6
Merge branch 'trunk' into add/outbox-collection
pfefferle Oct 22, 2024
43a3343
Update class-activitypub.php
pfefferle Oct 22, 2024
8746447
fix PHPCS
pfefferle Oct 22, 2024
242dea4
more PHPCS fixes
pfefferle Oct 22, 2024
f64aade
fix namespace issue
pfefferle Oct 22, 2024
5ca19a6
remove unneeded function
pfefferle Oct 22, 2024
9f525b0
fix sticky post endpoint
pfefferle Oct 22, 2024
e0aeefd
no need to check for User-ID
pfefferle Oct 22, 2024
2d3ba4b
Merge branch 'trunk' into add/outbox-collection
pfefferle Oct 23, 2024
1433e4f
Merge branch 'trunk' into add/outbox-collection
pfefferle Oct 23, 2024
943fc02
Merge branch 'trunk' into add/outbox-collection
pfefferle Oct 24, 2024
7f0a227
Merge branch 'trunk' into add/outbox-collection
pfefferle Oct 25, 2024
fb98bd2
Merge branch 'trunk' into add/outbox-collection
pfefferle Oct 26, 2024
945b335
Merge branch 'trunk' into add/outbox-collection
pfefferle Nov 5, 2024
9345991
support JSON and Arrays beside WP_Comments and WP_Posts
pfefferle Nov 5, 2024
d1e9839
fix auto-complete issue
pfefferle Nov 5, 2024
59ab8b9
simplify code.
pfefferle Nov 5, 2024
8133a1c
convert JSON to Activity_Object to not have everything duplicated
pfefferle Nov 5, 2024
0bdb30d
add support for maps and mentions
pfefferle Nov 5, 2024
593740f
Merge branch 'trunk' into add/outbox-collection
pfefferle Nov 7, 2024
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
10 changes: 3 additions & 7 deletions includes/activity/class-base-object.php
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ public static function init_from_json( $json ) {
$array = \json_decode( $json, true );

if ( ! is_array( $array ) ) {
$array = array();
return new WP_Error( 'invalid_json', __( 'Invalid JSON', 'activitypub' ), array( 'status' => 400 ) );
}

return self::init_from_array( $array );
Expand All @@ -600,15 +600,11 @@ public static function init_from_json( $json ) {
*/
public static function init_from_array( $data ) {
if ( ! is_array( $data ) ) {
return new WP_Error( 'invalid_array', __( 'Invalid array', 'activitypub' ), array( 'status' => 404 ) );
return new WP_Error( 'invalid_array', __( 'Invalid array', 'activitypub' ), array( 'status' => 400 ) );
}

$object = new static();

foreach ( $data as $key => $value ) {
$key = camel_to_snake_case( $key );
call_user_func( array( $object, 'set_' . $key ), $value );
}
$object->from_array( $data );

return $object;
}
Expand Down
21 changes: 20 additions & 1 deletion includes/class-activitypub.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Activitypub;

use Exception;
use Activitypub\Collection\Outbox;
use Activitypub\Collection\Followers;
use Activitypub\Collection\Extra_Fields;

Expand Down Expand Up @@ -453,7 +454,7 @@ public static function plugin_update_message( $data ) {
}

/**
* Register the "Followers" Taxonomy.
* Register Custom Post Types.
*/
private static function register_post_types() {
\register_post_type(
Expand Down Expand Up @@ -523,6 +524,24 @@ private static function register_post_types() {
)
);

// Register Outbox Post-Type.
register_post_type(
Outbox::POST_TYPE,
array(
'labels' => array(
'name' => _x( 'Outbox', 'post_type plural name', 'activitypub' ),
'singular_name' => _x( 'Outbox Item', 'post_type single name', 'activitypub' ),
),
'public' => false,
'hierarchical' => false,
'rewrite' => false,
'query_var' => false,
'delete_with_user' => false,
'can_export' => true,
'supports' => array(),
)
);

// Both User and Blog Extra Fields types have the same args.
$args = array(
'labels' => array(
Expand Down
1 change: 0 additions & 1 deletion includes/class-scheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ public static function deregister_schedules() {
wp_unschedule_hook( 'activitypub_cleanup_followers' );
}


/**
* Schedule Activities.
*
Expand Down
57 changes: 57 additions & 0 deletions includes/collection/class-outbox.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
/**
* Outbox collection file.
*
* @package Activitypub
*/

namespace Activitypub\Collection;

use Activitypub\Transformer\Factory;

/**
* ActivityPub Outbox Collection
*/
class Outbox {
const POST_TYPE = 'ap_outbox';

/**
* Add an Item to the outbox.
*
* @param string|array|Base_Object|WP_Post|WP_Comment $item The item to add.
* @param int $user_id The user ID.
* @param string $activity_type The activity type.
*
* @return mixed The added item or an error.
*/
public static function add_item( $item, $user_id, $activity_type = 'Create' ) {
$transformer = Factory::get_transformer( $item );
$object = $transformer->transform();

if ( ! $object || is_wp_error( $object ) ) {
return $object;
}

$outbox_item = array(
'post_type' => self::POST_TYPE,
'post_title' => $object->get_id(),
'post_content' => $object->to_json(),
'post_author' => $user_id,
'post_status' => 'draft',
);

$has_kses = false !== \has_filter( 'content_save_pre', 'wp_filter_post_kses' );
if ( $has_kses ) {
// Prevent KSES from corrupting JSON in post_content.
\kses_remove_filters();
}

$result = \wp_insert_post( $outbox_item, true );

if ( $has_kses ) {
\kses_init_filters();
}

return $result;
}
}
4 changes: 2 additions & 2 deletions includes/collection/class-replies.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private static function get_id( $wp_object ) {
} elseif ( $wp_object instanceof WP_Comment ) {
return get_rest_url_by_path( sprintf( 'comments/%d/replies', $wp_object->comment_ID ) );
} else {
return new WP_Error();
return new WP_Error( 'unsupported_object', 'The object is not a post or comment.' );
}
}

Expand All @@ -88,7 +88,7 @@ private static function get_id( $wp_object ) {
public static function get_collection( $wp_object ) {
$id = self::get_id( $wp_object );

if ( ! $id ) {
if ( ! $id || is_wp_error( $id ) ) {
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion includes/handler/class-update.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static function init() {
}

/**
* Handle "Update" requests
* Handle "Update" requests.
*
* @param array $activity The activity-object.
*/
Expand Down
155 changes: 155 additions & 0 deletions includes/transformer/class-activity-object.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php
/**
* Activity Object Transformer Class.
*
* @package Activitypub
*/

namespace Activitypub\Transformer;

/**
* Activity Object Transformer Class.
*/
class Activity_Object extends Base {
/**
* Transform the WordPress Object into an ActivityPub Object.
*
* @return Base_Object The ActivityPub Object.
*/
public function to_object() {
return $this->transform_object_properties( $this->item );
}

/**
* Get the ID of the WordPress Object.
*
* @return string The ID of the WordPress Object.
*/
protected function get_id() {
return '';
}

/**
* Helper function to get the @-Mentions from the post content.
*
* @return array The list of @-Mentions.
*/
protected function get_mentions() {
/**
* Filter the mentions in the post content.
*
* @param array $mentions The mentions.
* @param string $content The post content.
* @param WP_Post $post The post object.
*
* @return array The filtered mentions.
*/
return apply_filters(
'activitypub_extract_mentions',
array(),
$this->item->get_content() . ' ' . $this->item->get_summary(),
$this->item
);
}

/**
* Returns a list of Mentions, used in the Post.
*
* @see https://docs.joinmastodon.org/spec/activitypub/#Mention
*
* @return array The list of Mentions.
*/
protected function get_cc() {
$cc = array();
$mentions = $this->get_mentions();

if ( $mentions ) {
foreach ( $mentions as $url ) {
$cc[] = $url;
}
}

return $cc;
Comment on lines +63 to +72
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$cc = array();
$mentions = $this->get_mentions();
if ( $mentions ) {
foreach ( $mentions as $url ) {
$cc[] = $url;
}
}
return $cc;
return $this->get_mentions() ?: array();

Could this be simplified to ^?
If get_mentions() always returned an array, the fallback would not be needed.

Copy link
Member Author

@pfefferle pfefferle Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the if then and directly iterate but we can't simply return get_mention() because it has some more data, not only the list of URLs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we could use an array_filter instead!

Copy link
Member

@obenland obenland Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, maybe return array_values( $this->get_mentions() );?

Currently the function doesn't remove anything from $mentions, it just puts everything in $cc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔

Copy link
Member Author

@pfefferle pfefferle Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, array_values should do the trick! It thought it has some more informations, but it's simply the key is the WebFinger handle and the URL is the value!

}

/**
* Returns the content map for the post.
*
* @return array The content map for the post.
*/
protected function get_content_map() {
$content = $this->item->get_content();

if ( ! $content ) {
return null;
}

return array(
$this->get_locale() => $content,
);
}

/**
* Returns the name map for the post.
*
* @return array The name map for the post.
*/
protected function get_name_map() {
$name = $this->item->get_name();

if ( ! $name ) {
return null;
}

return array(
$this->get_locale() => $name,
);
}

/**
* Returns the summary map for the post.
*
* @return array The summary map for the post.
*/
protected function get_summary_map() {
$summary = $this->item->get_summary();

if ( ! $summary ) {
return null;
}

return array(
$this->get_locale() => $summary,
);
}

/**
* Returns a list of Tags, used in the Comment.
*
* This includes Hash-Tags and Mentions.
*
* @return array The list of Tags.
*/
protected function get_tag() {
$tags = $this->item->get_tags();

if ( ! $tags ) {
$tags = array();
}

$mentions = $this->get_mentions();

if ( $mentions ) {
foreach ( $mentions as $mention => $url ) {
$tag = array(
'type' => 'Mention',
'href' => \esc_url( $url ),
'name' => \esc_html( $mention ),
);
$tags[] = $tag;
}
}

return \array_unique( $tags, SORT_REGULAR );
}
}
6 changes: 3 additions & 3 deletions includes/transformer/class-attachment.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Attachment extends Post {
* @return array The Attachments.
*/
protected function get_attachment() {
$mime_type = get_post_mime_type( $this->wp_object->ID );
$mime_type = get_post_mime_type( $this->item->ID );
$media_type = preg_replace( '/(\/[a-zA-Z]+)/i', '', $mime_type );
$type = '';

Expand All @@ -40,11 +40,11 @@ protected function get_attachment() {

$attachment = array(
'type' => $type,
'url' => wp_get_attachment_url( $this->wp_object->ID ),
'url' => wp_get_attachment_url( $this->item->ID ),
'mediaType' => $mime_type,
);

$alt = \get_post_meta( $this->wp_object->ID, '_wp_attachment_image_alt', true );
$alt = \get_post_meta( $this->item->ID, '_wp_attachment_image_alt', true );
if ( $alt ) {
$attachment['name'] = $alt;
}
Expand Down
Loading
Loading