PluginProbe
ActivityPub / 8.2.0
ActivityPub v8.2.0
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
activitypub / includes / handler / outbox / class-arrive.php

class-arrive.php in ActivityPub 8.2.0, at includes/handler/outbox/class-arrive.php

188 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Outbox Arrive handler file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Handler\Outbox;
9
10 use Activitypub\Collection\Posts;
11
12 use function Activitypub\add_to_outbox;
13 use function Activitypub\is_activity_public;
14
15 /**
16 * Handle outgoing Arrive activities.
17 *
18 * @since 8.1.0
19 */
20 class Arrive {
21 /**
22 * Initialize the class, registering WordPress hooks.
23 *
24 * @since 8.1.0
25 */
26 public static function init() {
27 \add_filter( 'activitypub_outbox_arrive', array( self::class, 'handle_arrive' ), 10, 3 );
28 }
29
30 /**
31 * Handle outgoing "Arrive" activities from local actors.
32 *
33 * Arrive is an intransitive activity (no object) indicating that
34 * the actor has arrived at a location. Per the ActivityPub spec,
35 * the server must preserve the original activity type, so this
36 * handler adds the Arrive directly to the outbox as-is.
37 *
38 * As a local side effect, a WordPress post is created so the
39 * check-in appears on the blog with location geodata.
40 *
41 * @since 8.1.0
42 *
43 * @param array $data The activity data array.
44 * @param int $user_id The user ID.
45 * @param string|null $visibility Content visibility.
46 *
47 * @return int|\WP_Error|false The outbox post ID, error, or false.
48 */
49 public static function handle_arrive( $data, $user_id = null, $visibility = null ) {
50 // Create a blog post for public check-ins so they appear on the site.
51 if ( is_activity_public( $data ) ) {
52 $post = self::create_checkin_post( $data, $user_id, $visibility );
53
54 if ( ! \is_wp_error( $post ) ) {
55 $data['url'] = \get_permalink( $post );
56 }
57 }
58
59 /*
60 * Add the original Arrive activity to the outbox directly.
61 * This preserves the intransitive activity type per the
62 * ActivityPub spec (Section 6) instead of wrapping it in Create.
63 */
64 $outbox_id = add_to_outbox( $data, null, $user_id, $visibility );
65
66 if ( ! $outbox_id ) {
67 return new \WP_Error(
68 'activitypub_outbox_error',
69 \__( 'Failed to add Arrive activity to outbox.', 'activitypub' ),
70 array( 'status' => 500 )
71 );
72 }
73
74 return $outbox_id;
75 }
76
77 /**
78 * Create a WordPress post from the Arrive activity.
79 *
80 * Creates a blog post with the check-in content and saves
81 * location geodata so it can be displayed on the site.
82 *
83 * @since 8.1.0
84 *
85 * @param array $data The activity data.
86 * @param int $user_id The user ID.
87 * @param string|null $visibility Content visibility.
88 *
89 * @return \WP_Post|\WP_Error The created post or error.
90 */
91 private static function create_checkin_post( $data, $user_id, $visibility ) {
92 $location = $data['location'] ?? null;
93 $location_name = self::get_location_name( $location );
94
95 $title = $location_name
96 ? sprintf(
97 /* translators: %s: location name */
98 \__( 'Checked in at %s', 'activitypub' ),
99 $location_name
100 )
101 : \__( 'Check-in', 'activitypub' );
102
103 $activity = array(
104 'object' => array(
105 'type' => 'Note',
106 'name' => $title,
107 'content' => $data['content'] ?? $data['summary'] ?? '',
108 ),
109 'to' => $data['to'] ?? array(),
110 'cc' => $data['cc'] ?? array(),
111 );
112
113 $post = Posts::create( $activity, $user_id, $visibility );
114
115 if ( \is_wp_error( $post ) ) {
116 return $post;
117 }
118
119 self::save_location( $post->ID, $location );
120
121 /**
122 * Fires after an Arrive activity has created a local blog post.
123 *
124 * @since 8.1.0
125 *
126 * @param int $post_id The created post ID.
127 * @param array|null $location The location data from the activity.
128 * @param array $data The activity data.
129 * @param int $user_id The user ID.
130 */
131 \do_action( 'activitypub_outbox_arrive_sent', $post->ID, $location, $data, $user_id );
132
133 return $post;
134 }
135
136 /**
137 * Save location geodata on a post.
138 *
139 * Uses the standard `geo_*` meta keys that the Post transformer
140 * reads back when converting to ActivityPub Place objects.
141 *
142 * @since 8.1.0
143 *
144 * @param int $post_id The post ID.
145 * @param array|null $location The ActivityPub location data.
146 */
147 private static function save_location( $post_id, $location ) {
148 if ( ! \is_array( $location ) ) {
149 return;
150 }
151
152 if ( ! empty( $location['name'] ) ) {
153 \update_post_meta( $post_id, 'geo_address', \sanitize_text_field( $location['name'] ) );
154 }
155
156 if ( isset( $location['latitude'] ) && \is_numeric( $location['latitude'] ) ) {
157 \update_post_meta( $post_id, 'geo_latitude', (float) $location['latitude'] );
158 }
159
160 if ( isset( $location['longitude'] ) && \is_numeric( $location['longitude'] ) ) {
161 \update_post_meta( $post_id, 'geo_longitude', (float) $location['longitude'] );
162 }
163
164 if ( ! empty( $location['name'] ) || ( isset( $location['latitude'] ) && isset( $location['longitude'] ) ) ) {
165 \update_post_meta( $post_id, 'geo_public', '1' );
166 }
167 }
168
169 /**
170 * Extract a human-readable name from an ActivityPub location.
171 *
172 * @param mixed $location The location data (array or string).
173 *
174 * @return string|null The location name or null.
175 */
176 private static function get_location_name( $location ) {
177 if ( \is_array( $location ) && ! empty( $location['name'] ) ) {
178 return \sanitize_text_field( $location['name'] );
179 }
180
181 if ( \is_string( $location ) && ! empty( $location ) ) {
182 return \sanitize_text_field( $location );
183 }
184
185 return null;
186 }
187 }
188