PluginProbe
Friends / 4.2.1
Friends v4.2.1
4.3.2 4.3.1 4.3.0 4.2.2 4.2.1 4.2.0 4.1.0 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.0 2.9.1 All 88 releases
friends / includes / class-messages.php

class-messages.php in Friends 4.2.1, at includes/class-messages.php

841 lines 26.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Friends Messages
4 *
5 * This contains the functions for Messages.
6 *
7 * @package Friends
8 */
9
10 namespace Friends;
11
12 /**
13 * This is the class for the Messages part of the Friends Plugin.
14 *
15 * @since 0.21
16 *
17 * @package Friends
18 * @author Alex Kirk
19 */
20 class Messages {
21 const CPT = 'friend_message';
22 const TAXONOMY = 'friend_message_user';
23
24 /**
25 * Contains a reference to the Friends class.
26 *
27 * @var Friends
28 */
29 private $friends;
30
31 /**
32 * Constructor
33 *
34 * @param Friends $friends A reference to the Friends object.
35 */
36 public function __construct( Friends $friends ) {
37 $this->friends = $friends;
38 $this->register_hooks();
39 }
40
41 /**
42 * Register the WordPress hooks
43 */
44 private function register_hooks() {
45 add_action( 'init', array( $this, 'register_custom_post_type' ) );
46 add_action( 'init', array( $this, 'register_taxonomy' ) );
47 add_filter( 'post_type_link', array( $this, 'post_type_link' ), 10, 2 );
48 add_filter( 'friends_unread_count', array( $this, 'friends_unread_messages_count' ) );
49 add_action( 'friends_own_site_menu_top', array( $this, 'friends_add_menu_unread_messages' ) );
50 add_action( 'wp_ajax_friends-mark-read', array( $this, 'mark_message_read' ) );
51 add_action( 'friends_author_header', array( $this, 'friends_author_header' ), 10, 2 );
52 add_action( 'friends_after_header', array( $this, 'friends_display_messages' ), 10, 2 );
53 add_action( 'friends_after_header', array( $this, 'friends_message_form' ), 11, 2 );
54 add_filter( 'template_redirect', array( $this, 'handle_message_send' ), 10, 2 );
55 add_filter( 'friends_send_direct_message', array( $this, 'save_outgoing_message' ), 10, 6 );
56 add_filter( 'notify_friend_message_received', array( $this, 'save_incoming_message' ), 5, 6 );
57 add_filter( 'mastodon_api_conversation', array( $this, 'mastodon_api_conversation' ), 10, 2 );
58 add_filter( 'mastodon_api_conversations', array( $this, 'mastodon_api_conversations' ), 10, 3 );
59 add_filter( 'mastodon_api_status_context_post_types', array( $this, 'api_status_context_post_types' ), 10, 2 );
60 add_filter( 'mastodon_api_status_context_post_statuses', array( $this, 'api_status_context_post_statuses' ), 10, 2 );
61 add_filter( 'api_status_context_post_types', array( $this, 'api_status_context_post_types' ), 10, 2 ); // legacy filter.
62 add_filter( 'api_status_context_post_statuses', array( $this, 'api_status_context_post_statuses' ), 10, 2 ); // legacy filter.
63 add_filter( 'mastodon_api_submit_status', array( $this, 'mastodon_api_submit_status' ), 9, 6 );
64 add_filter( 'mastodon_api_conversation_mark_read', array( $this, 'mastodon_api_conversation_mark_read' ), 10 );
65 add_filter( 'mastodon_api_conversation_delete', array( $this, 'delete_conversation' ), 10 );
66 add_filter( 'mastodon_api_status', array( $this, 'mastodon_api_status' ), 20, 2 );
67 }
68
69 /**
70 * Registers the custom post type
71 */
72 public function register_custom_post_type() {
73 $labels = array(
74 'name' => __( 'Friend Messages', 'friends' ),
75 'singular_name' => __( 'Friend Message', 'friends' ),
76 'add_new' => _x( 'Add New', 'cached friend post', 'friends' ),
77 'add_new_item' => __( 'Add New Friend Message', 'friends' ),
78 'edit_item' => __( 'Edit Friend Message', 'friends' ),
79 'new_item' => __( 'New Friend Message', 'friends' ),
80 'all_items' => __( 'All Friend Messages', 'friends' ),
81 'view_item' => __( 'View Friend Message', 'friends' ),
82 'search_items' => __( 'Search Friend Messages', 'friends' ),
83 'not_found' => __( 'No Friend Messages found', 'friends' ),
84 'not_found_in_trash' => __( 'No Friend Messages found in the Trash', 'friends' ),
85 'parent_item_colon' => '',
86 'menu_name' => __( 'Friend Messages', 'friends' ),
87 );
88
89 $args = array(
90 'labels' => $labels,
91 'description' => "A friend's message",
92 'publicly_queryable' => Friends::has_required_privileges(),
93 'show_ui' => true,
94 'show_in_menu' => apply_filters( 'friends_show_cached_posts', false ),
95 'show_in_nav_menus' => false,
96 'show_in_admin_bar' => false,
97 'show_in_rest' => Friends::has_required_privileges(),
98 'exclude_from_search' => true,
99 'public' => false,
100 'delete_with_user' => true,
101 'menu_position' => 6,
102 'menu_icon' => 'dashicons-admin-comments',
103 'supports' => array( 'title', 'editor', 'author', 'revisions' ),
104 'taxonomies' => array( 'friend-reaction-' . get_current_user_id() ),
105 'has_archive' => true,
106 'rewrite' => false,
107 );
108
109 register_post_type( self::CPT, $args );
110
111 register_post_status(
112 'friends_unread',
113 array(
114 'label' => _x( 'Unread', 'message', 'friends' ),
115 'public' => false,
116 'exclude_from_search' => true,
117 'show_in_admin_all_list' => false,
118 'show_in_admin_status_list' => false,
119 // translators: %s; number of unread messages.
120 'label_count' => _n_noop( 'Unread (%s)', 'Unread (%s)', 'friends' ),
121 )
122 );
123
124 register_post_status(
125 'friends_read',
126 array(
127 'label' => _x( 'Read', 'message', 'friends' ),
128 'public' => false,
129 'exclude_from_search' => true,
130 'show_in_admin_all_list' => false,
131 'show_in_admin_status_list' => false,
132 // translators: %s; number of read messages.
133 'label_count' => _n_noop( 'Read (%s)', 'Read (%s)', 'friends' ),
134 )
135 );
136 }
137
138 public function register_taxonomy() {
139 $args = array(
140 'labels' => array(
141 'name' => _x( 'Friend Message User', 'taxonomy general name', 'friends' ),
142 'singular_name' => _x( 'Friend Message User', 'taxonomy singular name', 'friends' ),
143 'menu_name' => __( 'Friend Message User', 'friends' ),
144 ),
145 'hierarchical' => false,
146 'show_ui' => true,
147 'show_admin_column' => true,
148 'query_var' => true,
149 'rewrite' => false,
150 'public' => false,
151 );
152 register_taxonomy( self::TAXONOMY, self::CPT, $args );
153 }
154
155
156
157
158 public function friends_frontend_post_types_only_messages( $post_types ) {
159 $post_types = array( self::CPT );
160 return $post_types;
161 }
162
163
164 public function save_incoming_message( User $friend_user, $message, $subject = '', $feed_url = null, $remote_url = null, $reply_to = null ) {
165 $post_data = array(
166 'post_type' => self::CPT,
167 'post_title' => $subject,
168 'post_content' => $message,
169 'post_status' => 'friends_unread',
170 'guid' => $remote_url,
171 );
172
173 if ( $reply_to ) {
174 add_filter( 'friends_frontend_post_types', array( $this, 'friends_frontend_post_types_only_messages' ), 999 );
175 $reply_to_post_id = Feed::url_to_postid( $reply_to );
176 remove_filter( 'friends_frontend_post_types', array( $this, 'friends_frontend_post_types_only_messages' ), 999 );
177 if ( $reply_to_post_id ) {
178 $topmost_post = get_post( $reply_to_post_id );
179 while ( $topmost_post->post_parent ) {
180 $topmost_post = get_post( $topmost_post->post_parent );
181 }
182
183 $post_data['post_parent'] = $topmost_post->ID;
184 }
185 }
186
187 $post_id = $friend_user->insert_post( $post_data );
188 wp_set_post_terms( $post_id, strval( $friend_user->ID ), self::TAXONOMY );
189 if ( $feed_url ) {
190 update_post_meta( $post_id, 'friends_feed_url', $feed_url );
191 }
192
193 return $post_id;
194 }
195
196 /**
197 * Adds a message to friends_message post.
198 *
199 * @param int $post_id The return value that might be a post id.
200 * @param User $friend_user The friend user to which this should be associated.
201 * @param string $to The recipient as URL.
202 * @param string $message The message.
203 * @param string $subject The subject.
204 * @param int $reply_to_post_id The reply to post ID.
205 *
206 * @return int The post ID.
207 */
208 public function save_outgoing_message( $post_id, User $friend_user, $to, $message, $subject = '', $reply_to_post_id = null ) {
209 $content = \wpautop( $message );
210 $content = \preg_replace( '/[\n\r\t]/', '', $content );
211 $content = \trim( $content );
212
213 $post_data = array(
214 'post_type' => self::CPT,
215 'post_title' => $subject,
216 'post_content' => $content,
217 'post_status' => 'friends_read',
218 'post_parent' => $reply_to_post_id,
219 );
220
221 $post_id = wp_insert_post( $post_data );
222 wp_set_post_terms( $post_id, strval( $friend_user->ID ), self::TAXONOMY );
223 if ( $to ) {
224 update_post_meta( $post_id, 'friends_feed_url', $to );
225 }
226
227 return $post_id;
228 }
229
230 public function post_type_link( $post_link, \WP_Post $post ) {
231 if ( $post && self::CPT === $post->post_type ) {
232 if ( str_starts_with( $post->guid, home_url() ) || empty( $post->guid ) ) {
233 return home_url( '?p=' . $post->ID );
234 }
235
236 return $post->guid;
237 }
238
239 return $post_link;
240 }
241
242 /**
243 * Add the unread messages to the total.
244 *
245 * @param int $unread The unread count.
246 *
247 * @return int Unread count + unread messages.
248 */
249 public function friends_unread_messages_count( $unread ) {
250 $unread_messages = new \WP_Query(
251 array(
252 'post_type' => self::CPT,
253 'post_status' => 'friends_unread',
254 )
255 );
256 return $unread + $unread_messages->post_count;
257 }
258
259 /**
260 * Add entries to the menu for unread messages.
261 *
262 * @param \WP_Menu $wp_menu The wp menu.
263 */
264 public function friends_add_menu_unread_messages( $wp_menu ) {
265 global $post;
266 $unread_messages = new \WP_Query(
267 array(
268 'post_type' => self::CPT,
269 'post_status' => 'friends_unread',
270 )
271 );
272
273 $title = esc_html__( 'Direct Messages', 'friends' );
274 if ( $unread_messages->post_count > 0 ) {
275 $title .= ' <span class="wp-core-ui wp-ui-notification friends-open-requests">' . esc_html( number_format_i18n( $unread_messages->post_count ) ) . '</span>';
276 }
277
278 $wp_menu->add_menu(
279 array(
280 'id' => 'friends-direct-messages',
281 'parent' => 'friends-menu',
282 'title' => $title,
283 'href' => home_url( '/friends/messages/' ),
284 )
285 );
286
287 while ( $unread_messages->have_posts() ) {
288 $unread_messages->the_post();
289 $friend_user = User::get_post_author( $post );
290 $conversation = $post;
291 while ( $conversation->post_parent ) {
292 $conversation = get_post( $conversation->post_parent );
293 }
294 $conversation_url = add_query_arg( 'conversation', $conversation->ID, home_url( '/friends/messages/' ) );
295 $wp_menu->add_menu(
296 array(
297 'id' => 'friend-message-' . $friend_user->ID,
298 'parent' => 'friends-menu',
299 // translators: %s is the number of open friend requests.
300 'title' => '<span style="border-left: 2px solid #d63638; padding-left: .5em">' . esc_html( sprintf( __( 'New message from %s', 'friends' ), $friend_user->display_name ) ) . '</span>',
301 'href' => $conversation_url,
302 )
303 );
304 }
305 }
306
307 /**
308 * Ajax function to mark a message as read.
309 *
310 * @return \WP_Error The wp error.
311 */
312 public function mark_message_read() {
313 check_ajax_referer( 'friends-mark-read' );
314
315 if ( ! is_user_logged_in() ) {
316 return new \WP_Error( 'unauthorized', 'You are not authorized to send a reaction.' );
317 }
318
319 if ( ! isset( $_POST['post_id'] ) ) {
320 wp_send_json_error(
321 array(
322 'result' => false,
323 )
324 );
325 }
326 if ( ! is_numeric( $_POST['post_id'] ) || $_POST['post_id'] <= 0 ) {
327 wp_send_json_error(
328 array(
329 'result' => false,
330 )
331 );
332 }
333 $result = array();
334 $post_id = intval( $_POST['post_id'] );
335 if ( get_post_status( $post_id ) !== 'friends_read' ) {
336 wp_update_post(
337 array(
338 'ID' => $post_id,
339 'post_status' => 'friends_read',
340 )
341 );
342 $result[] = $post_id;
343 }
344 $child_posts = get_children(
345 array(
346 'post_parent' => $post_id,
347 'post_type' => self::CPT,
348 'post_status' => array( 'friends_unread' ),
349 )
350 );
351 foreach ( $child_posts as $child ) {
352 wp_update_post(
353 array(
354 'ID' => $child->ID,
355 'post_status' => 'friends_read',
356 )
357 );
358 $result[] = $child->ID;
359 }
360
361 do_action( 'friends_message_read', $post_id );
362
363 wp_send_json_success(
364 array(
365 'result' => $result,
366 )
367 );
368 }
369
370 /**
371 * Extend the author header.
372 *
373 * @param User $friend_user The friend user.
374 * @param array $args The arguments.
375 */
376 public function friends_author_header( User $friend_user, $args ) {
377 if ( apply_filters( 'friends_message_form_accounts', array(), $friend_user ) ) {
378 Friends::template_loader()->get_template_part(
379 'frontend/messages/author-header',
380 null,
381 $args
382 );
383
384 }
385 }
386
387 /**
388 * Display received messages by the friend user.
389 *
390 * @param array $args The arguments.
391 */
392 public function friends_display_messages( $args ) {
393 if ( ! isset( $args['friend_user'] ) || ! $args['friend_user'] ) {
394 return;
395 }
396
397 if ( apply_filters( 'friends_message_form_accounts', array(), $args['friend_user'] ) ) {
398 $args['existing_messages'] = new \WP_Query();
399 $args['existing_messages']->set( 'post_type', self::CPT );
400 $args['existing_messages']->set( 'post_parent', '0' );
401 $args['existing_messages']->set( 'post_status', array( 'friends_read', 'friends_unread' ) );
402 $tax_query = array();
403 $tax_query[] =
404 array(
405 'taxonomy' => self::TAXONOMY,
406 'field' => 'slug',
407 'terms' => $args['friend_user']->ID,
408 );
409 $args['existing_messages']->set( 'tax_query', $tax_query );
410 $args['existing_messages']->set( 'posts_per_page', -1 );
411 if ( ! $args['existing_messages']->get_posts() ) {
412 return;
413 }
414 $args['accounts'] = apply_filters( 'friends_message_form_accounts', array(), $args['friend_user'] );
415
416 add_filter( 'excerpt_length', array( $this, 'friends_message_excerpt_length' ) );
417
418 Friends::template_loader()->get_template_part(
419 'frontend/messages/friend',
420 null,
421 $args
422 );
423
424 remove_filter( 'excerpt_length', array( $this, 'friends_message_excerpt_length' ) );
425
426 }
427 }
428
429 public function friends_message_excerpt_length() {
430 return 10;
431 }
432
433 /**
434 * Embed the message form for the friend user.
435 *
436 * @param array $args The arguments.
437 */
438 public function friends_message_form( $args ) {
439 if ( ! isset( $args['friend_user'] ) || ! $args['friend_user'] ) {
440 return;
441 }
442
443 $args['blocks-everywhere'] = false;
444 $args['accounts'] = apply_filters( 'friends_message_form_accounts', array(), $args['friend_user'] );
445
446 if ( $args['accounts'] ) {
447 Friends::template_loader()->get_template_part(
448 'frontend/messages/message-form',
449 null,
450 $args
451 );
452
453 }
454 }
455
456 /**
457 * Sends a message to a friend.
458 *
459 * @param User $friend_user The friend user.
460 * @param string $to The recipient as URL.
461 * @param string $message The message.
462 * @param string $subject The subject.
463 * @param int $reply_to_post_id The reply to post ID.
464 *
465 * @return \WP_Error|int An error or the message post id.
466 */
467 public function send_message( User $friend_user, $to, $message, $subject = '', $reply_to_post_id = null ) {
468 $tos = apply_filters( 'friends_message_form_accounts', array(), $friend_user );
469 if ( ! isset( $tos[ $to ] ) ) {
470 return new \WP_Error( 'not-a-friend', __( 'You cannot send messages to this user.', 'friends' ) );
471 }
472 if ( ! $message || ! trim( $message ) ) {
473 return new \WP_Error( 'empty-message', __( 'You cannot send empty messages.', 'friends' ) );
474 }
475
476 $post_id = apply_filters( 'friends_send_direct_message', null, $friend_user, $to, $message, $subject, $reply_to_post_id );
477
478 return $post_id;
479 }
480
481 /**
482 * Delete a conversation
483 *
484 * @param int $parent_post_id The parent post id.
485 *
486 * @return \WP_Error|\WP_Post|bool The post or false.
487 */
488 public function delete_conversation( $parent_post_id ) {
489 if ( ! current_user_can( 'delete_post', $parent_post_id ) ) {
490 return new \WP_Error( 'cannot_delete', __( 'You cannot delete converations.', 'friends' ) );
491 }
492
493 $post = get_post( $parent_post_id );
494 if ( ! $post ) {
495 return new \WP_Error( 'friends_messages_delete_conversation', 'Record not found', array( 'status' => 404 ) );
496 }
497
498 $child_posts = get_children(
499 array(
500 'post_parent' => $parent_post_id,
501 'post_type' => self::CPT,
502 'post_status' => array( 'friends_read', 'friends_unread' ),
503 )
504 );
505
506 foreach ( $child_posts as $child_post ) {
507 wp_delete_post( $child_post->ID );
508 }
509
510 wp_delete_post( $parent_post_id );
511
512 return true;
513 }
514
515 /**
516 * Handle the message form.
517 */
518 public function handle_message_send() {
519 if ( ! isset( $_REQUEST['friends_message_recipient'] ) ) {
520 return;
521 }
522
523 if ( isset( $_REQUEST['friends_message_delete_conversation'] ) ) {
524 return $this->handle_conversation_delete();
525 }
526
527 if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'friends_send_message' ) ) {
528 wp_die( esc_html__( 'Invalid nonce.', 'friends' ) );
529 }
530
531 $friend_user = User::get_by_username( sanitize_text_field( wp_unslash( $_REQUEST['friends_message_recipient'] ) ) );
532
533 $subject = '';
534 if ( isset( $_REQUEST['friends_message_subject'] ) ) {
535 $subject = sanitize_text_field( wp_unslash( $_REQUEST['friends_message_subject'] ) );
536 }
537 $message = '';
538 if ( isset( $_REQUEST['friends_message_message'] ) ) {
539 $message = sanitize_text_field( wp_unslash( $_REQUEST['friends_message_message'] ) );
540 }
541 if ( ! trim( $message ) ) {
542 wp_die( esc_html__( 'You cannot send an empty message.', 'friends' ) );
543 }
544
545 $reply_to_post_id = null;
546 if ( isset( $_REQUEST['friends_message_reply_to'] ) ) {
547 $reply_to_post_id = intval( $_REQUEST['friends_message_reply_to'] );
548 }
549
550 $to = null;
551 if ( isset( $_REQUEST['friends_message_account'] ) ) {
552 $to = sanitize_text_field( wp_unslash( $_REQUEST['friends_message_account'] ) );
553 }
554
555 if ( ! $to ) {
556 wp_die( esc_html__( 'You cannot send a message without a recipient.', 'friends' ) );
557 }
558
559 $error = $this->send_message( $friend_user, $to, $message, $subject, $reply_to_post_id );
560
561 if ( is_wp_error( $error ) ) {
562 wp_die( esc_html( $error->get_error_message() ) );
563 }
564
565 if ( isset( $_REQUEST['friends_message_redirect_to'] ) ) {
566 $redirect_to = wp_validate_redirect( sanitize_url( wp_unslash( $_REQUEST['friends_message_redirect_to'] ) ), $friend_user->get_local_friends_page_url() );
567 } else {
568 $redirect_to = $friend_user->get_local_friends_page_url();
569 }
570
571 wp_safe_redirect( $redirect_to );
572 exit;
573 }
574
575 /**
576 * Handle the deletion of the conversation.
577 */
578 public function handle_conversation_delete() {
579 if ( ! isset( $_REQUEST['friends_message_delete_conversation'] ) || ! isset( $_REQUEST['friends_message_recipient'] ) ) {
580 return;
581 }
582
583 if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'friends_send_message' ) ) {
584 wp_die( esc_html__( 'Invalid nonce.', 'friends' ) );
585 }
586
587 $friend_user = User::get_by_username( sanitize_text_field( wp_unslash( $_REQUEST['friends_message_recipient'] ) ) );
588
589 $post_id = '';
590 if ( isset( $_REQUEST['friends_message_reply_to'] ) ) {
591 $post_id = sanitize_text_field( wp_unslash( $_REQUEST['friends_message_reply_to'] ) );
592 }
593 if ( ! $post_id ) {
594 wp_die( esc_html__( 'You cannot delete a conversation without a post id.', 'friends' ) );
595 }
596 $error = $this->delete_conversation( $post_id );
597
598 if ( is_wp_error( $error ) ) {
599 wp_die( esc_html( $error->get_error_message() ) );
600 }
601
602 if ( isset( $_REQUEST['friends_message_redirect_to'] ) ) {
603 $redirect_to = wp_validate_redirect( sanitize_url( wp_unslash( $_REQUEST['friends_message_redirect_to'] ) ), $friend_user->get_local_friends_page_url() );
604 } else {
605 $redirect_to = $friend_user->get_local_friends_page_url();
606 }
607
608 wp_safe_redirect( $redirect_to );
609 exit;
610 }
611
612 public function mastodon_api_conversation( $conversation, $post_id ) {
613 $message = get_post( $post_id );
614 if ( ! $message ) {
615 return new \WP_Error( 'friends_messages_mastodon_api_conversation', 'Record not found', array( 'status' => 404 ) );
616 }
617
618 $last_status = get_posts(
619 array(
620 'post_type' => self::CPT,
621 'post_parent' => $message->ID,
622 'post_status' => array( 'friends_read', 'friends_unread' ),
623 'orderby' => 'date',
624 'order' => 'DESC',
625 'numberposts' => 1,
626 )
627 );
628 if ( ! $last_status ) {
629 $last_status = $message;
630 } else {
631 $last_status = $last_status[0];
632 }
633
634 $unread = 'friends_unread' === $message->post_status;
635 if ( ! $unread ) {
636 $unread_posts = get_children(
637 array(
638 'post_parent' => $message->ID,
639 'post_type' => self::CPT,
640 'post_status' => array( 'friends_unread' ),
641 )
642 );
643 if ( $unread_posts ) {
644 $unread = true;
645 }
646 }
647 $conversation = new \Enable_Mastodon_Apps\Entity\Conversation();
648 $conversation->id = $message->ID;
649 $conversation->unread = $unread;
650 $conversation->last_status = apply_filters( 'mastodon_api_status', null, $last_status->ID );
651 $conversation->accounts = array();
652 $account_id = $message->post_author;
653 if ( ! $account_id ) {
654 $account_id = get_post_meta( $message->ID, 'friends_feed_url', true );
655 }
656 $account = apply_filters( 'mastodon_api_account', null, $account_id );
657 if ( $account ) {
658 $conversation->accounts[] = $account;
659 }
660
661 return $conversation;
662 }
663
664 public function mastodon_api_conversations( $conversations, $user_id, $limit = 20 ) {
665 $messages = new \WP_Query();
666 $messages->set( 'post_type', self::CPT );
667 $messages->set( 'post_parent', '0' );
668 $messages->set( 'post_status', array( 'friends_read', 'friends_unread' ) );
669 $messages->set( 'posts_per_page', $limit );
670 $messages->set( 'order', 'DESC' );
671
672 foreach ( $messages->get_posts() as $message ) {
673 $conversation = $this->mastodon_api_conversation( null, $message->ID );
674 if ( $conversation && ! is_wp_error( $conversation ) ) {
675 $conversations[] = $conversation;
676 }
677 }
678
679 return $conversations;
680 }
681
682 public function api_status_context_post_types( $post_types, $post_id ) {
683 if ( self::CPT === get_post_type( $post_id ) ) {
684 return array( self::CPT );
685 }
686 return $post_types;
687 }
688
689 public function api_status_context_post_statuses( $post_statuses, $post_id ) {
690 if ( self::CPT === get_post_type( $post_id ) ) {
691 return array( 'friends_read', 'friends_unread' );
692 }
693 return $post_statuses;
694 }
695
696 public function mastodon_api_submit_status( $status, $status_text, $in_reply_to_id, $media_ids, $post_format, $visibility ) {
697 if ( $status instanceof \WP_Error || $status instanceof \Enable_Mastodon_Apps\Entity\Status || 'direct' !== $visibility ) {
698 return $status;
699 }
700
701 $mentions = apply_filters(
702 'activitypub_extract_mentions',
703 array(),
704 $status_text,
705 null
706 );
707
708 /**
709 * Documented in Enable_Mastodon_Apps\Handler\Status\prepare_post_data()
710 */
711 $status_text = apply_filters( 'mastodon_api_submit_status_text', $status_text, $in_reply_to_id, $visibility );
712
713 $friend_user = false;
714 foreach ( $mentions as $mention ) {
715 $user_feed = User_Feed::get_by_url( $mention );
716 if ( $user_feed ) {
717 $friend_user = $user_feed->get_friend_user();
718 break;
719 }
720 if ( class_exists( Feed_Parser_ActivityPub::class ) ) {
721 $user_feed = User_Feed::get_by_url( $mention );
722 $url = Feed_Parser_ActivityPub::friends_webfinger_resolve( $mention, $mention );
723 $user_feed = User_Feed::get_by_url( $url );
724 if ( $user_feed ) {
725 $friend_user = $user_feed->get_friend_user();
726 break;
727 }
728 }
729 }
730
731 if ( ! $friend_user ) {
732 // we cannot find the mentioned user, we need to stop this.
733 // TODO: handle the non-ActivityPub case.
734 return new \WP_Error( 'friends_messages_mastodon_api_submit_status', 'Direct message without a known mentioned user', array( 'status' => 400 ) );
735
736 }
737
738 $app = \Enable_Mastodon_Apps\Mastodon_App::get_current_app();
739 if ( ! $app->get_disable_blocks() ) {
740 $status_text = \Enable_Mastodon_Apps\Handler\Status::convert_to_blocks( $status_text );
741 }
742
743 if ( ! empty( $media_ids ) ) {
744 foreach ( $media_ids as $media_id ) {
745 $media = get_post( $media_id );
746 if ( ! $media ) {
747 return new \WP_Error( 'friends_messages_mastodon_api_submit_status', 'Media not found', array( 'status' => 400 ) );
748 }
749 if ( 'attachment' !== $media->post_type ) {
750 return new \WP_Error( 'friends_messages_mastodon_api_submit_status', 'Media not found', array( 'status' => 400 ) );
751 }
752 $attachment = \wp_get_attachment_metadata( $media_id );
753 if ( \wp_attachment_is( 'image', $media_id ) ) {
754 $status_text .= PHP_EOL;
755 $meta_json = array(
756 'id' => intval( $media_id ),
757 'sizeSlug' => 'large',
758 );
759 $status_text .= '<!-- wp:image ' . wp_json_encode( $meta_json ) . ' -->' . PHP_EOL;
760 $status_text .= '<figure class="wp-block-image"><img src="' . esc_url( wp_get_attachment_url( $media_id ) ) . '" alt="" class="wp-image-' . esc_attr( $media_id ) . '"/></figure>' . PHP_EOL;
761 $status_text .= '<!-- /wp:image -->' . PHP_EOL;
762 } elseif ( \wp_attachment_is( 'video', $media_id ) ) {
763 $status_text .= PHP_EOL;
764 $status_text .= '<!-- wp:video ' . wp_json_encode( array( 'id' => $media_id ) ) . ' -->' . PHP_EOL;
765 $status_text .= '<figure class="wp-block-video"><video controls src="' . esc_url( wp_get_attachment_url( $media_id ) ) . '" width="' . esc_attr( $attachment['width'] ) . '" height="' . esc_attr( $attachment['height'] ) . '" /></figure>' . PHP_EOL;
766 $status_text .= '<!-- /wp:video -->' . PHP_EOL;
767 }
768 }
769 }
770
771 $post_id = $this->send_message( $friend_user, $user_feed->get_url(), $status_text, null, $in_reply_to_id );
772
773 if ( ! empty( $media_ids ) ) {
774 foreach ( $media_ids as $media_id ) {
775 wp_update_post(
776 array(
777 'ID' => $media_id,
778 'post_parent' => $post_id,
779 )
780 );
781 }
782 }
783
784 return apply_filters( 'mastodon_api_status', null, $post_id );
785 }
786
787 public function mastodon_api_conversation_mark_read( $post_id ) {
788 $post = get_post( $post_id );
789 if ( ! $post ) {
790 return new \WP_Error( 'friends_messages_mastodon_api_conversation_mark_read', 'Record not found', array( 'status' => 404 ) );
791 }
792
793 if ( get_post_status( $post_id ) !== 'friends_read' ) {
794 wp_update_post(
795 array(
796 'ID' => $post_id,
797 'post_status' => 'friends_read',
798 )
799 );
800 }
801
802 $child_posts = get_children(
803 array(
804 'post_parent' => $post_id,
805 'post_type' => self::CPT,
806 'post_status' => array( 'friends_unread' ),
807 )
808 );
809 foreach ( $child_posts as $child ) {
810 wp_update_post(
811 array(
812 'ID' => $child->ID,
813 'post_status' => 'friends_read',
814 )
815 );
816 }
817
818 wp_update_post(
819 array(
820 'ID' => $post_id,
821 'post_status' => 'friends_read',
822 )
823 );
824
825 return true;
826 }
827
828 public function mastodon_api_status( $status, $post_id ) {
829 if ( ! $status instanceof \Enable_Mastodon_Apps\Entity\Status || get_post_type( $post_id ) !== self::CPT ) {
830 return $status;
831 }
832 $status->visibility = 'direct';
833 $post = get_post( $post_id );
834 if ( $post->post_parent ) {
835 $status->in_reply_to_id = $post->post_parent;
836 $status->in_reply_to_account_id = strval( get_current_user_id() );
837 }
838 return $status;
839 }
840 }
841