PluginProbe
Friends / 2.8.8
Friends v2.8.8
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 2.8.8, at includes/class-messages.php

560 lines 15.7 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
23 /**
24 * Contains a reference to the Friends class.
25 *
26 * @var Friends
27 */
28 private $friends;
29
30 /**
31 * Constructor
32 *
33 * @param Friends $friends A reference to the Friends object.
34 */
35 public function __construct( Friends $friends ) {
36 $this->friends = $friends;
37 $this->register_hooks();
38 }
39
40 /**
41 * Register the WordPress hooks
42 */
43 private function register_hooks() {
44 add_action( 'init', array( $this, 'register_custom_post_type' ) );
45 add_filter( 'friends_unread_count', array( $this, 'friends_unread_messages_count' ) );
46 add_action( 'friends_own_site_menu_top', array( $this, 'friends_add_menu_unread_messages' ) );
47 add_action( 'wp_ajax_friends-mark-read', array( $this, 'mark_message_read' ) );
48 add_action( 'rest_api_init', array( $this, 'add_rest_routes' ) );
49 add_action( 'friends_author_header', array( $this, 'friends_author_header' ), 10, 2 );
50 add_action( 'friends_after_header', array( $this, 'friends_display_messages' ), 10, 2 );
51 add_action( 'friends_after_header', array( $this, 'friends_message_form' ), 11, 2 );
52 add_filter( 'template_redirect', array( $this, 'handle_message_send' ), 10, 2 );
53 }
54
55
56 /**
57 * Registers the custom post type
58 */
59 public function register_custom_post_type() {
60 $labels = array(
61 'name' => __( 'Friend Messages', 'friends' ),
62 'singular_name' => __( 'Friend Message', 'friends' ),
63 'add_new' => _x( 'Add New', 'cached friend post', 'friends' ),
64 'add_new_item' => __( 'Add New Friend Message', 'friends' ),
65 'edit_item' => __( 'Edit Friend Message', 'friends' ),
66 'new_item' => __( 'New Friend Message', 'friends' ),
67 'all_items' => __( 'All Friend Messages', 'friends' ),
68 'view_item' => __( 'View Friend Message', 'friends' ),
69 'search_items' => __( 'Search Friend Messages', 'friends' ),
70 'not_found' => __( 'No Friend Messages found', 'friends' ),
71 'not_found_in_trash' => __( 'No Friend Messages found in the Trash', 'friends' ),
72 'parent_item_colon' => '',
73 'menu_name' => __( 'Friend Messages', 'friends' ),
74 );
75
76 $args = array(
77 'labels' => $labels,
78 'description' => "A friend's message",
79 'publicly_queryable' => Friends::has_required_privileges(),
80 'show_ui' => true,
81 'show_in_menu' => apply_filters( 'friends_show_cached_posts', false ),
82 'show_in_nav_menus' => false,
83 'show_in_admin_bar' => false,
84 'show_in_rest' => Friends::has_required_privileges(),
85 'exclude_from_search' => true,
86 'public' => false,
87 'delete_with_user' => true,
88 'menu_position' => 6,
89 'menu_icon' => 'dashicons-admin-comments',
90 'supports' => array( 'title', 'editor', 'author', 'revisions' ),
91 'taxonomies' => array( 'friend-reaction-' . get_current_user_id() ),
92 'has_archive' => true,
93 'rewrite' => false,
94 );
95
96 register_post_type( self::CPT, $args );
97
98 register_post_status(
99 'friends_unread',
100 array(
101 'label' => _x( 'Unread', 'message', 'friends' ),
102 'public' => false,
103 'exclude_from_search' => true,
104 'show_in_admin_all_list' => false,
105 'show_in_admin_status_list' => false,
106 // translators: %s; number of unread messages.
107 'label_count' => _n_noop( 'Unread (%s)', 'Unread (%s)', 'friends' ),
108 )
109 );
110
111 register_post_status(
112 'friends_read',
113 array(
114 'label' => _x( 'Read', '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 read messages.
120 'label_count' => _n_noop( 'Read (%s)', 'Read (%s)', 'friends' ),
121 )
122 );
123 }
124
125 /**
126 * Add the REST API to send and receive friend requests
127 */
128 public function add_rest_routes() {
129 register_rest_route(
130 REST::PREFIX,
131 'message',
132 array(
133 'methods' => 'POST',
134 'callback' => array( $this, 'rest_receive_message' ),
135 'permission_callback' => Friends::authenticated_for_posts(),
136 )
137 );
138 }
139
140 /**
141 * Receive a message via REST
142 *
143 * @param \WP_REST_Request $request The incoming request.
144 * @return array The array to be returned via the REST API.
145 */
146 public function rest_receive_message( \WP_REST_Request $request ) {
147 $tokens = explode( '-', $request->get_param( 'auth' ) );
148 $user_id = $this->friends->access_control->verify_token( $tokens[0], isset( $tokens[1] ) ? $tokens[1] : null, isset( $tokens[2] ) ? $tokens[2] : null );
149 if ( ! $user_id ) {
150 return new \WP_Error(
151 'friends_request_failed',
152 __( 'Could not respond to the request.', 'friends' ),
153 array(
154 'status' => 403,
155 )
156 );
157 }
158
159 $friend_user = new User( $user_id );
160 if ( ! $friend_user->has_cap( self::get_minimum_cap() ) ) {
161 return new \WP_Error(
162 'friends_request_failed',
163 __( 'Could not respond to the request.', 'friends' ),
164 array(
165 'status' => 403,
166 )
167 );
168 }
169
170 $subject = wp_unslash( $request->get_param( 'subject' ) );
171 $message = wp_unslash( $request->get_param( 'message' ) );
172 $this->add_message_to_post( $friend_user, $friend_user, $subject, $message, true );
173
174 do_action( 'notify_friend_message_received', $friend_user, $message, $subject );
175
176 return array(
177 'status' => 'message-received',
178 );
179 }
180
181 /**
182 * Adds a message to friends_message post.
183 *
184 * @param \WP_User $sender The sender.
185 * @param User $friend_user The friend user to which this should be associated.
186 * @param string $subject The subject.
187 * @param string $message The message.
188 *
189 * @return int The post ID.
190 */
191 private function add_message_to_post( \WP_User $sender, User $friend_user, $subject, $message ) {
192 $existing_message = new \WP_Query(
193 array(
194 'post_type' => self::CPT,
195 'author' => $friend_user->ID,
196 'title' => $subject,
197 'post_status' => array( 'friends_read', 'friends_unread' ),
198 )
199 );
200 $mark_unread = $sender->ID === $friend_user->ID;
201
202 $content = '';
203 $content .= '<!-- wp:friends/message {"sender":' . $sender->ID . ',"date":' . time() . '} -->' . PHP_EOL;
204 $content .= '<div class="wp-block-friends-message">';
205 $content .= '<span class="date">' . esc_html( gmdate( 'Y-m-d H:i:s' ) ) . '</span> ';
206 $content .= '<strong>' . esc_html( $sender->display_name ) . '</strong>: ';
207 if ( false === strpos( $message, '<!-- wp:' ) ) {
208 $content .= '<!-- wp:paragraph -->' . PHP_EOL . '<p>' . wp_kses_post( $message );
209 $content .= '</p>' . PHP_EOL;
210 $content .= '<!-- /wp:paragraph -->';
211 } else {
212 $content .= wp_kses_post( $message );
213 }
214 $content .= '</div>';
215 $content .= '<!-- /wp:friends/message -->';
216
217 if ( $existing_message->have_posts() ) {
218 $post = $existing_message->next_post();
219 $post_id = $post->ID;
220 wp_update_post(
221 array(
222 'ID' => $post->ID,
223 'post_content' => $post->post_content . PHP_EOL . $content,
224 'post_status' => $mark_unread ? 'friends_unread' : 'friends_read',
225 )
226 );
227 } else {
228 $post_id = $friend_user->insert_post(
229 array(
230 'post_type' => self::CPT,
231 'post_title' => $subject,
232 'post_content' => $content,
233 'post_status' => $mark_unread ? 'friends_unread' : 'friends_read',
234 )
235 );
236 }
237
238 return $post_id;
239 }
240
241 /**
242 * Add the unread messages to the total.
243 *
244 * @param int $unread The unread count.
245 *
246 * @return int Unread count + unread messages.
247 */
248 public function friends_unread_messages_count( $unread ) {
249 $unread_messages = new \WP_Query(
250 array(
251 'post_type' => self::CPT,
252 'post_status' => 'friends_unread',
253 )
254 );
255 return $unread + $unread_messages->post_count;
256 }
257
258 /**
259 * Add entries to the menu for unread messages.
260 *
261 * @param \WP_Menu $wp_menu The wp menu.
262 */
263 public function friends_add_menu_unread_messages( $wp_menu ) {
264 global $post;
265 $unread_messages = new \WP_Query(
266 array(
267 'post_type' => self::CPT,
268 'post_status' => 'friends_unread',
269 )
270 );
271
272 while ( $unread_messages->have_posts() ) {
273 $unread_messages->the_post();
274 $friend_user = User::get_post_author( $post );
275 $wp_menu->add_menu(
276 array(
277 'id' => 'friend-message-' . $friend_user->ID,
278 'parent' => 'friends-menu',
279 // translators: %s is the number of open friend requests.
280 'title' => '<span style="border-left: 2px solid #d63638; padding-left: .5em">' . esc_html( sprintf( __( 'New message from %s', 'friends' ), $friend_user->display_name ) ) . '</span>',
281 'href' => $friend_user->get_local_friends_page_url(),
282 )
283 );
284 }
285 }
286
287 /**
288 * Ajax function to mark a message as read.
289 *
290 * @return \WP_Error The wp error.
291 */
292 public function mark_message_read() {
293 check_ajax_referer( 'friends-mark-read' );
294
295 if ( ! is_user_logged_in() ) {
296 return new \WP_Error( 'unauthorized', 'You are not authorized to send a reaction.' );
297 }
298
299 if ( ! isset( $_POST['post_id'] ) ) {
300 wp_send_json_error(
301 array(
302 'result' => false,
303 )
304 );
305 }
306 if ( ! is_numeric( $_POST['post_id'] ) || $_POST['post_id'] <= 0 ) {
307 wp_send_json_error(
308 array(
309 'result' => false,
310 )
311 );
312 }
313
314 $post_id = intval( $_POST['post_id'] );
315 wp_update_post(
316 array(
317 'ID' => $post_id,
318 'post_status' => 'friends_read',
319 )
320 );
321
322 do_action( 'friends_message_read', $post_id );
323
324 wp_send_json_success(
325 array(
326 'result' => true,
327 )
328 );
329 }
330
331 /**
332 * Adds the friend requests to the unread count.
333 *
334 * @param int $unread The unread count.
335 *
336 * @return int Unread count + friend requests.
337 */
338 public function friends_unread_friend_request_count( $unread ) {
339 $friend_requests = User_Query::all_friend_requests();
340 return $unread + $friend_requests->get_total();
341 }
342
343 /**
344 * Extend the author header.
345 *
346 * @param User $friend_user The friend user.
347 * @param array $args The arguments.
348 */
349 public function friends_author_header( User $friend_user, $args ) {
350 if ( $friend_user->has_cap( self::get_minimum_cap() ) ) {
351 Friends::template_loader()->get_template_part(
352 'frontend/messages/author-header',
353 null,
354 $args
355 );
356
357 }
358 }
359
360 /**
361 * Display received messages by the friend user.
362 *
363 * @param array $args The arguments.
364 */
365 public function friends_display_messages( $args ) {
366 if ( ! isset( $args['friend_user'] ) || ! $args['friend_user'] ) {
367 return;
368 }
369
370 if ( $args['friend_user']->has_cap( self::get_minimum_cap() ) ) {
371 $args['existing_messages'] = new \WP_Query(
372 array(
373 'post_type' => self::CPT,
374 'author' => $args['friend_user']->ID,
375 'post_status' => array( 'friends_read', 'friends_unread' ),
376 )
377 );
378
379 if ( ! $args['existing_messages']->have_posts() ) {
380 return;
381 }
382
383 Friends::template_loader()->get_template_part(
384 'frontend/messages/friend',
385 null,
386 $args
387 );
388
389 }
390 }
391
392 /**
393 * Embed the message form for the friend user.
394 *
395 * @param array $args The arguments.
396 */
397 public function friends_message_form( $args ) {
398 if ( ! isset( $args['friend_user'] ) || ! $args['friend_user'] ) {
399 return;
400 }
401
402 $args['blocks-everywhere'] = false;
403
404 if ( $args['friend_user']->has_cap( self::get_minimum_cap() ) ) {
405 Friends::template_loader()->get_template_part(
406 'frontend/messages/message-form',
407 null,
408 $args
409 );
410
411 }
412 }
413
414 /**
415 * Gets the minimum capability necessary to use messages.
416 *
417 * @return string The minimum capability.
418 */
419 public static function get_minimum_cap() {
420 return apply_filters( 'friends_message_minimum_cap', 'friend' );
421 }
422
423 /**
424 * Sends a message to a friend.
425 *
426 * @param User $friend_user The friend user.
427 * @param string $message The message.
428 * @param string $subject The subject.
429 *
430 * @return \WP_Error|int An error or the message post id.
431 */
432 public function send_message( User $friend_user, $message, $subject = null ) {
433 if ( ! $friend_user->has_cap( self::get_minimum_cap() ) ) {
434 return new \WP_Error( 'not-a-friend', __( 'You cannot send messages to this user.', 'friends' ) );
435 }
436 if ( ! trim( $message ) ) {
437 return new \WP_Error( 'empty-message', __( 'You cannot send empty messages.', 'friends' ) );
438 }
439
440 if ( empty( $subject ) ) {
441 $subject = sprintf(
442 // translators: %1$s is a date, %2$s is a time.
443 __( 'Conversation started on %1$s at %2$s', 'friends' ),
444 gmdate( 'Y-m-d' ),
445 gmdate( 'H:i:s' )
446 );
447 }
448
449 $post_id = $this->add_message_to_post( wp_get_current_user(), $friend_user, $subject, $message );
450
451 $body = array(
452 'subject' => $subject,
453 'message' => $message,
454 'auth' => $friend_user->get_friend_auth(),
455 );
456
457 $response = wp_safe_remote_post(
458 $friend_user->get_rest_url() . '/message',
459 array(
460 'body' => $body,
461 'timeout' => 20,
462 'redirection' => 5,
463 )
464 );
465
466 if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
467 // TODO find a way to message the user.
468 return new \WP_Error( 'invalid-response', __( 'We received an unexpected response to our message.', 'friends' ) );
469 }
470
471 return $post_id;
472 }
473
474 /**
475 * Delete a conversation
476 *
477 * @param User $friend_user The friend user.
478 * @param string $subject The subject.
479 *
480 * @return \WP_Error|\WP_Post|bool The post or false.
481 */
482 public function delete_conversation( User $friend_user, $subject ) {
483 if ( ! $friend_user->has_cap( self::get_minimum_cap() ) ) {
484 return new \WP_Error( 'not-a-friend', __( 'You cannot delete converations.', 'friends' ) );
485 }
486
487 $existing_message = new \WP_Query(
488 array(
489 'post_type' => self::CPT,
490 'author' => $friend_user->ID,
491 'title' => $subject,
492 'post_status' => array( 'friends_read', 'friends_unread' ),
493 )
494 );
495
496 if ( $existing_message->have_posts() ) {
497 $post = $existing_message->next_post();
498 return wp_trash_post( $post->ID );
499 }
500
501 return false;
502 }
503
504 /**
505 * Handle the message form.
506 */
507 public function handle_message_send() {
508 if ( ! isset( $_REQUEST['friends_message_recipient'] ) ) {
509 return;
510 }
511
512 if ( isset( $_REQUEST['friends_message_delete_conversation'] ) ) {
513 return $this->handle_conversation_delete();
514 }
515
516 if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'friends_send_message' ) ) {
517 wp_die( esc_html( __( 'Error - unable to verify nonce, please try again.', 'friends' ) ) );
518 }
519
520 $friend_user = new User( $_REQUEST['friends_message_recipient'] );
521
522 $subject = wp_unslash( $_REQUEST['friends_message_subject'] );
523 $message = wp_unslash( $_REQUEST['friends_message_message'] );
524
525 $error = $this->send_message( $friend_user, $message, $subject );
526
527 if ( is_wp_error( $error ) ) {
528 wp_die( esc_html( $error->get_error_message() ) );
529 }
530
531 wp_safe_redirect( $friend_user->get_local_friends_page_url() );
532 exit;
533 }
534
535 /**
536 * Handle the deletion of the conversation.
537 */
538 public function handle_conversation_delete() {
539 if ( ! isset( $_REQUEST['friends_message_delete_conversation'] ) ) {
540 return;
541 }
542
543 if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'friends_send_message' ) ) {
544 wp_die( esc_html( __( 'Error - unable to verify nonce, please try again.', 'friends' ) ) );
545 }
546
547 $friend_user = new User( $_REQUEST['friends_message_recipient'] );
548
549 $subject = wp_unslash( $_REQUEST['friends_message_subject'] );
550 $error = $this->delete_conversation( $friend_user, $subject );
551
552 if ( is_wp_error( $error ) ) {
553 wp_die( esc_html( $error->get_error_message() ) );
554 }
555
556 wp_safe_redirect( $friend_user->get_local_friends_page_url() );
557 exit;
558 }
559 }
560