Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve image attributes when cropping a custom logo image #2441

Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
66f9cc5
Copy attachment's description, title, and caption over to the cropped…
anton-vlasenko Mar 16, 2022
57c95ae
Improve detection of empty strings.
anton-vlasenko Mar 17, 2022
eaadcd3
1. Rename $attachmenet to $original_attachement.
anton-vlasenko Mar 17, 2022
0701c5a
Don't call get_post_meta to retrieve _wp_attachment_image_alt option.
anton-vlasenko Mar 17, 2022
3245151
1. Don't call wp_basename if the image has custom title.
anton-vlasenko Mar 17, 2022
4c5f314
Add test template.
anton-vlasenko Mar 17, 2022
37c0724
Implement test.
anton-vlasenko Mar 18, 2022
1007a0c
1. Fix code style.
anton-vlasenko Mar 21, 2022
28b56b4
Fix the test: properly delete cropped images.
anton-vlasenko Mar 21, 2022
24128be
Remove unused variables.
anton-vlasenko Mar 22, 2022
2493ce7
Copy the post title attribute from original image.
anton-vlasenko Mar 22, 2022
4209559
Fix covers tags.
anton-vlasenko Mar 22, 2022
bb8b3f9
Fix PHPDOC description.
anton-vlasenko Mar 22, 2022
fdaa748
Don't use Yoda conditions for comparisons.
anton-vlasenko Mar 22, 2022
4a54866
Update PHPDOC block.
anton-vlasenko Mar 22, 2022
f96f9d3
Add fail messages to assertions.
anton-vlasenko Mar 22, 2022
c55674c
Second attempt to implement tear_down method.
anton-vlasenko Mar 22, 2022
854fe54
Refactoring: move $this->_setRole( 'administrator' ); to the set_up m…
anton-vlasenko Mar 22, 2022
a4cb089
Core doesn't use @return void tags.
anton-vlasenko Mar 22, 2022
412e862
Refactor @covers tag.
anton-vlasenko Mar 22, 2022
743688e
The class name should follow this convention Tests_[group]_FunctionNa…
anton-vlasenko Mar 22, 2022
a2e8c86
Don't use mb_strlen to detect empty strings.
anton-vlasenko Mar 22, 2022
a39a551
Refactor Tests_Ajax_WpAjaxCropImage::make_attachment and use the _mak…
anton-vlasenko Mar 23, 2022
5d3b37d
Move fixture methods above tests for consistency.
hellofromtonya Mar 23, 2022
de84052
Check if original title was edited
hellofromtonya Mar 23, 2022
0757d9b
Fixed incorrect detection of an "edited" state.
anton-vlasenko Mar 23, 2022
06ecc30
Improve detection of the "edited" state.
anton-vlasenko Mar 23, 2022
8982741
Add assertions for cases when post_title gets populated.
anton-vlasenko Mar 24, 2022
e6261c9
Fix the code style.
anton-vlasenko Mar 24, 2022
9af19ec
Fix code style.
anton-vlasenko Mar 24, 2022
e6be279
Improve image title check comment.
hellofromtonya Mar 24, 2022
d8a1643
Wrap conditional for consistency.
hellofromtonya Mar 24, 2022
ca8fe3a
Formatting - remove empty line.
hellofromtonya Mar 24, 2022
9e2a262
Don't call update_post_meta because the _wp_attachment_image_alt attr…
anton-vlasenko Mar 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 18 additions & 2 deletions src/wp-admin/includes/ajax-actions.php
Expand Up @@ -3975,18 +3975,34 @@ function wp_ajax_crop_image() {

$size = wp_getimagesize( $cropped );
$image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
/** @var WP_Post $original_attachment */
$original_attachment = get_post( $attachment_id );
$has_title = 0 < mb_strlen( trim( $original_attachment->post_title ) );
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
$has_description = 0 < mb_strlen( trim( $original_attachment->post_content ) );
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved

$object = array(
'post_title' => wp_basename( $cropped ),
'post_content' => $url,
// Copy the image title attribute (post_content field) from the original image.
'post_title' => $has_title ? $original_attachment->post_title : wp_basename( $cropped ),
// Copy the image description attribute (post_content field) from the original image.
'post_content' => $has_description ? $original_attachment->post_content : $url,
'post_mime_type' => $image_type,
'guid' => $url,
'context' => $context,
);

// Copy the image caption attribute (post_excerpt field) from the original image.
if ( mb_strlen( trim( $original_attachment->post_excerpt ) ) ) {
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
$object['post_excerpt'] = $original_attachment->post_excerpt;
}

$attachment_id = wp_insert_attachment( $object, $cropped );
$metadata = wp_generate_attachment_metadata( $attachment_id, $cropped );

// Copy the image alt text attribute from the original image.
if ( mb_strlen( trim( $original_attachment->_wp_attachment_image_alt ) ) ) {
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
update_post_meta( $attachment_id, '_wp_attachment_image_alt', wp_slash( $original_attachment->_wp_attachment_image_alt ) );
}

/**
* Filters the cropped image attachment metadata.
*
Expand Down
150 changes: 150 additions & 0 deletions tests/phpunit/tests/ajax/CropImage.php
@@ -0,0 +1,150 @@
<?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.
*
* @group ajax
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
*/
class Tests_Ajax_CropImage extends WP_Ajax_UnitTestCase {
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved

/**
* @covers wp_ajax_crop_image
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
*/
public function test_it_copies_metadata_from_original_image() {

// Become an administrator.
$this->_setRole( 'administrator' );
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved

$attachment = $this->create_attachment( true );
$this->prepare_post( $attachment );

// Make the request.
try {
$this->_handleAjax( 'crop-image' );
} catch ( WPAjaxDieContinueException $e ) {
}

$response = json_decode( $this->_last_response, true );
$this->validate_response( $response );

$cropped_attachment = get_post( $response['data']['id'] );
$this->assertInstanceOf( WP_Post::class, $cropped_attachment );
$this->assertNotEmpty( $attachment->post_title );
$this->assertNotEmpty( $cropped_attachment->post_title );
$this->assertSame( $attachment->post_title, $cropped_attachment->post_title );
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
$this->assertSame( $attachment->post_content, $cropped_attachment->post_content );
$this->assertSame( $attachment->post_excerpt, $cropped_attachment->post_excerpt );
$this->assertSame( $attachment->_wp_attachment_image_alt, $cropped_attachment->_wp_attachment_image_alt );

wp_delete_attachment( $attachment->ID, true );
wp_delete_attachment( $cropped_attachment->ID, true );
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @covers wp_ajax_crop_image
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
*/
public function test_it_doesnt_generate_new_metadata_if_metadata_is_empty() {
// Become an administrator.
$this->_setRole( 'administrator' );

$attachment = $this->create_attachment( false );
$this->prepare_post( $attachment );

// Make the request.
try {
$this->_handleAjax( 'crop-image' );
} catch ( WPAjaxDieContinueException $e ) {
}

$response = json_decode( $this->_last_response, true );
$this->validate_response( $response );

$cropped_attachment = get_post( $response['data']['id'] );
$this->assertInstanceOf( WP_Post::class, $cropped_attachment );
$this->assertEmpty( $attachment->post_title );
$this->assertNotEmpty( $cropped_attachment->post_title );
$this->assertStringStartsWith( 'http', $cropped_attachment->post_content );
$this->assertEmpty( $cropped_attachment->post_excerpt );
$this->assertEmpty( $cropped_attachment->_wp_attachment_image_alt );

wp_delete_attachment( $attachment->ID, true );
wp_delete_attachment( $cropped_attachment->ID, true );
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Creates an attachment.
*
* @return WP_Post
*/
private function create_attachment( $with_metadata = true ) {
$uniq_id = uniqid( 'crop-image-ajax-action-test-' );
$object = array(
'post_title' => $with_metadata ? 'Title ' . $uniq_id : '',
'post_content' => $with_metadata ? 'Description ' . $uniq_id : '',
'post_mime_type' => 'image/jpg',
'guid' => 'http://localhost/foo.jpg',
'context' => 'custom-logo',
'post_excerpt' => $with_metadata ? 'Caption ' . $uniq_id : '',
);

$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_id = wp_insert_attachment( $object, $uploaded_file );

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

return get_post( $attachment_id );
}
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved

/**
* @param array $response Response to validate.
*
* @return void
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
*/
private function validate_response( $response ) {
$this->assertArrayHasKey( 'success', $response );
$this->assertArrayHasKey( 'data', $response );
$this->assertNotEmpty( $response['data']['id'] );
}

/**
* Prepares $_POST for crop-image ajax action.
*
* @param WP_Post $attachment
*
* @return void
hellofromtonya marked this conversation as resolved.
Show resolved Hide resolved
*/
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',
);
}
}