-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Rich image editing capabilities to Gutenberg (#21024)
Co-authored-by: Marcus Kazmierczak <marcus@mkaz.com> Co-authored-by: jasmussen <joen@automattic.com>
- Loading branch information
1 parent
8f892e6
commit 87fdc2c
Showing
17 changed files
with
1,445 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | ||
'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', | ||
'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 ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ); | ||
$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; | ||
$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(); | ||
} | ||
} |
Oops, something went wrong.