Make WordPress Core

Changeset 50124


Ignore:
Timestamp:
02/01/2021 06:35:38 PM (6 years ago)
Author:
antpb
Message:

REST API, Media: Add batch image editing endpoints.

Introduces new endpoints to allow for batch image editing using the REST API.

The new endpoints can take an array of modifiers that will be applied in the order they appear.

Props ajlende, TimothyBlynJacobs, hellofromTonya, Mista-Flo.
Fixes #52192.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php

    r49731 r50124  
    449449                }
    450450
    451                 // Check if we need to do anything.
    452                 $rotate = 0;
    453                 $crop   = false;
    454 
    455                 if ( ! empty( $request['rotation'] ) ) {
    456                         // Rotation direction: clockwise vs. counter clockwise.
    457                         $rotate = 0 - (int) $request['rotation'];
    458                 }
    459 
    460                 if ( isset( $request['x'], $request['y'], $request['width'], $request['height'] ) ) {
    461                         $crop = true;
    462                 }
    463 
    464                 if ( ! $rotate && ! $crop ) {
    465                         return new WP_Error(
    466                                 'rest_image_not_edited',
    467                                 __( 'The image was not edited. Edit the image before applying the changes.' ),
    468                                 array( 'status' => 400 )
    469                         );
     451                // The `modifiers` param takes precedence over the older format.
     452                if ( isset( $request['modifiers'] ) ) {
     453                        $modifiers = $request['modifiers'];
     454                } else {
     455                        $modifiers = array();
     456
     457                        if ( ! empty( $request['rotation'] ) ) {
     458                                $modifiers[] = array(
     459                                        'type' => 'rotate',
     460                                        'args' => array(
     461                                                'angle' => $request['rotation'],
     462                                        ),
     463                                );
     464                        }
     465
     466                        if ( isset( $request['x'], $request['y'], $request['width'], $request['height'] ) ) {
     467                                $modifiers[] = array(
     468                                        'type' => 'crop',
     469                                        'args' => array(
     470                                                'left'   => $request['x'],
     471                                                'top'    => $request['y'],
     472                                                'width'  => $request['width'],
     473                                                'height' => $request['height'],
     474                                        ),
     475                                );
     476                        }
     477
     478                        if ( 0 === count( $modifiers ) ) {
     479                                return new WP_Error(
     480                                        'rest_image_not_edited',
     481                                        __( 'The image was not edited. Edit the image before applying the changes.' ),
     482                                        array( 'status' => 400 )
     483                                );
     484                        }
    470485                }
    471486
     
    490505                }
    491506
    492                 if ( 0 !== $rotate ) {
    493                         $result = $image_editor->rotate( $rotate );
    494 
    495                         if ( is_wp_error( $result ) ) {
    496                                 return new WP_Error(
    497                                         'rest_image_rotation_failed',
    498                                         __( 'Unable to rotate this image.' ),
    499                                         array( 'status' => 500 )
    500                                 );
    501                         }
    502                 }
    503 
    504                 if ( $crop ) {
    505                         $size = $image_editor->get_size();
    506 
    507                         $crop_x = round( ( $size['width'] * (float) $request['x'] ) / 100.0 );
    508                         $crop_y = round( ( $size['height'] * (float) $request['y'] ) / 100.0 );
    509                         $width  = round( ( $size['width'] * (float) $request['width'] ) / 100.0 );
    510                         $height = round( ( $size['height'] * (float) $request['height'] ) / 100.0 );
    511 
    512                         $result = $image_editor->crop( $crop_x, $crop_y, $width, $height );
    513 
    514                         if ( is_wp_error( $result ) ) {
    515                                 return new WP_Error(
    516                                         'rest_image_crop_failed',
    517                                         __( 'Unable to crop this image.' ),
    518                                         array( 'status' => 500 )
    519                                 );
     507                foreach ( $modifiers as $modifier ) {
     508                        $args = $modifier['args'];
     509                        switch ( $modifier['type'] ) {
     510                                case 'rotate':
     511                                        // Rotation direction: clockwise vs. counter clockwise.
     512                                        $rotate = 0 - $args['angle'];
     513
     514                                        if ( 0 !== $rotate ) {
     515                                                $result = $image_editor->rotate( $rotate );
     516
     517                                                if ( is_wp_error( $result ) ) {
     518                                                        return new WP_Error(
     519                                                                'rest_image_rotation_failed',
     520                                                                __( 'Unable to rotate this image.' ),
     521                                                                array( 'status' => 500 )
     522                                                        );
     523                                                }
     524                                        }
     525
     526                                        break;
     527
     528                                case 'crop':
     529                                        $size = $image_editor->get_size();
     530
     531                                        $crop_x = round( ( $size['width'] * $args['left'] ) / 100.0 );
     532                                        $crop_y = round( ( $size['height'] * $args['top'] ) / 100.0 );
     533                                        $width  = round( ( $size['width'] * $args['width'] ) / 100.0 );
     534                                        $height = round( ( $size['height'] * $args['height'] ) / 100.0 );
     535
     536                                        if ( $size['width'] !== $width && $size['height'] !== $height ) {
     537                                                $result = $image_editor->crop( $crop_x, $crop_y, $width, $height );
     538
     539                                                if ( is_wp_error( $result ) ) {
     540                                                        return new WP_Error(
     541                                                                'rest_image_crop_failed',
     542                                                                __( 'Unable to crop this image.' ),
     543                                                                array( 'status' => 500 )
     544                                                        );
     545                                                }
     546                                        }
     547
     548                                        break;
     549
    520550                        }
    521551                }
     
    12871317        protected function get_edit_media_item_args() {
    12881318                return array(
    1289                         'rotation' => array(
    1290                                 'description'      => __( 'The amount to rotate the image clockwise in degrees.' ),
     1319                        'src'       => array(
     1320                                'description' => __( 'URL to the edited image file.' ),
     1321                                'type'        => 'string',
     1322                                'format'      => 'uri',
     1323                                'required'    => true,
     1324                        ),
     1325                        'modifiers' => array(
     1326                                'description' => __( 'Array of image edits.' ),
     1327                                'type'        => 'array',
     1328                                'minItems'    => 1,
     1329                                'items'       => array(
     1330                                        'description' => __( 'Image edit.' ),
     1331                                        'type'        => 'object',
     1332                                        'required'    => array(
     1333                                                'type',
     1334                                                'args',
     1335                                        ),
     1336                                        'oneOf'       => array(
     1337                                                array(
     1338                                                        'title'       => __( 'Rotation' ),
     1339                                                        'properties'  => array(
     1340                                                                'type' => array(
     1341                                                                        'description' => __( 'Rotation type.' ),
     1342                                                                        'type'        => 'string',
     1343                                                                        'enum'        => array( 'rotate' ),
     1344                                                                ),
     1345                                                                'args' => array(
     1346                                                                        'description' => __( 'Rotation arguments.' ),
     1347                                                                        'type'        => 'object',
     1348                                                                        'required'    => array(
     1349                                                                                'angle',
     1350                                                                        ),
     1351                                                                        'properties'  => array(
     1352                                                                                'angle' => array(
     1353                                                                                        'description' => __( 'Angle to rotate clockwise in degrees.' ),
     1354                                                                                        'type'        => 'number',
     1355                                                                                ),
     1356                                                                        ),
     1357                                                                ),
     1358                                                        ),
     1359                                                ),
     1360                                                array(
     1361                                                        'title'       => __( 'Crop' ),
     1362                                                        'properties'  => array(
     1363                                                                'type' => array(
     1364                                                                        'description' => __( 'Crop type.' ),
     1365                                                                        'type'        => 'string',
     1366                                                                        'enum'        => array( 'crop' ),
     1367                                                                ),
     1368                                                                'args' => array(
     1369                                                                        'description' => __( 'Crop arguments.' ),
     1370                                                                        'type'        => 'object',
     1371                                                                        'required'    => array(
     1372                                                                                'left',
     1373                                                                                'top',
     1374                                                                                'width',
     1375                                                                                'height',
     1376                                                                        ),
     1377                                                                        'properties'  => array(
     1378                                                                                'left'   => array(
     1379                                                                                        'description' => __( 'Horizontal position from the left to begin the crop as a percentage of the image width.' ),
     1380                                                                                        'type'        => 'number',
     1381                                                                                ),
     1382                                                                                'top'    => array(
     1383                                                                                        'description' => __( 'Vertical position from the top to begin the crop as a percentage of the image height.' ),
     1384                                                                                        'type'        => 'number',
     1385                                                                                ),
     1386                                                                                'width'  => array(
     1387                                                                                        'description' => __( 'Width of the crop as a percentage of the image width.' ),
     1388                                                                                        'type'        => 'number',
     1389                                                                                ),
     1390                                                                                'height' => array(
     1391                                                                                        'description' => __( 'Height of the crop as a percentage of the image height.' ),
     1392                                                                                        'type'        => 'number',
     1393                                                                                ),
     1394                                                                        ),
     1395                                                                ),
     1396                                                        ),
     1397                                                ),
     1398                                        ),
     1399                                ),
     1400                        ),
     1401                        'rotation'  => array(
     1402                                'description'      => __( 'The amount to rotate the image clockwise in degrees. DEPRECATED: Use `modifiers` instead.' ),
    12911403                                'type'             => 'integer',
    12921404                                'minimum'          => 0,
     
    12951407                                'exclusiveMaximum' => true,
    12961408                        ),
    1297                         'x'        => array(
    1298                                 'description' => __( 'As a percentage of the image, the x position to start the crop from.' ),
     1409                        'x'         => array(
     1410                                'description' => __( 'As a percentage of the image, the x position to start the crop from. DEPRECATED: Use `modifiers` instead.' ),
    12991411                                'type'        => 'number',
    13001412                                'minimum'     => 0,
    13011413                                'maximum'     => 100,
    13021414                        ),
    1303                         'y'        => array(
    1304                                 'description' => __( 'As a percentage of the image, the y position to start the crop from.' ),
     1415                        'y'         => array(
     1416                                'description' => __( 'As a percentage of the image, the y position to start the crop from. DEPRECATED: Use `modifiers` instead.' ),
    13051417                                'type'        => 'number',
    13061418                                'minimum'     => 0,
    13071419                                'maximum'     => 100,
    13081420                        ),
    1309                         'width'    => array(
    1310                                 'description' => __( 'As a percentage of the image, the width to crop the image to.' ),
     1421                        'width'     => array(
     1422                                'description' => __( 'As a percentage of the image, the width to crop the image to. DEPRECATED: Use `modifiers` instead.' ),
    13111423                                'type'        => 'number',
    13121424                                'minimum'     => 0,
    13131425                                'maximum'     => 100,
    13141426                        ),
    1315                         'height'   => array(
    1316                                 'description' => __( 'As a percentage of the image, the height to crop the image to.' ),
     1427                        'height'    => array(
     1428                                'description' => __( 'As a percentage of the image, the height to crop the image to. DEPRECATED: Use `modifiers` instead.' ),
    13171429                                'type'        => 'number',
    13181430                                'minimum'     => 0,
    13191431                                'maximum'     => 100,
    13201432                        ),
    1321                         'src'      => array(
    1322                                 'description' => __( 'URL to the edited image file.' ),
    1323                                 'type'        => 'string',
    1324                                 'format'      => 'uri',
    1325                                 'required'    => true,
    1326                         ),
    13271433                );
    13281434        }
  • trunk/tests/phpunit/tests/rest-api/rest-attachments-controller.php

    r50024 r50124  
    20802080
    20812081        /**
     2082         * @ticket 52192
     2083         * @requires function imagejpeg
     2084         */
     2085        public function test_batch_edit_image() {
     2086                wp_set_current_user( self::$superadmin_id );
     2087                $attachment = self::factory()->attachment->create_upload_object( $this->test_file );
     2088
     2089                $params = array(
     2090                        'modifiers' => array(
     2091                                array(
     2092                                        'type' => 'rotate',
     2093                                        'args' => array(
     2094                                                'angle' => 60,
     2095                                        ),
     2096                                ),
     2097                                array(
     2098                                        'type' => 'crop',
     2099                                        'args' => array(
     2100                                                'left'   => 50,
     2101                                                'top'    => 10,
     2102                                                'width'  => 10,
     2103                                                'height' => 5,
     2104                                        ),
     2105                                ),
     2106                        ),
     2107                        'src'       => wp_get_attachment_image_url( $attachment, 'full' ),
     2108                );
     2109
     2110                $request = new WP_REST_Request( 'POST', "/wp/v2/media/{$attachment}/edit" );
     2111                $request->set_body_params( $params );
     2112                $response = rest_do_request( $request );
     2113                $item     = $response->get_data();
     2114
     2115                $this->assertSame( 201, $response->get_status() );
     2116                $this->assertSame( rest_url( '/wp/v2/media/' . $item['id'] ), $response->get_headers()['Location'] );
     2117
     2118                $this->assertStringEndsWith( '-edited.jpg', $item['media_details']['file'] );
     2119                $this->assertArrayHasKey( 'parent_image', $item['media_details'] );
     2120                $this->assertEquals( $attachment, $item['media_details']['parent_image']['attachment_id'] );
     2121                $this->assertContains( 'canola', $item['media_details']['parent_image']['file'] );
     2122        }
     2123
     2124        /**
    20822125         * @ticket 50565
    20832126         */
Note: See TracChangeset for help on using the changeset viewer.