Copy over v4 and creating working dir
This commit is contained in:
parent
15107d2378
commit
c852671994
|
@ -0,0 +1,801 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Coupons controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /coupons endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Coupons controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Posts_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Coupons_V1_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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coupons 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(
|
||||||
|
'description' => __( 'Coupon code.', 'woocommerce' ),
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/(?P<id>[\d]+)',
|
||||||
|
array(
|
||||||
|
'args' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/batch',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'batch_items' ),
|
||||||
|
'permission_callback' => array( $this, 'batch_items_permissions_check' ),
|
||||||
|
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_batch_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get object.
|
||||||
|
*
|
||||||
|
* @since 3.0.0
|
||||||
|
* @param int $id Object ID.
|
||||||
|
* @return WC_Data
|
||||||
|
*/
|
||||||
|
protected function get_object( $id ) {
|
||||||
|
return new WC_Coupon( $id );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get formatted item data.
|
||||||
|
*
|
||||||
|
* @since 3.0.0
|
||||||
|
* @param WC_Data $object WC_Data instance.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function get_formatted_item_data( $object ) {
|
||||||
|
$data = $object->get_data();
|
||||||
|
|
||||||
|
$format_decimal = array( 'amount', 'minimum_amount', 'maximum_amount' );
|
||||||
|
$format_date = array( 'date_created', 'date_modified', 'date_expires' );
|
||||||
|
$format_null = array( 'usage_limit', 'usage_limit_per_user', 'limit_usage_to_x_items' );
|
||||||
|
|
||||||
|
// Format decimal values.
|
||||||
|
foreach ( $format_decimal as $key ) {
|
||||||
|
$data[ $key ] = wc_format_decimal( $data[ $key ], 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format date values.
|
||||||
|
foreach ( $format_date as $key ) {
|
||||||
|
$datetime = $data[ $key ];
|
||||||
|
$data[ $key ] = wc_rest_prepare_date_response( $datetime, false );
|
||||||
|
$data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format null values.
|
||||||
|
foreach ( $format_null as $key ) {
|
||||||
|
$data[ $key ] = $data[ $key ] ? $data[ $key ] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'id' => $object->get_id(),
|
||||||
|
'code' => $data['code'],
|
||||||
|
'amount' => $data['amount'],
|
||||||
|
'date_created' => $data['date_created'],
|
||||||
|
'date_created_gmt' => $data['date_created_gmt'],
|
||||||
|
'date_modified' => $data['date_modified'],
|
||||||
|
'date_modified_gmt' => $data['date_modified_gmt'],
|
||||||
|
'discount_type' => $data['discount_type'],
|
||||||
|
'description' => $data['description'],
|
||||||
|
'date_expires' => $data['date_expires'],
|
||||||
|
'date_expires_gmt' => $data['date_expires_gmt'],
|
||||||
|
'usage_count' => $data['usage_count'],
|
||||||
|
'individual_use' => $data['individual_use'],
|
||||||
|
'product_ids' => $data['product_ids'],
|
||||||
|
'excluded_product_ids' => $data['excluded_product_ids'],
|
||||||
|
'usage_limit' => $data['usage_limit'],
|
||||||
|
'usage_limit_per_user' => $data['usage_limit_per_user'],
|
||||||
|
'limit_usage_to_x_items' => $data['limit_usage_to_x_items'],
|
||||||
|
'free_shipping' => $data['free_shipping'],
|
||||||
|
'product_categories' => $data['product_categories'],
|
||||||
|
'excluded_product_categories' => $data['excluded_product_categories'],
|
||||||
|
'exclude_sale_items' => $data['exclude_sale_items'],
|
||||||
|
'minimum_amount' => $data['minimum_amount'],
|
||||||
|
'maximum_amount' => $data['maximum_amount'],
|
||||||
|
'email_restrictions' => $data['email_restrictions'],
|
||||||
|
'used_by' => $data['used_by'],
|
||||||
|
'meta_data' => $data['meta_data'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a single coupon output for response.
|
||||||
|
*
|
||||||
|
* @since 3.0.0
|
||||||
|
* @param WC_Data $object Object data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_object_for_response( $object, $request ) {
|
||||||
|
$data = $this->get_formatted_item_data( $object );
|
||||||
|
$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( $object, $request ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the data for a response.
|
||||||
|
*
|
||||||
|
* The dynamic portion of the hook name, $this->post_type,
|
||||||
|
* refers to object type being prepared for the response.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Response $response The response object.
|
||||||
|
* @param WC_Data $object Object data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
*/
|
||||||
|
return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare objects query.
|
||||||
|
*
|
||||||
|
* @since 3.0.0
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_objects_query( $request ) {
|
||||||
|
$args = parent::prepare_objects_query( $request );
|
||||||
|
|
||||||
|
if ( ! empty( $request['code'] ) ) {
|
||||||
|
$id = wc_get_coupon_id_by_code( $request['code'] );
|
||||||
|
$args['post__in'] = array( $id );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get only ids.
|
||||||
|
$args['fields'] = 'ids';
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a single coupon for create or update.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @param bool $creating If is creating a new object.
|
||||||
|
* @return WP_Error|WC_Data
|
||||||
|
*/
|
||||||
|
protected function prepare_object_for_database( $request, $creating = false ) {
|
||||||
|
$id = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
|
||||||
|
$coupon = new WC_Coupon( $id );
|
||||||
|
$schema = $this->get_item_schema();
|
||||||
|
$data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) );
|
||||||
|
|
||||||
|
// Validate required POST fields.
|
||||||
|
if ( $creating && empty( $request['code'] ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_empty_coupon_code', sprintf( __( 'The coupon code cannot be empty.', 'woocommerce' ), 'code' ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle all writable props.
|
||||||
|
foreach ( $data_keys as $key ) {
|
||||||
|
$value = $request[ $key ];
|
||||||
|
|
||||||
|
if ( ! is_null( $value ) ) {
|
||||||
|
switch ( $key ) {
|
||||||
|
case 'code':
|
||||||
|
$coupon_code = wc_format_coupon_code( $value );
|
||||||
|
$id = $coupon->get_id() ? $coupon->get_id() : 0;
|
||||||
|
$id_from_code = wc_get_coupon_id_by_code( $coupon_code, $id );
|
||||||
|
|
||||||
|
if ( $id_from_code ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_coupon_code_already_exists', __( 'The coupon code already exists', 'woocommerce' ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$coupon->set_code( $coupon_code );
|
||||||
|
break;
|
||||||
|
case 'meta_data':
|
||||||
|
if ( is_array( $value ) ) {
|
||||||
|
foreach ( $value as $meta ) {
|
||||||
|
$coupon->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'description':
|
||||||
|
$coupon->set_description( wp_filter_post_kses( $value ) );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if ( is_callable( array( $coupon, "set_{$key}" ) ) ) {
|
||||||
|
$coupon->{"set_{$key}"}( $value );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters an object before it is inserted via the REST API.
|
||||||
|
*
|
||||||
|
* The dynamic portion of the hook name, `$this->post_type`,
|
||||||
|
* refers to the object type slug.
|
||||||
|
*
|
||||||
|
* @param WC_Data $coupon Object object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @param bool $creating If is creating a new object.
|
||||||
|
*/
|
||||||
|
return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $coupon, $request, $creating );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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' ),
|
||||||
|
),
|
||||||
|
'amount' => array(
|
||||||
|
'description' => __( 'The amount of discount. Should always be numeric, even if setting a percentage.', '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_created_gmt' => array(
|
||||||
|
'description' => __( 'The date the coupon was created, as GMT.', '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,
|
||||||
|
),
|
||||||
|
'date_modified_gmt' => array(
|
||||||
|
'description' => __( 'The date the coupon was last modified, as GMT.', 'woocommerce' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'discount_type' => array(
|
||||||
|
'description' => __( 'Determines the type of discount that will be applied.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'fixed_cart',
|
||||||
|
'enum' => array_keys( wc_get_coupon_types() ),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'description' => array(
|
||||||
|
'description' => __( 'Coupon description.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'date_expires' => array(
|
||||||
|
'description' => __( "The date the coupon expires, in the site's timezone.", 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'date_expires_gmt' => array(
|
||||||
|
'description' => __( 'The date the coupon expires, as GMT.', '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' => __( 'If true, the coupon can only be used individually. Other applied coupons will be removed from the cart.', 'woocommerce' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'product_ids' => array(
|
||||||
|
'description' => __( 'List of product IDs the coupon can be used on.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'excluded_product_ids' => array(
|
||||||
|
'description' => __( 'List of product IDs the coupon cannot be used on.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'usage_limit' => array(
|
||||||
|
'description' => __( 'How many times the coupon can be used in total.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'usage_limit_per_user' => array(
|
||||||
|
'description' => __( 'How many times the coupon can be used 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' => __( 'If true and if the free shipping method requires a coupon, this coupon will enable free shipping.', 'woocommerce' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'product_categories' => array(
|
||||||
|
'description' => __( 'List of category IDs the coupon applies to.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'excluded_product_categories' => array(
|
||||||
|
'description' => __( 'List of category IDs the coupon does not apply to.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'exclude_sale_items' => array(
|
||||||
|
'description' => __( 'If true, this coupon will not be applied to items that have sale prices.', 'woocommerce' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'minimum_amount' => array(
|
||||||
|
'description' => __( 'Minimum order amount that needs to be in the cart before coupon applies.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'maximum_amount' => array(
|
||||||
|
'description' => __( 'Maximum order amount allowed when using the coupon.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'email_restrictions' => array(
|
||||||
|
'description' => __( 'List of email addresses that can use this coupon.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'used_by' => array(
|
||||||
|
'description' => __( 'List of user IDs (or guest email addresses) that have used the coupon.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'meta_data' => array(
|
||||||
|
'description' => __( 'Meta data.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Meta ID.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'key' => array(
|
||||||
|
'description' => __( 'Meta key.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'value' => array(
|
||||||
|
'description' => __( 'Meta value.', 'woocommerce' ),
|
||||||
|
'type' => 'mixed',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query args.
|
||||||
|
*
|
||||||
|
* @param array $args Query args.
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function query_args( $args, $request ) {
|
||||||
|
if ( ! empty( $request['code'] ) ) {
|
||||||
|
$id = wc_get_coupon_id_by_code( $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 ) {
|
||||||
|
$coupon = new WC_Coupon( (int) $post->ID );
|
||||||
|
$_data = $coupon->get_data();
|
||||||
|
|
||||||
|
$format_decimal = array( 'amount', 'minimum_amount', 'maximum_amount' );
|
||||||
|
$format_date = array( 'date_created', 'date_modified' );
|
||||||
|
$format_date_utc = array( 'date_expires' );
|
||||||
|
$format_null = array( 'usage_limit', 'usage_limit_per_user' );
|
||||||
|
|
||||||
|
// Format decimal values.
|
||||||
|
foreach ( $format_decimal as $key ) {
|
||||||
|
$_data[ $key ] = wc_format_decimal( $_data[ $key ], 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format date values.
|
||||||
|
foreach ( $format_date as $key ) {
|
||||||
|
$_data[ $key ] = $_data[ $key ] ? wc_rest_prepare_date_response( $_data[ $key ], false ) : null;
|
||||||
|
}
|
||||||
|
foreach ( $format_date_utc as $key ) {
|
||||||
|
$_data[ $key ] = $_data[ $key ] ? wc_rest_prepare_date_response( $_data[ $key ] ) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format null values.
|
||||||
|
foreach ( $format_null as $key ) {
|
||||||
|
$_data[ $key ] = $_data[ $key ] ? $_data[ $key ] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'id' => $_data['id'],
|
||||||
|
'code' => $_data['code'],
|
||||||
|
'date_created' => $_data['date_created'],
|
||||||
|
'date_modified' => $_data['date_modified'],
|
||||||
|
'discount_type' => $_data['discount_type'],
|
||||||
|
'description' => $_data['description'],
|
||||||
|
'amount' => $_data['amount'],
|
||||||
|
'expiry_date' => $_data['date_expires'],
|
||||||
|
'usage_count' => $_data['usage_count'],
|
||||||
|
'individual_use' => $_data['individual_use'],
|
||||||
|
'product_ids' => $_data['product_ids'],
|
||||||
|
'exclude_product_ids' => $_data['excluded_product_ids'],
|
||||||
|
'usage_limit' => $_data['usage_limit'],
|
||||||
|
'usage_limit_per_user' => $_data['usage_limit_per_user'],
|
||||||
|
'limit_usage_to_x_items' => $_data['limit_usage_to_x_items'],
|
||||||
|
'free_shipping' => $_data['free_shipping'],
|
||||||
|
'product_categories' => $_data['product_categories'],
|
||||||
|
'excluded_product_categories' => $_data['excluded_product_categories'],
|
||||||
|
'exclude_sale_items' => $_data['exclude_sale_items'],
|
||||||
|
'minimum_amount' => $_data['minimum_amount'],
|
||||||
|
'maximum_amount' => $_data['maximum_amount'],
|
||||||
|
'email_restrictions' => $_data['email_restrictions'],
|
||||||
|
'used_by' => $_data['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 );
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
$response->add_links( $this->prepare_links( $post, $request ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only return writable props from schema.
|
||||||
|
*
|
||||||
|
* @param array $schema Schema array.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function filter_writable_props( $schema ) {
|
||||||
|
return empty( $schema['readonly'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ) {
|
||||||
|
$id = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
|
||||||
|
$coupon = new WC_Coupon( $id );
|
||||||
|
$schema = $this->get_item_schema();
|
||||||
|
$data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) );
|
||||||
|
|
||||||
|
// Update to schema to make compatible with CRUD schema.
|
||||||
|
if ( $request['exclude_product_ids'] ) {
|
||||||
|
$request['excluded_product_ids'] = $request['exclude_product_ids'];
|
||||||
|
}
|
||||||
|
if ( $request['expiry_date'] ) {
|
||||||
|
$request['date_expires'] = $request['expiry_date'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate required POST fields.
|
||||||
|
if ( 'POST' === $request->get_method() && 0 === $coupon->get_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 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle all writable props.
|
||||||
|
foreach ( $data_keys as $key ) {
|
||||||
|
$value = $request[ $key ];
|
||||||
|
|
||||||
|
if ( ! is_null( $value ) ) {
|
||||||
|
switch ( $key ) {
|
||||||
|
case 'code':
|
||||||
|
$coupon_code = wc_format_coupon_code( $value );
|
||||||
|
$id = $coupon->get_id() ? $coupon->get_id() : 0;
|
||||||
|
$id_from_code = wc_get_coupon_id_by_code( $coupon_code, $id );
|
||||||
|
|
||||||
|
if ( $id_from_code ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_coupon_code_already_exists', __( 'The coupon code already exists', 'woocommerce' ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$coupon->set_code( $coupon_code );
|
||||||
|
break;
|
||||||
|
case 'description':
|
||||||
|
$coupon->set_description( wp_filter_post_kses( $value ) );
|
||||||
|
break;
|
||||||
|
case 'expiry_date':
|
||||||
|
$coupon->set_date_expires( $value );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if ( is_callable( array( $coupon, "set_{$key}" ) ) ) {
|
||||||
|
$coupon->{"set_{$key}"}( $value );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 WC_Coupon $coupon The coupon object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
*/
|
||||||
|
return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $coupon, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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'] ) ) {
|
||||||
|
/* translators: %s: post type */
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$coupon_id = $this->save_coupon( $request );
|
||||||
|
if ( is_wp_error( $coupon_id ) ) {
|
||||||
|
return $coupon_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = get_post( $coupon_id );
|
||||||
|
$this->update_additional_fields_for_object( $post, $request );
|
||||||
|
|
||||||
|
$this->add_post_meta_fields( $post, $request );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires after a single item is created or updated via the REST API.
|
||||||
|
*
|
||||||
|
* @param WP_Post $post 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a single coupon.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function update_item( $request ) {
|
||||||
|
try {
|
||||||
|
$post_id = (int) $request['id'];
|
||||||
|
|
||||||
|
if ( empty( $post_id ) || get_post_type( $post_id ) !== $this->post_type ) {
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$coupon_id = $this->save_coupon( $request );
|
||||||
|
if ( is_wp_error( $coupon_id ) ) {
|
||||||
|
return $coupon_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = get_post( $coupon_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 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 );
|
||||||
|
|
||||||
|
} catch ( Exception $e ) {
|
||||||
|
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,252 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Customer Downloads controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /customers/<customer_id>/downloads endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Customers controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Customer_Downloads_V1_Controller extends WC_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(
|
||||||
|
'args' => array(
|
||||||
|
'customer_id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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 does not exist.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! wc_rest_check_user_permissions( 'read', $customer->get_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'];
|
||||||
|
|
||||||
|
// Remove "product_name" since it's new in 3.0.
|
||||||
|
unset( $data['product_name'] );
|
||||||
|
|
||||||
|
$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' => __( 'Number of downloads remaining.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'access_expires' => array(
|
||||||
|
'description' => __( "The date when download access expires, in the site's timezone.", 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'file' => array(
|
||||||
|
'description' => __( 'File details.', 'woocommerce' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'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,924 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Customers controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /customers endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Customers controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Customers_V1_Controller extends WC_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,
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'New user email address.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
'username' => array(
|
||||||
|
'required' => 'no' === get_option( 'woocommerce_registration_generate_username', 'yes' ),
|
||||||
|
'description' => __( 'New user username.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
'password' => array(
|
||||||
|
'required' => 'no' === get_option( 'woocommerce_registration_generate_password', 'no' ),
|
||||||
|
'description' => __( 'New user password.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
) ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||||
|
'args' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
'reassign' => array(
|
||||||
|
'default' => 0,
|
||||||
|
'type' => 'integer',
|
||||||
|
'description' => __( 'ID to reassign posts to.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'batch_items' ),
|
||||||
|
'permission_callback' => array( $this, 'batch_items_permissions_check' ),
|
||||||
|
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_batch_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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given request has access batch create, update and delete items.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
public function batch_items_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_user_permissions( 'batch' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate 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 pagination 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 ) {
|
||||||
|
try {
|
||||||
|
if ( ! empty( $request['id'] ) ) {
|
||||||
|
throw new WC_REST_Exception( 'woocommerce_rest_customer_exists', __( 'Cannot create existing resource.', 'woocommerce' ), 400 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the username.
|
||||||
|
$request['username'] = ! empty( $request['username'] ) ? $request['username'] : '';
|
||||||
|
|
||||||
|
// Sets the password.
|
||||||
|
$request['password'] = ! empty( $request['password'] ) ? $request['password'] : '';
|
||||||
|
|
||||||
|
// Create customer.
|
||||||
|
$customer = new WC_Customer;
|
||||||
|
$customer->set_username( $request['username'] );
|
||||||
|
$customer->set_password( $request['password'] );
|
||||||
|
$customer->set_email( $request['email'] );
|
||||||
|
$this->update_customer_meta_fields( $customer, $request );
|
||||||
|
$customer->save();
|
||||||
|
|
||||||
|
if ( ! $customer->get_id() ) {
|
||||||
|
throw new WC_REST_Exception( 'woocommerce_rest_cannot_create', __( 'This resource cannot be created.', 'woocommerce' ), 400 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_data = get_userdata( $customer->get_id() );
|
||||||
|
$this->update_additional_fields_for_object( $user_data, $request );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires after a customer is created or updated via the REST API.
|
||||||
|
*
|
||||||
|
* @param WP_User $user_data 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', $user_data, $request, true );
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $user_data, $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->get_id() ) ) );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
} catch ( Exception $e ) {
|
||||||
|
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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'];
|
||||||
|
$user_data = get_userdata( $id );
|
||||||
|
|
||||||
|
if ( empty( $id ) || empty( $user_data->ID ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$customer = $this->prepare_item_for_response( $user_data, $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 ) {
|
||||||
|
try {
|
||||||
|
$id = (int) $request['id'];
|
||||||
|
$customer = new WC_Customer( $id );
|
||||||
|
|
||||||
|
if ( ! $customer->get_id() ) {
|
||||||
|
throw new WC_REST_Exception( 'woocommerce_rest_invalid_id', __( 'Invalid resource ID.', 'woocommerce' ), 400 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $request['email'] ) && email_exists( $request['email'] ) && $request['email'] !== $customer->get_email() ) {
|
||||||
|
throw new WC_REST_Exception( 'woocommerce_rest_customer_invalid_email', __( 'Email address is invalid.', 'woocommerce' ), 400 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $request['username'] ) && $request['username'] !== $customer->get_username() ) {
|
||||||
|
throw new WC_REST_Exception( 'woocommerce_rest_customer_invalid_argument', __( "Username isn't editable.", 'woocommerce' ), 400 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customer email.
|
||||||
|
if ( isset( $request['email'] ) ) {
|
||||||
|
$customer->set_email( sanitize_email( $request['email'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customer password.
|
||||||
|
if ( isset( $request['password'] ) ) {
|
||||||
|
$customer->set_password( $request['password'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->update_customer_meta_fields( $customer, $request );
|
||||||
|
$customer->save();
|
||||||
|
|
||||||
|
$user_data = get_userdata( $customer->get_id() );
|
||||||
|
$this->update_additional_fields_for_object( $user_data, $request );
|
||||||
|
|
||||||
|
if ( ! is_user_member_of_blog( $user_data->ID ) ) {
|
||||||
|
$user_data->add_role( 'customer' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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', $user_data, $request, false );
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $user_data, $request );
|
||||||
|
$response = rest_ensure_response( $response );
|
||||||
|
return $response;
|
||||||
|
} catch ( Exception $e ) {
|
||||||
|
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_data = get_userdata( $id );
|
||||||
|
if ( ! $user_data ) {
|
||||||
|
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( $user_data, $request );
|
||||||
|
|
||||||
|
/** Include admin customer functions to get access to wp_delete_user() */
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/user.php';
|
||||||
|
|
||||||
|
$customer = new WC_Customer( $id );
|
||||||
|
|
||||||
|
if ( ! is_null( $reassign ) ) {
|
||||||
|
$result = $customer->delete_and_reassign( $reassign );
|
||||||
|
} else {
|
||||||
|
$result = $customer->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 $user_data User 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', $user_data, $response, $request );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a single customer output for response.
|
||||||
|
*
|
||||||
|
* @param WP_User $user_data User object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response $response Response data.
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $user_data, $request ) {
|
||||||
|
$customer = new WC_Customer( $user_data->ID );
|
||||||
|
$_data = $customer->get_data();
|
||||||
|
$last_order = wc_get_customer_last_order( $customer->get_id() );
|
||||||
|
$format_date = array( 'date_created', 'date_modified' );
|
||||||
|
|
||||||
|
// Format date values.
|
||||||
|
foreach ( $format_date as $key ) {
|
||||||
|
$_data[ $key ] = $_data[ $key ] ? wc_rest_prepare_date_response( $_data[ $key ] ) : null; // v1 API used UTC.
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'id' => $_data['id'],
|
||||||
|
'date_created' => $_data['date_created'],
|
||||||
|
'date_modified' => $_data['date_modified'],
|
||||||
|
'email' => $_data['email'],
|
||||||
|
'first_name' => $_data['first_name'],
|
||||||
|
'last_name' => $_data['last_name'],
|
||||||
|
'username' => $_data['username'],
|
||||||
|
'last_order' => array(
|
||||||
|
'id' => is_object( $last_order ) ? $last_order->get_id() : null,
|
||||||
|
'date' => is_object( $last_order ) ? wc_rest_prepare_date_response( $last_order->get_date_created() ) : null, // v1 API used UTC.
|
||||||
|
),
|
||||||
|
'orders_count' => $customer->get_order_count(),
|
||||||
|
'total_spent' => $customer->get_total_spent(),
|
||||||
|
'avatar_url' => $customer->get_avatar_url(),
|
||||||
|
'billing' => $_data['billing'],
|
||||||
|
'shipping' => $_data['shipping'],
|
||||||
|
);
|
||||||
|
|
||||||
|
$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( $user_data ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter customer data returned from the REST API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Response $response The response object.
|
||||||
|
* @param WP_User $user_data User object used to create response.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_customer', $response, $user_data, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update customer meta fields.
|
||||||
|
*
|
||||||
|
* @param WC_Customer $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'] ) ) {
|
||||||
|
$customer->set_first_name( wc_clean( $request['first_name'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customer last name.
|
||||||
|
if ( isset( $request['last_name'] ) ) {
|
||||||
|
$customer->set_last_name( wc_clean( $request['last_name'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customer billing address.
|
||||||
|
if ( isset( $request['billing'] ) ) {
|
||||||
|
foreach ( array_keys( $schema['properties']['billing']['properties'] ) as $field ) {
|
||||||
|
if ( isset( $request['billing'][ $field ] ) && is_callable( array( $customer, "set_billing_{$field}" ) ) ) {
|
||||||
|
$customer->{"set_billing_{$field}"}( $request['billing'][ $field ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customer shipping address.
|
||||||
|
if ( isset( $request['shipping'] ) ) {
|
||||||
|
foreach ( array_keys( $schema['properties']['shipping']['properties'] ) as $field ) {
|
||||||
|
if ( isset( $request['shipping'][ $field ] ) && is_callable( array( $customer, "set_shipping_{$field}" ) ) ) {
|
||||||
|
$customer->{"set_shipping_{$field}"}( $request['shipping'][ $field ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, as GMT.', 'woocommerce' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_modified' => array(
|
||||||
|
'description' => __( 'The date the customer was last modified, as GMT.', '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' => __( 'The date of the customer last order, as GMT.', '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' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'avatar_url' => array(
|
||||||
|
'description' => __( 'Avatar URL.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'billing' => 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' => 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',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['include'] = array(
|
||||||
|
'description' => __( 'Limit result set to specific IDs.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'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,439 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Order Notes controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /orders/<order_id>/notes endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Order Notes controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Order_Notes_V1_Controller extends WC_REST_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'orders/(?P<order_id>[\d]+)/notes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post type.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $post_type = 'shop_order';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the routes for order notes.
|
||||||
|
*/
|
||||||
|
public function register_routes() {
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||||
|
'args' => array(
|
||||||
|
'order_id' => array(
|
||||||
|
'description' => __( 'The order ID.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Order note content.', 'woocommerce' ),
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
) ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||||
|
'args' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'order_id' => array(
|
||||||
|
'description' => __( 'The order ID.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'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( $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 create order notes.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
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 a order note.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|boolean
|
||||||
|
*/
|
||||||
|
public function get_item_permissions_check( $request ) {
|
||||||
|
$order = wc_get_order( (int) $request['order_id'] );
|
||||||
|
|
||||||
|
if ( $order && ! wc_rest_check_post_permissions( $this->post_type, 'read', $order->get_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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
public function delete_item_permissions_check( $request ) {
|
||||||
|
$order = wc_get_order( (int) $request['order_id'] );
|
||||||
|
|
||||||
|
if ( $order && ! wc_rest_check_post_permissions( $this->post_type, 'delete', $order->get_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|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$order = wc_get_order( (int) $request['order_id'] );
|
||||||
|
|
||||||
|
if ( ! $order || $this->post_type !== $order->get_type() ) {
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid order ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = array(
|
||||||
|
'post_id' => $order->get_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'] ) ) {
|
||||||
|
/* translators: %s: post type */
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$order = wc_get_order( (int) $request['order_id'] );
|
||||||
|
|
||||||
|
if ( ! $order || $this->post_type !== $order->get_type() ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_order_invalid_id', __( 'Invalid order ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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->get_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 = wc_get_order( (int) $request['order_id'] );
|
||||||
|
|
||||||
|
if ( ! $order || $this->post_type !== $order->get_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->get_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 = wc_get_order( (int) $request['order_id'] );
|
||||||
|
|
||||||
|
if ( ! $order || $this->post_type !== $order->get_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->get_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 = wc_delete_order_note( $note->comment_ID );
|
||||||
|
|
||||||
|
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' => (int) $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' => 'order_note',
|
||||||
|
'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 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,530 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Order Refunds controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /orders/<order_id>/refunds endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 2.6.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Order Refunds controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Orders_V1_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Order_Refunds_V1_Controller extends WC_REST_Orders_V1_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(
|
||||||
|
'args' => array(
|
||||||
|
'order_id' => array(
|
||||||
|
'description' => __( 'The order ID.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
'args' => array(
|
||||||
|
'order_id' => array(
|
||||||
|
'description' => __( 'The order ID.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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' => true,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'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_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $post, $request ) {
|
||||||
|
$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 || $refund->get_parent_id() !== $order->get_id() ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_invalid_order_refund_id', __( 'Invalid order refund ID.', 'woocommerce' ), 404 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$dp = is_null( $request['dp'] ) ? wc_get_price_decimals() : absint( $request['dp'] );
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'id' => $refund->get_id(),
|
||||||
|
'date_created' => wc_rest_prepare_date_response( $refund->get_date_created() ),
|
||||||
|
'amount' => wc_format_decimal( $refund->get_amount(), $dp ),
|
||||||
|
'reason' => $refund->get_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 = $item->get_product_id();
|
||||||
|
$variation_id = $item->get_variation_id();
|
||||||
|
$product_sku = $product->get_sku();
|
||||||
|
}
|
||||||
|
|
||||||
|
$item_meta = array();
|
||||||
|
|
||||||
|
$hideprefix = 'true' === $request['all_item_meta'] ? null : '_';
|
||||||
|
|
||||||
|
foreach ( $item->get_formatted_meta_data( $hideprefix, true ) as $meta_key => $formatted_meta ) {
|
||||||
|
$item_meta[] = array(
|
||||||
|
'key' => $formatted_meta->key,
|
||||||
|
'label' => $formatted_meta->display_key,
|
||||||
|
'value' => wc_clean( $formatted_meta->display_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, $request ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return array Links for the given order refund.
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $refund, $request ) {
|
||||||
|
$order_id = $refund->get_parent_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, $refund->get_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 Request args.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function query_args( $args, $request ) {
|
||||||
|
$args['post_status'] = array_keys( wc_get_order_statuses() );
|
||||||
|
$args['post_parent__in'] = array( absint( $request['order_id'] ) );
|
||||||
|
|
||||||
|
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'] ) ) {
|
||||||
|
/* translators: %s: post type */
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the refund.
|
||||||
|
$refund = wc_create_refund( array(
|
||||||
|
'order_id' => $order_data->ID,
|
||||||
|
'amount' => $request['amount'],
|
||||||
|
'reason' => empty( $request['reason'] ) ? null : $request['reason'],
|
||||||
|
'refund_payment' => is_bool( $request['api_refund'] ) ? $request['api_refund'] : true,
|
||||||
|
'restock_items' => true,
|
||||||
|
) );
|
||||||
|
|
||||||
|
if ( is_wp_error( $refund ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', $refund->get_error_message(), 500 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $refund ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_create_order_refund', __( 'Cannot create order refund, please try again.', 'woocommerce' ), 500 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = get_post( $refund->get_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 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' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Item ID.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => __( 'Product name.', 'woocommerce' ),
|
||||||
|
'type' => 'mixed',
|
||||||
|
'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' => 'mixed',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'variation_id' => array(
|
||||||
|
'description' => __( 'Variation ID, if applicable.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'quantity' => array(
|
||||||
|
'description' => __( 'Quantity ordered.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'tax_class' => array(
|
||||||
|
'description' => __( 'Tax class of product.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'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' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotal_tax' => array(
|
||||||
|
'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'total' => array(
|
||||||
|
'description' => __( 'Line total (after discounts).', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'total_tax' => array(
|
||||||
|
'description' => __( 'Line total tax (after discounts).', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'taxes' => array(
|
||||||
|
'description' => __( 'Line taxes.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'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,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'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' => 'mixed',
|
||||||
|
'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' => wc_get_price_decimals(),
|
||||||
|
'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,240 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Product Attribute Terms controller
|
||||||
|
*
|
||||||
|
* Handles requests to the products/attributes/<attribute_id>/terms endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Product Attribute Terms controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Terms_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Product_Attribute_Terms_V1_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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the routes for terms.
|
||||||
|
*/
|
||||||
|
public function register_routes() {
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
|
||||||
|
'args' => array(
|
||||||
|
'attribute_id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the attribute of the terms.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Name for the resource.', 'woocommerce' ),
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
) ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
));
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||||
|
'args' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'attribute_id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the attribute of the terms.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
|
||||||
|
'args' => array(
|
||||||
|
'attribute_id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the attribute of the terms.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'batch_items' ),
|
||||||
|
'permission_callback' => array( $this, 'batch_items_permissions_check' ),
|
||||||
|
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_batch_schema' ),
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a single product attribute term output for response.
|
||||||
|
*
|
||||||
|
* @param WP_Term $item Term object.
|
||||||
|
* @param WP_REST_Request $request
|
||||||
|
* @return WP_REST_Response $response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $item, $request ) {
|
||||||
|
// Get term order.
|
||||||
|
$menu_order = get_term_meta( $item->term_id, 'order_' . $this->taxonomy, true );
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'id' => (int) $item->term_id,
|
||||||
|
'name' => $item->name,
|
||||||
|
'slug' => $item->slug,
|
||||||
|
'description' => $item->description,
|
||||||
|
'menu_order' => (int) $menu_order,
|
||||||
|
'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_term_meta( $id, 'order_' . $this->taxonomy, $request['menu_order'] );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'description' => array(
|
||||||
|
'description' => __( 'HTML description of the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'arg_options' => array(
|
||||||
|
'sanitize_callback' => 'wp_filter_post_kses',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'menu_order' => array(
|
||||||
|
'description' => __( 'Menu order, used to custom sort the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'count' => array(
|
||||||
|
'description' => __( 'Number of published products for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,586 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Product Attributes controller
|
||||||
|
*
|
||||||
|
* Handles requests to the products/attributes endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Product Attributes controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Product_Attributes_V1_Controller extends WC_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(
|
||||||
|
'description' => __( 'Name for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
) ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
));
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||||
|
'args' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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' => true,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'batch_items' ),
|
||||||
|
'permission_callback' => array( $this, 'batch_items_permissions_check' ),
|
||||||
|
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_batch_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 does not 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 does not 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 does not exist.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'attributes', '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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given request has access batch create, update and delete items.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
public function batch_items_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'attributes', 'batch' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate this 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;
|
||||||
|
|
||||||
|
$id = wc_create_attribute( array(
|
||||||
|
'name' => $request['name'],
|
||||||
|
'slug' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ),
|
||||||
|
'type' => ! empty( $request['type'] ) ? $request['type'] : 'select',
|
||||||
|
'order_by' => ! empty( $request['order_by'] ) ? $request['order_by'] : 'menu_order',
|
||||||
|
'has_archives' => true === $request['has_archives'],
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Checks for errors.
|
||||||
|
if ( is_wp_error( $id ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_create', $id->get_error_message(), 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, 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 ) );
|
||||||
|
|
||||||
|
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 ) {
|
||||||
|
$attribute = $this->get_attribute( (int) $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'];
|
||||||
|
$edited = wc_update_attribute( $id, array(
|
||||||
|
'name' => $request['name'],
|
||||||
|
'slug' => wc_sanitize_taxonomy_name( stripslashes( $request['slug'] ) ),
|
||||||
|
'type' => $request['type'],
|
||||||
|
'order_by' => $request['order_by'],
|
||||||
|
'has_archives' => $request['has_archives'],
|
||||||
|
) );
|
||||||
|
|
||||||
|
// Checks for errors.
|
||||||
|
if ( is_wp_error( $edited ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_edit', $edited->get_error_message(), 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 );
|
||||||
|
|
||||||
|
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 ) {
|
||||||
|
$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( (int) $request['id'] );
|
||||||
|
|
||||||
|
if ( is_wp_error( $attribute ) ) {
|
||||||
|
return $attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $attribute, $request );
|
||||||
|
|
||||||
|
$deleted = wc_delete_attribute( $attribute->attribute_id );
|
||||||
|
|
||||||
|
if ( false === $deleted ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 );
|
||||||
|
|
||||||
|
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.
|
||||||
|
* @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 string
|
||||||
|
*/
|
||||||
|
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 does not exist.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate attribute slug.
|
||||||
|
*
|
||||||
|
* @deprecated 3.2.0
|
||||||
|
* @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). Shorten it, please.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
|
||||||
|
} elseif ( 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. Change it, please.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
|
||||||
|
} elseif ( $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. Change it, please.', 'woocommerce' ), $slug ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule to flush rewrite rules.
|
||||||
|
*
|
||||||
|
* @deprecated 3.2.0
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
protected function flush_rewrite_rules() {
|
||||||
|
wp_schedule_single_event( time(), 'woocommerce_flush_rewrite_rules' );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Product Categories controller
|
||||||
|
*
|
||||||
|
* Handles requests to the products/categories endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Product Categories controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Terms_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Product_Categories_V1_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 WP_Term $item Term object.
|
||||||
|
* @param WP_REST_Request $request Request instance.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $item, $request ) {
|
||||||
|
// Get category display type.
|
||||||
|
$display_type = get_term_meta( $item->term_id, 'display_type', true );
|
||||||
|
|
||||||
|
// Get category order.
|
||||||
|
$menu_order = get_term_meta( $item->term_id, 'order', true );
|
||||||
|
|
||||||
|
$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' => null,
|
||||||
|
'menu_order' => (int) $menu_order,
|
||||||
|
'count' => (int) $item->count,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get category image.
|
||||||
|
$image_id = get_term_meta( $item->term_id, 'thumbnail_id', true );
|
||||||
|
if ( $image_id ) {
|
||||||
|
$attachment = get_post( $image_id );
|
||||||
|
|
||||||
|
$data['image'] = array(
|
||||||
|
'id' => (int) $image_id,
|
||||||
|
'date_created' => wc_rest_prepare_date_response( $attachment->post_date_gmt ),
|
||||||
|
'date_modified' => wc_rest_prepare_date_response( $attachment->post_modified_gmt ),
|
||||||
|
'src' => wp_get_attachment_url( $image_id ),
|
||||||
|
'title' => get_the_title( $attachment ),
|
||||||
|
'alt' => get_post_meta( $image_id, '_wp_attachment_image_alt', 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 );
|
||||||
|
|
||||||
|
$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 Term object.
|
||||||
|
* @param WP_REST_Request $request Request instance.
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
protected function update_term_meta_fields( $term, $request ) {
|
||||||
|
$id = (int) $term->term_id;
|
||||||
|
|
||||||
|
if ( isset( $request['display'] ) ) {
|
||||||
|
update_term_meta( $id, 'display_type', 'default' === $request['display'] ? '' : $request['display'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $request['menu_order'] ) ) {
|
||||||
|
update_term_meta( $id, 'order', $request['menu_order'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $request['image'] ) ) {
|
||||||
|
if ( empty( $request['image']['id'] ) && ! empty( $request['image']['src'] ) ) {
|
||||||
|
$upload = wc_rest_upload_image_from_url( esc_url_raw( $request['image']['src'] ) );
|
||||||
|
|
||||||
|
if ( is_wp_error( $upload ) ) {
|
||||||
|
return $upload;
|
||||||
|
}
|
||||||
|
|
||||||
|
$image_id = wc_rest_set_uploaded_image_as_attachment( $upload );
|
||||||
|
} else {
|
||||||
|
$image_id = isset( $request['image']['id'] ) ? absint( $request['image']['id'] ) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if image_id is a valid image attachment before updating the term meta.
|
||||||
|
if ( $image_id && wp_attachment_is_image( $image_id ) ) {
|
||||||
|
update_term_meta( $id, 'thumbnail_id', $image_id );
|
||||||
|
|
||||||
|
// Set the image alt.
|
||||||
|
if ( ! empty( $request['image']['alt'] ) ) {
|
||||||
|
update_post_meta( $image_id, '_wp_attachment_image_alt', wc_clean( $request['image']['alt'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the image title.
|
||||||
|
if ( ! empty( $request['image']['title'] ) ) {
|
||||||
|
wp_update_post( array(
|
||||||
|
'ID' => $image_id,
|
||||||
|
'post_title' => wc_clean( $request['image']['title'] ),
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete_term_meta( $id, 'thumbnail_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 data.', 'woocommerce' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Image ID.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'date_created' => array(
|
||||||
|
'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_modified' => array(
|
||||||
|
'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'src' => array(
|
||||||
|
'description' => __( 'Image URL.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'uri',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'title' => array(
|
||||||
|
'description' => __( 'Image name.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'alt' => array(
|
||||||
|
'description' => __( 'Image alternative text.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'menu_order' => array(
|
||||||
|
'description' => __( 'Menu order, used to custom sort the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'count' => array(
|
||||||
|
'description' => __( 'Number of published products for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,578 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Product Reviews Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /products/<product_id>/reviews.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Product Reviews Controller Class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Product_Reviews_V1_Controller extends WC_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(
|
||||||
|
'args' => array(
|
||||||
|
'product_id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the variation.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
'review' => array(
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Review content.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Name of the reviewer.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
'email' => array(
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Email of the reviewer.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
) ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||||
|
'args' => array(
|
||||||
|
'product_id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'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 product review.
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given request has access to create a new product review.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|boolean
|
||||||
|
*/
|
||||||
|
public function create_item_permissions_check( $request ) {
|
||||||
|
$post = get_post( (int) $request['product_id'] );
|
||||||
|
if ( $post && ! wc_rest_check_post_permissions( 'product', 'create', $post->ID ) ) {
|
||||||
|
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 update a product review.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|boolean
|
||||||
|
*/
|
||||||
|
public function update_item_permissions_check( $request ) {
|
||||||
|
$post = get_post( (int) $request['product_id'] );
|
||||||
|
if ( $post && ! wc_rest_check_post_permissions( 'product', 'edit', $post->ID ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you cannot edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given request has access to delete a product review.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|boolean
|
||||||
|
*/
|
||||||
|
public function delete_item_permissions_check( $request ) {
|
||||||
|
$post = get_post( (int) $request['product_id'] );
|
||||||
|
if ( $post && ! wc_rest_check_post_permissions( 'product', 'delete', $post->ID ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you cannot delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reviews from a product.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request
|
||||||
|
*
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$product_id = (int) $request['product_id'];
|
||||||
|
|
||||||
|
if ( 'product' !== get_post_type( $product_id ) ) {
|
||||||
|
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_id = (int) $request['product_id'];
|
||||||
|
|
||||||
|
if ( 'product' !== get_post_type( $product_id ) ) {
|
||||||
|
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 ) !== $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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a product review.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function create_item( $request ) {
|
||||||
|
$product_id = (int) $request['product_id'];
|
||||||
|
|
||||||
|
if ( 'product' !== get_post_type( $product_id ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_product_invalid_id', __( 'Invalid product ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$prepared_review = $this->prepare_item_for_database( $request );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a product review (comment) before it is inserted via the REST API.
|
||||||
|
*
|
||||||
|
* Allows modification of the comment right before it is inserted via `wp_insert_comment`.
|
||||||
|
*
|
||||||
|
* @param array $prepared_review The prepared comment data for `wp_insert_comment`.
|
||||||
|
* @param WP_REST_Request $request Request used to insert the comment.
|
||||||
|
*/
|
||||||
|
$prepared_review = apply_filters( 'rest_pre_insert_product_review', $prepared_review, $request );
|
||||||
|
|
||||||
|
$product_review_id = wp_insert_comment( $prepared_review );
|
||||||
|
if ( ! $product_review_id ) {
|
||||||
|
return new WP_Error( 'rest_product_review_failed_create', __( 'Creating product review failed.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
update_comment_meta( $product_review_id, 'rating', ( ! empty( $request['rating'] ) ? $request['rating'] : '0' ) );
|
||||||
|
|
||||||
|
$product_review = get_comment( $product_review_id );
|
||||||
|
$this->update_additional_fields_for_object( $product_review, $request );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires after a single item is created or updated via the REST API.
|
||||||
|
*
|
||||||
|
* @param WP_Comment $product_review Inserted object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @param boolean $creating True when creating item, false when updating.
|
||||||
|
*/
|
||||||
|
do_action( "woocommerce_rest_insert_product_review", $product_review, $request, true );
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $product_review, $request );
|
||||||
|
$response = rest_ensure_response( $response );
|
||||||
|
$response->set_status( 201 );
|
||||||
|
$base = str_replace( '(?P<product_id>[\d]+)', $product_id, $this->rest_base );
|
||||||
|
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $product_review_id ) ) );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a single product review.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function update_item( $request ) {
|
||||||
|
$product_review_id = (int) $request['id'];
|
||||||
|
$product_id = (int) $request['product_id'];
|
||||||
|
|
||||||
|
if ( 'product' !== get_post_type( $product_id ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_product_invalid_id', __( 'Invalid product ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$review = get_comment( $product_review_id );
|
||||||
|
|
||||||
|
if ( empty( $product_review_id ) || empty( $review ) || intval( $review->comment_post_ID ) !== $product_id ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_product_review_invalid_id', __( 'Invalid resource ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$prepared_review = $this->prepare_item_for_database( $request );
|
||||||
|
|
||||||
|
$updated = wp_update_comment( $prepared_review );
|
||||||
|
if ( 0 === $updated ) {
|
||||||
|
return new WP_Error( 'rest_product_review_failed_edit', __( 'Updating product review failed.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $request['rating'] ) ) {
|
||||||
|
update_comment_meta( $product_review_id, 'rating', $request['rating'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$product_review = get_comment( $product_review_id );
|
||||||
|
$this->update_additional_fields_for_object( $product_review, $request );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires after a single item is created or updated via the REST API.
|
||||||
|
*
|
||||||
|
* @param WP_Comment $comment Inserted object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @param boolean $creating True when creating item, false when updating.
|
||||||
|
*/
|
||||||
|
do_action( "woocommerce_rest_insert_product_review", $product_review, $request, true );
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $product_review, $request );
|
||||||
|
|
||||||
|
return rest_ensure_response( $response );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a product review.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function delete_item( $request ) {
|
||||||
|
$product_review_id = absint( is_array( $request['id'] ) ? $request['id']['id'] : $request['id'] );
|
||||||
|
$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
|
||||||
|
|
||||||
|
$product_review = get_comment( $product_review_id );
|
||||||
|
if ( empty( $product_review_id ) || empty( $product_review->comment_ID ) || empty( $product_review->comment_post_ID ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_product_review_invalid_id', __( 'Invalid product review ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter whether a product review is trashable.
|
||||||
|
*
|
||||||
|
* Return false to disable trash support for the product review.
|
||||||
|
*
|
||||||
|
* @param boolean $supports_trash Whether the object supports trashing.
|
||||||
|
* @param WP_Post $product_review The object being considered for trashing support.
|
||||||
|
*/
|
||||||
|
$supports_trash = apply_filters( 'rest_product_review_trashable', ( EMPTY_TRASH_DAYS > 0 ), $product_review );
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $product_review, $request );
|
||||||
|
|
||||||
|
if ( $force ) {
|
||||||
|
$result = wp_delete_comment( $product_review_id, true );
|
||||||
|
} else {
|
||||||
|
if ( ! $supports_trash ) {
|
||||||
|
return new WP_Error( 'rest_trash_not_supported', __( 'The product review does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 'trash' === $product_review->comment_approved ) {
|
||||||
|
return new WP_Error( 'rest_already_trashed', __( 'The comment has already been trashed.', 'woocommerce' ), array( 'status' => 410 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = wp_trash_comment( $product_review->comment_ID );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $result ) {
|
||||||
|
return new WP_Error( 'rest_cannot_delete', __( 'The product review cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires after a product review is deleted via the REST API.
|
||||||
|
*
|
||||||
|
* @param object $product_review The deleted item.
|
||||||
|
* @param WP_REST_Response $response The response data.
|
||||||
|
* @param WP_REST_Request $request The request sent to the API.
|
||||||
|
*/
|
||||||
|
do_action( 'rest_delete_product_review', $product_review, $response, $request );
|
||||||
|
|
||||||
|
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 ),
|
||||||
|
'name' => $review->comment_author,
|
||||||
|
'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 a single product review to be inserted into the database.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return array|WP_Error $prepared_review
|
||||||
|
*/
|
||||||
|
protected function prepare_item_for_database( $request ) {
|
||||||
|
$prepared_review = array( 'comment_approved' => 1, 'comment_type' => 'review' );
|
||||||
|
|
||||||
|
if ( isset( $request['id'] ) ) {
|
||||||
|
$prepared_review['comment_ID'] = (int) $request['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $request['review'] ) ) {
|
||||||
|
$prepared_review['comment_content'] = $request['review'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $request['product_id'] ) ) {
|
||||||
|
$prepared_review['comment_post_ID'] = (int) $request['product_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $request['name'] ) ) {
|
||||||
|
$prepared_review['comment_author'] = $request['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $request['email'] ) ) {
|
||||||
|
$prepared_review['comment_author_email'] = $request['email'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $request['date_created'] ) ) {
|
||||||
|
$prepared_review['comment_date'] = $request['date_created'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $request['date_created_gmt'] ) ) {
|
||||||
|
$prepared_review['comment_date_gmt'] = $request['date_created_gmt'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply_filters( 'rest_preprocess_product_review', $prepared_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', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'review' => array(
|
||||||
|
'description' => __( 'The content of the review.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'date_created' => array(
|
||||||
|
'description' => __( "The date the review was created, in the site's timezone.", 'woocommerce' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'rating' => array(
|
||||||
|
'description' => __( 'Review rating (0 to 5).', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => __( 'Reviewer name.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'email' => array(
|
||||||
|
'description' => __( 'Reviewer email.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'verified' => array(
|
||||||
|
'description' => __( 'Shows if the reviewer bought the product or not.', 'woocommerce' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'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,134 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Product Shipping Classes controller
|
||||||
|
*
|
||||||
|
* Handles requests to the products/shipping_classes endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Product Shipping Classes controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Terms_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Product_Shipping_Classes_V1_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,
|
||||||
|
'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',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'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' ),
|
||||||
|
'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/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Product Tags controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Terms_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Product_Tags_V1_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' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,397 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports controller
|
||||||
|
*
|
||||||
|
* Handles requests to the reports/sales endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Report Sales controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Report_Sales_V1_Controller extends WC_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' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
),
|
||||||
|
'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(
|
||||||
|
/* translators: %s: date format */
|
||||||
|
'description' => sprintf( __( 'Return sales for a specific start date, the date need to be in the %s format.', 'woocommerce' ), 'YYYY-MM-DD' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date',
|
||||||
|
'validate_callback' => 'wc_rest_validate_reports_request_arg',
|
||||||
|
'sanitize_callback' => 'sanitize_text_field',
|
||||||
|
),
|
||||||
|
'date_max' => array(
|
||||||
|
/* translators: %s: date format */
|
||||||
|
'description' => sprintf( __( 'Return sales for a specific end date, the date need to be in the %s format.', 'woocommerce' ), 'YYYY-MM-DD' ),
|
||||||
|
'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/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Report Top Sellers controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Report_Sales_V1_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Report_Top_Sellers_V1_Controller extends WC_REST_Report_Sales_V1_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(
|
||||||
|
'name' => $product->get_name(),
|
||||||
|
'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(
|
||||||
|
'name' => $top_seller->name,
|
||||||
|
'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(
|
||||||
|
'name' => array(
|
||||||
|
'description' => __( 'Product name.', '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,184 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports controller
|
||||||
|
*
|
||||||
|
* Handles requests to the reports endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Reports_V1_Controller extends WC_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 reports list.
|
||||||
|
*
|
||||||
|
* @since 3.5.0
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function get_reports() {
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'slug' => 'sales',
|
||||||
|
'description' => __( 'List of sales reports.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'top_sellers',
|
||||||
|
'description' => __( 'List of top sellers products.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$data = array();
|
||||||
|
$reports = $this->get_reports();
|
||||||
|
|
||||||
|
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,364 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Tax Classes controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /taxes/classes endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Tax Classes controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Tax_Classes_V1_Controller extends WC_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(
|
||||||
|
'args' => array(
|
||||||
|
'slug' => array(
|
||||||
|
'description' => __( 'Unique slug for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'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 resources.', '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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
$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,709 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Taxes controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /taxes endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Taxes controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Taxes_V1_Controller extends WC_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(
|
||||||
|
'args' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'batch_items' ),
|
||||||
|
'permission_callback' => array( $this, 'batch_items_permissions_check' ),
|
||||||
|
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_batch_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 resources.', '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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
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 bool|WP_Error
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given request has access batch create, update and delete items.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
public function batch_items_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'settings', 'batch' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate 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['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 pagination 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 ) );
|
||||||
|
|
||||||
|
// Calculate 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take tax data from the request and return the updated or newly created rate.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @param stdClass|null $current Existing tax object.
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
protected function create_or_update_tax( $request, $current = null ) {
|
||||||
|
$id = absint( isset( $request['id'] ) ? $request['id'] : 0 );
|
||||||
|
$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 ) {
|
||||||
|
// Keys via API differ from the stored names returned by _get_tax_rate.
|
||||||
|
$key = 'tax_rate' === $field ? 'rate' : str_replace( 'tax_rate_', '', $field );
|
||||||
|
|
||||||
|
// Remove data that was not posted.
|
||||||
|
if ( ! isset( $request[ $key ] ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test new data against current data.
|
||||||
|
if ( $current && $current->$field === $request[ $key ] ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to data array.
|
||||||
|
switch ( $key ) {
|
||||||
|
case 'tax_rate_priority' :
|
||||||
|
case 'tax_rate_compound' :
|
||||||
|
case 'tax_rate_shipping' :
|
||||||
|
case 'tax_rate_order' :
|
||||||
|
$data[ $field ] = absint( $request[ $key ] );
|
||||||
|
break;
|
||||||
|
case 'tax_rate_class' :
|
||||||
|
$data[ $field ] = 'standard' !== $request['tax_rate_class'] ? $request['tax_rate_class'] : '';
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
$data[ $field ] = wc_clean( $request[ $key ] );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $id ) {
|
||||||
|
WC_Tax::_update_tax_rate( $id, $data );
|
||||||
|
} else {
|
||||||
|
$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'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return WC_Tax::_get_tax_rate( $id, OBJECT );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$tax = $this->create_or_update_tax( $request );
|
||||||
|
|
||||||
|
$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, $tax->tax_rate_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'];
|
||||||
|
$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->create_or_update_tax( $request, $tax_obj );
|
||||||
|
|
||||||
|
$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' => 'string',
|
||||||
|
'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' ), WC_Tax::get_tax_class_slugs() ),
|
||||||
|
'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();
|
||||||
|
$params['context']['default'] = 'view';
|
||||||
|
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$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' ), WC_Tax::get_tax_class_slugs() ),
|
||||||
|
'sanitize_callback' => 'sanitize_title',
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,314 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Webhooks controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /webhooks/<webhook_id>/deliveries endpoint.
|
||||||
|
*
|
||||||
|
* @author WooThemes
|
||||||
|
* @category API
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Webhook Deliveries controller class.
|
||||||
|
*
|
||||||
|
* @deprecated 3.3.0 Webhooks deliveries logs now uses logging system.
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Webhook_Deliveries_V1_Controller extends WC_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(
|
||||||
|
'args' => array(
|
||||||
|
'webhook_id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the webhook.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
'args' => array(
|
||||||
|
'webhook_id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the webhook.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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 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( 'webhooks', '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 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( 'webhooks', 'read' ) ) {
|
||||||
|
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|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$webhook = wc_get_webhook( (int) $request['webhook_id'] );
|
||||||
|
|
||||||
|
if ( empty( $webhook ) || is_null( $webhook ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_webhook_invalid_id', __( 'Invalid webhook ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$logs = array();
|
||||||
|
$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 = wc_get_webhook( (int) $request['webhook_id'] );
|
||||||
|
|
||||||
|
if ( empty( $webhook ) || is_null( $webhook ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_webhook_invalid_id', __( 'Invalid webhook ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$log = array();
|
||||||
|
|
||||||
|
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;
|
||||||
|
$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' => __( 'Request headers.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'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,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'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,763 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Webhooks controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /webhooks endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Webhooks controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/RestApi
|
||||||
|
* @extends WC_REST_Controller
|
||||||
|
*/
|
||||||
|
class WC_REST_Webhooks_V1_Controller extends WC_REST_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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Webhook topic.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
'delivery_url' => array(
|
||||||
|
'required' => true,
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Webhook delivery URL.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
) ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
|
||||||
|
'args' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
) );
|
||||||
|
|
||||||
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'batch_items' ),
|
||||||
|
'permission_callback' => array( $this, 'batch_items_permissions_check' ),
|
||||||
|
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_batch_schema' ),
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a given request has permission to read webhooks.
|
||||||
|
*
|
||||||
|
* @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( 'webhooks', '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 webhooks.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
public function create_item_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'webhooks', '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 webhook.
|
||||||
|
*
|
||||||
|
* @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( 'webhooks', '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 webhook.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
public function update_item_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'webhooks', '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 webhook.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
public function delete_item_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'webhooks', '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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given request has access batch create, update and delete items.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
*
|
||||||
|
* @return bool|WP_Error
|
||||||
|
*/
|
||||||
|
public function batch_items_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'webhooks', 'batch' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default REST API version.
|
||||||
|
*
|
||||||
|
* @since 3.0.0
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function get_default_api_version() {
|
||||||
|
return 'wp_api_v1';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all webhooks.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['status'] = 'all' === $request['status'] ? '' : $request['status'];
|
||||||
|
$args['include'] = implode( ',', $request['include'] );
|
||||||
|
$args['exclude'] = implode( ',', $request['exclude'] );
|
||||||
|
$args['limit'] = $request['per_page'];
|
||||||
|
$args['search'] = $request['search'];
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
|
||||||
|
if ( empty( $request['offset'] ) ) {
|
||||||
|
$args['offset'] = 1 < $request['page'] ? ( $request['page'] - 1 ) * $args['limit'] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter arguments, before passing to WC_Webhook_Data_Store->search_webhooks, when querying webhooks via the REST API.
|
||||||
|
*
|
||||||
|
* @param array $args Array of arguments for $wpdb->get_results().
|
||||||
|
* @param WP_REST_Request $request The current request.
|
||||||
|
*/
|
||||||
|
$prepared_args = apply_filters( 'woocommerce_rest_webhook_query', $args, $request );
|
||||||
|
unset( $prepared_args['page'] );
|
||||||
|
$prepared_args['paginate'] = true;
|
||||||
|
|
||||||
|
// Get the webhooks.
|
||||||
|
$webhooks = array();
|
||||||
|
$data_store = WC_Data_Store::load( 'webhook' );
|
||||||
|
$results = $data_store->search_webhooks( $prepared_args );
|
||||||
|
$webhook_ids = $results->webhooks;
|
||||||
|
|
||||||
|
foreach ( $webhook_ids as $webhook_id ) {
|
||||||
|
$data = $this->prepare_item_for_response( $webhook_id, $request );
|
||||||
|
$webhooks[] = $this->prepare_response_for_collection( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $webhooks );
|
||||||
|
$per_page = (int) $prepared_args['limit'];
|
||||||
|
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
|
||||||
|
$total_webhooks = $results->total;
|
||||||
|
$max_pages = $results->max_num_pages;
|
||||||
|
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||||
|
|
||||||
|
$response->header( 'X-WP-Total', $total_webhooks );
|
||||||
|
$response->header( 'X-WP-TotalPages', $max_pages );
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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'];
|
||||||
|
|
||||||
|
if ( empty( $id ) ) {
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->prepare_item_for_response( $id, $request );
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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'] ) ) {
|
||||||
|
/* translators: %s: post type */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$webhook = new WC_Webhook();
|
||||||
|
$webhook->set_name( $post->post_title );
|
||||||
|
$webhook->set_user_id( $post->post_author );
|
||||||
|
$webhook->set_status( 'publish' === $post->post_status ? 'active' : 'disabled' );
|
||||||
|
$webhook->set_topic( $request['topic'] );
|
||||||
|
$webhook->set_delivery_url( $request['delivery_url'] );
|
||||||
|
$webhook->set_secret( ! empty( $request['secret'] ) ? $request['secret'] : wp_generate_password( 50, true, true ) );
|
||||||
|
$webhook->set_api_version( $this->get_default_api_version() );
|
||||||
|
$webhook->save();
|
||||||
|
|
||||||
|
$this->update_additional_fields_for_object( $webhook, $request );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires after a single item is created or updated via the REST API.
|
||||||
|
*
|
||||||
|
* @param WC_Webhook $webhook Webhook data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @param bool $creating True when creating item, false when updating.
|
||||||
|
*/
|
||||||
|
do_action( "woocommerce_rest_insert_webhook_object", $webhook, $request, true );
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $webhook->get_id(), $request );
|
||||||
|
$response = rest_ensure_response( $response );
|
||||||
|
$response->set_status( 201 );
|
||||||
|
$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $webhook->get_id() ) ) );
|
||||||
|
|
||||||
|
// Send ping.
|
||||||
|
$webhook->deliver_ping();
|
||||||
|
|
||||||
|
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'];
|
||||||
|
$webhook = wc_get_webhook( $id );
|
||||||
|
|
||||||
|
if ( empty( $webhook ) || is_null( $webhook ) ) {
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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'] ) ) {
|
||||||
|
if ( wc_is_webhook_valid_status( strtolower( $request['status'] ) ) ) {
|
||||||
|
$webhook->set_status( $request['status'] );
|
||||||
|
} else {
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_status", __( 'Webhook status must be valid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = $this->prepare_item_for_database( $request );
|
||||||
|
if ( is_wp_error( $post ) ) {
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $post->post_title ) ) {
|
||||||
|
$webhook->set_name( $post->post_title );
|
||||||
|
}
|
||||||
|
|
||||||
|
$webhook->save();
|
||||||
|
|
||||||
|
$this->update_additional_fields_for_object( $webhook, $request );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires after a single item is created or updated via the REST API.
|
||||||
|
*
|
||||||
|
* @param WC_Webhook $webhook Webhook data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @param bool $creating True when creating item, false when updating.
|
||||||
|
*/
|
||||||
|
do_action( "woocommerce_rest_insert_webhook_object", $webhook, $request, false );
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $webhook->get_id(), $request );
|
||||||
|
|
||||||
|
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 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$webhook = wc_get_webhook( $id );
|
||||||
|
|
||||||
|
if ( empty( $webhook ) || is_null( $webhook ) ) {
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->set_param( 'context', 'edit' );
|
||||||
|
$response = $this->prepare_item_for_response( $webhook, $request );
|
||||||
|
$result = $webhook->delete( true );
|
||||||
|
|
||||||
|
if ( ! $result ) {
|
||||||
|
/* translators: %s: post type */
|
||||||
|
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 WC_Webhook $webhook 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_webhook_object", $webhook, $response, $request );
|
||||||
|
|
||||||
|
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 ) {
|
||||||
|
$data = new stdClass;
|
||||||
|
|
||||||
|
// Post ID.
|
||||||
|
if ( isset( $request['id'] ) ) {
|
||||||
|
$data->ID = absint( $request['id'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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' ) ) ); // @codingStandardsIgnoreLine
|
||||||
|
|
||||||
|
// Post author.
|
||||||
|
$data->post_author = get_current_user_id();
|
||||||
|
|
||||||
|
// Post password.
|
||||||
|
$data->post_password = 'webhook_' . wp_generate_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 int $id Webhook ID or object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response $response Response data.
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $id, $request ) {
|
||||||
|
$webhook = wc_get_webhook( $id );
|
||||||
|
|
||||||
|
if ( empty( $webhook ) || is_null( $webhook ) ) {
|
||||||
|
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'id' => $webhook->get_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_date_created() ),
|
||||||
|
'date_modified' => wc_rest_prepare_date_response( $webhook->get_date_modified() ),
|
||||||
|
);
|
||||||
|
|
||||||
|
$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( $webhook->get_id() ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param int $id Webhook ID.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $id ) {
|
||||||
|
$links = array(
|
||||||
|
'self' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $id ) ),
|
||||||
|
),
|
||||||
|
'collection' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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',
|
||||||
|
'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_keys( wc_get_webhook_statuses() ),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'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,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'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 a MD5 hash from the current user's ID|username if not provided.", 'woocommerce' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'edit' ),
|
||||||
|
),
|
||||||
|
'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['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',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['include'] = array(
|
||||||
|
'description' => __( 'Limit result set to specific ids.', 'woocommerce' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'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',
|
||||||
|
'title',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,484 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Admin Notes controller
|
||||||
|
*
|
||||||
|
* Handles requests to the admin notes endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Admin Notes controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_CRUD_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Admin_Notes_Controller extends WC_REST_CRUD_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'admin/notes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the routes for admin 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(),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/(?P<id>[\d-]+)',
|
||||||
|
array(
|
||||||
|
'args' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique ID for the resource.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( $this, 'get_item' ),
|
||||||
|
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'update_item' ),
|
||||||
|
'permission_callback' => array( $this, 'update_items_permissions_check' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single note.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_REST_Response|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_item( $request ) {
|
||||||
|
$note = WC_Admin_Notes::get_note( $request->get_param( 'id' ) );
|
||||||
|
|
||||||
|
if ( ! $note ) {
|
||||||
|
return new WP_Error(
|
||||||
|
'woocommerce_admin_notes_invalid_id',
|
||||||
|
__( 'Sorry, there is no resouce with that ID.', 'woocommerce-admin' ),
|
||||||
|
array( 'status' => 404 )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( is_wp_error( $note ) ) {
|
||||||
|
return $note;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $note->get_data();
|
||||||
|
$data = $this->prepare_item_for_response( $data, $request );
|
||||||
|
$data = $this->prepare_response_for_collection( $data );
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all notes.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_objects_query( $request );
|
||||||
|
|
||||||
|
$notes = WC_Admin_Notes::get_notes( 'edit', $query_args );
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
foreach ( (array) $notes as $note_obj ) {
|
||||||
|
$note = $this->prepare_item_for_response( $note_obj, $request );
|
||||||
|
$note = $this->prepare_response_for_collection( $note );
|
||||||
|
$data[] = $note;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
$response->header( 'X-WP-Total', WC_Admin_Notes::get_notes_count( $query_args['type'], $query_args['status'] ) );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare objects query.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_objects_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['type'] = isset( $request['type'] ) ? $request['type'] : array();
|
||||||
|
$args['status'] = isset( $request['status'] ) ? $request['status'] : array();
|
||||||
|
|
||||||
|
if ( 'date' === $args['orderby'] ) {
|
||||||
|
$args['orderby'] = 'date_created';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_admin_notes_object_query', $args, $request );
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a given request has permission to read a single note.
|
||||||
|
*
|
||||||
|
* @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( 'system_status', 'read' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce-admin' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a given request has permission to read 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_manager_permissions( 'system_status', 'read' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce-admin' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a single note.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_REST_Request|WP_Error
|
||||||
|
*/
|
||||||
|
public function update_item( $request ) {
|
||||||
|
$note = WC_Admin_Notes::get_note( $request->get_param( 'id' ) );
|
||||||
|
|
||||||
|
if ( ! $note ) {
|
||||||
|
return new WP_Error(
|
||||||
|
'woocommerce_admin_notes_invalid_id',
|
||||||
|
__( 'Sorry, there is no resouce with that ID.', 'woocommerce-admin' ),
|
||||||
|
array( 'status' => 404 )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Status is the only field that can be updated at the moment. We should also implement the "date reminder" setting.
|
||||||
|
$note_changed = false;
|
||||||
|
if ( ! is_null( $request->get_param( 'status' ) ) ) {
|
||||||
|
$note->set_status( $request->get_param( 'status' ) );
|
||||||
|
$note_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! is_null( $request->get_param( 'date_reminder' ) ) ) {
|
||||||
|
$note->set_date_reminder( $request->get_param( 'date_reminder' ) );
|
||||||
|
$note_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $note_changed ) {
|
||||||
|
$note->save();
|
||||||
|
}
|
||||||
|
return $this->get_item( $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure the current user has access to WRITE the settings APIs.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full data about the request.
|
||||||
|
* @return WP_Error|bool
|
||||||
|
*/
|
||||||
|
public function update_items_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you cannot edit this resource.', 'woocommerce-admin' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a path or query for serialization to the client.
|
||||||
|
*
|
||||||
|
* @param string $query The query, path, or URL to transform.
|
||||||
|
* @return string A fully formed URL.
|
||||||
|
*/
|
||||||
|
public function prepare_query_for_response( $query ) {
|
||||||
|
if ( 'https://' === substr( $query, 0, 8 ) ) {
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
if ( 'http://' === substr( $query, 0, 7 ) ) {
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
if ( '?' === substr( $query, 0, 1 ) ) {
|
||||||
|
return admin_url( 'admin.php' . $query );
|
||||||
|
}
|
||||||
|
|
||||||
|
return admin_url( $query );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a note object for serialization.
|
||||||
|
*
|
||||||
|
* @param array $data Note data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response $response Response data.
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $data, $request ) {
|
||||||
|
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||||
|
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||||
|
$data['date_created_gmt'] = wc_rest_prepare_date_response( $data['date_created'] );
|
||||||
|
$data['date_created'] = wc_rest_prepare_date_response( $data['date_created'], false );
|
||||||
|
$data['date_reminder_gmt'] = wc_rest_prepare_date_response( $data['date_reminder'] );
|
||||||
|
$data['date_reminder'] = wc_rest_prepare_date_response( $data['date_reminder'], false );
|
||||||
|
$data['title'] = stripslashes( $data['title'] );
|
||||||
|
$data['content'] = stripslashes( $data['content'] );
|
||||||
|
$data['is_snoozable'] = (bool) $data['is_snoozable'];
|
||||||
|
foreach ( (array) $data['actions'] as $key => $value ) {
|
||||||
|
$data['actions'][ $key ]->label = stripslashes( $data['actions'][ $key ]->label );
|
||||||
|
$data['actions'][ $key ]->url = $this->prepare_query_for_response( $data['actions'][ $key ]->query );
|
||||||
|
$data['actions'][ $key ]->status = stripslashes( $data['actions'][ $key ]->status );
|
||||||
|
}
|
||||||
|
$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/%d', $this->namespace, $this->rest_base, $data['id'] ) ),
|
||||||
|
),
|
||||||
|
'collection' => array(
|
||||||
|
'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* Filter a note returned from the API.
|
||||||
|
*
|
||||||
|
* Allows modification of the note data right before it is returned.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Response $response The response object.
|
||||||
|
* @param array $data The original note.
|
||||||
|
* @param WP_REST_Request $request Request used to generate the response.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_admin_note', $response, $data, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = array();
|
||||||
|
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'note_id',
|
||||||
|
'date',
|
||||||
|
'type',
|
||||||
|
'title',
|
||||||
|
'status',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['type'] = array(
|
||||||
|
'description' => __( 'Type of note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => WC_Admin_Note::get_allowed_types(),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['status'] = array(
|
||||||
|
'description' => __( 'Status of note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => WC_Admin_Note::get_allowed_statuses(),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the note's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'note',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'ID of the note record.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => __( 'Name of the note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'type' => array(
|
||||||
|
'description' => __( 'The type of the note (e.g. error, warning, etc.).', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'locale' => array(
|
||||||
|
'description' => __( 'Locale used for the note title and content.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'title' => array(
|
||||||
|
'description' => __( 'Title of the note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'content' => array(
|
||||||
|
'description' => __( 'Content of the note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'icon' => array(
|
||||||
|
'description' => __( 'Icon (gridicon) for the note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'content_data' => array(
|
||||||
|
'description' => __( 'Content data for the note. JSON string. Available for re-localization.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'status' => array(
|
||||||
|
'description' => __( 'The status of the note (e.g. unactioned, actioned).', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
),
|
||||||
|
'source' => array(
|
||||||
|
'description' => __( 'Source of the note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_created' => array(
|
||||||
|
'description' => __( 'Date the note was created.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_created_gmt' => array(
|
||||||
|
'description' => __( 'Date the note was created (GMT).', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_reminder' => array(
|
||||||
|
'description' => __( 'Date after which the user should be reminded of the note, if any.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true, // @todo Allow date_reminder to be updated.
|
||||||
|
),
|
||||||
|
'date_reminder_gmt' => array(
|
||||||
|
'description' => __( 'Date after which the user should be reminded of the note, if any (GMT).', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'is_snoozable' => array(
|
||||||
|
'description' => __( 'Whether or a user can request to be reminded about the note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'actions' => array(
|
||||||
|
'description' => __( 'An array of actions, if any, for the note.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Coupons Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /coupons/*
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coupons controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Coupons_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Coupons_Controller extends WC_REST_Coupons_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = parent::get_collection_params();
|
||||||
|
$params['search'] = array(
|
||||||
|
'description' => __( 'Limit results to coupons with codes matching a given string.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add coupon code searching to the WC API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_objects_query( $request ) {
|
||||||
|
$args = parent::prepare_objects_query( $request );
|
||||||
|
|
||||||
|
if ( ! empty( $request['search'] ) ) {
|
||||||
|
$args['search'] = $request['search'];
|
||||||
|
$args['s'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a collection of posts and add the code search option to WP_Query.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
add_filter( 'posts_where', array( __CLASS__, 'add_wp_query_search_code_filter' ), 10, 2 );
|
||||||
|
$response = parent::get_items( $request );
|
||||||
|
remove_filter( 'posts_where', array( __CLASS__, 'add_wp_query_search_code_filter' ), 10 );
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add code searching to the WP Query
|
||||||
|
*
|
||||||
|
* @param string $where Where clause used to search posts.
|
||||||
|
* @param object $wp_query WP_Query object.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function add_wp_query_search_code_filter( $where, $wp_query ) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$search = $wp_query->get( 'search' );
|
||||||
|
if ( $search ) {
|
||||||
|
$search = $wpdb->esc_like( $search );
|
||||||
|
$search = "'%" . $search . "%'";
|
||||||
|
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE ' . $search;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $where;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Customers Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /customers/*
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customers controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_Admin_REST_Reports_Customers_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Customers_Controller extends WC_Admin_REST_Reports_Customers_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'customers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = parent::prepare_reports_query( $request );
|
||||||
|
$args['customers'] = $request['include'];
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = parent::get_collection_params();
|
||||||
|
$params['include'] = $params['customers'];
|
||||||
|
unset( $params['customers'] );
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Data Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /data
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Data_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Data_Controller extends WC_REST_Data_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of data resources.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$response = parent::get_items( $request );
|
||||||
|
$response->data[] = $this->prepare_response_for_collection(
|
||||||
|
$this->prepare_item_for_response(
|
||||||
|
(object) array(
|
||||||
|
'slug' => 'download-ips',
|
||||||
|
'description' => __( 'An endpoint used for searching download logs for a specific IP address.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
$request
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Data countries controller.
|
||||||
|
*
|
||||||
|
* Handles requests to the /data/countries endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Data countries controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Data_Countries_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Data_Countries_Controller extends WC_REST_Data_Countries_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Data Download IP Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /data/download-ips
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data Download IP controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Data_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Data_Download_Ips_Controller extends WC_REST_Data_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'data/download-ips';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register routes.
|
||||||
|
*
|
||||||
|
* @since 3.5.0
|
||||||
|
*/
|
||||||
|
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' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the download IPs matching the passed parameters.
|
||||||
|
*
|
||||||
|
* @since 3.5.0
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
if ( isset( $request['match'] ) ) {
|
||||||
|
$downloads = $wpdb->get_results(
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT DISTINCT( user_ip_address ) FROM {$wpdb->prefix}wc_download_log
|
||||||
|
WHERE user_ip_address LIKE %s
|
||||||
|
LIMIT 10",
|
||||||
|
$request['match'] . '%'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return new WP_Error( 'woocommerce_rest_data_download_ips_invalid_request', __( 'Invalid request. Please pass the match parameter.', 'woocommerce-admin' ), array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
if ( ! empty( $downloads ) ) {
|
||||||
|
foreach ( $downloads as $download ) {
|
||||||
|
$response = $this->prepare_item_for_response( $download, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $response );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the data object for response.
|
||||||
|
*
|
||||||
|
* @since 3.5.0
|
||||||
|
* @param object $item Data object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response $response Response data.
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $item, $request ) {
|
||||||
|
$data = $this->add_additional_fields_to_object( $item, $request );
|
||||||
|
$data = $this->filter_response_by_context( $data, 'view' );
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
|
||||||
|
$response->add_links( $this->prepare_links( $item ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list returned from the API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Response $response The response object.
|
||||||
|
* @param array $item The original item.
|
||||||
|
* @param WP_REST_Request $request Request used to generate the response.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_data_download_ip', $response, $item, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param object $item Data object.
|
||||||
|
* @return array Links for the given object.
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $item ) {
|
||||||
|
$links = array(
|
||||||
|
'collection' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = array();
|
||||||
|
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||||
|
$params['match'] = array(
|
||||||
|
'description' => __( 'A partial IP address can be passed and matching results will be returned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'data_download_ips',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'user_ip_address' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'IP address.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,546 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Leaderboards Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /leaderboards
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leaderboards controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Data_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Leaderboards_Controller extends WC_REST_Data_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'leaderboards';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register routes.
|
||||||
|
*/
|
||||||
|
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 . '/allowed',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( $this, 'get_allowed_items' ),
|
||||||
|
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_allowed_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data for the coupons leaderboard.
|
||||||
|
*
|
||||||
|
* @param int $per_page Number of rows.
|
||||||
|
* @param string $after Items after date.
|
||||||
|
* @param string $before Items before date.
|
||||||
|
* @param string $persisted_query URL query string.
|
||||||
|
*/
|
||||||
|
public function get_coupons_leaderboard( $per_page, $after, $before, $persisted_query ) {
|
||||||
|
$coupons_data_store = new WC_Admin_Reports_Coupons_Data_Store();
|
||||||
|
$coupons_data = $per_page > 0 ? $coupons_data_store->get_data(
|
||||||
|
array(
|
||||||
|
'orderby' => 'orders_count',
|
||||||
|
'order' => 'desc',
|
||||||
|
'after' => $after,
|
||||||
|
'before' => $before,
|
||||||
|
'per_page' => $per_page,
|
||||||
|
'extended_info' => true,
|
||||||
|
)
|
||||||
|
)->data : array();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ( $coupons_data as $coupon ) {
|
||||||
|
$url_query = wp_parse_args(
|
||||||
|
array(
|
||||||
|
'filter' => 'single_coupon',
|
||||||
|
'coupons' => $coupon['coupon_id'],
|
||||||
|
),
|
||||||
|
$persisted_query
|
||||||
|
);
|
||||||
|
$coupon_url = wc_admin_url( 'analytics/coupons', $url_query );
|
||||||
|
$coupon_code = isset( $coupon['extended_info'] ) && isset( $coupon['extended_info']['code'] ) ? $coupon['extended_info']['code'] : '';
|
||||||
|
$rows[] = array(
|
||||||
|
array(
|
||||||
|
'display' => "<a href='{$coupon_url}'>{$coupon_code}</a>",
|
||||||
|
'value' => $coupon_code,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'display' => wc_admin_number_format( $coupon['orders_count'] ),
|
||||||
|
'value' => $coupon['orders_count'],
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'display' => wc_price( $coupon['amount'] ),
|
||||||
|
'value' => $coupon['amount'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'id' => 'coupons',
|
||||||
|
'label' => __( 'Top Coupons - Number of Orders', 'woocommerce-admin' ),
|
||||||
|
'headers' => array(
|
||||||
|
array(
|
||||||
|
'label' => __( 'Coupon Code', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'label' => __( 'Orders', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'label' => __( 'Amount Discounted', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'rows' => $rows,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data for the categories leaderboard.
|
||||||
|
*
|
||||||
|
* @param int $per_page Number of rows.
|
||||||
|
* @param string $after Items after date.
|
||||||
|
* @param string $before Items before date.
|
||||||
|
* @param string $persisted_query URL query string.
|
||||||
|
*/
|
||||||
|
public function get_categories_leaderboard( $per_page, $after, $before, $persisted_query ) {
|
||||||
|
$categories_data_store = new WC_Admin_Reports_Categories_Data_Store();
|
||||||
|
$categories_data = $per_page > 0 ? $categories_data_store->get_data(
|
||||||
|
array(
|
||||||
|
'orderby' => 'items_sold',
|
||||||
|
'order' => 'desc',
|
||||||
|
'after' => $after,
|
||||||
|
'before' => $before,
|
||||||
|
'per_page' => $per_page,
|
||||||
|
'extended_info' => true,
|
||||||
|
)
|
||||||
|
)->data : array();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ( $categories_data as $category ) {
|
||||||
|
$url_query = wp_parse_args(
|
||||||
|
array(
|
||||||
|
'filter' => 'single_category',
|
||||||
|
'categories' => $category['category_id'],
|
||||||
|
),
|
||||||
|
$persisted_query
|
||||||
|
);
|
||||||
|
$category_url = wc_admin_url( 'analytics/categories', $url_query );
|
||||||
|
$category_name = isset( $category['extended_info'] ) && isset( $category['extended_info']['name'] ) ? $category['extended_info']['name'] : '';
|
||||||
|
$rows[] = array(
|
||||||
|
array(
|
||||||
|
'display' => "<a href='{$category_url}'>{$category_name}</a>",
|
||||||
|
'value' => $category_name,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'display' => wc_admin_number_format( $category['items_sold'] ),
|
||||||
|
'value' => $category['items_sold'],
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'display' => wc_price( $category['net_revenue'] ),
|
||||||
|
'value' => $category['net_revenue'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'id' => 'categories',
|
||||||
|
'label' => __( 'Top Categories - Items Sold', 'woocommerce-admin' ),
|
||||||
|
'headers' => array(
|
||||||
|
array(
|
||||||
|
'label' => __( 'Category', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'label' => __( 'Items Sold', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'label' => __( 'Net Revenue', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'rows' => $rows,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data for the customers leaderboard.
|
||||||
|
*
|
||||||
|
* @param int $per_page Number of rows.
|
||||||
|
* @param string $after Items after date.
|
||||||
|
* @param string $before Items before date.
|
||||||
|
* @param string $persisted_query URL query string.
|
||||||
|
*/
|
||||||
|
public function get_customers_leaderboard( $per_page, $after, $before, $persisted_query ) {
|
||||||
|
$customers_data_store = new WC_Admin_Reports_Customers_Data_Store();
|
||||||
|
$customers_data = $per_page > 0 ? $customers_data_store->get_data(
|
||||||
|
array(
|
||||||
|
'orderby' => 'total_spend',
|
||||||
|
'order' => 'desc',
|
||||||
|
'order_after' => $after,
|
||||||
|
'order_before' => $before,
|
||||||
|
'per_page' => $per_page,
|
||||||
|
)
|
||||||
|
)->data : array();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ( $customers_data as $customer ) {
|
||||||
|
$url_query = wp_parse_args(
|
||||||
|
array(
|
||||||
|
'filter' => 'single_customer',
|
||||||
|
'customers' => $customer['id'],
|
||||||
|
),
|
||||||
|
$persisted_query
|
||||||
|
);
|
||||||
|
$customer_url = wc_admin_url( 'analytics/customers', $url_query );
|
||||||
|
$rows[] = array(
|
||||||
|
array(
|
||||||
|
'display' => "<a href='{$customer_url}'>{$customer['name']}</a>",
|
||||||
|
'value' => $customer['name'],
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'display' => wc_admin_number_format( $customer['orders_count'] ),
|
||||||
|
'value' => $customer['orders_count'],
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'display' => wc_price( $customer['total_spend'] ),
|
||||||
|
'value' => $customer['total_spend'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'id' => 'customers',
|
||||||
|
'label' => __( 'Top Customers - Total Spend', 'woocommerce-admin' ),
|
||||||
|
'headers' => array(
|
||||||
|
array(
|
||||||
|
'label' => __( 'Customer Name', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'label' => __( 'Orders', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'label' => __( 'Total Spend', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'rows' => $rows,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data for the products leaderboard.
|
||||||
|
*
|
||||||
|
* @param int $per_page Number of rows.
|
||||||
|
* @param string $after Items after date.
|
||||||
|
* @param string $before Items before date.
|
||||||
|
* @param string $persisted_query URL query string.
|
||||||
|
*/
|
||||||
|
public function get_products_leaderboard( $per_page, $after, $before, $persisted_query ) {
|
||||||
|
$products_data_store = new WC_Admin_Reports_Products_Data_Store();
|
||||||
|
$products_data = $per_page > 0 ? $products_data_store->get_data(
|
||||||
|
array(
|
||||||
|
'orderby' => 'items_sold',
|
||||||
|
'order' => 'desc',
|
||||||
|
'after' => $after,
|
||||||
|
'before' => $before,
|
||||||
|
'per_page' => $per_page,
|
||||||
|
'extended_info' => true,
|
||||||
|
)
|
||||||
|
)->data : array();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ( $products_data as $product ) {
|
||||||
|
$url_query = wp_parse_args(
|
||||||
|
array(
|
||||||
|
'filter' => 'single_product',
|
||||||
|
'products' => $product['product_id'],
|
||||||
|
),
|
||||||
|
$persisted_query
|
||||||
|
);
|
||||||
|
$product_url = wc_admin_url( 'analytics/products', $url_query );
|
||||||
|
$product_name = isset( $product['extended_info'] ) && isset( $product['extended_info']['name'] ) ? $product['extended_info']['name'] : '';
|
||||||
|
$rows[] = array(
|
||||||
|
array(
|
||||||
|
'display' => "<a href='{$product_url}'>{$product_name}</a>",
|
||||||
|
'value' => $product_name,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'display' => wc_admin_number_format( $product['items_sold'] ),
|
||||||
|
'value' => $product['items_sold'],
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'display' => wc_price( $product['net_revenue'] ),
|
||||||
|
'value' => $product['net_revenue'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'id' => 'products',
|
||||||
|
'label' => __( 'Top Products - Items Sold', 'woocommerce-admin' ),
|
||||||
|
'headers' => array(
|
||||||
|
array(
|
||||||
|
'label' => __( 'Product', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'label' => __( 'Items Sold', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'label' => __( 'Net Revenue', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'rows' => $rows,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of all leaderboards.
|
||||||
|
*
|
||||||
|
* @param int $per_page Number of rows.
|
||||||
|
* @param string $after Items after date.
|
||||||
|
* @param string $before Items before date.
|
||||||
|
* @param string $persisted_query URL query string.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_leaderboards( $per_page, $after, $before, $persisted_query ) {
|
||||||
|
$leaderboards = array(
|
||||||
|
$this->get_customers_leaderboard( $per_page, $after, $before, $persisted_query ),
|
||||||
|
$this->get_coupons_leaderboard( $per_page, $after, $before, $persisted_query ),
|
||||||
|
$this->get_categories_leaderboard( $per_page, $after, $before, $persisted_query ),
|
||||||
|
$this->get_products_leaderboard( $per_page, $after, $before, $persisted_query ),
|
||||||
|
);
|
||||||
|
|
||||||
|
return apply_filters( 'woocommerce_leaderboards', $leaderboards, $per_page, $after, $before, $persisted_query );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all leaderboards.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$persisted_query = json_decode( $request['persisted_query'], true );
|
||||||
|
$leaderboards = $this->get_leaderboards( $request['per_page'], $request['after'], $request['before'], $persisted_query );
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
if ( ! empty( $leaderboards ) ) {
|
||||||
|
foreach ( $leaderboards as $leaderboard ) {
|
||||||
|
$response = $this->prepare_item_for_response( $leaderboard, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $response );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of allowed leaderboards.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_allowed_items( $request ) {
|
||||||
|
$leaderboards = $this->get_leaderboards( 0, null, null, null );
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
foreach ( $leaderboards as $leaderboard ) {
|
||||||
|
$data[] = (object) array(
|
||||||
|
'id' => $leaderboard['id'],
|
||||||
|
'label' => $leaderboard['label'],
|
||||||
|
'headers' => $leaderboard['headers'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$objects = array();
|
||||||
|
foreach ( $data as $item ) {
|
||||||
|
$prepared = $this->prepare_item_for_response( $item, $request );
|
||||||
|
$objects[] = $this->prepare_response_for_collection( $prepared );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $objects );
|
||||||
|
$response->header( 'X-WP-Total', count( $data ) );
|
||||||
|
$response->header( 'X-WP-TotalPages', 1 );
|
||||||
|
|
||||||
|
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the data object for response.
|
||||||
|
*
|
||||||
|
* @param object $item Data object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response $response Response data.
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $item, $request ) {
|
||||||
|
$data = $this->add_additional_fields_to_object( $item, $request );
|
||||||
|
$data = $this->filter_response_by_context( $data, 'view' );
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list returned from the API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Response $response The response object.
|
||||||
|
* @param array $item The original item.
|
||||||
|
* @param WP_REST_Request $request Request used to generate the response.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_leaderboard', $response, $item, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = array();
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 5,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 20,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['persisted_query'] = array(
|
||||||
|
'description' => __( 'URL query to persist across links.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'leaderboard',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Leaderboard ID.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'label' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Displayed title for the leaderboard.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'headers' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => __( 'Table headers.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'properties' => array(
|
||||||
|
'label' => array(
|
||||||
|
'description' => __( 'Table column header.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'rows' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => __( 'Table rows.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'properties' => array(
|
||||||
|
'display' => array(
|
||||||
|
'description' => __( 'Table cell display.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'value' => array(
|
||||||
|
'description' => __( 'Table cell value.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get schema for the list of allowed leaderboards.
|
||||||
|
*
|
||||||
|
* @return array $schema
|
||||||
|
*/
|
||||||
|
public function get_public_allowed_item_schema() {
|
||||||
|
$schema = $this->get_public_item_schema();
|
||||||
|
unset( $schema['properties']['rows'] );
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,264 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Onboarding Levels Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /onboarding/levels
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Onboarding Levels controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Data_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Onboarding_Levels_Controller extends WC_REST_Data_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc-admin/v1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'onboarding/levels';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register routes.
|
||||||
|
*/
|
||||||
|
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' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of all levels and child tasks.
|
||||||
|
*
|
||||||
|
* @todo Status values below should pull from the database task status once implemented.
|
||||||
|
*/
|
||||||
|
public function get_levels() {
|
||||||
|
$levels = array(
|
||||||
|
'account' => array(
|
||||||
|
'tasks' => array(
|
||||||
|
'create_account' => array(
|
||||||
|
'label' => __( 'Create an account', 'woocommerce-admin' ),
|
||||||
|
'description' => __( 'Speed up & secure your store', 'woocommerce-admin' ),
|
||||||
|
'illustration' => '',
|
||||||
|
'status' => 'visible',
|
||||||
|
'is_required' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'storefront' => array(
|
||||||
|
'tasks' => array(
|
||||||
|
'add_products' => array(
|
||||||
|
'label' => __( 'Add your products', 'woocommerce-admin' ),
|
||||||
|
'description' => __( 'Bring your store to life', 'woocommerce-admin' ),
|
||||||
|
'illustration' => '',
|
||||||
|
'status' => 'visible',
|
||||||
|
'is_required' => true,
|
||||||
|
),
|
||||||
|
'customize_appearance' => array(
|
||||||
|
'label' => __( 'Customize Appearance', 'woocommerce-admin' ),
|
||||||
|
'description' => __( 'Ensure your store is on-brand', 'woocommerce-admin' ),
|
||||||
|
'illustration' => '',
|
||||||
|
'status' => 'visible',
|
||||||
|
'is_required' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'checkout' => array(
|
||||||
|
'id' => 'checkout',
|
||||||
|
'tasks' => array(
|
||||||
|
'configure_shipping' => array(
|
||||||
|
'label' => __( 'Configure shipping', 'woocommerce-admin' ),
|
||||||
|
'description' => __( 'Set up prices and destinations', 'woocommerce-admin' ),
|
||||||
|
'illustration' => '',
|
||||||
|
'status' => 'visible',
|
||||||
|
'is_required' => true,
|
||||||
|
),
|
||||||
|
'configure_taxes' => array(
|
||||||
|
'label' => __( 'Configure taxes', 'woocommerce-admin' ),
|
||||||
|
'description' => __( 'Set up sales tax rates', 'woocommerce-admin' ),
|
||||||
|
'illustration' => '',
|
||||||
|
'status' => 'visible',
|
||||||
|
'is_required' => false,
|
||||||
|
),
|
||||||
|
'configure_payments' => array(
|
||||||
|
'label' => __( 'Configure payments', 'woocommerce-admin' ),
|
||||||
|
'description' => __( 'Choose payment providers', 'woocommerce-admin' ),
|
||||||
|
'illustration' => '',
|
||||||
|
'status' => 'visible',
|
||||||
|
'is_required' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return apply_filters( 'woocommerce_onboarding_levels', $levels );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all level items and child tasks.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$levels = $this->get_levels();
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
if ( ! empty( $levels ) ) {
|
||||||
|
foreach ( $levels as $id => $level ) {
|
||||||
|
$level = $this->convert_to_non_associative( $level, $id );
|
||||||
|
$response = $this->prepare_item_for_response( $level, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $response );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the data object for response.
|
||||||
|
*
|
||||||
|
* @param object $item Data object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response $response Response data.
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $item, $request ) {
|
||||||
|
$data = $this->add_additional_fields_to_object( $item, $request );
|
||||||
|
$data = $this->filter_response_by_context( $data, 'view' );
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
|
||||||
|
$response->add_links( $this->prepare_links( $item ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list returned from the API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Response $response The response object.
|
||||||
|
* @param array $item The original item.
|
||||||
|
* @param WP_REST_Request $request Request used to generate the response.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_onboarding_level', $response, $item, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the associative levels and tasks to non-associative for JSON use.
|
||||||
|
*
|
||||||
|
* @param array $item Level.
|
||||||
|
* @param string $id Level ID.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function convert_to_non_associative( $item, $id ) {
|
||||||
|
$item = array( 'id' => $id ) + $item;
|
||||||
|
|
||||||
|
$tasks = array();
|
||||||
|
foreach ( $item['tasks'] as $key => $task ) {
|
||||||
|
$tasks[] = array( 'id' => $key ) + $task;
|
||||||
|
}
|
||||||
|
$item['tasks'] = $tasks;
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param object $item Data object.
|
||||||
|
* @return array Links for the given object.
|
||||||
|
* @todo Check to make sure this generates a valid URL after #1897.
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $item ) {
|
||||||
|
$links = array(
|
||||||
|
'collection' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/onboarding/tasks?level=%s', $this->namespace, $item['id'] ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'onboarding_level',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Level ID.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'tasks' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => __( 'Array of tasks under the level.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Task ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'label' => array(
|
||||||
|
'description' => __( 'Task label.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'description' => array(
|
||||||
|
'description' => __( 'Task description.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'illustration' => array(
|
||||||
|
'description' => __( 'URL for illustration used.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'status' => array(
|
||||||
|
'description' => __( 'Task status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'visible', 'hidden', 'in-progress', 'skipped', 'completed' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,289 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Onboarding Plugins Controller
|
||||||
|
*
|
||||||
|
* Handles requests to install and activate depedent plugins.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Onboarding Plugins Controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Data_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Onboarding_Plugins_Controller extends WC_REST_Data_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc-admin/v1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'onboarding/plugins';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register routes.
|
||||||
|
*/
|
||||||
|
public function register_routes() {
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/install',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'install_plugin' ),
|
||||||
|
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/activate',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'activate_plugin' ),
|
||||||
|
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/connect-jetpack',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( $this, 'connect_jetpack' ),
|
||||||
|
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_connect_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given request has access to manage plugins.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|boolean
|
||||||
|
*/
|
||||||
|
public function update_item_permissions_check( $request ) {
|
||||||
|
if ( ! current_user_can( 'install_plugins' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot manage plugins.', 'woocommerce-admin' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of plugins that can be installed & activated via the endpoints.
|
||||||
|
*/
|
||||||
|
public function get_allowed_plugins() {
|
||||||
|
return apply_filters(
|
||||||
|
'woocommerce_onboarding_plugins_whitelist',
|
||||||
|
array(
|
||||||
|
'jetpack' => 'jetpack/jetpack.php',
|
||||||
|
'woocommerce-services' => 'woocommerce-services/woocommerce-services.php',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs the requested plugin.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return array Plugin Status
|
||||||
|
*/
|
||||||
|
public function install_plugin( $request ) {
|
||||||
|
$allowed_plugins = $this->get_allowed_plugins();
|
||||||
|
$plugin = sanitize_title_with_dashes( $request['plugin'] );
|
||||||
|
if ( ! in_array( $plugin, array_keys( $allowed_plugins ), true ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_invalid_plugin', __( 'Invalid plugin.', 'woocommerce-admin' ), 404 );
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||||
|
|
||||||
|
$slug = $plugin;
|
||||||
|
$path = $allowed_plugins[ $slug ];
|
||||||
|
$installed_plugins = get_plugins();
|
||||||
|
|
||||||
|
if ( in_array( $path, array_keys( $installed_plugins ), true ) ) {
|
||||||
|
return( array(
|
||||||
|
'slug' => $slug,
|
||||||
|
'name' => $installed_plugins[ $path ]['Name'],
|
||||||
|
'status' => 'success',
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
include_once ABSPATH . '/wp-admin/includes/admin.php';
|
||||||
|
include_once ABSPATH . '/wp-admin/includes/plugin-install.php';
|
||||||
|
include_once ABSPATH . '/wp-admin/includes/plugin.php';
|
||||||
|
include_once ABSPATH . '/wp-admin/includes/class-wp-upgrader.php';
|
||||||
|
include_once ABSPATH . '/wp-admin/includes/class-plugin-upgrader.php';
|
||||||
|
|
||||||
|
$api = plugins_api(
|
||||||
|
'plugin_information',
|
||||||
|
array(
|
||||||
|
'slug' => sanitize_key( $slug ),
|
||||||
|
'fields' => array(
|
||||||
|
'sections' => false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( is_wp_error( $api ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_plugin_install', __( 'The requested plugin could not be installed.', 'woocommerce-admin' ), 500 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
|
||||||
|
$result = $upgrader->install( $api->download_link );
|
||||||
|
|
||||||
|
if ( is_wp_error( $result ) || is_null( $result ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_plugin_install', __( 'The requested plugin could not be installed.', 'woocommerce-admin' ), 500 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'slug' => $slug,
|
||||||
|
'name' => $api->name,
|
||||||
|
'status' => 'success',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate the requested plugin.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return array Plugin Status
|
||||||
|
*/
|
||||||
|
public function activate_plugin( $request ) {
|
||||||
|
$allowed_plugins = $this->get_allowed_plugins();
|
||||||
|
$plugin = sanitize_title_with_dashes( $request['plugin'] );
|
||||||
|
if ( ! in_array( $plugin, array_keys( $allowed_plugins ), true ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_invalid_plugin', __( 'Invalid plugin.', 'woocommerce-admin' ), 404 );
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||||
|
|
||||||
|
$slug = $plugin;
|
||||||
|
$path = $allowed_plugins[ $slug ];
|
||||||
|
$installed_plugins = get_plugins();
|
||||||
|
|
||||||
|
if ( ! in_array( $path, array_keys( $installed_plugins ), true ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_invalid_plugin', __( 'Invalid plugin.', 'woocommerce-admin' ), 404 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = activate_plugin( $path );
|
||||||
|
if ( ! is_null( $result ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_invalid_plugin', __( 'The requested plugin could not be activated.', 'woocommerce-admin' ), 500 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( array(
|
||||||
|
'slug' => $slug,
|
||||||
|
'name' => $installed_plugins[ $path ]['Name'],
|
||||||
|
'status' => 'success',
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a Jetpack Connect URL.
|
||||||
|
*
|
||||||
|
* @return array Connection URL for Jetpack
|
||||||
|
*/
|
||||||
|
public function connect_jetpack() {
|
||||||
|
if ( ! class_exists( 'Jetpack' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_jetpack_not_active', __( 'Jetpack is not installed or active.', 'woocommerce-admin' ), 404 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$next_step_slug = apply_filters( 'woocommerce_onboarding_after_jetpack_step', 'store-details' );
|
||||||
|
$redirect_url = esc_url_raw(
|
||||||
|
add_query_arg(
|
||||||
|
array(
|
||||||
|
'page' => 'wc-admin',
|
||||||
|
),
|
||||||
|
admin_url( 'admin.php' )
|
||||||
|
) . '#/?step=' . $next_step_slug
|
||||||
|
);
|
||||||
|
|
||||||
|
$connect_url = Jetpack::init()->build_connect_url( true, $redirect_url, 'woocommerce-setup-wizard' );
|
||||||
|
|
||||||
|
// Redirect to local calypso instead of production.
|
||||||
|
if ( defined( 'WOOCOMMERCE_CALYPSO_LOCAL' ) && WOOCOMMERCE_CALYPSO_LOCAL ) {
|
||||||
|
$connect_url = add_query_arg(
|
||||||
|
array(
|
||||||
|
'calypso_env' => 'development',
|
||||||
|
),
|
||||||
|
$connect_url
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return( array(
|
||||||
|
'slug' => $slug,
|
||||||
|
'name' => __( 'Jetpack', 'woocommerce-admin' ),
|
||||||
|
'connectAction' => $connect_url,
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'onboarding_plugin',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'slug' => array(
|
||||||
|
'description' => __( 'Plugin slug.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => __( 'Plugin name.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'status' => array(
|
||||||
|
'description' => __( 'Plugin status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_connect_schema() {
|
||||||
|
$schema = $this->get_item_schema();
|
||||||
|
unset( $schema['properties']['status'] );
|
||||||
|
$schema['properties']['connectAction'] = array(
|
||||||
|
'description' => __( 'Action that should be completed to connect Jetpack.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
);
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,348 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Onboarding Profile Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /onboarding/profile
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Onboarding Profile controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Data_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Onboarding_Profile_Controller extends WC_REST_Data_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc-admin/v1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'onboarding/profile';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register routes.
|
||||||
|
*/
|
||||||
|
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' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base,
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'update_items' ),
|
||||||
|
'permission_callback' => array( $this, 'update_items_permissions_check' ),
|
||||||
|
'args' => $this->get_collection_params(),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a given request has permission to read onboarding profile data.
|
||||||
|
*
|
||||||
|
* @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 resources.', 'woocommerce-admin' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a given request has permission to edit onboarding profile data.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|boolean
|
||||||
|
*/
|
||||||
|
public function update_items_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot edit this resource.', 'woocommerce-admin' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all onboarding profile data.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
|
||||||
|
$item_schema = $this->get_item_schema();
|
||||||
|
|
||||||
|
$items = array();
|
||||||
|
foreach ( $item_schema['properties'] as $key => $property_schema ) {
|
||||||
|
$items[ $key ] = isset( $onboarding_data[ $key ] ) ? $onboarding_data[ $key ] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->prepare_item_for_response( $items, $request );
|
||||||
|
$data = $this->prepare_response_for_collection( $item );
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update onboarding profile data.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function update_items( $request ) {
|
||||||
|
$params = $request->get_json_params();
|
||||||
|
$query_args = $this->prepare_objects_query( $params );
|
||||||
|
$onboarding_data = get_option( 'wc_onboarding_profile', array() );
|
||||||
|
update_option( 'wc_onboarding_profile', array_merge( $onboarding_data, $query_args ) );
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => __( 'Onboarding profile data has been updated.', 'woocommerce-admin' ),
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->prepare_item_for_response( $result, $request );
|
||||||
|
$data = $this->prepare_response_for_collection( $response );
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare objects query.
|
||||||
|
*
|
||||||
|
* @param array $params The params sent in the request.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_objects_query( $params ) {
|
||||||
|
$args = array();
|
||||||
|
$properties = self::get_profile_properties();
|
||||||
|
|
||||||
|
foreach ( $properties as $key => $property ) {
|
||||||
|
if ( isset( $params[ $key ] ) ) {
|
||||||
|
$args[ $key ] = $params[ $key ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 array $params The params sent in the request.
|
||||||
|
*/
|
||||||
|
$args = apply_filters( 'woocommerce_rest_onboarding_profile_object_query', $args, $params );
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the data object for response.
|
||||||
|
*
|
||||||
|
* @param object $item Data object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response $response Response data.
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $item, $request ) {
|
||||||
|
$data = $this->add_additional_fields_to_object( $item, $request );
|
||||||
|
$data = $this->filter_response_by_context( $data, 'view' );
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list returned from the API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Response $response The response object.
|
||||||
|
* @param array $item The original item.
|
||||||
|
* @param WP_REST_Request $request Request used to generate the response.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_onboarding_profile', $response, $item, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get onboarding profile properties.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function get_profile_properties() {
|
||||||
|
$properties = array(
|
||||||
|
'completed' => array(
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Whether or not the profile was completed.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
),
|
||||||
|
'skipped' => array(
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Whether or not the profile was skipped.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
),
|
||||||
|
'account_type' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Account type used for Jetpack.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'enum' => array(
|
||||||
|
'new',
|
||||||
|
'existing',
|
||||||
|
'google',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'industry' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => __( 'Industry.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => array_keys( WC_Admin_Onboarding::get_allowed_industries() ),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'product_types' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => __( 'Types of products sold.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => array_keys( WC_Admin_Onboarding::get_allowed_product_types() ),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'product_count' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Number of products to be added.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'enum' => array(
|
||||||
|
'1-10',
|
||||||
|
'11-100',
|
||||||
|
'101-1000',
|
||||||
|
'1000+',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'selling_venues' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Other places the store is selling products.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'enum' => array(
|
||||||
|
'no',
|
||||||
|
'other',
|
||||||
|
'brick-mortar',
|
||||||
|
'brick-mortar-other',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'other_platform' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Name of other platform used to sell.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'enum' => array(
|
||||||
|
'shopify',
|
||||||
|
'bigcommerce',
|
||||||
|
'magento',
|
||||||
|
'wix',
|
||||||
|
'other',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'theme' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => __( 'Selected store theme.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_title_with_dashes',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
),
|
||||||
|
'items_purchased' => array(
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __( 'Whether or not the user opted to purchase items now or later.', 'woocommerce-admin' ),
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return apply_filters( 'woocommerce_onboarding_profile_properties', $properties );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
// Unset properties used for collection params.
|
||||||
|
$properties = self::get_profile_properties();
|
||||||
|
foreach ( $properties as $key => $property ) {
|
||||||
|
unset( $properties[ $key ]['default'] );
|
||||||
|
unset( $properties[ $key ]['items'] );
|
||||||
|
unset( $properties[ $key ]['validate_callback'] );
|
||||||
|
unset( $properties[ $key ]['sanitize_callback'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'onboarding_profile',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => $properties,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
// Unset properties used for item schema.
|
||||||
|
$params = self::get_profile_properties();
|
||||||
|
foreach ( $params as $key => $param ) {
|
||||||
|
unset( $params[ $key ]['context'] );
|
||||||
|
unset( $params[ $key ]['readonly'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||||
|
|
||||||
|
return apply_filters( 'rest_onboarding_profile_collection_params', $params );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Orders Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /orders/*
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Orders controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Orders_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Orders_Controller extends WC_REST_Orders_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = parent::get_collection_params();
|
||||||
|
// This needs to remain a string to support extensions that filter Order Number.
|
||||||
|
$params['number'] = array(
|
||||||
|
'description' => __( 'Limit result set to orders matching part of an order number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare objects query.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_objects_query( $request ) {
|
||||||
|
global $wpdb;
|
||||||
|
$args = parent::prepare_objects_query( $request );
|
||||||
|
|
||||||
|
// Search by partial order number.
|
||||||
|
if ( ! empty( $request['number'] ) ) {
|
||||||
|
$partial_number = trim( $request['number'] );
|
||||||
|
$limit = intval( $args['posts_per_page'] );
|
||||||
|
$order_ids = $wpdb->get_col(
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT ID
|
||||||
|
FROM {$wpdb->prefix}posts
|
||||||
|
WHERE post_type = 'shop_order'
|
||||||
|
AND ID LIKE %s
|
||||||
|
LIMIT %d",
|
||||||
|
$wpdb->esc_like( absint( $partial_number ) ) . '%',
|
||||||
|
$limit
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Force WP_Query return empty if don't found any order.
|
||||||
|
$order_ids = empty( $order_ids ) ? array( 0 ) : $order_ids;
|
||||||
|
$args['post__in'] = $order_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Product Categories Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /products/categories.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product categories controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Product_Categories_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Product_Categories_Controller extends WC_REST_Product_Categories_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Product Reviews Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /products/reviews.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product reviews controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Product_Reviews_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Product_Reviews_Controller extends WC_REST_Product_Reviews_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param WP_Comment $review Product review object.
|
||||||
|
* @return array Links for the given product review.
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $review ) {
|
||||||
|
$links = array(
|
||||||
|
'self' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $review->comment_ID ) ),
|
||||||
|
),
|
||||||
|
'collection' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if ( 0 !== (int) $review->comment_post_ID ) {
|
||||||
|
$links['up'] = array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $review->comment_post_ID ) ),
|
||||||
|
'embeddable' => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ( 0 !== (int) $review->user_id ) {
|
||||||
|
$links['reviewer'] = array(
|
||||||
|
'href' => rest_url( 'wp/v2/users/' . $review->user_id ),
|
||||||
|
'embeddable' => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Product Variations Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /products/variations.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product variations controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Product_Variations_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Product_Variations_Controller extends WC_REST_Product_Variations_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = parent::get_collection_params();
|
||||||
|
$params['search'] = array(
|
||||||
|
'description' => __( 'Search by similar product name or sku.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add product name and sku filtering to the WC API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_objects_query( $request ) {
|
||||||
|
$args = parent::prepare_objects_query( $request );
|
||||||
|
|
||||||
|
if ( ! empty( $request['search'] ) ) {
|
||||||
|
$args['search'] = $request['search'];
|
||||||
|
unset( $args['s'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a collection of posts and add the post title filter option to WP_Query.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
add_filter( 'posts_where', array( 'WC_Admin_REST_Products_Controller', 'add_wp_query_filter' ), 10, 2 );
|
||||||
|
add_filter( 'posts_join', array( 'WC_Admin_REST_Products_Controller', 'add_wp_query_join' ), 10, 2 );
|
||||||
|
add_filter( 'posts_groupby', array( 'WC_Admin_REST_Products_Controller', 'add_wp_query_group_by' ), 10, 2 );
|
||||||
|
$response = parent::get_items( $request );
|
||||||
|
remove_filter( 'posts_where', array( 'WC_Admin_REST_Products_Controller', 'add_wp_query_filter' ), 10 );
|
||||||
|
remove_filter( 'posts_join', array( 'WC_Admin_REST_Products_Controller', 'add_wp_query_join' ), 10 );
|
||||||
|
remove_filter( 'posts_groupby', array( 'WC_Admin_REST_Products_Controller', 'add_wp_query_group_by' ), 10 );
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Product's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$schema = parent::get_item_schema();
|
||||||
|
|
||||||
|
$schema['properties']['name'] = array(
|
||||||
|
'description' => __( 'Product parent name.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
);
|
||||||
|
$schema['properties']['type'] = array(
|
||||||
|
'description' => __( 'Product type.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'variation',
|
||||||
|
'enum' => array( 'variation' ),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
);
|
||||||
|
$schema['properties']['parent_id'] = array(
|
||||||
|
'description' => __( 'Product parent ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a single variation output for response.
|
||||||
|
*
|
||||||
|
* @param WC_Data $object Object data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_object_for_response( $object, $request ) {
|
||||||
|
$context = empty( $request['context'] ) ? 'view' : $request['context'];
|
||||||
|
$response = parent::prepare_object_for_response( $object, $request );
|
||||||
|
$data = $response->get_data();
|
||||||
|
|
||||||
|
$data['name'] = $object->get_name( $context );
|
||||||
|
$data['type'] = $object->get_type();
|
||||||
|
$data['parent_id'] = $object->get_parent_id( $context );
|
||||||
|
|
||||||
|
$response->set_data( $data );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Products Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /products/*
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Products controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Products_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Products_Controller extends WC_REST_Products_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds properties that can be embed via ?_embed=1.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$schema = parent::get_item_schema();
|
||||||
|
|
||||||
|
$properties_to_embed = array(
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'slug',
|
||||||
|
'permalink',
|
||||||
|
'images',
|
||||||
|
'description',
|
||||||
|
'short_description',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $properties_to_embed as $property ) {
|
||||||
|
$schema['properties'][ $property ]['context'][] = 'embed';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = parent::get_collection_params();
|
||||||
|
$params['low_in_stock'] = array(
|
||||||
|
'description' => __( 'Limit result set to products that are low or out of stock.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'sanitize_callback' => 'wc_string_to_bool',
|
||||||
|
);
|
||||||
|
$params['search'] = array(
|
||||||
|
'description' => __( 'Search by similar product name or sku.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add product name and sku filtering to the WC API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_objects_query( $request ) {
|
||||||
|
$args = parent::prepare_objects_query( $request );
|
||||||
|
|
||||||
|
if ( ! empty( $request['search'] ) ) {
|
||||||
|
$args['search'] = trim( $request['search'] );
|
||||||
|
unset( $args['s'] );
|
||||||
|
}
|
||||||
|
if ( ! empty( $request['low_in_stock'] ) ) {
|
||||||
|
$args['low_in_stock'] = $request['low_in_stock'];
|
||||||
|
$args['post_type'] = array( 'product', 'product_variation' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a collection of posts and add the post title filter option to WP_Query.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full details about the request.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
add_filter( 'posts_where', array( __CLASS__, 'add_wp_query_filter' ), 10, 2 );
|
||||||
|
add_filter( 'posts_join', array( __CLASS__, 'add_wp_query_join' ), 10, 2 );
|
||||||
|
add_filter( 'posts_groupby', array( __CLASS__, 'add_wp_query_group_by' ), 10, 2 );
|
||||||
|
$response = parent::get_items( $request );
|
||||||
|
remove_filter( 'posts_where', array( __CLASS__, 'add_wp_query_filter' ), 10 );
|
||||||
|
remove_filter( 'posts_join', array( __CLASS__, 'add_wp_query_join' ), 10 );
|
||||||
|
remove_filter( 'posts_groupby', array( __CLASS__, 'add_wp_query_group_by' ), 10 );
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add in conditional search filters for products.
|
||||||
|
*
|
||||||
|
* @param string $where Where clause used to search posts.
|
||||||
|
* @param object $wp_query WP_Query object.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function add_wp_query_filter( $where, $wp_query ) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$search = $wp_query->get( 'search' );
|
||||||
|
if ( $search ) {
|
||||||
|
$search = $wpdb->esc_like( $search );
|
||||||
|
$search = "'%" . $search . "%'";
|
||||||
|
$where .= " AND ({$wpdb->posts}.post_title LIKE {$search}";
|
||||||
|
$where .= wc_product_sku_enabled() ? ' OR ps_post_meta.meta_key = "_sku" AND ps_post_meta.meta_value LIKE ' . $search . ')' : ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $wp_query->get( 'low_in_stock' ) ) {
|
||||||
|
$low_stock_amount = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
|
||||||
|
$where .= " AND lis_postmeta2.meta_key = '_manage_stock'
|
||||||
|
AND lis_postmeta2.meta_value = 'yes'
|
||||||
|
AND lis_postmeta.meta_key = '_stock'
|
||||||
|
AND lis_postmeta.meta_value IS NOT NULL
|
||||||
|
AND lis_postmeta3.meta_key = '_low_stock_amount'
|
||||||
|
AND (
|
||||||
|
lis_postmeta3.meta_value > ''
|
||||||
|
AND CAST(lis_postmeta.meta_value AS SIGNED) <= CAST(lis_postmeta3.meta_value AS SIGNED)
|
||||||
|
OR lis_postmeta3.meta_value <= ''
|
||||||
|
AND CAST(lis_postmeta.meta_value AS SIGNED) <= {$low_stock_amount}
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $where;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join posts meta tables when product search or low stock query is present.
|
||||||
|
*
|
||||||
|
* @param string $join Join clause used to search posts.
|
||||||
|
* @param object $wp_query WP_Query object.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function add_wp_query_join( $join, $wp_query ) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$search = $wp_query->get( 'search' );
|
||||||
|
if ( $search && wc_product_sku_enabled() ) {
|
||||||
|
$join .= " INNER JOIN {$wpdb->postmeta} AS ps_post_meta ON ps_post_meta.post_id = {$wpdb->posts}.ID";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $wp_query->get( 'low_in_stock' ) ) {
|
||||||
|
$join .= " INNER JOIN {$wpdb->postmeta} AS lis_postmeta ON {$wpdb->posts}.ID = lis_postmeta.post_id
|
||||||
|
INNER JOIN {$wpdb->postmeta} AS lis_postmeta2 ON {$wpdb->posts}.ID = lis_postmeta2.post_id
|
||||||
|
INNER JOIN {$wpdb->postmeta} AS lis_postmeta3 ON {$wpdb->posts}.ID = lis_postmeta3.post_id";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $join;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group by post ID to prevent duplicates.
|
||||||
|
*
|
||||||
|
* @param string $groupby Group by clause used to organize posts.
|
||||||
|
* @param object $wp_query WP_Query object.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function add_wp_query_group_by( $groupby, $wp_query ) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$search = $wp_query->get( 'search' );
|
||||||
|
$low_in_stock = $wp_query->get( 'low_in_stock' );
|
||||||
|
if ( empty( $groupby ) && ( $search || $low_in_stock ) ) {
|
||||||
|
$groupby = $wpdb->posts . '.ID';
|
||||||
|
}
|
||||||
|
return $groupby;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,320 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports categories controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/categories endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports categories controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_Admin_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Categories_Controller extends WC_Admin_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/categories';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['interval'] = $request['interval'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['extended_info'] = $request['extended_info'];
|
||||||
|
$args['categories'] = (array) $request['categories'];
|
||||||
|
$args['status_is'] = (array) $request['status_is'];
|
||||||
|
$args['status_is_not'] = (array) $request['status_is_not'];
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$categories_query = new WC_Admin_Reports_Categories_Query( $query_args );
|
||||||
|
$report_data = $categories_query->get_data();
|
||||||
|
|
||||||
|
if ( is_wp_error( $report_data ) ) {
|
||||||
|
return $report_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! isset( $report_data->data ) || ! isset( $report_data->page_no ) || ! isset( $report_data->pages ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_reports_categories_invalid_response', __( 'Invalid response from data store.', 'woocommerce-admin' ), array( 'status' => 500 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$out_data = array();
|
||||||
|
|
||||||
|
foreach ( $report_data->data as $datum ) {
|
||||||
|
$item = $this->prepare_item_for_response( $datum, $request );
|
||||||
|
$out_data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $out_data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param stdClass $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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( $report ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_categories', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param WC_Admin_Reports_Query $object Object data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
$links = array(
|
||||||
|
'category' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/products/categories/%d', $this->namespace, $object['category_id'] ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_categories',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'category_id' => array(
|
||||||
|
'description' => __( 'Category ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'items_sold' => array(
|
||||||
|
'description' => __( 'Amount of items sold.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'net_revenue' => array(
|
||||||
|
'description' => __( 'Gross revenue.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Amount of orders.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'products_count' => array(
|
||||||
|
'description' => __( 'Amount of products.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'extended_info' => array(
|
||||||
|
'name' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Category name.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'category_id',
|
||||||
|
'enum' => array(
|
||||||
|
'category_id',
|
||||||
|
'items_sold',
|
||||||
|
'net_revenue',
|
||||||
|
'orders_count',
|
||||||
|
'products_count',
|
||||||
|
'category',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['interval'] = array(
|
||||||
|
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'week',
|
||||||
|
'enum' => array(
|
||||||
|
'hour',
|
||||||
|
'day',
|
||||||
|
'week',
|
||||||
|
'month',
|
||||||
|
'quarter',
|
||||||
|
'year',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['status_is'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified order status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => $this->get_order_statuses(),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['status_is_not'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified order status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => $this->get_order_statuses(),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['categories'] = array(
|
||||||
|
'description' => __( 'Limit result set to all items that have the specified term assigned in the categories taxonomy.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['extended_info'] = array(
|
||||||
|
'description' => __( 'Add additional piece of info about each category to the report.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'sanitize_callback' => 'wc_string_to_bool',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,303 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports controller extended by WC Admin plugin.
|
||||||
|
*
|
||||||
|
* Handles requests to the reports endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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-admin' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$data = array();
|
||||||
|
$reports = array(
|
||||||
|
array(
|
||||||
|
'slug' => 'performance-indicators',
|
||||||
|
'description' => __( 'Batch endpoint for getting specific performance indicators from `stats` endpoints.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'revenue/stats',
|
||||||
|
'description' => __( 'Stats about revenue.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'orders/stats',
|
||||||
|
'description' => __( 'Stats about orders.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'products',
|
||||||
|
'description' => __( 'Products detailed reports.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'products/stats',
|
||||||
|
'description' => __( 'Stats about products.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'categories',
|
||||||
|
'description' => __( 'Product categories detailed reports.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'categories/stats',
|
||||||
|
'description' => __( 'Stats about product categories.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'coupons',
|
||||||
|
'description' => __( 'Coupons detailed reports.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'coupons/stats',
|
||||||
|
'description' => __( 'Stats about coupons.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'taxes',
|
||||||
|
'description' => __( 'Taxes detailed reports.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'taxes/stats',
|
||||||
|
'description' => __( 'Stats about taxes.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'downloads',
|
||||||
|
'description' => __( 'Product downloads detailed reports.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'downloads/files',
|
||||||
|
'description' => __( 'Product download files detailed reports.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'downloads/stats',
|
||||||
|
'description' => __( 'Stats about product downloads.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'slug' => 'customers',
|
||||||
|
'description' => __( 'Customers detailed reports.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list of allowed reports, so that data can be loaded from third party extensions in addition to WooCommerce core.
|
||||||
|
* Array items should be in format of array( 'slug' => 'downloads/stats', 'description' => '',
|
||||||
|
* 'url' => '', and 'path' => '/wc-ext/v1/...'.
|
||||||
|
*
|
||||||
|
* @param array $endpoints The list of allowed reports..
|
||||||
|
*/
|
||||||
|
$reports = apply_filters( 'woocommerce_admin_reports', $reports );
|
||||||
|
|
||||||
|
foreach ( $reports as $report ) {
|
||||||
|
if ( empty( $report['slug'] ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $report['path'] ) ) {
|
||||||
|
$report['path'] = '/' . $this->namespace . '/reports/' . $report['slug'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows a different admin page to be loaded here,
|
||||||
|
// or allows an empty url if no report exists for a set of performance indicators.
|
||||||
|
if ( ! isset( $report['url'] ) ) {
|
||||||
|
if ( '/stats' === substr( $report['slug'], -6 ) ) {
|
||||||
|
$url_slug = substr( $report['slug'], 0, -6 );
|
||||||
|
} else {
|
||||||
|
$url_slug = $report['slug'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$report['url'] = '/analytics/' . $url_slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->prepare_item_for_response( (object) $report, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the order number for an order. If no filter is present for `woocommerce_order_number`, we can just return the ID.
|
||||||
|
* Returns the parent order number if the order is actually a refund.
|
||||||
|
*
|
||||||
|
* @param int $order_id Order ID.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_order_number( $order_id ) {
|
||||||
|
$order = wc_get_order( $order_id );
|
||||||
|
|
||||||
|
if ( 'shop_order_refund' === $order->get_type() ) {
|
||||||
|
$order = wc_get_order( $order->get_parent_id() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! has_filter( 'woocommerce_order_number' ) ) {
|
||||||
|
return $order->get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $order->get_order_number();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param stdClass $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = array(
|
||||||
|
'slug' => $report->slug,
|
||||||
|
'description' => $report->description,
|
||||||
|
'path' => $report->path,
|
||||||
|
);
|
||||||
|
|
||||||
|
$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( $report->path ),
|
||||||
|
),
|
||||||
|
'report' => array(
|
||||||
|
'href' => $report->url,
|
||||||
|
),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'description' => array(
|
||||||
|
'description' => __( 'A human-readable description of the resource.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'path' => array(
|
||||||
|
'description' => __( 'API path.', 'woocommerce-admin' ),
|
||||||
|
'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' ) ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get order statuses without prefixes.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_order_statuses() {
|
||||||
|
$order_statuses = array();
|
||||||
|
|
||||||
|
foreach ( array_keys( wc_get_order_statuses() ) as $status ) {
|
||||||
|
$order_statuses[] = str_replace( 'wc-', '', $status );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $order_statuses;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports coupons controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/coupons endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports coupons controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Coupons_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/coupons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['coupons'] = (array) $request['coupons'];
|
||||||
|
$args['extended_info'] = $request['extended_info'];
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$coupons_query = new WC_Admin_Reports_Coupons_Query( $query_args );
|
||||||
|
$report_data = $coupons_query->get_data();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
foreach ( $report_data->data as $coupons_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $coupons_data, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param stdClass $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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( $report ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_coupons', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param WC_Reports_Query $object Object data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
$links = array(
|
||||||
|
'coupon' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/coupons/%d', $this->namespace, $object['coupon_id'] ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_coupons',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'coupon_id' => array(
|
||||||
|
'description' => __( 'Coupon ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'amount' => array(
|
||||||
|
'description' => __( 'Net discount amount.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Amount of orders.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'extended_info' => array(
|
||||||
|
'code' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Coupon code.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'date_created' => array(
|
||||||
|
'type' => 'date-time',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Coupon creation date.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'date_created_gmt' => array(
|
||||||
|
'type' => 'date-time',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Coupon creation date in GMT.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'date_expires' => array(
|
||||||
|
'type' => 'date-time',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Coupon expiration date.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'date_expires_gmt' => array(
|
||||||
|
'type' => 'date-time',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Coupon expiration date in GMT.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'discount_type' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'enum' => array_keys( wc_get_coupon_types() ),
|
||||||
|
'description' => __( 'Coupon discount type.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'coupon_id',
|
||||||
|
'enum' => array(
|
||||||
|
'coupon_id',
|
||||||
|
'code',
|
||||||
|
'amount',
|
||||||
|
'orders_count',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['coupons'] = array(
|
||||||
|
'description' => __( 'Limit result set to coupons assigned specific coupon IDs.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['extended_info'] = array(
|
||||||
|
'description' => __( 'Add additional piece of info about each coupon to the report.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'sanitize_callback' => 'wc_string_to_bool',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,353 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports coupons stats controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/coupons/stats endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports coupons stats controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Coupons_Stats_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/coupons/stats';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['interval'] = $request['interval'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['coupons'] = (array) $request['coupons'];
|
||||||
|
$args['segmentby'] = $request['segmentby'];
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$coupons_query = new WC_Admin_Reports_Coupons_Stats_Query( $query_args );
|
||||||
|
try {
|
||||||
|
$report_data = $coupons_query->get_data();
|
||||||
|
} catch ( WC_Admin_Reports_Parameter_Exception $e ) {
|
||||||
|
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$out_data = array(
|
||||||
|
'totals' => get_object_vars( $report_data->totals ),
|
||||||
|
'intervals' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $report_data->intervals as $interval_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( (object) $interval_data, $request );
|
||||||
|
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $out_data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param stdClass $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = get_object_vars( $report );
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_coupons_stats', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$data_values = array(
|
||||||
|
'amount' => array(
|
||||||
|
'description' => __( 'Net discount amount.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'coupons_count' => array(
|
||||||
|
'description' => __( 'Amount of coupons.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Amount of discounted orders.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$segments = array(
|
||||||
|
'segments' => array(
|
||||||
|
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'segment_id' => array(
|
||||||
|
'description' => __( 'Segment identificator.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $data_values,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$totals = array_merge( $data_values, $segments );
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_coupons_stats',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'totals' => array(
|
||||||
|
'description' => __( 'Totals data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
'intervals' => array(
|
||||||
|
'description' => __( 'Reports data grouped by intervals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'interval' => array(
|
||||||
|
'description' => __( 'Type of interval.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||||
|
),
|
||||||
|
'date_start' => array(
|
||||||
|
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_start_gmt' => array(
|
||||||
|
'description' => __( 'The date the report start, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end' => array(
|
||||||
|
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end_gmt' => array(
|
||||||
|
'description' => __( 'The date the report end, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'amount',
|
||||||
|
'coupons_count',
|
||||||
|
'orders_count',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['interval'] = array(
|
||||||
|
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'week',
|
||||||
|
'enum' => array(
|
||||||
|
'hour',
|
||||||
|
'day',
|
||||||
|
'week',
|
||||||
|
'month',
|
||||||
|
'quarter',
|
||||||
|
'year',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['coupons'] = array(
|
||||||
|
'description' => __( 'Limit result set to coupons assigned specific coupon IDs.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['segmentby'] = array(
|
||||||
|
'description' => __( 'Segment the response by additional constraint.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array(
|
||||||
|
'product',
|
||||||
|
'variation',
|
||||||
|
'category',
|
||||||
|
'coupon',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,517 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports customers controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/customers endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports customers controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Customers_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/customers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['registered_before'] = $request['registered_before'];
|
||||||
|
$args['registered_after'] = $request['registered_after'];
|
||||||
|
$args['order_before'] = $request['before'];
|
||||||
|
$args['order_after'] = $request['after'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['match'] = $request['match'];
|
||||||
|
$args['search'] = $request['search'];
|
||||||
|
$args['searchby'] = $request['searchby'];
|
||||||
|
$args['name_includes'] = $request['name_includes'];
|
||||||
|
$args['name_excludes'] = $request['name_excludes'];
|
||||||
|
$args['username_includes'] = $request['username_includes'];
|
||||||
|
$args['username_excludes'] = $request['username_excludes'];
|
||||||
|
$args['email_includes'] = $request['email_includes'];
|
||||||
|
$args['email_excludes'] = $request['email_excludes'];
|
||||||
|
$args['country_includes'] = $request['country_includes'];
|
||||||
|
$args['country_excludes'] = $request['country_excludes'];
|
||||||
|
$args['last_active_before'] = $request['last_active_before'];
|
||||||
|
$args['last_active_after'] = $request['last_active_after'];
|
||||||
|
$args['orders_count_min'] = $request['orders_count_min'];
|
||||||
|
$args['orders_count_max'] = $request['orders_count_max'];
|
||||||
|
$args['total_spend_min'] = $request['total_spend_min'];
|
||||||
|
$args['total_spend_max'] = $request['total_spend_max'];
|
||||||
|
$args['avg_order_value_min'] = $request['avg_order_value_min'];
|
||||||
|
$args['avg_order_value_max'] = $request['avg_order_value_max'];
|
||||||
|
$args['last_order_before'] = $request['last_order_before'];
|
||||||
|
$args['last_order_after'] = $request['last_order_after'];
|
||||||
|
$args['customers'] = $request['customers'];
|
||||||
|
|
||||||
|
$between_params_numeric = array( 'orders_count', 'total_spend', 'avg_order_value' );
|
||||||
|
$normalized_params_numeric = WC_Admin_Reports_Interval::normalize_between_params( $request, $between_params_numeric, false );
|
||||||
|
$between_params_date = array( 'last_active', 'registered' );
|
||||||
|
$normalized_params_date = WC_Admin_Reports_Interval::normalize_between_params( $request, $between_params_date, true );
|
||||||
|
$args = array_merge( $args, $normalized_params_numeric, $normalized_params_date );
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$customers_query = new WC_Admin_Reports_Customers_Query( $query_args );
|
||||||
|
$report_data = $customers_query->get_data();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
foreach ( $report_data->data as $customer_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $customer_data, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||||
|
$data = $this->add_additional_fields_to_object( $report, $request );
|
||||||
|
$data['date_registered_gmt'] = wc_rest_prepare_date_response( $data['date_registered'] );
|
||||||
|
$data['date_registered'] = wc_rest_prepare_date_response( $data['date_registered'], false );
|
||||||
|
$data['date_last_active_gmt'] = wc_rest_prepare_date_response( $data['date_last_active'] );
|
||||||
|
$data['date_last_active'] = wc_rest_prepare_date_response( $data['date_last_active'], false );
|
||||||
|
$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( $report ) );
|
||||||
|
/**
|
||||||
|
* 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_customers', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param array $object Object data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
if ( empty( $object['user_id'] ) ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'customer' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $object['user_id'] ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_customers',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Customer ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'user_id' => array(
|
||||||
|
'description' => __( 'User ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => __( 'Name.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'username' => array(
|
||||||
|
'description' => __( 'Username.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'country' => array(
|
||||||
|
'description' => __( 'Country.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'city' => array(
|
||||||
|
'description' => __( 'City.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'postcode' => array(
|
||||||
|
'description' => __( 'Postal code.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_registered' => array(
|
||||||
|
'description' => __( 'Date registered.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_registered_gmt' => array(
|
||||||
|
'description' => __( 'Date registered GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_last_active' => array(
|
||||||
|
'description' => __( 'Date last active.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_last_active_gmt' => array(
|
||||||
|
'description' => __( 'Date last active GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Order count.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'total_spend' => array(
|
||||||
|
'description' => __( 'Total spend.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'avg_order_value' => array(
|
||||||
|
'description' => __( 'Avg order value.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'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 = array();
|
||||||
|
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||||
|
$params['registered_before'] = array(
|
||||||
|
'description' => __( 'Limit response to objects registered before (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['registered_after'] = array(
|
||||||
|
'description' => __( 'Limit response to objects registered after (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources with orders published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['before'] = array(
|
||||||
|
'description' => __( 'Limit response to resources with orders published before a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date_registered',
|
||||||
|
'enum' => array(
|
||||||
|
'username',
|
||||||
|
'name',
|
||||||
|
'country',
|
||||||
|
'city',
|
||||||
|
'postcode',
|
||||||
|
'date_registered',
|
||||||
|
'date_last_active',
|
||||||
|
'orders_count',
|
||||||
|
'total_spend',
|
||||||
|
'avg_order_value',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['match'] = array(
|
||||||
|
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'all',
|
||||||
|
'enum' => array(
|
||||||
|
'all',
|
||||||
|
'any',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['search'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with a customer field containing the search term. Searches the field provided by `searchby`.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['searchby'] = array(
|
||||||
|
'description' => 'Limit results with `search` and `searchby` to specific fields containing the search term.',
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'name',
|
||||||
|
'enum' => array(
|
||||||
|
'name',
|
||||||
|
'username',
|
||||||
|
'email',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['name_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with specfic names.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['name_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects excluding specfic names.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['username_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with specfic usernames.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['username_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects excluding specfic usernames.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['email_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects including emails.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['email_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects excluding emails.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['country_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with specfic countries.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['country_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects excluding specfic countries.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['last_active_before'] = array(
|
||||||
|
'description' => __( 'Limit response to objects last active before (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['last_active_after'] = array(
|
||||||
|
'description' => __( 'Limit response to objects last active after (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['last_active_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects last active between two given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_date_arg' ),
|
||||||
|
);
|
||||||
|
$params['registered_before'] = array(
|
||||||
|
'description' => __( 'Limit response to objects registered before (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['registered_after'] = array(
|
||||||
|
'description' => __( 'Limit response to objects registered after (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['registered_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects last active between two given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_date_arg' ),
|
||||||
|
);
|
||||||
|
$params['orders_count_min'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an order count greater than or equal to given integer.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orders_count_max'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an order count less than or equal to given integer.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orders_count_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an order count between two given integers.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_numeric_arg' ),
|
||||||
|
);
|
||||||
|
$params['total_spend_min'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with a total order spend greater than or equal to given number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['total_spend_max'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with a total order spend less than or equal to given number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['total_spend_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with a total order spend between two given numbers.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_numeric_arg' ),
|
||||||
|
);
|
||||||
|
$params['avg_order_value_min'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an average order spend greater than or equal to given number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['avg_order_value_max'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an average order spend less than or equal to given number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['avg_order_value_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an average order spend between two given numbers.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_numeric_arg' ),
|
||||||
|
);
|
||||||
|
$params['last_order_before'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with last order before (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['last_order_after'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with last order after (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['customers'] = array(
|
||||||
|
'description' => __( 'Limit result to items with specified customer ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,364 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports customers stats controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/customers/stats endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports customers stats controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Customers_Stats_Controller extends WC_REST_Reports_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/customers/stats';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['registered_before'] = $request['registered_before'];
|
||||||
|
$args['registered_after'] = $request['registered_after'];
|
||||||
|
$args['match'] = $request['match'];
|
||||||
|
$args['search'] = $request['search'];
|
||||||
|
$args['name_includes'] = $request['name_includes'];
|
||||||
|
$args['name_excludes'] = $request['name_excludes'];
|
||||||
|
$args['username_includes'] = $request['username_includes'];
|
||||||
|
$args['username_excludes'] = $request['username_excludes'];
|
||||||
|
$args['email_includes'] = $request['email_includes'];
|
||||||
|
$args['email_excludes'] = $request['email_excludes'];
|
||||||
|
$args['country_includes'] = $request['country_includes'];
|
||||||
|
$args['country_excludes'] = $request['country_excludes'];
|
||||||
|
$args['last_active_before'] = $request['last_active_before'];
|
||||||
|
$args['last_active_after'] = $request['last_active_after'];
|
||||||
|
$args['orders_count_min'] = $request['orders_count_min'];
|
||||||
|
$args['orders_count_max'] = $request['orders_count_max'];
|
||||||
|
$args['total_spend_min'] = $request['total_spend_min'];
|
||||||
|
$args['total_spend_max'] = $request['total_spend_max'];
|
||||||
|
$args['avg_order_value_min'] = $request['avg_order_value_min'];
|
||||||
|
$args['avg_order_value_max'] = $request['avg_order_value_max'];
|
||||||
|
$args['last_order_before'] = $request['last_order_before'];
|
||||||
|
$args['last_order_after'] = $request['last_order_after'];
|
||||||
|
$args['customers'] = $request['customers'];
|
||||||
|
|
||||||
|
$between_params_numeric = array( 'orders_count', 'total_spend', 'avg_order_value' );
|
||||||
|
$normalized_params_numeric = WC_Admin_Reports_Interval::normalize_between_params( $request, $between_params_numeric, false );
|
||||||
|
$between_params_date = array( 'last_active', 'registered' );
|
||||||
|
$normalized_params_date = WC_Admin_Reports_Interval::normalize_between_params( $request, $between_params_date, true );
|
||||||
|
$args = array_merge( $args, $normalized_params_numeric, $normalized_params_date );
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$customers_query = new WC_Admin_Reports_Customers_Stats_Query( $query_args );
|
||||||
|
$report_data = $customers_query->get_data();
|
||||||
|
$out_data = array(
|
||||||
|
'totals' => $report_data,
|
||||||
|
);
|
||||||
|
|
||||||
|
return rest_ensure_response( $out_data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param Array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_customers_stats', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
// @todo Should any of these be 'indicator's?
|
||||||
|
$totals = array(
|
||||||
|
'customers_count' => array(
|
||||||
|
'description' => __( 'Number of customers.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'avg_orders_count' => array(
|
||||||
|
'description' => __( 'Average number of orders.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'avg_total_spend' => array(
|
||||||
|
'description' => __( 'Average total spend per customer.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'avg_avg_order_value' => array(
|
||||||
|
'description' => __( 'Average AOV per customer.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_customers_stats',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'totals' => array(
|
||||||
|
'description' => __( 'Totals data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['registered_before'] = array(
|
||||||
|
'description' => __( 'Limit response to objects registered before (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['registered_after'] = array(
|
||||||
|
'description' => __( 'Limit response to objects registered after (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['match'] = array(
|
||||||
|
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'all',
|
||||||
|
'enum' => array(
|
||||||
|
'all',
|
||||||
|
'any',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['search'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with a customer field containing the search term. Searches the field provided by `searchby`.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['searchby'] = array(
|
||||||
|
'description' => 'Limit results with `search` and `searchby` to specific fields containing the search term.',
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'name',
|
||||||
|
'enum' => array(
|
||||||
|
'name',
|
||||||
|
'username',
|
||||||
|
'email',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['name_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with specfic names.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['name_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects excluding specfic names.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['username_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with specfic usernames.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['username_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects excluding specfic usernames.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['email_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects including emails.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['email_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects excluding emails.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['country_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with specfic countries.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['country_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects excluding specfic countries.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['last_active_before'] = array(
|
||||||
|
'description' => __( 'Limit response to objects last active before (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['last_active_after'] = array(
|
||||||
|
'description' => __( 'Limit response to objects last active after (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['last_active_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects last active between two given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_date_arg' ),
|
||||||
|
);
|
||||||
|
$params['registered_before'] = array(
|
||||||
|
'description' => __( 'Limit response to objects registered before (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['registered_after'] = array(
|
||||||
|
'description' => __( 'Limit response to objects registered after (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['registered_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects last active between two given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_date_arg' ),
|
||||||
|
);
|
||||||
|
$params['orders_count_min'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an order count greater than or equal to given integer.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orders_count_max'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an order count less than or equal to given integer.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orders_count_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an order count between two given integers.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_numeric_arg' ),
|
||||||
|
);
|
||||||
|
$params['total_spend_min'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with a total order spend greater than or equal to given number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['total_spend_max'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with a total order spend less than or equal to given number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['total_spend_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with a total order spend between two given numbers.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_numeric_arg' ),
|
||||||
|
);
|
||||||
|
$params['avg_order_value_min'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an average order spend greater than or equal to given number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['avg_order_value_max'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an average order spend less than or equal to given number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['avg_order_value_between'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with an average order spend between two given numbers.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => array( 'WC_Admin_Reports_Interval', 'rest_validate_between_numeric_arg' ),
|
||||||
|
);
|
||||||
|
$params['last_order_before'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with last order before (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['last_order_after'] = array(
|
||||||
|
'description' => __( 'Limit response to objects with last order after (or at) a given ISO8601 compliant datetime.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['customers'] = array(
|
||||||
|
'description' => __( 'Limit result to items with specified customer ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,380 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports downloads controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/downloads endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports downloads controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Downloads_Controller extends WC_Admin_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/downloads';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get items.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$registered = array_keys( $this->get_collection_params() );
|
||||||
|
foreach ( $registered as $param_name ) {
|
||||||
|
if ( isset( $request[ $param_name ] ) ) {
|
||||||
|
$args[ $param_name ] = $request[ $param_name ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$reports = new WC_Admin_Reports_Downloads_Query( $args );
|
||||||
|
$downloads_data = $reports->get_data();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
foreach ( $downloads_data->data as $download_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $download_data, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
|
||||||
|
$response->header( 'X-WP-Total', (int) $downloads_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $downloads_data->pages );
|
||||||
|
|
||||||
|
$page = $downloads_data->page_no;
|
||||||
|
$max_pages = $downloads_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param Array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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( $report ) );
|
||||||
|
|
||||||
|
$response->data['date'] = get_date_from_gmt( $data['date_gmt'], 'Y-m-d H:i:s' );
|
||||||
|
|
||||||
|
// Figure out file name.
|
||||||
|
// Matches https://github.com/woocommerce/woocommerce/blob/4be0018c092e617c5d2b8c46b800eb71ece9ddef/includes/class-wc-download-handler.php#L197.
|
||||||
|
$product_id = intval( $data['product_id'] );
|
||||||
|
$_product = wc_get_product( $product_id );
|
||||||
|
$file_path = $_product->get_file_download_path( $data['download_id'] );
|
||||||
|
$filename = basename( $file_path );
|
||||||
|
$response->data['file_name'] = apply_filters( 'woocommerce_file_download_filename', $filename, $product_id );
|
||||||
|
$response->data['file_path'] = $file_path;
|
||||||
|
$customer = new WC_Customer( $data['user_id'] );
|
||||||
|
$response->data['username'] = $customer->get_username();
|
||||||
|
$response->data['order_number'] = $this->get_order_number( $data['order_id'] );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_downloads', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param Array $object Object data.
|
||||||
|
* @return array Links for the given post.
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
$links = array(
|
||||||
|
'product' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, 'products', $object['product_id'] ) ),
|
||||||
|
'embeddable' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_downloads',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'ID.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'product_id' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product ID.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'date' => array(
|
||||||
|
'description' => __( "The date of the download, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_gmt' => array(
|
||||||
|
'description' => __( 'The date of the download, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'download_id' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Download ID.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'file_name' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'File name.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'file_path' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'File URL.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'product_id' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product ID.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'order_id' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Order ID.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'order_number' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Order Number.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'user_id' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'User ID for the downloader.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'username' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'User name of the downloader.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'ip_address' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'IP address for the downloader.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'product',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['match'] = array(
|
||||||
|
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: products, orders, username, ip_address.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'all',
|
||||||
|
'enum' => array(
|
||||||
|
'all',
|
||||||
|
'any',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['product_includes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified product(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['product_excludes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified product(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['order_includes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified order ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['order_excludes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified order ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['customer_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects that have the specified user ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['customer_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects that don\'t have the specified user ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['ip_address_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects that have a specified ip address.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$params['ip_address_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects that don\'t have a specified ip address.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports downloads files controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/downloads/files endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports downloads files controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Downloads_Files_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/downloads/files';
|
||||||
|
}
|
|
@ -0,0 +1,372 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports downloads stats controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/downloads/stats endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports downloads stats controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Downloads_Stats_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/downloads/stats';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['interval'] = $request['interval'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['match'] = $request['match'];
|
||||||
|
$args['product_includes'] = (array) $request['product_includes'];
|
||||||
|
$args['product_excludes'] = (array) $request['product_excludes'];
|
||||||
|
$args['customer_includes'] = (array) $request['customer_includes'];
|
||||||
|
$args['customer_excludes'] = (array) $request['customer_excludes'];
|
||||||
|
$args['order_includes'] = (array) $request['order_includes'];
|
||||||
|
$args['order_excludes'] = (array) $request['order_excludes'];
|
||||||
|
$args['ip_address_includes'] = (array) $request['ip_address_includes'];
|
||||||
|
$args['ip_address_excludes'] = (array) $request['ip_address_excludes'];
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$downloads_query = new WC_Admin_Reports_Downloads_Stats_Query( $query_args );
|
||||||
|
$report_data = $downloads_query->get_data();
|
||||||
|
|
||||||
|
$out_data = array(
|
||||||
|
'totals' => get_object_vars( $report_data->totals ),
|
||||||
|
'intervals' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $report_data->intervals as $interval_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $interval_data, $request );
|
||||||
|
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $out_data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param Array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_downloads_stats', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$totals = array(
|
||||||
|
'download_count' => array(
|
||||||
|
'description' => __( 'Number of downloads.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_orders_stats',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'totals' => array(
|
||||||
|
'description' => __( 'Totals data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
'intervals' => array(
|
||||||
|
'description' => __( 'Reports data grouped by intervals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'interval' => array(
|
||||||
|
'description' => __( 'Type of interval.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||||
|
),
|
||||||
|
'date_start' => array(
|
||||||
|
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_start_gmt' => array(
|
||||||
|
'description' => __( 'The date the report start, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end' => array(
|
||||||
|
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end_gmt' => array(
|
||||||
|
'description' => __( 'The date the report end, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'download_count',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['interval'] = array(
|
||||||
|
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'week',
|
||||||
|
'enum' => array(
|
||||||
|
'hour',
|
||||||
|
'day',
|
||||||
|
'week',
|
||||||
|
'month',
|
||||||
|
'quarter',
|
||||||
|
'year',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['match'] = array(
|
||||||
|
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'all',
|
||||||
|
'enum' => array(
|
||||||
|
'all',
|
||||||
|
'any',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['product_includes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified product(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
|
||||||
|
);
|
||||||
|
$params['product_excludes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified product(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['order_includes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified order ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['order_excludes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified order ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['customer_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects that have the specified customer ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['customer_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects that don\'t have the specified customer ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['ip_address_includes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects that have a specified ip address.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$params['ip_address_excludes'] = array(
|
||||||
|
'description' => __( 'Limit response to objects that don\'t have a specified ip address.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,315 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports Import Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /reports/import
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports Imports controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Data_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Import_Controller extends WC_Admin_REST_Reports_Controller {
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/import';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register routes.
|
||||||
|
*/
|
||||||
|
public function register_routes() {
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base,
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'import_items' ),
|
||||||
|
'permission_callback' => array( $this, 'import_permissions_check' ),
|
||||||
|
'args' => $this->get_import_collection_params(),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_import_public_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/cancel',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'cancel_import' ),
|
||||||
|
'permission_callback' => array( $this, 'import_permissions_check' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_import_public_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/delete',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::EDITABLE,
|
||||||
|
'callback' => array( $this, 'delete_imported_items' ),
|
||||||
|
'permission_callback' => array( $this, 'import_permissions_check' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_import_public_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/status',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( $this, 'get_import_status' ),
|
||||||
|
'permission_callback' => array( $this, 'import_permissions_check' ),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_import_public_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/totals',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( $this, 'get_import_totals' ),
|
||||||
|
'permission_callback' => array( $this, 'import_permissions_check' ),
|
||||||
|
'args' => $this->get_import_collection_params(),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_import_public_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure the current user has access to WRITE the settings APIs.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Full data about the request.
|
||||||
|
* @return WP_Error|bool
|
||||||
|
*/
|
||||||
|
public function import_permissions_check( $request ) {
|
||||||
|
if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you cannot edit this resource.', 'woocommerce-admin' ), array( 'status' => rest_authorization_required_code() ) );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import data based on user request params.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function import_items( $request ) {
|
||||||
|
$query_args = $this->prepare_objects_query( $request );
|
||||||
|
$import = WC_Admin_Reports_Sync::regenerate_report_data( $query_args['days'], $query_args['skip_existing'] );
|
||||||
|
|
||||||
|
if ( is_wp_error( $import ) ) {
|
||||||
|
$result = array(
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $import->get_error_message(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$result = array(
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => $import,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->prepare_item_for_response( $result, $request );
|
||||||
|
$data = $this->prepare_response_for_collection( $response );
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare request object as query args.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_objects_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['skip_existing'] = $request['skip_existing'];
|
||||||
|
$args['days'] = $request['days'];
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the data object for response.
|
||||||
|
*
|
||||||
|
* @param object $item Data object.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response $response Response data.
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $item, $request ) {
|
||||||
|
$data = $this->add_additional_fields_to_object( $item, $request );
|
||||||
|
$data = $this->filter_response_by_context( $data, 'view' );
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the list returned from the API.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Response $response The response object.
|
||||||
|
* @param array $item The original item.
|
||||||
|
* @param WP_REST_Request $request Request used to generate the response.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_reports_import', $response, $item, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_import_collection_params() {
|
||||||
|
$params = array();
|
||||||
|
$params['days'] = array(
|
||||||
|
'description' => __( 'Number of days to import.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['skip_existing'] = array(
|
||||||
|
'description' => __( 'Skip importing existing order data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'sanitize_callback' => 'wc_string_to_bool',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_import_public_schema() {
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_import',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'status' => array(
|
||||||
|
'description' => __( 'Regeneration status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'message' => array(
|
||||||
|
'description' => __( 'Regenerate data message.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel all queued import actions.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function cancel_import( $request ) {
|
||||||
|
WC_Admin_Reports_Sync::clear_queued_actions();
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => __( 'All pending and in-progress import actions have been cancelled.', 'woocommerce-admin' ),
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->prepare_item_for_response( $result, $request );
|
||||||
|
$data = $this->prepare_response_for_collection( $response );
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all imported items.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function delete_imported_items( $request ) {
|
||||||
|
$delete = WC_Admin_Reports_Sync::delete_report_data();
|
||||||
|
|
||||||
|
if ( is_wp_error( $delete ) ) {
|
||||||
|
$result = array(
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => $delete->get_error_message(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$result = array(
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => $delete,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->prepare_item_for_response( $result, $request );
|
||||||
|
$data = $this->prepare_response_for_collection( $response );
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the status of the current import.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_import_status( $request ) {
|
||||||
|
$result = array(
|
||||||
|
'is_importing' => WC_Admin_Reports_Sync::is_importing(),
|
||||||
|
'customers_total' => get_option( 'wc_admin_import_customers_total', 0 ),
|
||||||
|
'customers_count' => get_option( 'wc_admin_import_customers_count', 0 ),
|
||||||
|
'orders_total' => get_option( 'wc_admin_import_orders_total', 0 ),
|
||||||
|
'orders_count' => get_option( 'wc_admin_import_orders_count', 0 ),
|
||||||
|
'imported_from' => get_option( 'wc_admin_imported_from_date', false ),
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->prepare_item_for_response( $result, $request );
|
||||||
|
$data = $this->prepare_response_for_collection( $response );
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total orders and customers based on user supplied params.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return WP_Error|WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function get_import_totals( $request ) {
|
||||||
|
$query_args = $this->prepare_objects_query( $request );
|
||||||
|
$totals = WC_Admin_Reports_Sync::get_import_totals( $query_args['days'], $query_args['skip_existing'] );
|
||||||
|
|
||||||
|
$response = $this->prepare_item_for_response( $totals, $request );
|
||||||
|
$data = $this->prepare_response_for_collection( $response );
|
||||||
|
|
||||||
|
return rest_ensure_response( $data );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,377 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports orders controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/orders endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports orders controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_Admin_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Orders_Controller extends WC_Admin_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/orders';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['product_includes'] = (array) $request['product_includes'];
|
||||||
|
$args['product_excludes'] = (array) $request['product_excludes'];
|
||||||
|
$args['coupon_includes'] = (array) $request['coupon_includes'];
|
||||||
|
$args['coupon_excludes'] = (array) $request['coupon_excludes'];
|
||||||
|
$args['status_is'] = (array) $request['status_is'];
|
||||||
|
$args['status_is_not'] = (array) $request['status_is_not'];
|
||||||
|
$args['customer_type'] = $request['customer_type'];
|
||||||
|
$args['extended_info'] = $request['extended_info'];
|
||||||
|
$args['refunds'] = $request['refunds'];
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$orders_query = new WC_Admin_Reports_Orders_Query( $query_args );
|
||||||
|
$report_data = $orders_query->get_data();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
foreach ( $report_data->data as $orders_data ) {
|
||||||
|
$orders_data['order_number'] = $this->get_order_number( $orders_data['order_id'] );
|
||||||
|
$item = $this->prepare_item_for_response( $orders_data, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param stdClass $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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( $report ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_orders', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param WC_Reports_Query $object Object data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
$links = array(
|
||||||
|
'order' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $object['order_id'] ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_orders',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'order_id' => array(
|
||||||
|
'description' => __( 'Order ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'order_number' => array(
|
||||||
|
'description' => __( 'Order Number.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_created' => array(
|
||||||
|
'description' => __( 'Date the order was created.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'status' => array(
|
||||||
|
'description' => __( 'Order status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'customer_id' => array(
|
||||||
|
'description' => __( 'Customer ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'num_items_sold' => array(
|
||||||
|
'description' => __( 'Number of items sold.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'net_total' => array(
|
||||||
|
'description' => __( 'Net total revenue.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'float',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'customer_type' => array(
|
||||||
|
'description' => __( 'Returning or new customer.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'extended_info' => array(
|
||||||
|
'products' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'List of product IDs and names.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'categories' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Category IDs.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 0,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'num_items_sold',
|
||||||
|
'net_total',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['product_includes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified product(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['product_excludes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified product(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['coupon_includes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified coupon(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['coupon_excludes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified coupon(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['status_is'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified order status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => $this->get_order_statuses(),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['status_is_not'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified order status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => $this->get_order_statuses(),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['customer_type'] = array(
|
||||||
|
'description' => __( 'Limit result set to returning or new customers.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => '',
|
||||||
|
'enum' => array(
|
||||||
|
'',
|
||||||
|
'returning',
|
||||||
|
'new',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['refunds'] = array(
|
||||||
|
'description' => __( 'Limit result set to specific types of refunds.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => '',
|
||||||
|
'enum' => array(
|
||||||
|
'',
|
||||||
|
'all',
|
||||||
|
'partial',
|
||||||
|
'full',
|
||||||
|
'none',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['extended_info'] = array(
|
||||||
|
'description' => __( 'Add additional piece of info about each coupon to the report.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'sanitize_callback' => 'wc_string_to_bool',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,493 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports orders stats controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/orders/stats endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports orders stats controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_Admin_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Orders_Stats_Controller extends WC_Admin_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/orders/stats';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['interval'] = $request['interval'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
|
||||||
|
$args['match'] = $request['match'];
|
||||||
|
$args['status_is'] = (array) $request['status_is'];
|
||||||
|
$args['status_is_not'] = (array) $request['status_is_not'];
|
||||||
|
$args['product_includes'] = (array) $request['product_includes'];
|
||||||
|
$args['product_excludes'] = (array) $request['product_excludes'];
|
||||||
|
$args['coupon_includes'] = (array) $request['coupon_includes'];
|
||||||
|
$args['coupon_excludes'] = (array) $request['coupon_excludes'];
|
||||||
|
$args['customer'] = $request['customer'];
|
||||||
|
$args['refunds'] = $request['refunds'];
|
||||||
|
$args['categories'] = (array) $request['categories'];
|
||||||
|
$args['segmentby'] = $request['segmentby'];
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$orders_query = new WC_Admin_Reports_Orders_Stats_Query( $query_args );
|
||||||
|
try {
|
||||||
|
$report_data = $orders_query->get_data();
|
||||||
|
} catch ( WC_Admin_Reports_Parameter_Exception $e ) {
|
||||||
|
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$out_data = array(
|
||||||
|
'totals' => get_object_vars( $report_data->totals ),
|
||||||
|
'intervals' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $report_data->intervals as $interval_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $interval_data, $request );
|
||||||
|
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $out_data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param Array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_orders_stats', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$data_values = array(
|
||||||
|
'net_revenue' => array(
|
||||||
|
'description' => __( 'Net revenue.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Amount of orders', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
),
|
||||||
|
'avg_order_value' => array(
|
||||||
|
'description' => __( 'Average order value.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'avg_items_per_order' => array(
|
||||||
|
'description' => __( 'Average items per order', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'num_items_sold' => array(
|
||||||
|
'description' => __( 'Number of items sold', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'coupons' => array(
|
||||||
|
'description' => __( 'Amount discounted by coupons.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'coupons_count' => array(
|
||||||
|
'description' => __( 'Unique coupons count.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'num_returning_customers' => array(
|
||||||
|
'description' => __( 'Number of orders done by returning customers', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'num_new_customers' => array(
|
||||||
|
'description' => __( 'Number of orders done by new customers', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'products' => array(
|
||||||
|
'description' => __( 'Number of distinct products sold.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$segments = array(
|
||||||
|
'segments' => array(
|
||||||
|
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'segment_id' => array(
|
||||||
|
'description' => __( 'Segment identificator.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $data_values,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$totals = array_merge( $data_values, $segments );
|
||||||
|
|
||||||
|
// Products is not shown in intervals.
|
||||||
|
unset( $data_values['products'] );
|
||||||
|
|
||||||
|
$intervals = array_merge( $data_values, $segments );
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_orders_stats',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'totals' => array(
|
||||||
|
'description' => __( 'Totals data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
'intervals' => array(
|
||||||
|
'description' => __( 'Reports data grouped by intervals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'interval' => array(
|
||||||
|
'description' => __( 'Type of interval.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||||
|
),
|
||||||
|
'date_start' => array(
|
||||||
|
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_start_gmt' => array(
|
||||||
|
'description' => __( 'The date the report start, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end' => array(
|
||||||
|
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end_gmt' => array(
|
||||||
|
'description' => __( 'The date the report end, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $intervals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'net_revenue',
|
||||||
|
'orders_count',
|
||||||
|
'avg_order_value',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['interval'] = array(
|
||||||
|
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'week',
|
||||||
|
'enum' => array(
|
||||||
|
'hour',
|
||||||
|
'day',
|
||||||
|
'week',
|
||||||
|
'month',
|
||||||
|
'quarter',
|
||||||
|
'year',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['match'] = array(
|
||||||
|
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'all',
|
||||||
|
'enum' => array(
|
||||||
|
'all',
|
||||||
|
'any',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['status_is'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified order status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'default' => null,
|
||||||
|
'items' => array(
|
||||||
|
'enum' => $this->get_order_statuses(),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['status_is_not'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified order status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_slug_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'enum' => $this->get_order_statuses(),
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['product_includes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified product(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
|
||||||
|
);
|
||||||
|
$params['product_excludes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified product(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['coupon_includes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified coupon(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['coupon_excludes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified coupon(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['customer'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that don\'t have the specified coupon(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array(
|
||||||
|
'new',
|
||||||
|
'returning',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['refunds'] = array(
|
||||||
|
'description' => __( 'Limit result set to specific types of refunds.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => '',
|
||||||
|
'enum' => array(
|
||||||
|
'',
|
||||||
|
'all',
|
||||||
|
'partial',
|
||||||
|
'full',
|
||||||
|
'none',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['segmentby'] = array(
|
||||||
|
'description' => __( 'Segment the response by additional constraint.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array(
|
||||||
|
'product',
|
||||||
|
'category',
|
||||||
|
'variation',
|
||||||
|
'coupon',
|
||||||
|
'customer_type', // new vs returning.
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,505 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Performance indicators controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/store-performance endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports Performance indicators controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Performance_Indicators_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/performance-indicators';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of endpoints by report slug.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $endpoints = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of allowed stats.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $allowed_stats = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of stat labels.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $labels = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a list of endpoints by url.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $urls = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace,
|
||||||
|
'/' . $this->rest_base . '/allowed',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => WP_REST_Server::READABLE,
|
||||||
|
'callback' => array( $this, 'get_allowed_items' ),
|
||||||
|
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
||||||
|
'args' => $this->get_collection_params(),
|
||||||
|
),
|
||||||
|
'schema' => array( $this, 'get_public_allowed_item_schema' ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['stats'] = $request['stats'];
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get information such as allowed stats, stat labels, and endpoint data from stats reports.
|
||||||
|
*
|
||||||
|
* @return WP_Error|True
|
||||||
|
*/
|
||||||
|
private function get_indicator_data() {
|
||||||
|
// Data already retrieved.
|
||||||
|
if ( ! empty( $this->endpoints ) && ! empty( $this->labels ) && ! empty( $this->allowed_stats ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = new WP_REST_Request( 'GET', '/wc/v4/reports' );
|
||||||
|
$response = rest_do_request( $request );
|
||||||
|
$endpoints = $response->get_data();
|
||||||
|
$allowed_stats = array();
|
||||||
|
if ( 200 !== $response->get_status() ) {
|
||||||
|
return new WP_Error( 'woocommerce_reports_performance_indicators_result_failed', __( 'Sorry, fetching performance indicators failed.', 'woocommerce-admin' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $endpoints as $endpoint ) {
|
||||||
|
if ( '/stats' === substr( $endpoint['slug'], -6 ) ) {
|
||||||
|
$request = new WP_REST_Request( 'OPTIONS', $endpoint['path'] );
|
||||||
|
$response = rest_do_request( $request );
|
||||||
|
$data = $response->get_data();
|
||||||
|
|
||||||
|
$prefix = substr( $endpoint['slug'], 0, -6 );
|
||||||
|
|
||||||
|
if ( empty( $data['schema']['properties']['totals']['properties'] ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $data['schema']['properties']['totals']['properties'] as $property_key => $schema_info ) {
|
||||||
|
if ( empty( $schema_info['indicator'] ) || ! $schema_info['indicator'] ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stat = $prefix . '/' . $property_key;
|
||||||
|
$allowed_stats[] = $stat;
|
||||||
|
|
||||||
|
$this->labels[ $stat ] = trim( preg_replace( '/\W+/', ' ', $schema_info['description'] ) );
|
||||||
|
$this->formats[ $stat ] = isset( $schema_info['format'] ) ? $schema_info['format'] : 'number';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->endpoints[ $prefix ] = $endpoint['path'];
|
||||||
|
$this->urls[ $prefix ] = $endpoint['_links']['report'][0]['href'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->allowed_stats = $allowed_stats;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of allowed performance indicators.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_allowed_items( $request ) {
|
||||||
|
$indicator_data = $this->get_indicator_data();
|
||||||
|
if ( is_wp_error( $indicator_data ) ) {
|
||||||
|
return $indicator_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
foreach ( $this->allowed_stats as $stat ) {
|
||||||
|
$pieces = $this->get_stats_parts( $stat );
|
||||||
|
$report = $pieces[0];
|
||||||
|
$chart = $pieces[1];
|
||||||
|
$data[] = (object) array(
|
||||||
|
'stat' => $stat,
|
||||||
|
'chart' => $chart,
|
||||||
|
'label' => $this->labels[ $stat ],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
usort( $data, array( $this, 'sort' ) );
|
||||||
|
|
||||||
|
$objects = array();
|
||||||
|
foreach ( $data as $item ) {
|
||||||
|
$prepared = $this->prepare_item_for_response( $item, $request );
|
||||||
|
$objects[] = $this->prepare_response_for_collection( $prepared );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $objects );
|
||||||
|
$response->header( 'X-WP-Total', count( $data ) );
|
||||||
|
$response->header( 'X-WP-TotalPages', 1 );
|
||||||
|
|
||||||
|
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the list of stats. Sorted by custom arrangement.
|
||||||
|
*
|
||||||
|
* @see https://github.com/woocommerce/woocommerce-admin/issues/1282
|
||||||
|
* @param object $a First item.
|
||||||
|
* @param object $b Second item.
|
||||||
|
* @return order
|
||||||
|
*/
|
||||||
|
public function sort( $a, $b ) {
|
||||||
|
/**
|
||||||
|
* Custom ordering for store performance indicators.
|
||||||
|
*
|
||||||
|
* @see https://github.com/woocommerce/woocommerce-admin/issues/1282
|
||||||
|
* @param array $indicators A list of ordered indicators.
|
||||||
|
*/
|
||||||
|
$stat_order = apply_filters(
|
||||||
|
'woocommerce_rest_report_sort_performance_indicators',
|
||||||
|
array(
|
||||||
|
'revenue/gross_revenue',
|
||||||
|
'revenue/net_revenue',
|
||||||
|
'orders/orders_count',
|
||||||
|
'orders/avg_order_value',
|
||||||
|
'products/items_sold',
|
||||||
|
'revenue/refunds',
|
||||||
|
'coupons/orders_count',
|
||||||
|
'coupons/amount',
|
||||||
|
'taxes/total_tax',
|
||||||
|
'taxes/order_tax',
|
||||||
|
'taxes/shipping_tax',
|
||||||
|
'revenue/shipping',
|
||||||
|
'downloads/download_count',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$a = array_search( $a->stat, $stat_order );
|
||||||
|
$b = array_search( $b->stat, $stat_order );
|
||||||
|
|
||||||
|
if ( false === $a && false === $b ) {
|
||||||
|
return 0;
|
||||||
|
} elseif ( false === $a ) {
|
||||||
|
return 1;
|
||||||
|
} elseif ( false === $b ) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return $a - $b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$indicator_data = $this->get_indicator_data();
|
||||||
|
if ( is_wp_error( $indicator_data ) ) {
|
||||||
|
return $indicator_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
if ( empty( $query_args['stats'] ) ) {
|
||||||
|
return new WP_Error( 'woocommerce_reports_performance_indicators_empty_query', __( 'A list of stats to query must be provided.', 'woocommerce-admin' ), 400 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$stats = array();
|
||||||
|
foreach ( $query_args['stats'] as $stat ) {
|
||||||
|
$is_error = false;
|
||||||
|
|
||||||
|
$pieces = $this->get_stats_parts( $stat );
|
||||||
|
$report = $pieces[0];
|
||||||
|
$chart = $pieces[1];
|
||||||
|
|
||||||
|
if ( ! in_array( $stat, $this->allowed_stats ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request_url = $this->endpoints[ $report ];
|
||||||
|
$request = new WP_REST_Request( 'GET', $request_url );
|
||||||
|
$request->set_param( 'before', $query_args['before'] );
|
||||||
|
$request->set_param( 'after', $query_args['after'] );
|
||||||
|
|
||||||
|
$response = rest_do_request( $request );
|
||||||
|
|
||||||
|
$data = $response->get_data();
|
||||||
|
$format = $this->formats[ $stat ];
|
||||||
|
$label = $this->labels[ $stat ];
|
||||||
|
|
||||||
|
if ( 200 !== $response->get_status() || ! isset( $data['totals'][ $chart ] ) ) {
|
||||||
|
$stats[] = (object) array(
|
||||||
|
'stat' => $stat,
|
||||||
|
'chart' => $chart,
|
||||||
|
'label' => $label,
|
||||||
|
'format' => $format,
|
||||||
|
'value' => null,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stats[] = (object) array(
|
||||||
|
'stat' => $stat,
|
||||||
|
'chart' => $chart,
|
||||||
|
'label' => $label,
|
||||||
|
'format' => $format,
|
||||||
|
'value' => $data['totals'][ $chart ],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
usort( $stats, array( $this, 'sort' ) );
|
||||||
|
|
||||||
|
$objects = array();
|
||||||
|
foreach ( $stats as $stat ) {
|
||||||
|
$data = $this->prepare_item_for_response( $stat, $request );
|
||||||
|
$objects[] = $this->prepare_response_for_collection( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $objects );
|
||||||
|
$response->header( 'X-WP-Total', count( $stats ) );
|
||||||
|
$response->header( 'X-WP-TotalPages', 1 );
|
||||||
|
|
||||||
|
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param stdClass $stat_data Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $stat_data, $request ) {
|
||||||
|
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||||
|
$data = $this->add_additional_fields_to_object( $stat_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( $data ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_performance_indicators', $response, $stat_data, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param WC_Admin_Reports_Query $object Object data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
$pieces = $this->get_stats_parts( $object->stat );
|
||||||
|
$endpoint = $pieces[0];
|
||||||
|
$stat = $pieces[1];
|
||||||
|
$url = $this->urls[ $endpoint ];
|
||||||
|
|
||||||
|
$links = array(
|
||||||
|
'api' => array(
|
||||||
|
'href' => rest_url( $this->endpoints[ $endpoint ] ),
|
||||||
|
),
|
||||||
|
'report' => array(
|
||||||
|
'href' => ! empty( $url ) ? $url : '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the endpoint part of a stat request (prefix) and the actual stat total we want.
|
||||||
|
* To allow extensions to namespace (example: fue/emails/sent), we break on the last forward slash.
|
||||||
|
*
|
||||||
|
* @param string $full_stat A stat request string like orders/avg_order_value or fue/emails/sent.
|
||||||
|
* @return array Containing the prefix (endpoint) and suffix (stat).
|
||||||
|
*/
|
||||||
|
private function get_stats_parts( $full_stat ) {
|
||||||
|
$endpoint = substr( $full_stat, 0, strrpos( $full_stat, '/' ) );
|
||||||
|
$stat = substr( $full_stat, ( strrpos( $full_stat, '/' ) + 1 ) );
|
||||||
|
return array(
|
||||||
|
$endpoint,
|
||||||
|
$stat,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$indicator_data = $this->get_indicator_data();
|
||||||
|
if ( is_wp_error( $indicator_data ) ) {
|
||||||
|
$allowed_stats = array();
|
||||||
|
} else {
|
||||||
|
$allowed_stats = $this->allowed_stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_performance_indicator',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'stat' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => $allowed_stats,
|
||||||
|
),
|
||||||
|
'chart' => array(
|
||||||
|
'description' => __( 'The specific chart this stat referrers to.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'label' => array(
|
||||||
|
'description' => __( 'Human readable label for the stat.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'format' => array(
|
||||||
|
'description' => __( 'Format of the stat.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'number', 'currency' ),
|
||||||
|
),
|
||||||
|
'value' => array(
|
||||||
|
'description' => __( 'Value of the stat. Returns null if the stat does not exist or cannot be loaded.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get schema for the list of allowed performance indicators.
|
||||||
|
*
|
||||||
|
* @return array $schema
|
||||||
|
*/
|
||||||
|
public function get_public_allowed_item_schema() {
|
||||||
|
$schema = $this->get_public_item_schema();
|
||||||
|
unset( $schema['properties']['value'] );
|
||||||
|
unset( $schema['properties']['format'] );
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$indicator_data = $this->get_indicator_data();
|
||||||
|
if ( is_wp_error( $indicator_data ) ) {
|
||||||
|
$allowed_stats = __( 'There was an issue loading the report endpoints', 'woocommerce-admin' );
|
||||||
|
} else {
|
||||||
|
$allowed_stats = implode( ', ', $this->allowed_stats );
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = array();
|
||||||
|
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||||
|
$params['stats'] = array(
|
||||||
|
'description' => sprintf(
|
||||||
|
/* translators: Allowed values is a list of stat endpoints. */
|
||||||
|
__( 'Limit response to specific report stats. Allowed values: %s.', 'woocommerce-admin' ),
|
||||||
|
$allowed_stats
|
||||||
|
),
|
||||||
|
'type' => 'array',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,344 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports products controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/products endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports products controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Products_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/products';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping between external parameter name and name used in query class.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $param_mapping = array(
|
||||||
|
'products' => 'product_includes',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get items.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
*
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$registered = array_keys( $this->get_collection_params() );
|
||||||
|
foreach ( $registered as $param_name ) {
|
||||||
|
if ( isset( $request[ $param_name ] ) ) {
|
||||||
|
if ( isset( $this->param_mapping[ $param_name ] ) ) {
|
||||||
|
$args[ $this->param_mapping[ $param_name ] ] = $request[ $param_name ];
|
||||||
|
} else {
|
||||||
|
$args[ $param_name ] = $request[ $param_name ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$reports = new WC_Admin_Reports_Products_Query( $args );
|
||||||
|
$products_data = $reports->get_data();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
foreach ( $products_data->data as $product_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $product_data, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $products_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $products_data->pages );
|
||||||
|
|
||||||
|
$page = $products_data->page_no;
|
||||||
|
$max_pages = $products_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param Array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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( $report ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_products', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param Array $object Object data.
|
||||||
|
* @return array Links for the given post.
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
$links = array(
|
||||||
|
'product' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, 'products', $object['product_id'] ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_products',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'product_id' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product ID.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'items_sold' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Number of items sold.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'net_revenue' => array(
|
||||||
|
'type' => 'number',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Total net revenue of all items sold.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Number of orders product appeared in.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'extended_info' => array(
|
||||||
|
'name' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product name.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'price' => array(
|
||||||
|
'type' => 'number',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product price.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'image' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product image.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'permalink' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product link.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'attributes' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product attributes.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'stock_status' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product inventory status.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'stock_quantity' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product inventory quantity.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'low_stock_amount' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product inventory threshold for low stock.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'variations' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product variations IDs.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'sku' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product SKU.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'net_revenue',
|
||||||
|
'orders_count',
|
||||||
|
'items_sold',
|
||||||
|
'product_name',
|
||||||
|
'variations',
|
||||||
|
'sku',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['categories'] = array(
|
||||||
|
'description' => __( 'Limit result to items from the specified categories.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['match'] = array(
|
||||||
|
'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'all',
|
||||||
|
'enum' => array(
|
||||||
|
'all',
|
||||||
|
'any',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['products'] = array(
|
||||||
|
'description' => __( 'Limit result to items with specified product ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
|
||||||
|
);
|
||||||
|
$params['extended_info'] = array(
|
||||||
|
'description' => __( 'Add additional piece of info about each product to the report.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'sanitize_callback' => 'wc_string_to_bool',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,416 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports products stats controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/products/stats endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports products stats controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Products_Stats_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/products/stats';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping between external parameter name and name used in query class.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $param_mapping = array(
|
||||||
|
'products' => 'product_includes',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
add_filter( 'woocommerce_reports_products_stats_select_query', array( $this, 'set_default_report_data' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = array(
|
||||||
|
'fields' => array(
|
||||||
|
'items_sold',
|
||||||
|
'net_revenue',
|
||||||
|
'orders_count',
|
||||||
|
'products_count',
|
||||||
|
'variations_count',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$registered = array_keys( $this->get_collection_params() );
|
||||||
|
foreach ( $registered as $param_name ) {
|
||||||
|
if ( isset( $request[ $param_name ] ) ) {
|
||||||
|
if ( isset( $this->param_mapping[ $param_name ] ) ) {
|
||||||
|
$query_args[ $this->param_mapping[ $param_name ] ] = $request[ $param_name ];
|
||||||
|
} else {
|
||||||
|
$query_args[ $param_name ] = $request[ $param_name ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = new WC_Admin_Reports_Products_Stats_Query( $query_args );
|
||||||
|
try {
|
||||||
|
$report_data = $query->get_data();
|
||||||
|
} catch ( WC_Admin_Reports_Parameter_Exception $e ) {
|
||||||
|
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$out_data = array(
|
||||||
|
'totals' => get_object_vars( $report_data->totals ),
|
||||||
|
'intervals' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $report_data->intervals as $interval_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $interval_data, $request );
|
||||||
|
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $out_data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param Array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_products_stats', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$data_values = array(
|
||||||
|
'items_sold' => array(
|
||||||
|
'description' => __( 'Number of items sold.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
),
|
||||||
|
'net_revenue' => array(
|
||||||
|
'description' => __( 'Net revenue.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Number of orders.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$segments = array(
|
||||||
|
'segments' => array(
|
||||||
|
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'segment_id' => array(
|
||||||
|
'description' => __( 'Segment identificator.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'segment_label' => array(
|
||||||
|
'description' => __( 'Human readable segment label, either product or variation name.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $data_values,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$totals = array_merge( $data_values, $segments );
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_products_stats',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'totals' => array(
|
||||||
|
'description' => __( 'Totals data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
'intervals' => array(
|
||||||
|
'description' => __( 'Reports data grouped by intervals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'interval' => array(
|
||||||
|
'description' => __( 'Type of interval.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||||
|
),
|
||||||
|
'date_start' => array(
|
||||||
|
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_start_gmt' => array(
|
||||||
|
'description' => __( 'The date the report start, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end' => array(
|
||||||
|
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end_gmt' => array(
|
||||||
|
'description' => __( 'The date the report end, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->add_additional_fields_schema( $schema );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default results to 0 if API returns an empty array
|
||||||
|
*
|
||||||
|
* @param Mixed $results Report data.
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function set_default_report_data( $results ) {
|
||||||
|
if ( empty( $results ) ) {
|
||||||
|
$results = new stdClass();
|
||||||
|
$results->total = 0;
|
||||||
|
$results->totals = new stdClass();
|
||||||
|
$results->totals->items_sold = 0;
|
||||||
|
$results->totals->net_revenue = 0;
|
||||||
|
$results->totals->orders_count = 0;
|
||||||
|
$results->intervals = array();
|
||||||
|
$results->pages = 1;
|
||||||
|
$results->page_no = 1;
|
||||||
|
}
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = array();
|
||||||
|
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'net_revenue',
|
||||||
|
'coupons',
|
||||||
|
'refunds',
|
||||||
|
'shipping',
|
||||||
|
'taxes',
|
||||||
|
'net_revenue',
|
||||||
|
'orders_count',
|
||||||
|
'items_sold',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['interval'] = array(
|
||||||
|
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'week',
|
||||||
|
'enum' => array(
|
||||||
|
'hour',
|
||||||
|
'day',
|
||||||
|
'week',
|
||||||
|
'month',
|
||||||
|
'quarter',
|
||||||
|
'year',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['categories'] = array(
|
||||||
|
'description' => __( 'Limit result to items from the specified categories.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['products'] = array(
|
||||||
|
'description' => __( 'Limit result to items with specified product ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['variations'] = array(
|
||||||
|
'description' => __( 'Limit result to items with specified variation ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['segmentby'] = array(
|
||||||
|
'description' => __( 'Segment the response by additional constraint.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array(
|
||||||
|
'product',
|
||||||
|
'category',
|
||||||
|
'variation',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,402 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports revenue stats controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/revenue/stats endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports revenue stats controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Revenue_Stats_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/revenue/stats';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['interval'] = $request['interval'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['segmentby'] = $request['segmentby'];
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$reports_revenue = new WC_Admin_Reports_Revenue_Query( $query_args );
|
||||||
|
try {
|
||||||
|
$report_data = $reports_revenue->get_data();
|
||||||
|
} catch ( WC_Admin_Reports_Parameter_Exception $e ) {
|
||||||
|
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$out_data = array(
|
||||||
|
'totals' => get_object_vars( $report_data->totals ),
|
||||||
|
'intervals' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $report_data->intervals as $interval_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $interval_data, $request );
|
||||||
|
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $out_data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param Array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_revenue_stats', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$data_values = array(
|
||||||
|
'gross_revenue' => array(
|
||||||
|
'description' => __( 'Gross revenue.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'net_revenue' => array(
|
||||||
|
'description' => __( 'Net revenue.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'coupons' => array(
|
||||||
|
'description' => __( 'Amount discounted by coupons.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'coupons_count' => array(
|
||||||
|
'description' => __( 'Unique coupons count.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'shipping' => array(
|
||||||
|
'description' => __( 'Total of shipping.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'taxes' => array(
|
||||||
|
'description' => __( 'Total of taxes.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'refunds' => array(
|
||||||
|
'description' => __( 'Total of refunds.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Amount of orders.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'num_items_sold' => array(
|
||||||
|
'description' => __( 'Items sold.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'products' => array(
|
||||||
|
'description' => __( 'Products sold.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$segments = array(
|
||||||
|
'segments' => array(
|
||||||
|
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'segment_id' => array(
|
||||||
|
'description' => __( 'Segment identificator.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $data_values,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$totals = array_merge( $data_values, $segments );
|
||||||
|
|
||||||
|
// Products is not shown in intervals.
|
||||||
|
unset( $data_values['products'] );
|
||||||
|
|
||||||
|
$intervals = array_merge( $data_values, $segments );
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_revenue_stats',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'totals' => array(
|
||||||
|
'description' => __( 'Totals data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
'intervals' => array(
|
||||||
|
'description' => __( 'Reports data grouped by intervals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'interval' => array(
|
||||||
|
'description' => __( 'Type of interval.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||||
|
),
|
||||||
|
'date_start' => array(
|
||||||
|
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_start_gmt' => array(
|
||||||
|
'description' => __( 'The date the report start, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end' => array(
|
||||||
|
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end_gmt' => array(
|
||||||
|
'description' => __( 'The date the report end, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $intervals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'gross_revenue',
|
||||||
|
'coupons',
|
||||||
|
'refunds',
|
||||||
|
'shipping',
|
||||||
|
'taxes',
|
||||||
|
'net_revenue',
|
||||||
|
'orders_count',
|
||||||
|
'items_sold',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['interval'] = array(
|
||||||
|
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'week',
|
||||||
|
'enum' => array(
|
||||||
|
'hour',
|
||||||
|
'day',
|
||||||
|
'week',
|
||||||
|
'month',
|
||||||
|
'quarter',
|
||||||
|
'year',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['segmentby'] = array(
|
||||||
|
'description' => __( 'Segment the response by additional constraint.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array(
|
||||||
|
'product',
|
||||||
|
'category',
|
||||||
|
'variation',
|
||||||
|
'coupon',
|
||||||
|
'customer_type', // new vs returning.
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,419 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports stock controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/stock endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports stock controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Stock_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/stock';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $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['post_parent__in'] = $request['parent'];
|
||||||
|
$args['post_parent__not_in'] = $request['parent_exclude'];
|
||||||
|
|
||||||
|
if ( 'date' === $args['orderby'] ) {
|
||||||
|
$args['orderby'] = 'date ID';
|
||||||
|
} elseif ( 'stock_status' === $args['orderby'] ) {
|
||||||
|
$args['meta_query'] = array( // WPCS: slow query ok.
|
||||||
|
'relation' => 'AND',
|
||||||
|
'_stock_status' => array(
|
||||||
|
'key' => '_stock_status',
|
||||||
|
'compare' => 'EXISTS',
|
||||||
|
),
|
||||||
|
'_stock' => array(
|
||||||
|
'key' => '_stock',
|
||||||
|
'compare' => 'EXISTS',
|
||||||
|
'type' => 'NUMERIC',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$args['orderby'] = array(
|
||||||
|
'_stock_status' => $args['order'],
|
||||||
|
'_stock' => 'desc' === $args['order'] ? 'asc' : 'desc',
|
||||||
|
);
|
||||||
|
} elseif ( 'stock_quantity' === $args['orderby'] ) {
|
||||||
|
$args['meta_key'] = '_stock'; // WPCS: slow query ok.
|
||||||
|
$args['orderby'] = 'meta_value_num';
|
||||||
|
} elseif ( 'include' === $args['orderby'] ) {
|
||||||
|
$args['orderby'] = 'post__in';
|
||||||
|
} elseif ( 'id' === $args['orderby'] ) {
|
||||||
|
$args['orderby'] = 'ID'; // ID must be capitalized.
|
||||||
|
} elseif ( 'sku' === $args['orderby'] ) {
|
||||||
|
$args['meta_key'] = '_sku'; // WPCS: slow query ok.
|
||||||
|
$args['orderby'] = 'meta_value';
|
||||||
|
}
|
||||||
|
|
||||||
|
$args['post_type'] = array( 'product', 'product_variation' );
|
||||||
|
|
||||||
|
if ( 'lowstock' === $request['type'] ) {
|
||||||
|
$low_stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
|
||||||
|
$no_stock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
|
||||||
|
|
||||||
|
$args['meta_query'] = array( // WPCS: slow query ok.
|
||||||
|
array(
|
||||||
|
'key' => '_manage_stock',
|
||||||
|
'value' => 'yes',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'key' => '_stock',
|
||||||
|
'value' => array( $no_stock, $low_stock ),
|
||||||
|
'compare' => 'BETWEEN',
|
||||||
|
'type' => 'NUMERIC',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'key' => '_stock_status',
|
||||||
|
'value' => 'instock',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} elseif ( in_array( $request['type'], array_keys( wc_get_product_stock_status_options() ), true ) ) {
|
||||||
|
$args['meta_query'] = array( // WPCS: slow query ok.
|
||||||
|
array(
|
||||||
|
'key' => '_stock_status',
|
||||||
|
'value' => $request['type'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query_args['ignore_sticky_posts'] = true;
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query products.
|
||||||
|
*
|
||||||
|
* @param array $query_args Query args.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function get_products( $query_args ) {
|
||||||
|
$query = new WP_Query();
|
||||||
|
$result = $query->query( $query_args );
|
||||||
|
|
||||||
|
$total_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'objects' => array_map( 'wc_get_product', $result ),
|
||||||
|
'total' => (int) $total_posts,
|
||||||
|
'pages' => (int) ceil( $total_posts / (int) $query->query_vars['posts_per_page'] ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$query_results = $this->get_products( $query_args );
|
||||||
|
|
||||||
|
$objects = array();
|
||||||
|
foreach ( $query_results['objects'] as $object ) {
|
||||||
|
$data = $this->prepare_item_for_response( $object, $request );
|
||||||
|
$objects[] = $this->prepare_response_for_collection( $data );
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = (int) $query_args['paged'];
|
||||||
|
$max_pages = $query_results['pages'];
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $objects );
|
||||||
|
$response->header( 'X-WP-Total', $query_results['total'] );
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param WC_Product $product Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $product, $request ) {
|
||||||
|
$data = array(
|
||||||
|
'id' => $product->get_id(),
|
||||||
|
'parent_id' => $product->get_parent_id(),
|
||||||
|
'name' => $product->get_name(),
|
||||||
|
'sku' => $product->get_sku(),
|
||||||
|
'stock_status' => $product->get_stock_status(),
|
||||||
|
'stock_quantity' => (float) $product->get_stock_quantity(),
|
||||||
|
'manage_stock' => $product->get_manage_stock(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$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( $product ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 WC_Product $product The original product object.
|
||||||
|
* @param WP_REST_Request $request Request used to generate the response.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_report_stock', $response, $product, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param WC_Product $product Object data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $product ) {
|
||||||
|
if ( $product->is_type( 'variation' ) ) {
|
||||||
|
$links = array(
|
||||||
|
'product' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/products/%d/variations/%d', $this->namespace, $product->get_parent_id(), $product->get_id() ) ),
|
||||||
|
),
|
||||||
|
'parent' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product->get_parent_id() ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} elseif ( $product->get_parent_id() ) {
|
||||||
|
$links = array(
|
||||||
|
'product' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product->get_id() ) ),
|
||||||
|
),
|
||||||
|
'parent' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product->get_parent_id() ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$links = array(
|
||||||
|
'product' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product->get_id() ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_stock',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'id' => array(
|
||||||
|
'description' => __( 'Unique identifier for the resource.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'parent_id' => array(
|
||||||
|
'description' => __( 'Product parent ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => __( 'Product name.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'sku' => array(
|
||||||
|
'description' => __( 'Unique identifier.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'stock_status' => array(
|
||||||
|
'description' => __( 'Stock status.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array_keys( wc_get_product_stock_status_options() ),
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'stock_quantity' => array(
|
||||||
|
'description' => __( 'Stock quantity.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'manage_stock' => array(
|
||||||
|
'description' => __( 'Manage stock.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'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 = array();
|
||||||
|
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['exclude'] = array(
|
||||||
|
'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['include'] = array(
|
||||||
|
'description' => __( 'Limit result set to specific ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
);
|
||||||
|
$params['offset'] = array(
|
||||||
|
'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'asc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'stock_status',
|
||||||
|
'enum' => array(
|
||||||
|
'stock_status',
|
||||||
|
'stock_quantity',
|
||||||
|
'date',
|
||||||
|
'id',
|
||||||
|
'include',
|
||||||
|
'title',
|
||||||
|
'sku',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['parent'] = array(
|
||||||
|
'description' => __( 'Limit result set to those of particular parent IDs.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'default' => array(),
|
||||||
|
);
|
||||||
|
$params['type'] = array(
|
||||||
|
'description' => __( 'Limit result set to items assigned a stock report type.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'all',
|
||||||
|
'enum' => array_merge( array( 'all', 'lowstock' ), array_keys( wc_get_product_stock_status_options() ) ),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports stock stats controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/stock/stats endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports stock stats controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Stock_Stats_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/stock/stats';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Stock Status Totals.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$stock_query = new WC_Admin_Reports_Stock_Stats_Query();
|
||||||
|
$report_data = $stock_query->get_data();
|
||||||
|
$out_data = array(
|
||||||
|
'totals' => $report_data,
|
||||||
|
);
|
||||||
|
return rest_ensure_response( $out_data );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param WC_Product $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 WC_Product $product The original bject.
|
||||||
|
* @param WP_REST_Request $request Request used to generate the response.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_rest_prepare_report_stock_stats', $response, $product, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$totals = array(
|
||||||
|
'products' => array(
|
||||||
|
'description' => __( 'Number of products.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'lowstock' => array(
|
||||||
|
'description' => __( 'Number of low stock products.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$status_options = wc_get_product_stock_status_options();
|
||||||
|
foreach ( $status_options as $status => $label ) {
|
||||||
|
$totals[ $status ] = array(
|
||||||
|
/* translators: Stock status. Example: "Number of low stock products */
|
||||||
|
'description' => sprintf( __( 'Number of %s products.', 'woocommerce-admin' ), $label ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_customers_stats',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'totals' => array(
|
||||||
|
'description' => __( 'Totals data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports taxes controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/taxes endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports taxes controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Taxes_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/taxes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['taxes'] = $request['taxes'];
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$taxes_query = new WC_Admin_Reports_Taxes_Query( $query_args );
|
||||||
|
$report_data = $taxes_query->get_data();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
foreach ( $report_data->data as $tax_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( (object) $tax_data, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param stdClass $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
||||||
|
$report = $this->add_additional_fields_to_object( $report, $request );
|
||||||
|
$report = $this->filter_response_by_context( $report, $context );
|
||||||
|
|
||||||
|
// Wrap the data in a response object.
|
||||||
|
$response = rest_ensure_response( $report );
|
||||||
|
$response->add_links( $this->prepare_links( $report ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_taxes', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param WC_Reports_Query $object Object data.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
$links = array(
|
||||||
|
'tax' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/taxes/%d', $this->namespace, $object->tax_rate_id ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_taxes',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'tax_rate_id' => array(
|
||||||
|
'description' => __( 'Tax rate ID.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'name' => array(
|
||||||
|
'description' => __( 'Tax rate name.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'tax_rate' => array(
|
||||||
|
'description' => __( 'Tax rate.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'country' => array(
|
||||||
|
'description' => __( 'Country.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'state' => array(
|
||||||
|
'description' => __( 'State.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'priority' => array(
|
||||||
|
'description' => __( 'Priority.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'total_tax' => array(
|
||||||
|
'description' => __( 'Total tax.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'order_tax' => array(
|
||||||
|
'description' => __( 'Order tax.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'shipping_tax' => array(
|
||||||
|
'description' => __( 'Shipping tax.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Amount of orders.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'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 = array();
|
||||||
|
$params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'tax_rate_id',
|
||||||
|
'enum' => array(
|
||||||
|
'name',
|
||||||
|
'tax_rate_id',
|
||||||
|
'tax_code',
|
||||||
|
'rate',
|
||||||
|
'order_tax',
|
||||||
|
'total_tax',
|
||||||
|
'shipping_tax',
|
||||||
|
'orders_count',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['taxes'] = array(
|
||||||
|
'description' => __( 'Limit result set to items assigned one or more tax rates.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,391 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports taxes stats controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/taxes/stats endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports taxes stats controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Taxes_Stats_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/taxes/stats';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
add_filter( 'woocommerce_reports_taxes_stats_select_query', array( $this, 'set_default_report_data' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default results to 0 if API returns an empty array
|
||||||
|
*
|
||||||
|
* @param Mixed $results Report data.
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function set_default_report_data( $results ) {
|
||||||
|
if ( empty( $results ) ) {
|
||||||
|
$results = new stdClass();
|
||||||
|
$results->total = 0;
|
||||||
|
$results->totals = new stdClass();
|
||||||
|
$results->totals->tax_codes = 0;
|
||||||
|
$results->totals->total_tax = 0;
|
||||||
|
$results->totals->order_tax = 0;
|
||||||
|
$results->totals->shipping_tax = 0;
|
||||||
|
$results->totals->orders = 0;
|
||||||
|
$results->intervals = array();
|
||||||
|
$results->pages = 1;
|
||||||
|
$results->page_no = 1;
|
||||||
|
}
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps query arguments from the REST request.
|
||||||
|
*
|
||||||
|
* @param array $request Request array.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function prepare_reports_query( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$args['before'] = $request['before'];
|
||||||
|
$args['after'] = $request['after'];
|
||||||
|
$args['interval'] = $request['interval'];
|
||||||
|
$args['page'] = $request['page'];
|
||||||
|
$args['per_page'] = $request['per_page'];
|
||||||
|
$args['orderby'] = $request['orderby'];
|
||||||
|
$args['order'] = $request['order'];
|
||||||
|
$args['taxes'] = (array) $request['taxes'];
|
||||||
|
$args['segmentby'] = $request['segmentby'];
|
||||||
|
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all reports.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$query_args = $this->prepare_reports_query( $request );
|
||||||
|
$taxes_query = new WC_Admin_Reports_Taxes_Stats_Query( $query_args );
|
||||||
|
$report_data = $taxes_query->get_data();
|
||||||
|
|
||||||
|
$out_data = array(
|
||||||
|
'totals' => get_object_vars( $report_data->totals ),
|
||||||
|
'intervals' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $report_data->intervals as $interval_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( (object) $interval_data, $request );
|
||||||
|
$out_data['intervals'][] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $out_data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $report_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $report_data->pages );
|
||||||
|
|
||||||
|
$page = $report_data->page_no;
|
||||||
|
$max_pages = $report_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param stdClass $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = get_object_vars( $report );
|
||||||
|
|
||||||
|
$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 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_taxes_stats', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Report's schema, conforming to JSON Schema.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_item_schema() {
|
||||||
|
$data_values = array(
|
||||||
|
'total_tax' => array(
|
||||||
|
'description' => __( 'Total tax.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'order_tax' => array(
|
||||||
|
'description' => __( 'Order tax.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'shipping_tax' => array(
|
||||||
|
'description' => __( 'Shipping tax.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'number',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'indicator' => true,
|
||||||
|
'format' => 'currency',
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'description' => __( 'Amount of orders.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'tax_codes' => array(
|
||||||
|
'description' => __( 'Amount of tax codes.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$segments = array(
|
||||||
|
'segments' => array(
|
||||||
|
'description' => __( 'Reports data grouped by segment condition.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'segment_id' => array(
|
||||||
|
'description' => __( 'Segment identificator.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $data_values,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$totals = array_merge( $data_values, $segments );
|
||||||
|
|
||||||
|
$schema = array(
|
||||||
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||||
|
'title' => 'report_taxes_stats',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'totals' => array(
|
||||||
|
'description' => __( 'Totals data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
'intervals' => array(
|
||||||
|
'description' => __( 'Reports data grouped by intervals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'interval' => array(
|
||||||
|
'description' => __( 'Type of interval.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'enum' => array( 'day', 'week', 'month', 'year' ),
|
||||||
|
),
|
||||||
|
'date_start' => array(
|
||||||
|
'description' => __( "The date the report start, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_start_gmt' => array(
|
||||||
|
'description' => __( 'The date the report start, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end' => array(
|
||||||
|
'description' => __( "The date the report end, in the site's timezone.", 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'date_end_gmt' => array(
|
||||||
|
'description' => __( 'The date the report end, as GMT.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'date-time',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
),
|
||||||
|
'subtotals' => array(
|
||||||
|
'description' => __( 'Interval subtotals.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'object',
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'readonly' => true,
|
||||||
|
'properties' => $totals,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'items_sold',
|
||||||
|
'gross_revenue',
|
||||||
|
'orders_count',
|
||||||
|
'products_count',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['interval'] = array(
|
||||||
|
'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'week',
|
||||||
|
'enum' => array(
|
||||||
|
'hour',
|
||||||
|
'day',
|
||||||
|
'week',
|
||||||
|
'month',
|
||||||
|
'quarter',
|
||||||
|
'year',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['taxes'] = array(
|
||||||
|
'description' => __( 'Limit result set to all items that have the specified term assigned in the taxes taxonomy.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['segmentby'] = array(
|
||||||
|
'description' => __( 'Segment the response by additional constraint.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => array(
|
||||||
|
'tax_rate_id',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,328 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Reports products controller
|
||||||
|
*
|
||||||
|
* Handles requests to the /reports/products endpoint.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API Reports products controller class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce/API
|
||||||
|
* @extends WC_REST_Reports_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Reports_Variations_Controller extends WC_REST_Reports_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route base.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rest_base = 'reports/variations';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping between external parameter name and name used in query class.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $param_mapping = array(
|
||||||
|
'products' => 'product_includes',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get items.
|
||||||
|
*
|
||||||
|
* @param WP_REST_Request $request Request data.
|
||||||
|
*
|
||||||
|
* @return array|WP_Error
|
||||||
|
*/
|
||||||
|
public function get_items( $request ) {
|
||||||
|
$args = array();
|
||||||
|
$registered = array_keys( $this->get_collection_params() );
|
||||||
|
foreach ( $registered as $param_name ) {
|
||||||
|
if ( isset( $request[ $param_name ] ) ) {
|
||||||
|
if ( isset( $this->param_mapping[ $param_name ] ) ) {
|
||||||
|
$args[ $this->param_mapping[ $param_name ] ] = $request[ $param_name ];
|
||||||
|
} else {
|
||||||
|
$args[ $param_name ] = $request[ $param_name ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$reports = new WC_Admin_Reports_Variations_Query( $args );
|
||||||
|
$products_data = $reports->get_data();
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
foreach ( $products_data->data as $product_data ) {
|
||||||
|
$item = $this->prepare_item_for_response( $product_data, $request );
|
||||||
|
$data[] = $this->prepare_response_for_collection( $item );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = rest_ensure_response( $data );
|
||||||
|
$response->header( 'X-WP-Total', (int) $products_data->total );
|
||||||
|
$response->header( 'X-WP-TotalPages', (int) $products_data->pages );
|
||||||
|
|
||||||
|
$page = $products_data->page_no;
|
||||||
|
$max_pages = $products_data->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a report object for serialization.
|
||||||
|
*
|
||||||
|
* @param array $report Report data.
|
||||||
|
* @param WP_REST_Request $request Request object.
|
||||||
|
* @return WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function prepare_item_for_response( $report, $request ) {
|
||||||
|
$data = $report;
|
||||||
|
|
||||||
|
$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( $report ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_variations', $response, $report, $request );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare links for the request.
|
||||||
|
*
|
||||||
|
* @param array $object Object data.
|
||||||
|
* @return array Links for the given post.
|
||||||
|
*/
|
||||||
|
protected function prepare_links( $object ) {
|
||||||
|
$links = array(
|
||||||
|
'product' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, 'products', $object['product_id'] ) ),
|
||||||
|
),
|
||||||
|
'variation' => array(
|
||||||
|
'href' => rest_url( sprintf( '/%s/%s/%d/%s/%d', $this->namespace, 'products', $object['product_id'], 'variation', $object['variation_id'] ) ),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_varitations',
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => array(
|
||||||
|
'product_id' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product ID.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'variation_id' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product ID.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'items_sold' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Number of items sold.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'net_revenue' => array(
|
||||||
|
'type' => 'number',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Total net revenue of all items sold.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'orders_count' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Number of orders product appeared in.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'extended_info' => array(
|
||||||
|
'name' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product name.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'price' => array(
|
||||||
|
'type' => 'number',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product price.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'image' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product image.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'permalink' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product link.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'attributes' => array(
|
||||||
|
'type' => 'array',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product attributes.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'stock_status' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product inventory status.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'stock_quantity' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product inventory quantity.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
'low_stock_amount' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'readonly' => true,
|
||||||
|
'context' => array( 'view', 'edit' ),
|
||||||
|
'description' => __( 'Product inventory threshold for low stock.', 'woocommerce-admin' ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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' ) );
|
||||||
|
$params['page'] = array(
|
||||||
|
'description' => __( 'Current page of the collection.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 1,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'minimum' => 1,
|
||||||
|
);
|
||||||
|
$params['per_page'] = array(
|
||||||
|
'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => 10,
|
||||||
|
'minimum' => 1,
|
||||||
|
'maximum' => 100,
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['after'] = array(
|
||||||
|
'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce-admin' ),
|
||||||
|
'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-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['order'] = array(
|
||||||
|
'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'desc',
|
||||||
|
'enum' => array( 'asc', 'desc' ),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['orderby'] = array(
|
||||||
|
'description' => __( 'Sort collection by object attribute.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'date',
|
||||||
|
'enum' => array(
|
||||||
|
'date',
|
||||||
|
'net_revenue',
|
||||||
|
'orders_count',
|
||||||
|
'items_sold',
|
||||||
|
'sku',
|
||||||
|
),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['products'] = array(
|
||||||
|
'description' => __( 'Limit result to items with specified product ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['variations'] = array(
|
||||||
|
'description' => __( 'Limit result to items with specified variation ids.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'sanitize_callback' => 'wp_parse_id_list',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$params['extended_info'] = array(
|
||||||
|
'description' => __( 'Add additional piece of info about each product to the report.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => false,
|
||||||
|
'sanitize_callback' => 'wc_string_to_bool',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Setting Options Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /settings/{option}
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting Options controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Setting_Options_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Setting_Options_Controller extends WC_REST_Setting_Options_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* REST API Taxes Controller
|
||||||
|
*
|
||||||
|
* Handles requests to /taxes/*
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taxes controller.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Admin/API
|
||||||
|
* @extends WC_REST_Taxes_Controller
|
||||||
|
*/
|
||||||
|
class WC_Admin_REST_Taxes_Controller extends WC_REST_Taxes_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'wc/v4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the query params for collections.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_collection_params() {
|
||||||
|
$params = parent::get_collection_params();
|
||||||
|
$params['code'] = array(
|
||||||
|
'description' => __( 'Search by similar tax code.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'string',
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
$params['include'] = array(
|
||||||
|
'description' => __( 'Limit result set to items that have the specified rate ID(s) assigned.', 'woocommerce-admin' ),
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
'default' => array(),
|
||||||
|
'validate_callback' => 'rest_validate_request_arg',
|
||||||
|
);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all taxes and allow filtering by tax code.
|
||||||
|
*
|
||||||
|
* @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['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'];
|
||||||
|
$prepared_args['code'] = $request['code'];
|
||||||
|
$prepared_args['include'] = $request['include'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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'";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by tax code.
|
||||||
|
$tax_code_search = $prepared_args['code'];
|
||||||
|
if ( $tax_code_search ) {
|
||||||
|
$tax_code_search = $wpdb->esc_like( $tax_code_search );
|
||||||
|
$tax_code_search = ' \'%' . $tax_code_search . '%\'';
|
||||||
|
$query .= ' AND CONCAT_WS( "-", NULLIF(tax_rate_country, ""), NULLIF(tax_rate_state, ""), NULLIF(tax_rate_name, ""), NULLIF(tax_rate_priority, "") ) LIKE ' . $tax_code_search;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by included tax rate IDs.
|
||||||
|
$included_taxes = $prepared_args['include'];
|
||||||
|
if ( ! empty( $included_taxes ) ) {
|
||||||
|
$included_taxes = implode( ',', $prepared_args['include'] );
|
||||||
|
$query .= " AND tax_rate_id IN ({$included_taxes})";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ); // @codingStandardsIgnoreLine.
|
||||||
|
|
||||||
|
$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 pagination 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 ) ); // @codingStandardsIgnoreLine.
|
||||||
|
|
||||||
|
// Calculate 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue