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-user.php

class-user.php in Friends 2.8.8, at includes/class-user.php

1,285 lines 36.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Friend User
4 *
5 * This wraps \WP_User and adds friend specific functions.
6 *
7 * @package Friends
8 */
9
10 namespace Friends;
11
12 /**
13 * This is the class for the User part of the Friends Plugin.
14 *
15 * @since 0.21
16 *
17 * @package Friends
18 * @author Alex Kirk
19 */
20 class User extends \WP_User {
21 /**
22 * Caches the feed rules.
23 *
24 * @var array
25 */
26 public static $feed_rules = array();
27
28 /**
29 * Caches the feed catch all action.
30 *
31 * @var array
32 */
33 public static $feed_catch_all = array();
34
35 public static function get_by_username( $username ) {
36 $subscription = Subscription::get_by_username( $username );
37 if ( $subscription && ! is_wp_error( $subscription ) ) {
38 return $subscription;
39 }
40
41 $user = get_user_by( 'login', $username );
42 if ( $user && ! is_wp_error( $user ) ) {
43 return new self( $user );
44 }
45 return $user;
46 }
47
48 public static function get_post_author( \WP_Post $post ) {
49 $subscriptions = wp_get_object_terms( $post->ID, Subscription::TAXONOMY );
50 if ( empty( $subscriptions ) ) {
51 return new self( $post->post_author );
52 }
53
54 return new Subscription( reset( $subscriptions ) );
55 }
56
57 /**
58 * Create a User with a specific Friends-related role
59 *
60 * @param string $user_login The user login.
61 * @param string $role The role: subscription,
62 * pending_friend_request,
63 * or friend_request.
64 * @param string $url The site URL for which
65 * to create the user.
66 * @param string $display_name The user's display name.
67 * @param string $avatar_url The user_avatar_url URL.
68 * @param string $description A description for the user.
69 * @param string $user_registered When the user was registered.
70 * @param bool $subscription_override Whether to override the automatic creation of a subscription.
71 *
72 * @return User|\WP_Error The created user or an error.
73 */
74 public static function create( $user_login, $role, $url, $display_name = null, $avatar_url = null, $description = null, $user_registered = null, $subscription_override = false ) {
75 if ( 'subscription' === $role && ! $subscription_override ) {
76 return Subscription::create( $user_login, $role, $url, $display_name, $avatar_url, $description );
77 }
78
79 $role_rank = array_flip(
80 array(
81 'subscription',
82 'pending_friend_request',
83 'friend_request',
84 )
85 );
86 if ( ! isset( $role_rank[ $role ] ) ) {
87 return new \WP_Error( 'invalid_role', 'Invalid role for creation specified' );
88 }
89
90 if ( is_multisite() ) {
91 $user = get_user_by( 'login', $user_login );
92 if ( $user && ! self::is_friends_plugin_user( $user ) ) {
93 if ( ! is_user_member_of_blog( $user->ID, get_current_blog_id() ) ) {
94 add_user_to_blog( get_current_blog_id(), $user->ID, $role );
95 }
96 }
97 }
98
99 $friend_user = self::get_user( $user_login );
100 if ( $friend_user && ! is_wp_error( $friend_user ) ) {
101
102 foreach ( $role_rank as $_role => $rank ) {
103 if ( $rank > $role_rank[ $role ] ) {
104 break;
105 }
106 if ( $friend_user->has_cap( $_role ) ) {
107 // Upgrade user role.
108 $friend_user->set_role( $role );
109 break;
110 }
111 }
112 return $friend_user;
113 }
114
115 $userdata = array(
116 'user_login' => $user_login,
117 'display_name' => $display_name,
118 'first_name' => $display_name,
119 'nickname' => $display_name,
120 'description' => $description,
121 'user_url' => $url,
122 'user_pass' => wp_generate_password( 256 ),
123 'role' => $role,
124 );
125
126 if ( $user_registered ) {
127 $userdata['user_registered'] = $user_registered;
128 }
129
130 $friend_id = wp_insert_user( $userdata );
131
132 $friend_user = new User( $friend_id );
133 $friend_user->update_user_option( 'friends_new_friend', true );
134 $friend_user->update_user_icon_url( $avatar_url );
135
136 do_action( 'friends_after_create_friend_user', $friend_user );
137 return $friend_user;
138 }
139
140 public static function register_wrapper_hooks() {
141 add_filter( 'friends_get_user_feeds', array( 'Friends\User', 'friends_get_user_feeds' ), 10, 2 );
142 }
143
144 /**
145 * Get the feeds for a user.
146 *
147 * @param array $feeds The feeds.
148 * @param \WP_User $user The user.
149 */
150 public static function friends_get_user_feeds( $feeds, $user ) {
151 $user_feeds = $user->get_feeds();
152 if ( is_array( $user_feeds ) ) {
153 $feeds = array_merge( $feeds, $user_feeds );
154 }
155 return $feeds;
156 }
157
158 /**
159 * Convert a site URL to a username
160 *
161 * @param string $url The site URL in question.
162 * @param bool $multisite_shortname Whether to use the multisite shortname.
163 * @return string The corresponding username.
164 */
165 public static function get_user_login_for_url( $url, $multisite_shortname = true ) {
166 $multisite_user = self::get_multisite_user( $url );
167 if ( $multisite_user && $multisite_shortname ) {
168 return $multisite_user->user_login;
169 }
170
171 $user_login = self::sanitize_username( self::get_display_name_for_url( $url, $multisite_shortname ) );
172 return $user_login;
173 }
174
175 /**
176 * Convert a site URL to a display name
177 *
178 * @param string $url The site URL in question.
179 * @param bool $multisite_shortname Whether to use the multisite shortname.
180 * @return string The corresponding display name.
181 */
182 public static function get_display_name_for_url( $url, $multisite_shortname = true ) {
183 $multisite_user = self::get_multisite_user( $url );
184 if ( $multisite_user && $multisite_shortname ) {
185 return $multisite_user->display_name;
186 }
187
188 $host = wp_parse_url( $url, PHP_URL_HOST );
189 $path = wp_parse_url( $url, PHP_URL_PATH );
190
191 $display_name = sanitize_text_field( preg_replace( '#^www\.#', '', preg_replace( '#[^a-z0-9.-]+#i', ' ', strtolower( $host . ' ' . $path ) ) ) );
192 return $display_name;
193 }
194
195 /**
196 * Discover a display name from feeds
197 *
198 * @param array $feeds A list of feeds.
199 * @return string The corresponding display name.
200 */
201 public static function get_display_name_from_feeds( $feeds ) {
202 foreach ( $feeds as $feed ) {
203 if ( 'self' === $feed['rel'] ) {
204 return sanitize_text_field( $feed['title'] );
205 }
206 }
207
208 return false;
209 }
210
211 /**
212 * If the URL is on the same multisite, get the main user.
213 *
214 * @param string $url The site URL in question.
215 * @return bool|WP_User false or the user.
216 */
217 public static function get_multisite_user( $url ) {
218 if ( ! is_multisite() ) {
219 return false;
220 }
221
222 $host = wp_parse_url( $url, PHP_URL_HOST );
223 if ( ! $host ) {
224 return false;
225 }
226 $path = wp_parse_url( $url, PHP_URL_PATH );
227
228 $site_id = get_blog_id_from_url( $host, trailingslashit( $path ) );
229 if ( ! $site_id ) {
230 return false;
231 }
232
233 // Let's find the user that is associated with that blog.
234 switch_to_blog( $site_id );
235 $friend_user_id = Friends::get_main_friend_user_id();
236 restore_current_blog();
237
238 return get_user_by( 'id', $friend_user_id );
239 }
240
241 /**
242 * Sanitize the username according to some more rules than just sanitize_user()
243 *
244 * @param string $username The username.
245 *
246 * @return string The sanitized username.
247 */
248 public static function sanitize_username( $username ) {
249 $username = preg_replace( '/[^a-z0-9.]+/', '-', strtolower( $username ) );
250 $username = sanitize_user( $username );
251 return $username;
252 }
253
254 /**
255 * Determines whether the specified user is a friends plugin user.
256 *
257 * @param \WP_User $user The user.
258 *
259 * @return bool True if the specified user is a friends plugin user, False otherwise.
260 */
261 public static function is_friends_plugin_user( \WP_User $user ) {
262 return $user->has_cap( 'friend' ) || $user->has_cap( 'pending_friend_request' ) || $user->has_cap( 'friend_request' ) || $user->has_cap( 'subscription' );
263 }
264
265 /**
266 * Get a friend user for a user_login.
267 *
268 * @param string $user_login The user login.
269 * @return User|false The friend user or false.
270 */
271 public static function get_user( $user_login ) {
272 $user = get_user_by( 'login', $user_login );
273 if ( $user ) {
274 if ( self::is_friends_plugin_user( $user ) ) {
275 return new self( $user );
276 }
277
278 return false;
279 }
280 return $user;
281 }
282
283 /**
284 * Get a friend user for a user_id.
285 *
286 * @param string $user_id The user ID.
287 * @return User|false The friend user or false.
288 */
289 public static function get_user_by_id( $user_id ) {
290 if ( 0 === strpos( $user_id, 'friends-virtual-user-' ) ) {
291 $term_id = substr( $user_id, strlen( 'friends-virtual-user-' ) );
292 $term = get_term( intval( $term_id ), Subscription::TAXONOMY );
293 if ( $term ) {
294 return new Subscription( $term );
295 }
296 }
297
298 $user = get_user_by( 'ID', $user_id );
299 if ( $user ) {
300 if ( $user->has_cap( 'friend' ) || $user->has_cap( 'pending_friend_request' ) || $user->has_cap( 'friend_request' ) || $user->has_cap( 'subscription' ) ) {
301 return new self( $user );
302 }
303
304 return false;
305 }
306 return $user;
307 }
308
309 public function __get( $key ) {
310 if ( 'user_url' === $key && empty( $this->data->user_url ) && is_multisite() ) {
311 $site = get_active_blog_for_user( $this->ID );
312 if ( $site ) {
313 // Ensure we're using the same URL protocol.
314 $this->data->user_url = set_url_scheme( $site->siteurl );
315 return $this->data->user_url;
316 }
317 }
318
319 return parent::__get( $key );
320 }
321 /**
322 * Sends a message to the friend..
323 *
324 * @param string $message The message.
325 * @param string $subject The subject.
326 *
327 * @return \WP_Error|bool True if the message was sent successfully.
328 */
329 public function send_message( $message, $subject = null ) {
330 $friends = Friends::get_instance();
331 return $friends->messages->send_message( $this, $message, $subject );
332 }
333
334 public function insert_post( array $postarr, $wp_error = false, $fire_after_hooks = true ) {
335 $current_user = wp_get_current_user();
336
337 // Posts and revisions should be associated with this user.
338 wp_set_current_user( $this->ID );
339
340 $post = wp_insert_post( $postarr, $wp_error, $fire_after_hooks );
341
342 if ( $current_user ) {
343 wp_set_current_user( $current_user->ID );
344 }
345
346 return $post;
347 }
348
349 public function get_object_id() {
350 return $this->ID;
351 }
352
353 public function save() {
354 return wp_update_user( $this );
355 }
356
357 /**
358 * Save multiple feeds for a user.
359 *
360 * @param string $feeds The feed URLs to subscribe to.
361 *
362 * @return array(\WP_Term)|\WP_error $user The new associated user or an error object.
363 */
364 public function save_feeds( $feeds = array() ) {
365 $errors = new \WP_Error();
366 foreach ( $feeds as $feed_url => $options ) {
367 if ( ! is_string( $feed_url ) || ! Friends::check_url( $feed_url ) ) {
368 $errors->add( 'invalid-url', 'An invalid URL was provided' );
369 unset( $feeds[ $feed_url ] );
370 continue;
371 }
372
373 $default_options = array(
374 'active' => false,
375 'parser' => 'simplepie',
376 'post-format' => 'standard',
377 'mime-type' => 'application/rss+xml',
378 'title' => $this->display_name . ' RSS Feed',
379 );
380
381 $feeds[ $feed_url ] = array_merge( $default_options, $options );
382 }
383
384 $all_urls = array();
385 foreach ( wp_get_object_terms( $this->get_object_id(), User_Feed::TAXONOMY ) as $term ) {
386 $all_urls[ $term->name ] = $term->term_id;
387 }
388
389 $user_feeds = wp_set_object_terms( $this->get_object_id(), array_keys( array_merge( $all_urls, $feeds ) ), User_Feed::TAXONOMY );
390 if ( is_wp_error( $user_feeds ) ) {
391 return $user_feeds;
392 }
393
394 foreach ( wp_get_object_terms( $this->get_object_id(), User_Feed::TAXONOMY ) as $term ) {
395 $all_urls[ $term->name ] = $term->term_id;
396 }
397
398 foreach ( $feeds as $url => $feed_options ) {
399 if ( ! isset( $all_urls[ $url ] ) ) {
400 continue;
401 }
402 $term_id = $all_urls[ $url ];
403 foreach ( $feed_options as $key => $value ) {
404 if ( in_array( $key, array( 'active', 'parser', 'post-format', 'mime-type', 'title', 'interval', 'modifier' ) ) ) {
405
406 if ( metadata_exists( 'term', $term_id, $key ) ) {
407 update_metadata( 'term', $term_id, $key, $value );
408 } else {
409 add_metadata( 'term', $term_id, $key, $value, true );
410 }
411 }
412 }
413 }
414
415 if ( $errors->has_errors() ) {
416 return $errors;
417 }
418
419 return $all_urls;
420 }
421
422 /**
423 * Save a feed url for a user.
424 *
425 * @param string $feed_url The feed URL to subscribe to.
426 * @param array $options The options.
427 *
428 * @return User_Feed|\WP_Error $user The new feed or an error object.
429 */
430 public function save_feed( $feed_url, $options = array() ) {
431 $all_urls = $this->save_feeds( array( $feed_url => $options ) );
432
433 if ( is_wp_error( $all_urls ) ) {
434 return $all_urls;
435 }
436
437 if ( ! isset( $all_urls[ $feed_url ] ) ) {
438 return new \WP_Error(
439 'error-saving-feed',
440 sprintf(
441 // translators: %s is a URL.
442 __( 'The feed %s could not be saved', 'friends' ),
443 $feed_url
444 )
445 );
446 }
447
448 $term = get_term( $all_urls[ $feed_url ], User_Feed::TAXONOMY );
449
450 return new User_Feed( $term );
451 }
452
453 /**
454 * Subscribe to a friends site without becoming a friend
455 *
456 * @param string $feed_url The feed URL to subscribe to.
457 * @param array $options The options.
458 *
459 * @return User_Feed|\WP_error $user The new associated user or an error object.
460 */
461 public function subscribe( $feed_url, $options = array() ) {
462 $options['active'] = true;
463 return $this->save_feed( $feed_url, $options );
464 }
465
466 /**
467 * Retrieve the posts for this user
468 *
469 * @return array The new posts.
470 */
471 public function retrieve_posts_from_active_feeds() {
472 return $this->retrieve_posts_from_feeds( $this->get_active_feeds() );
473 }
474
475 /**
476 * Retrieve the posts for these user feeds
477 *
478 * @param array $feeds The feeds to retrieve from.
479 *
480 * @return array The new posts.
481 */
482 public function retrieve_posts_from_feeds( array $feeds ) {
483 $friends = Friends::get_instance();
484 $new_posts = array();
485 foreach ( $feeds as $feed ) {
486 $posts = $friends->feed->retrieve_feed( $feed );
487 if ( ! is_wp_error( $posts ) ) {
488 $new_posts = array_merge( $new_posts, $posts );
489 }
490 }
491
492 $deleted_posts = $this->delete_outdated_posts();
493
494 return array_values( array_diff( $new_posts, $deleted_posts ) );
495 }
496
497 public function modify_query_by_author( \WP_Query $query ) {
498 $query->set( 'author', $this->ID );
499 if ( ! user_can( $this->ID, 'friends_plugin' ) || user_can( $this->ID, 'administrator' ) ) {
500 // If the user doesn't belong to the friends plugin, only show their local posts so that subcriptions don't spill in.
501 $query->set( 'post_type', 'post' );
502 }
503 return $query;
504 }
505
506 public function modify_get_posts_args_by_author( $args ) {
507 $args['author'] = $this->ID;
508 return $args;
509 }
510
511 public function delete() {
512 if ( is_multisite() ) {
513 remove_user_from_blog( $this->ID, get_current_blog_id() );
514 } else {
515 wp_delete_user( $this->ID );
516 }
517 // The content (posts and feeds) will be deleted with the 'delete_user' hook.
518 }
519
520 /**
521 * Delete posts the user decided to automatically delete.
522 */
523 public function delete_outdated_posts() {
524 $deleted_posts = array();
525
526 $args = array(
527 'post_type' => Friends::CPT,
528 );
529
530 if ( $this->is_retention_days_enabled() ) {
531 $args['date_query'] = array(
532 'before' => gmdate( 'Y-m-d H:i:s', strtotime( '-' . ( $this->get_retention_days() * 24 ) . 'hours' ) ),
533 );
534 } elseif ( get_option( 'friends_enable_retention_days' ) ) {
535 $args['date_query'] = array(
536 'before' => gmdate( 'Y-m-d H:i:s', strtotime( '-' . ( Friends::get_retention_days() * 24 ) . 'hours' ) ),
537 );
538 }
539
540 if ( isset( $args['date_query'] ) ) {
541 $query = new \WP_Query();
542 foreach ( $args as $key => $value ) {
543 $query->set( $key, $value );
544 }
545 $query = $this->modify_query_by_author( $query );
546
547 foreach ( $query->get_posts() as $post ) {
548 if ( apply_filters( 'friends_debug', false ) && ! wp_doing_cron() ) {
549 echo 'Deleting ', esc_html( $post->ID ), '<br/>';
550 }
551 wp_delete_post( $post->ID, true );
552 $deleted_posts[] = $post->ID;
553 }
554 }
555
556 unset( $args['date_query'] );
557 $args['orderby'] = 'date';
558 $args['order'] = 'asc';
559 if ( $this->is_retention_number_enabled() ) {
560 $args['offset'] = $this->get_retention_number();
561 $query = new \WP_Query();
562 foreach ( $args as $key => $value ) {
563 $query->set( $key, $value );
564 }
565 $query = $this->modify_query_by_author( $query );
566
567 foreach ( $query->get_posts() as $post ) {
568 if ( apply_filters( 'friends_debug', false ) && ! wp_doing_cron() ) {
569 echo 'Deleting ', esc_html( $post->ID ), '<br/>';
570 }
571 wp_delete_post( $post->ID, true );
572 $deleted_posts[] = $post->ID;
573 }
574 }
575
576 // Global setting.
577 if ( get_option( 'friends_enable_retention_number' ) ) {
578 unset( $args['author'] );
579 $args['offset'] = Friends::get_retention_number();
580 $query = new \WP_Query();
581 foreach ( $args as $key => $value ) {
582 $query->set( $key, $value );
583 }
584 $query = $this->modify_query_by_author( $query );
585
586 foreach ( $query->get_posts() as $post ) {
587 if ( apply_filters( 'friends_debug', false ) && ! wp_doing_cron() ) {
588 echo 'Deleting ', esc_html( $post->ID ), '<br/>';
589 }
590 wp_delete_post( $post->ID, true );
591 $deleted_posts[] = $post->ID;
592 }
593 }
594
595 // In any case, don't overflow the trash.
596 $args = array(
597 'post_type' => Friends::CPT,
598 'post_status' => 'trash',
599 'offset' => 30,
600 );
601
602 $query = new \WP_Query();
603 foreach ( $args as $key => $value ) {
604 $query->set( $key, $value );
605 }
606 $query = $this->modify_query_by_author( $query );
607
608 foreach ( $query->get_posts() as $post ) {
609 if ( apply_filters( 'friends_debug', false ) && ! wp_doing_cron() ) {
610 echo 'Deleting ', esc_html( $post->ID ), '<br/>';
611 }
612 wp_delete_post( $post->ID, true );
613 $deleted_posts[ $post->ID ] = $post->ID;
614 }
615
616 return $deleted_posts;
617 }
618
619 /**
620 * Check whether the retention by number of posts is enabled.
621 *
622 * @return boolean Whether the retention by number of posts is enabled.
623 */
624 public function is_retention_number_enabled() {
625 return $this->get_user_option( 'friends_enable_retention_number' );
626 }
627
628 /**
629 * Enable or disable the retention by number of posts.
630 *
631 * @param boolean $enabled Whether the retention by number of posts should be enabled.
632 * @return boolean Whether the retention by number of posts is enabled.
633 */
634 public function set_retention_number_enabled( $enabled ) {
635 $this->update_user_option( 'friends_enable_retention_number', boolval( $enabled ) );
636 return boolval( $enabled );
637 }
638
639 /**
640 * Get the retention by number of posts.
641 *
642 * @return int The retention by number of posts.
643 */
644 public function get_retention_number() {
645 $number = $this->get_user_option( 'friends_retention_number' );
646 if ( $number <= 0 ) {
647 return 200;
648 }
649
650 return $number;
651 }
652
653 /**
654 * Set the retention by number of posts.
655 *
656 * @param int $number The retention by number of posts.
657 * @return int The retention by number of posts.
658 */
659 public function set_retention_number( $number ) {
660 $number = max( 1, $number );
661 $this->update_user_option( 'friends_retention_number', $number );
662 return $number;
663 }
664 /**
665 * Check whether the retention by days of posts is enabled.
666 *
667 * @return boolean Whether the retention by days of posts is enabled.
668 */
669 public function is_retention_days_enabled() {
670 return $this->get_user_option( 'friends_enable_retention_days' );
671 }
672
673 /**
674 * Enable or disable the retention by days of posts.
675 *
676 * @param boolean $enabled Whether the retention by days of posts should be enabled.
677 * @return boolean Whether the retention by days of posts is enabled.
678 */
679 public function set_retention_days_enabled( $enabled ) {
680 $this->update_user_option( 'friends_enable_retention_days', boolval( $enabled ) );
681 return boolval( $enabled );
682 }
683
684 /**
685 * Get the retention by days of posts.
686 *
687 * @return int The retention by days of posts.
688 */
689 public function get_retention_days() {
690 $days = $this->get_user_option( 'friends_retention_days' );
691 if ( $days <= 0 ) {
692 return 14;
693 }
694
695 return $days;
696 }
697
698 /**
699 * Set the retention by days of posts.
700 *
701 * @param int $days The retention by days of posts.
702 * @return int The retention by days of posts.
703 */
704 public function set_retention_days( $days ) {
705 $days = max( 1, intval( $days ) );
706 return $this->update_user_option( 'friends_retention_days', $days );
707 return $days;
708 }
709
710 /**
711 * Gets the post counts by post format.
712 *
713 * @return int The post count.
714 */
715 public function get_post_in_trash_count() {
716 global $wpdb;
717 $post_types = apply_filters( 'friends_frontend_post_types', array() );
718
719 $count = $wpdb->get_var(
720 $wpdb->prepare(
721 "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type IN ( " . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ' ) AND post_status = "trash" AND post_author = %d',
722 array_merge( $post_types, array( $this->ID ) )
723 )
724 );
725
726 return intval( $count );
727 }
728
729 /**
730 * Gets the post counts by post format.
731 *
732 * @return array The post counts.
733 */
734 public function get_post_count_by_post_format() {
735 $cache_key = 'friends_post_count_by_post_format_author_' . $this->ID;
736
737 $counts = get_transient( $cache_key );
738 if ( true || false === $counts ) {
739 $counts = array();
740 $post_types = apply_filters( 'friends_frontend_post_types', array() );
741 $post_formats_term_ids = array();
742 foreach ( get_post_format_slugs() as $post_format ) {
743 $term = get_term_by( 'slug', 'post-format-' . $post_format, 'post_format' );
744 if ( $term ) {
745 $post_formats_term_ids[ $term->term_id ] = $post_format;
746 }
747 }
748
749 global $wpdb;
750
751 $counts = array();
752 $counts['standard'] = $wpdb->get_var(
753 $wpdb->prepare(
754 sprintf(
755 "SELECT COUNT(DISTINCT posts.ID)
756 FROM %s AS posts
757 JOIN %s AS relationships_post_format
758
759 WHERE posts.post_author = %s
760 AND posts.post_status IN ( 'publish', 'private' )
761 AND posts.post_type IN ( %s )
762 AND relationships_post_format.object_id = posts.ID",
763 $wpdb->posts,
764 $wpdb->term_relationships,
765 '%d',
766 implode( ',', array_fill( 0, count( $post_types ), '%s' ) )
767 ),
768 array_merge(
769 array( $this->ID ),
770 $post_types
771 )
772 )
773 );
774
775 if ( ! empty( $post_formats_term_ids ) ) {
776 $post_format_counts = $wpdb->get_results(
777 $wpdb->prepare(
778 sprintf(
779 "SELECT relationships_post_format.term_taxonomy_id AS post_format_id, COUNT(relationships_post_format.term_taxonomy_id) AS count
780 FROM %s AS posts
781 JOIN %s AS relationships_post_format
782
783 WHERE posts.post_author = %s
784 AND posts.post_status IN ( 'publish', 'private' )
785 AND posts.post_type IN ( %s )
786 AND relationships_post_format.object_id = posts.ID
787 AND relationships_post_format.term_taxonomy_id IN ( %s )
788 GROUP BY relationships_post_format.term_taxonomy_id",
789 $wpdb->posts,
790 $wpdb->term_relationships,
791 '%d',
792 implode( ',', array_fill( 0, count( $post_types ), '%s' ) ),
793 implode( ',', array_fill( 0, count( $post_formats_term_ids ), '%d' ) )
794 ),
795 array_merge(
796 array( $this->ID ),
797 $post_types,
798 array_keys( $post_formats_term_ids )
799 )
800 )
801 );
802
803 foreach ( $post_format_counts as $row ) {
804 $counts[ $post_formats_term_ids[ $row->post_format_id ] ] = $row->count;
805 $counts['standard'] -= $row->count;
806 }
807 }
808
809 $counts = array_filter( $counts );
810
811 set_transient( $cache_key, $counts, HOUR_IN_SECONDS );
812 }
813
814 return $counts;
815 }
816
817 /**
818 * Gets the post stats.
819 *
820 * @return object The post stats.
821 */
822 public function get_post_stats() {
823 global $wpdb;
824 $post_types = apply_filters( 'friends_frontend_post_types', array() );
825 $post_stats = $wpdb->get_row(
826 $wpdb->prepare(
827 'SELECT SUM(
828 LENGTH( ID ) +
829 LENGTH( post_author ) +
830 LENGTH( post_date ) +
831 LENGTH( post_date_gmt ) +
832 LENGTH( post_content ) +
833 LENGTH( post_title ) +
834 LENGTH( post_excerpt ) +
835 LENGTH( post_status ) +
836 LENGTH( comment_status ) +
837 LENGTH( ping_status ) +
838 LENGTH( post_password ) +
839 LENGTH( post_name ) +
840 LENGTH( to_ping ) +
841 LENGTH( pinged ) +
842 LENGTH( post_modified ) +
843 LENGTH( post_modified_gmt ) +
844 LENGTH( post_content_filtered ) +
845 LENGTH( post_parent ) +
846 LENGTH( guid ) +
847 LENGTH( menu_order ) +
848 LENGTH( post_type ) +
849 LENGTH( post_mime_type ) +
850 LENGTH( comment_count )
851 ) AS total_size,
852 COUNT(*) as post_count
853 FROM ' . $wpdb->posts . ' WHERE post_author = %d AND post_type IN ( ' . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ' )',
854 array_merge( array( $this->ID ), $post_types )
855 ),
856 ARRAY_A
857 );
858 $post_stats['earliest_post_date'] = mysql2date(
859 'U',
860 $wpdb->get_var(
861 $wpdb->prepare(
862 "SELECT MIN(post_date) FROM $wpdb->posts WHERE post_author = %d AND post_status = 'publish' AND post_type IN ( " . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ' )',
863 array_merge( array( $this->ID ), $post_types )
864 )
865 )
866 );
867 return $post_stats;
868 }
869
870 public function get_all_post_ids() {
871 global $wpdb;
872 $post_types_to_delete = implode( "', '", apply_filters( 'friends_frontend_post_types', array() ) );
873
874 $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d AND post_type IN ('$post_types_to_delete')", $this->ID ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
875 return $post_ids;
876 }
877
878 /**
879 * Update a friend's avatar URL
880 *
881 * @param string $user_icon_url The user icon URL.
882 * @return string|false The URL that was set or false.
883 */
884 public function update_user_icon_url( $user_icon_url ) {
885 if ( ! $user_icon_url ) {
886 $user_icon_url = Mf2\resolveUrl( $this->user_url, '/favicon.ico' );
887 }
888
889 if ( $user_icon_url && Friends::check_url( $user_icon_url ) ) {
890 $this->update_user_option( 'friends_user_icon_url', $user_icon_url );
891 return $user_icon_url;
892 }
893
894 return false;
895 }
896
897 /**
898 * Retrieve the rules for this feed.
899 *
900 * @return array The rules set by the user for this feed.
901 */
902 public function get_feed_rules() {
903 if ( ! isset( self::$feed_rules[ $this->ID ] ) ) {
904 $this->set_feed_rules( $this->get_user_option( 'friends_feed_rules' ) );
905 }
906 return self::$feed_rules[ $this->ID ];
907 }
908
909 /**
910 * Set the rules for this feed.
911 *
912 * @param array $rules The rules to set for this feed.
913 * @return array The rules set by the user for this feed.
914 */
915 public function set_feed_rules( $rules ) {
916 $friends = Friends::get_instance();
917 self::$feed_rules[ $this->ID ] = $friends->feed->validate_feed_rules( $rules );
918 return self::$feed_rules[ $this->ID ];
919 }
920
921
922 /**
923 * Retrieve the catch_all value for this feed.
924 *
925 * @return array The rules set by the user for this feed.
926 */
927 public function get_feed_catch_all() {
928 if ( ! isset( self::$feed_catch_all[ $this->ID ] ) ) {
929 $this->set_feed_catch_all( $this->get_user_option( 'friends_feed_catch_all' ) );
930 }
931 return self::$feed_catch_all[ $this->ID ];
932 }
933
934
935 /**
936 * Set the catch_all value for this feed.
937 *
938 * @param array $catchall The catchall rule to set for this feed.
939 * @return array The catchall rukle set by the user for this feed.
940 */
941 public function set_feed_catch_all( $catchall ) {
942 $friends = Friends::get_instance();
943 self::$feed_catch_all[ $this->ID ] = $friends->feed->validate_feed_catch_all( $catchall );
944 return self::$feed_catch_all[ $this->ID ];
945 }
946
947 /**
948 * Retrieve the remote post ids.
949 *
950 * @return array A mapping of the remote post ids.
951 */
952 public function get_remote_post_ids() {
953 $remote_post_ids = array();
954 $existing_posts = new \WP_Query(
955 array(
956 'post_type' => Friends::CPT,
957 'post_status' => array( 'publish', 'private', 'trash' ),
958 'author' => $this->ID,
959 'nopaging' => true,
960 )
961 );
962
963 if ( $existing_posts->have_posts() ) {
964 while ( $existing_posts->have_posts() ) {
965 $post = $existing_posts->next_post();
966 $remote_post_id = get_post_meta( $post->ID, 'remote_post_id', true );
967 if ( $remote_post_id ) {
968 $remote_post_ids[ $remote_post_id ] = $post->ID;
969 }
970 $permalink = get_permalink( $post );
971 $remote_post_ids[ $permalink ] = $post->ID;
972 $permalink = str_replace( array( '&#38;', '&#038;' ), '&', ent2ncr( $permalink ) );
973 $remote_post_ids[ $permalink ] = $post->ID;
974 }
975 }
976
977 do_action( 'friends_remote_post_ids', $remote_post_ids );
978 return $remote_post_ids;
979 }
980
981 /**
982 * Get the user's feeds (and potentially convert old-style feed URL).
983 *
984 * @return array An array of User_Feed items.
985 */
986 public function get_feeds() {
987 $term_query = new \WP_Term_Query(
988 array(
989 'taxonomy' => User_Feed::TAXONOMY,
990 'object_ids' => $this->get_object_id(),
991 )
992 );
993
994 $feeds = array();
995 foreach ( $term_query->get_terms() as $term ) {
996 $feeds[ $term->term_id ] = new User_Feed( $term, $this );
997 }
998
999 return $feeds;
1000 }
1001
1002 /**
1003 * Get just the user's active feeds.
1004 *
1005 * @return array An array of active User_Feed items.
1006 */
1007 public function get_active_feeds() {
1008 $active_feeds = array();
1009 foreach ( $this->get_feeds() as $feed ) {
1010 if ( $feed->is_active() ) {
1011 $active_feeds[] = $feed;
1012 }
1013 }
1014 return $active_feeds;
1015 }
1016
1017 /**
1018 * Get just the user's active feeds.
1019 *
1020 * @return array An array of active User_Feed items.
1021 */
1022 public function get_due_feeds() {
1023 $due_feeds = array();
1024 foreach ( $this->get_active_feeds() as $feed ) {
1025 // Explicitly use time() to allow mocking it inside the namespace.
1026 if ( gmdate( 'Y-m-d H:i:s', time() ) >= $feed->get_next_poll() ) {
1027 $due_feeds[] = $feed;
1028 }
1029 }
1030 return $due_feeds;
1031 }
1032
1033 /**
1034 * Determines whether the user can have feeds refreshed.
1035 *
1036 * @return bool True if able to refresh feeds, False otherwise.
1037 */
1038 public function can_refresh_feeds() {
1039 return $this->has_cap( 'subscription' ) ||
1040 $this->has_cap( 'acquaintance' ) ||
1041 $this->has_cap( 'friend' ) ||
1042 $this->has_cap( 'pending_friend_request' );
1043 }
1044
1045 /**
1046 * Convert a user to a friend
1047 *
1048 * @param string $out_token The token to authenticate against the remote.
1049 * @param string $in_token The token the remote needs to use to authenticate to us.
1050 */
1051 public function make_friend( $out_token, $in_token ) {
1052 $this->update_user_option( 'friends_out_token', $out_token );
1053 if ( $this->update_user_option( 'friends_in_token', $in_token ) ) {
1054 update_option( 'friends_in_token_' . $in_token, $this->ID );
1055 }
1056 $this->set_role( get_option( 'friends_default_friend_role', 'friend' ) );
1057 }
1058
1059 /**
1060 * Check whether this is a valid friend
1061 *
1062 * @return boolean Whether the user has valid friend data.
1063 */
1064 public function is_valid_friend() {
1065 if ( ! $this->has_cap( 'friend' ) ) {
1066 return false;
1067 }
1068
1069 if ( ! $this->data->user_url ) {
1070 return false;
1071 }
1072
1073 if ( ! $this->get_user_option( 'friends_in_token' ) ) {
1074 return false;
1075 }
1076
1077 if ( ! $this->get_user_option( 'friends_out_token' ) ) {
1078 return false;
1079 }
1080
1081 return true;
1082 }
1083
1084 /**
1085 * Gets the role name (for a specific count).
1086 *
1087 * @param bool $group_subscriptions Whether to group all types of subscriptions into the name "Subscriptions".
1088 * @param int $count The count if more than one.
1089 *
1090 * @return string The role name.
1091 */
1092 public function get_role_name( $group_subscriptions = false, $count = 1 ) {
1093 $name = false;
1094
1095 if ( ! $name && in_array( 'acquaintance', $this->roles ) ) {
1096 return _nx( 'Acquaintance', 'Acquaintances', $count, 'User role', 'friends' );
1097 }
1098
1099 if ( ! $name && in_array( 'friend', $this->roles ) && $this->is_valid_friend() ) {
1100 return _nx( 'Friend', 'Friends', $count, 'User role', 'friends' );
1101 }
1102
1103 if ( ! $name && in_array( 'subscription', $this->roles ) || ( $group_subscriptions && ( in_array( 'friend_request', $this->roles ) || in_array( 'pending_friend_request', $this->roles ) ) ) ) {
1104 return _nx( 'Subscription', 'Subscriptions', $count, 'User role', 'friends' );
1105 }
1106
1107 if ( ! $name && in_array( 'friend_request', $this->roles ) ) {
1108 return _nx( 'Friend Request', 'Friend Requests', $count, 'User role', 'friends' );
1109 }
1110
1111 if ( ! $name && in_array( 'pending_friend_request', $this->roles ) ) {
1112 return _nx( 'Pending Friend Request', 'Pending Friend Requests', $count, 'User role', 'friends' );
1113 }
1114
1115 $name = apply_filters( 'friend_user_role_name', $name, $this );
1116
1117 if ( ! $name ) {
1118 $name = _x( 'Unknown', 'User role', 'friends' );
1119 }
1120
1121 return $name;
1122 }
1123
1124 /**
1125 * Determines if starred.
1126 *
1127 * @return bool True if starred, False otherwise.
1128 */
1129 public function is_starred() {
1130 return $this->get_user_option( 'friends_starred' );
1131 }
1132
1133 /**
1134 * Marks a friend as starred or unstarred.
1135 *
1136 * @param bool $starred Whether to star the friend.
1137 *
1138 * @return bool The new star status.
1139 */
1140 public function set_starred( $starred ) {
1141 if ( $starred ) {
1142 $this->update_user_option( 'friends_starred', true );
1143 return true;
1144 }
1145
1146 $this->delete_user_option( 'friends_starred' );
1147 return false;
1148 }
1149
1150 /**
1151 * Gets the local friends page url.
1152 *
1153 * @param integer $post_id The post identifier.
1154 *
1155 * @return string The local friends page url.
1156 */
1157 public function get_local_friends_page_url( $post_id = null ) {
1158 $path = '/';
1159 if ( $post_id ) {
1160 $path = '/' . $post_id . '/';
1161 }
1162 return home_url( '/friends/' . self::get_user_login_for_url( $this->user_login ) . $path );
1163 }
1164
1165 /**
1166 * Gets the local friends page url for a post format.
1167 *
1168 * @param string|array $post_format The post format.
1169 *
1170 * @return string The local friends page url.
1171 */
1172 public function get_local_friends_page_post_format_url( $post_format ) {
1173 if ( is_array( $post_format ) ) {
1174 $post_format = implode( ',', $post_format );
1175 }
1176 return home_url( '/friends/' . $this->user_login . '/type/' . $post_format . '/' );
1177 }
1178
1179 /**
1180 * Gets the local friends page url for a reaction.
1181 *
1182 * @param string $slug The reaction slug.
1183 *
1184 * @return string The local friends page url.
1185 */
1186 public function get_local_friends_page_reaction_url( $slug ) {
1187 return home_url( '/friends/' . $this->user_login . '/reaction' . $slug . '/' );
1188 }
1189
1190 /**
1191 * Gets the friend auth to be used as a GET parameter.
1192 *
1193 * @param integer $validity The validity in seconds.
1194 *
1195 * @return string The friend auth.
1196 */
1197 public function get_friend_auth( $validity = 3600 ) {
1198 $friends = Friends::get_instance();
1199 $friend_auth = $friends->access_control->get_friend_auth( $this, $validity );
1200 if ( empty( $friend_auth ) ) {
1201 return '';
1202 }
1203 return $friend_auth['me'] . '-' . $friend_auth['until'] . '-' . $friend_auth['auth'];
1204 }
1205
1206 /**
1207 * Determines whether the specified url is friend url.
1208 *
1209 * @param string $url The url.
1210 *
1211 * @return bool True if the specified url is friend url, False otherwise.
1212 */
1213 public function is_friend_url( $url ) {
1214 if ( ! $this->user_url ) {
1215 return false;
1216 }
1217 if ( 0 !== strpos( $url, $this->user_url ) ) {
1218 return false;
1219 }
1220 return true;
1221 }
1222
1223 /**
1224 * Get the REST URL for the friend
1225 *
1226 * @return string The REST URL.
1227 */
1228 public function get_rest_url() {
1229 $friends = Friends::get_instance();
1230 $rest_url = $this->get_user_option( 'friends_rest_url' );
1231 if ( ! $rest_url || false === strpos( $rest_url, REST::PREFIX ) ) {
1232 $rest_url = $friends->rest->discover_rest_url( $this->user_url );
1233 if ( is_wp_error( $rest_url ) ) {
1234 return null;
1235 }
1236
1237 if ( $rest_url ) {
1238 $this->update_user_option( 'friends_rest_url', $rest_url );
1239 }
1240 }
1241 return $rest_url;
1242 }
1243
1244 public function get_avatar_url() {
1245 return $this->get_user_option( 'friends_user_icon_url' );
1246 }
1247
1248 /**
1249 * Wrap get_user_option
1250 *
1251 * @param string $option_name User option name.
1252 * @return int|bool User meta ID if the option didn't exist, true on successful update,
1253 * false on failure.
1254 */
1255 public function get_user_option( $option_name ) {
1256 return get_user_option( $option_name, $this->ID );
1257 }
1258
1259 /**
1260 * Wrap update_user_option
1261 *
1262 * @param string $option_name User option name.
1263 * @param mixed $new_value User option value.
1264 * @param bool $is_global Optional. Whether option name is global or blog specific.
1265 * efault false (blog specific).
1266 * @return int|bool User meta ID if the option didn't exist, true on successful update,
1267 * false on failure.
1268 */
1269 public function update_user_option( $option_name, $new_value, $is_global = false ) {
1270 return update_user_option( $this->ID, $option_name, $new_value, $is_global );
1271 }
1272
1273 /**
1274 * Wrap delete_user_option
1275 *
1276 * @param string $option_name User option name.
1277 * @param bool $is_global Optional. Whether option name is global or blog specific.
1278 * Default false (blog specific).
1279 * @return bool True on success, false on failure.
1280 */
1281 public function delete_user_option( $option_name, $is_global = false ) {
1282 return delete_user_option( $this->ID, $option_name, $is_global );
1283 }
1284 }
1285