PluginProbe
Friends / 2.8.4
Friends v2.8.4
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.4, at includes/class-user.php

1,280 lines 35.9 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 )
444 );
445 }
446
447 $term = get_term( $all_urls[ $feed_url ], User_Feed::TAXONOMY );
448
449 return new User_Feed( $term );
450 }
451
452 /**
453 * Subscribe to a friends site without becoming a friend
454 *
455 * @param string $feed_url The feed URL to subscribe to.
456 * @param array $options The options.
457 *
458 * @return User_Feed|\WP_error $user The new associated user or an error object.
459 */
460 public function subscribe( $feed_url, $options = array() ) {
461 $options['active'] = true;
462 return $this->save_feed( $feed_url, $options );
463 }
464
465 /**
466 * Retrieve the posts for this user
467 *
468 * @return array The new posts.
469 */
470 public function retrieve_posts_from_active_feeds() {
471 return $this->retrieve_posts_from_feeds( $this->get_active_feeds() );
472 }
473
474 /**
475 * Retrieve the posts for these user feeds
476 *
477 * @param array $feeds The feeds to retrieve from.
478 *
479 * @return array The new posts.
480 */
481 public function retrieve_posts_from_feeds( array $feeds ) {
482 $friends = Friends::get_instance();
483 $new_posts = array();
484 foreach ( $feeds as $feed ) {
485 $posts = $friends->feed->retrieve_feed( $feed );
486 if ( ! is_wp_error( $posts ) ) {
487 $new_posts = array_merge( $new_posts, $posts );
488 }
489 }
490
491 $deleted_posts = $this->delete_outdated_posts();
492
493 return array_values( array_diff( $new_posts, $deleted_posts ) );
494 }
495
496 public function modify_query_by_author( \WP_Query $query ) {
497 $query->set( 'author', $this->ID );
498 return $query;
499 }
500
501 public function modify_get_posts_args_by_author( $args ) {
502 $args['author'] = $this->ID;
503 return $args;
504 }
505
506 public function delete() {
507 if ( is_multisite() ) {
508 remove_user_from_blog( $this->ID, get_current_blog_id() );
509 } else {
510 wp_delete_user( $this->ID );
511 }
512 // The content (posts and feeds) will be deleted with the 'delete_user' hook.
513 }
514
515 /**
516 * Delete posts the user decided to automatically delete.
517 */
518 public function delete_outdated_posts() {
519 $deleted_posts = array();
520
521 $args = array(
522 'post_type' => Friends::CPT,
523 );
524
525 if ( $this->is_retention_days_enabled() ) {
526 $args['date_query'] = array(
527 'before' => gmdate( 'Y-m-d H:i:s', strtotime( '-' . ( $this->get_retention_days() * 24 ) . 'hours' ) ),
528 );
529 } elseif ( get_option( 'friends_enable_retention_days' ) ) {
530 $args['date_query'] = array(
531 'before' => gmdate( 'Y-m-d H:i:s', strtotime( '-' . ( Friends::get_retention_days() * 24 ) . 'hours' ) ),
532 );
533 }
534
535 if ( isset( $args['date_query'] ) ) {
536 $query = new \WP_Query();
537 foreach ( $args as $key => $value ) {
538 $query->set( $key, $value );
539 }
540 $query = $this->modify_query_by_author( $query );
541
542 foreach ( $query->get_posts() as $post ) {
543 if ( apply_filters( 'friends_debug', false ) && ! wp_doing_cron() ) {
544 echo 'Deleting ', esc_html( $post->ID ), '<br/>';
545 }
546 wp_delete_post( $post->ID, true );
547 $deleted_posts[] = $post->ID;
548 }
549 }
550
551 unset( $args['date_query'] );
552 $args['orderby'] = 'date';
553 $args['order'] = 'asc';
554 if ( $this->is_retention_number_enabled() ) {
555 $args['offset'] = $this->get_retention_number();
556 $query = new \WP_Query();
557 foreach ( $args as $key => $value ) {
558 $query->set( $key, $value );
559 }
560 $query = $this->modify_query_by_author( $query );
561
562 foreach ( $query->get_posts() as $post ) {
563 if ( apply_filters( 'friends_debug', false ) && ! wp_doing_cron() ) {
564 echo 'Deleting ', esc_html( $post->ID ), '<br/>';
565 }
566 wp_delete_post( $post->ID, true );
567 $deleted_posts[] = $post->ID;
568 }
569 }
570
571 // Global setting.
572 if ( get_option( 'friends_enable_retention_number' ) ) {
573 unset( $args['author'] );
574 $args['offset'] = Friends::get_retention_number();
575 $query = new \WP_Query();
576 foreach ( $args as $key => $value ) {
577 $query->set( $key, $value );
578 }
579 $query = $this->modify_query_by_author( $query );
580
581 foreach ( $query->get_posts() as $post ) {
582 if ( apply_filters( 'friends_debug', false ) && ! wp_doing_cron() ) {
583 echo 'Deleting ', esc_html( $post->ID ), '<br/>';
584 }
585 wp_delete_post( $post->ID, true );
586 $deleted_posts[] = $post->ID;
587 }
588 }
589
590 // In any case, don't overflow the trash.
591 $args = array(
592 'post_type' => Friends::CPT,
593 'post_status' => 'trash',
594 'offset' => 30,
595 );
596
597 $query = new \WP_Query();
598 foreach ( $args as $key => $value ) {
599 $query->set( $key, $value );
600 }
601 $query = $this->modify_query_by_author( $query );
602
603 foreach ( $query->get_posts() as $post ) {
604 if ( apply_filters( 'friends_debug', false ) && ! wp_doing_cron() ) {
605 echo 'Deleting ', esc_html( $post->ID ), '<br/>';
606 }
607 wp_delete_post( $post->ID, true );
608 $deleted_posts[ $post->ID ] = $post->ID;
609 }
610
611 return $deleted_posts;
612 }
613
614 /**
615 * Check whether the retention by number of posts is enabled.
616 *
617 * @return boolean Whether the retention by number of posts is enabled.
618 */
619 public function is_retention_number_enabled() {
620 return $this->get_user_option( 'friends_enable_retention_number' );
621 }
622
623 /**
624 * Enable or disable the retention by number of posts.
625 *
626 * @param boolean $enabled Whether the retention by number of posts should be enabled.
627 * @return boolean Whether the retention by number of posts is enabled.
628 */
629 public function set_retention_number_enabled( $enabled ) {
630 $this->update_user_option( 'friends_enable_retention_number', boolval( $enabled ) );
631 return boolval( $enabled );
632 }
633
634 /**
635 * Get the retention by number of posts.
636 *
637 * @return int The retention by number of posts.
638 */
639 public function get_retention_number() {
640 $number = $this->get_user_option( 'friends_retention_number' );
641 if ( $number <= 0 ) {
642 return 200;
643 }
644
645 return $number;
646 }
647
648 /**
649 * Set the retention by number of posts.
650 *
651 * @param int $number The retention by number of posts.
652 * @return int The retention by number of posts.
653 */
654 public function set_retention_number( $number ) {
655 $number = max( 1, $number );
656 $this->update_user_option( 'friends_retention_number', $number );
657 return $number;
658 }
659 /**
660 * Check whether the retention by days of posts is enabled.
661 *
662 * @return boolean Whether the retention by days of posts is enabled.
663 */
664 public function is_retention_days_enabled() {
665 return $this->get_user_option( 'friends_enable_retention_days' );
666 }
667
668 /**
669 * Enable or disable the retention by days of posts.
670 *
671 * @param boolean $enabled Whether the retention by days of posts should be enabled.
672 * @return boolean Whether the retention by days of posts is enabled.
673 */
674 public function set_retention_days_enabled( $enabled ) {
675 $this->update_user_option( 'friends_enable_retention_days', boolval( $enabled ) );
676 return boolval( $enabled );
677 }
678
679 /**
680 * Get the retention by days of posts.
681 *
682 * @return int The retention by days of posts.
683 */
684 public function get_retention_days() {
685 $days = $this->get_user_option( 'friends_retention_days' );
686 if ( $days <= 0 ) {
687 return 14;
688 }
689
690 return $days;
691 }
692
693 /**
694 * Set the retention by days of posts.
695 *
696 * @param int $days The retention by days of posts.
697 * @return int The retention by days of posts.
698 */
699 public function set_retention_days( $days ) {
700 $days = max( 1, intval( $days ) );
701 return $this->update_user_option( 'friends_retention_days', $days );
702 return $days;
703 }
704
705 /**
706 * Gets the post counts by post format.
707 *
708 * @return int The post count.
709 */
710 public function get_post_in_trash_count() {
711 global $wpdb;
712 $post_types = apply_filters( 'friends_frontend_post_types', array() );
713
714 $count = $wpdb->get_var(
715 $wpdb->prepare(
716 "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',
717 array_merge( $post_types, array( $this->ID ) )
718 )
719 );
720
721 return intval( $count );
722 }
723
724 /**
725 * Gets the post counts by post format.
726 *
727 * @return array The post counts.
728 */
729 public function get_post_count_by_post_format() {
730 $cache_key = 'friends_post_count_by_post_format_author_' . $this->ID;
731
732 $counts = get_transient( $cache_key );
733 if ( true || false === $counts ) {
734 $counts = array();
735 $post_types = apply_filters( 'friends_frontend_post_types', array() );
736 $post_formats_term_ids = array();
737 foreach ( get_post_format_slugs() as $post_format ) {
738 $term = get_term_by( 'slug', 'post-format-' . $post_format, 'post_format' );
739 if ( $term ) {
740 $post_formats_term_ids[ $term->term_id ] = $post_format;
741 }
742 }
743
744 global $wpdb;
745
746 $counts = array();
747 $counts['standard'] = $wpdb->get_var(
748 $wpdb->prepare(
749 sprintf(
750 "SELECT COUNT(DISTINCT posts.ID)
751 FROM %s AS posts
752 JOIN %s AS relationships_post_format
753
754 WHERE posts.post_author = %s
755 AND posts.post_status IN ( 'publish', 'private' )
756 AND posts.post_type IN ( %s )
757 AND relationships_post_format.object_id = posts.ID",
758 $wpdb->posts,
759 $wpdb->term_relationships,
760 '%d',
761 implode( ',', array_fill( 0, count( $post_types ), '%s' ) )
762 ),
763 array_merge(
764 array( $this->ID ),
765 $post_types
766 )
767 )
768 );
769
770 if ( ! empty( $post_formats_term_ids ) ) {
771 $post_format_counts = $wpdb->get_results(
772 $wpdb->prepare(
773 sprintf(
774 "SELECT relationships_post_format.term_taxonomy_id AS post_format_id, COUNT(relationships_post_format.term_taxonomy_id) AS count
775 FROM %s AS posts
776 JOIN %s AS relationships_post_format
777
778 WHERE posts.post_author = %s
779 AND posts.post_status IN ( 'publish', 'private' )
780 AND posts.post_type IN ( %s )
781 AND relationships_post_format.object_id = posts.ID
782 AND relationships_post_format.term_taxonomy_id IN ( %s )
783 GROUP BY relationships_post_format.term_taxonomy_id",
784 $wpdb->posts,
785 $wpdb->term_relationships,
786 '%d',
787 implode( ',', array_fill( 0, count( $post_types ), '%s' ) ),
788 implode( ',', array_fill( 0, count( $post_formats_term_ids ), '%d' ) )
789 ),
790 array_merge(
791 array( $this->ID ),
792 $post_types,
793 array_keys( $post_formats_term_ids )
794 )
795 )
796 );
797
798 foreach ( $post_format_counts as $row ) {
799 $counts[ $post_formats_term_ids[ $row->post_format_id ] ] = $row->count;
800 $counts['standard'] -= $row->count;
801 }
802 }
803
804 $counts = array_filter( $counts );
805
806 set_transient( $cache_key, $counts, HOUR_IN_SECONDS );
807 }
808
809 return $counts;
810 }
811
812 /**
813 * Gets the post stats.
814 *
815 * @return object The post stats.
816 */
817 public function get_post_stats() {
818 global $wpdb;
819 $post_types = apply_filters( 'friends_frontend_post_types', array() );
820 $post_stats = $wpdb->get_row(
821 $wpdb->prepare(
822 'SELECT SUM(
823 LENGTH( ID ) +
824 LENGTH( post_author ) +
825 LENGTH( post_date ) +
826 LENGTH( post_date_gmt ) +
827 LENGTH( post_content ) +
828 LENGTH( post_title ) +
829 LENGTH( post_excerpt ) +
830 LENGTH( post_status ) +
831 LENGTH( comment_status ) +
832 LENGTH( ping_status ) +
833 LENGTH( post_password ) +
834 LENGTH( post_name ) +
835 LENGTH( to_ping ) +
836 LENGTH( pinged ) +
837 LENGTH( post_modified ) +
838 LENGTH( post_modified_gmt ) +
839 LENGTH( post_content_filtered ) +
840 LENGTH( post_parent ) +
841 LENGTH( guid ) +
842 LENGTH( menu_order ) +
843 LENGTH( post_type ) +
844 LENGTH( post_mime_type ) +
845 LENGTH( comment_count )
846 ) AS total_size,
847 COUNT(*) as post_count
848 FROM ' . $wpdb->posts . ' WHERE post_author = %d AND post_type IN ( ' . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ' )',
849 array_merge( array( $this->ID ), $post_types )
850 ),
851 ARRAY_A
852 );
853 $post_stats['earliest_post_date'] = mysql2date(
854 'U',
855 $wpdb->get_var(
856 $wpdb->prepare(
857 "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' ) ) . ' )',
858 array_merge( array( $this->ID ), $post_types )
859 )
860 )
861 );
862 return $post_stats;
863 }
864
865 public function get_all_post_ids() {
866 global $wpdb;
867 $post_types_to_delete = implode( "', '", apply_filters( 'friends_frontend_post_types', array() ) );
868
869 $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
870 return $post_ids;
871 }
872
873 /**
874 * Update a friend's avatar URL
875 *
876 * @param string $user_icon_url The user icon URL.
877 * @return string|false The URL that was set or false.
878 */
879 public function update_user_icon_url( $user_icon_url ) {
880 if ( ! $user_icon_url ) {
881 $user_icon_url = Mf2\resolveUrl( $this->user_url, '/favicon.ico' );
882 }
883
884 if ( $user_icon_url && Friends::check_url( $user_icon_url ) ) {
885 $this->update_user_option( 'friends_user_icon_url', $user_icon_url );
886 return $user_icon_url;
887 }
888
889 return false;
890 }
891
892 /**
893 * Retrieve the rules for this feed.
894 *
895 * @return array The rules set by the user for this feed.
896 */
897 public function get_feed_rules() {
898 if ( ! isset( self::$feed_rules[ $this->ID ] ) ) {
899 $this->set_feed_rules( $this->get_user_option( 'friends_feed_rules' ) );
900 }
901 return self::$feed_rules[ $this->ID ];
902 }
903
904 /**
905 * Set the rules for this feed.
906 *
907 * @param array $rules The rules to set for this feed.
908 * @return array The rules set by the user for this feed.
909 */
910 public function set_feed_rules( $rules ) {
911 $friends = Friends::get_instance();
912 self::$feed_rules[ $this->ID ] = $friends->feed->validate_feed_rules( $rules );
913 return self::$feed_rules[ $this->ID ];
914 }
915
916
917 /**
918 * Retrieve the catch_all value for this feed.
919 *
920 * @return array The rules set by the user for this feed.
921 */
922 public function get_feed_catch_all() {
923 if ( ! isset( self::$feed_catch_all[ $this->ID ] ) ) {
924 $this->set_feed_catch_all( $this->get_user_option( 'friends_feed_catch_all' ) );
925 }
926 return self::$feed_catch_all[ $this->ID ];
927 }
928
929
930 /**
931 * Set the catch_all value for this feed.
932 *
933 * @param array $catchall The catchall rule to set for this feed.
934 * @return array The catchall rukle set by the user for this feed.
935 */
936 public function set_feed_catch_all( $catchall ) {
937 $friends = Friends::get_instance();
938 self::$feed_catch_all[ $this->ID ] = $friends->feed->validate_feed_catch_all( $catchall );
939 return self::$feed_catch_all[ $this->ID ];
940 }
941
942 /**
943 * Retrieve the remote post ids.
944 *
945 * @return array A mapping of the remote post ids.
946 */
947 public function get_remote_post_ids() {
948 $remote_post_ids = array();
949 $existing_posts = new \WP_Query(
950 array(
951 'post_type' => Friends::CPT,
952 'post_status' => array( 'publish', 'private', 'trash' ),
953 'author' => $this->ID,
954 'nopaging' => true,
955 )
956 );
957
958 if ( $existing_posts->have_posts() ) {
959 while ( $existing_posts->have_posts() ) {
960 $post = $existing_posts->next_post();
961 $remote_post_id = get_post_meta( $post->ID, 'remote_post_id', true );
962 if ( $remote_post_id ) {
963 $remote_post_ids[ $remote_post_id ] = $post->ID;
964 }
965 $permalink = get_permalink( $post );
966 $remote_post_ids[ $permalink ] = $post->ID;
967 $permalink = str_replace( array( '&#38;', '&#038;' ), '&', ent2ncr( $permalink ) );
968 $remote_post_ids[ $permalink ] = $post->ID;
969 }
970 }
971
972 do_action( 'friends_remote_post_ids', $remote_post_ids );
973 return $remote_post_ids;
974 }
975
976 /**
977 * Get the user's feeds (and potentially convert old-style feed URL).
978 *
979 * @return array An array of User_Feed items.
980 */
981 public function get_feeds() {
982 $term_query = new \WP_Term_Query(
983 array(
984 'taxonomy' => User_Feed::TAXONOMY,
985 'object_ids' => $this->get_object_id(),
986 )
987 );
988
989 $feeds = array();
990 foreach ( $term_query->get_terms() as $term ) {
991 $feeds[ $term->term_id ] = new User_Feed( $term, $this );
992 }
993
994 return $feeds;
995 }
996
997 /**
998 * Get just the user's active feeds.
999 *
1000 * @return array An array of active User_Feed items.
1001 */
1002 public function get_active_feeds() {
1003 $active_feeds = array();
1004 foreach ( $this->get_feeds() as $feed ) {
1005 if ( $feed->is_active() ) {
1006 $active_feeds[] = $feed;
1007 }
1008 }
1009 return $active_feeds;
1010 }
1011
1012 /**
1013 * Get just the user's active feeds.
1014 *
1015 * @return array An array of active User_Feed items.
1016 */
1017 public function get_due_feeds() {
1018 $due_feeds = array();
1019 foreach ( $this->get_active_feeds() as $feed ) {
1020 // Explicitly use time() to allow mocking it inside the namespace.
1021 if ( gmdate( 'Y-m-d H:i:s', time() ) >= $feed->get_next_poll() ) {
1022 $due_feeds[] = $feed;
1023 }
1024 }
1025 return $due_feeds;
1026 }
1027
1028 /**
1029 * Determines whether the user can have feeds refreshed.
1030 *
1031 * @return bool True if able to refresh feeds, False otherwise.
1032 */
1033 public function can_refresh_feeds() {
1034 return $this->has_cap( 'subscription' ) ||
1035 $this->has_cap( 'acquaintance' ) ||
1036 $this->has_cap( 'friend' ) ||
1037 $this->has_cap( 'pending_friend_request' );
1038 }
1039
1040 /**
1041 * Convert a user to a friend
1042 *
1043 * @param string $out_token The token to authenticate against the remote.
1044 * @param string $in_token The token the remote needs to use to authenticate to us.
1045 */
1046 public function make_friend( $out_token, $in_token ) {
1047 $this->update_user_option( 'friends_out_token', $out_token );
1048 if ( $this->update_user_option( 'friends_in_token', $in_token ) ) {
1049 update_option( 'friends_in_token_' . $in_token, $this->ID );
1050 }
1051 $this->set_role( get_option( 'friends_default_friend_role', 'friend' ) );
1052 }
1053
1054 /**
1055 * Check whether this is a valid friend
1056 *
1057 * @return boolean Whether the user has valid friend data.
1058 */
1059 public function is_valid_friend() {
1060 if ( ! $this->has_cap( 'friend' ) ) {
1061 return false;
1062 }
1063
1064 if ( ! $this->data->user_url ) {
1065 return false;
1066 }
1067
1068 if ( ! $this->get_user_option( 'friends_in_token' ) ) {
1069 return false;
1070 }
1071
1072 if ( ! $this->get_user_option( 'friends_out_token' ) ) {
1073 return false;
1074 }
1075
1076 return true;
1077 }
1078
1079 /**
1080 * Gets the role name (for a specific count).
1081 *
1082 * @param bool $group_subscriptions Whether to group all types of subscriptions into the name "Subscriptions".
1083 * @param int $count The count if more than one.
1084 *
1085 * @return string The role name.
1086 */
1087 public function get_role_name( $group_subscriptions = false, $count = 1 ) {
1088 $name = false;
1089
1090 if ( ! $name && in_array( 'acquaintance', $this->roles ) ) {
1091 return _nx( 'Acquaintance', 'Acquaintances', $count, 'User role', 'friends' );
1092 }
1093
1094 if ( ! $name && in_array( 'friend', $this->roles ) && $this->is_valid_friend() ) {
1095 return _nx( 'Friend', 'Friends', $count, 'User role', 'friends' );
1096 }
1097
1098 if ( ! $name && in_array( 'subscription', $this->roles ) || ( $group_subscriptions && ( in_array( 'friend_request', $this->roles ) || in_array( 'pending_friend_request', $this->roles ) ) ) ) {
1099 return _nx( 'Subscription', 'Subscriptions', $count, 'User role', 'friends' );
1100 }
1101
1102 if ( ! $name && in_array( 'friend_request', $this->roles ) ) {
1103 return _nx( 'Friend Request', 'Friend Requests', $count, 'User role', 'friends' );
1104 }
1105
1106 if ( ! $name && in_array( 'pending_friend_request', $this->roles ) ) {
1107 return _nx( 'Pending Friend Request', 'Pending Friend Requests', $count, 'User role', 'friends' );
1108 }
1109
1110 $name = apply_filters( 'friend_user_role_name', $name, $this );
1111
1112 if ( ! $name ) {
1113 $name = _x( 'Unknown', 'User role', 'friends' );
1114 }
1115
1116 return $name;
1117 }
1118
1119 /**
1120 * Determines if starred.
1121 *
1122 * @return bool True if starred, False otherwise.
1123 */
1124 public function is_starred() {
1125 return $this->get_user_option( 'friends_starred' );
1126 }
1127
1128 /**
1129 * Marks a friend as starred or unstarred.
1130 *
1131 * @param bool $starred Whether to star the friend.
1132 *
1133 * @return bool The new star status.
1134 */
1135 public function set_starred( $starred ) {
1136 if ( $starred ) {
1137 $this->update_user_option( 'friends_starred', true );
1138 return true;
1139 }
1140
1141 $this->delete_user_option( 'friends_starred' );
1142 return false;
1143 }
1144
1145 /**
1146 * Gets the local friends page url.
1147 *
1148 * @param integer $post_id The post identifier.
1149 *
1150 * @return string The local friends page url.
1151 */
1152 public function get_local_friends_page_url( $post_id = null ) {
1153 $path = '/';
1154 if ( $post_id ) {
1155 $path = '/' . $post_id . '/';
1156 }
1157 return home_url( '/friends/' . self::get_user_login_for_url( $this->user_login ) . $path );
1158 }
1159
1160 /**
1161 * Gets the local friends page url for a post format.
1162 *
1163 * @param string|array $post_format The post format.
1164 *
1165 * @return string The local friends page url.
1166 */
1167 public function get_local_friends_page_post_format_url( $post_format ) {
1168 if ( is_array( $post_format ) ) {
1169 $post_format = implode( ',', $post_format );
1170 }
1171 return home_url( '/friends/' . $this->user_login . '/type/' . $post_format . '/' );
1172 }
1173
1174 /**
1175 * Gets the local friends page url for a reaction.
1176 *
1177 * @param string $slug The reaction slug.
1178 *
1179 * @return string The local friends page url.
1180 */
1181 public function get_local_friends_page_reaction_url( $slug ) {
1182 return home_url( '/friends/' . $this->user_login . '/reaction' . $slug . '/' );
1183 }
1184
1185 /**
1186 * Gets the friend auth to be used as a GET parameter.
1187 *
1188 * @param integer $validity The validity in seconds.
1189 *
1190 * @return string The friend auth.
1191 */
1192 public function get_friend_auth( $validity = 3600 ) {
1193 $friends = Friends::get_instance();
1194 $friend_auth = $friends->access_control->get_friend_auth( $this, $validity );
1195 if ( empty( $friend_auth ) ) {
1196 return '';
1197 }
1198 return $friend_auth['me'] . '-' . $friend_auth['until'] . '-' . $friend_auth['auth'];
1199 }
1200
1201 /**
1202 * Determines whether the specified url is friend url.
1203 *
1204 * @param string $url The url.
1205 *
1206 * @return bool True if the specified url is friend url, False otherwise.
1207 */
1208 public function is_friend_url( $url ) {
1209 if ( ! $this->user_url ) {
1210 return false;
1211 }
1212 if ( 0 !== strpos( $url, $this->user_url ) ) {
1213 return false;
1214 }
1215 return true;
1216 }
1217
1218 /**
1219 * Get the REST URL for the friend
1220 *
1221 * @return string The REST URL.
1222 */
1223 public function get_rest_url() {
1224 $friends = Friends::get_instance();
1225 $rest_url = $this->get_user_option( 'friends_rest_url' );
1226 if ( ! $rest_url || false === strpos( $rest_url, REST::PREFIX ) ) {
1227 $rest_url = $friends->rest->discover_rest_url( $this->user_url );
1228 if ( is_wp_error( $rest_url ) ) {
1229 return null;
1230 }
1231
1232 if ( $rest_url ) {
1233 $this->update_user_option( 'friends_rest_url', $rest_url );
1234 }
1235 }
1236 return $rest_url;
1237 }
1238
1239 public function get_avatar_url() {
1240 return $this->get_user_option( 'friends_user_icon_url' );
1241 }
1242
1243 /**
1244 * Wrap get_user_option
1245 *
1246 * @param string $option_name User option name.
1247 * @return int|bool User meta ID if the option didn't exist, true on successful update,
1248 * false on failure.
1249 */
1250 public function get_user_option( $option_name ) {
1251 return get_user_option( $option_name, $this->ID );
1252 }
1253
1254 /**
1255 * Wrap update_user_option
1256 *
1257 * @param string $option_name User option name.
1258 * @param mixed $new_value User option value.
1259 * @param bool $is_global Optional. Whether option name is global or blog specific.
1260 * efault false (blog specific).
1261 * @return int|bool User meta ID if the option didn't exist, true on successful update,
1262 * false on failure.
1263 */
1264 public function update_user_option( $option_name, $new_value, $is_global = false ) {
1265 return update_user_option( $this->ID, $option_name, $new_value, $is_global );
1266 }
1267
1268 /**
1269 * Wrap delete_user_option
1270 *
1271 * @param string $option_name User option name.
1272 * @param bool $is_global Optional. Whether option name is global or blog specific.
1273 * Default false (blog specific).
1274 * @return bool True on success, false on failure.
1275 */
1276 public function delete_user_option( $option_name, $is_global = false ) {
1277 return delete_user_option( $this->ID, $option_name, $is_global );
1278 }
1279 }
1280