PluginProbe
Friends / 2.8.5
Friends v2.8.5
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.5, at includes/class-user.php

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