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

Add Rich image editing capabilities to Gutenberg #21024

Merged
merged 53 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
5e8769b
Init commit of adding rich image editing to Gutenberg
mkaz Mar 4, 2020
720e57f
Remove image filters
ajlende Mar 16, 2020
62e168d
Replace custom notice with snackbar
ajlende Mar 16, 2020
f3656b1
Add react-easy-crop
ajlende Mar 17, 2020
03025d5
Move dropdowns to open down and right
ajlende Mar 17, 2020
baab84e
Center crop icon
ajlende Mar 17, 2020
cc476db
Replace image cropping tool
ajlende Mar 18, 2020
11a940a
Add crop aspect ratios
ajlende Mar 18, 2020
4d79072
Keep original image aspect ratio when cropping
ajlende Mar 19, 2020
cc32ad5
Clean up cropper functions
ajlende Mar 19, 2020
5366dfd
Remove error_log for debug
ajlende Mar 19, 2020
c07405c
Fix sending percentages for cropping
ajlende Mar 19, 2020
a1bb170
Add range control for zoom
ajlende Mar 19, 2020
9c895cd
Finish removing filters
ajlende Mar 19, 2020
d372d3c
Remove ReactCrop CSS
ajlende Mar 19, 2020
b96eeef
Style full width rather than natural width
ajlende Mar 19, 2020
a1cf03e
Fix package.json and lockfile
ajlende Mar 20, 2020
6db2f6c
Remove more filter scss
ajlende Mar 20, 2020
d5c73ee
Remove unused SCSS variables
ajlende Mar 20, 2020
0de6203
Name zoom constants
ajlende Mar 20, 2020
da7c016
Remove redundant inProgress
ajlende Mar 20, 2020
c35716d
Fix missing block light wrapper for crop
ajlende Mar 24, 2020
94e090c
Add minimal styling for zoom control
ajlende Mar 30, 2020
43a5b67
Replace in progress snackbar with spinner
ajlende Mar 30, 2020
0a382f0
Refactor toolbar and fix spacing
ajlende Mar 30, 2020
3a821d0
Refactor crop toolbar
ajlende Mar 30, 2020
b200192
Add back wrapper div
ajlende Mar 30, 2020
24c763a
Remove unused attributes
ajlende May 4, 2020
1f780dd
Merge branch 'master' into try/13748-rich-image
ajlende May 15, 2020
cd318eb
Update react-easy-crop to 3.0.0
ajlende May 15, 2020
cb46a13
Merge branch 'master' into try/13748-rich-image
ajlende May 18, 2020
4b4037e
Rename files for phpcs
ajlende May 19, 2020
401f705
Rename classes for phpcs
ajlende May 19, 2020
ef8ac13
Refactor image editor controller to match other controllers
ajlende May 19, 2020
284f16f
Convert bracket syntax to array
ajlende May 19, 2020
2340e58
Fix spacing in associative arrays
ajlende May 19, 2020
4a320a9
Add full stops to comments
ajlende May 19, 2020
96d7bd8
Rename register image editor
ajlende May 19, 2020
65b4638
Refactor get_all_modifiers
ajlende May 19, 2020
ff50131
Refactor to create only one image editor
ajlende May 19, 2020
91f5825
Fix assignment spacing for phpcs
ajlende May 19, 2020
f2cf507
Fix yoda conditions for phpcs
ajlende May 19, 2020
8843284
Refactor permission_callback to return a WP_Error
ajlende May 19, 2020
fe7411d
Refactor route args
ajlende May 19, 2020
edaef9d
Add documentation for phpcs
ajlende May 20, 2020
d318808
Simplify apply_to_image to only modify the image
ajlende May 20, 2020
3c9246e
Remove unused variable
ajlende May 20, 2020
16b0d54
Center the spinner.
jasmussen May 21, 2020
27f77de
Make rich image phase 2
ajlende May 21, 2020
6066802
Remove rich image from cover block
ajlende May 24, 2020
674d40d
Merge branch 'master' into try/13748-rich-image
ajlende Jun 1, 2020
fc648d8
Fix for new prettier version
ajlende Jun 2, 2020
4006c43
Merge branch 'master' into try/13748-rich-image
ajlende Jun 2, 2020
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
184 changes: 184 additions & 0 deletions lib/class-wp-rest-image-editor-controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<?php
/**
* Start: Include for phase 2
* REST API: WP_REST_Menus_Controller class
*
* @package WordPress
* @subpackage REST_API
*/

/**
* Image editor
*/
include_once __DIR__ . '/image-editor/class-image-editor.php';

/**
* Controller which provides REST API endpoints for image editing.
*
* @since 7.x ?
*
* @see WP_REST_Controller
*/
class WP_REST_Image_Editor_Controller extends WP_REST_Controller {

/**
* Constructs the controller.
*
* @since 7.x ?
* @access public
*/
public function __construct() {
$this->namespace = '__experimental';
$this->rest_base = '/richimage/(?P<mediaID>[\d]+)';
$this->editor = new Image_Editor();
}

/**
* Registers the necessary REST API routes.
*
* @since 7.x ?
* @access public
*/
public function register_routes() {
register_rest_route(
$this->namespace,
$this->rest_base . '/rotate',
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'rotate_image' ),
'permission_callback' => array( $this, 'permission_callback' ),
'args' => array(
'angle' => array(
'type' => 'integer',
'required' => true,
),
),
),
)
);

register_rest_route(
$this->namespace,
$this->rest_base . '/flip',
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'flip_image' ),
'permission_callback' => array( $this, 'permission_callback' ),
'args' => array(
'direction' => array(
'type' => 'enum',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum isn't a type, it should instead be string.

'enum' => array( 'vertical', 'horizontal' ),
'required' => true,
),
),
),
)
);

register_rest_route(
$this->namespace,
$this->rest_base . '/crop',
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'crop_image' ),
'permission_callback' => array( $this, 'permission_callback' ),
'args' => array(
'cropX' => array(
'type' => 'float',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

float is not a json schema type, it should be number.

'minimum' => 0,
'required' => true,
),
'cropY' => array(
'type' => 'float',
'minimum' => 0,
'required' => true,
),
'cropWidth' => array(
'type' => 'float',
'minimum' => 1,
'required' => true,
),
'cropHeight' => array(
'type' => 'float',
'minimum' => 1,
'required' => true,
),
),
),
)
);
}

/**
* Checks if the user has permissions to make the request.
*
* @since 7.x ?
* @access public
*
* @param WP_REST_Request $request Full details about the request.
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
*/
public function permission_callback( $request ) {
$params = $request->get_params();

if ( ! current_user_can( 'edit_post', $params['mediaID'] ) ) {
return new WP_Error( 'rest_cannot_edit_image', __( 'Sorry, you are not allowed to edit images.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) );
}

return true;
}

/**
* Rotates an image.
*
* @since 7.x ?
* @access public
*
* @param WP_REST_Request $request Full details about the request.
* @return array|WP_Error If successful image JSON for the modified image, otherwise a WP_Error.
*/
public function rotate_image( $request ) {
$params = $request->get_params();

$modifier = new Image_Editor_Rotate( $params['angle'] );

return $this->editor->modify_image( $params['mediaID'], $modifier );
}

/**
* Flips/mirrors an image.
*
* @since 7.x ?
* @access public
*
* @param WP_REST_Request $request Full details about the request.
* @return array|WP_Error If successful image JSON for the modified image, otherwise a WP_Error.
*/
public function flip_image( $request ) {
$params = $request->get_params();

$modifier = new Image_Editor_Flip( $params['direction'] );

return $this->editor->modify_image( $params['mediaID'], $modifier );
}

/**
* Crops an image.
*
* @since 7.x ?
* @access public
*
* @param WP_REST_Request $request Full details about the request.
* @return array|WP_Error If successful image JSON for the modified image, otherwise a WP_Error.
*/
public function crop_image( $request ) {
$params = $request->get_params();

$modifier = new Image_Editor_Crop( $params['cropX'], $params['cropY'], $params['cropWidth'], $params['cropHeight'] );

return $this->editor->modify_image( $params['mediaID'], $modifier );
}
}
126 changes: 126 additions & 0 deletions lib/image-editor/class-image-editor-crop.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php
/**
* Start: Include for phase 2
* Image Editor: Image_Editor_Crop class
*
* @package gutenberg
* @since 7.x ?
*/

/**
* Crop image modifier.
*/
class Image_Editor_Crop extends Image_Editor_Modifier {
/**
* Pixels from the left for the crop.
*
* @var integer
*/
private $crop_x = 0;

/**
* Pixels from the top for the crop.
*
* @var integer
*/
private $crop_y = 0;

/**
* Width in pixels for the crop.
*
* @var integer
*/
private $width = 0;

/**
* Height in pixels for the crop.
*
* @var integer
*/
private $height = 0;

/**
* Constructor.
*
* Will populate object properties from the provided arguments.
*
* @param integer $crop_x Pixels from the left for the crop.
* @param integer $crop_y Pixels from the top for the crop.
* @param integer $width Width in pixels for the crop.
* @param integer $height Height in pixels for the crop.
*/
public function __construct( $crop_x, $crop_y, $width, $height ) {
$this->crop_x = floatval( $crop_x );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we using floatval is the types are documented as integer? Which is correct?

$this->crop_y = floatval( $crop_y );
$this->width = floatval( $width );
$this->height = floatval( $height );
}

/**
* Update the image metadata with the modifier.
*
* @access public
*
* @param array $meta Metadata to update.
* @return array Updated metadata.
*/
public function apply_to_meta( $meta ) {
$meta['cropX'] = $this->crop_x;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these based off an existing pattern that means these need to be camelCase? Core uses snake_case in PHP land.

$meta['cropY'] = $this->crop_y;
$meta['cropWidth'] = $this->width;
$meta['cropHeight'] = $this->height;

return $meta;
}

/**
* Apply the modifier to the image
*
* @access public
*
* @param WP_Image_Editor $image Image editor.
* @return bool|WP_Error True on success, WP_Error object or false on failure.
*/
public function apply_to_image( $image ) {
$size = $image->get_size();

$crop_x = round( ( $size['width'] * $this->crop_x ) / 100.0 );
$crop_y = round( ( $size['height'] * $this->crop_y ) / 100.0 );
$width = round( ( $size['width'] * $this->width ) / 100.0 );
$height = round( ( $size['height'] * $this->height ) / 100.0 );

return $image->crop( $crop_x, $crop_y, $width, $height );
}

/**
* Gets the new filename based on metadata.
*
* @access public
*
* @param array $meta Image metadata.
* @return string Filename for the edited image.
*/
public static function get_filename( $meta ) {
if ( isset( $meta['cropWidth'] ) && $meta['cropWidth'] > 0 ) {
$target_file = sprintf( 'crop-%d-%d-%d-%d', round( $meta['cropX'], 2 ), round( $meta['cropY'], 2 ), round( $meta['cropWidth'], 2 ), round( $meta['cropHeight'], 2 ) );

// We need to change the original name to include the crop. This way if it's cropped again we won't clash.
$meta['original_name'] = $target_file;

return $target_file;
}

return false;
}

/**
* Gets the default metadata for the crop modifier.
*
* @access public
*
* @return array Default metadata.
*/
public static function get_default_meta() {
return array();
}
}
Loading