diff --git a/composer.lock b/composer.lock index 44c77ba7..00a942a8 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "codeinwp/themeisle-sdk", - "version": "3.3.17", + "version": "3.3.18", "source": { "type": "git", "url": "https://github.com/Codeinwp/themeisle-sdk.git", - "reference": "b5698f21640d0156bd6777aa5c71e0704748755c" + "reference": "5463d7170ed7b9735223d6715442b8670a477688" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/b5698f21640d0156bd6777aa5c71e0704748755c", - "reference": "b5698f21640d0156bd6777aa5c71e0704748755c", + "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/5463d7170ed7b9735223d6715442b8670a477688", + "reference": "5463d7170ed7b9735223d6715442b8670a477688", "shasum": "" }, "require-dev": { @@ -42,9 +42,9 @@ ], "support": { "issues": "https://github.com/Codeinwp/themeisle-sdk/issues", - "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.17" + "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.18" }, - "time": "2024-04-01T07:35:31+00:00" + "time": "2024-04-05T09:47:58+00:00" } ], "packages-dev": [ diff --git a/cypress/e2e/gutenberg/gutenberg_free.js b/cypress/e2e/gutenberg/gutenberg_free.js index aa1e7d78..5cd86ba0 100644 --- a/cypress/e2e/gutenberg/gutenberg_free.js +++ b/cypress/e2e/gutenberg/gutenberg_free.js @@ -21,9 +21,9 @@ describe('Test Free - gutenberg', function() { cy.get('.edit-post-visual-editor__post-title-wrapper .editor-post-title__input').type(PREFIX); // insert a feedzy block - cy.get('div.edit-post-header__toolbar button.edit-post-header-toolbar__inserter-toggle').click({force:true}); - cy.get('.edit-post-editor__inserter-panel-content').then(function ($popup) { - cy.wrap($popup).find('.components-search-control__input').type('feedzy'); + cy.get('div.edit-post-header__toolbar button.editor-document-tools__inserter-toggle').click({force:true}); + cy.get('.editor-inserter-sidebar__content').then(function ($popup) { + cy.wrap($popup).find('.components-search-control input.components-input-control__input').type('feedzy'); cy.wrap($popup).find('.block-editor-block-types-list .editor-block-list-item-feedzy-rss-feeds-feedzy-block').should('have.length', 1); cy.wrap($popup).find('.block-editor-block-types-list .editor-block-list-item-feedzy-rss-feeds-feedzy-block').click({force:true}); }); @@ -62,7 +62,7 @@ describe('Test Free - gutenberg', function() { // item options cy.get('div.edit-post-sidebar div.components-panel__body.feedzy-advanced-options div.components-base-control.feedzy-meta input.components-text-control__input').type(gutenberg.meta, {force: true}); cy.get('div.edit-post-sidebar div.components-panel__body.feedzy-advanced-options div.components-base-control.feedzy-multiple-meta input.components-text-control__input').type(gutenberg.multiple_meta, {force: true}); - /* for pro + /* for pro cy.get('div.edit-post-sidebar div.components-panel__body.feedzy-advanced-options div.components-base-control.feedzy-include input.components-text-control__input').type(gutenberg.include); cy.get('div.edit-post-sidebar div.components-panel__body.feedzy-advanced-options div.components-base-control.feedzy-ban input.components-text-control__input').type(gutenberg.ban); */ @@ -116,7 +116,7 @@ describe('Test Free - gutenberg', function() { // item options cy.get('div.edit-post-sidebar div.components-panel__body.feedzy-advanced-options div.components-base-control.feedzy-meta input.components-text-control__input').should('have.value', gutenberg.meta); cy.get('div.edit-post-sidebar div.components-panel__body.feedzy-advanced-options div.components-base-control.feedzy-multiple-meta input.components-text-control__input').should('have.value', gutenberg.multiple_meta); - /* for pro + /* for pro cy.get('div.edit-post-sidebar div.components-panel__body.feedzy-advanced-options div.components-base-control.feedzy-include input.components-text-control__input').should('have.value', gutenberg.include); cy.get('div.edit-post-sidebar div.components-panel__body.feedzy-advanced-options div.components-base-control.feedzy-ban input.components-text-control__input').should('have.value', gutenberg.ban); */ diff --git a/includes/abstract/feedzy-rss-feeds-admin-abstract.php b/includes/abstract/feedzy-rss-feeds-admin-abstract.php index 1e7c78e9..b80d2996 100644 --- a/includes/abstract/feedzy-rss-feeds-admin-abstract.php +++ b/includes/abstract/feedzy-rss-feeds-admin-abstract.php @@ -191,10 +191,10 @@ public function get_usage_data( $data ) { */ public function feedzy_default_error_notice( $errors, $feed, $feed_url ) { global $post; - // Show the error message only if the user who has created this post (which contains the feed) is logged in. + // Show the error message only if the user who has created this post (which contains the feed) is logged in and the user has admin privileges. // Or if this is in the dry run window. // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison - $show_error = is_admin() || ( is_user_logged_in() && $post && get_current_user_id() == $post->post_author ); + $show_error = is_admin() || ( is_user_logged_in() && $post && current_user_can( 'manage_options' ) && get_current_user_id() == $post->post_author ); $error_msg = ''; if ( is_array( $errors ) ) { diff --git a/includes/admin/feedzy-rss-feeds-import.php b/includes/admin/feedzy-rss-feeds-import.php index cc4a9f78..f9e036d5 100644 --- a/includes/admin/feedzy-rss-feeds-import.php +++ b/includes/admin/feedzy-rss-feeds-import.php @@ -1977,6 +1977,25 @@ public function tryReuseExistingFeaturedImage( &$result, $title_feed, $post_id = return true; } + /** + * Will retireve the file type of a file by its URL. + * + * @param string $url The URL of the file. + * + * @return string + */ + private function get_file_type_by_url( $url ) { + $response = wp_remote_get( $url ); + + // wp_remote_retrieve_header can return an array if there are multiple headers with the same name + $content_type = wp_remote_retrieve_header( $response, 'content-type' ); + if ( is_array( $content_type ) ) { + $content_type = $content_type[0]; + } + + return $content_type; + } + /** * Downloads and sets a post featured image if possible. * @@ -1999,7 +2018,8 @@ private function try_save_featured_image( $img_source_url, $post_id, $post_title if ( ! $id ) { - if ( filter_var( $img_source_url, FILTER_VALIDATE_URL ) === false ) { + // We escape the URL to ensure that valid URLs are passed by the filter. + if ( filter_var( esc_url( $img_source_url ), FILTER_VALIDATE_URL ) === false ) { $import_errors[] = 'Invalid Featured Image URL: ' . $img_source_url; return false; } @@ -2019,7 +2039,17 @@ private function try_save_featured_image( $img_source_url, $post_id, $post_title return false; } - $type = mime_content_type( $local_file ); + $type = ''; + // try first to get the file type using the built-in function if available. + if ( function_exists( 'mime_content_type' ) ) { + $type = mime_content_type( $local_file ); + } + + // if the file type is not found, try to get it from the URL. + if ( empty( $type ) ) { + $type = $this->get_file_type_by_url( $img_source_url ); + } + // the file is downloaded with a .tmp extension // if the URL mentions the extension of the file, the upload succeeds // but if the URL is like https://source.unsplash.com/random, then the upload fails diff --git a/tests/test-image-import.php b/tests/test-image-import.php new file mode 100644 index 00000000..37616005 --- /dev/null +++ b/tests/test-image-import.php @@ -0,0 +1,62 @@ +getMethod( 'try_save_featured_image' ); + $try_save_featured_image->setAccessible( true ); + + // Check that NON-IMAGE URL returns invalid + $import_errors = array(); + $import_info = array(); + $arguments = array( 'a random string', 0, 'Post Title', &$import_errors, &$import_info, array() ); + $response = $try_save_featured_image->invokeArgs( $feedzy, $arguments ); + + $this->assertFalse( $response ); + + $this->assertTrue( count( $import_errors ) > 0 ); + $this->assertEquals( 'Invalid Featured Image URL: a random string', $import_errors[0] ); + + + // For the next test, we will use a valid URL, but the image does not exist. We will check that the error is logged and is the expected one. + add_filter( 'themeisle_log_event', function( $product, $message, $type, $file, $line ) { + if ( $type === 'error' ) { + $this->assertTrue( strpos( $message, 'Unable to download file' ) !== false ); + } + }, 10, 5 ); + + $import_errors = array(); + $import_info = array(); + $arguments = array( 'https://example.com/path_to_image/image.jpeg', 0, 'Post Title', &$import_errors, &$import_info, array() ); + $response = $try_save_featured_image->invokeArgs( $feedzy, $arguments ); + + // expected response is false because the image does not exist, but the URL is valid so no $import_errors should be set. + $this->assertFalse( $response ); + $this->assertTrue( empty( $import_errors ) ); + + $import_errors = array(); + $import_info = array(); + $arguments = array( 'https://example.com/path_to_image/image w space in name.jpeg', 0, 'Post Title', &$import_errors, &$import_info, array() ); + $response = $try_save_featured_image->invokeArgs( $feedzy, $arguments ); + + // expected response is false because the image does not exist, but the URL is valid so no $import_errors should be set. + $this->assertFalse( $response ); + $this->assertTrue( empty( $import_errors ) ); + } +} diff --git a/tests/test-import.php b/tests/test-import.php index 7fd6a6d4..38a9f204 100644 --- a/tests/test-import.php +++ b/tests/test-import.php @@ -10,6 +10,8 @@ */ class Test_Feedzy_Import extends WP_UnitTestCase { + private $import_limit = 1; + /** * Sets up the test methods. */ @@ -28,10 +30,10 @@ public function setUp(): void { * @access public * @dataProvider importDataProvider */ - public function test_feedzy_imports( $random_name1, $random_name2, $urls, $magic_tags = '[#item_content]', $use_filter = false ) { + public function test_feedzy_imports( $random_name1, $random_name2, $urls, $magic_tags = '[#item_content]', $use_filter = false, $type = 'post' ) { do_action( 'init' ); - $num_items = 1; + $num_items = $this->import_limit; $user_id = $this->factory->user->create( array( 'role' => 'administrator', @@ -76,7 +78,7 @@ public function test_feedzy_imports( $random_name1, $random_name2, $urls, $magic $_POST['feedzy_post_nonce'] = wp_create_nonce( 'feedzy_post_nonce' ); $_POST['post_type'] = 'feedzy_imports'; $_POST['feedzy_meta_data']['source'] = $slug; - $_POST['feedzy_meta_data']['import_post_type'] = 'post'; + $_POST['feedzy_meta_data']['import_post_type'] = $type; $_POST['feedzy_meta_data']['import_post_term'] = 'category_' . $category_id; $_POST['feedzy_meta_data']['import_post_status'] = 'publish'; $_POST['feedzy_meta_data']['inc_key'] = ''; @@ -106,7 +108,7 @@ public function test_feedzy_imports( $random_name1, $random_name2, $urls, $magic $import_custom_fields = get_post_meta( $p->ID, 'imports_custom_fields', true ); $import_feed_limit = get_post_meta( $p->ID, 'import_feed_limit', true ); - $this->assertEquals( 'post', $import_post_type ); + $this->assertEquals( $type, $import_post_type ); $this->assertEquals( 'category_' . $category_id, $import_post_term ); $this->assertEquals( 'publish', $import_post_status ); $this->assertEquals( $slug, $source ); @@ -174,6 +176,13 @@ public function test_feedzy_imports( $random_name1, $random_name2, $urls, $magic do_action( 'feedzy_cron', '1' ); + /** + * We bail for non post types as the subsequent tests might not apply. + */ + if ( $type !== 'post' ) { + return; + } + $created = get_posts( array( 'numberposts' => $num_items, @@ -276,6 +285,24 @@ public function test_canonical_url( $post ) { } + /** + * Test the attachment import works and the mime type is correct. + */ + public function test_attachement_import() { + $this->test_feedzy_imports( $this->get_rand_name(), $this->get_rand_name(), $this->get_two_rand_feeds(), '[#item_content]', false, 'attachment' ); + $args = array( + 'post_type' =>'attachment', + 'numberposts' => -1, + 'orderby' => 'date', + 'order' => 'DESC', + ); + $attachments = get_posts($args); + $this->assertEquals( $this->import_limit, count( $attachments ) ); + + $this->assertTrue( isset( $attachments[0]->post_mime_type ) ); + $this->assertTrue( $attachments[0]->post_mime_type === 'image/jpeg' ); + } + /** * Utility method to generate a random 5 char string. diff --git a/tests/test-post-access.php b/tests/test-post-access.php index 15fc1532..4c019f5c 100644 --- a/tests/test-post-access.php +++ b/tests/test-post-access.php @@ -62,4 +62,23 @@ public function test_custom_post_access() { } + public function test_contributor_user_with_errors() { + $feedzy = new Feedzy_Rss_Feeds_Admin('feedzy', 'latest'); + $contributor_id = $this->factory->user->create( + array( + 'role' => 'contributor', + ) + ); + wp_set_current_user( $contributor_id ); + $post_id = $this->factory->post->create( array( 'post_author' => get_current_user_id() ) ); + $GLOBALS['post'] = get_post( $post_id ); + // Mock feed object and errors. + $feed = (object) array( 'multifeed_url' => array( 'http://example.com/feed' ) ); + $errors = array( 'Error 1' ); + + + $actual_output = $feedzy->feedzy_default_error_notice( $errors, $feed, 'http://example.com/feed' ); + + $this->assertEquals( '', $actual_output ); + } }