Make WordPress Core


Ignore:
Timestamp:
10/20/2016 02:54:12 AM (8 years ago)
Author:
rachelbaker
Message:

REST API: Introduce the Content API endpoints.

REST API endpoints for your WordPress content. These endpoints provide machine-readable external access to your WordPress site with a clear, standards-driven interface, allowing new and innovative apps for interacting with your site. These endpoints support all of the following:

  • Posts: Read and write access to all post data, for all types of post-based data, including pages and media.
  • Comments: Read and write access to all comment data. This includes pingbacks and trackbacks.
  • Terms: Read and write access to all term data.
  • Users: Read and write access to all user data. This includes public access to some data for post authors.
  • Meta: Read and write access to metadata for posts, comments, terms, and users, on an opt-in basis from plugins.
  • Settings: Read and write access to settings, on an opt-in basis from plugins and core. This enables API management of key site content values that are technically stored in options, such as site title and byline.

Love your REST API, WordPress! The infrastructure says, "Let's do lunch!" but the content API endpoints say, "You're paying!"

Props rmccue, rachelbaker, danielbachhuber, joehoyle, adamsilverstein, afurculita, ahmadawais, airesvsg, alisspers, antisilent, apokalyptik, artoliukkonen, attitude, boonebgorges, bradyvercher, brianhogg, caseypatrickdriscoll, chopinbach, chredd, christianesperar, chrisvanpatten, claudiolabarbera, claudiosmweb, cmmarslender, codebykat, coderkevin, codfish, codonnell822, daggerhart, danielpunkass, davidbhayes, delphinus, desrosj, dimadin, dotancohen, DrewAPicture, Dudo1985, duncanjbrown, eherman24, eivhyl, eliorivero, elyobo, en-alis, ericandrewlewis, ericpedia, evansobkowicz, fjarrett, frozzare, georgestephanis, greatislander, guavaworks, hideokamoto, hkdobrev, hubdotcom, hurtige, iandunn, ircrash, ironpaperweight, iseulde, Japh, jaredcobb, JDGrimes, jdolan, jdoubleu, jeremyfelt, jimt, jjeaton, jmusal, jnylen0, johanmynhardt, johnbillion, jonathanbardo, jorbin, joshkadis, JPry, jshreve, jtsternberg, JustinSainton, kacperszurek, kadamwhite, kalenjohnson, kellbot, kjbenk, kokarn, krogsgard, kuchenundkakao, kuldipem, kwight, lgedeon, lukepettway, mantismamita, markoheijnen, matrixik, mattheu, mauteri, maxcutler, mayukojpn, michael-arestad, miyauchi, mjbanks, modemlooper, mrbobbybryant, NateWr, nathanrice, netweb, NikV, nullvariable, oskosk, oso96_2000, oxymoron, pcfreak30, pento, peterwilsoncc, Pezzab, phh, pippinsplugins, pjgalbraith, pkevan, pollyplummer, pushred, quasel, QWp6t, schlessera, schrapel, Shelob9, shprink, simonlampen, Soean, solal, tapsboy, tfrommen, tharsheblows, thenbrent, tierra, tlovett1, tnegri, tobych, Toddses, toro_unit, traversal, vanillalounge, vishalkakadiya, wanecek, web2style, webbgaraget, websupporter, westonruter, whyisjake, wonderboymusic, wpsmith, xknown, zyphonic.
Fixes #38373.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api.php

    r38806 r38832  
    7272
    7373/**
     74 * Registers a new field on an existing WordPress object type.
     75 *
     76 * @since 4.7.0
     77 *
     78 * @global array $wp_rest_additional_fields Holds registered fields, organized
     79 *                                          by object type.
     80 *
     81 * @param string|array $object_type Object(s) the field is being registered
     82 *                                  to, "post"|"term"|"comment" etc.
     83 * @param string $attribute         The attribute name.
     84 * @param array  $args {
     85 *     Optional. An array of arguments used to handle the registered field.
     86 *
     87 *     @type string|array|null $get_callback    Optional. The callback function used to retrieve the field
     88 *                                              value. Default is 'null', the field will not be returned in
     89 *                                              the response.
     90 *     @type string|array|null $update_callback Optional. The callback function used to set and update the
     91 *                                              field value. Default is 'null', the value cannot be set or
     92 *                                              updated.
     93 *     @type string|array|null $schema          Optional. The callback function used to create the schema for
     94 *                                              this field. Default is 'null', no schema entry will be returned.
     95 * }
     96 */
     97function register_rest_field( $object_type, $attribute, $args = array() ) {
     98    $defaults = array(
     99        'get_callback'    => null,
     100        'update_callback' => null,
     101        'schema'          => null,
     102    );
     103
     104    $args = wp_parse_args( $args, $defaults );
     105
     106    global $wp_rest_additional_fields;
     107
     108    $object_types = (array) $object_type;
     109
     110    foreach ( $object_types as $object_type ) {
     111        $wp_rest_additional_fields[ $object_type ][ $attribute ] = $args;
     112    }
     113}
     114
     115/**
    74116 * Registers rewrite rules for the API.
    75117 *
     
    123165
    124166    add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 );
     167}
     168
     169/**
     170 * Registers default REST API routes.
     171 *
     172 * @since 4.7.0
     173 */
     174function create_initial_rest_routes() {
     175    foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
     176        $class = ! empty( $post_type->rest_controller_class ) ? $post_type->rest_controller_class : 'WP_REST_Posts_Controller';
     177
     178        if ( ! class_exists( $class ) ) {
     179            continue;
     180        }
     181        $controller = new $class( $post_type->name );
     182        if ( ! is_subclass_of( $controller, 'WP_REST_Controller' ) ) {
     183            continue;
     184        }
     185
     186        $controller->register_routes();
     187
     188        if ( post_type_supports( $post_type->name, 'revisions' ) ) {
     189            $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name );
     190            $revisions_controller->register_routes();
     191        }
     192    }
     193
     194    // Post types.
     195    $controller = new WP_REST_Post_Types_Controller;
     196    $controller->register_routes();
     197
     198    // Post statuses.
     199    $controller = new WP_REST_Post_Statuses_Controller;
     200    $controller->register_routes();
     201
     202    // Taxonomies.
     203    $controller = new WP_REST_Taxonomies_Controller;
     204    $controller->register_routes();
     205
     206    // Terms.
     207    foreach ( get_taxonomies( array( 'show_in_rest' => true ), 'object' ) as $taxonomy ) {
     208        $class = ! empty( $taxonomy->rest_controller_class ) ? $taxonomy->rest_controller_class : 'WP_REST_Terms_Controller';
     209
     210        if ( ! class_exists( $class ) ) {
     211            continue;
     212        }
     213        $controller = new $class( $taxonomy->name );
     214        if ( ! is_subclass_of( $controller, 'WP_REST_Controller' ) ) {
     215            continue;
     216        }
     217
     218        $controller->register_routes();
     219    }
     220
     221    // Users.
     222    $controller = new WP_REST_Users_Controller;
     223    $controller->register_routes();
     224
     225    // Comments.
     226    $controller = new WP_REST_Comments_Controller;
     227    $controller->register_routes();
     228
     229    // Settings.
     230    $controller = new WP_REST_Settings_Controller;
     231    $controller->register_routes();
    125232}
    126233
     
    684791    return array( $local, $utc );
    685792}
     793
     794/**
     795 * Returns a contextual HTTP error code for authorization failure.
     796 *
     797 * @since 4.7.0
     798 *
     799 * @return integer 401 if the user is not logged in, 403 if the user is logged in.
     800 */
     801function rest_authorization_required_code() {
     802    return is_user_logged_in() ? 403 : 401;
     803}
     804
     805/**
     806 * Validate a request argument based on details registered to the route.
     807 *
     808 * @since 4.7.0
     809 *
     810 * @param  mixed            $value
     811 * @param  WP_REST_Request  $request
     812 * @param  string           $param
     813 * @return WP_Error|boolean
     814 */
     815function rest_validate_request_arg( $value, $request, $param ) {
     816    $attributes = $request->get_attributes();
     817    if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
     818        return true;
     819    }
     820    $args = $attributes['args'][ $param ];
     821
     822    if ( ! empty( $args['enum'] ) ) {
     823        if ( ! in_array( $value, $args['enum'], true ) ) {
     824            return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: list of valid values */ __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) );
     825        }
     826    }
     827
     828    if ( 'integer' === $args['type'] && ! is_numeric( $value ) ) {
     829        return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s.' ), $param, 'integer' ) );
     830    }
     831
     832    if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) {
     833        return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s.' ), $value, 'boolean' ) );
     834    }
     835
     836    if ( 'string' === $args['type'] && ! is_string( $value ) ) {
     837        return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s.' ), $param, 'string' ) );
     838    }
     839
     840    if ( isset( $args['format'] ) ) {
     841        switch ( $args['format'] ) {
     842            case 'date-time' :
     843                if ( ! rest_parse_date( $value ) ) {
     844                    return new WP_Error( 'rest_invalid_date', __( 'The date you provided is invalid.' ) );
     845                }
     846                break;
     847
     848            case 'email' :
     849                if ( ! is_email( $value ) ) {
     850                    return new WP_Error( 'rest_invalid_email', __( 'The email address you provided is invalid.' ) );
     851                }
     852                break;
     853            case 'ipv4' :
     854                if ( ! rest_is_ip_address( $value ) ) {
     855                    return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.' ), $value ) );
     856                }
     857                break;
     858        }
     859    }
     860
     861    if ( in_array( $args['type'], array( 'numeric', 'integer' ), true ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) {
     862        if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) {
     863            if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) {
     864                return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) );
     865            } elseif ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) {
     866                return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (inclusive)' ), $param, $args['minimum'] ) );
     867            }
     868        } elseif ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) {
     869            if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) {
     870                return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (exclusive)' ), $param, $args['maximum'] ) );
     871            } elseif ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) {
     872                return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (inclusive)' ), $param, $args['maximum'] ) );
     873            }
     874        } elseif ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) {
     875            if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) {
     876                if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) {
     877                    return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) );
     878                }
     879            } elseif ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) {
     880                if ( $value >= $args['maximum'] || $value < $args['minimum'] ) {
     881                    return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) );
     882                }
     883            } elseif ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) {
     884                if ( $value > $args['maximum'] || $value <= $args['minimum'] ) {
     885                    return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) );
     886                }
     887            } elseif ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) {
     888                if ( $value > $args['maximum'] || $value < $args['minimum'] ) {
     889                    return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) );
     890                }
     891            }
     892        }
     893    }
     894
     895    return true;
     896}
     897
     898/**
     899 * Sanitize a request argument based on details registered to the route.
     900 *
     901 * @since 4.7.0
     902 *
     903 * @param  mixed            $value
     904 * @param  WP_REST_Request  $request
     905 * @param  string           $param
     906 * @return mixed
     907 */
     908function rest_sanitize_request_arg( $value, $request, $param ) {
     909    $attributes = $request->get_attributes();
     910    if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
     911        return $value;
     912    }
     913    $args = $attributes['args'][ $param ];
     914
     915    if ( 'integer' === $args['type'] ) {
     916        return (int) $value;
     917    }
     918
     919    if ( 'boolean' === $args['type'] ) {
     920        return rest_sanitize_boolean( $value );
     921    }
     922
     923    if ( isset( $args['format'] ) ) {
     924        switch ( $args['format'] ) {
     925            case 'date-time' :
     926                return sanitize_text_field( $value );
     927
     928            case 'email' :
     929                /*
     930                 * sanitize_email() validates, which would be unexpected
     931                 */
     932                return sanitize_text_field( $value );
     933
     934            case 'uri' :
     935                return esc_url_raw( $value );
     936
     937            case 'ipv4' :
     938                return sanitize_text_field( $value );
     939        }
     940    }
     941
     942    return $value;
     943}
     944
     945/**
     946 * Parse a request argument based on details registered to the route.
     947 *
     948 * Runs a validation check and sanitizes the value, primarily to be used via
     949 * the `sanitize_callback` arguments in the endpoint args registration.
     950 *
     951 * @since 4.7.0
     952 *
     953 * @param  mixed            $value
     954 * @param  WP_REST_Request  $request
     955 * @param  string           $param
     956 * @return mixed
     957 */
     958function rest_parse_request_arg( $value, $request, $param ) {
     959    $is_valid = rest_validate_request_arg( $value, $request, $param );
     960
     961    if ( is_wp_error( $is_valid ) ) {
     962        return $is_valid;
     963    }
     964
     965    $value = rest_sanitize_request_arg( $value, $request, $param );
     966
     967    return $value;
     968}
     969
     970/**
     971 * Determines if a IPv4 address is valid.
     972 *
     973 * Does not handle IPv6 addresses.
     974 *
     975 * @since 4.7.0
     976 *
     977 * @param  string $ipv4 IP 32-bit address.
     978 * @return string|false The valid IPv4 address, otherwise false.
     979 */
     980function rest_is_ip_address( $ipv4 ) {
     981    $pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';
     982
     983    if ( ! preg_match( $pattern, $ipv4 ) ) {
     984        return false;
     985    }
     986
     987    return $ipv4;
     988}
     989
     990/**
     991 * Changes a boolean-like value into the proper boolean value.
     992 *
     993 * @since 4.7.0
     994 *
     995 * @param bool|string|int $value The value being evaluated.
     996 * @return boolean Returns the proper associated boolean value.
     997 */
     998function rest_sanitize_boolean( $value ) {
     999    // String values are translated to `true`; make sure 'false' is false.
     1000    if ( is_string( $value )  ) {
     1001        $value = strtolower( $value );
     1002        if ( in_array( $value, array( 'false', '0' ), true ) ) {
     1003            $value = false;
     1004        }
     1005    }
     1006
     1007    // Everything else will map nicely to boolean.
     1008    return (boolean) $value;
     1009}
     1010
     1011/**
     1012 * Determines if a given value is boolean-like.
     1013 *
     1014 * @since 4.7.0
     1015 *
     1016 * @param bool|string $maybe_bool The value being evaluated.
     1017 * @return boolean True if a boolean, otherwise false.
     1018 */
     1019function rest_is_boolean( $maybe_bool ) {
     1020    if ( is_bool( $maybe_bool ) ) {
     1021        return true;
     1022    }
     1023
     1024    if ( is_string( $maybe_bool ) ) {
     1025        $maybe_bool = strtolower( $maybe_bool );
     1026
     1027        $valid_boolean_values = array(
     1028            'false',
     1029            'true',
     1030            '0',
     1031            '1',
     1032        );
     1033
     1034        return in_array( $maybe_bool, $valid_boolean_values, true );
     1035    }
     1036
     1037    if ( is_int( $maybe_bool ) ) {
     1038        return in_array( $maybe_bool, array( 0, 1 ), true );
     1039    }
     1040
     1041    return false;
     1042}
     1043
     1044/**
     1045 * Retrieves the avatar urls in various sizes based on a given email address.
     1046 *
     1047 * @since 4.7.0
     1048 *
     1049 * @see get_avatar_url()
     1050 *
     1051 * @param string $email Email address.
     1052 * @return array $urls Gravatar url for each size.
     1053 */
     1054function rest_get_avatar_urls( $email ) {
     1055    $avatar_sizes = rest_get_avatar_sizes();
     1056
     1057    $urls = array();
     1058    foreach ( $avatar_sizes as $size ) {
     1059        $urls[ $size ] = get_avatar_url( $email, array( 'size' => $size ) );
     1060    }
     1061
     1062    return $urls;
     1063}
     1064
     1065/**
     1066 * Retrieves the pixel sizes for avatars.
     1067 *
     1068 * @since 4.7.0
     1069 *
     1070 * @return array List of pixel sizes for avatars. Default `[ 24, 48, 96 ]`.
     1071 */
     1072function rest_get_avatar_sizes() {
     1073    /**
     1074     * Filter the REST avatar sizes.
     1075     *
     1076     * Use this filter to adjust the array of sizes returned by the
     1077     * `rest_get_avatar_sizes` function.
     1078     *
     1079     * @since 4.4.0
     1080     *
     1081     * @param array $sizes An array of int values that are the pixel sizes for avatars.
     1082     *                     Default `[ 24, 48, 96 ]`.
     1083     */
     1084    return apply_filters( 'rest_avatar_sizes', array( 24, 48, 96 ) );
     1085}
Note: See TracChangeset for help on using the changeset viewer.