Skip to content
Open
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
9 changes: 7 additions & 2 deletions src/wp-admin/includes/image-edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,10 @@ function wp_stream_image( $image, $mime_type, $attachment_id ) {
* @param int $attachment_id The attachment post ID.
*/
$image = apply_filters( 'image_editor_save_pre', $image, $attachment_id );

// Get the mine if we can aftert eh filter in case the image got changed.
if ( method_exists( $image, 'get_mime_type' ) ) {
$mime_type = $image->get_mime_type();
}
if ( is_wp_error( $image->stream( $mime_type ) ) ) {
return false;
}
Expand Down Expand Up @@ -787,7 +790,9 @@ function stream_preview_image( $post_id ) {
return false;
}

return wp_stream_image( $img, $post->post_mime_type, $post_id );
$mime_type = $post->post_mime_type;

return wp_stream_image( $img, $mime_type, $post_id );
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/class-wp-image-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ protected function make_image( $filename, $callback, $arguments ) {
* @param string $extension
* @return string|false
*/
protected static function get_mime_type( $extension = null ) {
public static function get_mime_type( $extension = null ) {
if ( ! $extension ) {
return false;
}
Expand Down
176 changes: 176 additions & 0 deletions tests/phpunit/tests/ajax/wpAjaxImgeditPreview.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

/**
* Admin Ajax functions to be tested.
*/
require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';

/**
* Class for testing ajax crop image functionality.
*
*
* @covers ::wp_ajax_crop_image
*/
class Tests_Ajax_WpAjaxImgeditPreview extends WP_Ajax_UnitTestCase {

/**
* @var WP_Post|null
*/
private $attachment;

/**
* @var WP_Post|null
*/
private $cropped_attachment;

protected static $attachment_id;

public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
require_once ABSPATH . 'wp-admin/includes/image-edit.php';
self::$attachment_id = $factory->attachment->create_upload_object( DIR_TESTDATA . '/images/test-image.jpg' );
}

public static function wpTearDownAfterClass() {
wp_delete_attachment( self::$attachment_id, true );
}

public function set_up() {
parent::set_up();

// Become an administrator.
$this->_setRole( 'administrator' );
}

public function tear_down() {
if ( $this->attachment instanceof WP_Post ) {
wp_delete_attachment( $this->attachment->ID, true );
}

if ( $this->cropped_attachment instanceof WP_Post ) {
wp_delete_attachment( $this->cropped_attachment->ID, true );
}
$this->attachment = null;
$this->cropped_attachment = null;

parent::tear_down();
}

/**
* Tests that attachment properties are copied over to the cropped image.
*
* @group ajax
*
* @ticket 37750
*/
public function test_stream_preview_image() {
$this->attachment = $this->make_attachment( true );
$this->prepare_post( $this->attachment );
$this->_setRole( 'editor' );

$_GET = wp_parse_args(
array(
'action' => 'imgedit-preview',
'_ajax_nonce' => wp_create_nonce( 'image_editor-' . $this->attachment->ID ),
'postid' => $this->attachment->ID,
'rand' => random_int( 1, 1000 ),
)
);

try {
$this->_handleAjax( 'imgedit-preview' );
} catch ( WPAjaxDieContinueException $e ) {
}

$this->assertStringStartsWith( "\xFF\xD8", $this->_last_response );
}

/**
* Test stream_preview_image function with non-image attachment.
*/
public function test_stream_preview_image_non_image() {
$attachment_id = self::factory()->attachment->create_upload_object( DIR_TESTDATA . '/functions/dummy.txt' );

$result = stream_preview_image( $attachment_id );

$this->assertFalse( $result );

wp_delete_attachment( $attachment_id, true );
}


/**
* Test stream_preview_image function with invalid post ID.
*/
public function test_stream_preview_image_invalid_id() {
$result = stream_preview_image( 0 );

$this->assertFalse( $result );
}

/**
* Creates an attachment.
*
* @return WP_Post
*/
private function make_attachment( $with_metadata = true ) {
$uniq_id = uniqid( 'crop-image-ajax-action-test-' );

$test_file = DIR_TESTDATA . '/images/test-image.jpg';
$upload_directory = wp_upload_dir();
$uploaded_file = $upload_directory['path'] . '/' . $uniq_id . '.jpg';
$filesystem = new WP_Filesystem_Direct( true );
$filesystem->copy( $test_file, $uploaded_file );

$attachment_data = array(
'file' => $uploaded_file,
'type' => 'image/jpg',
'url' => 'http://localhost/foo.jpg',
);

$attachment_id = $this->_make_attachment( $attachment_data );
$post_data = array(
'ID' => $attachment_id,
'post_title' => $with_metadata ? 'Title ' . $uniq_id : '',
'post_content' => $with_metadata ? 'Description ' . $uniq_id : '',
'context' => 'custom-logo',
'post_excerpt' => $with_metadata ? 'Caption ' . $uniq_id : '',
);

// Update the post because _make_attachment method doesn't support these arguments.
wp_update_post( $post_data );

if ( $with_metadata ) {
update_post_meta( $attachment_id, '_wp_attachment_image_alt', wp_slash( 'Alt ' . $uniq_id ) );
}

return get_post( $attachment_id );
}

/**
* Prepares $_POST for crop-image ajax action.
*
* @param WP_Post $attachment
*/
private function prepare_post( WP_Post $attachment ) {
$_POST = array(
'wp_customize' => 'on',
'nonce' => wp_create_nonce( 'image_editor-' . $attachment->ID ),
'id' => $attachment->ID,
'context' => 'custom_logo',
'cropDetails' =>
array(
'x1' => '0',
'y1' => '0',
'x2' => '100',
'y2' => '100',
'width' => '100',
'height' => '100',
'dst_width' => '100',
'dst_height' => '100',
),
'action' => 'crop-image',
);
}
}
120 changes: 120 additions & 0 deletions tests/phpunit/tests/image/streamPreviewImage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

/**
* @group image
* @group media
* @group upload
*/
class StreamPreviewImage extends WP_UnitTestCase {
protected static $attachment_id;

public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
require_once ABSPATH . 'wp-admin/includes/image-edit.php';
self::$attachment_id = $factory->attachment->create_upload_object( DIR_TESTDATA . '/images/test-image.jpg' );
}

public static function wpTearDownAfterClass() {
wp_delete_attachment( self::$attachment_id, true );
}

/**
* Test stream_preview_image function with invalid post ID.
*/
public function test_stream_preview_image_invalid_id() {
$result = stream_preview_image( 0 );

$this->assertFalse( $result );
}

/**
* Test stream_preview_image function with non-image attachment.
*/
public function test_stream_preview_image_non_image() {
$attachment_id = self::factory()->attachment->create_upload_object( DIR_TESTDATA . '/functions/dummy.txt' );

$result = stream_preview_image( $attachment_id );

$this->assertFalse( $result );

wp_delete_attachment( $attachment_id, true );
}

/**
* Test stream_preview_image function with error.
*/
public function test_stream_preview_image() {
$attachment_id = self::$attachment_id;
$mime_type = 'image/jpeg';

// Get the image editor.
$image_path = get_attached_file( $attachment_id );
$image = wp_get_image_editor( $image_path );

if ( is_wp_error( $image ) ) {
$this->markTestSkipped( 'WP_Image_Editor not available.' );
}

// Create a custom mock class that extends the actual WP_Image_Editor class.
$mock_class = new class( $image_path ) extends WP_Image_Editor_Imagick {

public function __construct( $image_path ) {
parent::__construct( $image_path );
$this->load();
}

// Mocked method to stream image content.
//header( "Content-Type: $mime_type" );
//print $this->image->getImageBlob();
public function stream( $mime_type = null ) {
list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type );

try {
// Temporarily change format for stream.
$this->image->setImageFormat( strtoupper( $extension ) );

// Output stream of image content.
//header( "Content-Type: $mime_type" );
print $this->image->getImageBlob();

// Reset image to original format.
$this->image->setImageFormat( $this->get_extension( $this->mime_type ) );
} catch ( Exception $e ) {
return new WP_Error( 'image_stream_error', $e->getMessage() );
}
}

};

// Apply the filter to replace the image editor with our mock.
add_filter(
'image_editor_save_pre',
function ( $img ) use ( $mock_class ) {
return $mock_class;
}
);
// Backup the current output buffering level
$ob_level = ob_get_level();
// Capture the output.
ob_start();
$result = wp_stream_image( $mock_class, $mime_type, $attachment_id );
// Get the output
$output = ob_get_clean();

// Restore output buffering to its original state
while ( ob_get_level() > $ob_level ) {
ob_end_clean();
}

// Assert that the function returned true.
$this->assertTrue( $result );

// Assert that output is not empty
$this->assertNotEmpty( $output );

// Assert that the output starts with the JPEG file signature
$this->assertStringStartsWith( "\xFF\xD8", $output );

// Remove the filter.
remove_all_filters( 'image_editor_save_pre' );
}
}
Loading