[API] Một ví dụ nghiên cứu register_rest_route callback && permission_callback (ok)
<?php
add_action('rest_api_init', function () {
register_rest_route(
'myplugin/v1',
'/post',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'get_items',
'permission_callback' => 'get_items_permissions_check',
'args' => get_collection_params(),
)
)
);
});
function get_items_permissions_check($request) {
$post_type = get_post_type_object('post');
if ('edit' === $request['context'] && !current_user_can($post_type->cap->edit_posts)) {
return new WP_Error(
'rest_forbidden_context',
__('Sorry, you are not allowed to edit posts in this post type.'),
array('status' => rest_authorization_required_code())
);
}
return true;
}
function create_item_permissions_check($request) {
if (!empty($request['id'])) {
return new WP_Error(
'rest_post_exists',
__('Cannot create existing post.'),
array('status' => 400)
);
}
$post_type = get_post_type_object('post');
if (!empty($request['author']) && get_current_user_id() !== $request['author'] && !current_user_can($post_type->cap->edit_others_posts)) {
return new WP_Error(
'rest_cannot_edit_others',
__('Sorry, you are not allowed to create posts as this user.'),
array('status' => rest_authorization_required_code())
);
}
if (!empty($request['sticky']) && !current_user_can($post_type->cap->edit_others_posts) && !current_user_can($post_type->cap->publish_posts)) {
return new WP_Error(
'rest_cannot_assign_sticky',
__('Sorry, you are not allowed to make posts sticky.'),
array('status' => rest_authorization_required_code())
);
}
if (!current_user_can($post_type->cap->create_posts)) {
return new WP_Error(
'rest_cannot_create',
__('Sorry, you are not allowed to create posts as this user.'),
array('status' => rest_authorization_required_code())
);
}
if (!$this->check_assign_terms_permission($request)) {
return new WP_Error(
'rest_cannot_assign_term',
__('Sorry, you are not allowed to assign the provided terms.'),
array('status' => rest_authorization_required_code())
);
}
return true;
}
function get_collection_params() {
$query_params = get_collection_params_parent();
$query_params['context']['default'] = 'view';
$query_params['after'] = array(
'description' => __('Limit response to posts published after a given ISO8601 compliant date.'),
'type' => 'string',
'format' => 'date-time',
);
if (post_type_supports('post', 'author')) {
$query_params['author'] = array(
'description' => __('Limit result set to posts assigned to specific authors.'),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'default' => array(),
);
$query_params['author_exclude'] = array(
'description' => __('Ensure result set excludes posts assigned to specific authors.'),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'default' => array(),
);
}
$query_params['before'] = array(
'description' => __('Limit response to posts published before a given ISO8601 compliant date.'),
'type' => 'string',
'format' => 'date-time',
);
$query_params['exclude'] = array(
'description' => __('Ensure result set excludes specific IDs.'),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'default' => array(),
);
$query_params['include'] = array(
'description' => __('Limit result set to specific IDs.'),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'default' => array(),
);
if ('page' === 'post' || post_type_supports('post', 'page-attributes')) {
$query_params['menu_order'] = array(
'description' => __('Limit result set to posts with a specific menu_order value.'),
'type' => 'integer',
);
}
$query_params['offset'] = array(
'description' => __('Offset the result set by a specific number of items.'),
'type' => 'integer',
);
$query_params['order'] = array(
'description' => __('Order sort attribute ascending or descending.'),
'type' => 'string',
'default' => 'desc',
'enum' => array('asc', 'desc'),
);
$query_params['orderby'] = array(
'description' => __('Sort collection by object attribute.'),
'type' => 'string',
'default' => 'date',
'enum' => array(
'author',
'date',
'id',
'include',
'modified',
'parent',
'relevance',
'slug',
'include_slugs',
'title',
),
);
if ('page' === 'post' || post_type_supports('post', 'page-attributes')) {
$query_params['orderby']['enum'][] = 'menu_order';
}
$post_type = get_post_type_object('post');
if ($post_type->hierarchical || 'attachment' === 'post') {
$query_params['parent'] = array(
'description' => __('Limit result set to items with particular parent IDs.'),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'default' => array(),
);
$query_params['parent_exclude'] = array(
'description' => __('Limit result set to all items except those of a particular parent ID.'),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'default' => array(),
);
}
$query_params['slug'] = array(
'description' => __('Limit result set to posts with one or more specific slugs.'),
'type' => 'array',
'items' => array(
'type' => 'string',
),
'sanitize_callback' => 'wp_parse_slug_list',
);
$query_params['status'] = array(
'default' => 'publish',
'description' => __('Limit result set to posts assigned one or more statuses.'),
'type' => 'array',
'items' => array(
'enum' => array_merge(array_keys(get_post_stati()), array('any')),
'type' => 'string',
),
'sanitize_callback' => 'sanitize_post_statuses',
);
$taxonomies = wp_list_filter(get_object_taxonomies('post', 'objects'), array('show_in_rest' => true));
if (!empty($taxonomies)) {
$query_params['tax_relation'] = array(
'description' => __('Limit result set based on relationship between multiple taxonomies.'),
'type' => 'string',
'enum' => array('AND', 'OR'),
);
}
foreach ($taxonomies as $taxonomy) {
$base = !empty($taxonomy->rest_base) ? $taxonomy->rest_base : $taxonomy->name;
$query_params[$base] = array(
/* translators: %s: Taxonomy name. */
'description' => sprintf(__('Limit result set to all items that have the specified term assigned in the %s taxonomy.'), $base),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'default' => array(),
);
$query_params[$base . '_exclude'] = array(
/* translators: %s: Taxonomy name. */
'description' => sprintf(__('Limit result set to all items except those that have the specified term assigned in the %s taxonomy.'), $base),
'type' => 'array',
'items' => array(
'type' => 'integer',
),
'default' => array(),
);
}
if ('post' === 'post') {
$query_params['sticky'] = array(
'description' => __('Limit result set to items that are sticky.'),
'type' => 'boolean',
);
}
return apply_filters("rest_{'post'}_collection_params", $query_params, $post_type);
}
function get_collection_params_parent() {
return array(
'context' => get_context_param(),
'page' => array(
'description' => __('Current page of the collection.'),
'type' => 'integer',
'default' => 1,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
'minimum' => 1,
),
'per_page' => array(
'description' => __('Maximum number of items to be returned in result set.'),
'type' => 'integer',
'default' => 10,
'minimum' => 1,
'maximum' => 100,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
),
'search' => array(
'description' => __('Limit results to those matching a string.'),
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
),
);
}
function get_context_param($args = array()) {
$param_details = array(
'description' => __('Scope under which the request is made; determines fields present in response.'),
'type' => 'string',
'sanitize_callback' => 'sanitize_key',
'validate_callback' => 'rest_validate_request_arg',
);
$schema = get_item_schema();
if (empty($schema['properties'])) {
return array_merge($param_details, $args);
}
$contexts = array();
foreach ($schema['properties'] as $attributes) {
if (!empty($attributes['context'])) {
$contexts = array_merge($contexts, $attributes['context']);
}
}
if (!empty($contexts)) {
$param_details['enum'] = array_unique($contexts);
rsort($param_details['enum']);
}
return array_merge($param_details, $args);
}
function get_item_schema() {
return null;
}
function sanitize_post_statuses($statuses, $request, $parameter) {
$statuses = wp_parse_slug_list($statuses);
// The default status is different in WP_REST_Attachments_Controller.
$attributes = $request->get_attributes();
$default_status = $attributes['args']['status']['default'];
foreach ($statuses as $status) {
if ($status === $default_status) {
continue;
}
$post_type_obj = get_post_type_object('post');
if (current_user_can($post_type_obj->cap->edit_posts) || 'private' === $status && current_user_can($post_type_obj->cap->read_private_posts)) {
$result = rest_validate_request_arg($status, $request, $parameter);
if (is_wp_error($result)) {
return $result;
}
} else {
return new WP_Error(
'rest_forbidden_status',
__('Status is forbidden.'),
array('status' => rest_authorization_required_code())
);
}
}
return $statuses;
}
function get_items($request) {
// Ensure a search string is set in case the orderby is set to 'relevance'.
if (!empty($request['orderby']) && 'relevance' === $request['orderby'] && empty($request['search'])) {
return new WP_Error(
'rest_no_search_term_defined',
__('You need to define a search term to order by relevance.'),
array('status' => 400)
);
}
// Ensure an include parameter is set in case the orderby is set to 'include'.
if (!empty($request['orderby']) && 'include' === $request['orderby'] && empty($request['include'])) {
return new WP_Error(
'rest_orderby_include_missing_include',
__('You need to define an include parameter to order by include.'),
array('status' => 400)
);
}
// Retrieve the list of registered collection query parameters.
$registered = get_collection_params();
$args = array();
/*
* This array defines mappings between public API query parameters whose
* values are accepted as-passed, and their internal WP_Query parameter
* name equivalents (some are the same). Only values which are also
* present in $registered will be set.
*/
$parameter_mappings = array(
'author' => 'author__in',
'author_exclude' => 'author__not_in',
'exclude' => 'post__not_in',
'include' => 'post__in',
'menu_order' => 'menu_order',
'offset' => 'offset',
'order' => 'order',
'orderby' => 'orderby',
'page' => 'paged',
'parent' => 'post_parent__in',
'parent_exclude' => 'post_parent__not_in',
'search' => 's',
'slug' => 'post_name__in',
'status' => 'post_status',
);
/*
* For each known parameter which is both registered and present in the request,
* set the parameter's value on the query $args.
*/
foreach ($parameter_mappings as $api_param => $wp_param) {
if (isset($registered[$api_param], $request[$api_param])) {
$args[$wp_param] = $request[$api_param];
}
}
// Check for & assign any parameters which require special handling or setting.
$args['date_query'] = array();
// Set before into date query. Date query must be specified as an array of an array.
if (isset($registered['before'], $request['before'])) {
$args['date_query'][0]['before'] = $request['before'];
}
// Set after into date query. Date query must be specified as an array of an array.
if (isset($registered['after'], $request['after'])) {
$args['date_query'][0]['after'] = $request['after'];
}
// Ensure our per_page parameter overrides any provided posts_per_page filter.
if (isset($registered['per_page'])) {
$args['posts_per_page'] = $request['per_page'];
}
if (isset($registered['sticky'], $request['sticky'])) {
$sticky_posts = get_option('sticky_posts', array());
if (!is_array($sticky_posts)) {
$sticky_posts = array();
}
if ($request['sticky']) {
/*
* As post__in will be used to only get sticky posts,
* we have to support the case where post__in was already
* specified.
*/
$args['post__in'] = $args['post__in'] ? array_intersect($sticky_posts, $args['post__in']) : $sticky_posts;
/*
* If we intersected, but there are no post IDs in common,
* WP_Query won't return "no posts" for post__in = array()
* so we have to fake it a bit.
*/
if (!$args['post__in']) {
$args['post__in'] = array(0);
}
} elseif ($sticky_posts) {
/*
* As post___not_in will be used to only get posts that
* are not sticky, we have to support the case where post__not_in
* was already specified.
*/
$args['post__not_in'] = array_merge($args['post__not_in'], $sticky_posts);
}
}
// Force the post_type argument, since it's not a user input variable.
$args['post_type'] = 'post';
/**
* Filters the query arguments for a request.
*
* Enables adding extra arguments or setting defaults for a post collection request.
*
* @since 4.7.0
*
* @link https://developer.wordpress.org/reference/classes/wp_query/
*
* @param array $args Key value array of query var to query value.
* @param WP_REST_Request $request The request used.
*/
$args = apply_filters("rest_{'post'}_query", $args, $request);
$query_args = prepare_items_query($args, $request);
$taxonomies = wp_list_filter(get_object_taxonomies('post', 'objects'), array('show_in_rest' => true));
if (!empty($request['tax_relation'])) {
$query_args['tax_query'] = array('relation' => $request['tax_relation']);
}
foreach ($taxonomies as $taxonomy) {
$base = !empty($taxonomy->rest_base) ? $taxonomy->rest_base : $taxonomy->name;
$tax_exclude = $base . '_exclude';
if (!empty($request[$base])) {
$query_args['tax_query'][] = array(
'taxonomy' => $taxonomy->name,
'field' => 'term_id',
'terms' => $request[$base],
'include_children' => false,
);
}
if (!empty($request[$tax_exclude])) {
$query_args['tax_query'][] = array(
'taxonomy' => $taxonomy->name,
'field' => 'term_id',
'terms' => $request[$tax_exclude],
'include_children' => false,
'operator' => 'NOT IN',
);
}
}
$posts_query = new WP_Query();
$query_result = $posts_query->query($query_args);
// Allow access to all password protected posts if the context is edit.
if ('edit' === $request['context']) {
add_filter('post_password_required', '__return_false');
}
$posts = array();
foreach ($query_result as $post) {
if (!check_read_permission($post)) {
continue;
}
$data = prepare_item_for_response($post, $request);
$posts[] = prepare_response_for_collection($data);
}
// Reset filter.
if ('edit' === $request['context']) {
remove_filter('post_password_required', '__return_false');
}
$page = (int) $query_args['paged'];
$total_posts = $posts_query->found_posts;
if ($total_posts < 1) {
// Out-of-bounds, run the query again without LIMIT for total count.
unset($query_args['paged']);
$count_query = new WP_Query();
$count_query->query($query_args);
$total_posts = $count_query->found_posts;
}
$max_pages = ceil($total_posts / (int) $posts_query->query_vars['posts_per_page']);
if ($page > $max_pages && $total_posts > 0) {
return new WP_Error(
'rest_post_invalid_page_number',
__('The page number requested is larger than the number of pages available.'),
array('status' => 400)
);
}
$response = rest_ensure_response($posts);
$response->header('X-WP-Total', (int) $total_posts);
$response->header('X-WP-TotalPages', (int) $max_pages);
$request_params = $request->get_query_params();
$base = add_query_arg(urlencode_deep($request_params), rest_url(sprintf('%s/%s', 'myplugin/v1', 'post')));
if ($page > 1) {
$prev_page = $page - 1;
if ($prev_page > $max_pages) {
$prev_page = $max_pages;
}
$prev_link = add_query_arg('page', $prev_page, $base);
$response->link_header('prev', $prev_link);
}
if ($max_pages > $page) {
$next_page = $page + 1;
$next_link = add_query_arg('page', $next_page, $base);
$response->link_header('next', $next_link);
}
return $response;
}
function prepare_items_query($prepared_args = array(), $request = null) {
$query_args = array();
foreach ($prepared_args as $key => $value) {
/**
* Filters the query_vars used in get_items() for the constructed query.
*
* The dynamic portion of the hook name, `$key`, refers to the query_var key.
*
* @since 4.7.0
*
* @param string $value The query_var value.
*/
$query_args[$key] = apply_filters("rest_query_var-{$key}", $value); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
}
if ('post' !== 'post' || !isset($query_args['ignore_sticky_posts'])) {
$query_args['ignore_sticky_posts'] = true;
}
// Map to proper WP_Query orderby param.
if (isset($query_args['orderby']) && isset($request['orderby'])) {
$orderby_mappings = array(
'id' => 'ID',
'include' => 'post__in',
'slug' => 'post_name',
'include_slugs' => 'post_name__in',
);
if (isset($orderby_mappings[$request['orderby']])) {
$query_args['orderby'] = $orderby_mappings[$request['orderby']];
}
}
return $query_args;
}
function check_read_permission($post) {
$post_type = get_post_type_object($post->post_type);
if (!check_is_post_type_allowed($post_type)) {
return false;
}
// Is the post readable?
if ('publish' === $post->post_status || current_user_can('read_post', $post->ID)) {
return true;
}
$post_status_obj = get_post_status_object($post->post_status);
if ($post_status_obj && $post_status_obj->public) {
return true;
}
// Can we read the parent if we're inheriting?
if ('inherit' === $post->post_status && $post->post_parent > 0) {
$parent = get_post($post->post_parent);
if ($parent) {
return check_read_permission($parent);
}
}
/*
* If there isn't a parent, but the status is set to inherit, assume
* it's published (as per get_post_status()).
*/
if ('inherit' === $post->post_status) {
return true;
}
return false;
}
function check_is_post_type_allowed($post_type) {
if (!is_object($post_type)) {
$post_type = get_post_type_object($post_type);
}
if (!empty($post_type) && !empty($post_type->show_in_rest)) {
return true;
}
return false;
}
function prepare_item_for_response($post, $request) {
$GLOBALS['post'] = $post;
setup_postdata($post);
$fields = get_fields_for_response($request);
// Base fields for every post.
$data = array();
if (rest_is_field_included('id', $fields)) {
$data['id'] = $post->ID;
}
if (rest_is_field_included('date', $fields)) {
$data['date'] = $this->prepare_date_response($post->post_date_gmt, $post->post_date);
}
if (rest_is_field_included('date_gmt', $fields)) {
/*
* For drafts, `post_date_gmt` may not be set, indicating that the date
* of the draft should be updated each time it is saved (see #38883).
* In this case, shim the value based on the `post_date` field
* with the site's timezone offset applied.
*/
if ('0000-00-00 00:00:00' === $post->post_date_gmt) {
$post_date_gmt = get_gmt_from_date($post->post_date);
} else {
$post_date_gmt = $post->post_date_gmt;
}
$data['date_gmt'] = $this->prepare_date_response($post_date_gmt);
}
if (rest_is_field_included('guid', $fields)) {
$data['guid'] = array(
/** This filter is documented in wp-includes/post-template.php */
'rendered' => apply_filters('get_the_guid', $post->guid, $post->ID),
'raw' => $post->guid,
);
}
if (rest_is_field_included('modified', $fields)) {
$data['modified'] = $this->prepare_date_response($post->post_modified_gmt, $post->post_modified);
}
if (rest_is_field_included('modified_gmt', $fields)) {
/*
* For drafts, `post_modified_gmt` may not be set (see `post_date_gmt` comments
* above). In this case, shim the value based on the `post_modified` field
* with the site's timezone offset applied.
*/
if ('0000-00-00 00:00:00' === $post->post_modified_gmt) {
$post_modified_gmt = gmdate('Y-m-d H:i:s', strtotime($post->post_modified) - (get_option('gmt_offset') * 3600));
} else {
$post_modified_gmt = $post->post_modified_gmt;
}
$data['modified_gmt'] = $this->prepare_date_response($post_modified_gmt);
}
if (rest_is_field_included('password', $fields)) {
$data['password'] = $post->post_password;
}
if (rest_is_field_included('slug', $fields)) {
$data['slug'] = $post->post_name;
}
if (rest_is_field_included('status', $fields)) {
$data['status'] = $post->post_status;
}
if (rest_is_field_included('type', $fields)) {
$data['type'] = $post->post_type;
}
if (rest_is_field_included('link', $fields)) {
$data['link'] = get_permalink($post->ID);
}
if (rest_is_field_included('title', $fields)) {
$data['title'] = array();
}
if (rest_is_field_included('title.raw', $fields)) {
$data['title']['raw'] = $post->post_title;
}
if (rest_is_field_included('title.rendered', $fields)) {
add_filter('protected_title_format', array($this, 'protected_title_format'));
$data['title']['rendered'] = get_the_title($post->ID);
remove_filter('protected_title_format', array($this, 'protected_title_format'));
}
$has_password_filter = false;
if (can_access_password_content($post, $request)) {
// Allow access to the post, permissions already checked before.
add_filter('post_password_required', '__return_false');
$has_password_filter = true;
}
if (rest_is_field_included('content', $fields)) {
$data['content'] = array();
}
if (rest_is_field_included('content.raw', $fields)) {
$data['content']['raw'] = $post->post_content;
}
if (rest_is_field_included('content.rendered', $fields)) {
/** This filter is documented in wp-includes/post-template.php */
$data['content']['rendered'] = post_password_required($post) ? '' : apply_filters('the_content', $post->post_content);
}
if (rest_is_field_included('content.protected', $fields)) {
$data['content']['protected'] = (bool) $post->post_password;
}
if (rest_is_field_included('content.block_version', $fields)) {
$data['content']['block_version'] = block_version($post->post_content);
}
if (rest_is_field_included('excerpt', $fields)) {
/** This filter is documented in wp-includes/post-template.php */
$excerpt = apply_filters('get_the_excerpt', $post->post_excerpt, $post);
/** This filter is documented in wp-includes/post-template.php */
$excerpt = apply_filters('the_excerpt', $excerpt);
$data['excerpt'] = array(
'raw' => $post->post_excerpt,
'rendered' => post_password_required($post) ? '' : $excerpt,
'protected' => (bool) $post->post_password,
);
}
if ($has_password_filter) {
// Reset filter.
remove_filter('post_password_required', '__return_false');
}
if (rest_is_field_included('author', $fields)) {
$data['author'] = (int) $post->post_author;
}
if (rest_is_field_included('featured_media', $fields)) {
$data['featured_media'] = (int) get_post_thumbnail_id($post->ID);
}
if (rest_is_field_included('parent', $fields)) {
$data['parent'] = (int) $post->post_parent;
}
if (rest_is_field_included('menu_order', $fields)) {
$data['menu_order'] = (int) $post->menu_order;
}
if (rest_is_field_included('comment_status', $fields)) {
$data['comment_status'] = $post->comment_status;
}
if (rest_is_field_included('ping_status', $fields)) {
$data['ping_status'] = $post->ping_status;
}
if (rest_is_field_included('sticky', $fields)) {
$data['sticky'] = is_sticky($post->ID);
}
if (rest_is_field_included('template', $fields)) {
$template = get_page_template_slug($post->ID);
if ($template) {
$data['template'] = $template;
} else {
$data['template'] = '';
}
}
if (rest_is_field_included('format', $fields)) {
$data['format'] = get_post_format($post->ID);
// Fill in blank post format.
if (empty($data['format'])) {
$data['format'] = 'standard';
}
}
if (rest_is_field_included('meta', $fields)) {
$data['meta'] = $this->meta->get_value($post->ID, $request);
}
$taxonomies = wp_list_filter(get_object_taxonomies('post', 'objects'), array('show_in_rest' => true));
foreach ($taxonomies as $taxonomy) {
$base = !empty($taxonomy->rest_base) ? $taxonomy->rest_base : $taxonomy->name;
if (rest_is_field_included($base, $fields)) {
$terms = get_the_terms($post, $taxonomy->name);
$data[$base] = $terms ? array_values(wp_list_pluck($terms, 'term_id')) : array();
}
}
$post_type_obj = get_post_type_object($post->post_type);
if (is_post_type_viewable($post_type_obj) && $post_type_obj->public) {
$permalink_template_requested = rest_is_field_included('permalink_template', $fields);
$generated_slug_requested = rest_is_field_included('generated_slug', $fields);
if ($permalink_template_requested || $generated_slug_requested) {
if (!function_exists('get_sample_permalink')) {
require_once ABSPATH . 'wp-admin/includes/post.php';
}
$sample_permalink = get_sample_permalink($post->ID, $post->post_title, '');
if ($permalink_template_requested) {
$data['permalink_template'] = $sample_permalink[0];
}
if ($generated_slug_requested) {
$data['generated_slug'] = $sample_permalink[1];
}
}
}
$context = !empty($request['context']) ? $request['context'] : 'view';
$data = add_additional_fields_to_object($data, $request);
$data = filter_response_by_context($data, $context);
// Wrap the data in a response object.
$response = rest_ensure_response($data);
$links = prepare_links($post);
$response->add_links($links);
if (!empty($links['self']['href'])) {
$actions = get_available_actions($post, $request);
$self = $links['self']['href'];
foreach ($actions as $rel) {
$response->add_link($rel, $self);
}
}
/**
* Filters the post data for a response.
*
* The dynamic portion of the hook name, `'post'`, refers to the post type slug.
*
* @since 4.7.0
*
* @param WP_REST_Response $response The response object.
* @param WP_Post $post Post object.
* @param WP_REST_Request $request Request object.
*/
return apply_filters("rest_prepare_{'post'}", $response, $post, $request);
}
function prepare_response_for_collection($response) {
if (!($response instanceof WP_REST_Response)) {
return $response;
}
$data = (array) $response->get_data();
$server = rest_get_server();
$links = $server::get_compact_response_links($response);
if (!empty($links)) {
$data['_links'] = $links;
}
return $data;
}
function get_fields_for_response($request) {
$schema = get_item_schema();
$properties = isset($schema['properties']) ? $schema['properties'] : array();
$additional_fields = get_additional_fields();
foreach ($additional_fields as $field_name => $field_options) {
// For back-compat, include any field with an empty schema
// because it won't be present in $this->get_item_schema().
if (is_null($field_options['schema'])) {
$properties[$field_name] = $field_options;
}
}
// Exclude fields that specify a different context than the request context.
$context = $request['context'];
if ($context) {
foreach ($properties as $name => $options) {
if (!empty($options['context']) && !in_array($context, $options['context'], true)) {
unset($properties[$name]);
}
}
}
$fields = array_keys($properties);
if (!isset($request['_fields'])) {
return $fields;
}
$requested_fields = wp_parse_list($request['_fields']);
if (0 === count($requested_fields)) {
return $fields;
}
// Trim off outside whitespace from the comma delimited list.
$requested_fields = array_map('trim', $requested_fields);
// Always persist 'id', because it can be needed for add_additional_fields_to_object().
if (in_array('id', $fields, true)) {
$requested_fields[] = 'id';
}
// Return the list of all requested fields which appear in the schema.
return array_reduce(
$requested_fields,
function ($response_fields, $field) use ($fields) {
if (in_array($field, $fields, true)) {
$response_fields[] = $field;
return $response_fields;
}
// Check for nested fields if $field is not a direct match.
$nested_fields = explode('.', $field);
// A nested field is included so long as its top-level property
// is present in the schema.
if (in_array($nested_fields[0], $fields, true)) {
$response_fields[] = $field;
}
return $response_fields;
},
array()
);
}
function get_additional_fields($object_type = null) {
if (!$object_type) {
$object_type = get_object_type();
}
if (!$object_type) {
return array();
}
global $wp_rest_additional_fields;
if (!$wp_rest_additional_fields || !isset($wp_rest_additional_fields[$object_type])) {
return array();
}
return $wp_rest_additional_fields[$object_type];
}
function get_object_type() {
$schema = get_item_schema();
if (!$schema || !isset($schema['title'])) {
return null;
}
return $schema['title'];
}
function can_access_password_content($post, $request) {
if (empty($post->post_password)) {
// No filter required.
return false;
}
// Edit context always gets access to password-protected posts.
if ('edit' === $request['context']) {
return true;
}
// No password, no auth.
if (empty($request['password'])) {
return false;
}
// Double-check the request password.
return hash_equals($post->post_password, $request['password']);
}
function add_additional_fields_to_object($prepared, $request) {
$additional_fields = get_additional_fields();
$requested_fields = get_fields_for_response($request);
foreach ($additional_fields as $field_name => $field_options) {
if (!$field_options['get_callback']) {
continue;
}
if (!rest_is_field_included($field_name, $requested_fields)) {
continue;
}
$prepared[$field_name] = call_user_func($field_options['get_callback'], $prepared, $field_name, $request, $this->get_object_type());
}
return $prepared;
}
function filter_response_by_context($data, $context) {
$schema = get_item_schema();
return rest_filter_response_by_context($data, $schema, $context);
}
function prepare_links($post) {
$base = sprintf('%s/%s', 'myplugin/v1', 'post');
// Entity meta.
$links = array(
'self' => array(
'href' => rest_url(trailingslashit($base) . $post->ID),
),
'collection' => array(
'href' => rest_url($base),
),
'about' => array(
'href' => rest_url('myplugin/v1/types/' . 'post'),
),
);
if ((in_array($post->post_type, array('post', 'page'), true) || post_type_supports($post->post_type, 'author'))
&& !empty($post->post_author)) {
$links['author'] = array(
'href' => rest_url('myplugin/v1/users/' . $post->post_author),
'embeddable' => true,
);
}
if (in_array($post->post_type, array('post', 'page'), true) || post_type_supports($post->post_type, 'comments')) {
$replies_url = rest_url('myplugin/v1/comments');
$replies_url = add_query_arg('post', $post->ID, $replies_url);
$links['replies'] = array(
'href' => $replies_url,
'embeddable' => true,
);
}
if (in_array($post->post_type, array('post', 'page'), true) || post_type_supports($post->post_type, 'revisions')) {
$revisions = wp_get_post_revisions($post->ID, array('fields' => 'ids'));
$revisions_count = count($revisions);
$links['version-history'] = array(
'href' => rest_url(trailingslashit($base) . $post->ID . '/revisions'),
'count' => $revisions_count,
);
if ($revisions_count > 0) {
$last_revision = array_shift($revisions);
$links['predecessor-version'] = array(
'href' => rest_url(trailingslashit($base) . $post->ID . '/revisions/' . $last_revision),
'id' => $last_revision,
);
}
}
$post_type_obj = get_post_type_object($post->post_type);
if ($post_type_obj->hierarchical && !empty($post->post_parent)) {
$links['up'] = array(
'href' => rest_url(trailingslashit($base) . (int) $post->post_parent),
'embeddable' => true,
);
}
// If we have a featured media, add that.
$featured_media = get_post_thumbnail_id($post->ID);
if ($featured_media) {
$image_url = rest_url('myplugin/v1/media/' . $featured_media);
$links['https://api.w.org/featuredmedia'] = array(
'href' => $image_url,
'embeddable' => true,
);
}
if (!in_array($post->post_type, array('attachment', 'nav_menu_item', 'revision'), true)) {
$attachments_url = rest_url('myplugin/v1/media');
$attachments_url = add_query_arg('parent', $post->ID, $attachments_url);
$links['https://api.w.org/attachment'] = array(
'href' => $attachments_url,
);
}
$taxonomies = get_object_taxonomies($post->post_type);
if (!empty($taxonomies)) {
$links['https://api.w.org/term'] = array();
foreach ($taxonomies as $tax) {
$taxonomy_obj = get_taxonomy($tax);
// Skip taxonomies that are not public.
if (empty($taxonomy_obj->show_in_rest)) {
continue;
}
$tax_base = !empty($taxonomy_obj->rest_base) ? $taxonomy_obj->rest_base : $tax;
$terms_url = add_query_arg(
'post',
$post->ID,
rest_url('myplugin/v1/' . $tax_base)
);
$links['https://api.w.org/term'][] = array(
'href' => $terms_url,
'taxonomy' => $tax,
'embeddable' => true,
);
}
}
return $links;
}
function get_available_actions($post, $request) {
if ('edit' !== $request['context']) {
return array();
}
$rels = array();
$post_type = get_post_type_object($post->post_type);
if ('attachment' !== 'post' && current_user_can($post_type->cap->publish_posts)) {
$rels[] = 'https://api.w.org/action-publish';
}
if (current_user_can('unfiltered_html')) {
$rels[] = 'https://api.w.org/action-unfiltered-html';
}
if ('post' === $post_type->name) {
if (current_user_can($post_type->cap->edit_others_posts) && current_user_can($post_type->cap->publish_posts)) {
$rels[] = 'https://api.w.org/action-sticky';
}
}
if (post_type_supports($post_type->name, 'author')) {
if (current_user_can($post_type->cap->edit_others_posts)) {
$rels[] = 'https://api.w.org/action-assign-author';
}
}
$taxonomies = wp_list_filter(get_object_taxonomies('post', 'objects'), array('show_in_rest' => true));
foreach ($taxonomies as $tax) {
$tax_base = !empty($tax->rest_base) ? $tax->rest_base : $tax->name;
$create_cap = is_taxonomy_hierarchical($tax->name) ? $tax->cap->edit_terms : $tax->cap->assign_terms;
if (current_user_can($create_cap)) {
$rels[] = 'https://api.w.org/action-create-' . $tax_base;
}
if (current_user_can($tax->cap->assign_terms)) {
$rels[] = 'https://api.w.org/action-assign-' . $tax_base;
}
}
return $rels;
}
?>
Previous[API] Nếu API công khai, bạn có thể sử dụng __return_true làm lệnh gọi lại (ok)Next[API] Một ví dụ lấy WP_REST_Request $request (ok)
Last updated