PluginProbe
Friends / 4.3.2
Friends v4.3.2
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-feed.php

class-user-feed.php in Friends 4.3.2, at includes/class-user-feed.php

860 lines 22.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Friends Feed URL
4 *
5 * This contains the functions for managing feed URLs.
6 *
7 * @package Friends
8 */
9
10 namespace Friends;
11
12 use WP_Error;
13
14 /**
15 * This is the class for the feed URLs part of the Friends Plugin.
16 *
17 * @since 1.0
18 *
19 * @package Friends
20 * @author Alex Kirk
21 */
22 class User_Feed {
23 const TAXONOMY = 'friend-user-feed';
24 const INTERVAL_BACKTRACK = 600;
25
26 /**
27 * Contains a reference to the \WP_Term for the feed.
28 *
29 * @var \WP_Term
30 */
31 private $term;
32
33 /**
34 * Contains a reference to the associated User.
35 *
36 * @var User
37 */
38 private $friend_user;
39
40 /**
41 * Constructor
42 *
43 * @param \WP_Term $term The WordPress term of the feed taxonomy.
44 * @param User|null $friend_user Optionally the associated User, if available.
45 */
46 public function __construct( \WP_Term $term, ?User $friend_user = null ) {
47 $this->term = $term;
48 $this->friend_user = $friend_user;
49 }
50
51 /**
52 * The string representation of the term = The URL.
53 *
54 * @return string Term name = URL.
55 */
56 public function __toString() {
57 return $this->term->name;
58 }
59
60 /**
61 * Gets the identifier.
62 *
63 * @return int The identifier.
64 */
65 public function get_id() {
66 return $this->term->term_id;
67 }
68
69 /**
70 * Gets the URL. For ActivityPub feeds linked to an ap_actor, this returns
71 * the URL from the ap_actor post (the source of truth). Otherwise returns
72 * the term name.
73 *
74 * @return string The feed URL.
75 */
76 public function get_url() {
77 $ap_actor_url = $this->get_ap_actor_url();
78 if ( $ap_actor_url ) {
79 return $ap_actor_url;
80 }
81 return $this->term->name;
82 }
83
84
85 /**
86 * Get the local feed URL. Dysfunctional at the moment.
87 *
88 * @return string The local feed URL.
89 */
90 public function get_local_url() {
91 $friend_user = $this->get_friend_user();
92 return home_url() . '/friends/' . $friend_user->user_login . '/feed/';
93 }
94
95 /**
96 * Get the local HTML URL. This would be the URL where to see the friends post on the friends page.
97 * Dysfunctional at the moment.
98 *
99 * @return string The local HTML URL.
100 */
101 public function get_local_html_url() {
102 $friend_user = $this->get_friend_user();
103 return $friend_user->get_local_friends_page_url();
104 }
105
106 /**
107 * Gets the friend user associated wit the term.
108 *
109 * @return User|null The associated user.
110 */
111 public function get_friend_user() {
112 if ( empty( $this->friend_user ) ) {
113 $users = $this->get_all_friend_users();
114 $this->friend_user = reset( $users );
115 }
116
117 return $this->friend_user;
118 }
119
120 /**
121 * Gets the friend users associated wit the term.
122 *
123 * @return array(User) The associated users.
124 */
125 public function get_all_friend_users() {
126 if ( $this->term->parent ) {
127 $term = get_term( $this->term->parent, Subscription::TAXONOMY );
128 if ( ! is_wp_error( $term ) && $term ) {
129 return array( new Subscription( $term ) );
130 }
131 }
132
133 // Feeds of a user-backed friend aren't child terms of a subscription
134 // term but are attached to the user as object terms, see
135 // User::save_feeds().
136 $object_ids = get_objects_in_term( $this->term->term_id, self::TAXONOMY );
137 if ( is_wp_error( $object_ids ) ) {
138 return array();
139 }
140
141 $users = array();
142 foreach ( $object_ids as $object_id ) {
143 $user = User::get_user_by_id( $object_id );
144 if ( ! $user ) {
145 continue;
146 }
147
148 // The same object id can belong to an ap_actor post, so only accept
149 // the user if this feed really is one of theirs.
150 $feeds = $user->get_feeds();
151 if ( isset( $feeds[ $this->term->term_id ] ) ) {
152 $users[] = $user;
153 }
154 }
155
156 return $users;
157 }
158
159 /**
160 * Whether the feed is active (=subscribed).
161 *
162 * @return bool Feed is active if true.
163 */
164 public function get_active() {
165 return self::validate_active( get_metadata( 'term', $this->term->term_id, 'active', true ) );
166 }
167
168 /**
169 * The post format to which the feed items should be imported.
170 *
171 * @return string The post format.
172 */
173 public function get_post_format() {
174 return self::validate_post_format( get_metadata( 'term', $this->term->term_id, 'post-format', true ) );
175 }
176
177 /**
178 * The parser to be used to fetch and parse the feed.
179 *
180 * @return string The parser slug.
181 */
182 public function get_parser() {
183 return self::validate_parser( get_metadata( 'term', $this->term->term_id, 'parser', true ) );
184 }
185
186 /**
187 * The (expected) mime-type of the feed.
188 *
189 * @return string The mime-type.
190 */
191 public function get_mime_type() {
192 return self::validate_mime_type( get_metadata( 'term', $this->term->term_id, 'mime-type', true ) );
193 }
194
195 /**
196 * Gets the linked ActivityPub actor post ID.
197 *
198 * @return int|null The ap_actor post ID, or null if not linked.
199 */
200 public function get_ap_actor_id() {
201 if ( ! class_exists( '\Activitypub\Collection\Remote_Actors' ) ) {
202 return null;
203 }
204
205 $object_ids = get_objects_in_term( $this->term->term_id, self::TAXONOMY );
206 if ( is_wp_error( $object_ids ) || empty( $object_ids ) ) {
207 return null;
208 }
209
210 foreach ( $object_ids as $object_id ) {
211 if ( 'ap_actor' === get_post_type( $object_id ) ) {
212 return (int) $object_id;
213 }
214 }
215
216 return null;
217 }
218
219 /**
220 * Checks if this feed is linked to an ActivityPub actor.
221 *
222 * @return bool True if this is an ActivityPub feed with a linked actor.
223 */
224 public function is_activitypub_feed() {
225 return 'activitypub' === $this->get_parser() && null !== $this->get_ap_actor_id();
226 }
227
228 /**
229 * Gets the URL from the linked ActivityPub actor post.
230 *
231 * @return string|null The actor URL from the ap_actor post, or null if not available.
232 */
233 public function get_ap_actor_url() {
234 $ap_actor_id = $this->get_ap_actor_id();
235 if ( ! $ap_actor_id ) {
236 return null;
237 }
238
239 $ap_actor = get_post( $ap_actor_id );
240 if ( ! $ap_actor ) {
241 return null;
242 }
243
244 return $ap_actor->guid;
245 }
246
247 /**
248 * Sets the linked ActivityPub actor post ID.
249 *
250 * @param int $ap_actor_id The ap_actor post ID.
251 * @return array|false|\WP_Error Array of term taxonomy IDs on success, false or WP_Error on failure.
252 */
253 public function set_ap_actor_id( $ap_actor_id ) {
254 // Ensure taxonomy is registered for ap_actor post type (may not be during cron).
255 if ( class_exists( '\Activitypub\Collection\Remote_Actors' ) ) {
256 register_taxonomy_for_object_type( self::TAXONOMY, \Activitypub\Collection\Remote_Actors::POST_TYPE );
257 }
258
259 $object_ids = get_objects_in_term( $this->term->term_id, self::TAXONOMY );
260 if ( ! is_wp_error( $object_ids ) ) {
261 foreach ( $object_ids as $object_id ) {
262 if ( absint( $ap_actor_id ) !== absint( $object_id ) && 'ap_actor' === get_post_type( $object_id ) ) {
263 wp_remove_object_terms( $object_id, $this->term->term_id, self::TAXONOMY );
264 }
265 }
266 }
267
268 return wp_set_object_terms( absint( $ap_actor_id ), $this->term->term_id, self::TAXONOMY );
269 }
270
271 /**
272 * The title of the feed.
273 *
274 * @return string The feed title.
275 */
276 public function get_title() {
277 return self::validate_title( get_metadata( 'term', $this->term->term_id, 'title', true ) );
278 }
279
280 /**
281 * A log entry of the last retrieval of the feed.
282 *
283 * @return string The log line.
284 */
285 public function get_last_log() {
286 return self::validate_last_log( get_metadata( 'term', $this->term->term_id, 'last-log', true ) );
287 }
288
289 /**
290 * Whether the feed is active.
291 *
292 * @return boolean Is the feed to be fetched?
293 */
294 public function is_active() {
295 return self::validate_active( get_metadata( 'term', $this->term->term_id, 'active', true ) );
296 }
297
298 /**
299 * The interval for calculating the next poll date.
300 *
301 * @return int The interval.
302 */
303 public function get_interval() {
304 return self::validate_interval( get_metadata( 'term', $this->term->term_id, 'interval', true ) );
305 }
306
307 /**
308 * The modifiert for the next interval.
309 *
310 * Examples:
311 * - interval 3600, modifier: 100. intervals: 3600, 3600, 3600, 3600
312 * - interval 3600, modifier: 110. intervals: 3600, 3960, 4356, 4792
313 * - interval 3600, modifier: 200. intervals: 3600, 7200, 14400, 28800
314 *
315 * @return int The modifier.
316 */
317 public function get_modifier() {
318 return self::validate_modifier( get_metadata( 'term', $this->term->term_id, 'modifier', true ) );
319 }
320
321 /**
322 * The datetime when the feed should be polled next.
323 *
324 * @return string The next poll date.
325 */
326 public function get_next_poll() {
327 return self::validate_poll_date( get_metadata( 'term', $this->term->term_id, 'next-poll', true ) );
328 }
329
330 /**
331 * Whether the feed poll has been started within the last 60 seconds.
332 *
333 * @return bool Is the feed currently being polled?
334 */
335 public function can_be_polled_now() {
336 $polling_now = get_metadata( 'term', $this->term->term_id, 'last-poll', true ) > gmdate( 'Y-m-d H:i:s', time() - 60 );
337 // Explicitly use time() to allow mocking it inside the namespace.
338 $due = gmdate( 'Y-m-d H:i:s', time() ) >= $this->get_next_poll();
339 return $due && ! $polling_now;
340 }
341
342 public function set_polling_now() {
343 // Explicitly use time() to allow mocking it inside the namespace.
344 $this->update_metadata( 'last-poll', gmdate( 'Y-m-d H:i:s', time() ) );
345 }
346
347 public function was_polled() {
348 $interval = $this->get_interval();
349 $percent = $this->get_modifier();
350
351 $updated_interval = $interval * $percent / 100;
352 if ( $interval !== $updated_interval ) {
353 $this->update_metadata( 'interval', $updated_interval );
354 }
355 // Set a time 5 minutes earlier so that an hourly job that takes longer gets a chance to refresh it.
356 return $this->update_metadata( 'next-poll', gmdate( 'Y-m-d H:i:s', time() + $interval - self::INTERVAL_BACKTRACK ) );
357 }
358
359
360 /**
361 * Validates the post format against defined post formats.
362 *
363 * @param string $post_format The post format to be validated.
364 * @return string A validated post format.
365 */
366 public static function validate_post_format( $post_format ) {
367 $post_formats = get_post_format_strings();
368 $post_formats['autodetect'] = true;
369 if ( isset( $post_formats[ $post_format ] ) ) {
370 return $post_format;
371 }
372 $keys = array_keys( $post_formats );
373 return reset( $keys );
374 }
375
376 /**
377 * Validates the parser against defined parsers.
378 *
379 * @param string $parser The parser to be validated.
380 * @return string A validated parser.
381 */
382 public static function validate_parser( $parser ) {
383 $friends = Friends::get_instance();
384 $parsers = $friends->feed->get_registered_parsers();
385 if ( isset( $parsers[ $parser ] ) ) {
386 return $parser;
387 }
388 // We're lax with parsers to allow deactivating parser plugins without deleting this information.
389 return $parser;
390 }
391
392 /**
393 * Validates the mime-type.
394 *
395 * @param string $mime_type The mime-type to be validated.
396 * @return string A validated mime-type.
397 */
398 public static function validate_mime_type( $mime_type ) {
399 return substr( preg_replace( '/[^a-z0-9\/_+-]/', '', $mime_type ), 0, 100 );
400 }
401
402 /**
403 * Validates the title.
404 *
405 * @param string $title The title to be validated.
406 * @return string A validated title.
407 */
408 public static function validate_title( $title ) {
409 return substr( $title, 0, 100 );
410 }
411
412 /**
413 * Validates the last log line.
414 *
415 * @param string $last_log The log line to be validated.
416 * @return string A validated log line.
417 */
418 public static function validate_last_log( $last_log ) {
419 return substr( $last_log, 0, 1000 );
420 }
421
422 /**
423 * Validates the polling interval.
424 *
425 * @param string $interval The interval to be validated.
426 * @return int A validated interval.
427 */
428 public static function validate_interval( $interval ) {
429 if ( $interval > WEEK_IN_SECONDS ) {
430 return WEEK_IN_SECONDS;
431 }
432 if ( $interval < HOUR_IN_SECONDS ) {
433 return HOUR_IN_SECONDS;
434 }
435 return intval( $interval );
436 }
437
438 /**
439 * Validates the interval percentage modifier.
440 *
441 * @param string $percentage The percentage modifier.
442 * @return int A validated percentage modifier.
443 */
444 public static function validate_modifier( $percentage ) {
445 if ( $percentage > 500 ) {
446 return 500;
447 }
448 if ( $percentage < 100 ) {
449 return 100;
450 }
451 return intval( $percentage );
452 }
453
454 /**
455 * Validates a poll date.
456 *
457 * @param string $poll_date The poll date to be validated.
458 * @return string A validated poll date.
459 */
460 public static function validate_poll_date( $poll_date ) {
461 if ( ! preg_match( '/^2\d{3}-[01]\d-[0123]\d [012]\d:[0-5]\d:[0-5]\d$/', $poll_date ) ) {
462 // Explicitly use time() to allow mocking it inside the namespace.
463 return gmdate( 'Y-m-d H:i:s', time() );
464 }
465 return $poll_date;
466 }
467
468 /**
469 * Validates the active attribute.
470 *
471 * @param string $active The active value to be validated.
472 * @return string A validated active value.
473 */
474 public static function validate_active( $active ) {
475 return (bool) $active;
476 }
477
478 /**
479 * Registers the taxonomy
480 */
481 public static function register_taxonomy() {
482 $args = array(
483 'labels' => array(
484 'name' => _x( 'Feed URL', 'taxonomy general name', 'friends' ),
485 'singular_name' => _x( 'Feed URL', 'taxonomy singular name', 'friends' ),
486 'menu_name' => __( 'Feed URL', 'friends' ),
487 ),
488 'hierarchical' => true,
489 'show_ui' => false,
490 'show_admin_column' => false,
491 'query_var' => true,
492 'rewrite' => false,
493 'public' => false,
494 'update_count_callback' => '_update_generic_term_count',
495 );
496 register_taxonomy( self::TAXONOMY, array(), $args );
497
498 // Register for ap_actor post type if ActivityPub plugin is active.
499 if ( class_exists( '\Activitypub\Collection\Remote_Actors' ) ) {
500 register_taxonomy_for_object_type( self::TAXONOMY, \Activitypub\Collection\Remote_Actors::POST_TYPE );
501 }
502
503 register_term_meta(
504 self::TAXONOMY,
505 'post-format',
506 array(
507 'type' => 'string',
508 'single' => true,
509 'sanitize_callback' => array( __CLASS__, 'validate_post_format' ),
510 )
511 );
512
513 register_term_meta(
514 self::TAXONOMY,
515 'mime-type',
516 array(
517 'type' => 'string',
518 'single' => true,
519 'sanitize_callback' => array( __CLASS__, 'validate_mime_type' ),
520 )
521 );
522
523 register_term_meta(
524 self::TAXONOMY,
525 'title',
526 array(
527 'type' => 'string',
528 'single' => true,
529 'sanitize_callback' => array( __CLASS__, 'validate_title' ),
530 )
531 );
532
533 register_term_meta(
534 self::TAXONOMY,
535 'last-log',
536 array(
537 'type' => 'string',
538 'single' => true,
539 'sanitize_callback' => array( __CLASS__, 'validate_last_log' ),
540 )
541 );
542
543 register_term_meta(
544 self::TAXONOMY,
545 'active',
546 array(
547 'type' => 'boolean',
548 'single' => true,
549 'sanitize_callback' => array( __CLASS__, 'validate_active' ),
550 )
551 );
552
553 register_term_meta(
554 self::TAXONOMY,
555 'parser',
556 array(
557 'type' => 'string',
558 'single' => true,
559 'sanitize_callback' => array( __CLASS__, 'validate_parser' ),
560 )
561 );
562
563 register_term_meta(
564 self::TAXONOMY,
565 'interval',
566 array(
567 'type' => 'integer',
568 'single' => true,
569 'sanitize_callback' => array( __CLASS__, 'validate_interval' ),
570 )
571 );
572
573 register_term_meta(
574 self::TAXONOMY,
575 'modifier',
576 array(
577 'type' => 'integer',
578 'single' => true,
579 'sanitize_callback' => array( __CLASS__, 'validate_modifier' ),
580 )
581 );
582
583 register_term_meta(
584 self::TAXONOMY,
585 'next-poll',
586 array(
587 'type' => 'string',
588 'single' => true,
589 'sanitize_callback' => array( __CLASS__, 'validate_poll_date' ),
590 )
591 );
592
593 register_term_meta(
594 self::TAXONOMY,
595 'last-poll',
596 array(
597 'type' => 'string',
598 'single' => true,
599 'sanitize_callback' => array( __CLASS__, 'validate_poll_date' ),
600 )
601 );
602
603 do_action( 'friends_after_register_feed_taxonomy' );
604 }
605
606 public function activate() {
607 if ( metadata_exists( 'term', $this->term->term_id, 'active' ) ) {
608 update_metadata( 'term', $this->term->term_id, 'active', true );
609 } else {
610 add_metadata( 'term', $this->term->term_id, 'active', true, true );
611 }
612 do_action( 'friends_user_feed_activated', $this );
613 }
614
615 public function deactivate() {
616 delete_metadata( 'term', $this->term->term_id, 'active' );
617 do_action( 'friends_user_feed_deactivated', $this );
618 }
619
620 /**
621 * Delete this feed.
622 */
623 public function delete() {
624 wp_delete_term( $this->term->term_id, self::TAXONOMY );
625 }
626
627 /**
628 * Saves a new feed as a term for the user.
629 *
630 * @param User $friend_user The user to be associated.
631 * @param string $url The feed URL.
632 * @param array $args Further parameters. Possibly array keys: active, parser, post_format, mime_type, title.
633 * @return User_Feed A newly created User_Feed.
634 */
635 public static function save( User $friend_user, $url, $args = array() ) {
636 $all_urls = $friend_user->save_feeds(
637 array(
638 $url => $args,
639 )
640 );
641
642 if ( is_wp_error( $all_urls ) ) {
643 return $all_urls;
644 }
645
646 if ( ! is_array( $all_urls ) || ! isset( $all_urls[ $url ] ) ) {
647 return new \WP_Error( 'cound-not-save-feed' );
648 }
649
650 $term_id = $all_urls[ $url ];
651
652 $term = get_term( $term_id );
653 if ( is_wp_error( $term ) ) {
654 return $term;
655 }
656
657 return new self( $term, $friend_user );
658 }
659
660 /**
661 * Generic function for getting User_Feed metadata.
662 *
663 * @param string $key The key.
664 */
665 public function get_metadata( $key ) {
666 if ( metadata_exists( 'term', $this->term->term_id, $key ) ) {
667 return get_metadata( 'term', $this->term->term_id, $key, true );
668 }
669 return null;
670 }
671
672 /**
673 * Generic function for updating User_Feed metadata.
674 *
675 * @param string $key The key.
676 * @param string $value The value.
677 */
678 public function update_metadata( $key, $value ) {
679 if ( metadata_exists( 'term', $this->term->term_id, $key ) ) {
680 return update_metadata( 'term', $this->term->term_id, $key, $value );
681 }
682 return add_metadata( 'term', $this->term->term_id, $key, $value, true );
683 }
684
685 /**
686 * Generic function for deleting User_Feed metadata.
687 *
688 * @param string $key The key.
689 */
690 public function delete_metadata( $key ) {
691 if ( metadata_exists( 'term', $this->term->term_id, $key ) ) {
692 return delete_metadata( 'term', $this->term->term_id, $key );
693 }
694 }
695
696 /**
697 * Update the last-log metadata.
698 *
699 * @param string $value The value to log.
700 *
701 * @return int|false The inserted term id.
702 */
703 public function update_last_log( $value ) {
704 // Explicitly use time() to allow mocking it inside the namespace.
705 return $this->update_metadata( 'last-log', gmdate( 'Y-m-d H:i:s', time() ) . ': ' . $value );
706 }
707
708 /**
709 * Get the feed with the specific id.
710 *
711 * @param int $id The feed id.
712 *
713 * @return object|\WP_Error A User_Feed object.
714 */
715 public static function get_by_id( $id ) {
716 $term = get_term( $id, self::TAXONOMY );
717 if ( is_wp_error( $term ) ) {
718 return $term;
719 }
720
721 return new self( $term );
722 }
723
724 /**
725 * Get the feed with a specific URL.
726 *
727 * @param string $url The feed URL.
728 *
729 * @return object|\WP_Error A User_Feed object.
730 */
731 public static function get_by_url( $url ) {
732 $term_query = new \WP_Term_Query(
733 array(
734 'taxonomy' => self::TAXONOMY,
735 'slug' => $url,
736 'hide_empty' => false,
737 )
738 );
739 foreach ( $term_query->get_terms() as $term ) {
740 return new self( $term );
741 }
742
743 return new \WP_Error( 'term_not_found' );
744 }
745
746 /**
747 * Get the feed linked to a specific ap_actor post ID.
748 *
749 * @param int $ap_actor_id The ap_actor post ID.
750 * @return User_Feed|\WP_Error A User_Feed object or WP_Error if not found.
751 */
752 public static function get_by_ap_actor_id( $ap_actor_id ) {
753 $terms = get_the_terms( $ap_actor_id, self::TAXONOMY );
754 if ( $terms && ! is_wp_error( $terms ) ) {
755 return new self( $terms[0] );
756 }
757
758 return new \WP_Error( 'term_not_found' );
759 }
760
761 public static function get_all_users() {
762 $term_query = new \WP_Term_Query(
763 array(
764 'taxonomy' => self::TAXONOMY,
765 'hide_empty' => false,
766 )
767 );
768 $users = array();
769 foreach ( $term_query->get_terms() as $term ) {
770 $feed = new self( $term );
771 $friend_user = $feed->get_friend_user();
772 if ( $friend_user ) {
773 $users[ $friend_user->ID ] = $friend_user;
774 }
775 }
776
777 return $users;
778 }
779
780 /**
781 * Get all feeds due.
782 *
783 * @param bool $ignore_due_date Whether to get also undue feeds.
784 *
785 * @return array An array of User_Feed objects.
786 */
787 public static function get_all_due( $ignore_due_date = false ) {
788 $term_query = new \WP_Term_Query(
789 array(
790 'taxonomy' => self::TAXONOMY,
791 'meta_key' => 'active', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
792 'meta_value' => true, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
793 'hide_empty' => false,
794 )
795 );
796
797 $due_feeds = array();
798 foreach ( $term_query->get_terms() as $term ) {
799 $feed = new self( $term );
800 // Explicitly use time() to allow mocking it inside the namespace.
801 if ( $ignore_due_date || gmdate( 'Y-m-d H:i:s', time() ) >= $feed->get_next_poll() ) {
802 $due_feeds[] = $feed;
803 }
804 }
805
806 // Let's poll the oldest feeds first.
807 usort(
808 $due_feeds,
809 function ( $a, $b ) {
810 return strcmp( $a->get_next_poll(), $b->get_next_poll() );
811 }
812 );
813
814 return $due_feeds;
815 }
816
817 /**
818 * Get all feeds for a parser
819 *
820 * @param string $parser The feed parser.
821 *
822 * @return array A list of user feeds.
823 */
824 public static function get_by_parser( $parser ) {
825 $term_query = new \WP_Term_Query(
826 array(
827 'taxonomy' => self::TAXONOMY,
828 'meta_key' => 'parser', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
829 'meta_value' => $parser, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
830 'hide_empty' => false,
831 )
832 );
833 $feeds = array();
834 foreach ( $term_query->get_terms() as $term ) {
835 $feeds[] = new self( $term );
836 }
837
838 return $feeds;
839 }
840
841 /**
842 * Get the parser for a post
843 *
844 * @param int $post_id The post id.
845 *
846 * @return string The feed parser.
847 */
848 public static function get_parser_for_post_id( $post_id ) {
849 $user_feeds = wp_get_object_terms( $post_id, User_Feed::TAXONOMY );
850 if ( empty( $user_feeds ) ) {
851 // We used to save the parser as post meta.
852 return get_post_meta( $post_id, 'parser', true );
853 }
854
855 $user_feed = reset( $user_feeds );
856 $user_feed = new self( $user_feed );
857 return $user_feed->get_parser();
858 }
859 }
860