<?php
if (!defined('ABSPATH')) {
exit;
}
class WC_API_Resource
{
protected $server;
protected $base;
public function __construct(WC_API_Server $server)
{
$this->server = $server;
add_filter('woocommerce_api_endpoints', array($this, 'register_routes'));
foreach (array('order', 'coupon', 'customer', 'product', 'report') as $resource) {
add_filter("woocommerce_api_{$resource}_response", array($this, 'maybe_add_meta'), 15, 2);
}
$response_names = array('order', 'coupon', 'customer', 'product', 'report', 'customer_orders', 'customer_downloads', 'order_note', 'order_refund', 'product_reviews', 'product_category');
foreach ($response_names as $name) {
add_filter("woocommerce_api_{$name}_response", array($this, 'filter_response_fields'), 20, 3);
}
}
protected function validate_request($id, $type, $context)
{
if ('shop_order' === $type || 'shop_coupon' === $type || 'shop_webhook' === $type) {
$resource_name = str_replace('shop_', '', $type);
} else {
$resource_name = $type;
}
$id = absint($id);
if (empty($id)) {
return new WP_Error("woocommerce_api_invalid_{$resource_name}_id", sprintf(__('Invalid %s ID', 'woocommerce'), $type), array('status' => 404));
}
if ('customer' !== $type) {
$post = get_post($id);
if (null === $post) {
return new WP_Error("woocommerce_api_no_{$resource_name}_found", sprintf(__('No %1$s found with the ID equal to %2$s', 'woocommerce'), $resource_name, $id), array('status' => 404));
}
$post_type = 'product_variation' === $post->post_type ? 'product' : $post->post_type;
if ($type !== $post_type) {
return new WP_Error("woocommerce_api_invalid_{$resource_name}", sprintf(__('Invalid %s', 'woocommerce'), $resource_name), array('status' => 404));
}
switch ($context) {
case 'read':
if (!$this->is_readable($post)) {
return new WP_Error("woocommerce_api_user_cannot_read_{$resource_name}", sprintf(__('You do not have permission to read this %s', 'woocommerce'), $resource_name), array('status' => 401));
}
break;
case 'edit':
if (!$this->is_editable($post)) {
return new WP_Error("woocommerce_api_user_cannot_edit_{$resource_name}", sprintf(__('You do not have permission to edit this %s', 'woocommerce'), $resource_name), array('status' => 401));
}
break;
case 'delete':
if (!$this->is_deletable($post)) {
return new WP_Error("woocommerce_api_user_cannot_delete_{$resource_name}", sprintf(__('You do not have permission to delete this %s', 'woocommerce'), $resource_name), array('status' => 401));
}
break;
}
}
return $id;
}
protected function merge_query_args($base_args, $request_args)
{
$args = array();
if (!empty($request_args['created_at_min']) || !empty($request_args['created_at_max']) || !empty($request_args['updated_at_min']) || !empty($request_args['updated_at_max'])) {
$args['date_query'] = array();
if (!empty($request_args['created_at_min'])) {
$args['date_query'][] = array('column' => 'post_date_gmt', 'after' => $this->server->parse_datetime($request_args['created_at_min']), 'inclusive' => true);
}
if (!empty($request_args['created_at_max'])) {
$args['date_query'][] = array('column' => 'post_date_gmt', 'before' => $this->server->parse_datetime($request_args['created_at_max']), 'inclusive' => true);
}
if (!empty($request_args['updated_at_min'])) {
$args['date_query'][] = array('column' => 'post_modified_gmt', 'after' => $this->server->parse_datetime($request_args['updated_at_min']), 'inclusive' => true);
}
if (!empty($request_args['updated_at_max'])) {
$args['date_query'][] = array('column' => 'post_modified_gmt', 'before' => $this->server->parse_datetime($request_args['updated_at_max']), 'inclusive' => true);
}
}
if (!empty($request_args['q'])) {
$args['s'] = $request_args['q'];
}
if (!empty($request_args['limit'])) {
$args['posts_per_page'] = $request_args['limit'];
}
if (!empty($request_args['offset'])) {
$args['offset'] = $request_args['offset'];
}
if (!empty($request_args['order'])) {
$args['order'] = $request_args['order'];
}
if (!empty($request_args['orderby'])) {
$args['orderby'] = $request_args['orderby'];
if (!empty($request_args['orderby_meta_key'])) {
$args['meta_key'] = $request_args['orderby_meta_key'];
}
}
if (!empty($request_args['post_status'])) {
$args['post_status'] = $request_args['post_status'];
unset($request_args['post_status']);
}
if (!empty($request_args['in'])) {
$args['post__in'] = explode(',', $request_args['in']);
unset($request_args['in']);
}
if (!empty($request_args['in'])) {
$args['post__in'] = explode(',', $request_args['in']);
unset($request_args['in']);
}
$args['paged'] = isset($request_args['page']) ? absint($request_args['page']) : 1;
$args = apply_filters('woocommerce_api_query_args', $args, $request_args);
return array_merge($base_args, $args);
}
public function maybe_add_meta($data, $resource)
{
if (isset($this->server->params['GET']['filter']['meta']) && 'true' === $this->server->params['GET']['filter']['meta'] && is_object($resource)) {
if (preg_grep('/[a-z]+_meta/', array_keys($data))) {
return $data;
}
switch (get_class($resource)) {
case 'WC_Order':
$meta_name = 'order_meta';
break;
case 'WC_Coupon':
$meta_name = 'coupon_meta';
break;
case 'WP_User':
$meta_name = 'customer_meta';
break;
default:
$meta_name = 'product_meta';
break;
}
if (is_a($resource, 'WP_User')) {
$meta = (array) get_user_meta($resource->ID);
} else {
$meta = (array) get_post_meta($resource->get_id());
}
foreach ($meta as $meta_key => $meta_value) {
if (!is_protected_meta($meta_key)) {
$data[$meta_name][$meta_key] = maybe_unserialize($meta_value[0]);
}
}
}
return $data;
}
public function filter_response_fields($data, $resource, $fields)
{
if (!is_array($data) || empty($fields)) {
return $data;
}
$fields = explode(',', $fields);
$sub_fields = array();
foreach ($fields as $field) {
if (false !== strpos($field, '.')) {
list($name, $value) = explode('.', $field);
$sub_fields[$name] = $value;
}
}
foreach ($data as $data_field => $data_value) {
if (is_array($data_value) && in_array($data_field, array_keys($sub_fields))) {
foreach ($data_value as $sub_field => $sub_field_value) {
if (!in_array($sub_field, $sub_fields)) {
unset($data[$data_field][$sub_field]);
}
}
} else {
if (!in_array($data_field, $fields)) {
unset($data[$data_field]);
}
}
}
return $data;
}
protected function delete($id, $type, $force = false)
{
if ('shop_order' === $type || 'shop_coupon' === $type) {
$resource_name = str_replace('shop_', '', $type);
} else {
$resource_name = $type;
}
if ('customer' === $type) {
$result = wp_delete_user($id);
if ($result) {
return array('message' => __('Permanently deleted customer', 'woocommerce'));
} else {
return new WP_Error('woocommerce_api_cannot_delete_customer', __('The customer cannot be deleted', 'woocommerce'), array('status' => 500));
}
} else {
$result = $force ? wp_delete_post($id, true) : wp_trash_post($id);
if (!$result) {
return new WP_Error("woocommerce_api_cannot_delete_{$resource_name}", sprintf(__('This %s cannot be deleted', 'woocommerce'), $resource_name), array('status' => 500));
}
if ($force) {
return array('message' => sprintf(__('Permanently deleted %s', 'woocommerce'), $resource_name));
} else {
$this->server->send_status('202');
return array('message' => sprintf(__('Deleted %s', 'woocommerce'), $resource_name));
}
}
}
protected function is_readable($post)
{
return $this->check_permission($post, 'read');
}
protected function is_editable($post)
{
return $this->check_permission($post, 'edit');
}
protected function is_deletable($post)
{
return $this->check_permission($post, 'delete');
}
private function check_permission($post, $context)
{
if (!is_a($post, 'WP_Post')) {
$post = get_post($post);
}
if (is_null($post)) {
return false;
}
$post_type = get_post_type_object($post->post_type);
if ('read' === $context) {
return 'revision' !== $post->post_type && current_user_can($post_type->cap->read_private_posts, $post->ID);
} elseif ('edit' === $context) {
return current_user_can($post_type->cap->edit_post, $post->ID);
} elseif ('delete' === $context) {
return current_user_can($post_type->cap->delete_post, $post->ID);
} else {
return false;
}
}
}