WordPress.org

Make WordPress Core


Ignore:
Timestamp:
10/20/16 02:54:12 (8 months 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.