Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions includes/Abilities/Image/Import_Base64_Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,29 @@ protected function meta(): array {
);
}

/**
* Creates a temporary file path for the imported image.
*
* @since x.x.x
*
* @return string|false Temporary file path, or false on failure.
*/
protected function create_temp_file() {
return wp_tempnam( 'ai-image' );
}

/**
* Gets the default file extension for a MIME type.
*
* @since x.x.x
*
* @param string $mime_type The MIME type.
* @return string|false File extension, or false when unknown.
*/
protected function get_default_extension_for_mime_type( string $mime_type ) {
return wp_get_default_extension_for_mime_type( $mime_type );
}

/**
* Imports an image from a base64 encoded string into the media library.
*
Expand Down Expand Up @@ -263,7 +286,14 @@ protected function import_image( string $data, array $args = array() ) {
}

// Create a temporary file.
$temp_file = wp_tempnam( 'ai-image' );
$temp_file = $this->create_temp_file();

if ( ! $temp_file ) {
return new WP_Error(
'temp_file_failed',
esc_html__( 'Failed to create a temporary image file.', 'ai' )
);
}

// Write the decoded data to the temporary file.
$bytes_written = file_put_contents( $temp_file, $decoded_data ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents
Expand All @@ -277,7 +307,15 @@ protected function import_image( string $data, array $args = array() ) {
}

// Determine file extension from MIME type.
$extension = wp_get_default_extension_for_mime_type( $args['mime_type'] );
$extension = $this->get_default_extension_for_mime_type( $args['mime_type'] );

if ( ! $extension ) {
wp_delete_file( $temp_file );
return new WP_Error(
'invalid_mime_type',
esc_html__( 'Could not determine the image file extension from the MIME type.', 'ai' )
);
}

/**
* Filters the base filename (without extension) used when importing an
Expand Down
138 changes: 138 additions & 0 deletions tests/Integration/Includes/Abilities/Image_ImportTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,84 @@ public function register(): void {
}
}

/**
* Testable image import ability.
*
* @since x.x.x
*/
class Testable_Image_Import extends Import {
/**
* Temporary file path to return.
*
* @var string|false|null
*/
private $temp_file = null;

/**
* Extension to return.
*
* @var string|false|null
*/
private $extension = null;

/**
* Sets the temporary file path to return.
*
* @since x.x.x
*
* @param string|false $temp_file Temporary file path, or false.
*/
public function set_temp_file( $temp_file ): void {
$this->temp_file = $temp_file;
}

/**
* Sets the extension to return.
*
* @since x.x.x
*
* @param string|false $extension Extension, or false.
*/
public function set_extension( $extension ): void {
$this->extension = $extension;
}

/**
* Calls the protected image import method.
*
* @since x.x.x
*
* @param string $data Base64 image data.
* @param array<string, mixed> $args Import arguments.
* @return array<string, mixed>|\WP_Error Attachment data, or error.
*/
public function call_import_image( string $data, array $args = array() ) {
return $this->import_image( $data, $args );
}

/**
* {@inheritDoc}
*/
protected function create_temp_file() {
if ( null !== $this->temp_file ) {
return $this->temp_file;
}

return parent::create_temp_file();
}

/**
* {@inheritDoc}
*/
protected function get_default_extension_for_mime_type( string $mime_type ) {
if ( null !== $this->extension ) {
return $this->extension;
}

return parent::get_default_extension_for_mime_type( $mime_type );
}
}

/**
* Image_Import Ability test case.
*
Expand Down Expand Up @@ -225,6 +303,66 @@ public function test_execute_callback_with_valid_data() {
$this->assertNotEmpty( $result['image']['url'], 'Image URL should not be empty' );
}

/**
* Test that import_image() returns an error when a temp file cannot be created.
*
* @since x.x.x
*/
public function test_import_image_returns_error_when_temp_file_creation_fails() {
$ability = new Testable_Image_Import(
'ai/image-import',
array(
'label' => $this->experiment->get_label(),
'description' => $this->experiment->get_description(),
)
);
$ability->set_temp_file( false );

$result = $ability->call_import_image(
$this->valid_base64_image,
array(
'filename' => 'test-image',
'title' => 'Test Image',
'description' => '',
'alt_text' => '',
'mime_type' => 'image/png',
)
);

$this->assertInstanceOf( WP_Error::class, $result, 'Result should be WP_Error' );
$this->assertEquals( 'temp_file_failed', $result->get_error_code(), 'Error code should be temp_file_failed' );
}

/**
* Test that import_image() returns an error when MIME extension cannot be determined.
*
* @since x.x.x
*/
public function test_import_image_returns_error_when_mime_extension_is_unknown() {
$ability = new Testable_Image_Import(
'ai/image-import',
array(
'label' => $this->experiment->get_label(),
'description' => $this->experiment->get_description(),
)
);
$ability->set_extension( false );

$result = $ability->call_import_image(
$this->valid_base64_image,
array(
'filename' => 'test-image',
'title' => 'Test Image',
'description' => '',
'alt_text' => '',
'mime_type' => 'image/png',
)
);

$this->assertInstanceOf( WP_Error::class, $result, 'Result should be WP_Error' );
$this->assertEquals( 'invalid_mime_type', $result->get_error_code(), 'Error code should be invalid_mime_type' );
}

/**
* Test that execute_callback() handles custom filename, title, and description.
*
Expand Down
Loading