WP-API
This commit is contained in:
commit
b6d33ee906
|
@ -0,0 +1,678 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract Rest Posts Controler Class
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/Abstracts
|
||||
* @version 2.6.0
|
||||
*/
|
||||
abstract class WC_REST_Posts_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = '';
|
||||
|
||||
/**
|
||||
* Post type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $post_type = '';
|
||||
|
||||
/**
|
||||
* Controls visibility on frontend.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $public = false;
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read items.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_post_permissions( $this->post_type, 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to create an item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_post_permissions( $this->post_type, 'create' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read an item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
$post = get_post( (int) $request['id'] );
|
||||
|
||||
if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'read', $post->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to update an item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function update_item_permissions_check( $request ) {
|
||||
$post = get_post( $request['id'] );
|
||||
|
||||
if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'edit', $post->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to delete an item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
$post = get_post( $request['id'] );
|
||||
|
||||
if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'delete', $post->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$post = get_post( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
||||
return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'Invalid id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $post, $request );
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
if ( $this->public ) {
|
||||
$response->link_header( 'alternate', get_permalink( $id ), array( 'type' => 'text/html' ) );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
if ( ! empty( $request['id'] ) ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$post = $this->prepare_item_for_database( $request );
|
||||
if ( is_wp_error( $post ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
$post->post_type = $this->post_type;
|
||||
$post_id = wp_insert_post( $post, true );
|
||||
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
|
||||
if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ) ) ) {
|
||||
$post_id->add_data( array( 'status' => 500 ) );
|
||||
} else {
|
||||
$post_id->add_data( array( 'status' => 400 ) );
|
||||
}
|
||||
return $post_id;
|
||||
}
|
||||
$post->ID = $post_id;
|
||||
$schema = $this->get_item_schema();
|
||||
$post = get_post( $post_id );
|
||||
|
||||
$this->update_additional_fields_for_object( $post, $request );
|
||||
|
||||
// Add meta fields.
|
||||
$meta_fields = $this->add_post_meta_fields( $post, $request );
|
||||
if ( is_wp_error( $meta_fields ) ) {
|
||||
// Remove post.
|
||||
$this->delete_post( $post );
|
||||
|
||||
return $meta_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single item is created or updated via the REST API.
|
||||
*
|
||||
* @param object $post Inserted object (not a WP_Post object).
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating item, false when updating.
|
||||
*/
|
||||
do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add post meta fields.
|
||||
*
|
||||
* @param WP_Post $post
|
||||
* @param WP_REST_Request $request
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function add_post_meta_fields( $post, $request ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete post.
|
||||
*
|
||||
* @param WP_Post $post
|
||||
*/
|
||||
protected function delete_post( $post ) {
|
||||
wp_delete_post( $post->ID, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single post.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$post = get_post( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$post = $this->prepare_item_for_database( $request );
|
||||
if ( is_wp_error( $post ) ) {
|
||||
return $post;
|
||||
}
|
||||
// Convert the post object to an array, otherwise wp_update_post will expect non-escaped input.
|
||||
$post_id = wp_update_post( (array) $post, true );
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ) ) ) {
|
||||
$post_id->add_data( array( 'status' => 500 ) );
|
||||
} else {
|
||||
$post_id->add_data( array( 'status' => 400 ) );
|
||||
}
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
$schema = $this->get_item_schema();
|
||||
|
||||
$post = get_post( $post_id );
|
||||
$this->update_additional_fields_for_object( $post, $request );
|
||||
|
||||
// Update meta fields.
|
||||
$meta_fields = $this->update_post_meta_fields( $post, $request );
|
||||
if ( is_wp_error( $meta_fields ) ) {
|
||||
return $meta_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single item is created or updated via the REST API.
|
||||
*
|
||||
* @param object $post Inserted object (not a WP_Post object).
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating item, false when updating.
|
||||
*/
|
||||
do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, false );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of posts.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$args = array();
|
||||
$args['offset'] = $request['offset'];
|
||||
$args['order'] = $request['order'];
|
||||
$args['orderby'] = $request['orderby'];
|
||||
$args['paged'] = $request['page'];
|
||||
$args['post__in'] = $request['include'];
|
||||
$args['post__not_in'] = $request['exclude'];
|
||||
$args['posts_per_page'] = $request['per_page'];
|
||||
$args['name'] = $request['slug'];
|
||||
$args['post_parent__in'] = $request['parent'];
|
||||
$args['post_parent__not_in'] = $request['parent_exclude'];
|
||||
$args['s'] = $request['search'];
|
||||
|
||||
$args['date_query'] = array();
|
||||
// Set before into date query. Date query must be specified as an array of an array.
|
||||
if ( isset( $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( $request['after'] ) ) {
|
||||
$args['date_query'][0]['after'] = $request['after'];
|
||||
}
|
||||
|
||||
if ( is_array( $request['filter'] ) ) {
|
||||
$args = array_merge( $args, $request['filter'] );
|
||||
unset( $args['filter'] );
|
||||
}
|
||||
|
||||
// Force the post_type argument, since it's not a user input variable.
|
||||
$args['post_type'] = $this->post_type;
|
||||
|
||||
/**
|
||||
* Filter the query arguments for a request.
|
||||
*
|
||||
* Enables adding extra arguments or setting defaults for a post
|
||||
* collection request.
|
||||
*
|
||||
* @param array $args Key value array of query var to query value.
|
||||
* @param WP_REST_Request $request The request used.
|
||||
*/
|
||||
$args = apply_filters( "woocommerce_rest_{$this->post_type}_query", $args, $request );
|
||||
$query_args = $this->prepare_items_query( $args, $request );
|
||||
|
||||
$posts_query = new WP_Query();
|
||||
$query_result = $posts_query->query( $query_args );
|
||||
|
||||
$posts = array();
|
||||
foreach ( $query_result as $post ) {
|
||||
if ( ! wc_rest_check_post_permissions( $this->post_type, 'read', $post->ID ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $this->prepare_item_for_response( $post, $request );
|
||||
$posts[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
$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) $query_args['posts_per_page'] );
|
||||
|
||||
$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();
|
||||
if ( ! empty( $request_params['filter'] ) ) {
|
||||
// Normalize the pagination params.
|
||||
unset( $request_params['filter']['posts_per_page'] );
|
||||
unset( $request_params['filter']['paged'] );
|
||||
}
|
||||
$base = add_query_arg( $request_params, rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$force = (bool) $request['force'];
|
||||
$post = get_post( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid post id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$supports_trash = EMPTY_TRASH_DAYS > 0;
|
||||
|
||||
/**
|
||||
* Filter whether an item is trashable.
|
||||
*
|
||||
* Return false to disable trash support for the item.
|
||||
*
|
||||
* @param boolean $supports_trash Whether the item type support trashing.
|
||||
* @param WP_Post $post The Post object being considered for trashing support.
|
||||
*/
|
||||
$supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_trashable", $supports_trash, $post );
|
||||
|
||||
if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $post->ID ) ) {
|
||||
return new WP_Error( "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
|
||||
// If we're forcing, then delete permanently.
|
||||
if ( $force ) {
|
||||
$result = wp_delete_post( $id, true );
|
||||
} else {
|
||||
// If we don't support trashing for this type, error out.
|
||||
if ( ! $supports_trash ) {
|
||||
return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501 ) );
|
||||
}
|
||||
|
||||
// Otherwise, only trash if we haven't already.
|
||||
if ( 'trash' === $post->post_status ) {
|
||||
return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410 ) );
|
||||
}
|
||||
|
||||
// (Note that internally this falls through to `wp_delete_post` if
|
||||
// the trash is disabled.)
|
||||
$result = wp_trash_post( $id );
|
||||
}
|
||||
|
||||
if ( ! $result ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single item is deleted or trashed via the REST API.
|
||||
*
|
||||
* @param object $post The deleted or trashed item.
|
||||
* @param WP_REST_Response $response The response data.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( "woocommerce_rest_delete_{$this->post_type}", $post, $response, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param WP_Post $post Post object.
|
||||
* @return array Links for the given post.
|
||||
*/
|
||||
protected function prepare_links( $post ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the allowed query_vars for a get_items() response and
|
||||
* prepare for WP_Query.
|
||||
*
|
||||
* @param array $prepared_args
|
||||
* @param WP_REST_Request $request
|
||||
* @return array $query_args
|
||||
*/
|
||||
protected function prepare_items_query( $prepared_args = array(), $request = null ) {
|
||||
|
||||
$valid_vars = array_flip( $this->get_allowed_query_vars() );
|
||||
$query_args = array();
|
||||
foreach ( $valid_vars as $var => $index ) {
|
||||
if ( isset( $prepared_args[ $var ] ) ) {
|
||||
/**
|
||||
* Filter the query_vars used in `get_items` for the constructed query.
|
||||
*
|
||||
* The dynamic portion of the hook name, $var, refers to the query_var key.
|
||||
*
|
||||
* @param mixed $prepared_args[ $var ] The query_var value.
|
||||
*
|
||||
*/
|
||||
$query_args[ $var ] = apply_filters( "woocommerce_rest_query_var-{$var}", $prepared_args[ $var ] );
|
||||
}
|
||||
}
|
||||
|
||||
$query_args['ignore_sticky_posts'] = true;
|
||||
|
||||
if ( 'include' === $query_args['orderby'] ) {
|
||||
$query_args['orderby'] = 'post__in';
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the WP Query vars that are allowed for the API request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_allowed_query_vars() {
|
||||
global $wp;
|
||||
|
||||
/**
|
||||
* Filter the publicly allowed query vars.
|
||||
*
|
||||
* Allows adjusting of the default query vars that are made public.
|
||||
*
|
||||
* @param array Array of allowed WP_Query query vars.
|
||||
*/
|
||||
$valid_vars = apply_filters( 'query_vars', $wp->public_query_vars );
|
||||
|
||||
$post_type_obj = get_post_type_object( $this->post_type );
|
||||
if ( current_user_can( $post_type_obj->cap->edit_posts ) ) {
|
||||
/**
|
||||
* Filter the allowed 'private' query vars for authorized users.
|
||||
*
|
||||
* If the user has the `edit_posts` capability, we also allow use of
|
||||
* private query parameters, which are only undesirable on the
|
||||
* frontend, but are safe for use in query strings.
|
||||
*
|
||||
* To disable anyway, use
|
||||
* `add_filter( 'woocommerce_rest_private_query_vars', '__return_empty_array' );`
|
||||
*
|
||||
* @param array $private_query_vars Array of allowed query vars for authorized users.
|
||||
* }
|
||||
*/
|
||||
$private = apply_filters( 'woocommerce_rest_private_query_vars', $wp->private_query_vars );
|
||||
$valid_vars = array_merge( $valid_vars, $private );
|
||||
}
|
||||
// Define our own in addition to WP's normal vars.
|
||||
$rest_valid = array(
|
||||
'date_query',
|
||||
'ignore_sticky_posts',
|
||||
'offset',
|
||||
'post__in',
|
||||
'post__not_in',
|
||||
'post_parent',
|
||||
'post_parent__in',
|
||||
'post_parent__not_in',
|
||||
'posts_per_page',
|
||||
'meta_query',
|
||||
'tax_query',
|
||||
);
|
||||
$valid_vars = array_merge( $valid_vars, $rest_valid );
|
||||
|
||||
/**
|
||||
* Filter allowed query vars for the REST API.
|
||||
*
|
||||
* This filter allows you to add or remove query vars from the final allowed
|
||||
* list for all requests, including unauthenticated ones. To alter the
|
||||
* vars for editors only.
|
||||
*
|
||||
* @param array {
|
||||
* Array of allowed WP_Query query vars.
|
||||
*
|
||||
* @param string $allowed_query_var The query var to allow.
|
||||
* }
|
||||
*/
|
||||
$valid_vars = apply_filters( 'woocommerce_rest_query_vars', $valid_vars );
|
||||
|
||||
return $valid_vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections of attachments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
|
||||
$params['context']['default'] = 'view';
|
||||
|
||||
$params['after'] = array(
|
||||
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['before'] = array(
|
||||
'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['exclude'] = array(
|
||||
'description' => __( 'Ensure result set excludes specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['include'] = array(
|
||||
'description' => __( 'Limit result set to specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['offset'] = array(
|
||||
'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'desc',
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'date',
|
||||
'enum' => array(
|
||||
'date',
|
||||
'id',
|
||||
'include',
|
||||
'title',
|
||||
'slug',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
$post_type_obj = get_post_type_object( $this->post_type );
|
||||
if ( $post_type_obj->hierarchical ) {
|
||||
$params['parent'] = array(
|
||||
'description' => __( 'Limit result set to those of particular parent ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'default' => array(),
|
||||
);
|
||||
$params['parent_exclude'] = array(
|
||||
'description' => __( 'Limit result set to all items except those of a particular parent id.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
'default' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$params['slug'] = array(
|
||||
'description' => __( 'Limit result set to posts with a specific slug.', 'woocommerce', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
$params['filter'] = array(
|
||||
'description' => __( 'Use WP Query arguments to modify the response; private query vars require appropriate authorization.', 'woocommerce' ),
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update post meta fields.
|
||||
*
|
||||
* @param WP_Post $post
|
||||
* @param WP_REST_Request $request
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function update_post_meta_fields( $post, $request ) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,750 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract Rest Terms Controler Class
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/Abstracts
|
||||
* @version 2.6.0
|
||||
*/
|
||||
abstract class WC_REST_Terms_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = '';
|
||||
|
||||
/**
|
||||
* Taxonomy.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $taxonomy = '';
|
||||
|
||||
/**
|
||||
* Register the routes for terms.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
|
||||
'name' => array(
|
||||
'required' => true,
|
||||
),
|
||||
) ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
));
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read the terms.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
$permissions = $this->check_permissions( $request, 'read' );
|
||||
if ( is_wp_error( $permissions ) ) {
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
if ( ! $permissions ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to create a term.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
$permissions = $this->check_permissions( $request, 'create' );
|
||||
if ( is_wp_error( $permissions ) ) {
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
if ( ! $permissions ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you cannot create new resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read a term.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
$permissions = $this->check_permissions( $request, 'read' );
|
||||
if ( is_wp_error( $permissions ) ) {
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
if ( ! $permissions ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to update a term.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function update_item_permissions_check( $request ) {
|
||||
$permissions = $this->check_permissions( $request, 'edit' );
|
||||
if ( is_wp_error( $permissions ) ) {
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
if ( ! $permissions ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to delete a term.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
$permissions = $this->check_permissions( $request, 'delete' );
|
||||
if ( is_wp_error( $permissions ) ) {
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
if ( ! $permissions ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you cannot delete resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permissions.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @param string $context Request context.
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function check_permissions( $request, $context = 'read' ) {
|
||||
// Get taxonomy.
|
||||
$taxonomy = $this->get_taxonomy( $request );
|
||||
if ( ! $taxonomy ) {
|
||||
return new WP_Error( 'woocommerce_rest_taxonomy_invalid', __( "Taxonomy doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
// Check permissions for a single term.
|
||||
if ( $id = intval( $request['id'] ) ) {
|
||||
$term = get_term( $id, $taxonomy );
|
||||
|
||||
if ( ! $term || $term->taxonomy !== $taxonomy ) {
|
||||
return new WP_Error( 'woocommerce_rest_term_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
return wc_rest_check_product_term_permissions( $taxonomy, $context, $term->term_id );
|
||||
}
|
||||
|
||||
return wc_rest_check_product_term_permissions( $taxonomy, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get terms associated with a taxonomy.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$taxonomy = $this->get_taxonomy( $request );
|
||||
$prepared_args = array(
|
||||
'exclude' => $request['exclude'],
|
||||
'include' => $request['include'],
|
||||
'order' => $request['order'],
|
||||
'orderby' => $request['orderby'],
|
||||
'product' => $request['product'],
|
||||
'hide_empty' => $request['hide_empty'],
|
||||
'number' => $request['per_page'],
|
||||
'search' => $request['search'],
|
||||
'slug' => $request['slug'],
|
||||
);
|
||||
|
||||
if ( ! empty( $request['offset'] ) ) {
|
||||
$prepared_args['offset'] = $request['offset'];
|
||||
} else {
|
||||
$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
|
||||
}
|
||||
|
||||
$taxonomy_obj = get_taxonomy( $taxonomy );
|
||||
|
||||
if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) {
|
||||
if ( 0 === $request['parent'] ) {
|
||||
// Only query top-level terms.
|
||||
$prepared_args['parent'] = 0;
|
||||
} else {
|
||||
if ( $request['parent'] ) {
|
||||
$prepared_args['parent'] = $request['parent'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the query arguments, before passing them to `get_terms()`.
|
||||
*
|
||||
* Enables adding extra arguments or setting defaults for a terms
|
||||
* collection request.
|
||||
*
|
||||
* @see https://developer.wordpress.org/reference/functions/get_terms/
|
||||
*
|
||||
* @param array $prepared_args Array of arguments to be
|
||||
* passed to get_terms.
|
||||
* @param WP_REST_Request $request The current request.
|
||||
*/
|
||||
$prepared_args = apply_filters( "woocommerce_rest_{$taxonomy}_query", $prepared_args, $request );
|
||||
|
||||
if ( ! empty( $prepared_args['product'] ) ) {
|
||||
$query_result = $this->get_terms_for_product( $prepared_args );
|
||||
$total_terms = $this->total_terms;
|
||||
} else {
|
||||
$query_result = get_terms( $taxonomy, $prepared_args );
|
||||
|
||||
$count_args = $prepared_args;
|
||||
unset( $count_args['number'] );
|
||||
unset( $count_args['offset'] );
|
||||
$total_terms = wp_count_terms( $taxonomy, $count_args );
|
||||
|
||||
// Ensure we don't return results when offset is out of bounds.
|
||||
// See https://core.trac.wordpress.org/ticket/35935
|
||||
if ( $prepared_args['offset'] >= $total_terms ) {
|
||||
$query_result = array();
|
||||
}
|
||||
|
||||
// wp_count_terms can return a falsy value when the term has no children.
|
||||
if ( ! $total_terms ) {
|
||||
$total_terms = 0;
|
||||
}
|
||||
}
|
||||
$response = array();
|
||||
foreach ( $query_result as $term ) {
|
||||
$data = $this->prepare_item_for_response( $term, $request );
|
||||
$response[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $response );
|
||||
|
||||
// Store pagation values for headers then unset for count query.
|
||||
$per_page = (int) $prepared_args['number'];
|
||||
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
|
||||
|
||||
$response->header( 'X-WP-Total', (int) $total_terms );
|
||||
$max_pages = ceil( $total_terms / $per_page );
|
||||
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
||||
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( '/' . $this->namespace . '/' . $this->rest_base ) );
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single term for a taxonomy.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Request|WP_Error
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
$taxonomy = $this->get_taxonomy( $request );
|
||||
$name = $request['name'];
|
||||
$args = array();
|
||||
$schema = $this->get_item_schema();
|
||||
|
||||
if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) {
|
||||
$args['description'] = $request['description'];
|
||||
}
|
||||
if ( isset( $request['slug'] ) ) {
|
||||
$args['slug'] = $request['slug'];
|
||||
}
|
||||
|
||||
if ( isset( $request['parent'] ) ) {
|
||||
if ( ! is_taxonomy_hierarchical( $taxonomy ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$parent = get_term( (int) $request['parent'], $taxonomy );
|
||||
|
||||
if ( ! $parent ) {
|
||||
return new WP_Error( 'woocommerce_rest_term_invalid', __( "Parent resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$args['parent'] = $parent->term_id;
|
||||
}
|
||||
|
||||
$term = wp_insert_term( $name, $taxonomy, $args );
|
||||
if ( is_wp_error( $term ) ) {
|
||||
|
||||
// If we're going to inform the client that the term exists, give them the identifier
|
||||
// they can actually use.
|
||||
if ( ( $term_id = $term->get_error_data( 'term_exists' ) ) ) {
|
||||
$existing_term = get_term( $term_id, $taxonomy );
|
||||
$term->add_data( $existing_term->term_id, 'term_exists' );
|
||||
}
|
||||
|
||||
return $term;
|
||||
}
|
||||
|
||||
$term = get_term( $term['term_id'], $taxonomy );
|
||||
|
||||
$this->update_additional_fields_for_object( $term, $request );
|
||||
|
||||
// Add term data.
|
||||
$meta_fields = $this->update_term_meta_fields( $term, $request );
|
||||
if ( is_wp_error( $meta_fields ) ) {
|
||||
return $meta_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single term is created or updated via the REST API.
|
||||
*
|
||||
* @param WP_Term $term Inserted Term object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating term, false when updating.
|
||||
*/
|
||||
do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $term, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
|
||||
$base = '/' . $this->namespace . '/' . $this->rest_base;
|
||||
if ( ! empty( $request['attribute_id'] ) ) {
|
||||
$base = str_replace( '(?P<attribute_id>[\d]+)', (int) $request['attribute_id'], $base );
|
||||
}
|
||||
|
||||
$response->header( 'Location', rest_url( $base . '/' . $term->term_id ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single term from a taxonomy.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Request|WP_Error
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$taxonomy = $this->get_taxonomy( $request );
|
||||
$term = get_term( (int) $request['id'], $taxonomy );
|
||||
|
||||
if ( is_wp_error( $term ) ) {
|
||||
return $term;
|
||||
}
|
||||
|
||||
$response = $this->prepare_item_for_response( $term, $request );
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single term from a taxonomy.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Request|WP_Error
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
$taxonomy = $this->get_taxonomy( $request );
|
||||
$term = get_term( (int) $request['id'], $taxonomy );
|
||||
$schema = $this->get_item_schema();
|
||||
$prepared_args = array();
|
||||
|
||||
if ( isset( $request['name'] ) ) {
|
||||
$prepared_args['name'] = $request['name'];
|
||||
}
|
||||
if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) {
|
||||
$prepared_args['description'] = $request['description'];
|
||||
}
|
||||
if ( isset( $request['slug'] ) ) {
|
||||
$prepared_args['slug'] = $request['slug'];
|
||||
}
|
||||
|
||||
if ( isset( $request['parent'] ) ) {
|
||||
if ( ! is_taxonomy_hierarchical( $taxonomy ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$parent = get_term( (int) $request['parent'], $taxonomy );
|
||||
|
||||
if ( ! $parent ) {
|
||||
return new WP_Error( 'woocommerce_rest_term_invalid', __( "Parent resource doesn't exist.", 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$prepared_args['parent'] = $parent->term_id;
|
||||
}
|
||||
|
||||
// Only update the term if we haz something to update.
|
||||
if ( ! empty( $prepared_args ) ) {
|
||||
$update = wp_update_term( $term->term_id, $term->taxonomy, $prepared_args );
|
||||
if ( is_wp_error( $update ) ) {
|
||||
return $update;
|
||||
}
|
||||
}
|
||||
|
||||
$term = get_term( (int) $request['id'], $taxonomy );
|
||||
|
||||
$this->update_additional_fields_for_object( $term, $request );
|
||||
|
||||
// Update term data.
|
||||
$meta_fields = $this->update_term_meta_fields( $term, $request );
|
||||
if ( is_wp_error( $meta_fields ) ) {
|
||||
return $meta_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single term is created or updated via the REST API.
|
||||
*
|
||||
* @param WP_Term $term Inserted Term object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating term, false when updating.
|
||||
*/
|
||||
do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, false );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $term, $request );
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single term from a taxonomy.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
$taxonomy = $this->get_taxonomy( $request );
|
||||
$term = get_term( (int) $request['id'], $taxonomy );
|
||||
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
|
||||
|
||||
// We don't support trashing for this type, error out.
|
||||
if ( ! $force ) {
|
||||
return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
|
||||
}
|
||||
|
||||
$term = get_term( (int) $request['id'], $taxonomy );
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $term, $request );
|
||||
|
||||
$retval = wp_delete_term( $term->term_id, $term->taxonomy );
|
||||
if ( ! $retval ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single term is deleted via the REST API.
|
||||
*
|
||||
* @param WP_Term $term The deleted term.
|
||||
* @param WP_REST_Response $response The response data.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( "woocommerce_rest_delete_{$taxonomy}", $term, $response, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param object $term Term object.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return array Links for the given term.
|
||||
*/
|
||||
protected function prepare_links( $term, $request ) {
|
||||
$base = '/' . $this->namespace . '/' . $this->rest_base;
|
||||
|
||||
if ( ! empty( $request['attribute_id'] ) ) {
|
||||
$base = str_replace( '(?P<attribute_id>[\d]+)', (int) $request['attribute_id'], $base );
|
||||
}
|
||||
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( trailingslashit( $base ) . $term->term_id ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( $base ),
|
||||
),
|
||||
);
|
||||
|
||||
if ( $term->parent ) {
|
||||
$parent_term = get_term( (int) $term->parent, $term->taxonomy );
|
||||
if ( $parent_term ) {
|
||||
$links['up'] = array(
|
||||
'href' => rest_url( trailingslashit( $base ) . $parent_term->term_id ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update term meta fields.
|
||||
*
|
||||
* @param WP_Term $term
|
||||
* @param WP_REST_Request $request
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function update_term_meta_fields( $term, $request ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the terms attached to a product.
|
||||
*
|
||||
* This is an alternative to `get_terms()` that uses `get_the_terms()`
|
||||
* instead, which hits the object cache. There are a few things not
|
||||
* supported, notably `include`, `exclude`. In `self::get_items()` these
|
||||
* are instead treated as a full query.
|
||||
*
|
||||
* @param array $prepared_args Arguments for `get_terms()`.
|
||||
* @return array List of term objects. (Total count in `$this->total_terms`).
|
||||
*/
|
||||
protected function get_terms_for_product( $prepared_args ) {
|
||||
$taxonomy = $this->get_taxonomy( $request );
|
||||
|
||||
$query_result = get_the_terms( $prepared_args['product'], $taxonomy );
|
||||
if ( empty( $query_result ) ) {
|
||||
$this->total_terms = 0;
|
||||
return array();
|
||||
}
|
||||
|
||||
// get_items() verifies that we don't have `include` set, and default.
|
||||
// ordering is by `name`.
|
||||
if ( ! in_array( $prepared_args['orderby'], array( 'name', 'none', 'include' ) ) ) {
|
||||
switch ( $prepared_args['orderby'] ) {
|
||||
case 'id' :
|
||||
$this->sort_column = 'term_id';
|
||||
break;
|
||||
|
||||
case 'slug' :
|
||||
case 'term_group' :
|
||||
case 'description' :
|
||||
case 'count' :
|
||||
$this->sort_column = $prepared_args['orderby'];
|
||||
break;
|
||||
}
|
||||
usort( $query_result, array( $this, 'compare_terms' ) );
|
||||
}
|
||||
if ( strtolower( $prepared_args['order'] ) !== 'asc' ) {
|
||||
$query_result = array_reverse( $query_result );
|
||||
}
|
||||
|
||||
// Pagination.
|
||||
$this->total_terms = count( $query_result );
|
||||
$query_result = array_slice( $query_result, $prepared_args['offset'], $prepared_args['number'] );
|
||||
|
||||
return $query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison function for sorting terms by a column.
|
||||
*
|
||||
* Uses `$this->sort_column` to determine field to sort by.
|
||||
*
|
||||
* @param stdClass $left Term object.
|
||||
* @param stdClass $right Term object.
|
||||
* @return int <0 if left is higher "priority" than right, 0 if equal, >0 if right is higher "priority" than left.
|
||||
*/
|
||||
protected function compare_terms( $left, $right ) {
|
||||
$col = $this->sort_column;
|
||||
$left_val = $left->$col;
|
||||
$right_val = $right->$col;
|
||||
|
||||
if ( is_int( $left_val ) && is_int( $right_val ) ) {
|
||||
return $left_val - $right_val;
|
||||
}
|
||||
|
||||
return strcmp( $left_val, $right_val );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
|
||||
if ( '' !== $this->taxonomy ) {
|
||||
$taxonomy = get_taxonomy( $this->taxonomy );
|
||||
} else {
|
||||
$taxonomy = new stdClass();
|
||||
$taxonomy->hierarchical = true;
|
||||
}
|
||||
|
||||
$params['context']['default'] = 'view';
|
||||
|
||||
$params['exclude'] = array(
|
||||
'description' => __( 'Ensure result set excludes specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['include'] = array(
|
||||
'description' => __( 'Limit result set to specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
if ( ! $taxonomy->hierarchical ) {
|
||||
$params['offset'] = array(
|
||||
'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
}
|
||||
$params['order'] = array(
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'default' => 'asc',
|
||||
'enum' => array(
|
||||
'asc',
|
||||
'desc',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'description' => __( 'Sort collection by resource attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'default' => 'name',
|
||||
'enum' => array(
|
||||
'id',
|
||||
'include',
|
||||
'name',
|
||||
'slug',
|
||||
'term_group',
|
||||
'description',
|
||||
'count',
|
||||
),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['hide_empty'] = array(
|
||||
'description' => __( 'Whether to hide resources not assigned to any products.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
if ( $taxonomy->hierarchical ) {
|
||||
$params['parent'] = array(
|
||||
'description' => __( 'Limit result set to resources assigned to a specific parent.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
}
|
||||
$params['product'] = array(
|
||||
'description' => __( 'Limit result set to resources assigned to a specific product.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => null,
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['slug'] = array(
|
||||
'description' => __( 'Limit result set to resources with a specific slug.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get taxonomy.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return int|WP_Error
|
||||
*/
|
||||
protected function get_taxonomy( $request ) {
|
||||
// Check if taxonomy is defined.
|
||||
// Prevents check for attribute taxonomy more than one time for each query.
|
||||
if ( '' !== $this->taxonomy ) {
|
||||
return $this->taxonomy;
|
||||
}
|
||||
|
||||
if ( ! empty( $request['attribute_id'] ) ) {
|
||||
$taxonomy = wc_attribute_taxonomy_name_by_id( (int) $request['attribute_id'] );
|
||||
|
||||
$this->taxonomy = $taxonomy;
|
||||
}
|
||||
|
||||
return $this->taxonomy;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Authentication
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WC_REST_Authentication {
|
||||
|
||||
/**
|
||||
* Initialize authentication actions.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'determine_current_user', array( $this, 'authenticate' ), 100 );
|
||||
add_filter( 'rest_authentication_errors', array( $this, 'check_authentication_error' ) );
|
||||
add_filter( 'rest_post_dispatch', array( $this, 'send_unauthorized_headers' ), 50 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate user.
|
||||
*
|
||||
* @param int|false $user_id User ID if one has been determined, false otherwise.
|
||||
* @return int|false
|
||||
*/
|
||||
public function authenticate( $user_id ) {
|
||||
// Do not authenticate twice!
|
||||
if ( ! empty( $user_id ) ) {
|
||||
return $user_id;
|
||||
}
|
||||
|
||||
if ( is_ssl() ) {
|
||||
return $this->perform_basic_authentication();
|
||||
} else {
|
||||
return $this->perform_oauth_authentication();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for authentication error.
|
||||
*
|
||||
* @param WP_Error|null|bool $error
|
||||
* @return WP_Error|null|bool
|
||||
*/
|
||||
public function check_authentication_error( $error ) {
|
||||
global $wc_rest_authentication_error;
|
||||
|
||||
// Passthrough other errors.
|
||||
if ( ! empty( $error ) ) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
return $wc_rest_authentication_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Authentication.
|
||||
*
|
||||
* SSL-encrypted requests are not subject to sniffing or man-in-the-middle
|
||||
* attacks, so the request can be authenticated by simply looking up the user
|
||||
* associated with the given consumer key and confirming the consumer secret
|
||||
* provided is valid.
|
||||
*
|
||||
* @return int|bool
|
||||
*/
|
||||
private function perform_basic_authentication() {
|
||||
global $wc_rest_authentication_error;
|
||||
|
||||
$user = null;
|
||||
$consumer_key = '';
|
||||
$consumer_secret = '';
|
||||
|
||||
// If the $_GET parameters are present, use those first.
|
||||
if ( ! empty( $_GET['consumer_key'] ) && ! empty( $_GET['consumer_secret'] ) ) {
|
||||
$consumer_key = $_GET['consumer_key'];
|
||||
$consumer_secret = $_GET['consumer_secret'];
|
||||
}
|
||||
|
||||
// If the above is not present, we will do full basic auth.
|
||||
if ( ! $consumer_key && ! empty( $_SERVER['PHP_AUTH_USER'] ) && ! empty( $_SERVER['PHP_AUTH_PW'] ) ) {
|
||||
$consumer_key = $_SERVER['PHP_AUTH_USER'];
|
||||
$consumer_secret = $_SERVER['PHP_AUTH_PW'];
|
||||
}
|
||||
|
||||
// Stop if don't have any key.
|
||||
if ( ! $consumer_key || ! $consumer_secret ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get user data.
|
||||
$user = $this->get_user_data_by_consumer_key( $consumer_key );
|
||||
if ( empty( $user ) ) {
|
||||
$wc_rest_authentication_error = new WP_Error( 'woocommerce_rest_authentication_error', __( 'Consumer Key is invalid.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate user secret.
|
||||
if ( ! hash_equals( $user->consumer_secret, $consumer_secret ) ) {
|
||||
$wc_rest_authentication_error = new WP_Error( 'woocommerce_rest_authentication_error', __( 'Consumer Secret is invalid.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check API Key permissions.
|
||||
if ( ! $this->check_permissions( $user->permissions ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update last access.
|
||||
$this->update_last_access( $user->key_id );
|
||||
|
||||
return $user->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform OAuth 1.0a "one-legged" (http://oauthbible.com/#oauth-10a-one-legged) authentication for non-SSL requests.
|
||||
*
|
||||
* This is required so API credentials cannot be sniffed or intercepted when making API requests over plain HTTP.
|
||||
*
|
||||
* This follows the spec for simple OAuth 1.0a authentication (RFC 5849) as closely as possible, with two exceptions:
|
||||
*
|
||||
* 1) There is no token associated with request/responses, only consumer keys/secrets are used.
|
||||
*
|
||||
* 2) The OAuth parameters are included as part of the request query string instead of part of the Authorization header,
|
||||
* This is because there is no cross-OS function within PHP to get the raw Authorization header.
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc5849 for the full spec.
|
||||
*
|
||||
* @return int|bool
|
||||
*/
|
||||
private function perform_oauth_authentication() {
|
||||
global $wc_rest_authentication_error;
|
||||
|
||||
$params = array( 'oauth_consumer_key', 'oauth_timestamp', 'oauth_nonce', 'oauth_signature', 'oauth_signature_method' );
|
||||
|
||||
// Check for required OAuth parameters.
|
||||
foreach ( $params as $param ) {
|
||||
if ( empty( $_GET[ $param ] ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch WP user by consumer key
|
||||
$user = $this->get_user_data_by_consumer_key( $_GET['oauth_consumer_key'] );
|
||||
|
||||
if ( empty( $user ) ) {
|
||||
$wc_rest_authentication_error = new WP_Error( 'woocommerce_rest_authentication_error', __( 'Consumer Key is invalid.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform OAuth validation.
|
||||
$wc_rest_authentication_error = $this->check_oauth_signature( $user, $_GET );
|
||||
if ( is_wp_error( $wc_rest_authentication_error ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$wc_rest_authentication_error = $this->check_oauth_timestamp_and_nonce( $user, $_GET['oauth_timestamp'], $_GET['oauth_nonce'] );
|
||||
if ( is_wp_error( $wc_rest_authentication_error ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check API Key permissions.
|
||||
if ( ! $this->check_permissions( $user->permissions ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update last access.
|
||||
$this->update_last_access( $user->key_id );
|
||||
|
||||
return $user->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the consumer-provided request signature matches our generated signature,
|
||||
* this ensures the consumer has a valid key/secret.
|
||||
*
|
||||
* @param stdClass $user
|
||||
* @param array $params The request parameters.
|
||||
* @return null|WP_Error
|
||||
*/
|
||||
private function check_oauth_signature( $user, $params ) {
|
||||
$http_method = strtoupper( $_SERVER['REQUEST_METHOD'] );
|
||||
$request_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
|
||||
$wp_base = get_home_url( null, '/', 'relative' );
|
||||
if ( substr( $request_path, 0, strlen( $wp_base ) ) === $wp_base ) {
|
||||
$request_path = substr( $request_path, strlen( $wp_base ) );
|
||||
}
|
||||
$base_request_uri = rawurlencode( get_home_url( null, $request_path ) );
|
||||
|
||||
// Get the signature provided by the consumer and remove it from the parameters prior to checking the signature.
|
||||
$consumer_signature = rawurldecode( $params['oauth_signature'] );
|
||||
unset( $params['oauth_signature'] );
|
||||
|
||||
// Sort parameters.
|
||||
if ( ! uksort( $params, 'strcmp' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_authentication_error', __( 'Invalid Signature - failed to sort parameters.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
}
|
||||
|
||||
// Normalize parameter key/values.
|
||||
$params = $this->normalize_parameters( $params );
|
||||
$query_parameters = array();
|
||||
foreach ( $params as $param_key => $param_value ) {
|
||||
if ( is_array( $param_value ) ) {
|
||||
foreach ( $param_value as $param_key_inner => $param_value_inner ) {
|
||||
$query_parameters[] = $param_key . '%255B' . $param_key_inner . '%255D%3D' . $param_value_inner;
|
||||
}
|
||||
} else {
|
||||
$query_parameters[] = $param_key . '%3D' . $param_value; // Join with equals sign.
|
||||
}
|
||||
}
|
||||
$query_string = implode( '%26', $query_parameters ); // Join with ampersand.
|
||||
$string_to_sign = $http_method . '&' . $base_request_uri . '&' . $query_string;
|
||||
|
||||
if ( $params['oauth_signature_method'] !== 'HMAC-SHA1' && $params['oauth_signature_method'] !== 'HMAC-SHA256' ) {
|
||||
return new WP_Error( 'woocommerce_rest_authentication_error', __( 'Invalid Signature - signature method is invalid.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
}
|
||||
|
||||
$hash_algorithm = strtolower( str_replace( 'HMAC-', '', $params['oauth_signature_method'] ) );
|
||||
$secret = $user->consumer_secret . '&';
|
||||
$signature = base64_encode( hash_hmac( $hash_algorithm, $string_to_sign, $secret, true ) );
|
||||
|
||||
if ( ! hash_equals( $signature, $consumer_signature ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_authentication_error', __( 'Invalid Signature - provided signature does not match.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize each parameter by assuming each parameter may have already been
|
||||
* encoded, so attempt to decode, and then re-encode according to RFC 3986.
|
||||
*
|
||||
* Note both the key and value is normalized so a filter param like:
|
||||
*
|
||||
* 'filter[period]' => 'week'
|
||||
*
|
||||
* is encoded to:
|
||||
*
|
||||
* 'filter%5Bperiod%5D' => 'week'
|
||||
*
|
||||
* This conforms to the OAuth 1.0a spec which indicates the entire query string
|
||||
* should be URL encoded.
|
||||
*
|
||||
* @see rawurlencode()
|
||||
* @param array $parameters Un-normalized pararmeters.
|
||||
* @return array Normalized parameters.
|
||||
*/
|
||||
private function normalize_parameters( $parameters ) {
|
||||
$keys = wc_rest_urlencode_rfc3986( array_keys( $parameters ) );
|
||||
$values = wc_rest_urlencode_rfc3986( array_values( $parameters ) );
|
||||
$parameters = array_combine( $keys, $values );
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the timestamp and nonce provided with the request are valid. This prevents replay attacks where
|
||||
* an attacker could attempt to re-send an intercepted request at a later time.
|
||||
*
|
||||
* - A timestamp is valid if it is within 15 minutes of now.
|
||||
* - A nonce is valid if it has not been used within the last 15 minutes.
|
||||
*
|
||||
* @param stdClass $user
|
||||
* @param int $timestamp the unix timestamp for when the request was made
|
||||
* @param string $nonce a unique (for the given user) 32 alphanumeric string, consumer-generated
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
private function check_oauth_timestamp_and_nonce( $user, $timestamp, $nonce ) {
|
||||
global $wpdb;
|
||||
|
||||
$valid_window = 15 * 60; // 15 minute window.
|
||||
|
||||
if ( ( $timestamp < time() - $valid_window ) || ( $timestamp > time() + $valid_window ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_authentication_error', __( 'Invalid timestamp.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
}
|
||||
|
||||
$used_nonces = maybe_unserialize( $user->nonces );
|
||||
|
||||
if ( empty( $used_nonces ) ) {
|
||||
$used_nonces = array();
|
||||
}
|
||||
|
||||
if ( in_array( $nonce, $used_nonces ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_authentication_error', __( 'Invalid nonce - nonce has already been used.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
}
|
||||
|
||||
$used_nonces[ $timestamp ] = $nonce;
|
||||
|
||||
// Remove expired nonces.
|
||||
foreach ( $used_nonces as $nonce_timestamp => $nonce ) {
|
||||
if ( $nonce_timestamp < ( time() - $valid_window ) ) {
|
||||
unset( $used_nonces[ $nonce_timestamp ] );
|
||||
}
|
||||
}
|
||||
|
||||
$used_nonces = maybe_serialize( $used_nonces );
|
||||
|
||||
$wpdb->update(
|
||||
$wpdb->prefix . 'woocommerce_api_keys',
|
||||
array( 'nonces' => $used_nonces ),
|
||||
array( 'key_id' => $user->key_id ),
|
||||
array( '%s' ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user data for the given consumer_key.
|
||||
*
|
||||
* @param string $consumer_key
|
||||
* @return array
|
||||
*/
|
||||
private function get_user_data_by_consumer_key( $consumer_key ) {
|
||||
global $wpdb;
|
||||
|
||||
$consumer_key = wc_api_hash( sanitize_text_field( $consumer_key ) );
|
||||
$user = $wpdb->get_row( $wpdb->prepare( "
|
||||
SELECT key_id, user_id, permissions, consumer_key, consumer_secret, nonces
|
||||
FROM {$wpdb->prefix}woocommerce_api_keys
|
||||
WHERE consumer_key = %s
|
||||
", $consumer_key ) );
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the API keys provided have the proper key-specific permissions to either read or write API resources.
|
||||
*
|
||||
* @param string $permissions
|
||||
* @return bool
|
||||
*/
|
||||
private function check_permissions( $permissions ) {
|
||||
global $wc_rest_authentication_error;
|
||||
|
||||
$valid = true;
|
||||
|
||||
if ( ! isset( $_SERVER['REQUEST_METHOD'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ( $_SERVER['REQUEST_METHOD'] ) {
|
||||
|
||||
case 'HEAD' :
|
||||
case 'GET' :
|
||||
if ( 'read' !== $permissions && 'read_write' !== $permissions ) {
|
||||
$wc_rest_authentication_error = new WP_Error( 'woocommerce_rest_authentication_error', __( 'The API key provided does not have read permissions.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
$valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'POST' :
|
||||
case 'PUT' :
|
||||
case 'PATCH' :
|
||||
case 'DELETE' :
|
||||
if ( 'write' !== $permissions && 'read_write' !== $permissions ) {
|
||||
$wc_rest_authentication_error = new WP_Error( 'woocommerce_rest_authentication_error', __( 'The API key provided does not have write permissions.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
$valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updated API Key last access datetime.
|
||||
*
|
||||
* @param int $key_id
|
||||
*/
|
||||
private function update_last_access( $key_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->update(
|
||||
$wpdb->prefix . 'woocommerce_api_keys',
|
||||
array( 'last_access' => current_time( 'mysql' ) ),
|
||||
array( 'key_id' => $key_id ),
|
||||
array( '%s' ),
|
||||
array( '%d' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the consumer_key and consumer_secret $_GET parameters are NOT provided
|
||||
* and the Basic auth headers are either not present or the consumer secret does not match the consumer
|
||||
* key provided, then return the correct Basic headers and an error message.
|
||||
*
|
||||
* @param WP_REST_Response $response Current response being served.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function send_unauthorized_headers( $response ) {
|
||||
global $wc_rest_authentication_error;
|
||||
|
||||
if ( is_wp_error( $wc_rest_authentication_error ) && is_ssl() ) {
|
||||
$auth_message = __( 'WooCommerce API - Use a consumer key in the username field and a consumer secret in the password field.', 'woocommerce' );
|
||||
$response->header( 'WWW-Authenticate', 'Basic realm="' . $auth_message . '"', true );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
new WC_REST_Authentication();
|
|
@ -0,0 +1,569 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Coupons controller
|
||||
*
|
||||
* Handles requests to the /coupons endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Coupons controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WC_REST_Posts_Controller
|
||||
*/
|
||||
class WC_REST_Coupons_Controller extends WC_REST_Posts_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'coupons';
|
||||
|
||||
/**
|
||||
* Post type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $post_type = 'shop_coupon';
|
||||
|
||||
/**
|
||||
* Order refunds actions.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'query_args' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the routes for coupons.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
|
||||
'code' => array(
|
||||
'required' => true,
|
||||
),
|
||||
) ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Query args.
|
||||
*
|
||||
* @param array $args
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function query_args( $args, $request ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! empty( $request['code'] ) ) {
|
||||
$id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish'", $request['code'] ) );
|
||||
|
||||
$args['post__in'] = array( $id );
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single coupon output for response.
|
||||
*
|
||||
* @param WP_Post $post Post object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $data
|
||||
*/
|
||||
public function prepare_item_for_response( $post, $request ) {
|
||||
global $wpdb;
|
||||
|
||||
// Get the coupon code.
|
||||
$code = $wpdb->get_var( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE id = %s AND post_type = 'shop_coupon' AND post_status = 'publish'", $post->ID ) );
|
||||
|
||||
$coupon = new WC_Coupon( $code );
|
||||
|
||||
$data = array(
|
||||
'id' => $coupon->id,
|
||||
'code' => $coupon->code,
|
||||
'date_created' => wc_rest_prepare_date_response( $post->post_date_gmt ),
|
||||
'date_modified' => wc_rest_prepare_date_response( $post->post_modified_gmt ),
|
||||
'discount_type' => $coupon->type,
|
||||
'description' => $post->post_excerpt,
|
||||
'amount' => wc_format_decimal( $coupon->coupon_amount, 2 ),
|
||||
'expiry_date' => ( ! empty( $coupon->expiry_date ) ) ? wc_rest_prepare_date_response( $coupon->expiry_date ) : null,
|
||||
'usage_count' => (int) $coupon->usage_count,
|
||||
'individual_use' => ( 'yes' === $coupon->individual_use ),
|
||||
'product_ids' => array_map( 'absint', (array) $coupon->product_ids ),
|
||||
'exclude_product_ids' => array_map( 'absint', (array) $coupon->exclude_product_ids ),
|
||||
'usage_limit' => ( ! empty( $coupon->usage_limit ) ) ? $coupon->usage_limit : null,
|
||||
'usage_limit_per_user' => ( ! empty( $coupon->usage_limit_per_user ) ) ? $coupon->usage_limit_per_user : null,
|
||||
'limit_usage_to_x_items' => (int) $coupon->limit_usage_to_x_items,
|
||||
'free_shipping' => $coupon->enable_free_shipping(),
|
||||
'product_categories' => array_map( 'absint', (array) $coupon->product_categories ),
|
||||
'excluded_product_categories' => array_map( 'absint', (array) $coupon->exclude_product_categories ),
|
||||
'exclude_sale_items' => $coupon->exclude_sale_items(),
|
||||
'minimum_amount' => wc_format_decimal( $coupon->minimum_amount, 2 ),
|
||||
'maximum_amount' => wc_format_decimal( $coupon->maximum_amount, 2 ),
|
||||
'email_restrictions' => $coupon->customer_email,
|
||||
'used_by' => $coupon->get_used_by(),
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $post ) );
|
||||
|
||||
/**
|
||||
* Filter the data for a response.
|
||||
*
|
||||
* The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
|
||||
* prepared for the response.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Post $post Post object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $post, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single coupon for create or update.
|
||||
*
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_Error|stdClass $data Post object.
|
||||
*/
|
||||
protected function prepare_item_for_database( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$data = new stdClass;
|
||||
|
||||
// ID.
|
||||
if ( isset( $request['id'] ) ) {
|
||||
$data->ID = absint( $request['id'] );
|
||||
}
|
||||
|
||||
$schema = $this->get_item_schema();
|
||||
|
||||
// Validate required POST fields.
|
||||
if ( 'POST' === $request->get_method() && empty( $data->ID ) ) {
|
||||
if ( empty( $request['code'] ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_empty_coupon_code', sprintf( __( 'The coupon code cannot be empty.', 'woocommerce' ), 'code' ), array( 'status' => 400 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Code.
|
||||
if ( ! empty( $schema['properties']['code'] ) && ! empty( $request['code'] ) ) {
|
||||
$coupon_code = apply_filters( 'woocommerce_coupon_code', $request['code'] );
|
||||
$id = isset( $data->ID ) ? $data->ID : 0;
|
||||
|
||||
// Check for duplicate coupon codes.
|
||||
$coupon_found = $wpdb->get_var( $wpdb->prepare( "
|
||||
SELECT $wpdb->posts.ID
|
||||
FROM $wpdb->posts
|
||||
WHERE $wpdb->posts.post_type = 'shop_coupon'
|
||||
AND $wpdb->posts.post_status = 'publish'
|
||||
AND $wpdb->posts.post_title = '%s'
|
||||
AND $wpdb->posts.ID != %s
|
||||
", $coupon_code, $id ) );
|
||||
|
||||
if ( $coupon_found ) {
|
||||
return new WP_Error( 'woocommerce_rest_coupon_code_already_exists', __( 'The coupon code already exists', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$data->post_title = $coupon_code;
|
||||
}
|
||||
|
||||
// Content.
|
||||
$data->post_content = '';
|
||||
|
||||
// Excerpt.
|
||||
if ( ! empty( $schema['properties']['excerpt'] ) && isset( $request['description'] ) ) {
|
||||
$data->post_excerpt = wp_filter_post_kses( $request['description'] );
|
||||
}
|
||||
|
||||
// Post type.
|
||||
$data->post_type = $this->post_type;
|
||||
|
||||
// Post status.
|
||||
$data->post_status = 'publish';
|
||||
|
||||
// Comment status.
|
||||
$data->comment_status = 'closed';
|
||||
|
||||
// Ping status.
|
||||
$data->ping_status = 'closed';
|
||||
|
||||
/**
|
||||
* Filter the query_vars used in `get_items` for the constructed query.
|
||||
*
|
||||
* The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
|
||||
* prepared for insertion.
|
||||
*
|
||||
* @param stdClass $data An object representing a single item prepared
|
||||
* for inserting or updating the database.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $data, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Expiry date format.
|
||||
*
|
||||
* @param string $expiry_date
|
||||
* @return string
|
||||
*/
|
||||
protected function get_coupon_expiry_date( $expiry_date ) {
|
||||
if ( '' != $expiry_date ) {
|
||||
return date( 'Y-m-d', strtotime( $expiry_date ) );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add post meta fields.
|
||||
*
|
||||
* @param WP_Post $post
|
||||
* @param WP_REST_Request $request
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function add_post_meta_fields( $post, $request ) {
|
||||
$data = $request->get_json_params();
|
||||
|
||||
$defaults = array(
|
||||
'discount_type' => 'fixed_cart',
|
||||
'amount' => 0,
|
||||
'individual_use' => false,
|
||||
'product_ids' => array(),
|
||||
'exclude_product_ids' => array(),
|
||||
'usage_limit' => '',
|
||||
'usage_limit_per_user' => '',
|
||||
'limit_usage_to_x_items' => '',
|
||||
'usage_count' => '',
|
||||
'expiry_date' => '',
|
||||
'free_shipping' => false,
|
||||
'product_categories' => array(),
|
||||
'excluded_product_categories' => array(),
|
||||
'exclude_sale_items' => false,
|
||||
'minimum_amount' => '',
|
||||
'maximum_amount' => '',
|
||||
'email_restrictions' => array(),
|
||||
'description' => ''
|
||||
);
|
||||
|
||||
$data = wp_parse_args( $data, $defaults );
|
||||
|
||||
// Set coupon meta.
|
||||
update_post_meta( $post->ID, 'discount_type', $data['discount_type'] );
|
||||
update_post_meta( $post->ID, 'coupon_amount', wc_format_decimal( $data['amount'] ) );
|
||||
update_post_meta( $post->ID, 'individual_use', ( true === $data['individual_use'] ) ? 'yes' : 'no' );
|
||||
update_post_meta( $post->ID, 'product_ids', implode( ',', array_filter( array_map( 'intval', $data['product_ids'] ) ) ) );
|
||||
update_post_meta( $post->ID, 'exclude_product_ids', implode( ',', array_filter( array_map( 'intval', $data['exclude_product_ids'] ) ) ) );
|
||||
update_post_meta( $post->ID, 'usage_limit', absint( $data['usage_limit'] ) );
|
||||
update_post_meta( $post->ID, 'usage_limit_per_user', absint( $data['usage_limit_per_user'] ) );
|
||||
update_post_meta( $post->ID, 'limit_usage_to_x_items', absint( $data['limit_usage_to_x_items'] ) );
|
||||
update_post_meta( $post->ID, 'usage_count', absint( $data['usage_count'] ) );
|
||||
update_post_meta( $post->ID, 'expiry_date', $this->get_coupon_expiry_date( wc_clean( $data['expiry_date'] ) ) );
|
||||
update_post_meta( $post->ID, 'free_shipping', ( true === $data['free_shipping'] ) ? 'yes' : 'no' );
|
||||
update_post_meta( $post->ID, 'product_categories', array_filter( array_map( 'intval', $data['product_categories'] ) ) );
|
||||
update_post_meta( $post->ID, 'exclude_product_categories', array_filter( array_map( 'intval', $data['excluded_product_categories'] ) ) );
|
||||
update_post_meta( $post->ID, 'exclude_sale_items', ( true === $data['exclude_sale_items'] ) ? 'yes' : 'no' );
|
||||
update_post_meta( $post->ID, 'minimum_amount', wc_format_decimal( $data['minimum_amount'] ) );
|
||||
update_post_meta( $post->ID, 'maximum_amount', wc_format_decimal( $data['maximum_amount'] ) );
|
||||
update_post_meta( $post->ID, 'customer_email', array_filter( array_map( 'sanitize_email', $data['email_restrictions'] ) ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update post meta fields.
|
||||
*
|
||||
* @param WP_Post $post
|
||||
* @param WP_REST_Request $request
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function update_post_meta_fields( $post, $request ) {
|
||||
if ( isset( $request['amount'] ) ) {
|
||||
update_post_meta( $post->ID, 'coupon_amount', wc_format_decimal( $request['amount'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['individual_use'] ) ) {
|
||||
update_post_meta( $post->ID, 'individual_use', ( true === $request['individual_use'] ) ? 'yes' : 'no' );
|
||||
}
|
||||
|
||||
if ( isset( $request['product_ids'] ) ) {
|
||||
update_post_meta( $post->ID, 'product_ids', implode( ',', array_filter( array_map( 'intval', $request['product_ids'] ) ) ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['exclude_product_ids'] ) ) {
|
||||
update_post_meta( $post->ID, 'exclude_product_ids', implode( ',', array_filter( array_map( 'intval', $request['exclude_product_ids'] ) ) ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['usage_limit'] ) ) {
|
||||
update_post_meta( $post->ID, 'usage_limit', absint( $request['usage_limit'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['usage_limit_per_user'] ) ) {
|
||||
update_post_meta( $post->ID, 'usage_limit_per_user', absint( $request['usage_limit_per_user'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['limit_usage_to_x_items'] ) ) {
|
||||
update_post_meta( $post->ID, 'limit_usage_to_x_items', absint( $request['limit_usage_to_x_items'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['usage_count'] ) ) {
|
||||
update_post_meta( $post->ID, 'usage_count', absint( $request['usage_count'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['expiry_date'] ) ) {
|
||||
update_post_meta( $post->ID, 'expiry_date', $this->get_coupon_expiry_date( wc_clean( $request['expiry_date'] ) ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['free_shipping'] ) ) {
|
||||
update_post_meta( $post->ID, 'free_shipping', ( true === $request['free_shipping'] ) ? 'yes' : 'no' );
|
||||
}
|
||||
|
||||
if ( isset( $request['product_categories'] ) ) {
|
||||
update_post_meta( $post->ID, 'product_categories', array_filter( array_map( 'intval', $request['product_categories'] ) ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['excluded_product_categories'] ) ) {
|
||||
update_post_meta( $post->ID, 'exclude_product_categories', array_filter( array_map( 'intval', $request['excluded_product_categories'] ) ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['exclude_sale_items'] ) ) {
|
||||
update_post_meta( $post->ID, 'exclude_sale_items', ( true === $request['exclude_sale_items'] ) ? 'yes' : 'no' );
|
||||
}
|
||||
|
||||
if ( isset( $request['minimum_amount'] ) ) {
|
||||
update_post_meta( $post->ID, 'minimum_amount', wc_format_decimal( $request['minimum_amount'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['maximum_amount'] ) ) {
|
||||
update_post_meta( $post->ID, 'maximum_amount', wc_format_decimal( $request['maximum_amount'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $request['email_restrictions'] ) ) {
|
||||
update_post_meta( $post->ID, 'customer_email', array_filter( array_map( 'sanitize_email', $request['email_restrictions'] ) ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Coupon's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => $this->post_type,
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the object.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'code' => array(
|
||||
'description' => __( 'Coupon code.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'date_created' => array(
|
||||
'description' => __( "The date the coupon was created, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_modified' => array(
|
||||
'description' => __( "The date the coupon was last modified, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'Coupon description.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'discount_type' => array(
|
||||
'description' => __( 'Determines the type of discount that will be applied.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array_keys( wc_get_coupon_types() ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'amount' => array(
|
||||
'description' => __( 'The amount of discount.', 'woocommerce' ),
|
||||
'type' => 'float',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'expiry_date' => array(
|
||||
'description' => __( 'UTC DateTime when the coupon expires.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'usage_count' => array(
|
||||
'description' => __( 'Number of times the coupon has been used already.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'individual_use' => array(
|
||||
'description' => __( 'Whether coupon can only be used individually.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'product_ids' => array(
|
||||
'description' => __( "List of product ID's the coupon can be used on.", 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'exclude_product_ids' => array(
|
||||
'description' => __( "List of product ID's the coupon cannot be used on.", 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'usage_limit' => array(
|
||||
'description' => __( 'How many times the coupon can be used.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'usage_limit_per_user' => array(
|
||||
'description' => __( 'How many times the coupon can be user per customer.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'limit_usage_to_x_items' => array(
|
||||
'description' => __( 'Max number of items in the cart the coupon can be applied to.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'free_shipping' => array(
|
||||
'description' => __( 'Define if can be applied for free shipping.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'product_categories' => array(
|
||||
'description' => __( "List of category ID's the coupon applies to.", 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'excluded_product_categories' => array(
|
||||
'description' => __( "List of category ID's the coupon does not apply to.", 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'exclude_sale_items' => array(
|
||||
'description' => __( 'Define if should not apply when have sale items.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'minimum_amount' => array(
|
||||
'description' => __( 'Minimum order amount that needs to be in the cart before coupon applies.', 'woocommerce' ),
|
||||
'type' => 'float',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'maximum_amount' => array(
|
||||
'description' => __( 'Maximum order amount allowed when using the coupon.', 'woocommerce' ),
|
||||
'type' => 'float',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'email_restrictions' => array(
|
||||
'description' => __( 'List of email addresses that can use this coupon.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'used_by' => array(
|
||||
'description' => __( 'List of user IDs who have used the coupon.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections of attachments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
|
||||
$params['code'] = array(
|
||||
'description' => __( 'Limit result set to resources with a specific code.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Customer Downloads controller
|
||||
*
|
||||
* Handles requests to the /customers/<customer_id>/downloads endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Customers controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Customer_Downloads_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'customers/(?P<customer_id>[\d]+)/downloads';
|
||||
|
||||
/**
|
||||
* Register the routes for customers.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read customers.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
$customer = get_user_by( 'id', (int) $request['customer_id'] );
|
||||
|
||||
if ( ! $customer ) {
|
||||
return new WP_Error( "woocommerce_rest_customer_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! wc_rest_check_user_permissions( 'read', $customer->id ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all customer downloads.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$downloads = wc_get_customer_available_downloads( (int) $request['customer_id'] );
|
||||
|
||||
$data = array();
|
||||
foreach ( $downloads as $download_data ) {
|
||||
$download = $this->prepare_item_for_response( (object) $download_data, $request );
|
||||
$download = $this->prepare_response_for_collection( $download );
|
||||
$data[] = $download;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single download output for response.
|
||||
*
|
||||
* @param stdObject $download Download object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $download, $request ) {
|
||||
$data = (array) $download;
|
||||
$data['access_expires'] = $data['access_expires'] ? wc_rest_prepare_date_response( $data['access_expires'] ) : 'never';
|
||||
$data['downloads_remaining'] = '' === $data['downloads_remaining'] ? 'unlimited' : $data['downloads_remaining'];
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $download, $request ) );
|
||||
|
||||
/**
|
||||
* Filter customer download data returned from the REST API.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param stdObject $download Download object used to create response.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_customer_download', $response, $download, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param stdClass $download Download object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return array Links for the given customer download.
|
||||
*/
|
||||
protected function prepare_links( $download, $request ) {
|
||||
$base = str_replace( '(?P<customer_id>[\d]+)', $request['customer_id'], $this->rest_base );
|
||||
$links = array(
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),
|
||||
),
|
||||
'product' => array(
|
||||
'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $download->product_id ) ),
|
||||
),
|
||||
'order' => array(
|
||||
'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $download->order_id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Customer Download's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'customer_download',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'download_url' => array(
|
||||
'description' => __( 'Download file URL.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'download_id' => array(
|
||||
'description' => __( 'Download ID (MD5).', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'product_id' => array(
|
||||
'description' => __( 'Downloadable product ID.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'download_name' => array(
|
||||
'description' => __( 'Downloadable file name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'order_id' => array(
|
||||
'description' => __( 'Order ID.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'order_key' => array(
|
||||
'description' => __( 'Order key.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'downloads_remaining' => array(
|
||||
'description' => __( 'Amount of downloads remaining.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'access_expires' => array(
|
||||
'description' => __( "The date when the download access expires, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'file' => array(
|
||||
'description' => __( 'File details', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'File name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'file' => array(
|
||||
'description' => __( 'File URL.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,903 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Customers controller
|
||||
*
|
||||
* Handles requests to the /customers endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Customers controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Customers_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'customers';
|
||||
|
||||
/**
|
||||
* Register the routes for customers.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
|
||||
'email' => array(
|
||||
'required' => true,
|
||||
),
|
||||
'username' => array(
|
||||
'required' => 'no' === get_option( 'woocommerce_registration_generate_username', 'yes' ),
|
||||
),
|
||||
'password' => array(
|
||||
'required' => 'no' === get_option( 'woocommerce_registration_generate_password', 'no' ),
|
||||
),
|
||||
) ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||
),
|
||||
'reassign' => array(),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/me', array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_current_item' ),
|
||||
'args' => array(
|
||||
'context' => array(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read customers.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_user_permissions( 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access create customers.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_user_permissions( 'create' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read a customer.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
|
||||
if ( ! wc_rest_check_user_permissions( 'read', $id ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access update a customer.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function update_item_permissions_check( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
|
||||
if ( ! wc_rest_check_user_permissions( 'edit', $id ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access delete a customer.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
|
||||
if ( ! wc_rest_check_user_permissions( 'delete', $id ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all customers.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$prepared_args = array();
|
||||
$prepared_args['exclude'] = $request['exclude'];
|
||||
$prepared_args['include'] = $request['include'];
|
||||
$prepared_args['order'] = $request['order'];
|
||||
$prepared_args['number'] = $request['per_page'];
|
||||
if ( ! empty( $request['offset'] ) ) {
|
||||
$prepared_args['offset'] = $request['offset'];
|
||||
} else {
|
||||
$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
|
||||
}
|
||||
$orderby_possibles = array(
|
||||
'id' => 'ID',
|
||||
'include' => 'include',
|
||||
'name' => 'display_name',
|
||||
'registered_date' => 'registered',
|
||||
);
|
||||
$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
|
||||
$prepared_args['search'] = $request['search'];
|
||||
|
||||
if ( '' !== $prepared_args['search'] ) {
|
||||
$prepared_args['search'] = '*' . $prepared_args['search'] . '*';
|
||||
}
|
||||
|
||||
// Filter by email.
|
||||
if ( ! empty( $request['email'] ) ) {
|
||||
$prepared_args['search'] = $request['email'];
|
||||
$prepared_args['search_columns'] = array( 'user_email' );
|
||||
}
|
||||
|
||||
// Filter by role.
|
||||
if ( 'all' !== $request['role'] ) {
|
||||
$prepared_args['role'] = $request['role'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter arguments, before passing to WP_User_Query, when querying users via the REST API.
|
||||
*
|
||||
* @see https://developer.wordpress.org/reference/classes/wp_user_query/
|
||||
*
|
||||
* @param array $prepared_args Array of arguments for WP_User_Query.
|
||||
* @param WP_REST_Request $request The current request.
|
||||
*/
|
||||
$prepared_args = apply_filters( 'woocommerce_rest_customer_query', $prepared_args, $request );
|
||||
|
||||
$query = new WP_User_Query( $prepared_args );
|
||||
|
||||
$users = array();
|
||||
foreach ( $query->results as $user ) {
|
||||
$data = $this->prepare_item_for_response( $user, $request );
|
||||
$users[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $users );
|
||||
|
||||
// Store pagation values for headers then unset for count query.
|
||||
$per_page = (int) $prepared_args['number'];
|
||||
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
|
||||
|
||||
$prepared_args['fields'] = 'ID';
|
||||
|
||||
$total_users = $query->get_total();
|
||||
if ( $total_users < 1 ) {
|
||||
// Out-of-bounds, run the query again without LIMIT for total count.
|
||||
unset( $prepared_args['number'] );
|
||||
unset( $prepared_args['offset'] );
|
||||
$count_query = new WP_User_Query( $prepared_args );
|
||||
$total_users = $count_query->get_total();
|
||||
}
|
||||
$response->header( 'X-WP-Total', (int) $total_users );
|
||||
$max_pages = ceil( $total_users / $per_page );
|
||||
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
||||
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single customer.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
if ( ! empty( $request['id'] ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_customer_exists', __( 'Cannot create existing resource.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// Sets the username.
|
||||
$request['username'] = ! empty( $request['username'] ) ? $request['username'] : '';
|
||||
|
||||
// Sets the password.
|
||||
$request['password'] = ! empty( $request['password'] ) ? $request['password'] : '';
|
||||
|
||||
// Create customer.
|
||||
$customer_id = wc_create_new_customer( $request['email'], $request['username'], $request['password'] );;
|
||||
if ( is_wp_error( $customer_id ) ) {
|
||||
return $customer_id;
|
||||
}
|
||||
|
||||
$customer = get_user_by( 'id', $customer_id );
|
||||
|
||||
$this->update_additional_fields_for_object( $customer, $request );
|
||||
|
||||
// Add customer data.
|
||||
$this->update_customer_meta_fields( $customer, $request );
|
||||
|
||||
/**
|
||||
* Fires after a customer is created or updated via the REST API.
|
||||
*
|
||||
* @param WP_User $customer Data used to create the customer.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating customer, false when updating customer.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_insert_customer', $customer, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $customer, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $customer_id ) ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single customer.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$customer = get_userdata( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $customer->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$customer = $this->prepare_item_for_response( $customer, $request );
|
||||
$response = rest_ensure_response( $customer );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single user.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$customer = get_userdata( $id );
|
||||
|
||||
if ( ! $customer ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $request['email'] ) && email_exists( $request['email'] ) && $request['email'] !== $customer->user_email ) {
|
||||
return new WP_Error( 'woocommerce_rest_customer_invalid_email', __( 'Email address is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $request['username'] ) && $request['username'] !== $customer->user_login ) {
|
||||
return new WP_Error( 'woocommerce_rest_customer_invalid_argument', __( "Username isn't editable", 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// Customer email.
|
||||
if ( isset( $request['email'] ) ) {
|
||||
wp_update_user( array( 'ID' => $customer->ID, 'user_email' => sanitize_email( $request['email'] ) ) );
|
||||
}
|
||||
|
||||
// Customer password.
|
||||
if ( isset( $request['password'] ) ) {
|
||||
wp_update_user( array( 'ID' => $customer->ID, 'user_pass' => wc_clean( $request['password'] ) ) );
|
||||
}
|
||||
|
||||
$this->update_additional_fields_for_object( $customer, $request );
|
||||
|
||||
// Update customer data.
|
||||
$this->update_customer_meta_fields( $customer, $request );
|
||||
|
||||
/**
|
||||
* Fires after a customer is created or updated via the REST API.
|
||||
*
|
||||
* @param WP_User $customer Data used to create the customer.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating customer, false when updating customer.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_insert_customer', $customer, $request, false );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $customer, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single customer.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$reassign = isset( $request['reassign'] ) ? absint( $request['reassign'] ) : null;
|
||||
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
|
||||
|
||||
// We don't support trashing for this type, error out.
|
||||
if ( ! $force ) {
|
||||
return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Customers do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
|
||||
}
|
||||
|
||||
$customer = get_userdata( $id );
|
||||
if ( ! $customer ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $reassign ) ) {
|
||||
if ( $reassign === $id || ! get_userdata( $reassign ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_customer_invalid_reassign', __( 'Invalid resource id for reassignment.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
}
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $customer, $request );
|
||||
|
||||
/** Include admin customer functions to get access to wp_delete_user() */
|
||||
require_once ABSPATH . 'wp-admin/includes/user.php';
|
||||
|
||||
$result = wp_delete_user( $id, $reassign );
|
||||
|
||||
if ( ! $result ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a customer is deleted via the REST API.
|
||||
*
|
||||
* @param WP_User $customer The customer data.
|
||||
* @param WP_REST_Response $response The response returned from the API.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_delete_customer', $customer, $response, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current customer.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_current_item( $request ) {
|
||||
$id = get_current_user_id();
|
||||
if ( empty( $id ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_not_logged_in', __( 'You are not currently logged in.', 'woocommerce' ), array( 'status' => 401 ) );
|
||||
}
|
||||
|
||||
if ( ! wc_rest_check_user_permissions( 'read', $id ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
$customer = wp_get_current_user();
|
||||
$response = $this->prepare_item_for_response( $customer, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) );
|
||||
$response->set_status( 302 );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single customer output for response.
|
||||
*
|
||||
* @param WP_User $customer Customer object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $customer, $request ) {
|
||||
$last_order = wc_get_customer_last_order( $customer->ID );
|
||||
|
||||
$data = array(
|
||||
'id' => $customer->ID,
|
||||
'date_created' => wc_rest_prepare_date_response( $customer->user_registered ),
|
||||
'date_modified' => $customer->last_update ? wc_rest_prepare_date_response( date( 'Y-m-d H:i:s', $customer->last_update ) ) : null,
|
||||
'email' => $customer->user_email,
|
||||
'first_name' => $customer->first_name,
|
||||
'last_name' => $customer->last_name,
|
||||
'username' => $customer->user_login,
|
||||
'last_order' => array(
|
||||
'id' => is_object( $last_order ) ? $last_order->id : null,
|
||||
'date' => is_object( $last_order ) ? wc_rest_prepare_date_response( $last_order->post->post_date_gmt ) : null
|
||||
),
|
||||
'orders_count' => wc_get_customer_order_count( $customer->ID ),
|
||||
'total_spent' => wc_format_decimal( wc_get_customer_total_spent( $customer->ID ), 2 ),
|
||||
'avatar_url' => wc_get_customer_avatar_url( $customer->customer_email ),
|
||||
'billing_address' => array(
|
||||
'first_name' => $customer->billing_first_name,
|
||||
'last_name' => $customer->billing_last_name,
|
||||
'company' => $customer->billing_company,
|
||||
'address_1' => $customer->billing_address_1,
|
||||
'address_2' => $customer->billing_address_2,
|
||||
'city' => $customer->billing_city,
|
||||
'state' => $customer->billing_state,
|
||||
'postcode' => $customer->billing_postcode,
|
||||
'country' => $customer->billing_country,
|
||||
'email' => $customer->billing_email,
|
||||
'phone' => $customer->billing_phone,
|
||||
),
|
||||
'shipping_address' => array(
|
||||
'first_name' => $customer->shipping_first_name,
|
||||
'last_name' => $customer->shipping_last_name,
|
||||
'company' => $customer->shipping_company,
|
||||
'address_1' => $customer->shipping_address_1,
|
||||
'address_2' => $customer->shipping_address_2,
|
||||
'city' => $customer->shipping_city,
|
||||
'state' => $customer->shipping_state,
|
||||
'postcode' => $customer->shipping_postcode,
|
||||
'country' => $customer->shipping_country,
|
||||
),
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $customer ) );
|
||||
|
||||
/**
|
||||
* Filter customer data returned from the REST API.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_User $customer User object used to create response.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_customer', $response, $customer, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update customer meta fields.
|
||||
*
|
||||
* @param WP_User $customer
|
||||
* @param WP_REST_Request $request
|
||||
*/
|
||||
protected function update_customer_meta_fields( $customer, $request ) {
|
||||
$schema = $this->get_item_schema();
|
||||
|
||||
// Customer first name.
|
||||
if ( isset( $request['first_name'] ) ) {
|
||||
update_user_meta( $customer->ID, 'first_name', wc_clean( $request['first_name'] ) );
|
||||
}
|
||||
|
||||
// Customer last name.
|
||||
if ( isset( $request['last_name'] ) ) {
|
||||
update_user_meta( $customer->ID, 'last_name', wc_clean( $request['last_name'] ) );
|
||||
}
|
||||
|
||||
// Customer billing address.
|
||||
if ( isset( $request['billing_address'] ) ) {
|
||||
foreach ( array_keys( $schema['properties']['billing_address']['properties'] ) as $address ) {
|
||||
if ( isset( $request['billing_address'][ $address ] ) ) {
|
||||
update_user_meta( $customer->ID, 'billing_' . $address, wc_clean( $request['billing_address'][ $address ] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Customer shipping address.
|
||||
if ( isset( $request['shipping_address'] ) ) {
|
||||
foreach ( array_keys( $schema['properties']['shipping_address']['properties'] ) as $address ) {
|
||||
if ( isset( $request['shipping_address'][ $address ] ) ) {
|
||||
update_user_meta( $customer->ID, 'shipping_' . $address, wc_clean( $request['shipping_address'][ $address ] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param WP_User $customer Customer object.
|
||||
* @return array Links for the given customer.
|
||||
*/
|
||||
protected function prepare_links( $customer ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $customer->ID ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Customer's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'customer',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_created' => array(
|
||||
'description' => __( "The date the customer was created, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_modified' => array(
|
||||
'description' => __( "The date the customer was last modified, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'email' => array(
|
||||
'description' => __( 'The email address for the customer.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'email',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'first_name' => array(
|
||||
'description' => __( 'Customer first name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
'last_name' => array(
|
||||
'description' => __( 'Customer last name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
'username' => array(
|
||||
'description' => __( 'Customer login name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_user',
|
||||
),
|
||||
),
|
||||
'password' => array(
|
||||
'description' => __( 'Customer password.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'edit' ),
|
||||
),
|
||||
'last_order' => array(
|
||||
'description' => __( 'Last order data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Last order ID.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date' => array(
|
||||
'description' => __( 'UTC DateTime of the customer last order.', 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
'orders_count' => array(
|
||||
'description' => __( 'Quantity of orders made by the customer.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'total_spent' => array(
|
||||
'description' => __( 'Total amount spent.', 'woocommerce' ),
|
||||
'type' => 'float',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'avatar_url' => array(
|
||||
'description' => __( 'Avatar URL.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'billing_address' => array(
|
||||
'description' => __( 'List of billing address data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'properties' => array(
|
||||
'first_name' => array(
|
||||
'description' => __( 'First name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'last_name' => array(
|
||||
'description' => __( 'Last name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'company' => array(
|
||||
'description' => __( 'Company name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'address_1' => array(
|
||||
'description' => __( 'Address line 1.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'address_2' => array(
|
||||
'description' => __( 'Address line 2.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'city' => array(
|
||||
'description' => __( 'City name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'state' => array(
|
||||
'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'postcode' => array(
|
||||
'description' => __( 'Postal code.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'country' => array(
|
||||
'description' => __( 'ISO code of the country.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'email' => array(
|
||||
'description' => __( 'Email address.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'email',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'phone' => array(
|
||||
'description' => __( 'Phone number.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'shipping_address' => array(
|
||||
'description' => __( 'List of shipping address data.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'properties' => array(
|
||||
'first_name' => array(
|
||||
'description' => __( 'First name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'last_name' => array(
|
||||
'description' => __( 'Last name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'company' => array(
|
||||
'description' => __( 'Company name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'address_1' => array(
|
||||
'description' => __( 'Address line 1.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'address_2' => array(
|
||||
'description' => __( 'Address line 2.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'city' => array(
|
||||
'description' => __( 'City name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'state' => array(
|
||||
'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'postcode' => array(
|
||||
'description' => __( 'Postal code.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'country' => array(
|
||||
'description' => __( 'ISO code of the country.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get role names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_role_names() {
|
||||
global $wp_roles;
|
||||
|
||||
return array_keys( $wp_roles->role_names );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
|
||||
$params['context']['default'] = 'view';
|
||||
|
||||
$params['exclude'] = array(
|
||||
'description' => __( 'Ensure result set excludes specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['include'] = array(
|
||||
'description' => __( 'Limit result set to specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['offset'] = array(
|
||||
'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'default' => 'asc',
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'type' => 'string',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'default' => 'name',
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'enum' => array(
|
||||
'id',
|
||||
'include',
|
||||
'name',
|
||||
'registered_date',
|
||||
),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'type' => 'string',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['email'] = array(
|
||||
'description' => __( 'Limit result set to resources with a specific email.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'email',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['role'] = array(
|
||||
'description' => __( 'Limit result set to resources with a specific role.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'customer',
|
||||
'enum' => array_merge( array( 'all' ), $this->get_role_names() ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
return $params;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce REST Exception Class
|
||||
*
|
||||
* Extends Exception to provide additional data.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WC_REST_Exception extends WC_API_Exception {
|
||||
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Order Notes controller
|
||||
*
|
||||
* Handles requests to the /orders/<order_id>/notes endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Order Notes controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Order_Notes_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'orders/(?P<order_id>[\d]+)/notes';
|
||||
|
||||
/**
|
||||
* Register the routes for order notes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
|
||||
'note' => array(
|
||||
'required' => true,
|
||||
),
|
||||
) ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read order notes.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_post_permissions( 'shop_order', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access create order notes.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_post_permissions( 'shop_order', 'create' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read a order note.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
$post = get_post( (int) $request['order_id'] );
|
||||
|
||||
if ( $post && ! wc_rest_check_post_permissions( 'shop_order', 'read', $post->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access delete a order note.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
$post = get_post( (int) $request['order_id'] );
|
||||
|
||||
if ( $post && ! wc_rest_check_post_permissions( 'shop_order', 'delete', $post->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order notes from an order.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$order = get_post( (int) $request['order_id'] );
|
||||
|
||||
if ( empty( $order->post_type ) || 'shop_order' !== $order->post_type ) {
|
||||
return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'post_id' => $order->ID,
|
||||
'approve' => 'approve',
|
||||
'type' => 'order_note',
|
||||
);
|
||||
|
||||
remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
|
||||
|
||||
$notes = get_comments( $args );
|
||||
|
||||
add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_order_comments' ), 10, 1 );
|
||||
|
||||
$data = array();
|
||||
foreach ( $notes as $note ) {
|
||||
$order_note = $this->prepare_item_for_response( $note, $request );
|
||||
$order_note = $this->prepare_response_for_collection( $order_note );
|
||||
$data[] = $order_note;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single order note.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
if ( ! empty( $request['id'] ) ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$order = get_post( (int) $request['order_id'] );
|
||||
|
||||
if ( empty( $order->post_type ) || 'shop_order' !== $order->post_type ) {
|
||||
return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$order = wc_get_order( $order );
|
||||
|
||||
// Create the note.
|
||||
$note_id = $order->add_order_note( $request['note'], $request['customer_note'] );
|
||||
|
||||
if ( ! $note_id ) {
|
||||
return new WP_Error( 'woocommerce_api_cannot_create_order_note', __( 'Cannot create order note, please try again.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
$note = get_comment( $note_id );
|
||||
$this->update_additional_fields_for_object( $note, $request );
|
||||
|
||||
/**
|
||||
* Fires after a order note is created or updated via the REST API.
|
||||
*
|
||||
* @param WP_Comment $note New order note object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating item, false when updating.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_insert_order_note', $note, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $note, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, str_replace( '(?P<order_id>[\d]+)', $order->id, $this->rest_base ), $note_id ) ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single order note.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$order = get_post( (int) $request['order_id'] );
|
||||
|
||||
if ( empty( $order->post_type ) || 'shop_order' !== $order->post_type ) {
|
||||
return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$note = get_comment( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $note ) || intval( $note->comment_post_ID ) !== intval( $order->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$order_note = $this->prepare_item_for_response( $note, $request );
|
||||
$response = rest_ensure_response( $order_note );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single order note.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
|
||||
|
||||
// We don't support trashing for this type, error out.
|
||||
if ( ! $force ) {
|
||||
return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Webhooks do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
|
||||
}
|
||||
|
||||
$order = get_post( (int) $request['order_id'] );
|
||||
|
||||
if ( empty( $order->post_type ) || 'shop_order' !== $order->post_type ) {
|
||||
return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$note = get_comment( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $note ) || intval( $note->comment_post_ID ) !== intval( $order->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $note, $request );
|
||||
|
||||
$result = wp_delete_comment( $note->comment_ID, true );;
|
||||
|
||||
if ( ! $result ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), 'order_note' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a order note is deleted or trashed via the REST API.
|
||||
*
|
||||
* @param WP_Comment $note The deleted or trashed order note.
|
||||
* @param WP_REST_Response $response The response data.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_delete_order_note', $note, $response, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single order note output for response.
|
||||
*
|
||||
* @param WP_Comment $note Order note object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $note, $request ) {
|
||||
$data = array(
|
||||
'id' => $note->comment_ID,
|
||||
'date_created' => wc_rest_prepare_date_response( $note->comment_date_gmt ),
|
||||
'note' => $note->comment_content,
|
||||
'customer_note' => (bool) get_comment_meta( $note->comment_ID, 'is_customer_note', true ),
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $note ) );
|
||||
|
||||
/**
|
||||
* Filter order note object returned from the REST API.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Comment $note Order note object used to create response.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_order_note', $response, $note, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param WP_Comment $note Delivery order_note object.
|
||||
* @return array Links for the given order note.
|
||||
*/
|
||||
protected function prepare_links( $note ) {
|
||||
$order_id = (int) $note->comment_post_ID;
|
||||
$base = str_replace( '(?P<order_id>[\d]+)', $order_id, $this->rest_base );
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $note->comment_ID ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),
|
||||
),
|
||||
'up' => array(
|
||||
'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order_id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Order Notes schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'tax',
|
||||
'type' => 'order_note',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_created' => array(
|
||||
'description' => __( "The date the order note was created, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'note' => array(
|
||||
'description' => __( 'Order note.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'customer_note' => array(
|
||||
'description' => __( 'Shows/define if the note is only for reference or for the customer (the user will be notified).', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,519 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Order Refunds controller
|
||||
*
|
||||
* Handles requests to the /orders/<order_id>/refunds endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Order Refunds controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WC_REST_Posts_Controller
|
||||
*/
|
||||
class WC_REST_Order_Refunds_Controller extends WC_REST_Posts_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'orders/(?P<order_id>[\d]+)/refunds';
|
||||
|
||||
/**
|
||||
* Post type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $post_type = 'shop_order_refund';
|
||||
|
||||
/**
|
||||
* Order refunds actions.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( "woocommerce_rest_{$this->post_type}_trashable", '__return_false' );
|
||||
add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'query_args' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the routes for order refunds.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
|
||||
'email' => array(
|
||||
'required' => true,
|
||||
),
|
||||
) ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||
),
|
||||
'reassign' => array(),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single order refund output for response.
|
||||
*
|
||||
* @param WP_Post $post Post object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $data
|
||||
*/
|
||||
public function prepare_item_for_response( $post, $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$order = wc_get_order( (int) $request['order_id'] );
|
||||
|
||||
if ( ! $order ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), 404 );
|
||||
}
|
||||
|
||||
$refund = wc_get_order( $post );
|
||||
|
||||
if ( ! $refund || intval( $refund->post->post_parent ) !== intval( $order->id ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_order_refund_id', __( 'Invalid order refund ID.', 'woocommerce' ), 404 );
|
||||
}
|
||||
|
||||
$dp = $request['dp'];
|
||||
|
||||
$data = array(
|
||||
'id' => $refund->id,
|
||||
'date_created' => wc_rest_prepare_date_response( $refund->date ),
|
||||
'amount' => wc_format_decimal( $refund->get_refund_amount(), $dp ),
|
||||
'reason' => $refund->get_refund_reason(),
|
||||
'line_items' => array(),
|
||||
);
|
||||
|
||||
// Add line items.
|
||||
foreach ( $refund->get_items() as $item_id => $item ) {
|
||||
$product = $refund->get_product_from_item( $item );
|
||||
$product_id = 0;
|
||||
$variation_id = 0;
|
||||
$product_sku = null;
|
||||
|
||||
// Check if the product exists.
|
||||
if ( is_object( $product ) ) {
|
||||
$product_id = $product->id;
|
||||
$variation_id = $product->variation_id;
|
||||
$product_sku = $product->get_sku();
|
||||
}
|
||||
|
||||
$meta = new WC_Order_Item_Meta( $item, $product );
|
||||
|
||||
$item_meta = array();
|
||||
|
||||
$hideprefix = 'true' === $request['all_item_meta'] ? null : '_';
|
||||
|
||||
foreach ( $meta->get_formatted( $hideprefix ) as $meta_key => $formatted_meta ) {
|
||||
$item_meta[] = array(
|
||||
'key' => $formatted_meta['key'],
|
||||
'label' => $formatted_meta['label'],
|
||||
'value' => $formatted_meta['value'],
|
||||
);
|
||||
}
|
||||
|
||||
$line_item = array(
|
||||
'id' => $item_id,
|
||||
'name' => $item['name'],
|
||||
'sku' => $product_sku,
|
||||
'product_id' => (int) $product_id,
|
||||
'variation_id' => (int) $variation_id,
|
||||
'quantity' => wc_stock_amount( $item['qty'] ),
|
||||
'tax_class' => ! empty( $item['tax_class'] ) ? $item['tax_class'] : '',
|
||||
'price' => wc_format_decimal( $refund->get_item_total( $item, false, false ), $dp ),
|
||||
'subtotal' => wc_format_decimal( $refund->get_line_subtotal( $item, false, false ), $dp ),
|
||||
'subtotal_tax' => wc_format_decimal( $item['line_subtotal_tax'], $dp ),
|
||||
'total' => wc_format_decimal( $refund->get_line_total( $item, false, false ), $dp ),
|
||||
'total_tax' => wc_format_decimal( $item['line_tax'], $dp ),
|
||||
'taxes' => array(),
|
||||
'meta' => $item_meta,
|
||||
);
|
||||
|
||||
$item_line_taxes = maybe_unserialize( $item['line_tax_data'] );
|
||||
if ( isset( $item_line_taxes['total'] ) ) {
|
||||
$line_tax = array();
|
||||
|
||||
foreach ( $item_line_taxes['total'] as $tax_rate_id => $tax ) {
|
||||
$line_tax[ $tax_rate_id ] = array(
|
||||
'id' => $tax_rate_id,
|
||||
'total' => $tax,
|
||||
'subtotal' => '',
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $item_line_taxes['subtotal'] as $tax_rate_id => $tax ) {
|
||||
$line_tax[ $tax_rate_id ]['subtotal'] = $tax;
|
||||
}
|
||||
|
||||
$line_item['taxes'] = array_values( $line_tax );
|
||||
}
|
||||
|
||||
$data['line_items'][] = $line_item;
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $refund ) );
|
||||
|
||||
/**
|
||||
* Filter the data for a response.
|
||||
*
|
||||
* The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
|
||||
* prepared for the response.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Post $post Post object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $post, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param WC_Order_Refund $refund Comment object.
|
||||
* @return array Links for the given order refund.
|
||||
*/
|
||||
protected function prepare_links( $refund ) {
|
||||
$order_id = $refund->post->post_parent;
|
||||
$base = str_replace( '(?P<order_id>[\d]+)', $order_id, $this->rest_base );
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $refund->id ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),
|
||||
),
|
||||
'up' => array(
|
||||
'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order_id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query args.
|
||||
*
|
||||
* @param array $args
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function query_args( $args, $request ) {
|
||||
// Set post_status.
|
||||
$args['post_status'] = 'any';
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
if ( ! empty( $request['id'] ) ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$order_data = get_post( (int) $request['order_id'] );
|
||||
|
||||
if ( empty( $order_data ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_order', __( 'Order is invalid', 'woocommerce' ), 400 );
|
||||
}
|
||||
|
||||
if ( 0 > $request['amount'] ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_order_refund', __( 'Refund amount must be greater than zero.', 'woocommerce' ), 400 );
|
||||
}
|
||||
|
||||
$api_refund = is_bool( $request['api_refund'] ) ? $request['api_refund'] : true;
|
||||
|
||||
$data = array(
|
||||
'order_id' => $order_data->ID,
|
||||
'amount' => $request['amount'],
|
||||
'line_items' => $request['line_items'],
|
||||
);
|
||||
|
||||
// Create the refund.
|
||||
$refund = wc_create_refund( $data );
|
||||
|
||||
if ( ! $refund ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', __( 'Cannot create order refund, please try again.', 'woocommerce' ), 500 );
|
||||
}
|
||||
|
||||
// Refund via API.
|
||||
if ( $api_refund ) {
|
||||
if ( WC()->payment_gateways() ) {
|
||||
$payment_gateways = WC()->payment_gateways->payment_gateways();
|
||||
}
|
||||
|
||||
$order = wc_get_order( $order_data );
|
||||
|
||||
if ( isset( $payment_gateways[ $order->payment_method ] ) && $payment_gateways[ $order->payment_method ]->supports( 'refunds' ) ) {
|
||||
$result = $payment_gateways[ $order->payment_method ]->process_refund( $order_id, $refund->get_refund_amount(), $refund->get_refund_reason() );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
} elseif ( ! $result ) {
|
||||
return new WP_Error( 'woocommerce_rest_create_order_refund_api_failed', __( 'An error occurred while attempting to create the refund using the payment gateway API.', 'woocommerce' ), 500 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$post = get_post( $refund->id );
|
||||
$this->update_additional_fields_for_object( $post, $request );
|
||||
|
||||
/**
|
||||
* Fires after a single item is created or updated via the REST API.
|
||||
*
|
||||
* @param object $post Inserted object (not a WP_Post object).
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating item, false when updating.
|
||||
*/
|
||||
do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Order's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => $this->post_type,
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_created' => array(
|
||||
'description' => __( "The date the order refund was created, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'amount' => array(
|
||||
'description' => __( 'Refund amount.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'reason' => array(
|
||||
'description' => __( 'Reason for refund', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'line_items' => array(
|
||||
'description' => __( 'Line items data.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Item ID.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Product name.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'sku' => array(
|
||||
'description' => __( 'Product SKU.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'product_id' => array(
|
||||
'description' => __( 'Product ID.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'variation_id' => array(
|
||||
'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'quantity' => array(
|
||||
'description' => __( 'Quantity ordered.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'tax_class' => array(
|
||||
'description' => __( 'Tax class of product.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'price' => array(
|
||||
'description' => __( 'Product price.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotal' => array(
|
||||
'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'subtotal_tax' => array(
|
||||
'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'total' => array(
|
||||
'description' => __( 'Line total (after discounts).', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'total_tax' => array(
|
||||
'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'taxes' => array(
|
||||
'description' => __( 'Line total tax.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Tax rate ID.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'total' => array(
|
||||
'description' => __( 'Tax total.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'subtotal' => array(
|
||||
'description' => __( 'Tax subtotal.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
'meta' => array(
|
||||
'description' => __( 'Line item meta data.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'key' => array(
|
||||
'description' => __( 'Meta key.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'label' => array(
|
||||
'description' => __( 'Meta label.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'value' => array(
|
||||
'description' => __( 'Meta value.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
|
||||
$params['dp'] = array(
|
||||
'default' => 2,
|
||||
'description' => __( 'Number of decimal points to use in each resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Product Attribute Terms controller
|
||||
*
|
||||
* Handles requests to the products/attributes/<attribute_id>/terms endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Product Attribute Terms controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WC_REST_Terms_Controller
|
||||
*/
|
||||
class WC_REST_Product_Attribute_Terms_Controller extends WC_REST_Terms_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'products/attributes/(?P<attribute_id>[\d]+)/terms';
|
||||
|
||||
/**
|
||||
* Prepare a single product attribute term output for response.
|
||||
*
|
||||
* @param obj $item Term object.
|
||||
* @param WP_REST_Request $request
|
||||
* @return WP_REST_Response $response
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$data = array(
|
||||
'id' => (int) $item->term_id,
|
||||
'name' => $item->name,
|
||||
'slug' => $item->slug,
|
||||
'count' => (int) $item->count,
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $item, $request ) );
|
||||
|
||||
/**
|
||||
* Filter a term item returned from the API.
|
||||
*
|
||||
* Allows modification of the term data right before it is returned.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $item The original term object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_prepare_{$this->taxonomy}", $response, $item, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Attribute Term's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'product_attribute_term',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Term name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the resource unique to its type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_title',
|
||||
),
|
||||
),
|
||||
'count' => array(
|
||||
'description' => __( 'Number of published products for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'woocommerce' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,621 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Product Attributes controller
|
||||
*
|
||||
* Handles requests to the products/attributes endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Product Attributes controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Product_Attributes_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'products/attributes';
|
||||
|
||||
/**
|
||||
* Attribute name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $attribute = '';
|
||||
|
||||
/**
|
||||
* Register the routes for product attributes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
|
||||
'name' => array(
|
||||
'required' => true,
|
||||
),
|
||||
) ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
));
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read the attributes.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'attributes', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to create a attribute.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'attributes', 'create' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you cannot create new resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read a attribute.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
if ( ! $this->get_taxonomy( $request ) ) {
|
||||
return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! wc_rest_check_manager_permissions( 'attributes', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to update a attribute.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function update_item_permissions_check( $request ) {
|
||||
if ( ! $this->get_taxonomy( $request ) ) {
|
||||
return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! wc_rest_check_manager_permissions( 'attributes', 'edit' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to delete a attribute.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
if ( ! $this->get_taxonomy( $request ) ) {
|
||||
return new WP_Error( "woocommerce_rest_taxonomy_invalid", __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! wc_rest_check_manager_permissions( 'attributes', 'delete' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you cannot delete resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all attributes.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$attributes = wc_get_attribute_taxonomies();
|
||||
$data = array();
|
||||
foreach ( $attributes as $attribute_obj ) {
|
||||
$attribute = $this->prepare_item_for_response( $attribute_obj, $request );
|
||||
$attribute = $this->prepare_response_for_collection( $attribute );
|
||||
$data[] = $attribute;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single attribute.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Request|WP_Error
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$args = array(
|
||||
'attribute_label' => $request['name'],
|
||||
'attribute_name' => $request['slug'],
|
||||
'attribute_type' => $request['type'],
|
||||
'attribute_orderby' => $request['order_by'],
|
||||
'attribute_public' => $request['has_archives'],
|
||||
);
|
||||
|
||||
// Set the attribute slug.
|
||||
if ( empty( $args['attribute_name'] ) ) {
|
||||
$args['attribute_name'] = wc_sanitize_taxonomy_name( stripslashes( $args['attribute_label'] ) );
|
||||
} else {
|
||||
$args['attribute_name'] = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( stripslashes( $args['attribute_name'] ) ) );
|
||||
}
|
||||
|
||||
$valid_slug = $this->validate_attribute_slug( $args['attribute_name'], true );
|
||||
if ( is_wp_error( $valid_slug ) ) {
|
||||
return $valid_slug;
|
||||
}
|
||||
|
||||
$insert = $wpdb->insert(
|
||||
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
|
||||
$args,
|
||||
array( '%s', '%s', '%s', '%s', '%d' )
|
||||
);
|
||||
|
||||
// Checks for errors.
|
||||
if ( is_wp_error( $insert ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create', $insert->get_error_message(), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$attribute = $this->get_attribute( $wpdb->insert_id );
|
||||
|
||||
if ( is_wp_error( $attribute ) ) {
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
$this->update_additional_fields_for_object( $attribute, $request );
|
||||
|
||||
/**
|
||||
* Fires after a single product attribute is created or updated via the REST API.
|
||||
*
|
||||
* @param stdObject $attribute Inserted attribute object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating attribute, false when updating.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_insert_product_attribute', $attribute, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $attribute, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', rest_url( '/' . $this->namespace . '/' . $this->rest_base . '/' . $attribute->attribute_id ) );
|
||||
|
||||
// Clear transients.
|
||||
flush_rewrite_rules();
|
||||
delete_transient( 'wc_attribute_taxonomies' );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single attribute.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Request|WP_Error
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$attribute = $this->get_attribute( $request['id'] );
|
||||
|
||||
if ( is_wp_error( $attribute ) ) {
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
$response = $this->prepare_item_for_response( $attribute, $request );
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single term from a taxonomy.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Request|WP_Error
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$id = (int) $request['id'];
|
||||
$format = array( '%s', '%s', '%s', '%s', '%d' );
|
||||
$args = array(
|
||||
'attribute_label' => $request['name'],
|
||||
'attribute_name' => $request['slug'],
|
||||
'attribute_type' => $request['type'],
|
||||
'attribute_orderby' => $request['order_by'],
|
||||
'attribute_public' => $request['has_archives'],
|
||||
);
|
||||
|
||||
$i = 0;
|
||||
foreach ( $args as $key => $value ) {
|
||||
if ( empty( $value ) && ! is_bool( $value ) ) {
|
||||
unset( $args[ $key ] );
|
||||
unset( $format[ $i ] );
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
// Set the attribute slug.
|
||||
if ( ! empty( $args['attribute_name'] ) ) {
|
||||
$args['attribute_name'] = preg_replace( '/^pa\_/', '', wc_sanitize_taxonomy_name( stripslashes( $args['attribute_name'] ) ) );
|
||||
|
||||
$valid_slug = $this->validate_attribute_slug( $args['attribute_name'], true );
|
||||
if ( is_wp_error( $valid_slug ) ) {
|
||||
return $valid_slug;
|
||||
}
|
||||
}
|
||||
|
||||
$update = $wpdb->update(
|
||||
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
|
||||
$args,
|
||||
array( 'attribute_id' => $id ),
|
||||
$format,
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
// Checks for errors.
|
||||
if ( false === $update ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Could not edit the attribute', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$attribute = $this->get_attribute( $id );
|
||||
|
||||
if ( is_wp_error( $attribute ) ) {
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
$this->update_additional_fields_for_object( $attribute, $request );
|
||||
|
||||
/**
|
||||
* Fires after a single product attribute is created or updated via the REST API.
|
||||
*
|
||||
* @param stdObject $attribute Inserted attribute object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating attribute, false when updating.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_insert_product_attribute', $attribute, $request, false );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $attribute, $request );
|
||||
|
||||
// Clear transients.
|
||||
flush_rewrite_rules();
|
||||
delete_transient( 'wc_attribute_taxonomies' );
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single attribute.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
|
||||
|
||||
// We don't support trashing for this type, error out.
|
||||
if ( ! $force ) {
|
||||
return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
|
||||
}
|
||||
|
||||
$attribute = $this->get_attribute( $request['id'] );
|
||||
|
||||
if ( is_wp_error( $attribute ) ) {
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $attribute, $request );
|
||||
|
||||
$deleted = $wpdb->delete(
|
||||
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
|
||||
array( 'attribute_id' => $attribute->attribute_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
if ( false === $deleted ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
$taxonomy = wc_attribute_taxonomy_name( $attribute->attribute_name );
|
||||
|
||||
if ( taxonomy_exists( $taxonomy ) ) {
|
||||
$terms = get_terms( $taxonomy, 'orderby=name&hide_empty=0' );
|
||||
foreach ( $terms as $term ) {
|
||||
wp_delete_term( $term->term_id, $taxonomy );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single attribute is deleted via the REST API.
|
||||
*
|
||||
* @param stdObject $attribute The deleted attribute.
|
||||
* @param WP_REST_Response $response The response data.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_delete_product_attribute', $attribute, $response, $request );
|
||||
|
||||
// Fires woocommerce_attribute_deleted hook.
|
||||
do_action( 'woocommerce_attribute_deleted', $attribute->attribute_id, $attribute->attribute_name, $taxonomy );
|
||||
|
||||
// Clear transients.
|
||||
flush_rewrite_rules();
|
||||
delete_transient( 'wc_attribute_taxonomies' );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single product attribute output for response.
|
||||
*
|
||||
* @param obj $item Term object.
|
||||
* @param WP_REST_Request $request
|
||||
* @return WP_REST_Response $response
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$data = array(
|
||||
'id' => (int) $item->attribute_id,
|
||||
'name' => $item->attribute_label,
|
||||
'slug' => wc_attribute_taxonomy_name( $item->attribute_name ),
|
||||
'type' => $item->attribute_type,
|
||||
'order_by' => $item->attribute_orderby,
|
||||
'has_archives' => (bool) $item->attribute_public,
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $item ) );
|
||||
|
||||
/**
|
||||
* Filter a attribute item returned from the API.
|
||||
*
|
||||
* Allows modification of the product attribute data right before it is returned.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $item The original attribute object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_product_attribute', $response, $item, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param object $attribute Attribute object.
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return array Links for the given attribute.
|
||||
*/
|
||||
protected function prepare_links( $attribute ) {
|
||||
$base = '/' . $this->namespace . '/' . $this->rest_base;
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( trailingslashit( $base ) . $attribute->attribute_id ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( $base ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Attribute's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'product_attribute',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Attribute name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the resource unique to its type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_title',
|
||||
),
|
||||
),
|
||||
'type' => array(
|
||||
'description' => __( 'Type of attribute.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'select',
|
||||
'enum' => array_keys( wc_get_attribute_types() ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'order_by' => array(
|
||||
'description' => __( 'Default sort order.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'menu_order',
|
||||
'enum' => array( 'menu_order', 'name', 'name_num', 'id' ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'has_archives' => array(
|
||||
'description' => __( 'Enable/Disable attribute archives.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = array();
|
||||
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute name.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return int|WP_Error
|
||||
*/
|
||||
protected function get_taxonomy( $request ) {
|
||||
if ( '' !== $this->attribute ) {
|
||||
return $this->attribute;
|
||||
}
|
||||
|
||||
if ( $request['id'] ) {
|
||||
$name = wc_attribute_taxonomy_name_by_id( (int) $request['id'] );
|
||||
|
||||
$this->attribute = $name;
|
||||
}
|
||||
|
||||
return $this->attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute data.
|
||||
*
|
||||
* @param int $id Attribute ID.
|
||||
* @return stdClass|WP_Error
|
||||
*/
|
||||
protected function get_attribute( $id ) {
|
||||
global $wpdb;
|
||||
|
||||
$attribute = $wpdb->get_row( $wpdb->prepare( "
|
||||
SELECT *
|
||||
FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
|
||||
WHERE attribute_id = %d
|
||||
", $id ) );
|
||||
|
||||
if ( is_wp_error( $attribute ) || is_null( $attribute ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_attribute_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate attribute slug.
|
||||
*
|
||||
* @param string $slug
|
||||
* @param bool $new_data
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function validate_attribute_slug( $slug, $new_data = true ) {
|
||||
if ( strlen( $slug ) >= 28 ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_product_attribute_slug_too_long', sprintf( __( 'Slug "%s" is too long (28 characters max).', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
|
||||
} else if ( wc_check_if_attribute_name_is_reserved( $slug ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_product_attribute_slug_reserved_name', sprintf( __( 'Slug "%s" is not allowed because it is a reserved term.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
|
||||
} else if ( $new_data && taxonomy_exists( wc_attribute_taxonomy_name( $slug ) ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_product_attribute_slug_already_exists', sprintf( __( 'Slug "%s" is already in use.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Product Categories controller
|
||||
*
|
||||
* Handles requests to the products/categories endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Product Categories controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WC_REST_Terms_Controller
|
||||
*/
|
||||
class WC_REST_Product_Categories_Controller extends WC_REST_Terms_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'products/categories';
|
||||
|
||||
/**
|
||||
* Taxonomy.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $taxonomy = 'product_cat';
|
||||
|
||||
/**
|
||||
* Prepare a single product category output for response.
|
||||
*
|
||||
* @param obj $item Term object.
|
||||
* @param WP_REST_Request $request
|
||||
* @return WP_REST_Response $response
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
// Get category display type.
|
||||
$display_type = get_woocommerce_term_meta( $item->term_id, 'display_type' );
|
||||
|
||||
// Get category image.
|
||||
$image = '';
|
||||
if ( $image_id = get_woocommerce_term_meta( $item->term_id, 'thumbnail_id' ) ) {
|
||||
$image = wp_get_attachment_url( $image_id );
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'id' => (int) $item->term_id,
|
||||
'name' => $item->name,
|
||||
'slug' => $item->slug,
|
||||
'parent' => (int) $item->parent,
|
||||
'description' => $item->description,
|
||||
'display' => $display_type ? $display_type : 'default',
|
||||
'image' => $image ? esc_url( $image ) : '',
|
||||
'count' => (int) $item->count,
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $item, $request ) );
|
||||
|
||||
/**
|
||||
* Filter a term item returned from the API.
|
||||
*
|
||||
* Allows modification of the term data right before it is returned.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $item The original term object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_prepare_{$this->taxonomy}", $response, $item, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update term meta fields.
|
||||
*
|
||||
* @param WP_Term $term
|
||||
* @param WP_REST_Request $request
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function update_term_meta_fields( $term, $request ) {
|
||||
$id = (int) $term->term_id;
|
||||
|
||||
update_woocommerce_term_meta( $id, 'display_type', $request['display'] );
|
||||
|
||||
if ( ! empty( $request['image'] ) ) {
|
||||
$upload = wc_rest_upload_image_from_url( esc_url_raw( $request['image'] ) );
|
||||
|
||||
if ( is_wp_error( $upload ) ) {
|
||||
return $upload;
|
||||
}
|
||||
|
||||
$image_id = wc_rest_set_uploaded_image_as_attachment( $upload );
|
||||
|
||||
// Check if image_id is a valid image attachment before updating the term meta.
|
||||
if ( $image_id && wp_attachment_is_image( $image_id ) ) {
|
||||
update_woocommerce_term_meta( $id, 'thumbnail_id', $image_id );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Category schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => $this->taxonomy,
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Category name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the resource unique to its type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_title',
|
||||
),
|
||||
),
|
||||
'parent' => array(
|
||||
'description' => __( 'The id for the parent of the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'HTML description of the resource.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'wp_filter_post_kses',
|
||||
),
|
||||
),
|
||||
'display' => array(
|
||||
'description' => __( 'Category archive display type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'default',
|
||||
'enum' => array( 'default', 'products', 'subcategories', 'both' ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'image' => array(
|
||||
'description' => __( 'Image URL.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'count' => array(
|
||||
'description' => __( 'Number of published products for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'woocommerce' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Product Reviews controller
|
||||
*
|
||||
* Handles requests to the /products/<product_id>/reviews endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Products controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Product_Reviews_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'products/(?P<product_id>[\d]+)/reviews';
|
||||
|
||||
/**
|
||||
* Register the routes for product reviews.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read webhook deliveries.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_post_permissions( 'product', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read a webhook develivery.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
$post = get_post( (int) $request['product_id'] );
|
||||
|
||||
if ( $post && ! wc_rest_check_post_permissions( 'product', 'read', $post->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all reviews from a product.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$product = get_post( (int) $request['product_id'] );
|
||||
|
||||
if ( empty( $product->post_type ) || 'product' !== $product->post_type ) {
|
||||
return new WP_Error( 'woocommerce_rest_product_invalid_id', __( 'Invalid product id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$reviews = get_approved_comments( $product->ID );
|
||||
$data = array();
|
||||
foreach ( $reviews as $review_data ) {
|
||||
$review = $this->prepare_item_for_response( $review_data, $request );
|
||||
$review = $this->prepare_response_for_collection( $review );
|
||||
$data[] = $review;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single product review.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$product = get_post( (int) $request['product_id'] );
|
||||
|
||||
if ( empty( $product->post_type ) || 'product' !== $product->post_type ) {
|
||||
return new WP_Error( 'woocommerce_rest_product_invalid_id', __( 'Invalid product id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$review = get_comment( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $review ) || intval( $review->comment_post_ID ) !== intval( $product->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$delivery = $this->prepare_item_for_response( $review, $request );
|
||||
$response = rest_ensure_response( $delivery );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single product review output for response.
|
||||
*
|
||||
* @param WP_Comment $review Product review object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $review, $request ) {
|
||||
$data = array(
|
||||
'id' => (int) $review->comment_ID,
|
||||
'date_created' => wc_rest_prepare_date_response( $review->comment_date_gmt ),
|
||||
'review' => $review->comment_content,
|
||||
'rating' => (int) get_comment_meta( $review->comment_ID, 'rating', true ),
|
||||
'reviewer_name' => $review->comment_author,
|
||||
'reviewer_email' => $review->comment_author_email,
|
||||
'verified' => wc_review_is_from_verified_owner( $review->comment_ID ),
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $review, $request ) );
|
||||
|
||||
/**
|
||||
* Filter product reviews object returned from the REST API.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WP_Comment $review Product review object used to create response.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_product_review', $response, $review, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param WP_Comment $review Product review object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return array Links for the given product review.
|
||||
*/
|
||||
protected function prepare_links( $review, $request ) {
|
||||
$product_id = (int) $request['product_id'];
|
||||
$base = str_replace( '(?P<product_id>[\d]+)', $product_id, $this->rest_base );
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $review->comment_ID ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),
|
||||
),
|
||||
'up' => array(
|
||||
'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product_id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Product Review's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'product_review',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_created' => array(
|
||||
'description' => __( "The date the review was created, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'rating' => array(
|
||||
'description' => __( 'Review rating (0 to 5).', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'reviewer_name' => array(
|
||||
'description' => __( 'Reviewer name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'reviewer_email' => array(
|
||||
'description' => __( 'Reviewer email.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'verified' => array(
|
||||
'description' => __( 'Shows if the reviewer bought the product or not.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Product Shipping Classes controller
|
||||
*
|
||||
* Handles requests to the products/shipping_classes endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Product Shipping Classes controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WC_REST_Terms_Controller
|
||||
*/
|
||||
class WC_REST_Product_Shipping_Classes_Controller extends WC_REST_Terms_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'products/shipping_classes';
|
||||
|
||||
/**
|
||||
* Taxonomy.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $taxonomy = 'product_shipping_class';
|
||||
|
||||
/**
|
||||
* Prepare a single product shipping class output for response.
|
||||
*
|
||||
* @param obj $item Term object.
|
||||
* @param WP_REST_Request $request
|
||||
* @return WP_REST_Response $response
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$data = array(
|
||||
'id' => (int) $item->term_id,
|
||||
'name' => $item->name,
|
||||
'slug' => $item->slug,
|
||||
'parent' => (int) $item->parent,
|
||||
'description' => $item->description,
|
||||
'count' => (int) $item->count,
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $item, $request ) );
|
||||
|
||||
/**
|
||||
* Filter a term item returned from the API.
|
||||
*
|
||||
* Allows modification of the term data right before it is returned.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $item The original term object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_prepare_{$this->taxonomy}", $response, $item, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Shipping Class schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => $this->taxonomy,
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Shipping class name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the resource unique to its type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_title',
|
||||
),
|
||||
),
|
||||
'parent' => array(
|
||||
'description' => __( 'The id for the parent of the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'HTML description of the resource.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'wp_filter_post_kses',
|
||||
),
|
||||
),
|
||||
'count' => array(
|
||||
'description' => __( 'Number of published products for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'woocommerce' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Product Tags controller
|
||||
*
|
||||
* Handles requests to the products/tags endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Product Tags controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WC_REST_Terms_Controller
|
||||
*/
|
||||
class WC_REST_Product_Tags_Controller extends WC_REST_Terms_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'products/tags';
|
||||
|
||||
/**
|
||||
* Taxonomy.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $taxonomy = 'product_tag';
|
||||
|
||||
/**
|
||||
* Prepare a single product tag output for response.
|
||||
*
|
||||
* @param obj $item Term object.
|
||||
* @param WP_REST_Request $request
|
||||
* @return WP_REST_Response $response
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
$data = array(
|
||||
'id' => (int) $item->term_id,
|
||||
'name' => $item->name,
|
||||
'slug' => $item->slug,
|
||||
'description' => $item->description,
|
||||
'count' => (int) $item->count,
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $item, $request ) );
|
||||
|
||||
/**
|
||||
* Filter a term item returned from the API.
|
||||
*
|
||||
* Allows modification of the term data right before it is returned.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $item The original term object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_prepare_{$this->taxonomy}", $response, $item, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Tag's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => $this->taxonomy,
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Tag name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the resource unique to its type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_title',
|
||||
),
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'HTML description of the resource.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'wp_filter_post_kses',
|
||||
),
|
||||
),
|
||||
'count' => array(
|
||||
'description' => __( 'Number of published products for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit', 'woocommerce' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,395 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Reports controller
|
||||
*
|
||||
* Handles requests to the reports/sales endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Report Sales controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Report_Sales_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'reports/sales';
|
||||
|
||||
/**
|
||||
* Report instance.
|
||||
*
|
||||
* @var WC_Admin_Report
|
||||
*/
|
||||
protected $report;
|
||||
|
||||
/**
|
||||
* Register the routes for sales reports.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read report.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'reports', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sales reports.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$data = array();
|
||||
$item = $this->prepare_item_for_response( null, $request );
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a report sales object for serialization.
|
||||
*
|
||||
* @param null $_
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $_, $request ) {
|
||||
// Set date filtering.
|
||||
$filter = array(
|
||||
'period' => $request['period'],
|
||||
'date_min' => $request['date_min'],
|
||||
'date_max' => $request['date_max'],
|
||||
);
|
||||
$this->setup_report( $filter );
|
||||
|
||||
// New customers.
|
||||
$users_query = new WP_User_Query(
|
||||
array(
|
||||
'fields' => array( 'user_registered' ),
|
||||
'role' => 'customer',
|
||||
)
|
||||
);
|
||||
|
||||
$customers = $users_query->get_results();
|
||||
|
||||
foreach ( $customers as $key => $customer ) {
|
||||
if ( strtotime( $customer->user_registered ) < $this->report->start_date || strtotime( $customer->user_registered ) > $this->report->end_date ) {
|
||||
unset( $customers[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
$total_customers = count( $customers );
|
||||
$report_data = $this->report->get_report_data();
|
||||
$period_totals = array();
|
||||
|
||||
// Setup period totals by ensuring each period in the interval has data.
|
||||
for ( $i = 0; $i <= $this->report->chart_interval; $i++ ) {
|
||||
|
||||
switch ( $this->report->chart_groupby ) {
|
||||
case 'day' :
|
||||
$time = date( 'Y-m-d', strtotime( "+{$i} DAY", $this->report->start_date ) );
|
||||
break;
|
||||
default :
|
||||
$time = date( 'Y-m', strtotime( "+{$i} MONTH", $this->report->start_date ) );
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the customer signups for each period.
|
||||
$customer_count = 0;
|
||||
foreach ( $customers as $customer ) {
|
||||
if ( date( ( 'day' == $this->report->chart_groupby ) ? 'Y-m-d' : 'Y-m', strtotime( $customer->user_registered ) ) == $time ) {
|
||||
$customer_count++;
|
||||
}
|
||||
}
|
||||
|
||||
$period_totals[ $time ] = array(
|
||||
'sales' => wc_format_decimal( 0.00, 2 ),
|
||||
'orders' => 0,
|
||||
'items' => 0,
|
||||
'tax' => wc_format_decimal( 0.00, 2 ),
|
||||
'shipping' => wc_format_decimal( 0.00, 2 ),
|
||||
'discount' => wc_format_decimal( 0.00, 2 ),
|
||||
'customers' => $customer_count,
|
||||
);
|
||||
}
|
||||
|
||||
// add total sales, total order count, total tax and total shipping for each period
|
||||
foreach ( $report_data->orders as $order ) {
|
||||
$time = ( 'day' === $this->report->chart_groupby ) ? date( 'Y-m-d', strtotime( $order->post_date ) ) : date( 'Y-m', strtotime( $order->post_date ) );
|
||||
|
||||
if ( ! isset( $period_totals[ $time ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$period_totals[ $time ]['sales'] = wc_format_decimal( $order->total_sales, 2 );
|
||||
$period_totals[ $time ]['tax'] = wc_format_decimal( $order->total_tax + $order->total_shipping_tax, 2 );
|
||||
$period_totals[ $time ]['shipping'] = wc_format_decimal( $order->total_shipping, 2 );
|
||||
}
|
||||
|
||||
foreach ( $report_data->order_counts as $order ) {
|
||||
$time = ( 'day' === $this->report->chart_groupby ) ? date( 'Y-m-d', strtotime( $order->post_date ) ) : date( 'Y-m', strtotime( $order->post_date ) );
|
||||
|
||||
if ( ! isset( $period_totals[ $time ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$period_totals[ $time ]['orders'] = (int) $order->count;
|
||||
}
|
||||
|
||||
// Add total order items for each period.
|
||||
foreach ( $report_data->order_items as $order_item ) {
|
||||
$time = ( 'day' === $this->report->chart_groupby ) ? date( 'Y-m-d', strtotime( $order_item->post_date ) ) : date( 'Y-m', strtotime( $order_item->post_date ) );
|
||||
|
||||
if ( ! isset( $period_totals[ $time ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$period_totals[ $time ]['items'] = (int) $order_item->order_item_count;
|
||||
}
|
||||
|
||||
// Add total discount for each period.
|
||||
foreach ( $report_data->coupons as $discount ) {
|
||||
$time = ( 'day' === $this->report->chart_groupby ) ? date( 'Y-m-d', strtotime( $discount->post_date ) ) : date( 'Y-m', strtotime( $discount->post_date ) );
|
||||
|
||||
if ( ! isset( $period_totals[ $time ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$period_totals[ $time ]['discount'] = wc_format_decimal( $discount->discount_amount, 2 );
|
||||
}
|
||||
|
||||
$sales_data = array(
|
||||
'total_sales' => $report_data->total_sales,
|
||||
'net_sales' => $report_data->net_sales,
|
||||
'average_sales' => $report_data->average_sales,
|
||||
'total_orders' => $report_data->total_orders,
|
||||
'total_items' => $report_data->total_items,
|
||||
'total_tax' => wc_format_decimal( $report_data->total_tax + $report_data->total_shipping_tax, 2 ),
|
||||
'total_shipping' => $report_data->total_shipping,
|
||||
'total_refunds' => $report_data->total_refunds,
|
||||
'total_discount' => $report_data->total_coupons,
|
||||
'totals_grouped_by' => $this->report->chart_groupby,
|
||||
'totals' => $period_totals,
|
||||
'total_customers' => $total_customers,
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $sales_data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->add_links( array(
|
||||
'about' => array(
|
||||
'href' => rest_url( sprintf( '%s/reports', $this->namespace ) ),
|
||||
),
|
||||
) );
|
||||
|
||||
/**
|
||||
* Filter a report sales returned from the API.
|
||||
*
|
||||
* Allows modification of the report sales data right before it is returned.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param stdClass $data The original report object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_report_sales', $response, (object) $sales_data, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the report object and parse any date filtering.
|
||||
*
|
||||
* @param array $filter date filtering
|
||||
*/
|
||||
protected function setup_report( $filter ) {
|
||||
include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-admin-report.php' );
|
||||
include_once( WC()->plugin_path() . '/includes/admin/reports/class-wc-report-sales-by-date.php' );
|
||||
|
||||
$this->report = new WC_Report_Sales_By_Date();
|
||||
|
||||
if ( empty( $filter['period'] ) ) {
|
||||
|
||||
// Custom date range.
|
||||
$filter['period'] = 'custom';
|
||||
|
||||
if ( ! empty( $filter['date_min'] ) || ! empty( $filter['date_max'] ) ) {
|
||||
|
||||
// Overwrite _GET to make use of WC_Admin_Report::calculate_current_range() for custom date ranges.
|
||||
$_GET['start_date'] = $filter['date_min'];
|
||||
$_GET['end_date'] = isset( $filter['date_max'] ) ? $filter['date_max'] : null;
|
||||
|
||||
} else {
|
||||
|
||||
// Default custom range to today.
|
||||
$_GET['start_date'] = $_GET['end_date'] = date( 'Y-m-d', current_time( 'timestamp' ) );
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$filter['period'] = empty( $filter['period'] ) ? 'week' : $filter['period'];
|
||||
|
||||
// Change "week" period to "7day".
|
||||
if ( 'week' === $filter['period'] ) {
|
||||
$filter['period'] = '7day';
|
||||
}
|
||||
}
|
||||
|
||||
$this->report->calculate_current_range( $filter['period'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'sales_report',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'total_sales' => array(
|
||||
'description' => __( 'Gross sales in the period.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'net_sales' => array(
|
||||
'description' => __( 'Net sales in the period.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'average_sales' => array(
|
||||
'description' => __( 'Average net daily sales.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'total_orders' => array(
|
||||
'description' => __( 'Total of orders placed.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'total_items' => array(
|
||||
'description' => __( 'Total of items purchased.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'total_tax' => array(
|
||||
'description' => __( 'Total charged for taxes.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'total_shipping' => array(
|
||||
'description' => __( 'Total charged for shipping.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'total_refunds' => array(
|
||||
'description' => __( 'Total of refunded orders.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'total_discount' => array(
|
||||
'description' => __( 'Total of coupons used.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'totals_grouped_by' => array(
|
||||
'description' => __( 'Group type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'totals' => array(
|
||||
'description' => __( 'Totals.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
'period' => array(
|
||||
'description' => __( 'Report period.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array( 'week', 'month', 'last_month', 'year' ),
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
'date_min' => array(
|
||||
'description' => sprintf( __( 'Return sales for a specific start date, the date need to be in the %s format.', 'woocommerce' ), 'YYYY-MM-AA' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date',
|
||||
'validate_callback' => 'wc_rest_validate_reports_request_arg',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
'date_max' => array(
|
||||
'description' => sprintf( __( 'Return sales for a specific end date, the date need to be in the %s format.', 'woocommerce' ), 'YYYY-MM-AA' ),
|
||||
'type' => 'string',
|
||||
'format' => 'date',
|
||||
'validate_callback' => 'wc_rest_validate_reports_request_arg',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Reports controller
|
||||
*
|
||||
* Handles requests to the reports/top_sellers endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Report Top Sellers controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WC_REST_Report_Sales_Controller
|
||||
*/
|
||||
class WC_REST_Report_Top_Sellers_Controller extends WC_REST_Report_Sales_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'reports/top_sellers';
|
||||
|
||||
/**
|
||||
* Get sales reports.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
// Set date filtering.
|
||||
$filter = array(
|
||||
'period' => $request['period'],
|
||||
'date_min' => $request['date_min'],
|
||||
'date_max' => $request['date_max'],
|
||||
);
|
||||
$this->setup_report( $filter );
|
||||
|
||||
$report_data = $this->report->get_order_report_data( array(
|
||||
'data' => array(
|
||||
'_product_id' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => '',
|
||||
'name' => 'product_id',
|
||||
),
|
||||
'_qty' => array(
|
||||
'type' => 'order_item_meta',
|
||||
'order_item_type' => 'line_item',
|
||||
'function' => 'SUM',
|
||||
'name' => 'order_item_qty',
|
||||
)
|
||||
),
|
||||
'order_by' => 'order_item_qty DESC',
|
||||
'group_by' => 'product_id',
|
||||
'limit' => isset( $filter['limit'] ) ? absint( $filter['limit'] ) : 12,
|
||||
'query_type' => 'get_results',
|
||||
'filter_range' => true,
|
||||
) );
|
||||
|
||||
$top_sellers = array();
|
||||
|
||||
foreach ( $report_data as $item ) {
|
||||
$product = wc_get_product( $item->product_id );
|
||||
|
||||
if ( $product ) {
|
||||
$top_sellers[] = array(
|
||||
'title' => $product->get_title(),
|
||||
'product_id' => (int) $item->product_id,
|
||||
'quantity' => wc_stock_amount( $item->order_item_qty ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$data = array();
|
||||
foreach ( $top_sellers as $top_seller ) {
|
||||
$item = $this->prepare_item_for_response( (object) $top_seller, $request );
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a report sales object for serialization.
|
||||
*
|
||||
* @param stdClass $top_seller
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $top_seller, $request ) {
|
||||
$data = array(
|
||||
'title' => $top_seller->title,
|
||||
'product_id' => $top_seller->product_id,
|
||||
'quantity' => $top_seller->quantity,
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->add_links( array(
|
||||
'about' => array(
|
||||
'href' => rest_url( sprintf( '%s/reports', $this->namespace ) ),
|
||||
),
|
||||
'product' => array(
|
||||
'href' => rest_url( sprintf( '/%s/products/%s', $this->namespace, $top_seller->product_id ) ),
|
||||
),
|
||||
) );
|
||||
|
||||
/**
|
||||
* Filter a report top sellers returned from the API.
|
||||
*
|
||||
* Allows modification of the report top sellers data right before it is returned.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param stdClass $top_seller The original report object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_report_top_sellers', $response, $top_seller, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'top_sellers_report',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'title' => array(
|
||||
'description' => __( 'Product title.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'product_id' => array(
|
||||
'description' => __( 'Product ID.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'quantity' => array(
|
||||
'description' => __( 'Total number of purchases.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Reports controller
|
||||
*
|
||||
* Handles requests to the reports endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Reports controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Reports_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'reports';
|
||||
|
||||
/**
|
||||
* Register the routes for reports.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read reports.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'reports', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all reports.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$data = array();
|
||||
$reports = array(
|
||||
array(
|
||||
'slug' => 'sales',
|
||||
'description' => __( 'List of sales reports.', 'woocommerce' ),
|
||||
),
|
||||
array(
|
||||
'slug' => 'top_sellers',
|
||||
'description' => __( 'List of top sellers products.', 'woocommerce' ),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ( $reports as $report ) {
|
||||
$item = $this->prepare_item_for_response( (object) $report, $request );
|
||||
$data[] = $this->prepare_response_for_collection( $item );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a report object for serialization.
|
||||
*
|
||||
* @param stdClass $report Report data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $report, $request ) {
|
||||
$data = array(
|
||||
'slug' => $report->slug,
|
||||
'description' => $report->description,
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
$response->add_links( array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $report->slug ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
) );
|
||||
|
||||
/**
|
||||
* Filter a report returned from the API.
|
||||
*
|
||||
* Allows modification of the report data right before it is returned.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param object $report The original report object.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_report', $response, $report, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Report's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'report',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'slug' => array(
|
||||
'description' => __( 'An alphanumeric identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => __( 'A human-readable description of the resource.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Tax Classes controller
|
||||
*
|
||||
* Handles requests to the /taxes/classes endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Tax Classes controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Tax_Classes_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'taxes/classes';
|
||||
|
||||
/**
|
||||
* Register the routes for tax classes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<slug>\w[\w\s\-]*)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read tax classes.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list tax classes.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access create tax classes.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'create' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access delete a tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'delete' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tax classes.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$tax_classes = array();
|
||||
|
||||
// Add standard class.
|
||||
$tax_classes[] = array(
|
||||
'slug' => 'standard',
|
||||
'name' => __( 'Standard Rate', 'woocommerce' ),
|
||||
);
|
||||
|
||||
$classes = WC_Tax::get_tax_classes();
|
||||
|
||||
foreach ( $classes as $class ) {
|
||||
$tax_classes[] = array(
|
||||
'slug' => sanitize_title( $class ),
|
||||
'name' => $class,
|
||||
);
|
||||
}
|
||||
|
||||
$data = array();
|
||||
foreach ( $tax_classes as $tax_class ) {
|
||||
$class = $this->prepare_item_for_response( $tax_class, $request );
|
||||
$class = $this->prepare_response_for_collection( $class );
|
||||
$data[] = $class;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
$exists = false;
|
||||
$classes = WC_Tax::get_tax_classes();
|
||||
$tax_class = array(
|
||||
'slug' => sanitize_title( $request['name'] ),
|
||||
'name' => $request['name'],
|
||||
);
|
||||
|
||||
// Check if class exists.
|
||||
foreach ( $classes as $key => $class ) {
|
||||
if ( sanitize_title( $class ) === $tax_class['slug'] ) {
|
||||
$exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return error if tax class already exists.
|
||||
if ( $exists ) {
|
||||
return new WP_Error( 'woocommerce_rest_tax_class_exists', __( 'Cannot create existing resource.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// Add the new class.
|
||||
$classes[] = $tax_class['name'];
|
||||
|
||||
update_option( 'woocommerce_tax_classes', implode( "\n", $classes ) );
|
||||
|
||||
$this->update_additional_fields_for_object( $tax_class, $request );
|
||||
|
||||
/**
|
||||
* Fires after a tax class is created or updated via the REST API.
|
||||
*
|
||||
* @param stdClass $tax_class Data used to create the tax class.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating tax class, false when updating tax class.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_insert_tax_class', (object) $tax_class, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $tax_class, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $tax_class['slug'] ) ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single tax class.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$id = (int) $request['id'];
|
||||
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
|
||||
|
||||
// We don't support trashing for this type, error out.
|
||||
if ( ! $force ) {
|
||||
return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Taxes do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
|
||||
}
|
||||
|
||||
$tax_class = array(
|
||||
'slug' => sanitize_title( $request['slug'] ),
|
||||
'name' => '',
|
||||
);
|
||||
$classes = WC_Tax::get_tax_classes();
|
||||
$deleted = false;
|
||||
|
||||
foreach ( $classes as $key => $class ) {
|
||||
if ( sanitize_title( $class ) === $tax_class['slug'] ) {
|
||||
$tax_class['name'] = $class;
|
||||
unset( $classes[ $key ] );
|
||||
$deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $deleted ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
update_option( 'woocommerce_tax_classes', implode( "\n", $classes ) );
|
||||
|
||||
// Delete tax rate locations locations from the selected class.
|
||||
$wpdb->query( $wpdb->prepare( "
|
||||
DELETE locations.*
|
||||
FROM {$wpdb->prefix}woocommerce_tax_rate_locations AS locations
|
||||
INNER JOIN
|
||||
{$wpdb->prefix}woocommerce_tax_rates AS rates
|
||||
ON rates.tax_rate_id = locations.tax_rate_id
|
||||
WHERE rates.tax_rate_class = '%s'
|
||||
", $tax_class['slug'] ) );
|
||||
|
||||
// Delete tax rates in the selected class.
|
||||
$wpdb->delete( $wpdb->prefix . 'woocommerce_tax_rates', array( 'tax_rate_class' => $tax_class['slug'] ), array( '%s' ) );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $tax_class, $request );
|
||||
|
||||
/**
|
||||
* Fires after a tax class is deleted via the REST API.
|
||||
*
|
||||
* @param stdClass $tax_class The tax data.
|
||||
* @param WP_REST_Response $response The response returned from the API.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_delete_tax', (object) $tax_class, $response, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single tax class output for response.
|
||||
*
|
||||
* @param array $tax_class Tax class data.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $tax_class, $request ) {
|
||||
$data = $tax_class;
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links() );
|
||||
|
||||
/**
|
||||
* Filter tax object returned from the REST API.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param stdClass $tax_class Tax object used to create response.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_tax', $response, (object) $tax_class, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @return array Links for the given tax class.
|
||||
*/
|
||||
protected function prepare_links() {
|
||||
$links = array(
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Tax Classes schema, conforming to JSON Schema
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'tax_class',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'slug' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Tax class name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'required' => true,
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,671 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Taxes controller
|
||||
*
|
||||
* Handles requests to the /taxes endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Taxes controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Taxes_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'taxes';
|
||||
|
||||
/**
|
||||
* Register the routes for taxes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read taxes.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list taxes.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access create taxes.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'create' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read a tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access update a tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function update_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access delete a tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return boolean
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_manager_permissions( 'settings', 'delete' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all taxes.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$prepared_args = array();
|
||||
$prepared_args['exclude'] = $request['exclude'];
|
||||
$prepared_args['include'] = $request['include'];
|
||||
$prepared_args['order'] = $request['order'];
|
||||
$prepared_args['number'] = $request['per_page'];
|
||||
if ( ! empty( $request['offset'] ) ) {
|
||||
$prepared_args['offset'] = $request['offset'];
|
||||
} else {
|
||||
$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
|
||||
}
|
||||
$orderby_possibles = array(
|
||||
'id' => 'tax_rate_id',
|
||||
'order' => 'tax_rate_order',
|
||||
);
|
||||
$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
|
||||
$prepared_args['class'] = $request['class'];
|
||||
|
||||
/**
|
||||
* Filter arguments, before passing to $wpdb->get_results(), when querying taxes via the REST API.
|
||||
*
|
||||
* @param array $prepared_args Array of arguments for $wpdb->get_results().
|
||||
* @param WP_REST_Request $request The current request.
|
||||
*/
|
||||
$prepared_args = apply_filters( 'woocommerce_rest_tax_query', $prepared_args, $request );
|
||||
|
||||
$query = "
|
||||
SELECT *
|
||||
FROM {$wpdb->prefix}woocommerce_tax_rates
|
||||
WHERE 1 = 1
|
||||
";
|
||||
|
||||
// Filter by tax class.
|
||||
if ( ! empty( $prepared_args['class'] ) ) {
|
||||
$class = 'standard' !== $prepared_args['class'] ? sanitize_title( $prepared_args['class'] ) : '';
|
||||
$query .= " AND tax_rate_class = '$class'";
|
||||
}
|
||||
|
||||
// Order tax rates.
|
||||
$order_by = sprintf( ' ORDER BY %s', sanitize_key( $prepared_args['orderby'] ) );
|
||||
|
||||
// Pagination.
|
||||
$pagination = sprintf( ' LIMIT %d, %d', $prepared_args['offset'], $prepared_args['number'] );
|
||||
|
||||
// Query taxes.
|
||||
$results = $wpdb->get_results( $query . $order_by . $pagination );
|
||||
|
||||
$taxes = array();
|
||||
foreach ( $results as $tax ) {
|
||||
$data = $this->prepare_item_for_response( $tax, $request );
|
||||
$taxes[] = $this->prepare_response_for_collection( $data );
|
||||
}
|
||||
|
||||
$response = rest_ensure_response( $taxes );
|
||||
|
||||
// Store pagation values for headers then unset for count query.
|
||||
$per_page = (int) $prepared_args['number'];
|
||||
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
|
||||
|
||||
// Query only for ids.
|
||||
$wpdb->get_results( str_replace( 'SELECT *', 'SELECT tax_rate_id', $query ) );
|
||||
|
||||
// Calcule totals.
|
||||
$total_taxes = (int) $wpdb->num_rows;
|
||||
$response->header( 'X-WP-Total', (int) $total_taxes );
|
||||
$max_pages = ceil( $total_taxes / $per_page );
|
||||
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
||||
|
||||
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
if ( ! empty( $request['id'] ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_tax_exists', __( 'Cannot create existing resource.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'tax_rate_country' => $request['country'],
|
||||
'tax_rate_state' => $request['state'],
|
||||
'tax_rate' => $request['rate'],
|
||||
'tax_rate_name' => $request['name'],
|
||||
'tax_rate_priority' => (int) $request['priority'],
|
||||
'tax_rate_compound' => (int) $request['compound'],
|
||||
'tax_rate_shipping' => (int) $request['shipping'],
|
||||
'tax_rate_order' => (int) $request['order'],
|
||||
'tax_rate_class' => 'standard' !== $request['class'] ? $request['class'] : '',
|
||||
);
|
||||
|
||||
// Create tax rate.
|
||||
$id = WC_Tax::_insert_tax_rate( $data );
|
||||
|
||||
// Add locales.
|
||||
if ( ! empty( $request['postcode'] ) ) {
|
||||
WC_Tax::_update_tax_rate_postcodes( $id, wc_clean( $request['postcode'] ) );
|
||||
}
|
||||
if ( ! empty( $request['city'] ) ) {
|
||||
WC_Tax::_update_tax_rate_cities( $id, wc_clean( $request['city'] ) );
|
||||
}
|
||||
|
||||
$tax = WC_Tax::_get_tax_rate( $id, OBJECT );
|
||||
|
||||
$this->update_additional_fields_for_object( $tax, $request );
|
||||
|
||||
/**
|
||||
* Fires after a tax is created or updated via the REST API.
|
||||
*
|
||||
* @param stdClass $tax Data used to create the tax.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating tax, false when updating tax.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_insert_tax', $tax, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $tax, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$tax_obj = WC_Tax::_get_tax_rate( $id, OBJECT );
|
||||
|
||||
if ( empty( $id ) || empty( $tax_obj ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$tax = $this->prepare_item_for_response( $tax_obj, $request );
|
||||
$response = rest_ensure_response( $tax );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$current_tax = WC_Tax::_get_tax_rate( $id, OBJECT );
|
||||
|
||||
if ( empty( $id ) || empty( $current_tax ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$fields = array(
|
||||
'tax_rate_country',
|
||||
'tax_rate_state',
|
||||
'tax_rate',
|
||||
'tax_rate_name',
|
||||
'tax_rate_priority',
|
||||
'tax_rate_compound',
|
||||
'tax_rate_shipping',
|
||||
'tax_rate_order',
|
||||
'tax_rate_class'
|
||||
);
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
$key = 'tax_rate' === $field ? 'rate' : str_replace( 'tax_rate_', '', $field );
|
||||
|
||||
if ( ! isset( $request[ $key ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $request[ $key ];
|
||||
|
||||
// Fix compund and shipping values.
|
||||
if ( in_array( $key, array( 'compound', 'shipping' ) ) ) {
|
||||
$value = (int) $request[ $key ];
|
||||
}
|
||||
|
||||
// Test new data against current data.
|
||||
if ( $current_tax->$field === $value ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[ $field ] = $request[ $key ];
|
||||
}
|
||||
|
||||
// Update tax rate.
|
||||
WC_Tax::_update_tax_rate( $id, $data );
|
||||
|
||||
// Update locales.
|
||||
if ( ! isset( $request['postcode'] ) ) {
|
||||
WC_Tax::_update_tax_rate_postcodes( $id, wc_clean( $request['postcode'] ) );
|
||||
}
|
||||
|
||||
if ( ! isset( $request['city'] ) ) {
|
||||
WC_Tax::_update_tax_rate_cities( $id, wc_clean( $request['city'] ) );
|
||||
}
|
||||
|
||||
$tax = WC_Tax::_get_tax_rate( $id, OBJECT );
|
||||
|
||||
$this->update_additional_fields_for_object( $tax, $request );
|
||||
|
||||
/**
|
||||
* Fires after a tax is created or updated via the REST API.
|
||||
*
|
||||
* @param stdClass $tax Data used to create the tax.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating tax, false when updating tax.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_insert_tax', $tax, $request, false );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $tax, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single tax.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$id = (int) $request['id'];
|
||||
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
|
||||
|
||||
// We don't support trashing for this type, error out.
|
||||
if ( ! $force ) {
|
||||
return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Taxes do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
|
||||
}
|
||||
|
||||
$tax = WC_Tax::_get_tax_rate( $id, OBJECT );
|
||||
|
||||
if ( empty( $id ) || empty( $tax ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $tax, $request );
|
||||
|
||||
WC_Tax::_delete_tax_rate( $id );
|
||||
|
||||
if ( 0 === $wpdb->rows_affected ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a tax is deleted via the REST API.
|
||||
*
|
||||
* @param stdClass $tax The tax data.
|
||||
* @param WP_REST_Response $response The response returned from the API.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( 'woocommerce_rest_delete_tax', $tax, $response, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single tax output for response.
|
||||
*
|
||||
* @param stdClass $tax Tax object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $tax, $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$id = (int) $tax->tax_rate_id;
|
||||
$data = array(
|
||||
'id' => $id,
|
||||
'country' => $tax->tax_rate_country,
|
||||
'state' => $tax->tax_rate_state,
|
||||
'postcode' => '',
|
||||
'city' => '',
|
||||
'rate' => $tax->tax_rate,
|
||||
'name' => $tax->tax_rate_name,
|
||||
'priority' => (int) $tax->tax_rate_priority,
|
||||
'compound' => (bool) $tax->tax_rate_compound,
|
||||
'shipping' => (bool) $tax->tax_rate_shipping,
|
||||
'order' => (int) $tax->tax_rate_order,
|
||||
'class' => $tax->tax_rate_class ? $tax->tax_rate_class : 'standard',
|
||||
);
|
||||
|
||||
// Get locales from a tax rate.
|
||||
$locales = $wpdb->get_results( $wpdb->prepare( "
|
||||
SELECT location_code, location_type
|
||||
FROM {$wpdb->prefix}woocommerce_tax_rate_locations
|
||||
WHERE tax_rate_id = %d
|
||||
", $id ) );
|
||||
|
||||
if ( ! is_wp_error( $tax ) && ! is_null( $tax ) ) {
|
||||
foreach ( $locales as $locale ) {
|
||||
$data[ $locale->location_type ] = $locale->location_code;
|
||||
}
|
||||
}
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $tax ) );
|
||||
|
||||
/**
|
||||
* Filter tax object returned from the REST API.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param stdClass $tax Tax object used to create response.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_tax', $response, $tax, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param stdClass $tax Tax object.
|
||||
* @return array Links for the given tax.
|
||||
*/
|
||||
protected function prepare_links( $tax ) {
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $tax->tax_rate_id ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Taxes schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'tax',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'country' => array(
|
||||
'description' => __( 'Country ISO 3166 code.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'state' => array(
|
||||
'description' => __( 'State code.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'postcode' => array(
|
||||
'description' => __( 'Postcode/ZIP.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'city' => array(
|
||||
'description' => __( 'City name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'rate' => array(
|
||||
'description' => __( 'Tax rate.', 'woocommerce' ),
|
||||
'type' => 'float',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'Tax rate name.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'priority' => array(
|
||||
'description' => __( 'Tax priority.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'default' => 1,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'compound' => array(
|
||||
'description' => __( 'Whether or not this is a compound rate.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'shipping' => array(
|
||||
'description' => __( 'Whether or not this tax rate also gets applied to shipping.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'order' => array(
|
||||
'description' => __( 'Indicates the order that will appear in queries.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'class' => array(
|
||||
'description' => __( 'Tax class.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'standard',
|
||||
'enum' => array_merge( array( 'standard' ), array_map( 'sanitize_title', WC_Tax::get_tax_classes() ) ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
|
||||
$params['context']['default'] = 'view';
|
||||
|
||||
$params['exclude'] = array(
|
||||
'description' => __( 'Ensure result set excludes specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['include'] = array(
|
||||
'description' => __( 'Limit result set to specific ids.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'default' => array(),
|
||||
'sanitize_callback' => 'wp_parse_id_list',
|
||||
);
|
||||
$params['offset'] = array(
|
||||
'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['order'] = array(
|
||||
'default' => 'asc',
|
||||
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
|
||||
'enum' => array( 'asc', 'desc' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'type' => 'string',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['orderby'] = array(
|
||||
'default' => 'order',
|
||||
'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
|
||||
'enum' => array(
|
||||
'id',
|
||||
'order',
|
||||
),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'type' => 'string',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
$params['class'] = array(
|
||||
'description' => __( 'Sort by tax class.', 'woocommerce' ),
|
||||
'enum' => array_merge( array( 'standard' ), array_map( 'sanitize_title', WC_Tax::get_tax_classes() ) ),
|
||||
'sanitize_callback' => 'sanitize_title',
|
||||
'type' => 'string',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Webhooks controller
|
||||
*
|
||||
* Handles requests to the /webhooks/<webhook_id>/deliveries endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Webhook Deliveries controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WP_REST_Controller
|
||||
*/
|
||||
class WC_REST_Webhook_Deliveries_Controller extends WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'webhooks/(?P<webhook_id>[\d]+)/deliveries';
|
||||
|
||||
/**
|
||||
* Register the routes for webhook deliveries.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given request has permission to read webhook deliveries.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
if ( ! wc_rest_check_post_permissions( 'shop_webhook', 'read' ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to read a webhook develivery.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
$post = get_post( (int) $request['webhook_id'] );
|
||||
|
||||
if ( $post && ! wc_rest_check_post_permissions( 'shop_webhook', 'read', $post->ID ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all webhook deliveries.
|
||||
*
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
$webhook = new WC_Webhook( (int) $request['webhook_id'] );
|
||||
|
||||
if ( empty( $webhook->post_data->post_type ) || 'shop_webhook' !== $webhook->post_data->post_type ) {
|
||||
return new WP_Error( 'woocommerce_rest_webhook_invalid_id', __( 'Invalid webhook id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$logs = $webhook->get_delivery_logs();
|
||||
|
||||
$data = array();
|
||||
foreach ( $logs as $log ) {
|
||||
$delivery = $this->prepare_item_for_response( (object) $log, $request );
|
||||
$delivery = $this->prepare_response_for_collection( $delivery );
|
||||
$data[] = $delivery;
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single webhook delivery.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$webhook = new WC_Webhook( (int) $request['webhook_id'] );
|
||||
|
||||
if ( empty( $webhook->post_data->post_type ) || 'shop_webhook' !== $webhook->post_data->post_type ) {
|
||||
return new WP_Error( 'woocommerce_rest_webhook_invalid_id', __( 'Invalid webhook id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$log = $webhook->get_delivery_log( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $log ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$delivery = $this->prepare_item_for_response( (object) $log, $request );
|
||||
$response = rest_ensure_response( $delivery );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single webhook delivery output for response.
|
||||
*
|
||||
* @param stdClass $log Delivery log object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $log, $request ) {
|
||||
$data = (array) $log;
|
||||
|
||||
// Add timestamp.
|
||||
$data['date_created'] = wc_rest_prepare_date_response( $log->comment->comment_date_gmt );
|
||||
|
||||
// Remove comment object.
|
||||
unset( $data['comment'] );
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $log ) );
|
||||
|
||||
/**
|
||||
* Filter webhook delivery object returned from the REST API.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param stdClass $log Delivery log object used to create response.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_rest_prepare_webhook_delivery', $response, $log, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare links for the request.
|
||||
*
|
||||
* @param stdClass $log Delivery log object.
|
||||
* @return array Links for the given webhook delivery.
|
||||
*/
|
||||
protected function prepare_links( $log ) {
|
||||
$webhook_id = (int) $log->request_headers['X-WC-Webhook-ID'];
|
||||
$base = str_replace( '(?P<webhook_id>[\d]+)', $webhook_id, $this->rest_base );
|
||||
$links = array(
|
||||
'self' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $log->id ) ),
|
||||
),
|
||||
'collection' => array(
|
||||
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),
|
||||
),
|
||||
'up' => array(
|
||||
'href' => rest_url( sprintf( '/%s/webhooks/%d', $this->namespace, $webhook_id ) ),
|
||||
),
|
||||
);
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Webhook's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'webhook_delivery',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'duration' => array(
|
||||
'description' => __( 'The delivery duration, in seconds.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'summary' => array(
|
||||
'description' => __( 'A friendly summary of the response including the HTTP response code, message, and body.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'request_url' => array(
|
||||
'description' => __( 'The URL where the webhook was delivered.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'request_headers' => array(
|
||||
'description' => __( 'The URL where the webhook was delivered.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'request_headers' => array(
|
||||
'description' => __( 'Request headers.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'request_body' => array(
|
||||
'description' => __( 'Request body.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'response_code' => array(
|
||||
'description' => __( 'The HTTP response code from the receiving server.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'response_message' => array(
|
||||
'description' => __( 'The HTTP response message from the receiving server.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'response_headers' => array(
|
||||
'description' => __( 'Array of the response headers from the receiving server.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'response_body' => array(
|
||||
'description' => __( 'The response body from the receiving server.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_created' => array(
|
||||
'description' => __( "The date the webhook delivery was logged, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,555 @@
|
|||
<?php
|
||||
/**
|
||||
* REST API Webhooks controller
|
||||
*
|
||||
* Handles requests to the /webhooks endpoint.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST API Webhooks controller class.
|
||||
*
|
||||
* @package WooCommerce/API
|
||||
* @extends WC_REST_Posts_Controller
|
||||
*/
|
||||
class WC_REST_Webhooks_Controller extends WC_REST_Posts_Controller {
|
||||
|
||||
/**
|
||||
* Endpoint namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v1';
|
||||
|
||||
/**
|
||||
* Route base.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'webhooks';
|
||||
|
||||
/**
|
||||
* Post type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $post_type = 'shop_webhook';
|
||||
|
||||
/**
|
||||
* Initialize Webhooks actions.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'query_args' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the routes for webhooks.
|
||||
*/
|
||||
public function register_routes() {
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_items' ),
|
||||
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||
'args' => $this->get_collection_params(),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
'callback' => array( $this, 'create_item' ),
|
||||
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
||||
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
|
||||
'topic' => array(
|
||||
'required' => true,
|
||||
),
|
||||
'delivery_url' => array(
|
||||
'required' => true,
|
||||
),
|
||||
'secret' => array(
|
||||
'required' => true,
|
||||
),
|
||||
) ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
|
||||
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_item' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_item' ),
|
||||
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::DELETABLE,
|
||||
'callback' => array( $this, 'delete_item' ),
|
||||
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
||||
'args' => array(
|
||||
'force' => array(
|
||||
'default' => false,
|
||||
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single webhook.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
if ( ! empty( $request['id'] ) ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// Validate topic.
|
||||
if ( empty( $request['topic'] ) || ! wc_is_webhook_valid_topic( strtolower( $request['topic'] ) ) ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_topic", __( 'Webhook topic is required and must be valid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// Validate delivery URL.
|
||||
if ( empty( $request['delivery_url'] ) || ! wc_is_valid_url( $request['delivery_url'] ) ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_delivery_url", __( 'Webhook delivery URL must be a valid URL starting with http:// or https://.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$post = $this->prepare_item_for_database( $request );
|
||||
if ( is_wp_error( $post ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
$post->post_type = $this->post_type;
|
||||
$post_id = wp_insert_post( $post, true );
|
||||
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
|
||||
if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ) ) ) {
|
||||
$post_id->add_data( array( 'status' => 500 ) );
|
||||
} else {
|
||||
$post_id->add_data( array( 'status' => 400 ) );
|
||||
}
|
||||
return $post_id;
|
||||
}
|
||||
$post->ID = $post_id;
|
||||
|
||||
$webhook = new WC_Webhook( $post_id );
|
||||
|
||||
// Set topic.
|
||||
$webhook->set_topic( $request['topic'] );
|
||||
|
||||
// Set delivery URL.
|
||||
$webhook->set_delivery_url( $request['delivery_url'] );
|
||||
|
||||
// Set secret.
|
||||
$webhook->set_secret( $request['secret'] );
|
||||
|
||||
// Set status.
|
||||
if ( ! empty( $request['status'] ) ) {
|
||||
$webhook->update_status( $request['status'] );
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
$this->update_additional_fields_for_object( $post, $request );
|
||||
|
||||
/**
|
||||
* Fires after a single item is created or updated via the REST API.
|
||||
*
|
||||
* @param WP_Post $post Inserted object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating item, false when updating.
|
||||
*/
|
||||
do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
$response = rest_ensure_response( $response );
|
||||
$response->set_status( 201 );
|
||||
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) );
|
||||
|
||||
// Send ping.
|
||||
$webhook->deliver_ping();
|
||||
|
||||
// Clear cache.
|
||||
delete_transient( 'woocommerce_webhook_ids' );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single webhook.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$post = get_post( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
$webhook = new WC_Webhook( $id );
|
||||
|
||||
// Update topic.
|
||||
if ( ! empty( $request['topic'] ) ) {
|
||||
if ( wc_is_webhook_valid_topic( strtolower( $request['topic'] ) ) ) {
|
||||
$webhook->set_topic( $request['topic'] );
|
||||
} else {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_topic", __( 'Webhook topic must be valid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Update delivery URL.
|
||||
if ( ! empty( $request['delivery_url'] ) ) {
|
||||
if ( wc_is_valid_url( $request['delivery_url'] ) ) {
|
||||
$webhook->set_delivery_url( $request['delivery_url'] );
|
||||
} else {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_delivery_url", __( 'Webhook delivery URL must be a valid URL starting with http:// or https://.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Update secret.
|
||||
if ( ! empty( $request['secret'] ) ) {
|
||||
$webhook->set_secret( $request['secret'] );
|
||||
}
|
||||
|
||||
// Update status.
|
||||
if ( ! empty( $request['status'] ) ) {
|
||||
$webhook->update_status( $request['status'] );
|
||||
}
|
||||
|
||||
$post = $this->prepare_item_for_database( $request );
|
||||
if ( is_wp_error( $post ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
// Convert the post object to an array, otherwise wp_update_post will expect non-escaped input.
|
||||
$post_id = wp_update_post( (array) $post, true );
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ) ) ) {
|
||||
$post_id->add_data( array( 'status' => 500 ) );
|
||||
} else {
|
||||
$post_id->add_data( array( 'status' => 400 ) );
|
||||
}
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
$this->update_additional_fields_for_object( $post, $request );
|
||||
|
||||
/**
|
||||
* Fires after a single item is created or updated via the REST API.
|
||||
*
|
||||
* @param WP_Post $post Inserted object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @param boolean $creating True when creating item, false when updating.
|
||||
*/
|
||||
do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, false );
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
|
||||
// Clear cache.
|
||||
delete_transient( 'woocommerce_webhook_ids' );
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single webhook.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return WP_REST_Response|WP_Error
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
$id = (int) $request['id'];
|
||||
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
|
||||
|
||||
// We don't support trashing for this type, error out.
|
||||
if ( ! $force ) {
|
||||
return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Webhooks do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
|
||||
}
|
||||
|
||||
$post = get_post( $id );
|
||||
|
||||
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
||||
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid post id.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
$request->set_param( 'context', 'edit' );
|
||||
$response = $this->prepare_item_for_response( $post, $request );
|
||||
|
||||
$result = wp_delete_post( $id, true );
|
||||
|
||||
if ( ! $result ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 500 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a single item is deleted or trashed via the REST API.
|
||||
*
|
||||
* @param object $post The deleted or trashed item.
|
||||
* @param WP_REST_Response $response The response data.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( "woocommerce_rest_delete_{$this->post_type}", $post, $response, $request );
|
||||
|
||||
// Clear cache.
|
||||
delete_transient( 'woocommerce_webhook_ids' );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single webhook for create or update.
|
||||
*
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_Error|stdClass $data Post object.
|
||||
*/
|
||||
protected function prepare_item_for_database( $request ) {
|
||||
global $wpdb;
|
||||
|
||||
$data = new stdClass;
|
||||
|
||||
// Post ID.
|
||||
if ( isset( $request['id'] ) ) {
|
||||
$data->ID = absint( $request['id'] );
|
||||
}
|
||||
|
||||
$schema = $this->get_item_schema();
|
||||
|
||||
// Validate required POST fields.
|
||||
if ( 'POST' === $request->get_method() && empty( $data->ID ) ) {
|
||||
$data->post_title = ! empty( $request['name'] ) ? $request['name'] : sprintf( __( 'Webhook created on %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Webhook created on date parsed by strftime', 'woocommerce' ) ) );
|
||||
|
||||
// Post author.
|
||||
$data->post_author = get_current_user_id();
|
||||
|
||||
// Post password.
|
||||
$password = strlen( uniqid( 'webhook_' ) );
|
||||
$data->post_password = $password > 20 ? substr( $password, 0, 20 ) : $password;
|
||||
|
||||
// Post status.
|
||||
$data->post_status = 'publish';
|
||||
} else {
|
||||
|
||||
// Allow edit post title.
|
||||
if ( ! empty( $request['name'] ) ) {
|
||||
$data->post_title = $request['name'];
|
||||
}
|
||||
}
|
||||
|
||||
// Comment status.
|
||||
$data->comment_status = 'closed';
|
||||
|
||||
// Ping status.
|
||||
$data->ping_status = 'closed';
|
||||
|
||||
/**
|
||||
* Filter the query_vars used in `get_items` for the constructed query.
|
||||
*
|
||||
* The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
|
||||
* prepared for insertion.
|
||||
*
|
||||
* @param stdClass $data An object representing a single item prepared
|
||||
* for inserting or updating the database.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $data, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single webhook output for response.
|
||||
*
|
||||
* @param WP_Post $webhook Webhook object.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response Response data.
|
||||
*/
|
||||
public function prepare_item_for_response( $post, $request ) {
|
||||
$id = (int) $post->ID;
|
||||
$webhook = new WC_Webhook( $id );
|
||||
$data = array(
|
||||
'id' => $webhook->id,
|
||||
'name' => $webhook->get_name(),
|
||||
'status' => $webhook->get_status(),
|
||||
'topic' => $webhook->get_topic(),
|
||||
'resource' => $webhook->get_resource(),
|
||||
'event' => $webhook->get_event(),
|
||||
'hooks' => $webhook->get_hooks(),
|
||||
'delivery_url' => $webhook->get_delivery_url(),
|
||||
'date_created' => wc_rest_prepare_date_response( $webhook->get_post_data()->post_date_gmt ),
|
||||
'date_modified' => wc_rest_prepare_date_response( $webhook->get_post_data()->post_modified_gmt ),
|
||||
);
|
||||
|
||||
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
$data = $this->filter_response_by_context( $data, $context );
|
||||
|
||||
// Wrap the data in a response object.
|
||||
$response = rest_ensure_response( $data );
|
||||
|
||||
$response->add_links( $this->prepare_links( $post ) );
|
||||
|
||||
/**
|
||||
* Filter webhook object returned from the REST API.
|
||||
*
|
||||
* @param WP_REST_Response $response The response object.
|
||||
* @param WC_Webhook $webhook Webhook object used to create response.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
*/
|
||||
return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $webhook, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Query args.
|
||||
*
|
||||
* @param array $args
|
||||
* @param WP_REST_Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function query_args( $args, $request ) {
|
||||
// Set post_status.
|
||||
switch ( $request['status'] ) {
|
||||
case 'active' :
|
||||
$args['post_status'] = 'publish';
|
||||
break;
|
||||
case 'paused' :
|
||||
$args['post_status'] = 'draft';
|
||||
break;
|
||||
case 'disabled' :
|
||||
$args['post_status'] = 'pending';
|
||||
break;
|
||||
default :
|
||||
$args['post_status'] = 'any';
|
||||
break;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Webhook's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
$schema = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => 'webhook',
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'A friendly name for the webhook.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'status' => array(
|
||||
'description' => __( 'Webhook status.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'default' => 'active',
|
||||
'enum' => array( 'active', 'paused', 'disabled' ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'arg_options' => array(
|
||||
'sanitize_callback' => 'wc_is_webhook_valid_topic',
|
||||
),
|
||||
),
|
||||
'topic' => array(
|
||||
'description' => __( 'Webhook topic.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'resource' => array(
|
||||
'description' => __( 'Webhook resource.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'event' => array(
|
||||
'description' => __( 'Webhook event.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'hooks' => array(
|
||||
'description' => __( 'WooCommerce action names associated with the webhook.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'delivery_url' => array(
|
||||
'description' => __( 'The URL where the webhook payload is delivered.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'secret' => array(
|
||||
'description' => __( "Secret key used to generate a hash of the delivered webhook and provided in the request headers. This will default to the current API user's consumer secret if not provided.", 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
'context' => array( 'edit' ),
|
||||
'writeonly' => true,
|
||||
),
|
||||
'date_created' => array(
|
||||
'description' => __( "The date the webhook was created, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'date_modified' => array(
|
||||
'description' => __( "The date the webhook was last modified, in the site's timezone.", 'woocommerce' ),
|
||||
'type' => 'date-time',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $this->add_additional_fields_schema( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections of attachments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
$params = parent::get_collection_params();
|
||||
|
||||
$params['status'] = array(
|
||||
'default' => 'all',
|
||||
'description' => __( 'Limit result set to webhooks assigned a specific status.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'enum' => array( 'all', 'active', 'paused', 'disabled' ),
|
||||
'sanitize_callback' => 'sanitize_key',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
);
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ class WC_API {
|
|||
* This is the major version for the REST API and takes
|
||||
* first-order position in endpoint URLs.
|
||||
*
|
||||
* @deprecated 2.6.0
|
||||
* @var string
|
||||
*/
|
||||
const VERSION = '3.1.0';
|
||||
|
@ -29,6 +30,7 @@ class WC_API {
|
|||
/**
|
||||
* The REST API server.
|
||||
*
|
||||
* @deprecated 2.6.0
|
||||
* @var WC_API_Server
|
||||
*/
|
||||
public $server;
|
||||
|
@ -36,6 +38,7 @@ class WC_API {
|
|||
/**
|
||||
* REST API authentication class instance.
|
||||
*
|
||||
* @deprecated 2.6.0
|
||||
* @var WC_API_Authentication
|
||||
*/
|
||||
public $authentication;
|
||||
|
@ -47,20 +50,23 @@ class WC_API {
|
|||
* @return WC_API
|
||||
*/
|
||||
public function __construct() {
|
||||
// add query vars
|
||||
// Add query vars.
|
||||
add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 );
|
||||
|
||||
// register API endpoints
|
||||
// Register API endpoints.
|
||||
add_action( 'init', array( $this, 'add_endpoint' ), 0 );
|
||||
|
||||
// handle REST API requests
|
||||
// Handle REST API requests.
|
||||
add_action( 'parse_request', array( $this, 'handle_rest_api_requests' ), 0 );
|
||||
|
||||
// handle wc-api endpoint requests
|
||||
// Handle wc-api endpoint requests.
|
||||
add_action( 'parse_request', array( $this, 'handle_api_requests' ), 0 );
|
||||
|
||||
// Ensure payment gateways are initialized in time for API requests
|
||||
// Ensure payment gateways are initialized in time for API requests.
|
||||
add_action( 'woocommerce_api_request', array( 'WC_Payment_Gateways', 'instance' ), 0 );
|
||||
|
||||
// WP REST API.
|
||||
$this->rest_api_init();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,8 +78,9 @@ class WC_API {
|
|||
*/
|
||||
public function add_query_vars( $vars ) {
|
||||
$vars[] = 'wc-api';
|
||||
$vars[] = 'wc-api-version';
|
||||
$vars[] = 'wc-api-route';
|
||||
$vars[] = 'wc-api-version'; // Deprecated since 2.6.0.
|
||||
$vars[] = 'wc-api-route'; // Deprecated since 2.6.0.
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
|
@ -84,11 +91,11 @@ class WC_API {
|
|||
*/
|
||||
public static function add_endpoint() {
|
||||
|
||||
// REST API
|
||||
// REST API, deprecated since 2.6.0.
|
||||
add_rewrite_rule( '^wc-api/v([1-3]{1})/?$', 'index.php?wc-api-version=$matches[1]&wc-api-route=/', 'top' );
|
||||
add_rewrite_rule( '^wc-api/v([1-3]{1})(.*)?', 'index.php?wc-api-version=$matches[1]&wc-api-route=$matches[2]', 'top' );
|
||||
|
||||
// WC API for payment gateway IPNs, etc
|
||||
// WC API for payment gateway IPNs, etc.
|
||||
add_rewrite_endpoint( 'wc-api', EP_ALL );
|
||||
}
|
||||
|
||||
|
@ -97,6 +104,7 @@ class WC_API {
|
|||
* Handle REST API requests.
|
||||
*
|
||||
* @since 2.2
|
||||
* @deprecated 2.6.0
|
||||
*/
|
||||
public function handle_rest_api_requests() {
|
||||
global $wp;
|
||||
|
@ -109,13 +117,13 @@ class WC_API {
|
|||
$wp->query_vars['wc-api-route'] = $_GET['wc-api-route'];
|
||||
}
|
||||
|
||||
// REST API request
|
||||
// REST API request.
|
||||
if ( ! empty( $wp->query_vars['wc-api-version'] ) && ! empty( $wp->query_vars['wc-api-route'] ) ) {
|
||||
|
||||
define( 'WC_API_REQUEST', true );
|
||||
define( 'WC_API_REQUEST_VERSION', absint( $wp->query_vars['wc-api-version'] ) );
|
||||
|
||||
// legacy v1 API request
|
||||
// Legacy v1 API request.
|
||||
if ( 1 === WC_API_REQUEST_VERSION ) {
|
||||
$this->handle_v1_rest_api_request();
|
||||
} else if ( 2 === WC_API_REQUEST_VERSION ) {
|
||||
|
@ -125,10 +133,10 @@ class WC_API {
|
|||
|
||||
$this->server = new WC_API_Server( $wp->query_vars['wc-api-route'] );
|
||||
|
||||
// load API resource classes
|
||||
// load API resource classes.
|
||||
$this->register_resources( $this->server );
|
||||
|
||||
// Fire off the request
|
||||
// Fire off the request.
|
||||
$this->server->serve_request();
|
||||
}
|
||||
|
||||
|
@ -140,29 +148,30 @@ class WC_API {
|
|||
* Include required files for REST API request.
|
||||
*
|
||||
* @since 2.1
|
||||
* @deprecated 2.6.0
|
||||
*/
|
||||
public function includes() {
|
||||
|
||||
// API server / response handlers
|
||||
include_once( 'api/class-wc-api-exception.php' );
|
||||
include_once( 'api/class-wc-api-server.php' );
|
||||
include_once( 'api/interface-wc-api-handler.php' );
|
||||
include_once( 'api/class-wc-api-json-handler.php' );
|
||||
// API server / response handlers.
|
||||
include_once( 'api/legacy/v3/class-wc-api-exception.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-server.php' );
|
||||
include_once( 'api/legacy/v3/interface-wc-api-handler.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-json-handler.php' );
|
||||
|
||||
// authentication
|
||||
include_once( 'api/class-wc-api-authentication.php' );
|
||||
// Authentication.
|
||||
include_once( 'api/legacy/v3/class-wc-api-authentication.php' );
|
||||
$this->authentication = new WC_API_Authentication();
|
||||
|
||||
include_once( 'api/class-wc-api-resource.php' );
|
||||
include_once( 'api/class-wc-api-coupons.php' );
|
||||
include_once( 'api/class-wc-api-customers.php' );
|
||||
include_once( 'api/class-wc-api-orders.php' );
|
||||
include_once( 'api/class-wc-api-products.php' );
|
||||
include_once( 'api/class-wc-api-reports.php' );
|
||||
include_once( 'api/class-wc-api-taxes.php' );
|
||||
include_once( 'api/class-wc-api-webhooks.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-resource.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-coupons.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-customers.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-orders.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-products.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-reports.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-taxes.php' );
|
||||
include_once( 'api/legacy/v3/class-wc-api-webhooks.php' );
|
||||
|
||||
// allow plugins to load other response handlers or resource classes
|
||||
// Allow plugins to load other response handlers or resource classes.
|
||||
do_action( 'woocommerce_api_loaded' );
|
||||
}
|
||||
|
||||
|
@ -170,6 +179,7 @@ class WC_API {
|
|||
* Register available API resources.
|
||||
*
|
||||
* @since 2.1
|
||||
* @deprecated 2.6.0
|
||||
* @param WC_API_Server $server the REST server
|
||||
*/
|
||||
public function register_resources( $server ) {
|
||||
|
@ -196,31 +206,32 @@ class WC_API {
|
|||
* Handle legacy v1 REST API requests.
|
||||
*
|
||||
* @since 2.2
|
||||
* @deprecated 2.6.0
|
||||
*/
|
||||
private function handle_v1_rest_api_request() {
|
||||
|
||||
// include legacy required files for v1 REST API request
|
||||
include_once( 'api/v1/class-wc-api-server.php' );
|
||||
include_once( 'api/v1/interface-wc-api-handler.php' );
|
||||
include_once( 'api/v1/class-wc-api-json-handler.php' );
|
||||
include_once( 'api/v1/class-wc-api-xml-handler.php' );
|
||||
// Include legacy required files for v1 REST API request.
|
||||
include_once( 'api/legacy/v1/class-wc-api-server.php' );
|
||||
include_once( 'api/legacy/v1/interface-wc-api-handler.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-json-handler.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-xml-handler.php' );
|
||||
|
||||
include_once( 'api/v1/class-wc-api-authentication.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-authentication.php' );
|
||||
$this->authentication = new WC_API_Authentication();
|
||||
|
||||
include_once( 'api/v1/class-wc-api-resource.php' );
|
||||
include_once( 'api/v1/class-wc-api-coupons.php' );
|
||||
include_once( 'api/v1/class-wc-api-customers.php' );
|
||||
include_once( 'api/v1/class-wc-api-orders.php' );
|
||||
include_once( 'api/v1/class-wc-api-products.php' );
|
||||
include_once( 'api/v1/class-wc-api-reports.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-resource.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-coupons.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-customers.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-orders.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-products.php' );
|
||||
include_once( 'api/legacy/v1/class-wc-api-reports.php' );
|
||||
|
||||
// allow plugins to load other response handlers or resource classes
|
||||
// Allow plugins to load other response handlers or resource classes.
|
||||
do_action( 'woocommerce_api_loaded' );
|
||||
|
||||
$this->server = new WC_API_Server( $GLOBALS['wp']->query_vars['wc-api-route'] );
|
||||
|
||||
// Register available resources for legacy v1 REST API request
|
||||
// Register available resources for legacy v1 REST API request.
|
||||
$api_classes = apply_filters( 'woocommerce_api_classes',
|
||||
array(
|
||||
'WC_API_Customers',
|
||||
|
@ -235,7 +246,7 @@ class WC_API {
|
|||
$this->$api_class = new $api_class( $this->server );
|
||||
}
|
||||
|
||||
// Fire off the request
|
||||
// Fire off the request.
|
||||
$this->server->serve_request();
|
||||
}
|
||||
|
||||
|
@ -243,30 +254,31 @@ class WC_API {
|
|||
* Handle legacy v2 REST API requests.
|
||||
*
|
||||
* @since 2.4
|
||||
* @deprecated 2.6.0
|
||||
*/
|
||||
private function handle_v2_rest_api_request() {
|
||||
include_once( 'api/v2/class-wc-api-exception.php' );
|
||||
include_once( 'api/v2/class-wc-api-server.php' );
|
||||
include_once( 'api/v2/interface-wc-api-handler.php' );
|
||||
include_once( 'api/v2/class-wc-api-json-handler.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-exception.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-server.php' );
|
||||
include_once( 'api/legacy/v2/interface-wc-api-handler.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-json-handler.php' );
|
||||
|
||||
include_once( 'api/v2/class-wc-api-authentication.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-authentication.php' );
|
||||
$this->authentication = new WC_API_Authentication();
|
||||
|
||||
include_once( 'api/v2/class-wc-api-resource.php' );
|
||||
include_once( 'api/v2/class-wc-api-coupons.php' );
|
||||
include_once( 'api/v2/class-wc-api-customers.php' );
|
||||
include_once( 'api/v2/class-wc-api-orders.php' );
|
||||
include_once( 'api/v2/class-wc-api-products.php' );
|
||||
include_once( 'api/v2/class-wc-api-reports.php' );
|
||||
include_once( 'api/v2/class-wc-api-webhooks.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-resource.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-coupons.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-customers.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-orders.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-products.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-reports.php' );
|
||||
include_once( 'api/legacy/v2/class-wc-api-webhooks.php' );
|
||||
|
||||
// allow plugins to load other response handlers or resource classes
|
||||
// allow plugins to load other response handlers or resource classes.
|
||||
do_action( 'woocommerce_api_loaded' );
|
||||
|
||||
$this->server = new WC_API_Server( $GLOBALS['wp']->query_vars['wc-api-route'] );
|
||||
|
||||
// Register available resources for legacy v2 REST API request
|
||||
// Register available resources for legacy v2 REST API request.
|
||||
$api_classes = apply_filters( 'woocommerce_api_classes',
|
||||
array(
|
||||
'WC_API_Customers',
|
||||
|
@ -282,15 +294,15 @@ class WC_API {
|
|||
$this->$api_class = new $api_class( $this->server );
|
||||
}
|
||||
|
||||
// Fire off the request
|
||||
// Fire off the request.
|
||||
$this->server->serve_request();
|
||||
}
|
||||
|
||||
/**
|
||||
* API request - Trigger any API requests.
|
||||
*
|
||||
* @since 2.0
|
||||
* @version 2.4
|
||||
* @since 2.0
|
||||
* @version 2.4
|
||||
*/
|
||||
public function handle_api_requests() {
|
||||
global $wp;
|
||||
|
@ -299,30 +311,132 @@ class WC_API {
|
|||
$wp->query_vars['wc-api'] = $_GET['wc-api'];
|
||||
}
|
||||
|
||||
// wc-api endpoint requests
|
||||
// wc-api endpoint requests.
|
||||
if ( ! empty( $wp->query_vars['wc-api'] ) ) {
|
||||
|
||||
// Buffer, we won't want any output here
|
||||
// Buffer, we won't want any output here.
|
||||
ob_start();
|
||||
|
||||
// No cache headers
|
||||
// No cache headers.
|
||||
nocache_headers();
|
||||
|
||||
// Clean the API request
|
||||
// Clean the API request.
|
||||
$api_request = strtolower( wc_clean( $wp->query_vars['wc-api'] ) );
|
||||
|
||||
// Trigger generic action before request hook
|
||||
// Trigger generic action before request hook.
|
||||
do_action( 'woocommerce_api_request', $api_request );
|
||||
|
||||
// Is there actually something hooked into this API request? If not trigger 400 - Bad request
|
||||
// Is there actually something hooked into this API request? If not trigger 400 - Bad request.
|
||||
status_header( has_action( 'woocommerce_api_' . $api_request ) ? 200 : 400 );
|
||||
|
||||
// Trigger an action which plugins can hook into to fulfill the request
|
||||
// Trigger an action which plugins can hook into to fulfill the request.
|
||||
do_action( 'woocommerce_api_' . $api_request );
|
||||
|
||||
// Done, clear buffer and exit
|
||||
// Done, clear buffer and exit.
|
||||
ob_end_clean();
|
||||
die('-1');
|
||||
die( '-1' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init WP REST API.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
private function rest_api_init() {
|
||||
global $wp_version;
|
||||
|
||||
// REST API was included starting WordPress 4.4.
|
||||
if ( version_compare( $wp_version, 4.4, '<' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->rest_api_includes();
|
||||
|
||||
// Init REST API routes.
|
||||
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Include REST API classes.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
private function rest_api_includes() {
|
||||
// Exception handler.
|
||||
if ( ! class_exists( 'WC_API_Exception' ) ) {
|
||||
include_once( 'api/legacy/v3/class-wc-api-exception.php' );
|
||||
}
|
||||
include_once( 'api/class-wc-rest-exception.php' );
|
||||
|
||||
// Authentication.
|
||||
include_once( 'api/class-wc-rest-authentication.php' );
|
||||
|
||||
// WP-API classes and functions.
|
||||
include_once( 'vendor/wp-rest-functions.php' );
|
||||
if ( ! class_exists( 'WP_REST_Controller' ) ) {
|
||||
include_once( 'vendor/class-wp-rest-controller.php' );
|
||||
}
|
||||
|
||||
// Abstract controllers.
|
||||
include_once( 'abstracts/abstract-wc-rest-posts-controller.php' );
|
||||
include_once( 'abstracts/abstract-wc-rest-terms-controller.php' );
|
||||
|
||||
// REST API controllers.
|
||||
include_once( 'api/class-wc-rest-coupons-controller.php' );
|
||||
include_once( 'api/class-wc-rest-customer-downloads-controller.php' );
|
||||
include_once( 'api/class-wc-rest-customers-controller.php' );
|
||||
include_once( 'api/class-wc-rest-order-notes-controller.php' );
|
||||
include_once( 'api/class-wc-rest-order-refunds-controller.php' );
|
||||
include_once( 'api/class-wc-rest-orders-controller.php' );
|
||||
include_once( 'api/class-wc-rest-product-attribute-terms-controller.php' );
|
||||
include_once( 'api/class-wc-rest-product-attributes-controller.php' );
|
||||
include_once( 'api/class-wc-rest-product-categories-controller.php' );
|
||||
include_once( 'api/class-wc-rest-product-reviews-controller.php' );
|
||||
include_once( 'api/class-wc-rest-product-shipping-classes-controller.php' );
|
||||
include_once( 'api/class-wc-rest-product-tags-controller.php' );
|
||||
include_once( 'api/class-wc-rest-products-controller.php' );
|
||||
include_once( 'api/class-wc-rest-report-sales-controller.php' );
|
||||
include_once( 'api/class-wc-rest-report-top-sellers-controller.php' );
|
||||
include_once( 'api/class-wc-rest-reports-controller.php' );
|
||||
include_once( 'api/class-wc-rest-tax-classes-controller.php' );
|
||||
include_once( 'api/class-wc-rest-taxes-controller.php' );
|
||||
include_once( 'api/class-wc-rest-webhook-deliveries.php' );
|
||||
include_once( 'api/class-wc-rest-webhooks-controller.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST API routes.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public function register_rest_routes() {
|
||||
$controllers = array(
|
||||
'WC_REST_Coupons_Controller',
|
||||
'WC_REST_Customer_Downloads_Controller',
|
||||
'WC_REST_Customers_Controller',
|
||||
'WC_REST_Order_Notes_Controller',
|
||||
'WC_REST_Order_Refunds_Controller',
|
||||
'WC_REST_Orders_Controller',
|
||||
'WC_REST_Product_Attribute_Terms_Controller',
|
||||
'WC_REST_Product_Attributes_Controller',
|
||||
'WC_REST_Product_Categories_Controller',
|
||||
'WC_REST_Product_Reviews_Controller',
|
||||
'WC_REST_Product_Shipping_Classes_Controller',
|
||||
'WC_REST_Product_Tags_Controller',
|
||||
'WC_REST_Products_Controller',
|
||||
'WC_REST_Report_Sales_Controller',
|
||||
'WC_REST_Report_Top_Sellers_Controller',
|
||||
'WC_REST_Reports_Controller',
|
||||
'WC_REST_Tax_Classes_Controller',
|
||||
'WC_REST_Taxes_Controller',
|
||||
'WC_REST_Webhook_Deliveries_Controller',
|
||||
'WC_REST_Webhooks_Controller',
|
||||
);
|
||||
|
||||
foreach ( $controllers as $controller ) {
|
||||
$this->$controller = new $controller();
|
||||
$this->$controller->register_routes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -727,18 +727,19 @@ class WC_Tax {
|
|||
* @since 2.5.0
|
||||
* @access private
|
||||
*
|
||||
* @param int $tax_rate_id
|
||||
* @param int $tax_rate_id
|
||||
* @param string $output_type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function _get_tax_rate( $tax_rate_id ) {
|
||||
public static function _get_tax_rate( $tax_rate_id, $output_type = ARRAY_A ) {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->get_row( $wpdb->prepare( "
|
||||
SELECT *
|
||||
FROM {$wpdb->prefix}woocommerce_tax_rates
|
||||
WHERE tax_rate_id = %d
|
||||
", $tax_rate_id ), ARRAY_A );
|
||||
", $tax_rate_id ), $output_type );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,478 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @version 2.0-beta12
|
||||
*/
|
||||
abstract class WP_REST_Controller {
|
||||
|
||||
/**
|
||||
* The namespace of this controller's route.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace;
|
||||
|
||||
/**
|
||||
* The base of this controller's route.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base;
|
||||
|
||||
/**
|
||||
* Register the routes for the objects of the controller.
|
||||
*/
|
||||
public function register_routes() {
|
||||
_doing_it_wrong( 'WP_REST_Controller::register_routes', 'The register_routes() method must be overriden', 'WPAPI-2.0' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to get items.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of items.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_items( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to get a specific item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_item_permissions_check( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one item from the collection.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function get_item( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to create items.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function create_item_permissions_check( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create one item from the collection.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function create_item( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to update a specific item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function update_item_permissions_check( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update one item from the collection.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function update_item( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given request has access to delete a specific item.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function delete_item_permissions_check( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete one item from the collection.
|
||||
*
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|WP_REST_Response
|
||||
*/
|
||||
public function delete_item( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the item for create or update operation.
|
||||
*
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_Error|object $prepared_item
|
||||
*/
|
||||
protected function prepare_item_for_database( $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the item for the REST response.
|
||||
*
|
||||
* @param mixed $item WordPress representation of the item.
|
||||
* @param WP_REST_Request $request Request object.
|
||||
* @return WP_REST_Response $response
|
||||
*/
|
||||
public function prepare_item_for_response( $item, $request ) {
|
||||
return new WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be over-ridden in subclass.", __METHOD__ ), array( 'status' => 405 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a response for inserting into a collection.
|
||||
*
|
||||
* @param WP_REST_Response $response Response object.
|
||||
* @return array Response data, ready for insertion into collection data.
|
||||
*/
|
||||
public function prepare_response_for_collection( $response ) {
|
||||
if ( ! ( $response instanceof WP_REST_Response ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$data = (array) $response->get_data();
|
||||
$links = WP_REST_Server::get_response_links( $response );
|
||||
if ( ! empty( $links ) ) {
|
||||
$data['_links'] = $links;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a response based on the context defined in the schema.
|
||||
*
|
||||
* @param array $data
|
||||
* @param string $context
|
||||
* @return array
|
||||
*/
|
||||
public function filter_response_by_context( $data, $context ) {
|
||||
|
||||
$schema = $this->get_item_schema();
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( empty( $schema['properties'][ $key ] ) || empty( $schema['properties'][ $key ]['context'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! in_array( $context, $schema['properties'][ $key ]['context'] ) ) {
|
||||
unset( $data[ $key ] );
|
||||
}
|
||||
|
||||
if ( 'object' === $schema['properties'][ $key ]['type'] && ! empty( $schema['properties'][ $key ]['properties'] ) ) {
|
||||
foreach ( $schema['properties'][ $key ]['properties'] as $attribute => $details ) {
|
||||
if ( empty( $details['context'] ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( ! in_array( $context, $details['context'] ) ) {
|
||||
if ( isset( $data[ $key ][ $attribute ] ) ) {
|
||||
unset( $data[ $key ][ $attribute ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item's schema, conforming to JSON Schema.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_schema() {
|
||||
return $this->add_additional_fields_schema( array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item's schema for display / public consumption purposes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_public_item_schema() {
|
||||
|
||||
$schema = $this->get_item_schema();
|
||||
|
||||
foreach ( $schema['properties'] as &$property ) {
|
||||
if ( isset( $property['arg_options'] ) ) {
|
||||
unset( $property['arg_options'] );
|
||||
}
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query params for collections.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'context' => $this->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',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the magical context param.
|
||||
*
|
||||
* Ensures consistent description between endpoints, and populates enum from schema.
|
||||
*
|
||||
* @param array $args
|
||||
* @return array
|
||||
*/
|
||||
public 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 = $this->get_item_schema();
|
||||
if ( empty( $schema['properties'] ) ) {
|
||||
return array_merge( $param_details, $args );
|
||||
}
|
||||
$contexts = array();
|
||||
foreach ( $schema['properties'] as $key => $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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the values from additional fields to a data object.
|
||||
*
|
||||
* @param array $object
|
||||
* @param WP_REST_Request $request
|
||||
* @return array modified object with additional fields.
|
||||
*/
|
||||
protected function add_additional_fields_to_object( $object, $request ) {
|
||||
|
||||
$additional_fields = $this->get_additional_fields();
|
||||
|
||||
foreach ( $additional_fields as $field_name => $field_options ) {
|
||||
|
||||
if ( ! $field_options['get_callback'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$object[ $field_name ] = call_user_func( $field_options['get_callback'], $object, $field_name, $request, $this->get_object_type() );
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the values of additional fields added to a data object.
|
||||
*
|
||||
* @param array $object
|
||||
* @param WP_REST_Request $request
|
||||
*/
|
||||
protected function update_additional_fields_for_object( $object, $request ) {
|
||||
|
||||
$additional_fields = $this->get_additional_fields();
|
||||
|
||||
foreach ( $additional_fields as $field_name => $field_options ) {
|
||||
|
||||
if ( ! $field_options['update_callback'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't run the update callbacks if the data wasn't passed in the request.
|
||||
if ( ! isset( $request[ $field_name ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
call_user_func( $field_options['update_callback'], $request[ $field_name ], $object, $field_name, $request, $this->get_object_type() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the schema from additional fields to an schema array.
|
||||
*
|
||||
* The type of object is inferred from the passed schema.
|
||||
*
|
||||
* @param array $schema Schema array.
|
||||
*/
|
||||
protected function add_additional_fields_schema( $schema ) {
|
||||
if ( empty( $schema['title'] ) ) {
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can't use $this->get_object_type otherwise we cause an inf loop.
|
||||
*/
|
||||
$object_type = $schema['title'];
|
||||
|
||||
$additional_fields = $this->get_additional_fields( $object_type );
|
||||
|
||||
foreach ( $additional_fields as $field_name => $field_options ) {
|
||||
if ( ! $field_options['schema'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$schema['properties'][ $field_name ] = $field_options['schema'];
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the registered additional fields for a given object-type.
|
||||
*
|
||||
* @param string $object_type
|
||||
* @return array
|
||||
*/
|
||||
protected function get_additional_fields( $object_type = null ) {
|
||||
|
||||
if ( ! $object_type ) {
|
||||
$object_type = $this->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 ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object type this controller is responsible for managing.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_object_type() {
|
||||
$schema = $this->get_item_schema();
|
||||
|
||||
if ( ! $schema || ! isset( $schema['title'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $schema['title'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of endpoint arguments from the item schema for the controller.
|
||||
*
|
||||
* @param string $method HTTP method of the request. The arguments
|
||||
* for `CREATABLE` requests are checked for required
|
||||
* values and may fall-back to a given default, this
|
||||
* is not done on `EDITABLE` requests. Default is
|
||||
* WP_REST_Server::CREATABLE.
|
||||
* @return array $endpoint_args
|
||||
*/
|
||||
public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) {
|
||||
|
||||
$schema = $this->get_item_schema();
|
||||
$schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array();
|
||||
$endpoint_args = array();
|
||||
|
||||
foreach ( $schema_properties as $field_id => $params ) {
|
||||
|
||||
// Arguments specified as `readonly` are not allowed to be set.
|
||||
if ( ! empty( $params['readonly'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$endpoint_args[ $field_id ] = array(
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
'sanitize_callback' => 'rest_sanitize_request_arg',
|
||||
);
|
||||
|
||||
if ( WP_REST_Server::CREATABLE === $method && isset( $params['default'] ) ) {
|
||||
$endpoint_args[ $field_id ]['default'] = $params['default'];
|
||||
}
|
||||
|
||||
if ( WP_REST_Server::CREATABLE === $method && ! empty( $params['required'] ) ) {
|
||||
$endpoint_args[ $field_id ]['required'] = true;
|
||||
}
|
||||
|
||||
foreach ( array( 'type', 'format', 'enum' ) as $schema_prop ) {
|
||||
if ( isset( $params[ $schema_prop ] ) ) {
|
||||
$endpoint_args[ $field_id ][ $schema_prop ] = $params[ $schema_prop ];
|
||||
}
|
||||
}
|
||||
|
||||
// Merge in any options provided by the schema property.
|
||||
if ( isset( $params['arg_options'] ) ) {
|
||||
|
||||
// Only use required / default from arg_options on CREATABLE endpoints.
|
||||
if ( WP_REST_Server::CREATABLE !== $method ) {
|
||||
$params['arg_options'] = array_diff_key( $params['arg_options'], array( 'required' => '', 'default' => '' ) );
|
||||
}
|
||||
|
||||
$endpoint_args[ $field_id ] = array_merge( $endpoint_args[ $field_id ], $params['arg_options'] );
|
||||
}
|
||||
}
|
||||
|
||||
return $endpoint_args;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
<?php
|
||||
/**
|
||||
* @version 2.0-beta12
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'rest_authorization_required_code' ) ) {
|
||||
/**
|
||||
* Returns a contextual HTTP error code for authorization failure.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
function rest_authorization_required_code() {
|
||||
return is_user_logged_in() ? 403 : 401;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'register_rest_field' ) ) {
|
||||
/**
|
||||
* Registers a new field on an existing WordPress object type.
|
||||
*
|
||||
* @global array $wp_rest_additional_fields Holds registered fields, organized
|
||||
* by object type.
|
||||
*
|
||||
* @param string|array $object_type Object(s) the field is being registered
|
||||
* to, "post"|"term"|"comment" etc.
|
||||
* @param string $attribute The attribute name.
|
||||
* @param array $args {
|
||||
* Optional. An array of arguments used to handle the registered field.
|
||||
*
|
||||
* @type string|array|null $get_callback Optional. The callback function used to retrieve the field
|
||||
* value. Default is 'null', the field will not be returned in
|
||||
* the response.
|
||||
* @type string|array|null $update_callback Optional. The callback function used to set and update the
|
||||
* field value. Default is 'null', the value cannot be set or
|
||||
* updated.
|
||||
* @type string|array|null $schema Optional. The callback function used to create the schema for
|
||||
* this field. Default is 'null', no schema entry will be returned.
|
||||
* }
|
||||
*/
|
||||
function register_rest_field( $object_type, $attribute, $args = array() ) {
|
||||
$defaults = array(
|
||||
'get_callback' => null,
|
||||
'update_callback' => null,
|
||||
'schema' => null,
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
global $wp_rest_additional_fields;
|
||||
|
||||
$object_types = (array) $object_type;
|
||||
|
||||
foreach ( $object_types as $object_type ) {
|
||||
$wp_rest_additional_fields[ $object_type ][ $attribute ] = $args;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'register_api_field' ) ) {
|
||||
/**
|
||||
* Backwards compat shim
|
||||
*/
|
||||
function register_api_field( $object_type, $attributes, $args = array() ) {
|
||||
_deprecated_function( 'register_api_field', 'WPAPI-2.0', 'register_rest_field' );
|
||||
register_rest_field( $object_type, $attributes, $args );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'rest_validate_request_arg' ) ) {
|
||||
/**
|
||||
* Validate a request argument based on details registered to the route.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param WP_REST_Request $request
|
||||
* @param string $param
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
function rest_validate_request_arg( $value, $request, $param ) {
|
||||
|
||||
$attributes = $request->get_attributes();
|
||||
if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
|
||||
return true;
|
||||
}
|
||||
$args = $attributes['args'][ $param ];
|
||||
|
||||
if ( ! empty( $args['enum'] ) ) {
|
||||
if ( ! in_array( $value, $args['enum'] ) ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not one of %s', 'woocommerce' ), $param, implode( ', ', $args['enum'] ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'integer' === $args['type'] && ! is_numeric( $value ) ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s', 'woocommerce' ), $param, 'integer' ) );
|
||||
}
|
||||
|
||||
if ( 'string' === $args['type'] && ! is_string( $value ) ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s', 'woocommerce' ), $param, 'string' ) );
|
||||
}
|
||||
|
||||
if ( isset( $args['format'] ) ) {
|
||||
switch ( $args['format'] ) {
|
||||
case 'date-time' :
|
||||
if ( ! rest_parse_date( $value ) ) {
|
||||
return new WP_Error( 'rest_invalid_date', __( 'The date you provided is invalid.', 'woocommerce' ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case 'email' :
|
||||
if ( ! is_email( $value ) ) {
|
||||
return new WP_Error( 'rest_invalid_email', __( 'The email address you provided is invalid.', 'woocommerce' ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( in_array( $args['type'], array( 'numeric', 'integer' ) ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) {
|
||||
if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) {
|
||||
if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be greater than %d (exclusive)', 'woocommerce' ), $param, $args['minimum'] ) );
|
||||
} else if ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be greater than %d (inclusive)', 'woocommerce' ), $param, $args['minimum'] ) );
|
||||
}
|
||||
} else if ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) {
|
||||
if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be less than %d (exclusive)', 'woocommerce' ), $param, $args['maximum'] ) );
|
||||
} else if ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be less than %d (inclusive)', 'woocommerce' ), $param, $args['maximum'] ) );
|
||||
}
|
||||
} else if ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) {
|
||||
if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) {
|
||||
if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (exclusive) and %d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) );
|
||||
}
|
||||
} else if ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) {
|
||||
if ( $value >= $args['maximum'] || $value < $args['minimum'] ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (inclusive) and %d (exclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) );
|
||||
}
|
||||
} else if ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) {
|
||||
if ( $value > $args['maximum'] || $value <= $args['minimum'] ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (exclusive) and %d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) );
|
||||
}
|
||||
} else if ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) {
|
||||
if ( $value > $args['maximum'] || $value < $args['minimum'] ) {
|
||||
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (inclusive) and %d (inclusive)', 'woocommerce' ), $param, $args['minimum'], $args['maximum'] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'rest_sanitize_request_arg' ) ) {
|
||||
/**
|
||||
* Sanitize a request argument based on details registered to the route.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param WP_REST_Request $request
|
||||
* @param string $param
|
||||
* @return mixed
|
||||
*/
|
||||
function rest_sanitize_request_arg( $value, $request, $param ) {
|
||||
|
||||
$attributes = $request->get_attributes();
|
||||
if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
|
||||
return $value;
|
||||
}
|
||||
$args = $attributes['args'][ $param ];
|
||||
|
||||
if ( 'integer' === $args['type'] ) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
if ( isset( $args['format'] ) ) {
|
||||
switch ( $args['format'] ) {
|
||||
case 'date-time' :
|
||||
return sanitize_text_field( $value );
|
||||
|
||||
case 'email' :
|
||||
/*
|
||||
* sanitize_email() validates, which would be unexpected
|
||||
*/
|
||||
return sanitize_text_field( $value );
|
||||
|
||||
case 'uri' :
|
||||
return esc_url_raw( $value );
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@ include( 'wc-product-functions.php' );
|
|||
include( 'wc-account-functions.php' );
|
||||
include( 'wc-term-functions.php' );
|
||||
include( 'wc-attribute-functions.php' );
|
||||
include( 'wc-rest-functions.php' );
|
||||
|
||||
/**
|
||||
* Filters on data used in admin and frontend.
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce REST Functions
|
||||
*
|
||||
* Functions for REST specific things.
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category Core
|
||||
* @package WooCommerce/Functions
|
||||
* @version 2.6.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and formats a MySQL datetime (Y-m-d H:i:s) for ISO8601/RFC3339.
|
||||
*
|
||||
* Requered WP 4.4 or later.
|
||||
* See https://developer.wordpress.org/reference/functions/mysql_to_rfc3339/
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param string $date_gmt
|
||||
* @param string|null $date
|
||||
* @return string|null ISO8601/RFC3339 formatted datetime.
|
||||
*/
|
||||
function wc_rest_prepare_date_response( $date_gmt, $date = null ) {
|
||||
// Check if mysql_to_rfc3339 exists first!
|
||||
if ( ! function_exists( 'mysql_to_rfc3339' ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use the date if passed.
|
||||
if ( isset( $date ) ) {
|
||||
return mysql_to_rfc3339( $date );
|
||||
}
|
||||
|
||||
// Return null if $date_gmt is empty/zeros.
|
||||
if ( '0000-00-00 00:00:00' === $date_gmt ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the formatted datetime.
|
||||
return mysql_to_rfc3339( $date_gmt );
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload image from URL.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param string $image_url
|
||||
* @return array|WP_Error Attachment data or error message.
|
||||
*/
|
||||
function wc_rest_upload_image_from_url( $image_url ) {
|
||||
$file_name = basename( current( explode( '?', $image_url ) ) );
|
||||
$wp_filetype = wp_check_filetype( $file_name, null );
|
||||
$parsed_url = @parse_url( $image_url );
|
||||
|
||||
// Check parsed URL.
|
||||
if ( ! $parsed_url || ! is_array( $parsed_url ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_image_url', sprintf( __( 'Invalid URL %s.', 'woocommerce' ), $image_url ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// Ensure url is valid.
|
||||
$image_url = esc_url_raw( $image_url );
|
||||
|
||||
// Get the file.
|
||||
$response = wp_safe_remote_get( $image_url, array(
|
||||
'timeout' => 10
|
||||
) );
|
||||
|
||||
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_remote_image_url', sprintf( __( 'Error getting remote image %s.', 'woocommerce' ), $image_url ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// Ensure we have a file name and type.
|
||||
if ( ! $wp_filetype['type'] ) {
|
||||
$headers = wp_remote_retrieve_headers( $response );
|
||||
if ( isset( $headers['content-disposition'] ) && strstr( $headers['content-disposition'], 'filename=' ) ) {
|
||||
$disposition = end( explode( 'filename=', $headers['content-disposition'] ) );
|
||||
$disposition = sanitize_file_name( $disposition );
|
||||
$file_name = $disposition;
|
||||
} elseif ( isset( $headers['content-type'] ) && strstr( $headers['content-type'], 'image/' ) ) {
|
||||
$file_name = 'image.' . str_replace( 'image/', '', $headers['content-type'] );
|
||||
}
|
||||
unset( $headers );
|
||||
}
|
||||
|
||||
// Upload the file.
|
||||
$upload = wp_upload_bits( $file_name, '', wp_remote_retrieve_body( $response ) );
|
||||
|
||||
if ( $upload['error'] ) {
|
||||
return new WP_Error( 'woocommerce_rest_image_upload_error', $upload['error'], array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
// Get filesize.
|
||||
$filesize = filesize( $upload['file'] );
|
||||
|
||||
if ( 0 == $filesize ) {
|
||||
@unlink( $upload['file'] );
|
||||
unset( $upload );
|
||||
|
||||
return new WP_Error( 'woocommerce_rest_image_upload_file_error', __( 'Zero size file downloaded.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_rest_api_uploaded_image_from_url', $upload, $image_url );
|
||||
|
||||
return $upload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set uploaded image as attachment.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param array $upload Upload information from wp_upload_bits.
|
||||
* @param int $id Post ID. Default to 0.
|
||||
* @return int Attachment ID
|
||||
*/
|
||||
function wc_rest_set_uploaded_image_as_attachment( $upload, $id = 0 ) {
|
||||
$info = wp_check_filetype( $upload['file'] );
|
||||
$title = '';
|
||||
$content = '';
|
||||
|
||||
if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
|
||||
include_once( ABSPATH . 'wp-admin/includes/image.php' );
|
||||
}
|
||||
|
||||
if ( $image_meta = wp_read_image_metadata( $upload['file'] ) ) {
|
||||
if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) {
|
||||
$title = $image_meta['title'];
|
||||
}
|
||||
if ( trim( $image_meta['caption'] ) ) {
|
||||
$content = $image_meta['caption'];
|
||||
}
|
||||
}
|
||||
|
||||
$attachment = array(
|
||||
'post_mime_type' => $info['type'],
|
||||
'guid' => $upload['url'],
|
||||
'post_parent' => $id,
|
||||
'post_title' => $title,
|
||||
'post_content' => $content,
|
||||
);
|
||||
|
||||
$attachment_id = wp_insert_attachment( $attachment, $upload['file'], $id );
|
||||
if ( ! is_wp_error( $attachment_id ) ) {
|
||||
wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $upload['file'] ) );
|
||||
}
|
||||
|
||||
return $attachment_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate reports request arguments.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param mixed $value
|
||||
* @param WP_REST_Request $request
|
||||
* @param string $param
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
function wc_rest_validate_reports_request_arg( $value, $request, $param ) {
|
||||
|
||||
$attributes = $request->get_attributes();
|
||||
if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {
|
||||
return true;
|
||||
}
|
||||
$args = $attributes['args'][ $param ];
|
||||
|
||||
if ( 'string' === $args['type'] && ! is_string( $value ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_param', sprintf( __( '%s is not of type %s.', 'woocommerce' ), $param, 'string' ) );
|
||||
}
|
||||
|
||||
if ( 'data' === $args['format'] ) {
|
||||
$regex = '#^\d{4}-\d{2}-\d{2}$#';
|
||||
|
||||
if ( ! preg_match( $regex, $value, $matches ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_invalid_date', __( 'The date you provided is invalid.', 'woocommerce' ) );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a value according to RFC 3986.
|
||||
* Supports multidimensional arrays.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param string|array $value The value to encode.
|
||||
* @return string|array Encoded values.
|
||||
*/
|
||||
function wc_rest_urlencode_rfc3986( $value ) {
|
||||
if ( is_array( $value ) ) {
|
||||
return array_map( 'wc_rest_urlencode_rfc3986', $value );
|
||||
} else {
|
||||
// Percent symbols (%) must be double-encoded.
|
||||
return str_replace( '%', '%25', rawurlencode( rawurldecode( $value ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permissions of posts on REST API.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param string $post_type Post type.
|
||||
* @param string $context Request context.
|
||||
* @param int $object_id Post ID.
|
||||
* @return bool
|
||||
*/
|
||||
function wc_rest_check_post_permissions( $post_type, $context = 'read', $object_id = 0 ) {
|
||||
$contexts = array(
|
||||
'read' => 'read_private_posts',
|
||||
'create' => 'publish_posts',
|
||||
'edit' => 'edit_post',
|
||||
'delete' => 'delete_post',
|
||||
);
|
||||
|
||||
if ( 'revision' === $post_type ) {
|
||||
$permission = false;
|
||||
} else {
|
||||
$cap = $contexts[ $context ];
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
$permission = current_user_can( $post_type_object->cap->$cap, $object_id );
|
||||
}
|
||||
|
||||
return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, $post_type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permissions of users on REST API.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param string $context Request context.
|
||||
* @param int $object_id Post ID.
|
||||
* @return bool
|
||||
*/
|
||||
function wc_rest_check_user_permissions( $context = 'read', $object_id = 0 ) {
|
||||
$contexts = array(
|
||||
'read' => 'list_users',
|
||||
'create' => 'edit_users',
|
||||
'edit' => 'edit_users',
|
||||
'delete' => 'delete_users',
|
||||
);
|
||||
|
||||
$permission = current_user_can( $contexts[ $context ], $object_id );
|
||||
|
||||
return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, 'user' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permissions of product terms on REST API.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param string $taxonomy Taxonomy.
|
||||
* @param string $context Request context.
|
||||
* @param int $object_id Post ID.
|
||||
* @return bool
|
||||
*/
|
||||
function wc_rest_check_product_term_permissions( $taxonomy, $context = 'read', $object_id = 0 ) {
|
||||
$contexts = array(
|
||||
'read' => 'manage_terms',
|
||||
'create' => 'edit_terms',
|
||||
'edit' => 'edit_terms',
|
||||
'delete' => 'delete_terms',
|
||||
);
|
||||
|
||||
$cap = $contexts[ $context ];
|
||||
$taxonomy_object = get_taxonomy( $taxonomy );
|
||||
$permission = current_user_can( $taxonomy_object->cap->$cap, $object_id );
|
||||
|
||||
return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, $taxonomy );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check manager permissions on REST API.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param string $object Object.
|
||||
* @param string $context Request context.
|
||||
* @return bool
|
||||
*/
|
||||
function wc_rest_check_manager_permissions( $object, $context = 'read' ) {
|
||||
$objects = array(
|
||||
'reports' => 'view_woocommerce_reports',
|
||||
'settings' => 'manage_woocommerce',
|
||||
'attributes' => 'manage_product_terms',
|
||||
);
|
||||
|
||||
$permission = current_user_can( $objects[ $object ] );
|
||||
|
||||
return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, 0, $object );
|
||||
}
|
||||
|
|
@ -622,3 +622,51 @@ function wc_set_user_last_update_time( $user_id ) {
|
|||
function wc_get_customer_saved_methods_list( $customer_id ) {
|
||||
return apply_filters( 'woocommerce_saved_payment_methods_list', array(), $customer_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info about customer's last order.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param int $customer_id Customer ID.
|
||||
* @return WC_Order|bool Order object if successful or false.
|
||||
*/
|
||||
function wc_get_customer_last_order( $customer_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$customer_id = absint( $customer_id );
|
||||
|
||||
$id = $wpdb->get_var( "SELECT id
|
||||
FROM $wpdb->posts AS posts
|
||||
LEFT JOIN {$wpdb->postmeta} AS meta on posts.ID = meta.post_id
|
||||
WHERE meta.meta_key = '_customer_user'
|
||||
AND meta.meta_value = {$customer_id}
|
||||
AND posts.post_type = 'shop_order'
|
||||
AND posts.post_status IN ( '" . implode( "','", array_keys( wc_get_order_statuses() ) ) . "' )
|
||||
ORDER BY posts.ID DESC
|
||||
" );
|
||||
|
||||
return wc_get_order( $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for @see get_avatar() which doesn't simply return
|
||||
* the URL so we need to pluck it from the HTML img tag.
|
||||
*
|
||||
* Kudos to https://github.com/WP-API/WP-API for offering a better solution.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param string $email the customer's email.
|
||||
* @return string the URL to the customer's avatar.
|
||||
*/
|
||||
function wc_get_customer_avatar_url( $email ) {
|
||||
$avatar_html = get_avatar( $email );
|
||||
|
||||
// Get the URL of the avatar from the provided HTML.
|
||||
preg_match( '/src=["|\'](.+)[\&|"|\']/U', $avatar_html, $matches );
|
||||
|
||||
if ( isset( $matches[1] ) && ! empty( $matches[1] ) ) {
|
||||
return esc_url_raw( $matches[1] );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue