WordPress.org

Make WordPress Core

Changeset 50124


Ignore:
Timestamp:
02/01/2021 06:35:38 PM (8 months 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.