| @@ -129,9 +129,9 @@ | ||
| 129 | 129 | return false; |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | 132 | $visibility = \get_post_meta( $post->ID, 'activitypub_content_visibility', true ); |
| 133 | - $is_local_or_private = in_array( $visibility, array( ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE ), true ); | |
| 133 | + $is_local_or_private = \in_array( $visibility, array( ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE ), true ); | |
| 134 | 134 | |
| 135 | 135 | /* |
| 136 | 136 | * An attachment (`inherit` status) inherits its parent's visibility. |
| 137 | 137 | * Recurse into the parent so the attachment also picks up the parent's |
| @@ -141,10 +141,10 @@ | ||
| 141 | 141 | $is_attachment_public = 'inherit' === $post->post_status && |
| 142 | 142 | 'attachment' === $post->post_type && |
| 143 | 143 | ( ! $post->post_parent || is_post_publicly_queryable( $post->post_parent ) ); |
| 144 | 144 | |
| 145 | - // Drafts and pending posts are allowed during preview requests so the Fediverse Preview works. | |
| 146 | - $is_preview = in_array( $post->post_status, array( 'draft', 'pending' ), true ) && | |
| 145 | + // Draft, pending, and scheduled posts are allowed during preview requests so the Fediverse Preview works. | |
| 146 | + $is_preview = \in_array( $post->post_status, array( 'draft', 'pending', 'future' ), true ) && | |
| 147 | 147 | \get_query_var( 'preview' ) && |
| 148 | 148 | \current_user_can( 'edit_post', $post->ID ); |
| 149 | 149 | |
| 150 | 150 | $is_public_status = 'publish' === $post->post_status || $is_attachment_public || $is_preview; |
| @@ -165,8 +165,44 @@ | ||
| 165 | 165 | return \apply_filters( 'activitypub_is_post_publicly_queryable', $queryable, $post ); |
| 166 | 166 | } |
| 167 | 167 | |
| 168 | 168 | /** |
| 169 | + * Check whether a post is federated. | |
| 170 | + * | |
| 171 | + * A post is federated when it has been sent to the Fediverse (its federation | |
| 172 | + * state is "federated") AND it is still publicly queryable, i.e. its post type | |
| 173 | + * is enabled for ActivityPub, it has a public status, it is not password- | |
| 174 | + * protected, and its content visibility allows it (see `is_post_publicly_queryable()`). | |
| 175 | + * | |
| 176 | + * Re-checking the live queryability alongside the stored state guards against a | |
| 177 | + * stale "federated" status left behind when a post is moved to a private status, | |
| 178 | + * switched to local visibility, or its post type loses ActivityPub support. | |
| 179 | + * | |
| 180 | + * The federation-state check also keeps `is_post_publicly_queryable()`'s | |
| 181 | + * preview allowance inert here: a draft/pending/scheduled post is never in the | |
| 182 | + * federated state, so the preview branch can never make this return true. | |
| 183 | + * | |
| 184 | + * @since 9.0.0 | |
| 185 | + * | |
| 186 | + * @param mixed $post The post ID or object. | |
| 187 | + * | |
| 188 | + * @return boolean True if the post is federated, false otherwise. | |
| 189 | + */ | |
| 190 | +function is_post_federated( $post ) { | |
| 191 | + if ( empty( $post ) ) { | |
| 192 | + return false; | |
| 193 | + } | |
| 194 | + | |
| 195 | + $post = \get_post( $post ); | |
| 196 | + | |
| 197 | + if ( ! $post ) { | |
| 198 | + return false; | |
| 199 | + } | |
| 200 | + | |
| 201 | + return ACTIVITYPUB_OBJECT_STATE_FEDERATED === get_wp_object_state( $post ) && is_post_publicly_queryable( $post ); | |
| 202 | +} | |
| 203 | + | |
| 204 | +/** | |
| 169 | 205 | * Check if a post is an ActivityPub post. |
| 170 | 206 | * |
| 171 | 207 | * @param mixed $post The post object or ID. |
| 172 | 208 | * |
| @@ -198,9 +234,9 @@ | ||
| 198 | 234 | case 'page': |
| 199 | 235 | $description = ''; |
| 200 | 236 | break; |
| 201 | 237 | case 'attachment': |
| 202 | - $description = ' - ' . __( 'Files uploaded to the media library (such as images, videos, documents, or other attachments). Note: This federates every file upload, not just published content.', 'activitypub' ); | |
| 238 | + $description = ' - ' . \__( 'Files uploaded to the media library (such as images, videos, documents, or other attachments). Note: This federates every file upload, not just published content.', 'activitypub' ); | |
| 203 | 239 | break; |
| 204 | 240 | default: |
| 205 | 241 | $description = ''; |
| 206 | 242 | if ( ! empty( $post_type->description ) ) { |
| @@ -214,9 +250,9 @@ | ||
| 214 | 250 | * @param string $description The description of the post type. |
| 215 | 251 | * @param string $post_type_name The post type name. |
| 216 | 252 | * @param \WP_Post_Type $post_type The post type object. |
| 217 | 253 | */ |
| 218 | - return apply_filters( 'activitypub_post_type_description', $description, $post_type->name, $post_type ); | |
| 254 | + return \apply_filters( 'activitypub_post_type_description', $description, $post_type->name, $post_type ); | |
| 219 | 255 | } |
| 220 | 256 | |
| 221 | 257 | /** |
| 222 | 258 | * Get the enclosures of a post. |
| @@ -225,22 +261,22 @@ | ||
| 225 | 261 | * |
| 226 | 262 | * @return array The enclosures. |
| 227 | 263 | */ |
| 228 | 264 | function get_enclosures( $post_id ) { |
| 229 | - $enclosures = get_post_meta( $post_id, 'enclosure', false ); | |
| 265 | + $enclosures = \get_post_meta( $post_id, 'enclosure', false ); | |
| 230 | 266 | |
| 231 | 267 | if ( ! $enclosures ) { |
| 232 | 268 | return array(); |
| 233 | 269 | } |
| 234 | 270 | |
| 235 | - $enclosures = array_map( | |
| 271 | + $enclosures = \array_map( | |
| 236 | 272 | static function ( $enclosure ) { |
| 237 | 273 | // Check if the enclosure is a string. |
| 238 | - if ( ! $enclosure || ! is_string( $enclosure ) ) { | |
| 274 | + if ( ! $enclosure || ! \is_string( $enclosure ) ) { | |
| 239 | 275 | return false; |
| 240 | 276 | } |
| 241 | 277 | |
| 242 | - $attributes = explode( "\n", $enclosure ); | |
| 278 | + $attributes = \explode( "\n", $enclosure ); | |
| 243 | 279 | |
| 244 | 280 | if ( ! isset( $attributes[0] ) || ! \wp_http_validate_url( $attributes[0] ) ) { |
| 245 | 281 | return false; |
| 246 | 282 | } |
| @@ -253,9 +289,9 @@ | ||
| 253 | 289 | }, |
| 254 | 290 | $enclosures |
| 255 | 291 | ); |
| 256 | 292 | |
| 257 | - return array_filter( $enclosures ); | |
| 293 | + return \array_filter( $enclosures ); | |
| 258 | 294 | } |
| 259 | 295 | |
| 260 | 296 | /** |
| 261 | 297 | * Generates a summary of a post. |
| @@ -269,9 +305,9 @@ | ||
| 269 | 305 | * |
| 270 | 306 | * @return string The generated post summary. |
| 271 | 307 | */ |
| 272 | 308 | function generate_post_summary( $post, $length = 500 ) { |
| 273 | - $post = get_post( $post ); | |
| 309 | + $post = \get_post( $post ); | |
| 274 | 310 | |
| 275 | 311 | if ( ! $post ) { |
| 276 | 312 | return ''; |
| 277 | 313 | } |
| @@ -331,14 +367,14 @@ | ||
| 331 | 367 | * |
| 332 | 368 | * @return string|false The content warning or false if not found. |
| 333 | 369 | */ |
| 334 | 370 | function get_content_warning( $post_id ) { |
| 335 | - $post = get_post( $post_id ); | |
| 371 | + $post = \get_post( $post_id ); | |
| 336 | 372 | if ( ! $post ) { |
| 337 | 373 | return false; |
| 338 | 374 | } |
| 339 | 375 | |
| 340 | - $warning = get_post_meta( $post->ID, 'activitypub_content_warning', true ); | |
| 376 | + $warning = \get_post_meta( $post->ID, 'activitypub_content_warning', true ); | |
| 341 | 377 | if ( empty( $warning ) ) { |
| 342 | 378 | return false; |
| 343 | 379 | } |
| 344 | 380 | |
| @@ -371,9 +407,9 @@ | ||
| 371 | 407 | * |
| 372 | 408 | * @return string|false The visibility of the post or false if not found. |
| 373 | 409 | */ |
| 374 | 410 | function get_content_visibility( $post_id ) { |
| 375 | - $post = get_post( $post_id ); | |
| 411 | + $post = \get_post( $post_id ); | |
| 376 | 412 | if ( ! $post ) { |
| 377 | 413 | return false; |
| 378 | 414 | } |
| 379 | 415 | |
| @@ -384,9 +420,9 @@ | ||
| 384 | 420 | ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, |
| 385 | 421 | ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL, |
| 386 | 422 | ); |
| 387 | 423 | |
| 388 | - if ( in_array( $visibility, $options, true ) ) { | |
| 424 | + if ( \in_array( $visibility, $options, true ) ) { | |
| 389 | 425 | $_visibility = $visibility; |
| 390 | 426 | } |
| 391 | 427 | |
| 392 | 428 | /** |