PluginProbe ʕ •ᴥ•ʔ
Custom Twitter Feeds – A Tweets Widget or X Feed Widget / 2.7.0
Custom Twitter Feeds – A Tweets Widget or X Feed Widget v2.7.0
2.8.0 2.7.0 2.6.1 2.6.0 1.0 1.0.1 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.2 1.2.1 1.2.10 1.2.11 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.4 1.4.1 1.5 1.5.1 1.6 1.6.1 1.7 1.8 1.8.1 1.8.2 1.8.3 1.8.4 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1 2.1.1 2.1.2 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.5.4 2.5.5 trunk
custom-twitter-feeds / inc / CTF_Feed.php
custom-twitter-feeds / inc Last commit date
Admin 1 month ago Builder 1 month ago Integrations 1 month ago SmashTwitter 1 month ago UsageTracking 1 month ago V2 1 month ago blocks 1 month ago CTF_Display_Elements.php 1 month ago CTF_Feed.php 1 month ago CTF_Feed_Locator.php 1 month ago CTF_GDPR_Integrations.php 1 month ago CTF_Parse.php 1 month ago CTF_Settings.php 1 month ago CtfAdmin.php 1 month ago CtfCache.php 1 month ago CtfFeed.php 1 month ago CtfOauthConnect.php 1 month ago SB_Twitter_Cron_Updater.php 1 month ago admin-hooks.php 1 month ago ctf-functions.php 1 month ago notices.php 1 month ago widget.php 1 month ago
CTF_Feed.php
1506 lines
1 <?php
2 /**
3 * Class SB_Instagram_Feed
4 *
5 * Retrieves data and generates the html for each feed. The
6 * "display_instagram" function in the if-functions.php file
7 * is where this class is primarily used.
8 *
9 * @since 2.0/4.0
10 */
11 namespace TwitterFeed;
12 use TwitterFeed\CtfCache;
13 use TwitterFeed\CTF_Parse;
14 use TwitterFeed\V2\CtfOauthConnect;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 die( '-1' );
18 }
19
20 class CTF_Feed
21 {
22 /**
23 * @var string
24 */
25 private $regular_feed_transient_name;
26
27 /**
28 * @var string
29 */
30 private $header_transient_name;
31
32 /**
33 * @var string
34 */
35 private $backup_feed_transient_name;
36
37 /**
38 * @var string
39 */
40 private $backup_header_transient_name;
41
42 /**
43 * @var array
44 */
45 private $post_data;
46
47 /**
48 * @var
49 */
50 private $header_data;
51
52 /**
53 * @var array
54 */
55 private $next_pages;
56
57 /**
58 * @var array
59 */
60 private $transient_atts;
61
62 /**
63 * @var int
64 */
65 private $last_retrieve;
66
67 /**
68 * @var bool
69 */
70 private $should_paginate;
71
72 /**
73 * @var int
74 */
75 private $num_api_calls;
76
77 /**
78 * @var array
79 */
80 private $image_ids_post_set;
81
82 /**
83 * @var bool
84 */
85 private $should_use_backup;
86
87 /**
88 * @var array
89 */
90 private $report;
91
92 /**
93 * @var array
94 *
95 * @since 2.1.1/5.2.1
96 */
97 private $resized_images;
98
99 /**
100 * @var array
101 *
102 * @since 2.1.3/5.2.3
103 */
104 protected $one_post_found;
105
106 private $last_id_data;
107
108 /**
109 * @var CtfCache
110 */
111 protected $cache;
112
113 /**
114 * SB_Instagram_Feed constructor.
115 *
116 * @param string $transient_name ID of this feed
117 * generated in the SB_Instagram_Settings class
118 */
119 public function __construct( $transient_name ) {
120 $this->regular_feed_transient_name = $transient_name;
121
122 $header_transient_name = str_replace( 'ctf_', 'ctf_header_', $transient_name );
123 $header_transient_name = substr($header_transient_name, 0, 44);
124 $this->header_transient_name = $header_transient_name;
125
126 $this->post_data = array();
127 $this->next_pages = array();
128 $this->should_paginate = true;
129
130 // this is a count of how many api calls have been made for each feed
131 // type and term.
132 // By default the limit is 10
133 $this->num_api_calls = 0;
134 $this->max_api_calls = 10;
135 $this->should_use_backup = false;
136
137 // used for errors and the sbi_debug report
138 $this->report = array();
139
140 $this->resized_images = array();
141
142 $this->one_post_found = false;
143 $this->cache = new CtfCache( $transient_name, 600, 1 );
144 }
145
146 public function set_cache( $cache_seconds, $settings ) {
147 $feed_id = $this->regular_feed_transient_name;
148
149 $feed_page = 1;
150 $this->cache = new CtfCache( $feed_id, $cache_seconds, $feed_page );
151 }
152
153 /**
154 * @return array
155 *
156 * @since 2.0/5.0
157 */
158 public function get_post_data() {
159 return $this->post_data;
160 }
161
162 /**
163 * @return array
164 *
165 * @since 2.0/5.0
166 */
167 public function set_post_data( $post_data ) {
168 $this->post_data = $post_data;
169 }
170
171 /**
172 * @return array
173 *
174 * @since 2.1.1/5.2.1
175 */
176 public function set_resized_images( $resized_image_data ) {
177 $this->resized_images = $resized_image_data;
178 }
179
180 /**
181 * @return array
182 *
183 * @since 2.0/5.0
184 */
185 public function get_next_pages() {
186 return $this->next_pages;
187 }
188
189 /**
190 * @return array
191 *
192 * @since 2.1.1/5.2.1
193 */
194 public function get_resized_images() {
195 return $this->resized_images;
196 }
197
198 /**
199 * Checks the database option related the transient expiration
200 * to ensure it will be available when the page loads
201 *
202 * @return bool
203 *
204 * @since 2.0/4.0
205 */
206 public function regular_cache_exists() {
207 //Check whether the cache transient exists in the database and is available for more than one more minute
208 if ( strpos( $this->regular_feed_transient_name, 'ctf_!' ) !== false ) {
209 $this->transient_data = $this->cache->get_transient($this->transient_name);
210
211 $transient_exists = $this->cache->get_persistent( $this->regular_feed_transient_name );
212 } else {
213 $transient_exists = $this->cache->get_transient( $this->regular_feed_transient_name );
214 }
215
216 return $transient_exists;
217 }
218
219 /**
220 * Checks the database option related the header transient
221 * expiration to ensure it will be available when the page loads
222 *
223 * @return bool
224 *
225 * @since 2.0/5.0
226 */
227 public function regular_header_cache_exists() {
228 $header_transient = $this->cache->get_transient( $this->header_transient_name );
229
230 return $header_transient;
231 }
232
233 /**
234 * @return bool
235 *
236 * @since 2.0/5.0
237 */
238 public function should_use_backup() {
239 return $this->should_use_backup || empty( $this->post_data );
240 }
241
242 /**
243 * The header is only displayed when the setting is enabled and
244 * an account has been connected
245 *
246 * Overwritten in the Pro version
247 *
248 * @param array $settings settings specific to this feed
249 * @param array $feed_types_and_terms organized settings related to feed data
250 * (ex. 'user' => array( 'smashballoon', 'custominstagramfeed' )
251 *
252 * @return bool
253 *
254 * @since 2.0/5.0
255 */
256 public function need_header( $settings, $feed_types_and_terms ) {
257 $showheader = ($settings['showheader'] === 'on' || $settings['showheader'] === 'true' || $settings['showheader'] === true);
258 return ($showheader && isset( $feed_types_and_terms['users'] ));
259 }
260
261 /**
262 * Use the transient name to retrieve cached data for header
263 *
264 * @since 2.0/5.0
265 */
266 public function set_header_data_from_cache() {
267 $header_cache = $this->cache->get_transient( $this->header_transient_name );
268
269 $header_cache = json_decode( $header_cache, true );
270
271 if ( ! empty( $header_cache ) ) {
272 $this->header_data = $header_cache;
273 }
274 }
275
276 public function set_header_data( $header_data ) {
277 $this->header_data = $header_data;
278 }
279
280 /**
281 * @since 2.0/5.0
282 */
283 public function get_header_data() {
284 return $this->header_data;
285 }
286
287 /**
288 * Sets the post data, pagination data, shortcode atts used (cron cache),
289 * and timestamp of last retrieval from transient (cron cache)
290 *
291 * @param array $atts available for cron caching
292 *
293 * @since 2.0/5.0
294 */
295 public function set_post_data_from_cache( $atts = array() ) {
296 if ( strpos( $this->regular_feed_transient_name, 'ctf_!' ) !== false ) {
297 $transient_data = $this->cache->get_persistent( $this->regular_feed_transient_name );
298 } else {
299 $transient_data = $this->cache->get_transient( $this->regular_feed_transient_name );
300
301 }
302
303 $transient_data = json_decode( $transient_data, true );
304
305 if ( $transient_data ) {
306 $post_data = isset( $transient_data['data'] ) ? $transient_data['data'] : array();
307 if ( empty( $post_data ) ) {
308 $post_data = isset( $transient_data[0]['id_str'] ) ? $transient_data : array();
309 }
310 $this->post_data = $post_data;
311 $this->next_pages = isset( $transient_data['pagination'] ) ? $transient_data['pagination'] : array();
312
313 if ( isset( $transient_data['atts'] ) ) {
314 $this->transient_atts = $transient_data['atts'];
315 $this->last_retrieve = $transient_data['last_retrieve'];
316 }
317 }
318 }
319
320 /**
321 * Checks to see if there are enough posts available to create
322 * the current page of the feed
323 *
324 * @param int $num
325 * @param int $offset
326 *
327 * @return bool
328 *
329 * @since 2.0/5.0
330 */
331 public function need_posts( $num, $offset = 0 ) {
332 $num_existing_posts = is_array( $this->post_data ) ? count( $this->post_data ) : 0;
333 $num_needed_for_page = (int)$num + (int)$offset;
334
335 ($num_existing_posts < $num_needed_for_page) ? $this->add_report( 'need more posts' ) : $this->add_report( 'have enough posts' );
336
337 return ($num_existing_posts < $num_needed_for_page);
338 }
339
340 /**
341 * Checks to see if there are additional pages available for any of the
342 * accounts in the feed and that the max conccurrent api request limit
343 * has not been reached
344 *
345 * @return bool
346 *
347 * @since 2.0/5.0
348 */
349 public function can_get_more_posts() {
350 $one_type_and_term_has_more_ages = $this->next_pages !== false;
351 $max_concurrent_api_calls_not_met = $this->num_api_calls < $this->max_api_calls;
352 $max_concurrent_api_calls_not_met ? $this->add_report( 'max conccurrent requests not met' ) : $this->add_report( 'max concurrent met' );
353 $one_type_and_term_has_more_ages ? $this->add_report( 'more pages available' ) : $this->add_report( 'no next page' );
354
355 return ($one_type_and_term_has_more_ages && $max_concurrent_api_calls_not_met);
356 }
357
358 /**
359 * Appends one filtered API request worth of posts for each feed term
360 *
361 * @param $settings
362 * @param array $feed_types_and_terms organized settings related to feed data
363 * (ex. 'user' => array( 'smashballoon', 'custominstagramfeed' )
364 * @param array $connected_accounts_for_feed connected account data for the
365 * feed types and terms
366 *
367 * @since 2.0/5.0
368 * @since 2.0/5.1 added logic to make a second attempt at an API connection
369 * @since 2.0/5.1.2 remote posts only retrieved if API requests are not
370 * delayed, terms shuffled if there are more than 5
371 * @since 2.2/5.3 added logic to refresh the access token for basic display
372 * accounts if needed before using it in an API request
373 */
374 public function add_remote_posts( $settings, $feed_types_and_terms ) {
375 $new_post_sets = array();
376 $next_pages = $this->next_pages;
377 /**
378 * Number of posts to retrieve in each API call
379 *
380 * @param int Minimum number of posts needed in each API request
381 * @param array $settings Settings for this feed
382 *
383 * @since 2.0/5.0
384 */
385 $num = apply_filters( 'ctf_num_in_request', $settings['apinum'], $settings );
386
387 if ( ! $settings['includereplies'] && ! $settings['selfreplies'] ) {
388 $num = min( 200, $num * 3 );
389 }
390
391 $one_successful_connection = false;
392 $one_post_found = false;
393 $next_page_found = false;
394 $one_api_request_delayed = false;
395
396 foreach ( $feed_types_and_terms as $type => $terms ) {
397 if ( is_array( $terms ) && count( $terms ) > 5 ) {
398 shuffle( $terms );
399 }
400 foreach ( $terms as $term_and_params ) {
401 $params = array(
402 'count' => $num
403 );
404 $this->add_report( 'num ' . $num );
405
406 $term = $term_and_params['term'];
407 $params = array_merge( $params, $term_and_params['params'] );
408
409 if ( ! empty( $next_pages[ $term . '_' . $type ] ) ) {
410 $params['count'] = min( $params['count'] + 1, 200 );
411 $params['max_id'] = $next_pages[ $term . '_' . $type ];
412 }
413
414 $params['tweet_mode'] = 'extended';
415
416 if ( $type === 'usertimeline' ) {
417 if ( ! empty ( $term ) ) {
418 $params['screen_name'] = $term;
419 }
420 //if ( $settings['includereplies'] || $settings['selfreplies'] ) {
421 // $params['exclude_replies'] = 'false';
422 //} else {
423 $params['exclude_replies'] = 'true';
424 //}
425 }
426
427 if ( $type === 'hometimeline' ) {
428 //if ( $settings['includereplies'] || $settings['selfreplies'] ) {
429 // $params['exclude_replies'] = 'false';
430 //} else {
431 $params['exclude_replies'] = 'true';
432 //}
433 }
434
435 if ( $type === 'search' || $type === 'hashtag' ) {
436 $params['q'] = $term;
437 }
438
439 if ( $type === 'lists' ) {
440 if ( ! empty ( $term ) ) {
441 $params['list_id'] = $term;
442 }
443 }
444
445 if ( $type === 'userslookup' ) {
446 if ( ! empty ( $term ) ) {
447 $params['screen_name'] = $term;
448 }
449 }
450
451 // max_id parameter should only be included for the second set of posts
452 $this->num_api_calls++;
453 $api_obj = $this->apiConnectionResponse( $params, $type, $settings );
454 $raw_tweet_data = json_decode( $api_obj->json , $assoc = true );
455 if ( isset( $raw_tweet_data['errors'][0] ) ) {
456 if ( empty( $api_obj ) ) {
457 $api_obj = new \stdClass();
458 }
459 $api_obj->api_error_no = $raw_tweet_data['errors'][0]['code'];
460 $api_obj->api_error_message = $raw_tweet_data['errors'][0]['message'];
461 $this->add_report( 'error ' . $api_obj->api_error_no . ' ' . $api_obj->api_error_message );
462
463 $next_pages[ $term . '_' . $type ] = false;
464 } else {
465 if ( isset( $raw_tweet_data['statuses'] ) ) {
466 $raw_tweet_data = $raw_tweet_data['statuses'];
467 }
468
469 if ( ! empty( $params['max_id'] ) ) {
470 // remove the first tweet as it was returned in the paginated request
471 array_shift( $raw_tweet_data );
472 }
473
474 if ( count( $raw_tweet_data ) > 0 ) {
475 if ( isset( $raw_tweet_data[ count( $raw_tweet_data ) - 1 ] ) ) {
476 $last_tweet = $raw_tweet_data[ count( $raw_tweet_data ) - 1 ];
477 $this->add_report( 'normal last tweet' );
478
479 } else {
480 $last_tweet = $raw_tweet_data[0];
481 $this->add_report( 'backup last tweet' );
482
483 }
484
485 $working_tweet_set = CTF_Feed::reduceTweetSetData( $raw_tweet_data );
486 $working_tweet_set = CTF_Feed::filterTweetSet( $working_tweet_set, $settings );
487
488 if ( count( $working_tweet_set ) > 1 ) {
489 $last_tweet = $raw_tweet_data[ count( $working_tweet_set ) - 1 ];
490 }
491 $next_pages[ $term . '_' . $type ] = CTF_Parse_Pro::get_tweet_id( $last_tweet );
492
493 $new_post_sets[] = $working_tweet_set;
494 $one_post_found = true;
495 $next_page_found = true;
496 } else {
497 $this->add_report( 'no raw tweet data' );
498 $this->add_report( $api_obj->json );
499
500 $next_pages[ $term . '_' . $type ] = false;
501 }
502 }
503 }
504 }
505
506
507
508 $posts = $this->merge_posts( $new_post_sets, $settings );
509
510 if ( ! empty( $this->post_data ) && is_array( $this->post_data ) ) {
511 $posts = array_merge( $this->post_data, $posts );
512 } elseif ( $one_post_found ) {
513 $this->one_post_found = true;
514 }
515
516 $this->post_data = $posts;
517
518 if ( isset( $next_page_found ) && $next_page_found ) {
519 $this->next_pages = $next_pages;
520 } else {
521 $this->next_pages = false;
522 }
523 }
524
525 /**
526 * attempts to connect and retrieve tweets from the Twitter api
527 *
528 * @return mixed|string object containing the response
529 */
530 public function apiConnectionResponse( $get_fields, $end_point, $settings ){
531 // Only can be set in the options page
532 $request_settings = array(
533 'consumer_key' => $settings['consumer_key'],
534 'consumer_secret' => $settings['consumer_secret'],
535 'access_token' => $settings['access_token'],
536 'access_token_secret' => $settings['access_token_secret'],
537 );
538
539
540 // actual connection
541 $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
542 $twitter_connect->setUrlBase();
543 $twitter_connect->setGetFields( $get_fields );
544 $twitter_connect->setRequestMethod( $settings['request_method'] );
545
546 return $twitter_connect->performRequest();
547 }
548
549 public static function reduceTweetSetData( $tweets ) {
550
551 $tweet_count = count( $tweets );
552 $len = count( $tweets );
553 $trimmed_tweets = array();
554
555 foreach ( $tweets as $tweet ) {
556
557 $trimmed_tweet = array();
558 $trimmed_tweet['user']['name'] = $tweet['user']['name'];
559 $trimmed_tweet['user']['screen_name'] = $tweet['user']['screen_name'];
560 $trimmed_tweet['user']['verified'] = $tweet['user']['verified'];
561 $trimmed_tweet['user']['profile_image_url_https'] = $tweet['user']['profile_image_url_https'];
562 $trimmed_tweet['user']['utc_offset'] = $tweet['user']['utc_offset'];
563 $trimmed_tweet['user']['statuses_count'] = $tweet['user']['statuses_count'];
564 $trimmed_tweet['user']['followers_count'] = $tweet['user']['followers_count'];
565 $trimmed_tweet['user']['description'] = $tweet['user']['description'];
566 $trimmed_tweet['text'] = isset( $tweet['text'] ) ? $tweet['text'] : $tweet['full_text'];
567 $trimmed_tweet['id_str'] = $tweet['id_str'];
568 $trimmed_tweet['created_at'] = $tweet['created_at'];
569 $trimmed_tweet['retweet_count'] = $tweet['retweet_count'];
570 $trimmed_tweet['favorite_count'] = $tweet['favorite_count'];
571
572 if ( isset( $tweet['entities']['urls'][0] ) ) {
573 foreach ( $tweet['entities']['urls'] as $url ) {
574 $trimmed_tweet['entities']['urls'][] = array(
575 'url' => $url['url'],
576 'expanded_url' => $url['expanded_url'],
577 'display_url' => $url['display_url'],
578
579 );
580 }
581 }
582
583 if ( isset( $tweet['retweeted_status'] ) ) {
584 $trimmed_tweet['retweeted_status']['user']['name'] = $tweet['retweeted_status']['user']['name'];
585 $trimmed_tweet['retweeted_status']['user']['screen_name'] = $tweet['retweeted_status']['user']['screen_name'];
586 $trimmed_tweet['retweeted_status']['user']['verified'] = $tweet['retweeted_status']['user']['verified'];
587 $trimmed_tweet['retweeted_status']['user']['profile_image_url_https'] = $tweet['retweeted_status']['user']['profile_image_url_https'];
588 $trimmed_tweet['retweeted_status']['user']['utc_offset'] = $tweet['retweeted_status']['user']['utc_offset'];
589 $trimmed_tweet['retweeted_status']['text'] = isset( $tweet['retweeted_status']['text'] ) ? $tweet['retweeted_status']['text'] : $tweet['retweeted_status']['full_text'];
590 $trimmed_tweet['retweeted_status']['id_str'] = $tweet['retweeted_status']['id_str'];
591 $trimmed_tweet['retweeted_status']['created_at'] = $tweet['retweeted_status']['created_at'];
592 $trimmed_tweet['retweeted_status']['retweet_count'] = $tweet['retweeted_status']['retweet_count'];
593 $trimmed_tweet['retweeted_status']['favorite_count'] = $tweet['retweeted_status']['favorite_count'];
594 if ( isset( $tweet['retweeted_status']['entities']['urls'][0] ) ) {
595 foreach ( $tweet['retweeted_status']['entities']['urls'] as $url ) {
596 $trimmed_tweet['retweeted_status']['entities']['urls'][] = array(
597 'url' => $url['url'],
598 'expanded_url' => $url['expanded_url'],
599 'display_url' => $url['display_url'],
600
601 );
602 }
603 }
604
605 if ( isset( $tweet['retweeted_status']['quoted_status'] ) ) {
606 $trimmed_tweet['retweeted_status']['quoted_status']['user']['name'] = $tweet['retweeted_status']['quoted_status']['user']['name'];
607 $trimmed_tweet['retweeted_status']['quoted_status']['user']['screen_name'] = $tweet['retweeted_status']['quoted_status']['user']['screen_name'];
608 $trimmed_tweet['retweeted_status']['quoted_status']['user']['verified'] = $tweet['retweeted_status']['quoted_status']['user']['verified'];
609 $trimmed_tweet['retweeted_status']['quoted_status']['user']['profile_image_url_https'] = $tweet['retweeted_status']['quoted_status']['user']['profile_image_url_https'];
610 $trimmed_tweet['retweeted_status']['quoted_status']['text'] = isset( $tweet['retweeted_status']['quoted_status']['text'] ) ? $tweet['retweeted_status']['quoted_status']['text'] : $tweet['retweeted_status']['quoted_status']['full_text'];
611 $trimmed_tweet['retweeted_status']['quoted_status']['id_str'] = $tweet['retweeted_status']['quoted_status']['id_str'];
612 $trimmed_tweet['retweeted_status']['quoted_status']['created_at'] = $tweet['retweeted_status']['quoted_status']['created_at'];
613 if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['urls'][0] ) ) {
614 foreach ( $tweet['retweeted_status']['quoted_status']['entities']['urls'] as $url ) {
615 $trimmed_tweet['retweeted_status']['quoted_status']['entities']['urls'][] = array(
616 'url' => $url['url'],
617 'expanded_url' => $url['expanded_url'],
618 'display_url' => $url['display_url'],
619 );
620 }
621 }
622 }
623 }
624
625 if ( isset( $tweet['quoted_status'] ) ) {
626 $trimmed_tweet['quoted_status']['user']['name'] = $tweet['quoted_status']['user']['name'];
627 $trimmed_tweet['quoted_status']['user']['screen_name'] = $tweet['quoted_status']['user']['screen_name'];
628 $trimmed_tweet['quoted_status']['user']['profile_image_url_https'] = $tweet['quoted_status']['user']['profile_image_url_https'];
629 $trimmed_tweet['quoted_status']['user']['verified'] = $tweet['quoted_status']['user']['verified'];
630 $trimmed_tweet['quoted_status']['text'] = isset( $tweet['quoted_status']['text'] ) ? $tweet['quoted_status']['text'] : $tweet['quoted_status']['full_text'];
631 $trimmed_tweet['quoted_status']['id_str'] = $tweet['quoted_status']['id_str'];
632 $trimmed_tweet['quoted_status']['created_at'] = $tweet['quoted_status']['created_at'];
633 if ( isset( $tweet['quoted_status']['entities']['urls'][0] ) ) {
634 foreach ( $tweet['quoted_status']['entities']['urls'] as $url ) {
635 $trimmed_tweet['quoted_status']['entities']['urls'][] = array(
636 'url' => $url['url'],
637 'expanded_url' => $url['expanded_url'],
638 'display_url' => $url['display_url'],
639 );
640 }
641 }
642 }
643
644 if ( isset( $tweet['in_reply_to_screen_name'] ) ) {
645 $trimmed_tweet['in_reply_to_screen_name'] = $tweet['in_reply_to_screen_name'];
646 $trimmed_tweet['entities']['user_mentions'][0]['name'] = isset( $tweet['entities']['user_mentions'][0]['name'] ) ? $tweet['entities']['user_mentions'][0]['name'] : '';
647 $trimmed_tweet['in_reply_to_status_id_str'] = $tweet['in_reply_to_status_id_str'];
648 }
649
650 if ( isset( $tweet['extended_entities']['media'] ) ) {
651 // if there is media, we need to remove the media url from the tweet text
652 if ( isset( $tweet['extended_entities']['media'][0]['url'] ) ) {
653 $trimmed_tweet['text'] = CTF_Feed::removeStringFromText( $tweet['extended_entities']['media'][0]['url'], $trimmed_tweet['text'] );
654 }
655 $num_media = count( $tweet['extended_entities']['media'] );
656 for ( $i = 0; $i < $num_media; $i++ ) {
657 $trimmed_tweet['extended_entities']['media'][$i]['media_url_https'] = $tweet['extended_entities']['media'][$i]['media_url_https'];
658 $trimmed_tweet['extended_entities']['media'][$i]['type'] = $tweet['extended_entities']['media'][$i]['type'];
659 if ( isset( $tweet['extended_entities']['media'][$i]['sizes'] ) ) {
660 $trimmed_tweet['extended_entities']['media'][$i]['sizes'] = $tweet['extended_entities']['media'][$i]['sizes'];
661 }
662 if ( $tweet['extended_entities']['media'][$i]['type'] == 'video' || $tweet['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
663 $preferred_variant = $tweet['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
664 $highest_bitrate = 0;
665 foreach ( $tweet['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
666 if ( isset( $variant['content_type'] )
667 && $variant['content_type'] == 'video/mp4'
668 && $variant['bitrate'] > $highest_bitrate ) {
669 $highest_bitrate = $variant['bitrate'];
670 $preferred_variant = $variant['url'];
671 }
672 }
673 $trimmed_tweet['extended_entities']['media'][$i]['video_info']['variants'][0]['url'] = $preferred_variant;
674 $trimmed_tweet['extended_entities']['media'][$i]['video_info']['variants'][0]['bitrate'] = $highest_bitrate;
675
676 }
677 }
678
679 } elseif ( isset( $tweet['entities']['media'] ) ) {
680 // if there is media, we need to remove the media url from the tweet text
681 if ( isset( $tweet['entities']['media'][0]['url'] ) ) {
682 $trimmed_tweet['text'] = CTF_Feed::removeStringFromText( $tweet['entities']['media'][0]['url'], $trimmed_tweet['text'] );
683 }
684
685 $num_media = count( $tweet['entities']['media'] );
686 for ( $i = 0; $i < $num_media; $i++ ) {
687 $trimmed_tweet['entities']['media'][$i]['media_url_https'] = $tweet['entities']['media'][$i]['media_url_https'];
688 $trimmed_tweet['entities']['media'][$i]['type'] = $tweet['entities']['media'][$i]['type'];
689 if ( isset( $tweet['entities']['media'][$i]['sizes'] ) ) {
690 $trimmed_tweet['entities']['media'][$i]['sizes'] = $tweet['entities']['media'][$i]['sizes'];
691 }
692 if ( $tweet['entities']['media'][$i]['type'] == 'video' || $tweet['entities']['media'][$i]['type'] == 'animated_gif' ) {
693
694 $preferred_variant = $tweet['entities']['media'][$i]['video_info']['variants'][0]['url'];
695 $highest_bitrate = 0;
696 foreach ( $tweet['entities']['media'][$i]['video_info']['variants'] as $variant ) {
697 if ( isset( $variant['content_type'] )
698 && $variant['content_type'] == 'video/mp4'
699 && $variant['bitrate'] > $highest_bitrate ) {
700 $highest_bitrate = $variant['bitrate'];
701 $preferred_variant = $variant['url'];
702 }
703 }
704 $trimmed_tweet['entities']['media'][$i]['video_info']['variants'][0]['url'] = $preferred_variant;
705 $trimmed_tweet['entities']['media'][$i]['video_info']['variants'][0]['bitrate'] = $highest_bitrate;
706 }
707 }
708
709 }
710
711 if ( isset( $tweet['retweeted_status']['extended_entities']['media'] ) ) {
712 // if there is media, we need to remove the media url from the tweet text
713 $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
714 if ( isset( $tweet['retweeted_status']['extended_entities']['media'][0]['url'] ) ) {
715 $trimmed_tweet['retweeted_status']['text'] = CTF_Feed::removeStringFromText( $tweet['retweeted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
716 }
717
718 $num_media = count( $tweet['retweeted_status']['extended_entities']['media'] );
719 for ( $i = 0; $i < $num_media; $i++ ) {
720 $trimmed_tweet['retweeted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['media_url_https'];
721 $trimmed_tweet['retweeted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['type'];
722 if ( isset( $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
723 $trimmed_tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'];
724 }
725 if ( $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
726 foreach ( $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
727 if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
728 $trimmed_tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
729 }
730 }
731 if ( ! isset( $trimmed_tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
732 $trimmed_tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
733 }
734 }
735 }
736
737 } elseif ( isset( $tweet['retweeted_status']['entities']['media'] ) ) {
738 // if there is media, we need to remove the media url from the tweet text
739 $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
740 if ( isset( $tweet['retweeted_status']['entities']['media'][0]['url'] ) ) {
741 $trimmed_tweet['retweeted_status']['text'] = CTF_Feed::removeStringFromText( $tweet['retweeted_status']['entities']['media'][0]['url'], $retweeted_text );
742 }
743
744 $num_media = count( $tweet['retweeted_status']['entities']['media'] );
745 for( $i = 0; $i < $num_media; $i++ ) {
746 $trimmed_tweet['retweeted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['entities']['media'][$i]['media_url_https'];
747 $trimmed_tweet['retweeted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['entities']['media'][$i]['type'];
748 if ( isset( $tweet['retweeted_status']['entities']['media'][$i]['sizes'] ) ) {
749 $trimmed_tweet['retweeted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['entities']['media'][$i]['sizes'];
750 }
751 if ( $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
752 foreach ( $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
753 if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
754 $trimmed_tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
755 }
756 }
757 if ( ! isset( $trimmed_tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
758 $trimmed_tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
759 }
760 }
761 }
762
763 } elseif ( isset( $tweet['quoted_status']['extended_entities']['media'] ) ) {
764 // if there is media, we need to remove the media url from the tweet text
765 $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
766 if ( isset( $tweet['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
767 $trimmed_tweet['quoted_status']['text'] = CTF_Feed::removeStringFromText( $tweet['quoted_status']['extended_entities']['media'][0]['url'], $quoted_text );
768 }
769
770 $num_media = count( $tweet['quoted_status']['extended_entities']['media'] );
771 for( $i = 0; $i < $num_media; $i++ ) {
772 $trimmed_tweet['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
773 $trimmed_tweet['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['quoted_status']['extended_entities']['media'][$i]['type'];
774 if ( $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
775 foreach ( $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
776 if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
777 $trimmed_tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
778 }
779 }
780 if ( ! isset( $trimmed_tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
781 $trimmed_tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
782 }
783 }
784 }
785
786 } elseif ( isset( $tweet['quoted_status']['entities']['media'] ) ) {
787 // if there is media, we need to remove the media url from the tweet text
788 $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
789 if ( isset( $tweet['quoted_status']['entities']['media'][0]['url'] ) ) {
790 $trimmed_tweet['quoted_status']['text'] = CTF_Feed::removeStringFromText( $tweet['quoted_status']['entities']['media'][0]['url'], $quoted_text );
791 }
792
793 $num_media = count( $tweet['quoted_status']['entities']['media'] );
794 for( $i = 0; $i < $num_media; $i++ ) {
795 $trimmed_tweet['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['entities']['media'][$i]['media_url_https'];
796 $trimmed_tweet['quoted_status']['entities']['media'][$i]['type'] = $tweet['quoted_status']['entities']['media'][$i]['type'];
797 if ( $tweet['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
798 foreach ( $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
799 if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
800 $trimmed_tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
801 }
802 }
803 if ( ! isset( $trimmed_tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
804 $trimmed_tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
805 }
806 }
807 }
808
809 }
810
811 if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] ) ) {
812 // if there is media, we need to remove the media url from the tweet text
813 $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
814 if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
815 $trimmed_tweet['retweeted_status']['quoted_status']['text'] = CTF_Feed::removeStringFromText( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
816 }
817
818 $num_media = count( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] );
819 for ( $i = 0; $i < $num_media; $i++ ) {
820 $trimmed_tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
821 $trimmed_tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'];
822 if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
823 $trimmed_tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'];
824 }
825 if ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
826 foreach ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
827 if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
828 $trimmed_tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
829 }
830 }
831 if ( ! isset( $trimmed_tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
832 $trimmed_tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
833 }
834 }
835 }
836
837 } elseif ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'] ) ) {
838 // if there is media, we need to remove the media url from the tweet text
839 $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
840 if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'] ) ) {
841 $trimmed_tweet['retweeted_status']['quoted_status']['text'] = CTF_Feed::removeStringFromText( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'], $retweeted_text );
842 }
843
844 $num_media = count( $tweet['retweeted_status']['quoted_status']['entities']['media'] );
845 for( $i = 0; $i < $num_media; $i++ ) {
846 $trimmed_tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'];
847 $trimmed_tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'];
848 if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] ) ) {
849 $trimmed_tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'];
850 }
851 if ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
852 foreach ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
853 if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
854 $trimmed_tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
855 }
856 }
857 if ( ! isset( $trimmed_tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
858 $trimmed_tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
859 }
860 }
861 }
862
863 }
864
865 //remove the url from the text if it links to a quoted tweet that is already linked to
866 if ( isset( $tweet['quoted_status'] ) ) {
867 $maybe_remove_index = count( $tweet['entities']['urls'] ) - 1;
868 if ( isset( $tweet['entities']['urls'][$maybe_remove_index]['url'] ) ) {
869 $text = isset( $trimmed_tweet['full_text'] ) ? $trimmed_tweet['full_text'] : $trimmed_tweet['text'];
870 $trimmed_tweet['text'] = CTF_Feed::removeStringFromText( $tweet['entities']['urls'][$maybe_remove_index]['url'], $text );
871 }
872 }
873
874 $api_twitter_card = false;
875 if ( isset( $tweet['card'] ) ) {
876 $api_twitter_card = self::parse_api_card_data($tweet['card']);
877 }
878
879
880 // used to generate twitter cards
881 if ( isset( $tweet['entities']['urls'][0]['expanded_url'] ) ) {
882
883 $trimmed_tweet['entities']['urls'][0]['expanded_url'] = $tweet['entities']['urls'][0]['expanded_url'];
884
885 $twitter_card = CTF_Feed::maybeGetTwitterCardData( $tweet['entities']['urls'][0]['expanded_url'], $trimmed_tweet['id_str'] );
886
887 if ( ! empty( $twitter_card ) ) {
888 $trimmed_tweet['twitter_card'] = $twitter_card;
889
890 $remove_url_from_tweet = apply_filters( 'ctf_should_remove_url_from_text', true );
891 if ( ! empty( $twitter_card["twitter:card"] ) && isset( $tweet['entities']['urls'][0]['url'] ) && $remove_url_from_tweet ) {
892 $trimmed_tweet['text'] = CTF_Feed::removeStringFromText( $tweet['entities']['urls'][0]['url'], $trimmed_tweet['text'] );
893 }
894 } elseif ( $twitter_card !== false ) {
895 $trimmed_tweet['twitter_card'] = $twitter_card;
896 }
897
898 }
899
900 if ( isset( $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'] ) ) {
901
902 $trimmed_tweet['retweeted_status']['entities']['urls'][0]['expanded_url'] = $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'];
903
904 $twitter_card = CTF_Feed::maybeGetTwitterCardData( $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'], $trimmed_tweet['retweeted_status']['id_str'] );
905
906 if ( ! empty( $twitter_card ) ) {
907 $trimmed_tweet['retweeted_status']['twitter_card'] = $twitter_card;
908 $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
909
910 $remove_url_from_tweet = apply_filters( 'ctf_should_remove_url_from_text', true );
911 if ( ! empty( $twitter_card["twitter:card"] ) && isset( $tweet['retweeted_status']['entities']['urls'][0]['url'] ) && $remove_url_from_tweet ) {
912 $trimmed_tweet['retweeted_status']['text'] = CTF_Feed::removeStringFromText( $tweet['retweeted_status']['entities']['urls'][0]['url'], $retweeted_text );
913 }
914 } elseif ( $twitter_card !== false ) {
915 $trimmed_tweet['twitter_card'] = $twitter_card;
916 }
917 }
918
919 if ( ! empty( $api_twitter_card ) ) {
920 $trimmed_tweet['twitter_card'] = $api_twitter_card;
921
922 if ( isset( $tweet['entities']['urls'][0]['expanded_url'] ) ) {
923
924 $target_one = $tweet['entities']['urls'][0]['expanded_url'];
925 $target_two = $tweet['entities']['urls'][0]['url'];
926
927 $remove_url_from_tweet = apply_filters( 'ctf_should_remove_url_from_text', true );
928 if ( $remove_url_from_tweet ) {
929 $trimmed_tweet['text'] = CTF_Feed::removeStringFromText( $target_one, $trimmed_tweet['text'] );
930 $trimmed_tweet['text'] = CTF_Feed::removeStringFromText( $target_two, $trimmed_tweet['text'] );
931 }
932
933 }
934
935
936 if ( isset( $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'] ) ) {
937
938 $target_one = $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'];
939 $target_two = $tweet['retweeted_status']['entities']['urls'][0]['url'];
940
941 $remove_url_from_tweet = apply_filters( 'ctf_should_remove_url_from_text', true );
942 if ( $remove_url_from_tweet ) {
943 $trimmed_tweet['retweeted_status']['text'] = CTF_Feed::removeStringFromText( $target_one, $trimmed_tweet['retweeted_status']['text'] );
944 $trimmed_tweet['retweeted_status']['text'] = CTF_Feed::removeStringFromText( $target_two, $trimmed_tweet['retweeted_status']['text'] );
945 }
946 }
947 }
948
949 $trimmed_tweets[] = $trimmed_tweet;
950
951 }
952
953 return $trimmed_tweets;
954 }
955
956 public static function filterTweetSet( $tweet_set, $settings ){
957
958 $tweets_to_remove_strip_ctf = str_replace( 'ctf_', '', $settings['remove_by_id'] );
959 $ids_of_tweets_to_remove = ! empty( $tweets_to_remove_strip_ctf ) ? explode( ',', str_replace( ' ', '', $tweets_to_remove_strip_ctf ) ) : '';
960
961 $tweets_that_pass_filter = array();
962
963 foreach ( $tweet_set as $tweet ) {
964 $retweet_id = isset( $tweet['retweeted_status']['id_str'] ) ? $tweet['retweeted_status']['id_str'] : '';
965 $keep = true;
966 if ( ! empty( $retweet_id ) && ! $settings['includeretweets'] ) {
967 $keep = false;
968 } elseif ( !$settings['includereplies'] && !$settings['selfreplies'] && isset( $tweet['in_reply_to_screen_name'] ) ) {
969 $keep = false;
970 } elseif ( !$settings['includereplies']
971 && $settings['selfreplies']
972 && isset( $tweet['in_reply_to_screen_name'] )
973 && $tweet['in_reply_to_screen_name'] !== $tweet['user']['screen_name']) {
974 $keep = false;
975 } elseif ( ! empty( $ids_of_tweets_to_remove ) && in_array( $tweet['id_str'], $ids_of_tweets_to_remove ) ) {
976 $keep = false;
977 } elseif ( CTF_Feed::tweetShouldBeRemoved( $tweet, $settings ) ) {
978 $keep = false;
979 }
980
981 if ( $keep ) {
982 $tweets_that_pass_filter[] = $tweet;
983 }
984 }
985
986 return $tweets_that_pass_filter;
987 }
988
989 public static function tweetShouldBeRemoved( $tweet, $settings )
990 {
991 $return = false;
992 $good_text = ! empty( $settings['includewords'] ) ? explode( ',', str_replace( ' ', '', $settings['includewords'] ) ) : '';
993 $bad_text = ! empty( $settings['excludewords'] ) ? explode( ',', str_replace( ' ', '', $settings['excludewords'] ) ) : '';
994 $includewords_any_all = $settings['includeanyall'];
995 $excludewords_any_all = $settings['excludeanyall'];
996 $filter_and_or = $settings['filterandor'];
997 if ( isset( $tweet['retweeted_status']['full_text'] ) ) {
998 $tweet_text = $tweet['retweeted_status']['full_text'];
999 } elseif ( isset( $tweet['retweeted_status']['text'] ) ) {
1000 $tweet_text = $tweet['retweeted_status']['text'];
1001 } elseif ( isset( $tweet['full_text'] ) ) {
1002 $tweet_text = $tweet['full_text'];
1003 } elseif ( isset( $tweet['text'] ) ) {
1004 $tweet_text = $tweet['text'];
1005 } else {
1006 $tweet_text = '';
1007 }
1008 $tweet_text = ' ' . preg_replace( '/[,.!?:;"]+/', '', $tweet_text ) . ' '; // spaces added so that we can use strpos instead of regex to find words
1009 $tweet_text = strtolower( preg_replace( '/[\n]+/', ' ', $tweet_text ) );
1010 // don't bother with filtering process if both filters are empty
1011 if ( ! empty( $good_text ) || ! empty( $bad_text ) ) {
1012 if ( $filter_and_or == 'and' && ! empty( $good_text ) && ! empty( $bad_text ) ) {
1013 if ( CTF_Feed::hasGoodText( $good_text, $includewords_any_all, $tweet_text, true ) && CTF_Feed::hasNoBadText( $bad_text, 'any', $tweet_text, true ) ) {
1014 $return = false;
1015 } else {
1016 $return = true;
1017 }
1018 } else {
1019 if ( CTF_Feed::hasGoodText( $good_text, $includewords_any_all, $tweet_text, false ) || CTF_Feed::hasNoBadText( $bad_text, $excludewords_any_all, $tweet_text, false ) ) {
1020 $return = false;
1021 } else {
1022 $return = true;
1023 }
1024 }
1025 }
1026
1027 $return = apply_filters( 'ctf_filter_out_tweet', $return, $tweet_text, $tweet );
1028
1029 return $return;
1030 }
1031
1032 public static function hasGoodText( $good_text, $any_or_all, $tweet_text, $default )
1033 {
1034 if ( empty( $good_text ) ) { // don't factor in the includewords if there aren't any
1035 return $default;
1036 } else {
1037 $encoded_text = ' ' . str_replace( array( '+', '%0A' ), ' ', urlencode( str_replace( array( '#', '@' ), array( ' HASHTAG', ' MENTION' ), strtolower( $tweet_text ) ) ) ) . ' ';
1038
1039 if ( $any_or_all == 'any' ) {
1040 // as soon as we find any of the includewords, stop searching and return true
1041 foreach ( $good_text as $good ) {
1042 $converted_includeword = trim( str_replace('+', ' ', urlencode( str_replace( array( '#', '@' ), array( ' HASHTAG', ' MENTION' ), strtolower( $good ) ) ) ) );
1043
1044 if ( preg_match('/\b'.$converted_includeword.'\b/i', $encoded_text, $matches ) ) {
1045 return true;
1046 }
1047 }
1048 // if foreach finishes without finding any matches
1049 return false;
1050 } else {
1051 // to make sure all of the includewords are present, keep a count of
1052 // how many of the words are detected and compare it to the number that's needed
1053 $good_text_matches = 0;
1054 $number_of_good_text_to_look_for = count( $good_text );
1055 foreach ( $good_text as $good ) {
1056 $converted_includeword = trim( str_replace('+', ' ', urlencode( str_replace( array( '#', '@' ), array( ' HASHTAG', ' MENTION' ), strtolower( $good ) ) ) ) );
1057
1058 if ( preg_match('/\b'.$converted_includeword.'\b/i', $encoded_text, $matches ) ) {
1059 $good_text_matches++;
1060 }
1061
1062 }
1063 if ( $good_text_matches >= $number_of_good_text_to_look_for ) {
1064 return true;
1065 } else {
1066 return false;
1067 }
1068 }
1069 }
1070
1071 }
1072
1073 /**
1074 * if a filter is applied to this feed, check and see if this tweet needs to
1075 * or contains the excludewords text
1076 *
1077 * @param $bad_text array words the tweet cannot have to be included in the feed
1078 * @param $any_or_all enum whether any or all of the bad text words need to be included
1079 * @param $tweet_text string content text of the tweet
1080 * @param $default bool the default return type if nothing is set
1081 * @return bool whether the tweet meets the requirements for having no bad text
1082 */
1083 public static function hasNoBadText( $bad_text, $any_or_all, $tweet_text, $default )
1084 {
1085 if ( empty( $bad_text ) ) { // don't factor in the excludewords if there aren't any
1086 return $default;
1087 } else {
1088 $encoded_text = ' ' . str_replace( array( '+', '%0A' ), ' ', urlencode( str_replace( array( '#', '@' ), array( ' HASHTAG', ' MENTION' ), strtolower( $tweet_text ) ) ) ) . ' ';
1089
1090 if ( $any_or_all == 'any' ) {
1091 // as soon as we find any of the excludewords, stop searching and return false
1092 foreach ( $bad_text as $bad ) {
1093 if ( empty( $bad ) ) {
1094 return true;
1095 }
1096 $converted_excludeword = trim( str_replace('+', ' ', urlencode( str_replace( array( '#', '@' ), array( ' HASHTAG', ' MENTION' ), strtolower( $bad ) ) ) ) );
1097 if ( preg_match('/\b'.$converted_excludeword.'\b/i', $encoded_text, $matches ) ) {
1098 return false;
1099 }
1100 }
1101 // if foreach finishes without finding any matches
1102 return true;
1103 } else {
1104 // under this circumstance, all excludewords need to be present to remove
1105 // the tweet so a count is kept and compared to the number of words
1106 $bad_text_matches = 0;
1107 $number_of_bad_text_to_look_for = count( $bad_text );
1108 foreach ( $bad_text as $bad ) {
1109 $converted_excludeword = trim( str_replace('+', ' ', urlencode( str_replace( '#', 'HASHTAG', strtolower( $bad ) ) ) ) );
1110
1111 if ( preg_match('/\b'.$converted_excludeword.'\b/i', $encoded_text, $matches ) ) {
1112 $bad_text_matches++;
1113 }
1114
1115 }
1116 if ( $bad_text_matches >= $number_of_bad_text_to_look_for ) {
1117 return false;
1118 } else {
1119 return true;
1120 }
1121 }
1122 }
1123
1124 }
1125
1126 public static function removeStringFromText( $string, $text, $expanded_url = '' ) {
1127 $exceptions = array( '://fb.me/' );
1128
1129 if ( $expanded_url !== '' ) {
1130
1131 foreach ( $exceptions as $exception ) {
1132
1133 if ( strpos( $expanded_url, $exception ) !== false ) {
1134 return str_replace( $string, $expanded_url, $text );
1135 }
1136
1137 }
1138
1139 }
1140
1141 return str_replace( $string, '', $text );
1142 }
1143
1144 public static function maybeGetTwitterCardData( $url, $id ) {
1145 if ( ! ctf_is_pro_version() ) {
1146 return false;
1147 }
1148 $url_key = str_replace('&','038',$url);
1149 $url_key = preg_replace( '~[^a-zA-Z0-9]+~', '', $url_key );
1150
1151 $tc_data = get_option( 'ctf_twitter_cards', array() );
1152
1153 $card = false;
1154 if ( isset( $tc_data[ $url_key ] ) ) {
1155 $card = $tc_data[ $url_key ];
1156 } elseif ( isset( $tc_data[ $id ] ) ) {
1157 $card = $tc_data[ $id ];
1158 }
1159
1160 if ( $card && ! isset( $card['local'] ) ) {
1161 $card['local'] = CTF_Twitter_Card_Manager::add_local_image( $card, $id );
1162 }
1163
1164 return $card;
1165 }
1166
1167 public function set_next_pages( $next_pages ) {
1168 $this->next_pages = $next_pages;
1169 }
1170
1171 private function merge_posts( $post_sets, $settings ) {
1172 $merged_posts = array();
1173 $settings['sortby'] = isset( $settings['sortby'] ) ? $settings['sortby'] : 'date';
1174
1175 if ( $settings['sortby'] === 'alternate' ) {
1176 // don't bother merging posts if there is only one post set
1177 if ( isset( $post_sets[1] ) ) {
1178 $min_cycles = max( 1, (int)$settings['num'] );
1179 for( $i = 0; $i <= $min_cycles; $i++ ) {
1180 foreach ( $post_sets as $post_set ) {
1181 if ( isset( $post_set[ $i ] ) && isset( $post_set[ $i ]['id'] ) ) {
1182 $merged_posts[] = $post_set[ $i ];
1183 }
1184 }
1185 }
1186 } else {
1187 $merged_posts = isset( $post_sets[0] ) ? $post_sets[0] : array();
1188 }
1189 } elseif ( $settings['sortby'] === 'api' ) {
1190 if ( isset( $post_sets[0] ) ) {
1191 foreach ( $post_sets as $post_set ) {
1192 $merged_posts = array_merge( $merged_posts, $post_set );
1193 }
1194 }
1195 } else {
1196 // don't bother merging posts if there is only one post set
1197 if ( isset( $post_sets[1] ) ) {
1198 foreach ( $post_sets as $post_set ) {
1199 $merged_posts = array_merge( $merged_posts, $post_set );
1200 }
1201 } else {
1202 $merged_posts = isset( $post_sets[0] ) ? $post_sets[0] : array();
1203 }
1204 }
1205
1206
1207 return $merged_posts;
1208 }
1209
1210 /**
1211 * Connects to the Instagram API and records returned data
1212 *
1213 * @param $settings
1214 * @param array $feed_types_and_terms organized settings related to feed data
1215 * (ex. 'user' => array( 'smashballoon', 'custominstagramfeed' )
1216 * @param array $connected_accounts_for_feed connected account data for the
1217 * feed types and terms
1218 *
1219 * @since 2.0/5.0
1220 * @since 2.2/5.3 added logic to append bio data from the related
1221 * connected account if not available in the API response
1222 */
1223 public function set_remote_header_data( $settings, $feed_types_and_terms ) {
1224 $this->header_data = false;
1225 $endpoint = 'accountlookup';
1226 if ( $settings['type'] === 'usertimeline' ) {
1227 $endpoint = 'userslookup';
1228 }
1229
1230 // Only can be set in the options page
1231 $request_settings = array(
1232 'consumer_key' => $settings['consumer_key'],
1233 'consumer_secret' => $settings['consumer_secret'],
1234 'access_token' => $settings['access_token'],
1235 'access_token_secret' => $settings['access_token_secret'],
1236 );
1237
1238 $get_fields = $this->setGetFieldsArray( $endpoint, $settings['screenname'], $settings );
1239 // actual connection
1240 $twitter_connect = new CtfOauthConnect( $request_settings, $endpoint );
1241 $twitter_connect->setUrlBase();
1242 $twitter_connect->setGetFields( $get_fields );
1243 $twitter_connect->setRequestMethod( $settings['request_method'] );
1244
1245 $request_results = $twitter_connect->performRequest();
1246
1247 $header_json = isset( $request_results->json ) ? $request_results->json : false;
1248
1249 $header_data = json_decode( $header_json, true );
1250
1251 $this->header_data = $header_data;
1252 }
1253
1254 protected function setGetFieldsArray( $end_point, $feed_term, $settings )
1255 {
1256 $feed_type = $end_point;
1257
1258 $get_fields = array();
1259
1260 $get_fields['tweet_mode'] = 'extended';
1261
1262 if ( $feed_type === 'usertimeline' ) {
1263 if ( ! empty ( $feed_term ) ) {
1264 $get_fields['screen_name'] = $feed_term;
1265 }
1266 if ( $settings['includereplies'] || $settings['selfreplies'] ) {
1267 $get_fields['exclude_replies'] = 'false';
1268 } else {
1269 $get_fields['exclude_replies'] = 'true';
1270 }
1271 }
1272
1273 if ( $feed_type === 'hometimeline' ) {
1274 if ( $settings['includereplies'] || $settings['selfreplies'] ) {
1275 $get_fields['exclude_replies'] = 'false';
1276 } else {
1277 $get_fields['exclude_replies'] = 'true';
1278 }
1279 }
1280
1281 if ( $feed_type === 'search' || $feed_type === 'hashtag' ) {
1282 $get_fields['q'] = $feed_term;
1283 }
1284
1285 if ( $feed_type === 'lists' ) {
1286 if ( ! empty ( $feed_term ) ) {
1287 $get_fields['list_id'] = $feed_term;
1288 }
1289 }
1290
1291 if ( $feed_type === 'userslookup' ) {
1292 if ( ! empty ( $feed_term ) ) {
1293 $get_fields['screen_name'] = $feed_term;
1294 }
1295 }
1296
1297 return $get_fields;
1298 }
1299
1300
1301 /**
1302 * Stores feed data in a transient for a specified time
1303 *
1304 * @param int $cache_time
1305 * @param bool $save_backup
1306 *
1307 * @since 2.0/5.0
1308 * @since 2.0/5.1 duplicate posts removed
1309 */
1310 public function cache_feed_data( $cache_time, $save_backup = true ) {
1311 if ( ! empty( $this->post_data ) || ! empty( $this->next_pages ) ) {
1312 $this->remove_duplicate_posts();
1313 $this->trim_posts_to_max();
1314
1315 $to_cache = array(
1316 'data' => $this->post_data,
1317 'pagination' => $this->next_pages
1318 );
1319
1320 $this->cache->set_transient( $this->regular_feed_transient_name, wp_json_encode( $to_cache ), $cache_time );
1321
1322 if ( $save_backup ) {
1323 update_option( $this->backup_feed_transient_name, wp_json_encode( $to_cache ), false );
1324 }
1325 } else {
1326 $this->add_report( 'no data not caching' );
1327 }
1328 }
1329
1330 /**
1331 * Stores header data for a specified time as a transient
1332 *
1333 * @param int $cache_time
1334 * @param bool $save_backup
1335 *
1336 * @since 2.0/5.0
1337 */
1338 public function cache_header_data( $cache_time, $save_backup = true ) {
1339 if ( $this->header_data ) {
1340 $this->cache->set_transient( $this->header_transient_name, wp_json_encode( $this->header_data ), $cache_time );
1341
1342 if ( $save_backup ) {
1343 update_option( $this->backup_header_transient_name, wp_json_encode( $this->header_data ), false );
1344 }
1345 }
1346 }
1347
1348 /**
1349 * Determines if pagination can and should be used based on settings and available feed data
1350 *
1351 * @param array $settings
1352 * @param int $offset
1353 *
1354 * @return bool
1355 *
1356 * @since 2.0/5.0
1357 */
1358 public function should_use_pagination( $settings, $offset = 0 ) {
1359
1360 if ( $settings['minnum'] < 1 ) {
1361 $this->add_report( 'minnum too small' );
1362
1363 return false;
1364 }
1365 $posts_available = count( $this->post_data ) - ($offset + $settings['minnum']);
1366 $show_loadmore_button_by_settings = ($settings['showbutton'] == 'on' || $settings['showbutton'] == 'true' || $settings['showbutton'] == true ) && $settings['showbutton'] !== 'false';
1367
1368 if ( $show_loadmore_button_by_settings ) {
1369 if ( $posts_available > 0 ) {
1370 $this->add_report( 'do pagination, posts available' );
1371 return true;
1372 }
1373 $pages = $this->next_pages;
1374
1375 if ( $pages && ! $this->should_use_backup() ) {
1376 foreach ( $pages as $page ) {
1377 if ( ! empty( $page ) ) {
1378 return true;
1379 }
1380 }
1381 }
1382
1383 }
1384
1385
1386 $this->add_report( 'no pagination, no posts available' );
1387
1388 return false;
1389 }
1390
1391 /**
1392 * Overwritten in the Pro version
1393 *
1394 * @param $feed_types_and_terms
1395 *
1396 * @return string
1397 *
1398 * @since 2.1/5.2
1399 */
1400 public function get_first_user( $feed_types_and_terms ) {
1401 if ( isset( $feed_types_and_terms['users'][0] ) ) {
1402 return $feed_types_and_terms['users'][0][1];
1403 } else {
1404 return '';
1405 }
1406 }
1407
1408 /**
1409 * Adds recorded strings to an array
1410 *
1411 * @param $to_add
1412 *
1413 * @since 2.0/5.0
1414 */
1415 public function add_report( $to_add ) {
1416 $this->report[] = $to_add;
1417 }
1418
1419 /**
1420 * @return array
1421 *
1422 * @since 2.0/5.0
1423 */
1424 public function get_report() {
1425 return $this->report;
1426 }
1427
1428 /**
1429 * Used for filtering a single API request worth of posts
1430 *
1431 * Overwritten in the Pro version
1432 *
1433 * @param array $post_set a single set of post data from the api
1434 *
1435 * @return mixed|array
1436 *
1437 * @since 2.0/5.0
1438 */
1439 protected function filter_posts( $post_set, $settings = array() ) {
1440 // array_unique( $post_set, SORT_REGULAR);
1441
1442 return $post_set;
1443 }
1444
1445 protected function handle_no_posts_found( $settings = array(), $feed_types_and_terms = array() ) {
1446
1447 }
1448
1449 protected function remove_duplicate_posts() {
1450 $posts = $this->post_data;
1451 $ids_in_feed = array();
1452 $non_duplicate_posts = array();
1453 $removed = array();
1454
1455 foreach ( $posts as $post ) {
1456 $post_id = CTF_Parse::get_post_id( $post );
1457 if ( ! in_array( $post_id, $ids_in_feed, true ) ) {
1458 $ids_in_feed[] = $post_id;
1459 $non_duplicate_posts[] = $post;
1460 } else {
1461 $removed[] = $post_id;
1462 }
1463 }
1464
1465 $this->add_report( 'removed duplicates: ' . implode(', ', $removed ) );
1466 $this->set_post_data( $non_duplicate_posts );
1467 }
1468
1469 public static function parse_api_card_data( $data ) {
1470 $return = array(
1471 'twitter:title' => '',
1472 'twitter:description' => '',
1473 'twitter:image' => '',
1474 'twitter:site' => '',
1475 'twitter:card' => '',
1476
1477 );
1478
1479 if ( ! empty( $data['name'] ) ) {
1480 $return['twitter:card'] = $data['name'];
1481 }
1482
1483 if ( ! empty( $data['url'] ) ) {
1484 $return['twitter:site'] = $data['url'];
1485 }
1486
1487 if ( ! empty( $data['binding_values']['title']['string_value'] ) ) {
1488 $return['twitter:title'] = $data['binding_values']['title']['string_value'];
1489 }
1490
1491 if ( ! empty( $data['binding_values']['description']['string_value'] ) ) {
1492 $return['twitter:description'] = $data['binding_values']['description']['string_value'];
1493 }
1494 if ( ! empty( $data['binding_values']['thumbnail_image_large']['image_value']['url'] ) ) {
1495 $return['twitter:image'] = $data['binding_values']['thumbnail_image_large']['image_value']['url'];
1496 } elseif ( ! empty( $data['binding_values']['thumbnail_image']['image_value']['url'] ) ) {
1497 $return['twitter:image'] = $data['binding_values']['thumbnail_image']['image_value']['url'];
1498 } elseif ( ! empty( $data['binding_values']['thumbnail_image_small']['image_value']['url'] ) ) {
1499 $return['twitter:image'] = $data['binding_values']['thumbnail_image_small']['image_value']['url'];
1500 }
1501
1502 return $return;
1503 }
1504
1505 }
1506