WP RSS Aggregator - Version 4.20

Version Description

(2022-01-18) = Added - New option to use feed item GUIDs instead of permalinks to detect duplicate items.

Changed - Small performance improvement when importing feed items.

Fixed - A warning about get_headers() only working with URLs. - A warning about iteration over a non-array value. - An AJAX XSS vulnerability on the Feed Sources page. Thanks WPScan!

Download this release

Release Info

Developer Mekku
Plugin Icon 128x128 WP RSS Aggregator
Version 4.20
Comparing to
See all releases

Code changes from version 4.19.3 to 4.20

CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
  and this project adheres to [Semantic Versioning](http://semver.org/).
6
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  ## [4.19.3] - 2021-11-24
8
  ### Fixed
9
  * An error during cron schedule filtering.
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
  and this project adheres to [Semantic Versioning](http://semver.org/).
6
 
7
+ ## [4.20] - 2022-01-18
8
+ ### Added
9
+ * New option to use feed item GUIDs instead of permalinks to detect duplicate items.
10
+
11
+ ### Changed
12
+ * Small performance improvement when importing feed items.
13
+
14
+ ### Fixed
15
+ * A warning about `get_headers()` only working with URLs.
16
+ * A warning about iteration over a non-array value.
17
+ * An AJAX XSS vulnerability on the Feed Sources page. Thanks WPScan!
18
+
19
  ## [4.19.3] - 2021-11-24
20
  ### Fixed
21
  * An error during cron schedule filtering.
includes/Aventura/Wprss/Core/Licensing/Settings.php CHANGED
@@ -349,11 +349,13 @@ class Settings {
349
  );
350
 
351
  $license = $manager->getLicense($addonId);
 
 
352
 
353
- if ($license !== null && !$license->isInvalid() && ($licenseKey = $license->getKey()) && !empty($licenseKey)) {
354
  if (!is_object($data)) {
355
  printf(
356
- '<p><small>%</small></p>',
357
  __(
358
  'Failed to get license information. This is a temporary problem. Check your internet connection and try again later.',
359
  'wprss'
349
  );
350
 
351
  $license = $manager->getLicense($addonId);
352
+ $licenseValid = $license !== null && $license->isValid();
353
+ $licenseKey = $license !== null ? $license->getKey() : null;
354
 
355
+ if ($licenseValid && !empty($licenseKey)) {
356
  if (!is_object($data)) {
357
  printf(
358
+ '<p><small>%s</small></p>',
359
  __(
360
  'Failed to get license information. This is a temporary problem. Check your internet connection and try again later.',
361
  'wprss'
includes/admin-display.php CHANGED
@@ -483,27 +483,25 @@ function wprss_fetch_feeds_action_hook()
483
  {
484
  $response = wprss()->createAjaxResponse();
485
  $wprss = wprss();
486
- $kFeedSourceId = 'feed_source_id';
487
- try {
488
- $kId = 'id';
489
- if (!isset($_POST[$kId]) || empty($_POST[$kId])) {
490
- throw new Exception($wprss->__('Could not schedule fetch: source ID must be specified'));
491
- }
492
- $id = $_POST['id'];
493
- $response->setAjaxData($kFeedSourceId, $id);
494
 
 
495
  if (!current_user_can('edit_feed_sources')) {
496
- throw new Exception($wprss->__([
497
- 'Could not schedule fetch for source #%1$s: user must have sufficient privileges',
498
- $id,
499
- ]));
500
  }
501
 
502
  // Verify admin referer
503
  if (!wprss_verify_nonce('wprss_feed_source_action', 'wprss_admin_ajax_nonce')) {
504
- throw new Exception($wprss->__(['Could not schedule fetch for source #%1$s: nonce is expired', $id]));
505
  }
506
 
 
 
 
 
 
 
 
507
  update_post_meta($id, 'wprss_force_next_fetch', '1');
508
 
509
  // Prepare the schedule args
@@ -534,7 +532,7 @@ function wprss_fetch_feeds_action_hook()
534
  } catch (Exception $e) {
535
  $response = wprss()->createAjaxErrorResponse($e);
536
  if (isset($id)) {
537
- $response->setAjaxData($kFeedSourceId, $id);
538
  }
539
  echo $response->getBody();
540
  exit();
483
  {
484
  $response = wprss()->createAjaxResponse();
485
  $wprss = wprss();
486
+ $feedIdKey = 'feed_source_id';
 
 
 
 
 
 
 
487
 
488
+ try {
489
  if (!current_user_can('edit_feed_sources')) {
490
+ throw new Exception(__('Could not schedule fetch for feed source: user must have sufficient privileges.'));
 
 
 
491
  }
492
 
493
  // Verify admin referer
494
  if (!wprss_verify_nonce('wprss_feed_source_action', 'wprss_admin_ajax_nonce')) {
495
+ throw new Exception(__('Could not schedule fetch for feed source: nonce is invalid.', 'wprss'));
496
  }
497
 
498
+ $id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
499
+ if (!$id) {
500
+ throw new Exception($wprss->__('Could not schedule fetch: feed source ID is invalid or was not specified'));
501
+ }
502
+ $response->setAjaxData($feedIdKey, $id);
503
+
504
+
505
  update_post_meta($id, 'wprss_force_next_fetch', '1');
506
 
507
  // Prepare the schedule args
532
  } catch (Exception $e) {
533
  $response = wprss()->createAjaxErrorResponse($e);
534
  if (isset($id)) {
535
+ $response->setAjaxData($feedIdKey, $id);
536
  }
537
  echo $response->getBody();
538
  exit();
includes/admin-help-metaboxes.php CHANGED
@@ -56,6 +56,13 @@ add_action('plugins_loaded', function () {
56
  'wprss'
57
  ),
58
 
 
 
 
 
 
 
 
59
  'wprss_import_source' => __(
60
  'Tick this box to get the site name and URL from the RSS feed, for each item individually.' .
61
  "\n\n" .
56
  'wprss'
57
  ),
58
 
59
+ 'wprss_use_guids' => __(
60
+ 'Enable this option to identify duplicate feed items by their GUIDs, rather than by their permalink.' .
61
+ "\n\n" .
62
+ 'This can be useful when the feed items share the same permalink, and so not all feed items would get imported.',
63
+ 'wprss'
64
+ ),
65
+
66
  'wprss_import_source' => __(
67
  'Tick this box to get the site name and URL from the RSS feed, for each item individually.' .
68
  "\n\n" .
includes/admin-metaboxes.php CHANGED
@@ -122,6 +122,12 @@ function wprss_get_custom_fields()
122
  'type' => 'checkbox',
123
  ];
124
 
 
 
 
 
 
 
125
  // for extensibility, allows more meta fields to be added
126
  return apply_filters('wprss_fields', $wprss_meta_fields);
127
  }
122
  'type' => 'checkbox',
123
  ];
124
 
125
+ $wprss_meta_fields['use_guids'] = [
126
+ 'label' => __('Use GUIDs', 'wprss'),
127
+ 'id' => $prefix . 'use_guids',
128
+ 'type' => 'checkbox',
129
+ ];
130
+
131
  // for extensibility, allows more meta fields to be added
132
  return apply_filters('wprss_fields', $wprss_meta_fields);
133
  }
includes/feed-importing-images.php CHANGED
@@ -464,7 +464,10 @@ function wpra_get_item_enclosure_images($item)
464
  continue;
465
  }
466
 
467
- foreach ($enclosure->get_thumbnails() as $thumbnail) {
 
 
 
468
  if (empty($thumbnail)) {
469
  continue;
470
  }
@@ -1036,7 +1039,8 @@ function wpra_image_feature_enabled($feature)
1036
  */
1037
  function wpra_is_url_an_image($url)
1038
  {
1039
- $headers = get_headers($url, true);
 
1040
  $headers = array_change_key_case($headers, CASE_LOWER);
1041
 
1042
  if (empty($headers['content-type'])) {
464
  continue;
465
  }
466
 
467
+ $thumbnails = $enclosure->get_thumbnails();
468
+ $thumbnails = is_array($thumbnails) ? $thumbnails : [];
469
+
470
+ foreach ($thumbnails as $thumbnail) {
471
  if (empty($thumbnail)) {
472
  continue;
473
  }
1039
  */
1040
  function wpra_is_url_an_image($url)
1041
  {
1042
+ $headers = @get_headers($url, true);
1043
+ $headers = is_array($headers) ? $headers : [];
1044
  $headers = array_change_key_case($headers, CASE_LOWER);
1045
 
1046
  if (empty($headers['content-type'])) {
includes/feed-importing.php CHANGED
@@ -33,8 +33,10 @@ function wprss_fetch_insert_single_feed_items( $feed_ID ) {
33
  register_shutdown_function('wprss_detect_exec_timeout');
34
 
35
  $importFn = function ($feed_ID) {
 
 
36
  $logger = wpra_get_logger($feed_ID);
37
- $logger->info('Starting import of feed {id}', ['id' => $feed_ID]);
38
 
39
  $t = microtime(true);
40
 
@@ -106,13 +108,18 @@ function wprss_fetch_insert_single_feed_items( $feed_ID ) {
106
  // The import process will check not only the titles in the DB but the titles currently in the feed
107
  $existing_titles = [];
108
 
109
- // Gather the permalinks of existing feed item's related to this feed source
110
- $existing_permalinks = wprss_get_existing_permalinks( $feed_ID );
 
 
 
 
111
 
112
  // Generate a list of items fetched, that are not already in the DB
113
  $new_items = array();
114
  foreach ( $items_to_insert as $item ) {
115
  $item_title = $item->get_title();
 
116
  $permalink = $item->get_permalink();
117
  $permalink = wprss_normalize_permalink( $permalink, $item, $feed_ID );
118
 
@@ -124,7 +131,8 @@ function wprss_fetch_insert_single_feed_items( $feed_ID ) {
124
  }
125
 
126
  // Check if already imported
127
- if (array_key_exists($permalink, $existing_permalinks)) {
 
128
  $logger->debug('Item "{0}" already exists in the database', [$item_title]);
129
 
130
  continue;
@@ -145,6 +153,7 @@ function wprss_fetch_insert_single_feed_items( $feed_ID ) {
145
  }
146
  }
147
 
 
148
  $new_items[] = $item;
149
  }
150
 
@@ -572,17 +581,12 @@ function wprss_items_insert_post( $items, $feed_ID ) {
572
  update_post_meta( $feed_ID, 'wprss_feed_is_updating', $update_started_at = time() );
573
  $logger = wpra_get_logger($feed_ID);
574
 
575
- // Gather the permalinks of existing feed item's related to this feed source
576
- $existing_permalinks = wprss_get_existing_permalinks( $feed_ID );
577
-
578
  // Count of items inserted
579
  $items_inserted = 0;
580
 
581
  foreach ( $items as $i => $item ) {
582
-
583
- // Normalize the URL
584
- $permalink = $item->get_permalink(); // Link or enclosure URL
585
- $permalink = wprss_normalize_permalink( $permalink, $item, $feed_ID );
586
 
587
  $logger->debug('Beginning import for item "{0}"', [$item->get_title()]);
588
 
@@ -594,171 +598,164 @@ function wprss_items_insert_post( $items, $feed_ID ) {
594
  $enclosure_url = $enclosure->get_link();
595
  }
596
 
597
- // Check if newly fetched item already present in existing feed items,
598
- // if not insert it into wp_posts and insert post meta.
599
- if ( ! ( array_key_exists( $permalink, $existing_permalinks ) ) ) {
600
- // Extend the importing time and refresh the feed's updating flag to reflect that it is active
601
- $time_limit = wprss_get_item_import_time_limit();
602
- set_time_limit( $time_limit );
603
 
604
- global $wp_filter;
605
- if (isset($wp_filter['wprss_insert_post_item_conditionals'])) {
606
- $hook = $wp_filter['wprss_insert_post_item_conditionals'];
607
 
608
- if (count($hook->callbacks) > 0) {
609
- $logger->debug('Hooks for `wprss_insert_post_item_conditionals`:');
610
- }
611
 
612
- foreach ($hook->callbacks as $list) {
613
- foreach ($list as $callback) {
614
- $logger->debug('-> {0}', [wprss_format_hook_callback($callback)]);
615
- }
616
  }
617
  }
 
618
 
619
- $logger->debug('Checking conditionals ...');
620
 
621
- // Apply filters that determine if the feed item should be inserted into the DB or not.
622
- $ogItem = $item;
623
- $item = apply_filters( 'wprss_insert_post_item_conditionals', $item, $feed_ID, $permalink );
624
- /* @var $item SimplePie_Item */
625
 
626
- // Check if the imported count should still be updated, even if the item is NULL
627
- $still_update_count = apply_filters( 'wprss_still_update_import_count', FALSE );
628
 
629
- // If the item is not NULL, continue to inserting the feed item post into the DB
630
- if ( $item !== NULL && !is_bool($item) ) {
631
- $logger->debug('Resuming insertion into DB');
632
 
633
- $post_status = 'publish';
634
 
635
- // Get the date and GMT date and normalize if not valid or not given by the feed
636
- $format = 'Y-m-d H:i:s';
637
- $timestamp = $item->get_date( 'U' );
638
- $has_date = $timestamp ? true : false;
639
 
640
- if ($has_date) {
641
- $logger->debug('Feed item "{0}" date: {1}', [$item->get_title(), $item->get_date($format)]);
642
 
643
- if ($timestamp > time()) {
644
- // Item has a future timestamp ...
645
- $logger->debug('Item "{0}" has a future date', [$item->get_title()]);
646
 
647
- $schedule_items_filter = apply_filters('wpra/importer/allow_scheduled_items', false);
648
- $schedule_items_option = wprss_get_general_setting('schedule_future_items');
649
 
650
- if ($schedule_items_filter || $schedule_items_option) {
651
- // If can schedule future items, set the post status to "future" (aka scheduled)
652
- $post_status = 'future';
653
 
654
- $logger->debug('Setting future status');
655
- } else {
656
- // If cannot schedule future items, clamp the timestamp to the current time minus
657
- // 1 second for each iteration done so far
658
- $timestamp = min(time() - $i, $timestamp);
659
 
660
- $logger->debug('Date was clamped to present time');
661
- }
662
  }
663
- } else {
664
- // Item has no date ...
665
- $logger->debug('Item "{0}" has no date. Using current time', [$item->get_title()]);
666
- $timestamp = time();
667
- }
668
-
669
- $date = date( $format, $timestamp );
670
- $date_gmt = gmdate( $format, $item->get_gmdate( 'U' ) );
671
-
672
- $logger->debug('Date for "{0}" will be {1}', [$item->get_title(), $date]);
673
-
674
- // Do not let WordPress sanitize the excerpt
675
- // WordPress sanitizes the excerpt because it's expected to be typed by a user and sent in a POST
676
- // request. However, our excerpt is being inserted as a raw string with custom sanitization.
677
- remove_all_filters( 'excerpt_save_pre' );
678
-
679
- $title = trim(html_entity_decode($item->get_title()));
680
- $title = empty($title) ? $item->get_id() : $title;
681
-
682
- // Prepare the item data
683
- $feed_item = apply_filters(
684
- 'wprss_populate_post_data',
685
- array(
686
- 'post_title' => $title,
687
- 'post_content' => $item->get_content(),
688
- 'post_excerpt' => wprss_sanitize_excerpt($item->get_description()),
689
- 'post_status' => $post_status,
690
- 'post_type' => 'wprss_feed_item',
691
- 'post_date' => $date,
692
- 'post_date_gmt' => $date_gmt
693
- ),
694
- $item
695
- );
696
-
697
- if ( defined('ICL_SITEPRESS_VERSION') )
698
- @include_once( WP_PLUGIN_DIR . '/sitepress-multilingual-cms/inc/wpml-api.php' );
699
- if ( defined('ICL_LANGUAGE_CODE') ) {
700
- $_POST['icl_post_language'] = $language_code = ICL_LANGUAGE_CODE;
701
  }
 
 
 
 
 
702
 
703
- // Create and insert post object into the DB
704
- $inserted_ID = wp_insert_post( $feed_item );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
705
 
706
- if ( !is_wp_error( $inserted_ID ) ) {
 
 
 
 
707
 
708
- if ( is_object( $inserted_ID ) ) {
709
- if ( isset( $inserted_ID['ID'] ) ) {
710
- $inserted_ID = $inserted_ID['ID'];
711
- }
712
- elseif ( isset( $inserted_ID->ID ) ) {
713
- $inserted_ID = $inserted_ID->ID;
714
- }
715
- }
716
 
717
- $logger->debug('Item "{0}" was inserted into DB, ID: {1}', [
718
- $ogItem->get_title(),
719
- $inserted_ID,
720
- ]);
721
 
722
- // Create and insert post meta into the DB
723
- wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink );
 
 
 
 
 
 
724
 
725
- $logger->debug('Inserted meta data for item #{0}', [
726
- $inserted_ID,
727
- ]);
 
728
 
729
- // Remember newly added permalink
730
- $existing_permalinks[$permalink] = 1;
731
 
732
- // Increment the inserted items counter
733
- $items_inserted++;
 
734
 
735
- $logger->notice('Finished import for item {0}, ID {1}', [
736
- $ogItem->get_title(),
737
- $inserted_ID,
738
- ]);
739
- }
740
- else {
741
- update_post_meta( $feed_ID, 'wprss_error_last_import', 'An error occurred while inserting a feed item into the database.' );
742
 
743
- $logger->error('Failed to save item "{0}" into the database', [$item->get_title()]);
744
- }
745
- }
746
- // If the item is TRUE, then a hook function in the filter inserted the item.
747
- // increment the inserted counter
748
- elseif ( ( is_bool($item) && $item === TRUE ) || ( $still_update_count === TRUE && $item !== FALSE ) ) {
749
- $logger->debug('Item "{0}" was imported by an add-on or filter', [
750
  $ogItem->get_title(),
751
- ]);
752
- $items_inserted++;
753
- } elseif (has_filter('wprss_insert_post_item_conditionals', 'wprss_kf_check_post_item_keywords')) {
754
- $logger->info('Item "{0}" was rejected by your keyword or tag filtering.', [
755
- $ogItem->get_title()
756
- ]);
757
- } else {
758
- $logger->notice('Item "{0}" was rejected by an add-on or filter.', [
759
- $ogItem->get_title()
760
  ]);
761
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
762
  }
763
  }
764
 
@@ -777,8 +774,9 @@ function wprss_items_insert_post( $items, $feed_ID ) {
777
  * @param string $permalink The item's permalink.
778
  */
779
  function wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink ) {
780
- update_post_meta( $inserted_ID, 'wprss_item_date', $item->get_date(DATE_ISO8601) );
781
- update_post_meta( $inserted_ID, 'wprss_item_permalink', $permalink );
 
782
 
783
  $enclosure = $item->get_enclosure(0);
784
  $enclosureUrl = $enclosure ? $enclosure->get_link() : null;
33
  register_shutdown_function('wprss_detect_exec_timeout');
34
 
35
  $importFn = function ($feed_ID) {
36
+ $feedName = get_the_title($feed_ID);
37
+
38
  $logger = wpra_get_logger($feed_ID);
39
+ $logger->info('Starting import for "{0}"', [$feedName]);
40
 
41
  $t = microtime(true);
42
 
108
  // The import process will check not only the titles in the DB but the titles currently in the feed
109
  $existing_titles = [];
110
 
111
+ // Gather the existing feed item IDs for this feed source
112
+ $useGuids = get_post_meta($feed_ID, 'wprss_use_guids', true);
113
+ $useGuids = filter_var($useGuids, FILTER_VALIDATE_BOOLEAN);
114
+ $existingIds = $useGuids
115
+ ? wprss_get_existing_guids($feed_ID)
116
+ : wprss_get_existing_permalinks($feed_ID);
117
 
118
  // Generate a list of items fetched, that are not already in the DB
119
  $new_items = array();
120
  foreach ( $items_to_insert as $item ) {
121
  $item_title = $item->get_title();
122
+ $guid = $item->get_id();
123
  $permalink = $item->get_permalink();
124
  $permalink = wprss_normalize_permalink( $permalink, $item, $feed_ID );
125
 
131
  }
132
 
133
  // Check if already imported
134
+ $idToCheck = $useGuids ? $guid : $permalink;
135
+ if (array_key_exists($idToCheck, $existingIds)) {
136
  $logger->debug('Item "{0}" already exists in the database', [$item_title]);
137
 
138
  continue;
153
  }
154
  }
155
 
156
+ $existingIds[$idToCheck] = 1;
157
  $new_items[] = $item;
158
  }
159
 
581
  update_post_meta( $feed_ID, 'wprss_feed_is_updating', $update_started_at = time() );
582
  $logger = wpra_get_logger($feed_ID);
583
 
 
 
 
584
  // Count of items inserted
585
  $items_inserted = 0;
586
 
587
  foreach ( $items as $i => $item ) {
588
+ $permalink = $item->get_permalink();
589
+ $permalink = wprss_normalize_permalink($permalink, $item, $feed_ID);
 
 
590
 
591
  $logger->debug('Beginning import for item "{0}"', [$item->get_title()]);
592
 
598
  $enclosure_url = $enclosure->get_link();
599
  }
600
 
601
+ // Extend the importing time and refresh the feed's updating flag to reflect that it is active
602
+ $time_limit = wprss_get_item_import_time_limit();
603
+ set_time_limit( $time_limit );
 
 
 
604
 
605
+ global $wp_filter;
606
+ if (isset($wp_filter['wprss_insert_post_item_conditionals'])) {
607
+ $hook = $wp_filter['wprss_insert_post_item_conditionals'];
608
 
609
+ if (count($hook->callbacks) > 0) {
610
+ $logger->debug('Hooks for `wprss_insert_post_item_conditionals`:');
611
+ }
612
 
613
+ foreach ($hook->callbacks as $list) {
614
+ foreach ($list as $callback) {
615
+ $logger->debug('-> {0}', [wprss_format_hook_callback($callback)]);
 
616
  }
617
  }
618
+ }
619
 
620
+ $logger->debug('Checking conditionals ...');
621
 
622
+ // Apply filters that determine if the feed item should be inserted into the DB or not.
623
+ $ogItem = $item;
624
+ $item = apply_filters( 'wprss_insert_post_item_conditionals', $item, $feed_ID, $permalink );
625
+ /* @var $item SimplePie_Item */
626
 
627
+ // Check if the imported count should still be updated, even if the item is NULL
628
+ $still_update_count = apply_filters( 'wprss_still_update_import_count', FALSE );
629
 
630
+ // If the item is not NULL, continue to inserting the feed item post into the DB
631
+ if ( $item !== NULL && !is_bool($item) ) {
632
+ $logger->debug('Resuming insertion into DB');
633
 
634
+ $post_status = 'publish';
635
 
636
+ // Get the date and GMT date and normalize if not valid or not given by the feed
637
+ $format = 'Y-m-d H:i:s';
638
+ $timestamp = $item->get_date( 'U' );
639
+ $has_date = $timestamp ? true : false;
640
 
641
+ if ($has_date) {
642
+ $logger->debug('Feed item "{0}" date: {1}', [$item->get_title(), $item->get_date($format)]);
643
 
644
+ if ($timestamp > time()) {
645
+ // Item has a future timestamp ...
646
+ $logger->debug('Item "{0}" has a future date', [$item->get_title()]);
647
 
648
+ $schedule_items_filter = apply_filters('wpra/importer/allow_scheduled_items', false);
649
+ $schedule_items_option = wprss_get_general_setting('schedule_future_items');
650
 
651
+ if ($schedule_items_filter || $schedule_items_option) {
652
+ // If can schedule future items, set the post status to "future" (aka scheduled)
653
+ $post_status = 'future';
654
 
655
+ $logger->debug('Setting future status');
656
+ } else {
657
+ // If cannot schedule future items, clamp the timestamp to the current time minus
658
+ // 1 second for each iteration done so far
659
+ $timestamp = min(time() - $i, $timestamp);
660
 
661
+ $logger->debug('Date was clamped to present time');
 
662
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
  }
664
+ } else {
665
+ // Item has no date ...
666
+ $logger->debug('Item "{0}" has no date. Using current time', [$item->get_title()]);
667
+ $timestamp = time();
668
+ }
669
 
670
+ $date = date( $format, $timestamp );
671
+ $date_gmt = gmdate( $format, $item->get_gmdate( 'U' ) );
672
+
673
+ $logger->debug('Date for "{0}" will be {1}', [$item->get_title(), $date]);
674
+
675
+ // Do not let WordPress sanitize the excerpt
676
+ // WordPress sanitizes the excerpt because it's expected to be typed by a user and sent in a POST
677
+ // request. However, our excerpt is being inserted as a raw string with custom sanitization.
678
+ remove_all_filters( 'excerpt_save_pre' );
679
+
680
+ $title = trim(html_entity_decode($item->get_title()));
681
+ $title = empty($title) ? $item->get_id() : $title;
682
+
683
+ // Prepare the item data
684
+ $feed_item = apply_filters(
685
+ 'wprss_populate_post_data',
686
+ array(
687
+ 'post_title' => $title,
688
+ 'post_content' => $item->get_content(),
689
+ 'post_excerpt' => wprss_sanitize_excerpt($item->get_description()),
690
+ 'post_status' => $post_status,
691
+ 'post_type' => 'wprss_feed_item',
692
+ 'post_date' => $date,
693
+ 'post_date_gmt' => $date_gmt
694
+ ),
695
+ $item
696
+ );
697
 
698
+ if ( defined('ICL_SITEPRESS_VERSION') )
699
+ @include_once( WP_PLUGIN_DIR . '/sitepress-multilingual-cms/inc/wpml-api.php' );
700
+ if ( defined('ICL_LANGUAGE_CODE') ) {
701
+ $_POST['icl_post_language'] = $language_code = ICL_LANGUAGE_CODE;
702
+ }
703
 
704
+ // Create and insert post object into the DB
705
+ $inserted_ID = wp_insert_post( $feed_item );
 
 
 
 
 
 
706
 
707
+ if ( !is_wp_error( $inserted_ID ) ) {
 
 
 
708
 
709
+ if ( is_object( $inserted_ID ) ) {
710
+ if ( isset( $inserted_ID['ID'] ) ) {
711
+ $inserted_ID = $inserted_ID['ID'];
712
+ }
713
+ elseif ( isset( $inserted_ID->ID ) ) {
714
+ $inserted_ID = $inserted_ID->ID;
715
+ }
716
+ }
717
 
718
+ $logger->debug('Item "{0}" was inserted into DB, ID: {1}', [
719
+ $ogItem->get_title(),
720
+ $inserted_ID,
721
+ ]);
722
 
723
+ // Create and insert post meta into the DB
724
+ wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink );
725
 
726
+ $logger->debug('Inserted meta data for item #{0}', [
727
+ $inserted_ID,
728
+ ]);
729
 
730
+ // Increment the inserted items counter
731
+ $items_inserted++;
 
 
 
 
 
732
 
733
+ $logger->notice('Finished import for item {0}, ID {1}', [
 
 
 
 
 
 
734
  $ogItem->get_title(),
735
+ $inserted_ID,
 
 
 
 
 
 
 
 
736
  ]);
737
  }
738
+ else {
739
+ update_post_meta( $feed_ID, 'wprss_error_last_import', 'An error occurred while inserting a feed item into the database.' );
740
+
741
+ $logger->error('Failed to save item "{0}" into the database', [$item->get_title()]);
742
+ }
743
+ }
744
+ // If the item is TRUE, then a hook function in the filter inserted the item.
745
+ // increment the inserted counter
746
+ elseif ( ( is_bool($item) && $item === TRUE ) || ( $still_update_count === TRUE && $item !== FALSE ) ) {
747
+ $logger->debug('Item "{0}" was imported by an add-on or filter', [
748
+ $ogItem->get_title(),
749
+ ]);
750
+ $items_inserted++;
751
+ } elseif (has_filter('wprss_insert_post_item_conditionals', 'wprss_kf_check_post_item_keywords')) {
752
+ $logger->info('Item "{0}" was rejected by your keyword or tag filtering.', [
753
+ $ogItem->get_title()
754
+ ]);
755
+ } else {
756
+ $logger->notice('Item "{0}" was rejected by an add-on or filter.', [
757
+ $ogItem->get_title()
758
+ ]);
759
  }
760
  }
761
 
774
  * @param string $permalink The item's permalink.
775
  */
776
  function wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink ) {
777
+ update_post_meta($inserted_ID, 'wprss_item_guid', $item->get_id());
778
+ update_post_meta($inserted_ID, 'wprss_item_permalink', $permalink );
779
+ update_post_meta($inserted_ID, 'wprss_item_date', $item->get_date(DATE_ISO8601));
780
 
781
  $enclosure = $item->get_enclosure(0);
782
  $enclosureUrl = $enclosure ? $enclosure->get_link() : null;
includes/feed-processing.php CHANGED
@@ -1,806 +1,766 @@
1
  <?php
2
 
3
- define( 'WPRSS_TRANSIENT_NAME_IS_REIMPORTING', 'is_reimporting' );
4
-
5
- /**
6
- * Feed processing related functions
7
- *
8
- * @package WPRSSAggregator
9
- */
10
-
11
- /**
12
- * Returns whether or not the feed source will forcefully fetch the next fetch,
13
- * ignoring whether or not it is paused or not.
14
- *
15
- * @param $source_id The ID of the feed soruce
16
- * @return boolean
17
- * @since 3.7
18
- */
19
- function wprss_feed_source_force_next_fetch( $source_id ) {
20
- $force = get_post_meta( $source_id, 'wprss_force_next_fetch', TRUE );
21
- return ( $force !== '' || $force == '1' );
22
- }
23
-
24
-
25
- /**
26
- * Change the default feed cache recreation period to 2 hours
27
- *
28
- * Probably not needed since we are now disabling caching altogether
29
- *
30
- * @since 2.1
31
- */
32
- function wprss_feed_cache_lifetime( $seconds ) {
33
- return 1; // one second
34
- }
35
-
36
-
37
- /**
38
- * Disable caching of feeds in transients, we don't need it as we are storing them in the wp_posts table
39
- *
40
- * @since 3.0
41
- */
42
- function wprss_do_not_cache_feeds( &$feed ) {
43
- $feed->enable_cache( false );
44
- }
45
-
46
-
47
- /**
48
- * Parameters for query to get all feed sources
49
- *
50
- * @since 3.0
51
- */
52
- function wprss_get_all_feed_sources() {
53
- // Get all feed sources
54
- $feed_sources = new WP_Query( apply_filters(
55
- 'wprss_get_all_feed_sources',
56
- array(
57
- 'post_type' => 'wprss_feed',
58
- 'post_status' => 'publish',
59
- 'cache_results' => false, // Disable caching, used for one-off queries
60
- 'no_found_rows' => true, // We don't need pagination, so disable it
61
- 'posts_per_page' => -1
62
- )
63
- ) );
64
- return $feed_sources;
65
- }
66
-
67
-
68
- /**
69
- * Retrieves the query to use for retrieving imported items.
70
- *
71
- * @since 4.17.4
72
- */
73
- function wprss_get_imported_items_query($source_id = null) {
74
- $args = [
75
- 'post_type' => array_values(get_post_types()),
76
- 'post_status' => 'any',
77
- 'cache_results' => false,
78
- 'no_found_rows' => true,
79
- 'posts_per_page' => -1,
80
- 'ignore_sticky_posts' => 'true',
81
- 'orderby' => 'date',
82
- 'order' => 'DESC',
83
- 'meta_query' => [
84
- 'relation' => 'AND',
85
- ],
86
- 'suppress_filters' => 1
 
 
 
 
 
 
 
 
 
 
87
  ];
88
-
89
- if ($source_id !== null) {
90
- $args['meta_query'][] = [
91
- 'key' => 'wprss_feed_id',
92
- 'value' => (string) $source_id,
93
- 'compare' => '=',
94
- ];
95
- } else {
96
- $args['meta_query'][] = [
97
- 'key' => 'wprss_feed_id',
98
- 'compare' => 'EXISTS',
99
- ];
100
- }
101
-
102
- return apply_filters('wprss_get_feed_items_for_source_args', $args, $source_id);
103
- }
104
-
105
- /**
106
- * Queries for imported items.
107
- *
108
- * @since 4.17.4
109
- */
110
- function wprss_get_imported_items($source_id = null) {
111
- return new WP_Query(wprss_get_imported_items_query($source_id));
112
- }
113
-
114
- /**
115
- * Returns all the feed items of a source.
116
- *
117
- * @since 3.8
118
- */
119
- function wprss_get_feed_items_for_source( $source_id ) {
120
- return wprss_get_imported_items($source_id);
121
- }
122
-
123
-
124
- /**
125
- * Parameters for query to get feed sources
126
- *
127
- * @since 3.0
128
- */
129
- function wprss_get_feed_source() {
130
- // Get all feed sources
131
- $feed_sources = new WP_Query( apply_filters(
132
- 'wprss_get_all_feed_sources',
133
- array(
134
- 'post_type' => 'wprss_feed',
135
- 'post_status' => 'publish',
136
- 'cache_results' => false, // Disable caching, used for one-off queries
137
- 'no_found_rows' => true, // We don't need pagination, so disable it
138
- 'posts_per_page' => -1
139
- )
140
- ) );
141
- return $feed_sources;
142
- }
143
-
144
-
145
- /**
146
- * Database query to get existing permalinks
147
- *
148
- * @since 3.0
149
- */
150
- function wprss_get_existing_permalinks( $feed_ID ) {
151
- global $wpdb;
152
-
153
- $cols = $wpdb->get_col(
154
- "SELECT q.`meta_value`
155
- FROM {$wpdb->postmeta} AS p
156
- JOIN {$wpdb->postmeta} AS q ON (q.`meta_key` = 'wprss_item_permalink' AND p.`post_id` = q.`post_id`)
157
- WHERE p.`meta_key` = 'wprss_feed_id' AND p.`meta_value` = '{$feed_ID}'"
158
- );
159
-
160
- return @array_flip($cols);
161
  }
162
 
163
- /**
164
- * Checks if an item title exists in the database.
165
- *
166
- * @since 4.14
167
- *
168
- * @param string $title The title to search for.
169
- *
170
- * @return bool True if the title exists, false if not.
171
- */
172
- function wprss_item_title_exists( $title ) {
173
- global $wpdb;
174
-
175
- $query = $wpdb->prepare(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  "SELECT *
177
- FROM `{$wpdb->posts}` AS p
178
- JOIN `{$wpdb->postmeta}` AS q ON p.`ID` = q.`post_id`
179
- WHERE q.`meta_key` = 'wprss_feed_id' AND p.`post_title` = %s",
180
- [html_entity_decode($title)]
181
- );
182
-
183
- $cols = $wpdb->get_col($query);
184
-
185
- return count($cols) > 0;
186
- }
187
-
188
- /**
189
- * Database query to get existing titles
190
- *
191
- * @since 4.7
192
- */
193
- function wprss_get_existing_titles( $feed_ID = NULL ) {
194
- global $wpdb;
195
-
196
- $condition = ($feed_ID !== NULL) ? "AND q.`meta_value` = '{$feed_ID}'" : '';
197
-
198
- $cols = $wpdb->get_col(
199
- "SELECT p.`post_title`
200
- FROM `{$wpdb->posts}` AS p
201
- JOIN `{$wpdb->postmeta}` AS q ON p.`ID` = q.`post_id`
202
- WHERE q.`meta_key` = 'wprss_feed_id' $condition"
203
- );
204
-
205
- return @array_flip($cols);
206
- }
207
-
208
-
209
- /**
210
- * Checks if a permalink exists.
211
- *
212
- * Untested!
213
- *
214
- * @param string $permalink The permalink, expected to be normalized.
215
- * @return bool
216
- */
217
- function wprss_permalink_exists( $permalink ) {
218
- global $wpdb;
219
-
220
- $wpdb->query(
221
- $wpdb->prepare(
222
- "SELECT *
223
- FROM {$wpdb->postmeta}
224
- WHERE `meta_value` = '{$permalink}'"
225
- )
226
- );
227
-
228
- return $wpdb->num_rows > 0;
229
- }
230
-
231
-
232
- add_action( 'publish_wprss_feed', 'wprss_fetch_insert_feed_items', 10 );
233
- /**
234
- * Fetches feed items from source provided and inserts into db
235
- *
236
- * This function is used when inserting or untrashing a new feed source, it only gets feeds from that particular source
237
- *
238
- * @since 3.0
239
- */
240
- function wprss_fetch_insert_feed_items( $post_id ) {
241
- wp_schedule_single_event( time(), 'wprss_fetch_single_feed_hook', array( $post_id ) );
242
- }
243
-
244
-
245
- /**
246
- * Returns the image of the feed.
247
- * The reason this function exists is for add-ons to be able to detect if the plugin core
248
- * supports feed image functionality through a simple function_exists() call.
249
- *
250
- * @param $source_id The ID of the feed source
251
- * @return string The link to the feed image
252
- * @since 1.0
253
- */
254
- function wprss_get_feed_image( $source_id ) {
255
- return get_post_meta( $source_id, 'wprss_feed_image', true );
256
- }
257
-
258
-
259
- add_action( 'post_updated', 'wprss_updated_feed_source', 10, 3 );
260
- /**
261
- * This function is triggered just after a post is updated.
262
- * It checks if the updated post is a feed source, and carries out any
263
- * updating necassary.
264
- *
265
- * @since 3.3
266
- */
267
- function wprss_updated_feed_source( $post_ID, $post_after, $post_before ) {
268
- // Check if the post is a feed source and is published
269
-
270
- if ( ( $post_after->post_type == 'wprss_feed' ) && ( $post_after->post_status == 'publish' ) ) {
271
-
272
- if ( isset( $_POST['wprss_url'] ) && !empty( $_POST['wprss_url'] ) ) {
273
- $url = $_POST['wprss_url'];
274
- $feed = wprss_fetch_feed( $url );
275
- if ( $feed !== NULL && !is_wp_error( $feed ) ) {
276
- update_post_meta( $post_ID, 'wprss_site_url', $feed->get_permalink() );
277
- update_post_meta( $post_ID, 'wprss_feed_image', $feed->get_image_url() );
278
- }
279
  }
280
-
281
-
282
- if ( isset( $_POST['wprss_limit'] ) && !empty( $_POST['wprss_limit'] ) ) {
283
- // Checking feed limit change
284
- // Get the limit currently saved in db, and limit in POST request
285
- //$limit = get_post_meta( $post_ID, 'wprss_limit', true );
286
- $limit = $_POST['wprss_limit'];
287
- // Get all feed items for this source
288
- $feed_sources = new WP_Query(
289
- array(
290
- 'post_type' => 'wprss_feed_item',
291
- 'post_status' => 'publish',
292
- 'cache_results' => false, // Disable caching, used for one-off queries
293
- 'no_found_rows' => true, // We don't need pagination, so disable it
294
- 'posts_per_page' => -1,
295
- 'orderby' => 'date',
296
- 'order' => 'ASC',
297
- 'meta_query' => array(
298
- array(
299
- 'key' => 'wprss_feed_id',
300
- 'value' => $post_ID,
301
- 'compare' => 'LIKE'
302
- )
303
- )
304
- )
305
- );
306
- // If the limit is smaller than the number of found posts, delete the feed items
307
- // and re-import, to ensure that most recent feed items are present.
308
- $difference = intval( $feed_sources->post_count ) - intval( $limit );
309
- if ( $difference > 0 ) {
310
- // Loop and delete the excess feed items
311
- while ( $feed_sources->have_posts() && $difference > 0 ) {
312
- $feed_sources->the_post();
313
- wp_delete_post( get_the_ID(), true );
314
- $difference--;
315
- }
316
- }
317
- }
318
- }
319
- }
320
-
321
-
322
- add_action( 'added_post_meta', 'wprss_update_feed_meta', 10, 4 );
323
- add_action( 'updated_post_meta', 'wprss_update_feed_meta', 10, 4 );
324
- /**
325
- * This function is run whenever a post is saved or updated.
326
- *
327
- * @since 3.4
328
- */
329
- function wprss_update_feed_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
330
- $post = get_post( $post_id );
331
- if ( $post !== NULL && $post->post_status === 'publish' && $post->post_type === 'wprss_feed' ) {
332
- if ( $meta_key === 'wprss_url' )
333
- wprss_change_fb_url( $post_id, $meta_value );
334
  }
335
- }
336
-
337
 
338
- function wprss_change_fb_url( $post_id, $url ) {
339
- # Check if url begins with a known facebook hostname.
340
- if ( stripos( $url, 'http://facebook.com' ) === 0
341
- || stripos( $url, 'http://www.facebook.com' ) === 0
342
- || stripos( $url, 'https://facebook.com' ) === 0
343
- || stripos( $url, 'https://www.facebook.com' ) === 0
344
- ) {
345
- # Generate the new URL to FB Graph
346
- $com_index = stripos( $url, '.com' );
347
- $fb_page = substr( $url, $com_index + 4 ); # 4 = length of ".com"
348
- $fb_graph_url = 'https://graph.facebook.com' . $fb_page;
349
- # Contact FB Graph and get data
350
- $response = wp_remote_get( $fb_graph_url );
351
- # If the repsonse successful and has a body
352
- if ( !is_wp_error( $response ) && isset( $response['body'] ) ) {
353
- # Parse the body as a JSON string
354
- $json = json_decode( $response['body'], true );
355
- # If an id is present ...
356
- if ( isset( $json['id'] ) ) {
357
- # Generate the final URL for this feed and update the post meta
358
- $final_url = "https://www.facebook.com/feeds/page.php?format=rss20&id=" . $json['id'];
359
- update_post_meta( $post_id, 'wprss_url', $final_url, $url );
 
 
 
 
 
 
 
 
 
 
 
360
  }
361
  }
362
  }
363
  }
364
-
365
-
366
- add_action( 'trash_wprss_feed', 'wprss_delete_feed_items' ); // maybe use wp_trash_post action? wp_trash_wprss_feed
367
- /**
368
- * Delete feed items on trashing of corresponding feed source
369
- *
370
- * @since 2.0
371
- */
372
- function wprss_delete_feed_items ($source_id) {
373
- $force_delete = apply_filters('wprss_force_delete_when_by_source', true);
374
-
375
- // WPML fix: removes the current language from the query WHERE and JOIN clauses
376
- global $sitepress;
377
- if ($sitepress !== null) {
378
- remove_filter('posts_join', [$sitepress,'posts_join_filter']);
379
- remove_filter('posts_where', [$sitepress,'posts_where_filter']);
380
- }
381
-
382
- $args = wprss_get_imported_items_query($source_id);
383
- $items = get_posts($args);
384
-
385
- foreach ($items as $item) {
386
- wp_delete_post($item->ID, $force_delete);
387
- }
388
- }
389
-
390
-
391
- add_action( 'wprss_delete_all_feed_items_hook', 'wprss_delete_all_feed_items' );
392
- /**
393
- * Delete all feed items
394
- *
395
- * @since 3.0
396
- */
397
- function wprss_delete_all_feed_items() {
398
- $args = wprss_get_imported_items_query();
399
- $items = get_posts($args);
400
-
401
- foreach ($items as $item) {
402
- wp_delete_post($item->ID, true);
403
- }
404
- }
405
-
406
-
407
- /**
408
- * Marks the feed source as 'updating' (importing).
409
- *
410
- * @since 4.6.6
411
- * @return int The time value set in the 'updating' meta field
412
- */
413
- function wprss_flag_feed_as_updating( $feed_ID ) {
414
- update_post_meta( $feed_ID, 'wprss_feed_is_updating', $start_time = time() );
415
- return $start_time;
416
- }
417
-
418
- /**
419
- * Marks the feed source as 'idle' (not importing).
420
- *
421
- * @since 4.6.6
422
- */
423
- function wprss_flag_feed_as_idle( $feed_ID ) {
424
- update_post_meta( $feed_ID, 'wprss_feed_is_updating', '' );
425
- }
426
-
427
-
428
- /**
429
- * Returns whether or not the feed source is updating.
430
- *
431
- * @param (string|int) The id of the feed source
432
- * @return (bool) TRUE if the feed source is currently updating, FALSE otherwise.
433
- *
434
- */
435
- function wprss_is_feed_source_updating( $id ) {
436
- // Get the 'updating' meta field
437
- $is_updating_meta = get_post_meta( $id, 'wprss_feed_is_updating', TRUE );
438
-
439
- // Check if the feed has the 'updating' meta field set
440
- if ( $is_updating_meta === '' ) {
441
- // If not, then the feed is not updating
442
- return FALSE;
443
- }
444
-
445
- // Get the limit used for the feed
446
- $limit = get_post_meta( $id, 'wprss_limit', true );
447
- if ( $limit === '' || intval( $limit ) <= 0 ) {
448
- $global_limit = wprss_get_general_setting('limit_feed_items_imported');
449
- $limit = ( $global_limit === '' || intval( $global_limit ) <= 0 ) ? NULL : $global_limit;
450
  }
451
-
452
- // Calculate the allowed maximum time, based on the maximum number of items allowed to be
453
- // imported from this source.
454
- // If no limit is used, 60s (1min) is used.
455
- $single_item_time_limit = wprss_get_feed_fetch_time_limit();
456
- $allowed_time = $limit === NULL ? 120 : $single_item_time_limit * intval( $limit );
457
-
458
- // Calculate how many seconds have passed since the feed last signalled that it is updating
459
- $diff = time() - $is_updating_meta;
460
-
461
- // Get the transient that is set when the import function is called and the time of the next scheduled cron
462
- $is_updating_transient = get_transient('wpra/feeds/importing/' . $id);
463
- $scheduled = (wprss_get_next_feed_source_update($id) !== false);
464
- // If more than 5 seconds have passed and the transient is not yet set and the cron was not scheduled
465
- // then the cron probably failed to be registered
466
- if ( $diff > 5 && !$is_updating_transient && !$scheduled) {
467
- wprss_flag_feed_as_idle($id);
468
- update_post_meta(
469
- $id,
470
- 'wprss_error_last_import',
471
- __('The plugin failed to schedule a fetch for this feed. Please try again.', 'wprss')
472
- );
473
-
474
- return false;
475
- }
476
-
477
- // If the difference is greater than the allowed maximum amount of time, mark the feed as idle.
478
- if ( $diff > $allowed_time ) {
479
- wprss_flag_feed_as_idle( $id );
480
- // Feed is not updating
481
- return FALSE;
482
- }
483
-
484
- // Feed is updating
485
- return TRUE;
486
  }
487
-
488
-
489
- /**
490
- * Returns whether or not the feed source is deleting its feeed items.
491
- *
492
- * @param (string|int) The id of the feed source
493
- * @return (bool) TRUE if the feed source is currently deleting its items, FALSE otherwise.
494
- *
495
- */
496
- function wprss_is_feed_source_deleting( $id ) {
497
- $is_deleting_meta = get_post_meta( $id, 'wprss_feed_is_deleting_items', TRUE );
498
-
499
- if ( $is_deleting_meta === '' ) {
500
- return FALSE;
501
- }
502
-
503
- $diff = time() - $is_deleting_meta;
504
-
505
- $items = wprss_get_feed_items_for_source( $id );
506
- if ( $items->post_count === 0 || $diff > 300 ) {
507
- delete_post_meta( $id, 'wprss_feed_is_deleting_items' );
508
- return FALSE;
 
 
 
 
509
  }
510
-
511
- return TRUE;
512
- }
513
-
514
-
515
- /**
516
- * Returns the given parameter as a string. Used in wprss_truncate_posts()
517
- *
518
- * @return string The given parameter as a string
519
- * @since 3.5.1
520
- */
521
- function wprss_return_as_string( $item ) {
522
- return "'$item'";
523
- }
524
-
525
-
526
- /**
527
- * Returns true if the feed item is older than the given timestamp,
528
- * false otherwise;
529
- *
530
- * @since 3.8
531
- */
532
- function wprss_is_feed_item_older_than( $id, $timestamp ) {
533
- // GET THE DATE
534
- $age = get_the_time( 'U', $id );
535
- if ( $age === '' ) return FALSE;
536
- // Calculate the age difference
537
- $difference = $age - $timestamp;
538
- // Return whether the difference is negative ( the age is smaller than the timestamp )
539
- return ( $difference <= 0 );
540
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
 
542
-
543
- /**
544
- * Returns the maximum age setting for a feed source.
545
- *
546
- * @since 3.8
547
- */
548
- function wprss_get_max_age_for_feed_source( $source_id ) {
549
- $general_settings = get_option( 'wprss_settings_general' );
550
- // Get the meta data for age for this feed source
551
- $age_limit = trim( get_post_meta( $source_id, 'wprss_age_limit', TRUE ) );
552
- $age_unit = get_post_meta( $source_id, 'wprss_age_unit', TRUE );
553
-
554
- // If the meta does not exist, use the global settings
555
- if( $age_limit === '' ) {
556
- $age_limit = trim( wprss_get_general_setting( 'limit_feed_items_age' ) );
557
- $age_unit = wprss_get_general_setting( 'limit_feed_items_age_unit' );
558
- }
559
-
560
- // If the age limit is an empty string, use no limit
561
- if ( $age_limit === '' ) {
562
- return FALSE;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
  }
564
-
565
- // Return the timestamp of the max age date
566
- return strtotime( "-$age_limit $age_unit" );
567
  }
568
 
569
- /**
570
- * Truncates the items for a single feed source based on its age limit.
571
- *
572
- * @since 4.14
573
- *
574
- * @param int|WP_Post $source The source ID or post instance.
575
- */
576
- function wprss_truncate_items_for_source( $source )
577
- {
578
- $id = ( $source instanceof WP_Post )
579
- ? $source->ID
580
- : $source;
581
-
582
- $logger = wpra_get_logger($id);
583
-
584
- // Get the max age setting for this feed source
585
- $max_age = wprss_get_max_age_for_feed_source( $id );
586
-
587
- // If the data is empty, do not delete
588
- if ( $max_age === false ) {
589
- return;
590
- }
591
-
592
- // Get all feed items for this source
593
- $feed_items = wprss_get_feed_items_for_source( $id );
594
-
595
- // If there are no feed items, stop
596
- if ( ! $feed_items->have_posts() ) {
597
- return;
598
  }
599
-
600
- // Extend the timeout time limit for the deletion of the feed items
601
- set_time_limit( wprss_get_item_import_time_limit() );
602
-
603
- $logger->debug('Truncating existing items');
604
-
605
- // For each feed item
606
- while ( $feed_items->have_posts() ) {
607
- $feed_items->the_post();
608
- // If the post is older than the maximum age
609
- $item_id = get_the_ID();
610
-
611
- if ( wprss_is_feed_item_older_than( $item_id, $max_age ) === true ){
612
- // Delete the post
613
- wp_delete_post( $item_id, true );
614
- }
615
- }
616
-
617
- // Reset feed items query data
618
  wp_reset_postdata();
619
  }
620
 
621
- /**
622
- * Delete old feed items from the database to avoid bloat.
623
- * As of 3.8, it uses the new feed age system.
624
- *
625
- * @since 3.8
626
- */
627
- function wprss_truncate_posts() {
628
- // Get general settings
629
- $general_settings = get_option( 'wprss_settings_general' );
630
- // Get all feed sources
631
- $feed_sources = wprss_get_all_feed_sources();
632
-
633
- // Check if there are feed sources
634
- if( $feed_sources->have_posts() ) {
635
- // Truncate items for each feed source
636
- while ( $feed_sources->have_posts() ) {
637
- $feed_sources->the_post();
638
- wprss_truncate_items_for_source( get_the_ID() );
639
- }
640
- // Reset feed sources query data
641
- wp_reset_postdata();
642
- }
643
-
644
- // If the filter to use the fixed limit is enabled, call the old truncation function
645
- if ( apply_filters( 'wprss_use_fixed_feed_limit', FALSE ) === TRUE && isset( $general_settings['limit_feed_items_db'] ) ) {
646
- wprss_old_truncate_posts();
647
- }
648
  }
 
649
 
 
 
 
 
 
 
 
 
 
 
650
 
651
- /**
652
- * The old truncation function.
653
- * This truncation method uses the deprecated fixed feed limit.
654
- *
655
- * @since 2.0
656
- */
657
- function wprss_old_truncate_posts() {
658
- global $wpdb;
659
- $general_settings = get_option( 'wprss_settings_general' );
660
-
661
- if ( $general_settings['limit_feed_items_db'] == 0 ) {
662
- return;
663
- }
664
 
665
- // Set your threshold of max posts and post_type name
666
- $threshold = $general_settings['limit_feed_items_db'];
667
- $post_types = apply_filters( 'wprss_truncation_post_types', array( 'wprss_feed_item' ) );
668
- $post_types_str = array_map( 'wprss_return_as_string', $post_types );
669
 
670
- $post_type_list = implode( ',' , $post_types_str );
671
 
672
- // Query post type
673
- // $wpdb query allows me to select specific columns instead of grabbing the entire post object.
674
- $query = "
675
  SELECT ID, post_title FROM $wpdb->posts
676
  WHERE post_type IN ($post_type_list)
677
  AND post_status = 'publish'
678
  ORDER BY post_modified DESC
679
  ";
680
 
681
- $results = $wpdb->get_results( $query );
682
-
683
- // Check if there are any results
684
- $i = 0;
685
- if ( count( $results ) ){
686
- foreach ( $results as $post ) {
687
- $i++;
688
 
689
- // Skip any posts within our threshold
690
- if ( $i <= $threshold )
691
- continue;
 
 
692
 
693
- // Let the WordPress API do the heavy lifting for cleaning up entire post trails
694
- $purge = wp_delete_post( $post->ID, true );
 
695
  }
696
- }
697
- }
698
 
699
-
700
- add_filter( 'wprss_insert_post_item_conditionals', 'wprss_check_feed_item_date_on_import', 2, 3 );
701
- /**
702
- * When a feed item is imported, it's date is compared against the max age of it's feed source.
703
- *
704
- *
705
- * @since 3.8
706
- */
707
- function wprss_check_feed_item_date_on_import( $item, $source, $permalink ){
708
- if ( $item === NULL ) return NULL;
709
-
710
- // Get the age of the item and the max age setting for its feed source
711
- $age = $item->get_date( 'U' );
712
- $max_age = wprss_get_max_age_for_feed_source( $source );
713
-
714
- // If the age is not a valid timestamp, and the max age setting is disabled, return the item
715
- if ( $age === '' || $age === NULL || $max_age === FALSE || $max_age === NULL ) {
716
- return $item;
717
- }
718
-
719
- // Calculate the age difference
720
- $difference = $age - $max_age;
721
-
722
- if ( $difference <= 0 ) {
723
- wpra_get_logger()->debug('Item "{0}" was rejected by age limit settings.', [
724
- $item->get_title()
725
- ]);
726
-
727
- return NULL;
728
- } else {
729
- return $item;
730
  }
731
  }
 
732
 
733
-
734
- /**
735
- * Deletes all imported feeds.
736
- *
737
- * @since 3.0
738
- */
739
- function wprss_feed_reset() {
740
- wp_schedule_single_event( time(), 'wprss_delete_all_feed_items_hook' );
 
 
741
  }
742
 
 
 
 
743
 
 
 
 
 
744
 
745
- function wprss_schedule_reimport_all($deleted_ids) {
746
- if( !get_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING ) )
747
- return;
748
-
749
- wprss_log( 'Re-import scheduled...', __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM);
750
- delete_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING );
751
- wprss_fetch_insert_all_feed_items( TRUE );
752
- }
753
-
754
-
755
- /**
756
- * Deletes N oldest feed items for the given source
757
- *
758
- * @since 4.2
759
- * @deprecated
760
- */
761
- function wprss_delete_oldest_feed_items( $n, $source ) {
762
- // If the source does not exist, do nothing
763
- if ( get_post( $source ) == NULL ) return;
764
-
765
- // Make sure $n is an integer
766
- $n = intval($n);
767
-
768
- // Do nothing if n is zero or negative
769
- if ( $n <= 0 ) return;
770
 
771
- // Get the feed items, as an array, not WP_Query.
772
- // We will need to perform some array operations
773
- $feed_items = wprss_get_feed_items_for_source( $source );
774
- $feed_items = $feed_items->get_posts();
775
- // Get number of feed items
776
- $count = count( $feed_items );
777
 
778
- // Index of first feed item to delete
779
- $start = $count - $n;
780
- // Cut the array of feed items to get the items to delete
781
- $to_delete = array_slice( $feed_items, $start );
782
- // log -- for now
783
- foreach( $to_delete as $fi ) {
784
- //wprss_log_obj( "To delete" , $fi->ID );
785
- }
786
  }
 
787
 
 
 
 
 
 
 
 
 
 
788
 
789
- /**
790
- * Deletes the required number of feed items for the given source,
791
- * to keep the number of feed items below its limit.
792
- *
793
- * @since 4.2
794
- * @deprecated
795
- */
796
- function wprss_truncate_feed_items_for_source( $source ) {
797
- // Get the limit setting
798
- $limit = get_post_meta( $source, 'wprss_limit', true );
799
-
800
- // Calculate the number of feed items to delete
801
- $feed_items = wprss_get_feed_items_for_source( $source );
802
- $n = intval($feed_items->found_posts) - intval($limit);
803
-
804
- // Delete the feed items
805
- wprss_delete_oldest_feed_items( $n, $source );
806
  }
 
 
 
 
 
1
  <?php
2
 
3
+ define('WPRSS_TRANSIENT_NAME_IS_REIMPORTING', 'is_reimporting');
4
+
5
+ /**
6
+ * Returns whether or not the feed source will forcefully fetch the next fetch,
7
+ * ignoring whether or not it is paused or not.
8
+ *
9
+ * @since 3.7
10
+ *
11
+ * @param int $source_id The ID of the feed source
12
+ *
13
+ * @return boolean
14
+ */
15
+ function wprss_feed_source_force_next_fetch($source_id)
16
+ {
17
+ $force = get_post_meta($source_id, 'wprss_force_next_fetch', true);
18
+
19
+ return ($force !== '');
20
+ }
21
+
22
+ /**
23
+ * Change the default feed cache recreation period to 2 hours
24
+ *
25
+ * Probably not needed since we are now disabling caching altogether
26
+ *
27
+ * @since 2.1
28
+ */
29
+ function wprss_feed_cache_lifetime($seconds)
30
+ {
31
+ return 1; // one second
32
+ }
33
+
34
+ /**
35
+ * Disable caching of feeds in transients, we don't need it as we are storing them in the wp_posts table
36
+ *
37
+ * @since 3.0
38
+ */
39
+ function wprss_do_not_cache_feeds(&$feed)
40
+ {
41
+ $feed->enable_cache(false);
42
+ }
43
+
44
+ /**
45
+ * Parameters for query to get all feed sources
46
+ *
47
+ * @since 3.0
48
+ */
49
+ function wprss_get_all_feed_sources()
50
+ {
51
+ $queryArgs = apply_filters(
52
+ 'wprss_get_all_feed_sources',
53
+ [
54
+ 'post_type' => 'wprss_feed',
55
+ 'post_status' => 'publish',
56
+ 'cache_results' => false, // Disable caching, used for one-off queries
57
+ 'no_found_rows' => true, // We don't need pagination, so disable it
58
+ 'posts_per_page' => -1,
59
+ ]
60
+ );
61
+
62
+ return new WP_Query($queryArgs);
63
+ }
64
+
65
+ /**
66
+ * Retrieves the query to use for retrieving imported items.
67
+ *
68
+ * @since 4.17.4
69
+ */
70
+ function wprss_get_imported_items_query($source_id = null)
71
+ {
72
+ $args = [
73
+ 'post_type' => array_values(get_post_types()),
74
+ 'post_status' => 'any',
75
+ 'cache_results' => false,
76
+ 'no_found_rows' => true,
77
+ 'posts_per_page' => -1,
78
+ 'ignore_sticky_posts' => 'true',
79
+ 'orderby' => 'date',
80
+ 'order' => 'DESC',
81
+ 'meta_query' => [
82
+ 'relation' => 'AND',
83
+ ],
84
+ 'suppress_filters' => 1,
85
+ ];
86
+
87
+ if ($source_id !== null) {
88
+ $args['meta_query'][] = [
89
+ 'key' => 'wprss_feed_id',
90
+ 'value' => (string) $source_id,
91
+ 'compare' => '=',
92
+ ];
93
+ } else {
94
+ $args['meta_query'][] = [
95
+ 'key' => 'wprss_feed_id',
96
+ 'compare' => 'EXISTS',
97
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
99
 
100
+ return apply_filters('wprss_get_feed_items_for_source_args', $args, $source_id);
101
+ }
102
+
103
+ /**
104
+ * Queries for imported items.
105
+ *
106
+ * @since 4.17.4
107
+ */
108
+ function wprss_get_imported_items($source_id = null)
109
+ {
110
+ return new WP_Query(wprss_get_imported_items_query($source_id));
111
+ }
112
+
113
+ /**
114
+ * Returns all the feed items of a source.
115
+ *
116
+ * @since 3.8
117
+ */
118
+ function wprss_get_feed_items_for_source($source_id)
119
+ {
120
+ return wprss_get_imported_items($source_id);
121
+ }
122
+
123
+ /**
124
+ * Queries the DB to get the GUIDs for existing feed items.
125
+ *
126
+ * @param int|string $feedId The ID of the feed source.
127
+ */
128
+ function wprss_get_existing_guids($feedId) {
129
+ global $wpdb;
130
+
131
+ $cols = $wpdb->get_col(
132
+ "SELECT q.`meta_value`
133
+ FROM {$wpdb->postmeta} AS p
134
+ JOIN {$wpdb->postmeta} AS q
135
+ ON (q.`meta_key` = 'wprss_item_guid' AND p.`post_id` = q.`post_id`)
136
+ WHERE p.`meta_key` = 'wprss_feed_id' AND p.`meta_value` = '{$feedId}'"
137
+ );
138
+
139
+ return @array_flip($cols);
140
+ }
141
+
142
+ /**
143
+ * Database query to get existing permalinks
144
+ *
145
+ * @since 3.0
146
+ */
147
+ function wprss_get_existing_permalinks($feed_ID)
148
+ {
149
+ global $wpdb;
150
+
151
+ $cols = $wpdb->get_col(
152
+ "SELECT q.`meta_value`
153
+ FROM {$wpdb->postmeta} AS p
154
+ JOIN {$wpdb->postmeta} AS q
155
+ ON (q.`meta_key` = 'wprss_item_permalink' AND p.`post_id` = q.`post_id`)
156
+ WHERE p.`meta_key` = 'wprss_feed_id' AND p.`meta_value` = '{$feed_ID}'"
157
+ );
158
+
159
+ return @array_flip($cols);
160
+ }
161
+
162
+ /**
163
+ * Checks if an item title exists in the database.
164
+ *
165
+ * @since 4.14
166
+ *
167
+ * @param string $title The title to search for.
168
+ *
169
+ * @return bool True if the title exists, false if not.
170
+ */
171
+ function wprss_item_title_exists($title)
172
+ {
173
+ global $wpdb;
174
+
175
+ $query = $wpdb->prepare(
176
+ "SELECT *
177
+ FROM `{$wpdb->posts}` AS p
178
+ JOIN `{$wpdb->postmeta}` AS q
179
+ ON p.`ID` = q.`post_id`
180
+ WHERE q.`meta_key` = 'wprss_feed_id' AND p.`post_title` = %s",
181
+ [html_entity_decode($title)]
182
+ );
183
+
184
+ $cols = $wpdb->get_col($query);
185
+
186
+ return count($cols) > 0;
187
+ }
188
+
189
+ /**
190
+ * Database query to get existing titles
191
+ *
192
+ * @since 4.7
193
+ */
194
+ function wprss_get_existing_titles($feed_ID = null)
195
+ {
196
+ global $wpdb;
197
+
198
+ $condition = ($feed_ID !== null)
199
+ ? "AND q.`meta_value` = '{$feed_ID}'"
200
+ : '';
201
+
202
+ $cols = $wpdb->get_col(
203
+ "SELECT p.`post_title`
204
+ FROM `{$wpdb->posts}` AS p
205
+ JOIN `{$wpdb->postmeta}` AS q
206
+ ON p.`ID` = q.`post_id`
207
+ WHERE q.`meta_key` = 'wprss_feed_id' $condition"
208
+ );
209
+
210
+ return @array_flip($cols);
211
+ }
212
+
213
+ /**
214
+ * Checks if a permalink exists.
215
+ *
216
+ * Untested!
217
+ *
218
+ * @param string $permalink The permalink, expected to be normalized.
219
+ *
220
+ * @return bool
221
+ */
222
+ function wprss_permalink_exists($permalink)
223
+ {
224
+ global $wpdb;
225
+
226
+ $wpdb->query(
227
+ $wpdb->prepare(
228
  "SELECT *
229
+ FROM {$wpdb->postmeta}
230
+ WHERE `meta_value` = '{$permalink}'"
231
+ )
232
+ );
233
+
234
+ return $wpdb->num_rows > 0;
235
+ }
236
+
237
+ add_action('publish_wprss_feed', 'wprss_fetch_insert_feed_items', 10);
238
+ /**
239
+ * Fetches feed items from source provided and inserts into db
240
+ *
241
+ * This function is used when inserting or un-trashing a feed source, it only gets feeds from that particular source.
242
+ *
243
+ * @since 3.0
244
+ */
245
+ function wprss_fetch_insert_feed_items($post_id)
246
+ {
247
+ wp_schedule_single_event(time(), 'wprss_fetch_single_feed_hook', [$post_id]);
248
+ }
249
+
250
+ /**
251
+ * Returns the image of the feed.
252
+ * The reason this function exists is for add-ons to be able to detect if the plugin core
253
+ * supports feed image functionality through a simple function_exists() call.
254
+ *
255
+ * @since 1.0
256
+ *
257
+ * @param int $source_id The ID of the feed source
258
+ *
259
+ * @return string The link to the feed image
260
+ */
261
+ function wprss_get_feed_image($source_id)
262
+ {
263
+ return get_post_meta($source_id, 'wprss_feed_image', true);
264
+ }
265
+
266
+ add_action('post_updated', 'wprss_updated_feed_source', 10, 3);
267
+ /**
268
+ * This function is triggered just after a post is updated.
269
+ * It checks if the updated post is a feed source, and carries out any
270
+ * updating necessary.
271
+ *
272
+ * @since 3.3
273
+ */
274
+ function wprss_updated_feed_source($post_ID, $post_after, $post_before)
275
+ {
276
+ // Check if the post is a feed source and is published
277
+
278
+ if (($post_after->post_type == 'wprss_feed') && ($post_after->post_status == 'publish')) {
279
+ if (isset($_POST['wprss_url']) && !empty($_POST['wprss_url'])) {
280
+ $url = $_POST['wprss_url'];
281
+ $feed = wprss_fetch_feed($url);
282
+ if ($feed !== null && !is_wp_error($feed)) {
283
+ update_post_meta($post_ID, 'wprss_site_url', $feed->get_permalink());
284
+ update_post_meta($post_ID, 'wprss_feed_image', $feed->get_image_url());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  }
 
 
287
 
288
+ if (isset($_POST['wprss_limit']) && !empty($_POST['wprss_limit'])) {
289
+ // Checking feed limit change
290
+ // Get the limit currently saved in db, and limit in POST request
291
+ //$limit = get_post_meta( $post_ID, 'wprss_limit', true );
292
+ $limit = $_POST['wprss_limit'];
293
+ // Get all feed items for this source
294
+ $feed_sources = new WP_Query(
295
+ [
296
+ 'post_type' => 'wprss_feed_item',
297
+ 'post_status' => 'publish',
298
+ 'cache_results' => false, // Disable caching, used for one-off queries
299
+ 'no_found_rows' => true, // We don't need pagination, so disable it
300
+ 'posts_per_page' => -1,
301
+ 'orderby' => 'date',
302
+ 'order' => 'ASC',
303
+ 'meta_query' => [
304
+ [
305
+ 'key' => 'wprss_feed_id',
306
+ 'value' => $post_ID,
307
+ 'compare' => 'LIKE',
308
+ ],
309
+ ],
310
+ ]
311
+ );
312
+ // If the limit is smaller than the number of found posts, delete the feed items
313
+ // and re-import, to ensure that most recent feed items are present.
314
+ $difference = intval($feed_sources->post_count) - intval($limit);
315
+ if ($difference > 0) {
316
+ // Loop and delete the excess feed items
317
+ while ($feed_sources->have_posts() && $difference > 0) {
318
+ $feed_sources->the_post();
319
+ wp_delete_post(get_the_ID(), true);
320
+ $difference--;
321
  }
322
  }
323
  }
324
  }
325
+ }
326
+
327
+ add_action('added_post_meta', 'wprss_update_feed_meta', 10, 4);
328
+ add_action('updated_post_meta', 'wprss_update_feed_meta', 10, 4);
329
+ /**
330
+ * This function is run whenever a post is saved or updated.
331
+ *
332
+ * @since 3.4
333
+ */
334
+ function wprss_update_feed_meta($meta_id, $post_id, $meta_key, $meta_value)
335
+ {
336
+ $post = get_post($post_id);
337
+ if ($post !== null && $post->post_status === 'publish' && $post->post_type === 'wprss_feed') {
338
+ if ($meta_key === 'wprss_url') {
339
+ wprss_change_fb_url($post_id, $meta_value);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
  }
342
+ }
343
+
344
+ function wprss_change_fb_url($post_id, $url)
345
+ {
346
+ # Check if url begins with a known facebook hostname.
347
+ if (stripos($url, 'http://facebook.com') === 0
348
+ || stripos($url, 'http://www.facebook.com') === 0
349
+ || stripos($url, 'https://facebook.com') === 0
350
+ || stripos($url, 'https://www.facebook.com') === 0
351
+ ) {
352
+ # Generate the new URL to FB Graph
353
+ $com_index = stripos($url, '.com');
354
+ $fb_page = substr($url, $com_index + 4); # 4 = length of ".com"
355
+ $fb_graph_url = 'https://graph.facebook.com' . $fb_page;
356
+ # Contact FB Graph and get data
357
+ $response = wp_remote_get($fb_graph_url);
358
+ # If the response successful and has a body
359
+ if (!is_wp_error($response) && isset($response['body'])) {
360
+ # Parse the body as a JSON string
361
+ $json = json_decode($response['body'], true);
362
+ # If an id is present ...
363
+ if (isset($json['id'])) {
364
+ # Generate the final URL for this feed and update the post meta
365
+ $final_url = "https://www.facebook.com/feeds/page.php?format=rss20&id=" . $json['id'];
366
+ update_post_meta($post_id, 'wprss_url', $final_url, $url);
367
+ }
368
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  }
370
+ }
371
+
372
+ add_action('trash_wprss_feed', 'wprss_delete_feed_items'); // maybe use wp_trash_post action? wp_trash_wprss_feed
373
+ /**
374
+ * Delete feed items on trashing of corresponding feed source
375
+ *
376
+ * @since 2.0
377
+ */
378
+ function wprss_delete_feed_items($source_id)
379
+ {
380
+ $force_delete = apply_filters('wprss_force_delete_when_by_source', true);
381
+
382
+ // WPML fix: removes the current language from the query WHERE and JOIN clauses
383
+ global $sitepress;
384
+ if ($sitepress !== null) {
385
+ remove_filter('posts_join', [$sitepress, 'posts_join_filter']);
386
+ remove_filter('posts_where', [$sitepress, 'posts_where_filter']);
387
+ }
388
+
389
+ $args = wprss_get_imported_items_query($source_id);
390
+ $items = get_posts($args);
391
+
392
+ foreach ($items as $item) {
393
+ wp_delete_post($item->ID, $force_delete);
394
+ }
395
+ }
396
+
397
+ add_action('wprss_delete_all_feed_items_hook', 'wprss_delete_all_feed_items');
398
+ /**
399
+ * Delete all feed items
400
+ *
401
+ * @since 3.0
402
+ */
403
+ function wprss_delete_all_feed_items()
404
+ {
405
+ $args = wprss_get_imported_items_query();
406
+ $items = get_posts($args);
407
+
408
+ foreach ($items as $item) {
409
+ wp_delete_post($item->ID, true);
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Marks the feed source as 'updating' (importing).
415
+ *
416
+ * @since 4.6.6
417
+ * @return int The time value set in the 'updating' meta field
418
+ */
419
+ function wprss_flag_feed_as_updating($feed_ID)
420
+ {
421
+ update_post_meta($feed_ID, 'wprss_feed_is_updating', $start_time = time());
422
+ return $start_time;
423
+ }
424
+
425
+ /**
426
+ * Marks the feed source as 'idle' (not importing).
427
+ *
428
+ * @since 4.6.6
429
+ */
430
+ function wprss_flag_feed_as_idle($feed_ID)
431
+ {
432
+ update_post_meta($feed_ID, 'wprss_feed_is_updating', '');
433
+ }
434
+
435
+ /**
436
+ * Returns whether the feed source is updating.
437
+ *
438
+ * @param string|int The id of the feed source
439
+ *
440
+ * @return bool TRUE if the feed source is currently updating, FALSE otherwise.
441
+ */
442
+ function wprss_is_feed_source_updating($id)
443
+ {
444
+ // Get the 'updating' meta field
445
+ $is_updating_meta = get_post_meta($id, 'wprss_feed_is_updating', true);
446
+
447
+ // Check if the feed has the 'updating' meta field set
448
+ if ($is_updating_meta === '') {
449
+ // If not, then the feed is not updating
450
+ return false;
451
+ }
452
+
453
+ // Get the limit used for the feed
454
+ $limit = get_post_meta($id, 'wprss_limit', true);
455
+ if ($limit === '' || intval($limit) <= 0) {
456
+ $global_limit = wprss_get_general_setting('limit_feed_items_imported');
457
+ $limit = ($global_limit === '' || intval($global_limit) <= 0) ? null : $global_limit;
458
+ }
459
+
460
+ // Calculate the allowed maximum time, based on the maximum number of items allowed to be
461
+ // imported from this source.
462
+ // If no limit is used, 60s (1min) is used.
463
+ $single_item_time_limit = wprss_get_feed_fetch_time_limit();
464
+ $allowed_time = $limit === null ? 120 : $single_item_time_limit * intval($limit);
465
+
466
+ // Calculate how many seconds have passed since the feed last signalled that it is updating
467
+ $diff = time() - $is_updating_meta;
468
+
469
+ // Get the transient that is set when the import function is called and the time of the next scheduled cron
470
+ $is_updating_transient = get_transient('wpra/feeds/importing/' . $id);
471
+ $scheduled = (wprss_get_next_feed_source_update($id) !== false);
472
+ // If more than 5 seconds have passed and the transient is not yet set and the cron was not scheduled
473
+ // then the cron probably failed to be registered
474
+ if ($diff > 5 && !$is_updating_transient && !$scheduled) {
475
+ wprss_flag_feed_as_idle($id);
476
+ update_post_meta(
477
+ $id,
478
+ 'wprss_error_last_import',
479
+ __('The plugin failed to schedule a fetch for this feed. Please try again.', 'wprss')
480
+ );
481
 
482
+ return false;
483
+ }
484
+
485
+ // If the difference is greater than the allowed maximum amount of time, mark the feed as idle.
486
+ if ($diff > $allowed_time) {
487
+ wprss_flag_feed_as_idle($id);
488
+ // Feed is not updating
489
+ return false;
490
+ }
491
+
492
+ // Feed is updating
493
+ return true;
494
+ }
495
+
496
+ /**
497
+ * Returns whether the feed source is deleting its feed items.
498
+ *
499
+ * @param string|int The id of the feed source
500
+ *
501
+ * @return bool TRUE if the feed source is currently deleting its items, FALSE otherwise.
502
+ */
503
+ function wprss_is_feed_source_deleting($id)
504
+ {
505
+ $is_deleting_meta = get_post_meta($id, 'wprss_feed_is_deleting_items', true);
506
+
507
+ if ($is_deleting_meta === '') {
508
+ return false;
509
+ }
510
+
511
+ $diff = time() - $is_deleting_meta;
512
+
513
+ $items = wprss_get_feed_items_for_source($id);
514
+ if ($items->post_count === 0 || $diff > 300) {
515
+ delete_post_meta($id, 'wprss_feed_is_deleting_items');
516
+ return false;
517
+ }
518
+
519
+ return true;
520
+ }
521
+
522
+ /**
523
+ * Returns the given parameter as a string. Used in wprss_truncate_posts()
524
+ *
525
+ * @since 3.5.1
526
+ * @return string The given parameter as a string
527
+ */
528
+ function wprss_return_as_string($item)
529
+ {
530
+ return "'$item'";
531
+ }
532
+
533
+ /**
534
+ * Returns true if the feed item is older than the given timestamp,
535
+ * false otherwise;
536
+ *
537
+ * @since 3.8
538
+ */
539
+ function wprss_is_feed_item_older_than($id, $timestamp)
540
+ {
541
+ // GET THE DATE
542
+ $age = get_the_time('U', $id);
543
+ if ($age === '') {
544
+ return false;
545
+ }
546
+ // Calculate the age difference
547
+ $difference = $age - $timestamp;
548
+ // Return whether the difference is negative ( the age is smaller than the timestamp )
549
+ return ($difference <= 0);
550
+ }
551
+
552
+ /**
553
+ * Returns the maximum age setting for a feed source.
554
+ *
555
+ * @since 3.8
556
+ */
557
+ function wprss_get_max_age_for_feed_source($source_id)
558
+ {
559
+ $general_settings = get_option('wprss_settings_general');
560
+ // Get the metadata for age for this feed source
561
+ $age_limit = trim(get_post_meta($source_id, 'wprss_age_limit', true));
562
+ $age_unit = get_post_meta($source_id, 'wprss_age_unit', true);
563
+
564
+ // If the meta does not exist, use the global settings
565
+ if ($age_limit === '') {
566
+ $age_limit = trim(wprss_get_general_setting('limit_feed_items_age'));
567
+ $age_unit = wprss_get_general_setting('limit_feed_items_age_unit');
568
+ }
569
+
570
+ // If the age limit is an empty string, use no limit
571
+ if ($age_limit === '') {
572
+ return false;
573
+ }
574
+
575
+ // Return the timestamp of the max age date
576
+ return strtotime("-$age_limit $age_unit");
577
+ }
578
+
579
+ /**
580
+ * Truncates the items for a single feed source based on its age limit.
581
+ *
582
+ * @since 4.14
583
+ *
584
+ * @param int|WP_Post $source The source ID or post instance.
585
+ */
586
+ function wprss_truncate_items_for_source($source)
587
+ {
588
+ $id = ($source instanceof WP_Post)
589
+ ? $source->ID
590
+ : $source;
591
+
592
+ $logger = wpra_get_logger($id);
593
+
594
+ // Get the max age setting for this feed source
595
+ $max_age = wprss_get_max_age_for_feed_source($id);
596
+
597
+ // If the data is empty, do not delete
598
+ if ($max_age === false) {
599
+ return;
600
+ }
601
+
602
+ // Get all feed items for this source
603
+ $feed_items = wprss_get_feed_items_for_source($id);
604
+
605
+ // If there are no feed items, stop
606
+ if (!$feed_items->have_posts()) {
607
+ return;
608
+ }
609
+
610
+ // Extend the timeout time limit for the deletion of the feed items
611
+ set_time_limit(wprss_get_item_import_time_limit());
612
+
613
+ $logger->debug('Truncating existing items');
614
+
615
+ // For each feed item
616
+ while ($feed_items->have_posts()) {
617
+ $feed_items->the_post();
618
+ // If the post is older than the maximum age
619
+ $item_id = get_the_ID();
620
+
621
+ if (wprss_is_feed_item_older_than($item_id, $max_age) === true) {
622
+ // Delete the post
623
+ wp_delete_post($item_id, true);
624
  }
 
 
 
625
  }
626
 
627
+ // Reset feed items query data
628
+ wp_reset_postdata();
629
+ }
630
+
631
+ /**
632
+ * Delete old feed items from the database to avoid bloat.
633
+ * As of 3.8, it uses the new feed age system.
634
+ *
635
+ * @since 3.8
636
+ */
637
+ function wprss_truncate_posts()
638
+ {
639
+ // Get general settings
640
+ $general_settings = get_option('wprss_settings_general');
641
+ // Get all feed sources
642
+ $feed_sources = wprss_get_all_feed_sources();
643
+
644
+ // Check if there are feed sources
645
+ if ($feed_sources->have_posts()) {
646
+ // Truncate items for each feed source
647
+ while ($feed_sources->have_posts()) {
648
+ $feed_sources->the_post();
649
+ wprss_truncate_items_for_source(get_the_ID());
 
 
 
 
 
 
650
  }
651
+ // Reset feed sources query data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
652
  wp_reset_postdata();
653
  }
654
 
655
+ // If the filter to use the fixed limit is enabled, call the old truncation function
656
+ if (apply_filters('wprss_use_fixed_feed_limit',
657
+ false) === true && isset($general_settings['limit_feed_items_db'])) {
658
+ wprss_old_truncate_posts();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659
  }
660
+ }
661
 
662
+ /**
663
+ * The old truncation function.
664
+ * This truncation method uses the deprecated fixed feed limit.
665
+ *
666
+ * @since 2.0
667
+ */
668
+ function wprss_old_truncate_posts()
669
+ {
670
+ global $wpdb;
671
+ $general_settings = get_option('wprss_settings_general');
672
 
673
+ if ($general_settings['limit_feed_items_db'] == 0) {
674
+ return;
675
+ }
 
 
 
 
 
 
 
 
 
 
676
 
677
+ // Set your threshold of max posts and post_type name
678
+ $threshold = $general_settings['limit_feed_items_db'];
679
+ $post_types = apply_filters('wprss_truncation_post_types', ['wprss_feed_item']);
680
+ $post_types_str = array_map('wprss_return_as_string', $post_types);
681
 
682
+ $post_type_list = implode(',', $post_types_str);
683
 
684
+ // Query post type
685
+ // $wpdb query allows me to select specific columns instead of grabbing the entire post object.
686
+ $query = "
687
  SELECT ID, post_title FROM $wpdb->posts
688
  WHERE post_type IN ($post_type_list)
689
  AND post_status = 'publish'
690
  ORDER BY post_modified DESC
691
  ";
692
 
693
+ $results = $wpdb->get_results($query);
 
 
 
 
 
 
694
 
695
+ // Check if there are any results
696
+ $i = 0;
697
+ if (count($results)) {
698
+ foreach ($results as $post) {
699
+ $i++;
700
 
701
+ // Skip any posts within our threshold
702
+ if ($i <= $threshold) {
703
+ continue;
704
  }
 
 
705
 
706
+ // Let the WordPress API do the heavy lifting for cleaning up entire post trails
707
+ $purge = wp_delete_post($post->ID, true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
708
  }
709
  }
710
+ }
711
 
712
+ add_filter('wprss_insert_post_item_conditionals', 'wprss_check_feed_item_date_on_import', 2, 3);
713
+ /**
714
+ * When a feed item is imported, it's date is compared against the max age of it's feed source.
715
+ *
716
+ * @since 3.8
717
+ */
718
+ function wprss_check_feed_item_date_on_import($item, $source, $permalink)
719
+ {
720
+ if ($item === null) {
721
+ return null;
722
  }
723
 
724
+ // Get the age of the item and the max age setting for its feed source
725
+ $age = $item->get_date('U');
726
+ $max_age = wprss_get_max_age_for_feed_source($source);
727
 
728
+ // If the age is not a valid timestamp, and the max age setting is disabled, return the item
729
+ if ($age === '' || $age === null || $max_age === false || $max_age === null) {
730
+ return $item;
731
+ }
732
 
733
+ // Calculate the age difference
734
+ $difference = $age - $max_age;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
735
 
736
+ if ($difference <= 0) {
737
+ wpra_get_logger()->debug('Item "{0}" was rejected by age limit settings.', [
738
+ $item->get_title(),
739
+ ]);
 
 
740
 
741
+ return null;
742
+ } else {
743
+ return $item;
 
 
 
 
 
744
  }
745
+ }
746
 
747
+ /**
748
+ * Deletes all imported feeds.
749
+ *
750
+ * @since 3.0
751
+ */
752
+ function wprss_feed_reset()
753
+ {
754
+ wp_schedule_single_event(time(), 'wprss_delete_all_feed_items_hook');
755
+ }
756
 
757
+ function wprss_schedule_reimport_all($deleted_ids)
758
+ {
759
+ if (!get_transient(WPRSS_TRANSIENT_NAME_IS_REIMPORTING)) {
760
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
761
  }
762
+
763
+ wprss_log('Re-import scheduled...', __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM);
764
+ delete_transient(WPRSS_TRANSIENT_NAME_IS_REIMPORTING);
765
+ wprss_fetch_insert_all_feed_items(true);
766
+ }
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: RSS import, RSS aggregator, autoblog, feed to post, news aggregator, rss t
5
  Requires at least: 4.0 or higher
6
  Tested up to: 5.8
7
  Requires PHP: 5.4
8
- Stable tag: 4.19.3
9
  License: GPLv3
10
 
11
  The most powerful and reliable RSS aggregator for WordPress. Build a news aggregator, autoblog and more in minutes with unlimited RSS feeds.
@@ -254,6 +254,18 @@ Our complete Knowledge Base with FAQs can be found [here](https://kb.wprssaggreg
254
 
255
  == Changelog ==
256
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  = 4.19.3 (2021-11-24)
258
  **Fixed**
259
  - An error during cron schedule filtering.
5
  Requires at least: 4.0 or higher
6
  Tested up to: 5.8
7
  Requires PHP: 5.4
8
+ Stable tag: 4.20
9
  License: GPLv3
10
 
11
  The most powerful and reliable RSS aggregator for WordPress. Build a news aggregator, autoblog and more in minutes with unlimited RSS feeds.
254
 
255
  == Changelog ==
256
 
257
+ = 4.20 (2022-01-18) =
258
+ **Added**
259
+ - New option to use feed item GUIDs instead of permalinks to detect duplicate items.
260
+
261
+ **Changed**
262
+ - Small performance improvement when importing feed items.
263
+
264
+ **Fixed**
265
+ - A warning about `get_headers()` only working with URLs.
266
+ - A warning about iteration over a non-array value.
267
+ - An AJAX XSS vulnerability on the Feed Sources page. Thanks WPScan!
268
+
269
  = 4.19.3 (2021-11-24)
270
  **Fixed**
271
  - An error during cron schedule filtering.
vendor/composer/installed.php CHANGED
@@ -5,7 +5,7 @@
5
  'type' => 'wordpress-plugin',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
- 'reference' => '6ac1322b90948d7d50b0db7ff5ef6e3d08506d11',
9
  'name' => 'wprss/core',
10
  'dev' => false,
11
  ),
@@ -343,7 +343,7 @@
343
  'type' => 'wordpress-plugin',
344
  'install_path' => __DIR__ . '/../../',
345
  'aliases' => array(),
346
- 'reference' => '6ac1322b90948d7d50b0db7ff5ef6e3d08506d11',
347
  'dev_requirement' => false,
348
  ),
349
  ),
5
  'type' => 'wordpress-plugin',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
+ 'reference' => '935532e5af1a44ac838ebb1f7054b1963781c4dc',
9
  'name' => 'wprss/core',
10
  'dev' => false,
11
  ),
343
  'type' => 'wordpress-plugin',
344
  'install_path' => __DIR__ . '/../../',
345
  'aliases' => array(),
346
+ 'reference' => '935532e5af1a44ac838ebb1f7054b1963781c4dc',
347
  'dev_requirement' => false,
348
  ),
349
  ),
wp-rss-aggregator.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin Name: WP RSS Aggregator
5
  * Plugin URI: https://www.wprssaggregator.com/#utm_source=wpadmin&utm_medium=plugin&utm_campaign=wpraplugin
6
  * Description: Imports and aggregates multiple RSS Feeds.
7
- * Version: 4.19.3
8
  * Author: RebelCode
9
  * Author URI: https://www.wprssaggregator.com
10
  * Text Domain: wprss
@@ -76,7 +76,7 @@ use RebelCode\Wpra\Core\Plugin;
76
 
77
  // Set the version number of the plugin.
78
  if( !defined( 'WPRSS_VERSION' ) )
79
- define( 'WPRSS_VERSION', '4.19.3' );
80
 
81
  if( !defined( 'WPRSS_WP_MIN_VERSION' ) )
82
  define( 'WPRSS_WP_MIN_VERSION', '4.8' );
4
  * Plugin Name: WP RSS Aggregator
5
  * Plugin URI: https://www.wprssaggregator.com/#utm_source=wpadmin&utm_medium=plugin&utm_campaign=wpraplugin
6
  * Description: Imports and aggregates multiple RSS Feeds.
7
+ * Version: 4.20
8
  * Author: RebelCode
9
  * Author URI: https://www.wprssaggregator.com
10
  * Text Domain: wprss
76
 
77
  // Set the version number of the plugin.
78
  if( !defined( 'WPRSS_VERSION' ) )
79
+ define( 'WPRSS_VERSION', '4.20' );
80
 
81
  if( !defined( 'WPRSS_WP_MIN_VERSION' ) )
82
  define( 'WPRSS_WP_MIN_VERSION', '4.8' );