diff --git a/includes/abstract/feedzy-rss-feeds-admin-abstract.php b/includes/abstract/feedzy-rss-feeds-admin-abstract.php
index 1a04b070..99983e5b 100644
--- a/includes/abstract/feedzy-rss-feeds-admin-abstract.php
+++ b/includes/abstract/feedzy-rss-feeds-admin-abstract.php
@@ -2035,7 +2035,8 @@ public function feedzy_retrieve_image( $item, $sc = null ) {
return $the_thumbnail;
}
- if ( ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && ! empty( $sc['feeds'] ) ) {
+ // Import jobs (__jobID set) skip the display fallback image: it cannot be sideloaded (SVG); imports use the default_thumbnail_id setting instead.
+ if ( ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && ! empty( $sc['feeds'] ) && empty( $sc['__jobID'] ) ) {
$feed_url = $this->normalize_urls( $sc['feeds'] );
$the_thumbnail = ! empty( $the_thumbnail ) ? $the_thumbnail : apply_filters( 'feedzy_default_image', $sc['default'], $feed_url );
}
diff --git a/includes/admin/feedzy-rss-feeds-import.php b/includes/admin/feedzy-rss-feeds-import.php
index 0827bc94..9624651d 100644
--- a/includes/admin/feedzy-rss-feeds-import.php
+++ b/includes/admin/feedzy-rss-feeds-import.php
@@ -2711,7 +2711,7 @@ function ( $term ) {
$img_success = $this->try_save_featured_image( $image_source_url, $new_post_id, $img_title, $import_info );
Feedzy_Rss_Feeds_Log::debug(
- sprintf( 'Saved featured image for post ID %1$s: %2$s', $new_post_id, $image_source_url ),
+ sprintf( 'Tried to save featured image for post ID %1$s: %2$s. Success: %3$s', $new_post_id, $image_source_url, $img_success ? 'yes' : 'no' ),
array(
'job_id' => $job->ID,
'image_source_url' => $image_source_url,
diff --git a/tests/test-admin-abstract.php b/tests/test-admin-abstract.php
index deb12919..bc7d5f9e 100644
--- a/tests/test-admin-abstract.php
+++ b/tests/test-admin-abstract.php
@@ -624,4 +624,86 @@ public function test_feedzy_image_encode_edge_cases() {
$this->assertEquals($expected, $result, "Failed for input: $input");
}
}
-}
\ No newline at end of file
+
+ /**
+ * Build a SimplePie item that has no image anywhere (enclosure, media, content, description).
+ */
+ private function get_imageless_item() {
+ $feed = new SimplePie();
+ $feed->set_raw_data('
+
+
+ Test Feed
+ -
+ Item without image
+ https://example.com/article
+ Plain text, no image tag.
+
+
+ ');
+ $feed->init();
+ $items = $feed->get_items();
+ return $items[0];
+ }
+
+ /**
+ * During an import job the bundled default image (feedzy.svg) must NOT be used as
+ * the item image: WordPress rejects SVG uploads, causing Media Library errors.
+ * See https://github.com/Codeinwp/feedzy-rss-feeds/issues/1277.
+ */
+ public function test_feedzy_retrieve_image_import_job_skips_default_image() {
+ $sc = array(
+ 'feeds' => 'https://example.com/feed',
+ 'default' => '',
+ '__jobID' => 42,
+ );
+
+ $result = $this->feedzy_abstract->feedzy_retrieve_image( $this->get_imageless_item(), $sc );
+
+ $this->assertSame( '', $result, 'Import jobs must not fall back to the bundled default image' );
+ }
+
+ /**
+ * Display contexts (shortcode/block rendering, no __jobID) keep the default image
+ * fallback, where the bundled SVG is fine inside an
tag.
+ */
+ public function test_feedzy_retrieve_image_display_keeps_default_image() {
+ $sc = array(
+ 'feeds' => 'https://example.com/feed',
+ 'default' => '',
+ );
+
+ $result = $this->feedzy_abstract->feedzy_retrieve_image( $this->get_imageless_item(), $sc );
+
+ $this->assertSame( FEEDZY_ABSURL . 'img/feedzy.svg', $result, 'Display contexts must keep the default image fallback' );
+ }
+
+ /**
+ * The import-job guard must only suppress the fallback, never a real item image.
+ */
+ public function test_feedzy_retrieve_image_import_job_keeps_real_item_image() {
+ $feed = new SimplePie();
+ $feed->set_raw_data('
+
+
+ Test Feed
+ -
+ Item with image
+
+
+
+ ');
+ $feed->init();
+ $items = $feed->get_items();
+
+ $sc = array(
+ 'feeds' => 'https://example.com/feed',
+ 'default' => '',
+ '__jobID' => 42,
+ );
+
+ $result = $this->feedzy_abstract->feedzy_retrieve_image( $items[0], $sc );
+
+ $this->assertSame( 'https://example.com/real-image.jpg', $result, 'Import jobs must still use the real item image' );
+ }
+}
diff --git a/tests/test-image-import.php b/tests/test-image-import.php
index ecfea9c5..d8c77f31 100644
--- a/tests/test-image-import.php
+++ b/tests/test-image-import.php
@@ -10,6 +10,92 @@
*/
class Test_Image_Import extends WP_UnitTestCase {
+ /**
+ * End-to-end import of a feed whose items have no images: the bundled SVG default
+ * must not be sideloaded, and the configured global fallback thumbnail must be used.
+ * See https://github.com/Codeinwp/feedzy-rss-feeds/issues/1277.
+ */
+ public function test_import_without_item_images_uses_fallback_thumbnail() {
+ // Serve a two-item, image-less feed for any HTTP request, recording requested URLs.
+ $requested_urls = array();
+ add_filter(
+ 'pre_http_request',
+ function ( $preempt, $args, $url ) use ( &$requested_urls ) {
+ $requested_urls[] = $url;
+ return array(
+ 'headers' => array( 'content-type' => 'application/rss+xml' ),
+ 'body' => '
+ No Image Feedhttp://feedzy.testt
+ - Imageless onehttp://feedzy.test/1Plain text.fz-1
+ - Imageless twohttp://feedzy.test/2Also plain.fz-2
+ ',
+ 'response' => array( 'code' => 200, 'message' => 'OK' ),
+ 'cookies' => array(),
+ 'filename' => null,
+ );
+ },
+ 10,
+ 3
+ );
+
+ // Configure a global fallback thumbnail.
+ $fallback_id = self::factory()->attachment->create_upload_object( DIR_TESTDATA . '/images/2004-07-22-DSC_0007.jpg' );
+ $settings = get_option( 'feedzy-settings', array() );
+ $settings['general']['default-thumbnail-id'] = $fallback_id;
+ update_option( 'feedzy-settings', $settings );
+
+ // Create the import job.
+ $job_id = wp_insert_post(
+ array(
+ 'post_title' => 'No image import',
+ 'post_type' => 'feedzy_imports',
+ 'post_status' => 'publish',
+ )
+ );
+ foreach ( array(
+ 'source' => 'http://feedzy.test/feed.xml',
+ 'import_post_title' => '[#item_title]',
+ 'import_post_date' => '[#item_date]',
+ 'import_post_content' => '[#item_content]',
+ 'import_post_featured_img' => '[#item_image]',
+ 'import_post_type' => 'post',
+ 'import_post_status' => 'publish',
+ 'import_use_external_image' => 'no',
+ ) as $key => $value ) {
+ update_post_meta( $job_id, $key, $value );
+ }
+
+ $attachments_before = count( get_posts( array( 'post_type' => 'attachment', 'numberposts' => -1 ) ) );
+
+ // Fresh instance so free_settings picks up the option; run outside wp-cron/AJAX,
+ // like WP-CLI based cron runners (the path that leaked the SVG).
+ $import = new Feedzy_Rss_Feeds_Import( 'feedzy-rss-feeds', '1.2.0' );
+ $import->run_cron( 100, $job_id );
+
+ $created = get_posts( array( 'post_type' => 'post', 'post_status' => 'publish', 'numberposts' => -1 ) );
+ $this->assertCount( 2, $created, 'Both feed items should be imported' );
+
+ foreach ( $created as $post ) {
+ $this->assertEquals( $fallback_id, get_post_thumbnail_id( $post->ID ), 'Imported post must use the fallback thumbnail' );
+ }
+
+ // No sideload happened: the bundled SVG (or anything else) was not uploaded.
+ $attachments_after = count( get_posts( array( 'post_type' => 'attachment', 'numberposts' => -1 ) ) );
+ $this->assertEquals( $attachments_before, $attachments_after, 'Import must not upload any image to the Media Library' );
+
+ $this->assertEmpty( get_post_meta( $job_id, 'import_errors', true ), 'Import must finish without errors' );
+
+ // No fallback image may be fetched over HTTP during the import: neither the
+ // bundled SVG nor the configured fallback attachment (hosted on this site).
+ $fallback_requests = array_filter(
+ $requested_urls,
+ function ( $url ) {
+ return false !== strpos( $url, 'feedzy.svg' ) || 0 === strpos( $url, home_url() );
+ }
+ );
+ $this->assertEmpty( $fallback_requests, 'Import must not download any fallback image for imageless items: ' . implode( ', ', $fallback_requests ) );
+ }
+
/**
* Test that the image import allows valid image URLs and logs errors for invalid ones.
* Test introduced to cover this issue https://github.com/Codeinwp/feedzy-rss-feeds/issues/917.