qsdklsqkdmqskdmlqskd azoepqikdomlqskdjmlqs qpfklqjsfmklqsjdmqlsjdsqd PKo5\agox #woocommerce-rest-api/src/Server.phpnu[get_rest_namespaces() as $namespace => $controllers ) { foreach ( $controllers as $controller_name => $controller_class ) { $this->controllers[ $namespace ][ $controller_name ] = new $controller_class(); $this->controllers[ $namespace ][ $controller_name ]->register_routes(); } } } /** * Get API namespaces - new namespaces should be registered here. * * @return array List of Namespaces and Main controller classes. */ protected function get_rest_namespaces() { return apply_filters( 'woocommerce_rest_api_get_rest_namespaces', [ 'wc/v1' => $this->get_v1_controllers(), 'wc/v2' => $this->get_v2_controllers(), 'wc/v3' => $this->get_v3_controllers(), ] ); } /** * List of controllers in the wc/v1 namespace. * * @return array */ protected function get_v1_controllers() { return [ 'coupons' => 'WC_REST_Coupons_V1_Controller', 'customer-downloads' => 'WC_REST_Customer_Downloads_V1_Controller', 'customers' => 'WC_REST_Customers_V1_Controller', 'order-notes' => 'WC_REST_Order_Notes_V1_Controller', 'order-refunds' => 'WC_REST_Order_Refunds_V1_Controller', 'orders' => 'WC_REST_Orders_V1_Controller', 'product-attribute-terms' => 'WC_REST_Product_Attribute_Terms_V1_Controller', 'product-attributes' => 'WC_REST_Product_Attributes_V1_Controller', 'product-categories' => 'WC_REST_Product_Categories_V1_Controller', 'product-reviews' => 'WC_REST_Product_Reviews_V1_Controller', 'product-shipping-classes' => 'WC_REST_Product_Shipping_Classes_V1_Controller', 'product-tags' => 'WC_REST_Product_Tags_V1_Controller', 'products' => 'WC_REST_Products_V1_Controller', 'reports-sales' => 'WC_REST_Report_Sales_V1_Controller', 'reports-top-sellers' => 'WC_REST_Report_Top_Sellers_V1_Controller', 'reports' => 'WC_REST_Reports_V1_Controller', 'tax-classes' => 'WC_REST_Tax_Classes_V1_Controller', 'taxes' => 'WC_REST_Taxes_V1_Controller', 'webhooks' => 'WC_REST_Webhooks_V1_Controller', 'webhook-deliveries' => 'WC_REST_Webhook_Deliveries_V1_Controller', ]; } /** * List of controllers in the wc/v2 namespace. * * @return array */ protected function get_v2_controllers() { return [ 'coupons' => 'WC_REST_Coupons_V2_Controller', 'customer-downloads' => 'WC_REST_Customer_Downloads_V2_Controller', 'customers' => 'WC_REST_Customers_V2_Controller', 'network-orders' => 'WC_REST_Network_Orders_V2_Controller', 'order-notes' => 'WC_REST_Order_Notes_V2_Controller', 'order-refunds' => 'WC_REST_Order_Refunds_V2_Controller', 'orders' => 'WC_REST_Orders_V2_Controller', 'product-attribute-terms' => 'WC_REST_Product_Attribute_Terms_V2_Controller', 'product-attributes' => 'WC_REST_Product_Attributes_V2_Controller', 'product-categories' => 'WC_REST_Product_Categories_V2_Controller', 'product-reviews' => 'WC_REST_Product_Reviews_V2_Controller', 'product-shipping-classes' => 'WC_REST_Product_Shipping_Classes_V2_Controller', 'product-tags' => 'WC_REST_Product_Tags_V2_Controller', 'products' => 'WC_REST_Products_V2_Controller', 'product-variations' => 'WC_REST_Product_Variations_V2_Controller', 'reports-sales' => 'WC_REST_Report_Sales_V2_Controller', 'reports-top-sellers' => 'WC_REST_Report_Top_Sellers_V2_Controller', 'reports' => 'WC_REST_Reports_V2_Controller', 'settings' => 'WC_REST_Settings_V2_Controller', 'settings-options' => 'WC_REST_Setting_Options_V2_Controller', 'shipping-zones' => 'WC_REST_Shipping_Zones_V2_Controller', 'shipping-zone-locations' => 'WC_REST_Shipping_Zone_Locations_V2_Controller', 'shipping-zone-methods' => 'WC_REST_Shipping_Zone_Methods_V2_Controller', 'tax-classes' => 'WC_REST_Tax_Classes_V2_Controller', 'taxes' => 'WC_REST_Taxes_V2_Controller', 'webhooks' => 'WC_REST_Webhooks_V2_Controller', 'webhook-deliveries' => 'WC_REST_Webhook_Deliveries_V2_Controller', 'system-status' => 'WC_REST_System_Status_V2_Controller', 'system-status-tools' => 'WC_REST_System_Status_Tools_V2_Controller', 'shipping-methods' => 'WC_REST_Shipping_Methods_V2_Controller', 'payment-gateways' => 'WC_REST_Payment_Gateways_V2_Controller', ]; } /** * List of controllers in the wc/v3 namespace. * * @return array */ protected function get_v3_controllers() { return [ 'coupons' => 'WC_REST_Coupons_Controller', 'customer-downloads' => 'WC_REST_Customer_Downloads_Controller', 'customers' => 'WC_REST_Customers_Controller', 'network-orders' => 'WC_REST_Network_Orders_Controller', 'order-notes' => 'WC_REST_Order_Notes_Controller', 'order-refunds' => 'WC_REST_Order_Refunds_Controller', 'orders' => 'WC_REST_Orders_Controller', 'product-attribute-terms' => 'WC_REST_Product_Attribute_Terms_Controller', 'product-attributes' => 'WC_REST_Product_Attributes_Controller', 'product-categories' => 'WC_REST_Product_Categories_Controller', 'product-reviews' => 'WC_REST_Product_Reviews_Controller', 'product-shipping-classes' => 'WC_REST_Product_Shipping_Classes_Controller', 'product-tags' => 'WC_REST_Product_Tags_Controller', 'products' => 'WC_REST_Products_Controller', 'product-variations' => 'WC_REST_Product_Variations_Controller', 'reports-sales' => 'WC_REST_Report_Sales_Controller', 'reports-top-sellers' => 'WC_REST_Report_Top_Sellers_Controller', 'reports-orders-totals' => 'WC_REST_Report_Orders_Totals_Controller', 'reports-products-totals' => 'WC_REST_Report_Products_Totals_Controller', 'reports-customers-totals' => 'WC_REST_Report_Customers_Totals_Controller', 'reports-coupons-totals' => 'WC_REST_Report_Coupons_Totals_Controller', 'reports-reviews-totals' => 'WC_REST_Report_Reviews_Totals_Controller', 'reports' => 'WC_REST_Reports_Controller', 'settings' => 'WC_REST_Settings_Controller', 'settings-options' => 'WC_REST_Setting_Options_Controller', 'shipping-zones' => 'WC_REST_Shipping_Zones_Controller', 'shipping-zone-locations' => 'WC_REST_Shipping_Zone_Locations_Controller', 'shipping-zone-methods' => 'WC_REST_Shipping_Zone_Methods_Controller', 'tax-classes' => 'WC_REST_Tax_Classes_Controller', 'taxes' => 'WC_REST_Taxes_Controller', 'webhooks' => 'WC_REST_Webhooks_Controller', 'system-status' => 'WC_REST_System_Status_Controller', 'system-status-tools' => 'WC_REST_System_Status_Tools_Controller', 'shipping-methods' => 'WC_REST_Shipping_Methods_Controller', 'payment-gateways' => 'WC_REST_Payment_Gateways_Controller', 'data' => 'WC_REST_Data_Controller', 'data-continents' => 'WC_REST_Data_Continents_Controller', 'data-countries' => 'WC_REST_Data_Countries_Controller', 'data-currencies' => 'WC_REST_Data_Currencies_Controller', ]; } } PKo5\p6woocommerce-rest-api/src/Utilities/ImageAttachment.phpnu[id = (int) $id; $this->object_id = (int) $object_id; } /** * Upload an attachment file. * * @throws \WC_REST_Exception REST API exceptions. * @param string $src URL to file. */ public function upload_image_from_src( $src ) { $upload = wc_rest_upload_image_from_url( esc_url_raw( $src ) ); if ( is_wp_error( $upload ) ) { if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $this->object_id, $images ) ) { throw new \WC_REST_Exception( 'woocommerce_product_image_upload_error', $upload->get_error_message(), 400 ); } else { return; } } $this->id = wc_rest_set_uploaded_image_as_attachment( $upload, $this->object_id ); if ( ! wp_attachment_is_image( $this->id ) ) { /* translators: %s: image ID */ throw new \WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $this->id ), 400 ); } } /** * Update attachment alt text. * * @param string $text Text to set. */ public function update_alt_text( $text ) { if ( ! $this->id ) { return; } update_post_meta( $this->id, '_wp_attachment_image_alt', wc_clean( $text ) ); } /** * Update attachment name. * * @param string $text Text to set. */ public function update_name( $text ) { if ( ! $this->id ) { return; } wp_update_post( array( 'ID' => $this->id, 'post_title' => $text, ) ); } } PKo5\YK5woocommerce-rest-api/src/Utilities/SingletonTrait.phpnu[init(); } /** * Return the version of the package. * * @return string */ public static function get_version() { return self::VERSION; } /** * Return the path to the package. * * @return string */ public static function get_path() { return dirname( __DIR__ ); } } PKo5\ȆkDkD[woocommerce-rest-api/src/Controllers/Version1/class-wc-rest-order-refunds-v1-controller.phpnu[/refunds endpoint. * * @author WooThemes * @category API * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * REST API Order Refunds controller class. * * @package Automattic/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[\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[\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[\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; } } PKo5\[}8}8Ywoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-order-notes-v1-controller.phpnu[/notes endpoint. * * @author WooThemes * @category API * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * REST API Order Notes controller class. * * @package Automattic/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[\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[\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[\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[\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' ) ), ); } } PKo5\Dj??Zwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-product-tags-v1-controller.phpnu[ (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 ); } } PKo5\kF!F!`woocommerce-rest-api/src/Controllers/Version1/class-wc-rest-product-categories-v1-controller.phpnu[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 ); } } PKo5\kKf_`woocommerce-rest-api/src/Controllers/Version1/class-wc-rest-customer-downloads-v1-controller.phpnu[/downloads endpoint. * * @author WooThemes * @category API * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * REST API Customers controller class. * * @package Automattic/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[\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[\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' ) ), ); } } PKo5\j~N~NUwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-coupons-v1-controller.phpnu[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[\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' ), ) ); } /** * 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 * @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() ) ); } } /** * Saves a coupon to the database. * * @since 3.0.0 * @param WP_REST_Request $request Full details about the request. * @return WP_Error|int */ protected function save_coupon( $request ) { try { $coupon = $this->prepare_item_for_database( $request ); if ( is_wp_error( $coupon ) ) { return $coupon; } $coupon->save(); return $coupon->get_id(); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * Get the Coupon's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => $this->post_type, 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Unique identifier for the object.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'code' => array( 'description' => __( 'Coupon code.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_created' => array( 'description' => __( "The date the coupon was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified' => array( 'description' => __( "The date the coupon was last modified, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'Coupon description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'discount_type' => array( 'description' => __( 'Determines the type of discount that will be applied.', 'woocommerce' ), 'type' => 'string', 'default' => 'fixed_cart', 'enum' => array_keys( wc_get_coupon_types() ), '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' ), ), 'expiry_date' => array( 'description' => __( 'UTC DateTime when the coupon expires.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'usage_count' => array( 'description' => __( 'Number of times the coupon has been used already.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'individual_use' => array( 'description' => __( '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' ), ), 'exclude_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, ), ), ); 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; } } PKo5\PEZZSwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-taxes-v1-controller.phpnu[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[\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; } } PKo5\zs&&&&`woocommerce-rest-api/src/Controllers/Version1/class-wc-rest-webhook-deliveries-v1-controller.phpnu[/deliveries endpoint. * * @author WooThemes * @category API * @package Automattic/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 Automattic/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[\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[\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[\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' ) ), ); } } PKo5\PbX%%Ywoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-tax-classes-v1-controller.phpnu[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\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 class. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function create_item( $request ) { $tax_class = WC_Tax::create_tax_class( $request['name'] ); if ( is_wp_error( $tax_class ) ) { return new WP_Error( 'woocommerce_rest_' . $tax_class->get_error_code(), $tax_class->get_error_message(), array( 'status' => 400 ) ); } $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 = WC_Tax::get_tax_class_by( 'slug', sanitize_title( $request['slug'] ) ); $deleted = WC_Tax::delete_tax_class_by( 'slug', sanitize_title( $request['slug'] ) ); if ( ! $deleted ) { return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) ); } if ( is_wp_error( $deleted ) ) { return new WP_Error( 'woocommerce_rest_' . $deleted->get_error_code(), $deleted->get_error_message(), array( 'status' => 400 ) ); } $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' ) ), ); } } PKo5\ xoJoJ`woocommerce-rest-api/src/Controllers/Version1/class-wc-rest-product-attributes-v1-controller.phpnu[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[\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' ); } } PKo5\cccVwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-products-v1-controller.phpnu[post_type}_query", array( $this, 'query_args' ), 10, 2 ); add_action( "woocommerce_rest_insert_{$this->post_type}", array( $this, 'clear_transients' ) ); } /** * Register the routes for products. */ 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[\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, 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ), 'type' => 'boolean', ), ), ), '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 post types. * * @return array */ protected function get_post_types() { return array( 'product', 'product_variation' ); } /** * Query args. * * @param array $args Request args. * @param WP_REST_Request $request Request data. * @return array */ public function query_args( $args, $request ) { // Set post_status. $args['post_status'] = $request['status']; // Taxonomy query to filter products by type, category, // tag, shipping class, and attribute. $tax_query = array(); // Map between taxonomy name and arg's key. $taxonomies = array( 'product_cat' => 'category', 'product_tag' => 'tag', 'product_shipping_class' => 'shipping_class', ); // Set tax_query for each passed arg. foreach ( $taxonomies as $taxonomy => $key ) { if ( ! empty( $request[ $key ] ) && is_array( $request[ $key ] ) ) { $request[ $key ] = array_filter( $request[ $key ] ); } if ( ! empty( $request[ $key ] ) ) { $tax_query[] = array( 'taxonomy' => $taxonomy, 'field' => 'term_id', 'terms' => $request[ $key ], ); } } // Filter product type by slug. if ( ! empty( $request['type'] ) ) { $tax_query[] = array( 'taxonomy' => 'product_type', 'field' => 'slug', 'terms' => $request['type'], ); } // Filter by attribute and term. if ( ! empty( $request['attribute'] ) && ! empty( $request['attribute_term'] ) ) { if ( in_array( $request['attribute'], wc_get_attribute_taxonomy_names(), true ) ) { $tax_query[] = array( 'taxonomy' => $request['attribute'], 'field' => 'term_id', 'terms' => $request['attribute_term'], ); } } if ( ! empty( $tax_query ) ) { $args['tax_query'] = $tax_query; } // Filter by sku. if ( ! empty( $request['sku'] ) ) { $skus = explode( ',', $request['sku'] ); // Include the current string as a SKU too. if ( 1 < count( $skus ) ) { $skus[] = $request['sku']; } $args['meta_query'] = $this->add_meta_query( $args, array( 'key' => '_sku', 'value' => $skus, 'compare' => 'IN', ) ); } // Apply all WP_Query filters again. if ( is_array( $request['filter'] ) ) { $args = array_merge( $args, $request['filter'] ); unset( $args['filter'] ); } // Force the post_type argument, since it's not a user input variable. if ( ! empty( $request['sku'] ) ) { $args['post_type'] = array( 'product', 'product_variation' ); } else { $args['post_type'] = $this->post_type; } return $args; } /** * Get the downloads for a product or product variation. * * @param WC_Product|WC_Product_Variation $product Product instance. * @return array */ protected function get_downloads( $product ) { $downloads = array(); if ( $product->is_downloadable() ) { foreach ( $product->get_downloads() as $file_id => $file ) { $downloads[] = array( 'id' => $file_id, // MD5 hash. 'name' => $file['name'], 'file' => $file['file'], ); } } return $downloads; } /** * Get taxonomy terms. * * @param WC_Product $product Product instance. * @param string $taxonomy Taxonomy slug. * @return array */ protected function get_taxonomy_terms( $product, $taxonomy = 'cat' ) { $terms = array(); foreach ( wc_get_object_terms( $product->get_id(), 'product_' . $taxonomy ) as $term ) { $terms[] = array( 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug, ); } return $terms; } /** * Get the images for a product or product variation. * * @param WC_Product|WC_Product_Variation $product Product instance. * @return array */ protected function get_images( $product ) { $images = array(); $attachment_ids = array(); // Add featured image. if ( $product->get_image_id() ) { $attachment_ids[] = $product->get_image_id(); } // Add gallery images. $attachment_ids = array_merge( $attachment_ids, $product->get_gallery_image_ids() ); // Build image data. foreach ( $attachment_ids as $position => $attachment_id ) { $attachment_post = get_post( $attachment_id ); if ( is_null( $attachment_post ) ) { continue; } $attachment = wp_get_attachment_image_src( $attachment_id, 'full' ); if ( ! is_array( $attachment ) ) { continue; } $images[] = array( 'id' => (int) $attachment_id, 'date_created' => wc_rest_prepare_date_response( $attachment_post->post_date_gmt ), 'date_modified' => wc_rest_prepare_date_response( $attachment_post->post_modified_gmt ), 'src' => current( $attachment ), 'name' => get_the_title( $attachment_id ), 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), 'position' => (int) $position, ); } // Set a placeholder image if the product has no images set. if ( empty( $images ) ) { $images[] = array( 'id' => 0, 'date_created' => wc_rest_prepare_date_response( current_time( 'mysql' ) ), // Default to now. 'date_modified' => wc_rest_prepare_date_response( current_time( 'mysql' ) ), 'src' => wc_placeholder_img_src(), 'name' => __( 'Placeholder', 'woocommerce' ), 'alt' => __( 'Placeholder', 'woocommerce' ), 'position' => 0, ); } return $images; } /** * Get attribute taxonomy label. * * @param string $name Taxonomy name. * @return string */ protected function get_attribute_taxonomy_label( $name ) { $tax = get_taxonomy( $name ); $labels = get_taxonomy_labels( $tax ); return $labels->singular_name; } /** * Get default attributes. * * @param WC_Product $product Product instance. * @return array */ protected function get_default_attributes( $product ) { $default = array(); if ( $product->is_type( 'variable' ) ) { foreach ( array_filter( (array) $product->get_default_attributes(), 'strlen' ) as $key => $value ) { if ( 0 === strpos( $key, 'pa_' ) ) { $default[] = array( 'id' => wc_attribute_taxonomy_id_by_name( $key ), 'name' => $this->get_attribute_taxonomy_label( $key ), 'option' => $value, ); } else { $default[] = array( 'id' => 0, 'name' => wc_attribute_taxonomy_slug( $key ), 'option' => $value, ); } } } return $default; } /** * Get attribute options. * * @param int $product_id Product ID. * @param array $attribute Attribute data. * @return array */ protected function get_attribute_options( $product_id, $attribute ) { if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) { return wc_get_product_terms( $product_id, $attribute['name'], array( 'fields' => 'names' ) ); } elseif ( isset( $attribute['value'] ) ) { return array_map( 'trim', explode( '|', $attribute['value'] ) ); } return array(); } /** * Get the attributes for a product or product variation. * * @param WC_Product|WC_Product_Variation $product Product instance. * @return array */ protected function get_attributes( $product ) { $attributes = array(); if ( $product->is_type( 'variation' ) ) { // Variation attributes. foreach ( $product->get_variation_attributes() as $attribute_name => $attribute ) { $name = str_replace( 'attribute_', '', $attribute_name ); if ( ! $attribute ) { continue; } // Taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_`. if ( 0 === strpos( $attribute_name, 'attribute_pa_' ) ) { $option_term = get_term_by( 'slug', $attribute, $name ); $attributes[] = array( 'id' => wc_attribute_taxonomy_id_by_name( $name ), 'name' => $this->get_attribute_taxonomy_label( $name ), 'option' => $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $attribute, ); } else { $attributes[] = array( 'id' => 0, 'name' => $name, 'option' => $attribute, ); } } } else { foreach ( $product->get_attributes() as $attribute ) { if ( $attribute['is_taxonomy'] ) { $attributes[] = array( 'id' => wc_attribute_taxonomy_id_by_name( $attribute['name'] ), 'name' => $this->get_attribute_taxonomy_label( $attribute['name'] ), 'position' => (int) $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], 'options' => $this->get_attribute_options( $product->get_id(), $attribute ), ); } else { $attributes[] = array( 'id' => 0, 'name' => $attribute['name'], 'position' => (int) $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], 'options' => $this->get_attribute_options( $product->get_id(), $attribute ), ); } } } return $attributes; } /** * Get product menu order. * * @deprecated 3.0.0 * @param WC_Product $product Product instance. * @return int */ protected function get_product_menu_order( $product ) { return $product->get_menu_order(); } /** * Get product data. * * @param WC_Product $product Product instance. * @return array */ protected function get_product_data( $product ) { $data = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'slug' => $product->get_slug(), 'permalink' => $product->get_permalink(), 'date_created' => wc_rest_prepare_date_response( $product->get_date_created() ), 'date_modified' => wc_rest_prepare_date_response( $product->get_date_modified() ), 'type' => $product->get_type(), 'status' => $product->get_status(), 'featured' => $product->is_featured(), 'catalog_visibility' => $product->get_catalog_visibility(), 'description' => wpautop( do_shortcode( $product->get_description() ) ), 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ), 'sku' => $product->get_sku(), 'price' => $product->get_price(), 'regular_price' => $product->get_regular_price(), 'sale_price' => $product->get_sale_price() ? $product->get_sale_price() : '', 'date_on_sale_from' => $product->get_date_on_sale_from() ? date( 'Y-m-d', $product->get_date_on_sale_from()->getTimestamp() ) : '', 'date_on_sale_to' => $product->get_date_on_sale_to() ? date( 'Y-m-d', $product->get_date_on_sale_to()->getTimestamp() ) : '', 'price_html' => $product->get_price_html(), 'on_sale' => $product->is_on_sale(), 'purchasable' => $product->is_purchasable(), 'total_sales' => $product->get_total_sales(), 'virtual' => $product->is_virtual(), 'downloadable' => $product->is_downloadable(), 'downloads' => $this->get_downloads( $product ), 'download_limit' => $product->get_download_limit(), 'download_expiry' => $product->get_download_expiry(), 'download_type' => 'standard', 'external_url' => $product->is_type( 'external' ) ? $product->get_product_url() : '', 'button_text' => $product->is_type( 'external' ) ? $product->get_button_text() : '', 'tax_status' => $product->get_tax_status(), 'tax_class' => $product->get_tax_class(), 'manage_stock' => $product->managing_stock(), 'stock_quantity' => $product->get_stock_quantity(), 'in_stock' => $product->is_in_stock(), 'backorders' => $product->get_backorders(), 'backorders_allowed' => $product->backorders_allowed(), 'backordered' => $product->is_on_backorder(), 'sold_individually' => $product->is_sold_individually(), 'weight' => $product->get_weight(), 'dimensions' => array( 'length' => $product->get_length(), 'width' => $product->get_width(), 'height' => $product->get_height(), ), 'shipping_required' => $product->needs_shipping(), 'shipping_taxable' => $product->is_shipping_taxable(), 'shipping_class' => $product->get_shipping_class(), 'shipping_class_id' => $product->get_shipping_class_id(), 'reviews_allowed' => $product->get_reviews_allowed(), 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), 'rating_count' => $product->get_rating_count(), 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ), 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ), 'parent_id' => $product->get_parent_id(), 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ), 'categories' => $this->get_taxonomy_terms( $product ), 'tags' => $this->get_taxonomy_terms( $product, 'tag' ), 'images' => $this->get_images( $product ), 'attributes' => $this->get_attributes( $product ), 'default_attributes' => $this->get_default_attributes( $product ), 'variations' => array(), 'grouped_products' => array(), 'menu_order' => $product->get_menu_order(), ); return $data; } /** * Get an individual variation's data. * * @param WC_Product $product Product instance. * @return array */ protected function get_variation_data( $product ) { $variations = array(); foreach ( $product->get_children() as $child_id ) { $variation = wc_get_product( $child_id ); if ( ! $variation || ! $variation->exists() ) { continue; } $variations[] = array( 'id' => $variation->get_id(), 'date_created' => wc_rest_prepare_date_response( $variation->get_date_created() ), 'date_modified' => wc_rest_prepare_date_response( $variation->get_date_modified() ), 'permalink' => $variation->get_permalink(), 'sku' => $variation->get_sku(), 'price' => $variation->get_price(), 'regular_price' => $variation->get_regular_price(), 'sale_price' => $variation->get_sale_price(), 'date_on_sale_from' => $variation->get_date_on_sale_from() ? date( 'Y-m-d', $variation->get_date_on_sale_from()->getTimestamp() ) : '', 'date_on_sale_to' => $variation->get_date_on_sale_to() ? date( 'Y-m-d', $variation->get_date_on_sale_to()->getTimestamp() ) : '', 'on_sale' => $variation->is_on_sale(), 'purchasable' => $variation->is_purchasable(), 'visible' => $variation->is_visible(), 'virtual' => $variation->is_virtual(), 'downloadable' => $variation->is_downloadable(), 'downloads' => $this->get_downloads( $variation ), 'download_limit' => '' !== $variation->get_download_limit() ? (int) $variation->get_download_limit() : -1, 'download_expiry' => '' !== $variation->get_download_expiry() ? (int) $variation->get_download_expiry() : -1, 'tax_status' => $variation->get_tax_status(), 'tax_class' => $variation->get_tax_class(), 'manage_stock' => $variation->managing_stock(), 'stock_quantity' => $variation->get_stock_quantity(), 'in_stock' => $variation->is_in_stock(), 'backorders' => $variation->get_backorders(), 'backorders_allowed' => $variation->backorders_allowed(), 'backordered' => $variation->is_on_backorder(), 'weight' => $variation->get_weight(), 'dimensions' => array( 'length' => $variation->get_length(), 'width' => $variation->get_width(), 'height' => $variation->get_height(), ), 'shipping_class' => $variation->get_shipping_class(), 'shipping_class_id' => $variation->get_shipping_class_id(), 'image' => $this->get_images( $variation ), 'attributes' => $this->get_attributes( $variation ), ); } return $variations; } /** * Prepare a single product output for response. * * @param WP_Post $post Post object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function prepare_item_for_response( $post, $request ) { $product = wc_get_product( $post ); $data = $this->get_product_data( $product ); // Add variations to variable products. if ( $product->is_type( 'variable' ) && $product->has_child() ) { $data['variations'] = $this->get_variation_data( $product ); } // Add grouped products data. if ( $product->is_type( 'grouped' ) && $product->has_child() ) { $data['grouped_products'] = $product->get_children(); } $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, $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_Product $product Product object. * @param WP_REST_Request $request Request object. * @return array Links for the given product. */ protected function prepare_links( $product, $request ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $product->get_id() ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); if ( $product->get_parent_id() ) { $links['up'] = array( 'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product->get_parent_id() ) ), ); } return $links; } /** * Prepare a single product 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; // Type is the most important part here because we need to be using the correct class and methods. if ( isset( $request['type'] ) ) { $classname = WC_Product_Factory::get_classname_from_product_type( $request['type'] ); if ( ! class_exists( $classname ) ) { $classname = 'WC_Product_Simple'; } $product = new $classname( $id ); } elseif ( isset( $request['id'] ) ) { $product = wc_get_product( $id ); } else { $product = new WC_Product_Simple(); } // Post title. if ( isset( $request['name'] ) ) { $product->set_name( wp_filter_post_kses( $request['name'] ) ); } // Post content. if ( isset( $request['description'] ) ) { $product->set_description( wp_filter_post_kses( $request['description'] ) ); } // Post excerpt. if ( isset( $request['short_description'] ) ) { $product->set_short_description( wp_filter_post_kses( $request['short_description'] ) ); } // Post status. if ( isset( $request['status'] ) ) { $product->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' ); } // Post slug. if ( isset( $request['slug'] ) ) { $product->set_slug( $request['slug'] ); } // Menu order. if ( isset( $request['menu_order'] ) ) { $product->set_menu_order( $request['menu_order'] ); } // Comment status. if ( isset( $request['reviews_allowed'] ) ) { $product->set_reviews_allowed( $request['reviews_allowed'] ); } /** * 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_Product $product 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}", $product, $request ); } /** * Create a single product. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function create_item( $request ) { if ( ! empty( $request['id'] ) ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) ); } $product_id = 0; try { $product_id = $this->save_product( $request ); $post = get_post( $product_id ); $this->update_additional_fields_for_object( $post, $request ); $this->update_post_meta_fields( $post, $request ); /** * Fires after a single item is created or updated via the REST API. * * @param WP_Post $post Post data. * @param WP_REST_Request $request Request object. * @param boolean $creating True when creating item, false when updating. */ do_action( 'woocommerce_rest_insert_product', $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; } catch ( WC_Data_Exception $e ) { $this->delete_post( $product_id ); return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { $this->delete_post( $product_id ); return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * Update a single product. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function update_item( $request ) { $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 ) ); } try { $product_id = $this->save_product( $request ); $post = get_post( $product_id ); $this->update_additional_fields_for_object( $post, $request ); $this->update_post_meta_fields( $post, $request ); /** * Fires after a single item is created or updated via the REST API. * * @param WP_Post $post Post data. * @param WP_REST_Request $request Request object. * @param boolean $creating True when creating item, false when updating. */ do_action( 'woocommerce_rest_insert_product', $post, $request, false ); $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $post, $request ); return rest_ensure_response( $response ); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * Saves a product to the database. * * @param WP_REST_Request $request Full details about the request. * @return int */ public function save_product( $request ) { $product = $this->prepare_item_for_database( $request ); return $product->save(); } /** * Save product images. * * @deprecated 3.0.0 * @param int $product_id * @param array $images * @throws WC_REST_Exception */ protected function save_product_images( $product_id, $images ) { $product = wc_get_product( $product_id ); return set_product_images( $product, $images ); } /** * Set product images. * * @throws WC_REST_Exception REST API exceptions. * @param WC_Product $product Product instance. * @param array $images Images data. * @return WC_Product */ protected function set_product_images( $product, $images ) { if ( is_array( $images ) ) { $gallery = array(); foreach ( $images as $image ) { $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; if ( 0 === $attachment_id && isset( $image['src'] ) ) { $upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) ); if ( is_wp_error( $upload ) ) { if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $product->get_id(), $images ) ) { throw new WC_REST_Exception( 'woocommerce_product_image_upload_error', $upload->get_error_message(), 400 ); } else { continue; } } $attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $product->get_id() ); } if ( ! wp_attachment_is_image( $attachment_id ) ) { throw new WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 ); } if ( isset( $image['position'] ) && 0 === absint( $image['position'] ) ) { $product->set_image_id( $attachment_id ); } else { $gallery[] = $attachment_id; } // Set the image alt if present. if ( ! empty( $image['alt'] ) ) { update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) ); } // Set the image name if present. if ( ! empty( $image['name'] ) ) { wp_update_post( array( 'ID' => $attachment_id, 'post_title' => $image['name'] ) ); } } if ( ! empty( $gallery ) ) { $product->set_gallery_image_ids( $gallery ); } } else { $product->set_image_id( '' ); $product->set_gallery_image_ids( array() ); } return $product; } /** * Save product shipping data. * * @param WC_Product $product Product instance. * @param array $data Shipping data. * @return WC_Product */ protected function save_product_shipping_data( $product, $data ) { // Virtual. if ( isset( $data['virtual'] ) && true === $data['virtual'] ) { $product->set_weight( '' ); $product->set_height( '' ); $product->set_length( '' ); $product->set_width( '' ); } else { if ( isset( $data['weight'] ) ) { $product->set_weight( $data['weight'] ); } // Height. if ( isset( $data['dimensions']['height'] ) ) { $product->set_height( $data['dimensions']['height'] ); } // Width. if ( isset( $data['dimensions']['width'] ) ) { $product->set_width( $data['dimensions']['width'] ); } // Length. if ( isset( $data['dimensions']['length'] ) ) { $product->set_length( $data['dimensions']['length'] ); } } // Shipping class. if ( isset( $data['shipping_class'] ) ) { $data_store = $product->get_data_store(); $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data['shipping_class'] ) ); $product->set_shipping_class_id( $shipping_class_id ); } return $product; } /** * Save downloadable files. * * @param WC_Product $product Product instance. * @param array $downloads Downloads data. * @param int $deprecated Deprecated since 3.0. * @return WC_Product */ protected function save_downloadable_files( $product, $downloads, $deprecated = 0 ) { if ( $deprecated ) { wc_deprecated_argument( 'variation_id', '3.0', 'save_downloadable_files() not requires a variation_id anymore.' ); } $files = array(); foreach ( $downloads as $key => $file ) { if ( empty( $file['file'] ) ) { continue; } $download = new WC_Product_Download(); $download->set_id( ! empty( $file['id'] ) ? $file['id'] : wp_generate_uuid4() ); $download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) ); $download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) ); $files[] = $download; } $product->set_downloads( $files ); return $product; } /** * Save taxonomy terms. * * @param WC_Product $product Product instance. * @param array $terms Terms data. * @param string $taxonomy Taxonomy name. * @return WC_Product */ protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) { $term_ids = wp_list_pluck( $terms, 'id' ); if ( 'cat' === $taxonomy ) { $product->set_category_ids( $term_ids ); } elseif ( 'tag' === $taxonomy ) { $product->set_tag_ids( $term_ids ); } return $product; } /** * Save default attributes. * * @since 3.0.0 * * @param WC_Product $product Product instance. * @param WP_REST_Request $request Request data. * @return WC_Product */ protected function save_default_attributes( $product, $request ) { if ( isset( $request['default_attributes'] ) && is_array( $request['default_attributes'] ) ) { $attributes = $product->get_attributes(); $default_attributes = array(); foreach ( $request['default_attributes'] as $attribute ) { $attribute_id = 0; $attribute_name = ''; // Check ID for global attributes or name for product attributes. if ( ! empty( $attribute['id'] ) ) { $attribute_id = absint( $attribute['id'] ); $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); } elseif ( ! empty( $attribute['name'] ) ) { $attribute_name = sanitize_title( $attribute['name'] ); } if ( ! $attribute_id && ! $attribute_name ) { continue; } if ( isset( $attributes[ $attribute_name ] ) ) { $_attribute = $attributes[ $attribute_name ]; if ( $_attribute['is_variation'] ) { $value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; if ( ! empty( $_attribute['is_taxonomy'] ) ) { // If dealing with a taxonomy, we need to get the slug from the name posted to the API. $term = get_term_by( 'name', $value, $attribute_name ); if ( $term && ! is_wp_error( $term ) ) { $value = $term->slug; } else { $value = sanitize_title( $value ); } } if ( $value ) { $default_attributes[ $attribute_name ] = $value; } } } } $product->set_default_attributes( $default_attributes ); } return $product; } /** * Save product meta. * * @deprecated 3.0.0 * @param WC_Product $product * @param WP_REST_Request $request * @return bool * @throws WC_REST_Exception */ protected function save_product_meta( $product, $request ) { $product = $this->set_product_meta( $product, $request ); $product->save(); return true; } /** * Set product meta. * * @throws WC_REST_Exception REST API exceptions. * @param WC_Product $product Product instance. * @param WP_REST_Request $request Request data. * @return WC_Product */ protected function set_product_meta( $product, $request ) { // Virtual. if ( isset( $request['virtual'] ) ) { $product->set_virtual( $request['virtual'] ); } // Tax status. if ( isset( $request['tax_status'] ) ) { $product->set_tax_status( $request['tax_status'] ); } // Tax Class. if ( isset( $request['tax_class'] ) ) { $product->set_tax_class( $request['tax_class'] ); } // Catalog Visibility. if ( isset( $request['catalog_visibility'] ) ) { $product->set_catalog_visibility( $request['catalog_visibility'] ); } // Purchase Note. if ( isset( $request['purchase_note'] ) ) { $product->set_purchase_note( wp_kses_post( wp_unslash( $request['purchase_note'] ) ) ); } // Featured Product. if ( isset( $request['featured'] ) ) { $product->set_featured( $request['featured'] ); } // Shipping data. $product = $this->save_product_shipping_data( $product, $request ); // SKU. if ( isset( $request['sku'] ) ) { $product->set_sku( wc_clean( $request['sku'] ) ); } // Attributes. if ( isset( $request['attributes'] ) ) { $attributes = array(); foreach ( $request['attributes'] as $attribute ) { $attribute_id = 0; $attribute_name = ''; // Check ID for global attributes or name for product attributes. if ( ! empty( $attribute['id'] ) ) { $attribute_id = absint( $attribute['id'] ); $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); } elseif ( ! empty( $attribute['name'] ) ) { $attribute_name = wc_clean( $attribute['name'] ); } if ( ! $attribute_id && ! $attribute_name ) { continue; } if ( $attribute_id ) { if ( isset( $attribute['options'] ) ) { $options = $attribute['options']; if ( ! is_array( $attribute['options'] ) ) { // Text based attributes - Posted values are term names. $options = explode( WC_DELIMITER, $options ); } $values = array_map( 'wc_sanitize_term_text_based', $options ); $values = array_filter( $values, 'strlen' ); } else { $values = array(); } if ( ! empty( $values ) ) { // Add attribute to array, but don't set values. $attribute_object = new WC_Product_Attribute(); $attribute_object->set_id( $attribute_id ); $attribute_object->set_name( $attribute_name ); $attribute_object->set_options( $values ); $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; } } elseif ( isset( $attribute['options'] ) ) { // Custom attribute - Add attribute to array and set the values. if ( is_array( $attribute['options'] ) ) { $values = $attribute['options']; } else { $values = explode( WC_DELIMITER, $attribute['options'] ); } $attribute_object = new WC_Product_Attribute(); $attribute_object->set_name( $attribute_name ); $attribute_object->set_options( $values ); $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; } } $product->set_attributes( $attributes ); } // Sales and prices. if ( in_array( $product->get_type(), array( 'variable', 'grouped' ), true ) ) { $product->set_regular_price( '' ); $product->set_sale_price( '' ); $product->set_date_on_sale_to( '' ); $product->set_date_on_sale_from( '' ); $product->set_price( '' ); } else { // Regular Price. if ( isset( $request['regular_price'] ) ) { $product->set_regular_price( $request['regular_price'] ); } // Sale Price. if ( isset( $request['sale_price'] ) ) { $product->set_sale_price( $request['sale_price'] ); } if ( isset( $request['date_on_sale_from'] ) ) { $product->set_date_on_sale_from( $request['date_on_sale_from'] ); } if ( isset( $request['date_on_sale_to'] ) ) { $product->set_date_on_sale_to( $request['date_on_sale_to'] ); } } // Product parent ID for groups. if ( isset( $request['parent_id'] ) ) { $product->set_parent_id( $request['parent_id'] ); } // Sold individually. if ( isset( $request['sold_individually'] ) ) { $product->set_sold_individually( $request['sold_individually'] ); } // Stock status. if ( isset( $request['in_stock'] ) ) { $stock_status = true === $request['in_stock'] ? 'instock' : 'outofstock'; } else { $stock_status = $product->get_stock_status(); } // Stock data. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { // Manage stock. if ( isset( $request['manage_stock'] ) ) { $product->set_manage_stock( $request['manage_stock'] ); } // Backorders. if ( isset( $request['backorders'] ) ) { $product->set_backorders( $request['backorders'] ); } if ( $product->is_type( 'grouped' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } elseif ( $product->is_type( 'external' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( 'instock' ); } elseif ( $product->get_manage_stock() ) { // Stock status is always determined by children so sync later. if ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Stock quantity. if ( isset( $request['stock_quantity'] ) ) { $product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) ); } elseif ( isset( $request['inventory_delta'] ) ) { $stock_quantity = wc_stock_amount( $product->get_stock_quantity() ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); } } else { // Don't manage stock. $product->set_manage_stock( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } } elseif ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Upsells. if ( isset( $request['upsell_ids'] ) ) { $upsells = array(); $ids = $request['upsell_ids']; if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { if ( $id && $id > 0 ) { $upsells[] = $id; } } } $product->set_upsell_ids( $upsells ); } // Cross sells. if ( isset( $request['cross_sell_ids'] ) ) { $crosssells = array(); $ids = $request['cross_sell_ids']; if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { if ( $id && $id > 0 ) { $crosssells[] = $id; } } } $product->set_cross_sell_ids( $crosssells ); } // Product categories. if ( isset( $request['categories'] ) && is_array( $request['categories'] ) ) { $product = $this->save_taxonomy_terms( $product, $request['categories'] ); } // Product tags. if ( isset( $request['tags'] ) && is_array( $request['tags'] ) ) { $product = $this->save_taxonomy_terms( $product, $request['tags'], 'tag' ); } // Downloadable. if ( isset( $request['downloadable'] ) ) { $product->set_downloadable( $request['downloadable'] ); } // Downloadable options. if ( $product->get_downloadable() ) { // Downloadable files. if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { $product = $this->save_downloadable_files( $product, $request['downloads'] ); } // Download limit. if ( isset( $request['download_limit'] ) ) { $product->set_download_limit( $request['download_limit'] ); } // Download expiry. if ( isset( $request['download_expiry'] ) ) { $product->set_download_expiry( $request['download_expiry'] ); } } // Product url and button text for external products. if ( $product->is_type( 'external' ) ) { if ( isset( $request['external_url'] ) ) { $product->set_product_url( $request['external_url'] ); } if ( isset( $request['button_text'] ) ) { $product->set_button_text( $request['button_text'] ); } } // Save default attributes for variable products. if ( $product->is_type( 'variable' ) ) { $product = $this->save_default_attributes( $product, $request ); } return $product; } /** * Save variations. * * @throws WC_REST_Exception REST API exceptions. * @param WC_Product $product Product instance. * @param WP_REST_Request $request Request data. * @return bool */ protected function save_variations_data( $product, $request ) { foreach ( $request['variations'] as $menu_order => $data ) { $variation = new WC_Product_Variation( isset( $data['id'] ) ? absint( $data['id'] ) : 0 ); // Create initial name and status. if ( ! $variation->get_slug() ) { /* translators: 1: variation id 2: product name */ $variation->set_name( sprintf( __( 'Variation #%1$s of %2$s', 'woocommerce' ), $variation->get_id(), $product->get_name() ) ); $variation->set_status( isset( $data['visible'] ) && false === $data['visible'] ? 'private' : 'publish' ); } // Parent ID. $variation->set_parent_id( $product->get_id() ); // Menu order. $variation->set_menu_order( $menu_order ); // Status. if ( isset( $data['visible'] ) ) { $variation->set_status( false === $data['visible'] ? 'private' : 'publish' ); } // SKU. if ( isset( $data['sku'] ) ) { $variation->set_sku( wc_clean( $data['sku'] ) ); } // Thumbnail. if ( isset( $data['image'] ) && is_array( $data['image'] ) ) { $image = $data['image']; $image = current( $image ); if ( is_array( $image ) ) { $image['position'] = 0; } $variation = $this->set_product_images( $variation, array( $image ) ); } // Virtual variation. if ( isset( $data['virtual'] ) ) { $variation->set_virtual( $data['virtual'] ); } // Downloadable variation. if ( isset( $data['downloadable'] ) ) { $variation->set_downloadable( $data['downloadable'] ); } // Downloads. if ( $variation->get_downloadable() ) { // Downloadable files. if ( isset( $data['downloads'] ) && is_array( $data['downloads'] ) ) { $variation = $this->save_downloadable_files( $variation, $data['downloads'] ); } // Download limit. if ( isset( $data['download_limit'] ) ) { $variation->set_download_limit( $data['download_limit'] ); } // Download expiry. if ( isset( $data['download_expiry'] ) ) { $variation->set_download_expiry( $data['download_expiry'] ); } } // Shipping data. $variation = $this->save_product_shipping_data( $variation, $data ); // Stock handling. if ( isset( $data['manage_stock'] ) ) { $variation->set_manage_stock( $data['manage_stock'] ); } if ( isset( $data['in_stock'] ) ) { $variation->set_stock_status( true === $data['in_stock'] ? 'instock' : 'outofstock' ); } if ( isset( $data['backorders'] ) ) { $variation->set_backorders( $data['backorders'] ); } if ( $variation->get_manage_stock() ) { if ( isset( $data['stock_quantity'] ) ) { $variation->set_stock_quantity( $data['stock_quantity'] ); } elseif ( isset( $data['inventory_delta'] ) ) { $stock_quantity = wc_stock_amount( $variation->get_stock_quantity() ); $stock_quantity += wc_stock_amount( $data['inventory_delta'] ); $variation->set_stock_quantity( $stock_quantity ); } } else { $variation->set_backorders( 'no' ); $variation->set_stock_quantity( '' ); } // Regular Price. if ( isset( $data['regular_price'] ) ) { $variation->set_regular_price( $data['regular_price'] ); } // Sale Price. if ( isset( $data['sale_price'] ) ) { $variation->set_sale_price( $data['sale_price'] ); } if ( isset( $data['date_on_sale_from'] ) ) { $variation->set_date_on_sale_from( $data['date_on_sale_from'] ); } if ( isset( $data['date_on_sale_to'] ) ) { $variation->set_date_on_sale_to( $data['date_on_sale_to'] ); } // Tax class. if ( isset( $data['tax_class'] ) ) { $variation->set_tax_class( $data['tax_class'] ); } // Description. if ( isset( $data['description'] ) ) { $variation->set_description( wp_kses_post( $data['description'] ) ); } // Update taxonomies. if ( isset( $data['attributes'] ) ) { $attributes = array(); $parent_attributes = $product->get_attributes(); foreach ( $data['attributes'] as $attribute ) { $attribute_id = 0; $attribute_name = ''; // Check ID for global attributes or name for product attributes. if ( ! empty( $attribute['id'] ) ) { $attribute_id = absint( $attribute['id'] ); $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); } elseif ( ! empty( $attribute['name'] ) ) { $attribute_name = sanitize_title( $attribute['name'] ); } if ( ! $attribute_id && ! $attribute_name ) { continue; } if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) { continue; } $attribute_key = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() ); $attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) { // If dealing with a taxonomy, we need to get the slug from the name posted to the API. $term = get_term_by( 'name', $attribute_value, $attribute_name ); if ( $term && ! is_wp_error( $term ) ) { $attribute_value = $term->slug; } else { $attribute_value = sanitize_title( $attribute_value ); } } $attributes[ $attribute_key ] = $attribute_value; } $variation->set_attributes( $attributes ); } $variation->save(); do_action( 'woocommerce_rest_save_product_variation', $variation->get_id(), $menu_order, $data ); } return true; } /** * Add post meta fields. * * @param WP_Post $post Post data. * @param WP_REST_Request $request Request data. * @return bool|WP_Error */ protected function add_post_meta_fields( $post, $request ) { return $this->update_post_meta_fields( $post, $request ); } /** * Update post meta fields. * * @param WP_Post $post Post data. * @param WP_REST_Request $request Request data. * @return bool|WP_Error */ protected function update_post_meta_fields( $post, $request ) { $product = wc_get_product( $post ); // Check for featured/gallery images, upload it and set it. if ( isset( $request['images'] ) ) { $product = $this->set_product_images( $product, $request['images'] ); } // Save product meta fields. $product = $this->set_product_meta( $product, $request ); // Save the product data. $product->save(); // Save variations. if ( $product->is_type( 'variable' ) ) { if ( isset( $request['variations'] ) && is_array( $request['variations'] ) ) { $this->save_variations_data( $product, $request ); } } // Clear caches here so in sync with any new variations/children. wc_delete_product_transients( $product->get_id() ); wp_cache_delete( 'product-' . $product->get_id(), 'products' ); return true; } /** * Clear cache/transients. * * @param WP_Post $post Post data. */ public function clear_transients( $post ) { wc_delete_product_transients( $post->ID ); } /** * Delete post. * * @param int|WP_Post $id Post ID or WP_Post instance. */ protected function delete_post( $id ) { if ( ! empty( $id->ID ) ) { $id = $id->ID; } elseif ( ! is_numeric( $id ) || 0 >= $id ) { return; } // Delete product attachments. $attachments = get_posts( array( 'post_parent' => $id, 'post_status' => 'any', 'post_type' => 'attachment', ) ); foreach ( (array) $attachments as $attachment ) { wp_delete_attachment( $attachment->ID, true ); } // Delete product. $product = wc_get_product( $id ); $product->delete( true ); } /** * Delete a single item. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error */ public function delete_item( $request ) { $id = (int) $request['id']; $force = (bool) $request['force']; $post = get_post( $id ); $product = wc_get_product( $id ); if ( ! empty( $post->post_type ) && 'product_variation' === $post->post_type && 'product' === $this->post_type ) { return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404 ) ); } elseif ( empty( $id ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid post ID.', 'woocommerce' ), array( 'status' => 404 ) ); } $supports_trash = EMPTY_TRASH_DAYS > 0; /** * Filter whether an item is trashable. * * Return false to disable trash support for the item. * * @param boolean $supports_trash Whether the item type support trashing. * @param WP_Post $post The Post object being considered for trashing support. */ $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_trashable", $supports_trash, $post ); if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $post->ID ) ) { /* translators: %s: post type */ return new WP_Error( "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code() ) ); } $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $post, $request ); // If we're forcing, then delete permanently. if ( $force ) { if ( $product->is_type( 'variable' ) ) { foreach ( $product->get_children() as $child_id ) { $child = wc_get_product( $child_id ); if ( ! empty( $child ) ) { $child->delete( true ); } } } else { // For other product types, if the product has children, remove the relationship. foreach ( $product->get_children() as $child_id ) { $child = wc_get_product( $child_id ); if ( ! empty( $child ) ) { $child->set_parent_id( 0 ); $child->save(); } } } $product->delete( true ); $result = ! ( $product->get_id() > 0 ); } else { // If we don't support trashing for this type, error out. if ( ! $supports_trash ) { /* translators: %s: post type */ return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501 ) ); } // Otherwise, only trash if we haven't already. if ( 'trash' === $post->post_status ) { /* translators: %s: post type */ return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410 ) ); } // (Note that internally this falls through to `wp_delete_post` if // the trash is disabled.) $product->delete(); $result = 'trash' === $product->get_status(); } 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 ) ); } // Delete parent product transients. if ( $parent_id = wp_get_post_parent_id( $id ) ) { wc_delete_product_transients( $parent_id ); } /** * Fires after a single item is deleted or trashed via the REST API. * * @param object $post The deleted or trashed item. * @param WP_REST_Response $response The response data. * @param WP_REST_Request $request The request sent to the API. */ do_action( "woocommerce_rest_delete_{$this->post_type}", $post, $response, $request ); return $response; } /** * Get the Product's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $weight_unit = get_option( 'woocommerce_weight_unit' ); $dimension_unit = get_option( 'woocommerce_dimension_unit' ); $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, ), 'name' => array( 'description' => __( 'Product name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'slug' => array( 'description' => __( 'Product slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'permalink' => array( 'description' => __( 'Product URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created' => array( 'description' => __( "The date the product was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified' => array( 'description' => __( "The date the product was last modified, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'type' => array( 'description' => __( 'Product type.', 'woocommerce' ), 'type' => 'string', 'default' => 'simple', 'enum' => array_keys( wc_get_product_types() ), 'context' => array( 'view', 'edit' ), ), 'status' => array( 'description' => __( 'Product status (post status).', 'woocommerce' ), 'type' => 'string', 'default' => 'publish', 'enum' => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ), 'context' => array( 'view', 'edit' ), ), 'featured' => array( 'description' => __( 'Featured product.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'catalog_visibility' => array( 'description' => __( 'Catalog visibility.', 'woocommerce' ), 'type' => 'string', 'default' => 'visible', 'enum' => array( 'visible', 'catalog', 'search', 'hidden' ), 'context' => array( 'view', 'edit' ), ), 'description' => array( 'description' => __( 'Product description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'short_description' => array( 'description' => __( 'Product short description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sku' => array( 'description' => __( 'Unique identifier.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'price' => array( 'description' => __( 'Current product price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'regular_price' => array( 'description' => __( 'Product regular price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sale_price' => array( 'description' => __( 'Product sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from' => array( 'description' => __( 'Start date of sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to' => array( 'description' => __( 'End date of sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'price_html' => array( 'description' => __( 'Price formatted in HTML.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'on_sale' => array( 'description' => __( 'Shows if the product is on sale.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'purchasable' => array( 'description' => __( 'Shows if the product can be bought.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total_sales' => array( 'description' => __( 'Amount of sales.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'virtual' => array( 'description' => __( 'If the product is virtual.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloadable' => array( 'description' => __( 'If the product is downloadable.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloads' => array( 'description' => __( 'List of downloadable files.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'File ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'File name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'file' => array( 'description' => __( 'File URL.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'download_limit' => array( 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'download_expiry' => array( 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'download_type' => array( 'description' => __( 'Download type, this controls the schema on the front-end.', 'woocommerce' ), 'type' => 'string', 'default' => 'standard', 'enum' => array( 'standard' ), 'context' => array( 'view', 'edit' ), ), 'external_url' => array( 'description' => __( 'Product external URL. Only for external products.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), 'button_text' => array( 'description' => __( 'Product external button text. Only for external products.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'tax_status' => array( 'description' => __( 'Tax status.', 'woocommerce' ), 'type' => 'string', 'default' => 'taxable', 'enum' => array( 'taxable', 'shipping', 'none' ), 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'manage_stock' => array( 'description' => __( 'Stock management at product level.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'stock_quantity' => array( 'description' => __( 'Stock quantity.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'in_stock' => array( 'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ), 'type' => 'boolean', 'default' => true, 'context' => array( 'view', 'edit' ), ), 'backorders' => array( 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ), 'type' => 'string', 'default' => 'no', 'enum' => array( 'no', 'notify', 'yes' ), 'context' => array( 'view', 'edit' ), ), 'backorders_allowed' => array( 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'backordered' => array( 'description' => __( 'Shows if the product is on backordered.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'sold_individually' => array( 'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'weight' => array( /* translators: %s: weight unit */ 'description' => sprintf( __( 'Product weight (%s).', 'woocommerce' ), $weight_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'dimensions' => array( 'description' => __( 'Product dimensions.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'length' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product length (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'width' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product width (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'height' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product height (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'shipping_required' => array( 'description' => __( 'Shows if the product need to be shipped.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_taxable' => array( 'description' => __( 'Shows whether or not the product shipping is taxable.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_class' => array( 'description' => __( 'Shipping class slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'shipping_class_id' => array( 'description' => __( 'Shipping class ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'reviews_allowed' => array( 'description' => __( 'Allow reviews.', 'woocommerce' ), 'type' => 'boolean', 'default' => true, 'context' => array( 'view', 'edit' ), ), 'average_rating' => array( 'description' => __( 'Reviews average rating.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'rating_count' => array( 'description' => __( 'Amount of reviews that the product have.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'related_ids' => array( 'description' => __( 'List of related products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'upsell_ids' => array( 'description' => __( 'List of upsell products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), ), 'cross_sell_ids' => array( 'description' => __( 'List of cross-sell products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), ), 'parent_id' => array( 'description' => __( 'Product parent ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'purchase_note' => array( 'description' => __( 'Optional note to send the customer after purchase.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'categories' => array( 'description' => __( 'List of categories.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Category ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Category name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'slug' => array( 'description' => __( 'Category slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'tags' => array( 'description' => __( 'List of tags.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Tag ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Tag name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'slug' => array( 'description' => __( 'Tag slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'images' => array( 'description' => __( 'List of images.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', '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' ), ), 'name' => array( 'description' => __( 'Image name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'alt' => array( 'description' => __( 'Image alternative text.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'position' => array( 'description' => __( 'Image position. 0 means that the image is featured.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), ), ), ), 'attributes' => array( 'description' => __( 'List of attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'position' => array( 'description' => __( 'Attribute position.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'visible' => array( 'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'variation' => array( 'description' => __( 'Define if the attribute can be used as variation.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'options' => array( 'description' => __( 'List of available term names of the attribute.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), ), ), ), ), 'default_attributes' => array( 'description' => __( 'Defaults variation attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'option' => array( 'description' => __( 'Selected attribute term name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'variations' => array( 'description' => __( 'List of variations.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Variation ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created' => array( 'description' => __( "The date the variation was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified' => array( 'description' => __( "The date the variation was last modified, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'permalink' => array( 'description' => __( 'Variation URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'sku' => array( 'description' => __( 'Unique identifier.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'price' => array( 'description' => __( 'Current variation price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'regular_price' => array( 'description' => __( 'Variation regular price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sale_price' => array( 'description' => __( 'Variation sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from' => array( 'description' => __( 'Start date of sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to' => array( 'description' => __( 'End date of sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'on_sale' => array( 'description' => __( 'Shows if the variation is on sale.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'purchasable' => array( 'description' => __( 'Shows if the variation can be bought.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'visible' => array( 'description' => __( 'If the variation is visible.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), ), 'virtual' => array( 'description' => __( 'If the variation is virtual.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloadable' => array( 'description' => __( 'If the variation is downloadable.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloads' => array( 'description' => __( 'List of downloadable files.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'File ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'File name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'file' => array( 'description' => __( 'File URL.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'download_limit' => array( 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ), 'type' => 'integer', 'default' => null, 'context' => array( 'view', 'edit' ), ), 'download_expiry' => array( 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ), 'type' => 'integer', 'default' => null, 'context' => array( 'view', 'edit' ), ), 'tax_status' => array( 'description' => __( 'Tax status.', 'woocommerce' ), 'type' => 'string', 'default' => 'taxable', 'enum' => array( 'taxable', 'shipping', 'none' ), 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'manage_stock' => array( 'description' => __( 'Stock management at variation level.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'stock_quantity' => array( 'description' => __( 'Stock quantity.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'in_stock' => array( 'description' => __( 'Controls whether or not the variation is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ), 'type' => 'boolean', 'default' => true, 'context' => array( 'view', 'edit' ), ), 'backorders' => array( 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ), 'type' => 'string', 'default' => 'no', 'enum' => array( 'no', 'notify', 'yes' ), 'context' => array( 'view', 'edit' ), ), 'backorders_allowed' => array( 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'backordered' => array( 'description' => __( 'Shows if the variation is on backordered.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'weight' => array( /* translators: %s: weight unit */ 'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), $weight_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'dimensions' => array( 'description' => __( 'Variation dimensions.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'length' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation length (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'width' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation width (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'height' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation height (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'shipping_class' => array( 'description' => __( 'Shipping class slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'shipping_class_id' => array( 'description' => __( 'Shipping class ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'image' => array( 'description' => __( 'Variation 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' ), ), 'name' => array( 'description' => __( 'Image name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'alt' => array( 'description' => __( 'Image alternative text.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'position' => array( 'description' => __( 'Image position. 0 means that the image is featured.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), ), ), 'attributes' => array( 'description' => __( 'List of attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'option' => array( 'description' => __( 'Selected attribute term name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), ), ), ), 'grouped_products' => array( 'description' => __( 'List of grouped products ID.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'menu_order' => array( 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ), 'type' => 'integer', '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['slug'] = array( 'description' => __( 'Limit result set to products with a specific slug.', 'woocommerce' ), 'type' => 'string', 'validate_callback' => 'rest_validate_request_arg', ); $params['status'] = array( 'default' => 'any', 'description' => __( 'Limit result set to products assigned a specific status.', 'woocommerce' ), 'type' => 'string', 'enum' => array_merge( array( 'any', 'future' ), array_keys( get_post_statuses() ) ), 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); $params['type'] = array( 'description' => __( 'Limit result set to products assigned a specific type.', 'woocommerce' ), 'type' => 'string', 'enum' => array_keys( wc_get_product_types() ), 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); $params['category'] = array( 'description' => __( 'Limit result set to products assigned a specific category ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); $params['tag'] = array( 'description' => __( 'Limit result set to products assigned a specific tag ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); $params['shipping_class'] = array( 'description' => __( 'Limit result set to products assigned a specific shipping class ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); $params['attribute'] = array( 'description' => __( 'Limit result set to products with a specific attribute.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); $params['attribute_term'] = array( 'description' => __( 'Limit result set to products with a specific attribute term ID (required an assigned attribute).', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); $params['sku'] = array( 'description' => __( 'Limit result set to products with a specific SKU.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); return $params; } } PKo5\1+ggVwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-webhooks-v1-controller.phpnu[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[\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; } } PKo5\D`woocommerce-rest-api/src/Controllers/Version1/class-wc-rest-report-top-sellers-v1-controller.phpnu[ $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 ); } } PKo5\捝,Uwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-reports-v1-controller.phpnu[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' ) ), ); } } PKo5\w||Wwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-customers-v1-controller.phpnu[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[\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; } } PKo5\Ufwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-product-shipping-classes-v1-controller.phpnu[ (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 ); } } PKo5\z\11Zwoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-report-sales-v1-controller.phpnu[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', ), ); } } PKo5\CG?eeewoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-product-attribute-terms-v1-controller.phpnu[/terms endpoint. * * @author WooThemes * @category API * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * REST API Product Attribute Terms controller class. * * @package Automattic/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[\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[\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 ); } } PKo5\D++Twoocommerce-rest-api/src/Controllers/Version1/class-wc-rest-orders-v1-controller.phpnu[post_type}_query", array( $this, 'query_args' ), 10, 2 ); } /** * Register the routes for orders. */ 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[\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' ), ) ); } /** * Prepare a single order 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 ) { $order = wc_get_order( $post ); $dp = is_null( $request['dp'] ) ? wc_get_price_decimals() : absint( $request['dp'] ); $data = array( 'id' => $order->get_id(), 'parent_id' => $order->get_parent_id(), 'status' => $order->get_status(), 'order_key' => $order->get_order_key(), 'number' => $order->get_order_number(), 'currency' => $order->get_currency(), 'version' => $order->get_version(), 'prices_include_tax' => $order->get_prices_include_tax(), 'date_created' => wc_rest_prepare_date_response( $order->get_date_created() ), // v1 API used UTC. 'date_modified' => wc_rest_prepare_date_response( $order->get_date_modified() ), // v1 API used UTC. 'customer_id' => $order->get_customer_id(), 'discount_total' => wc_format_decimal( $order->get_total_discount(), $dp ), 'discount_tax' => wc_format_decimal( $order->get_discount_tax(), $dp ), 'shipping_total' => wc_format_decimal( $order->get_shipping_total(), $dp ), 'shipping_tax' => wc_format_decimal( $order->get_shipping_tax(), $dp ), 'cart_tax' => wc_format_decimal( $order->get_cart_tax(), $dp ), 'total' => wc_format_decimal( $order->get_total(), $dp ), 'total_tax' => wc_format_decimal( $order->get_total_tax(), $dp ), 'billing' => array(), 'shipping' => array(), 'payment_method' => $order->get_payment_method(), 'payment_method_title' => $order->get_payment_method_title(), 'transaction_id' => $order->get_transaction_id(), 'customer_ip_address' => $order->get_customer_ip_address(), 'customer_user_agent' => $order->get_customer_user_agent(), 'created_via' => $order->get_created_via(), 'customer_note' => $order->get_customer_note(), 'date_completed' => wc_rest_prepare_date_response( $order->get_date_completed(), false ), // v1 API used local time. 'date_paid' => wc_rest_prepare_date_response( $order->get_date_paid(), false ), // v1 API used local time. 'cart_hash' => $order->get_cart_hash(), 'line_items' => array(), 'tax_lines' => array(), 'shipping_lines' => array(), 'fee_lines' => array(), 'coupon_lines' => array(), 'refunds' => array(), ); // Add addresses. $data['billing'] = $order->get_address( 'billing' ); $data['shipping'] = $order->get_address( 'shipping' ); // Add line items. foreach ( $order->get_items() as $item_id => $item ) { $product = $order->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( $order->get_item_total( $item, false, false ), $dp ), 'subtotal' => wc_format_decimal( $order->get_line_subtotal( $item, false, false ), $dp ), 'subtotal_tax' => wc_format_decimal( $item['line_subtotal_tax'], $dp ), 'total' => wc_format_decimal( $order->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; } // Add taxes. foreach ( $order->get_items( 'tax' ) as $key => $tax ) { $tax_line = array( 'id' => $key, 'rate_code' => $tax['name'], 'rate_id' => $tax['rate_id'], 'label' => isset( $tax['label'] ) ? $tax['label'] : $tax['name'], 'compound' => (bool) $tax['compound'], 'tax_total' => wc_format_decimal( $tax['tax_amount'], $dp ), 'shipping_tax_total' => wc_format_decimal( $tax['shipping_tax_amount'], $dp ), ); $data['tax_lines'][] = $tax_line; } // Add shipping. foreach ( $order->get_shipping_methods() as $shipping_item_id => $shipping_item ) { $shipping_line = array( 'id' => $shipping_item_id, 'method_title' => $shipping_item['name'], 'method_id' => $shipping_item['method_id'], 'total' => wc_format_decimal( $shipping_item['cost'], $dp ), 'total_tax' => wc_format_decimal( '', $dp ), 'taxes' => array(), ); $shipping_taxes = $shipping_item->get_taxes(); if ( ! empty( $shipping_taxes['total'] ) ) { $shipping_line['total_tax'] = wc_format_decimal( array_sum( $shipping_taxes['total'] ), $dp ); foreach ( $shipping_taxes['total'] as $tax_rate_id => $tax ) { $shipping_line['taxes'][] = array( 'id' => $tax_rate_id, 'total' => $tax, ); } } $data['shipping_lines'][] = $shipping_line; } // Add fees. foreach ( $order->get_fees() as $fee_item_id => $fee_item ) { $fee_line = array( 'id' => $fee_item_id, 'name' => $fee_item['name'], 'tax_class' => ! empty( $fee_item['tax_class'] ) ? $fee_item['tax_class'] : '', 'tax_status' => 'taxable', 'total' => wc_format_decimal( $order->get_line_total( $fee_item ), $dp ), 'total_tax' => wc_format_decimal( $order->get_line_tax( $fee_item ), $dp ), 'taxes' => array(), ); $fee_line_taxes = maybe_unserialize( $fee_item['line_tax_data'] ); if ( isset( $fee_line_taxes['total'] ) ) { $fee_tax = array(); foreach ( $fee_line_taxes['total'] as $tax_rate_id => $tax ) { $fee_tax[ $tax_rate_id ] = array( 'id' => $tax_rate_id, 'total' => $tax, 'subtotal' => '', ); } if ( isset( $fee_line_taxes['subtotal'] ) ) { foreach ( $fee_line_taxes['subtotal'] as $tax_rate_id => $tax ) { $fee_tax[ $tax_rate_id ]['subtotal'] = $tax; } } $fee_line['taxes'] = array_values( $fee_tax ); } $data['fee_lines'][] = $fee_line; } // Add coupons. foreach ( $order->get_items( 'coupon' ) as $coupon_item_id => $coupon_item ) { $coupon_line = array( 'id' => $coupon_item_id, 'code' => $coupon_item['name'], 'discount' => wc_format_decimal( $coupon_item['discount_amount'], $dp ), 'discount_tax' => wc_format_decimal( $coupon_item['discount_amount_tax'], $dp ), ); $data['coupon_lines'][] = $coupon_line; } // Add refunds. foreach ( $order->get_refunds() as $refund ) { $data['refunds'][] = array( 'id' => $refund->get_id(), 'refund' => $refund->get_reason() ? $refund->get_reason() : '', 'total' => '-' . wc_format_decimal( $refund->get_amount(), $dp ), ); } $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( $order, $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 $order Order object. * @param WP_REST_Request $request Request object. * @return array Links for the given order. */ protected function prepare_links( $order, $request ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $order->get_id() ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); if ( 0 !== (int) $order->get_user_id() ) { $links['customer'] = array( 'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $order->get_user_id() ) ), ); } if ( 0 !== (int) $order->get_parent_id() ) { $links['up'] = array( 'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $order->get_parent_id() ) ), ); } return $links; } /** * Query args. * * @param array $args * @param WP_REST_Request $request * @return array */ public function query_args( $args, $request ) { global $wpdb; // Set post_status. if ( 'any' !== $request['status'] ) { $args['post_status'] = 'wc-' . $request['status']; } else { $args['post_status'] = 'any'; } if ( isset( $request['customer'] ) ) { if ( ! empty( $args['meta_query'] ) ) { $args['meta_query'] = array(); } $args['meta_query'][] = array( 'key' => '_customer_user', 'value' => $request['customer'], 'type' => 'NUMERIC', ); } // Search by product. if ( ! empty( $request['product'] ) ) { $order_ids = $wpdb->get_col( $wpdb->prepare( " SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d ) AND order_item_type = 'line_item' ", $request['product'] ) ); // Force WP_Query return empty if don't found any order. $order_ids = ! empty( $order_ids ) ? $order_ids : array( 0 ); $args['post__in'] = $order_ids; } // Search. if ( ! empty( $args['s'] ) ) { $order_ids = wc_order_search( $args['s'] ); if ( ! empty( $order_ids ) ) { unset( $args['s'] ); $args['post__in'] = array_merge( $order_ids, array( 0 ) ); } } return $args; } /** * Prepare a single order for create. * * @param WP_REST_Request $request Request object. * @return WP_Error|WC_Order $data Object. */ protected function prepare_item_for_database( $request ) { $id = isset( $request['id'] ) ? absint( $request['id'] ) : 0; $order = new WC_Order( $id ); $schema = $this->get_item_schema(); $data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) ); // Handle all writable props foreach ( $data_keys as $key ) { $value = $request[ $key ]; if ( ! is_null( $value ) ) { switch ( $key ) { case 'billing' : case 'shipping' : $this->update_address( $order, $value, $key ); break; case 'line_items' : case 'shipping_lines' : case 'fee_lines' : case 'coupon_lines' : if ( is_array( $value ) ) { foreach ( $value as $item ) { if ( is_array( $item ) ) { if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) { $order->remove_item( $item['id'] ); } else { $this->set_item( $order, $key, $item ); } } } } break; default : if ( is_callable( array( $order, "set_{$key}" ) ) ) { $order->{"set_{$key}"}( $value ); } break; } } } /** * Filter the data for the insert. * * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being * prepared for the response. * * @param WC_Order $order The order object. * @param WP_REST_Request $request Request object. */ return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $order, $request ); } /** * Create base WC Order object. * @deprecated 3.0.0 * @param array $data * @return WC_Order */ protected function create_base_order( $data ) { return wc_create_order( $data ); } /** * Only return writable props from schema. * @param array $schema * @return bool */ protected function filter_writable_props( $schema ) { return empty( $schema['readonly'] ); } /** * Create order. * * @param WP_REST_Request $request Full details about the request. * @return int|WP_Error */ protected function create_order( $request ) { try { // Make sure customer exists. if ( ! is_null( $request['customer_id'] ) && 0 !== $request['customer_id'] && false === get_user_by( 'id', $request['customer_id'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id',__( 'Customer ID is invalid.', 'woocommerce' ), 400 ); } // Make sure customer is part of blog. if ( is_multisite() && ! is_user_member_of_blog( $request['customer_id'] ) ) { add_user_to_blog( get_current_blog_id(), $request['customer_id'], 'customer' ); } $order = $this->prepare_item_for_database( $request ); $order->set_created_via( 'rest-api' ); $order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) ); $order->calculate_totals(); $order->save(); // Handle set paid. if ( true === $request['set_paid'] ) { $order->payment_complete( $request['transaction_id'] ); } return $order->get_id(); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * Update order. * * @param WP_REST_Request $request Full details about the request. * @return int|WP_Error */ protected function update_order( $request ) { try { $order = $this->prepare_item_for_database( $request ); $order->save(); // Handle set paid. if ( $order->needs_payment() && true === $request['set_paid'] ) { $order->payment_complete( $request['transaction_id'] ); } // If items have changed, recalculate order totals. if ( isset( $request['billing'] ) || isset( $request['shipping'] ) || isset( $request['line_items'] ) || isset( $request['shipping_lines'] ) || isset( $request['fee_lines'] ) || isset( $request['coupon_lines'] ) ) { $order->calculate_totals( true ); } return $order->get_id(); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * Update address. * * @param WC_Order $order * @param array $posted * @param string $type */ protected function update_address( $order, $posted, $type = 'billing' ) { foreach ( $posted as $key => $value ) { if ( is_callable( array( $order, "set_{$type}_{$key}" ) ) ) { $order->{"set_{$type}_{$key}"}( $value ); } } } /** * Gets the product ID from the SKU or posted ID. * * @throws WC_REST_Exception When SKU or ID is not valid. * @param array $posted Request data. * @param string $action 'create' to add line item or 'update' to update it. * @return int */ protected function get_product_id( $posted, $action = 'create' ) { if ( ! empty( $posted['sku'] ) ) { $product_id = (int) wc_get_product_id_by_sku( $posted['sku'] ); } elseif ( ! empty( $posted['product_id'] ) && empty( $posted['variation_id'] ) ) { $product_id = (int) $posted['product_id']; } elseif ( ! empty( $posted['variation_id'] ) ) { $product_id = (int) $posted['variation_id']; } elseif ( 'update' === $action ) { $product_id = 0; } else { throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or SKU is required.', 'woocommerce' ), 400 ); } return $product_id; } /** * Maybe set an item prop if the value was posted. * @param WC_Order_Item $item * @param string $prop * @param array $posted Request data. */ protected function maybe_set_item_prop( $item, $prop, $posted ) { if ( isset( $posted[ $prop ] ) ) { $item->{"set_$prop"}( $posted[ $prop ] ); } } /** * Maybe set item props if the values were posted. * @param WC_Order_Item $item * @param string[] $props * @param array $posted Request data. */ protected function maybe_set_item_props( $item, $props, $posted ) { foreach ( $props as $prop ) { $this->maybe_set_item_prop( $item, $prop, $posted ); } } /** * Create or update a line item. * * @param array $posted Line item data. * @param string $action 'create' to add line item or 'update' to update it. * * @return WC_Order_Item_Product * @throws WC_REST_Exception Invalid data, server error. */ protected function prepare_line_items( $posted, $action = 'create' ) { $item = new WC_Order_Item_Product( ! empty( $posted['id'] ) ? $posted['id'] : '' ); $product = wc_get_product( $this->get_product_id( $posted, $action ) ); if ( $product && $product !== $item->get_product() ) { $item->set_product( $product ); if ( 'create' === $action ) { $quantity = isset( $posted['quantity'] ) ? $posted['quantity'] : 1; $total = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) ); $item->set_total( $total ); $item->set_subtotal( $total ); } } $this->maybe_set_item_props( $item, array( 'name', 'quantity', 'total', 'subtotal', 'tax_class' ), $posted ); return $item; } /** * Create or update an order shipping method. * * @param $posted $shipping Item data. * @param string $action 'create' to add shipping or 'update' to update it. * * @return WC_Order_Item_Shipping * @throws WC_REST_Exception Invalid data, server error. */ protected function prepare_shipping_lines( $posted, $action ) { $item = new WC_Order_Item_Shipping( ! empty( $posted['id'] ) ? $posted['id'] : '' ); if ( 'create' === $action ) { if ( empty( $posted['method_id'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_item', __( 'Shipping method ID is required.', 'woocommerce' ), 400 ); } } $this->maybe_set_item_props( $item, array( 'method_id', 'method_title', 'total' ), $posted ); return $item; } /** * Create or update an order fee. * * @param array $posted Item data. * @param string $action 'create' to add fee or 'update' to update it. * * @return WC_Order_Item_Fee * @throws WC_REST_Exception Invalid data, server error. */ protected function prepare_fee_lines( $posted, $action ) { $item = new WC_Order_Item_Fee( ! empty( $posted['id'] ) ? $posted['id'] : '' ); if ( 'create' === $action ) { if ( empty( $posted['name'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee name is required.', 'woocommerce' ), 400 ); } } $this->maybe_set_item_props( $item, array( 'name', 'tax_class', 'tax_status', 'total' ), $posted ); return $item; } /** * Create or update an order coupon. * * @param array $posted Item data. * @param string $action 'create' to add coupon or 'update' to update it. * * @return WC_Order_Item_Coupon * @throws WC_REST_Exception Invalid data, server error. */ protected function prepare_coupon_lines( $posted, $action ) { $item = new WC_Order_Item_Coupon( ! empty( $posted['id'] ) ? $posted['id'] : '' ); if ( 'create' === $action ) { if ( empty( $posted['code'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 ); } } $this->maybe_set_item_props( $item, array( 'code', 'discount' ), $posted ); return $item; } /** * Wrapper method to create/update order items. * When updating, the item ID provided is checked to ensure it is associated * with the order. * * @param WC_Order $order order * @param string $item_type * @param array $posted item provided in the request body * @throws WC_REST_Exception If item ID is not associated with order */ protected function set_item( $order, $item_type, $posted ) { global $wpdb; if ( ! empty( $posted['id'] ) ) { $action = 'update'; } else { $action = 'create'; } $method = 'prepare_' . $item_type; // Verify provided line item ID is associated with order. if ( 'update' === $action ) { $result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d AND order_id = %d", absint( $posted['id'] ), absint( $order->get_id() ) ) ); if ( is_null( $result ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID provided is not associated with order.', 'woocommerce' ), 400 ); } } // Prepare item data $item = $this->$method( $posted, $action ); /** * Action hook to adjust item before save. * @since 3.0.0 */ do_action( 'woocommerce_rest_set_order_item', $item, $posted ); // Save or add to order if ( 'create' === $action ) { $order->add_item( $item ); } else { $item->save(); } } /** * Helper method to check if the resource ID associated with the provided item is null. * Items can be deleted by setting the resource ID to null. * * @param array $item Item provided in the request body. * @return bool True if the item resource ID is null, false otherwise. */ protected function item_is_null( $item ) { $keys = array( 'product_id', 'method_id', 'method_title', 'name', 'code' ); foreach ( $keys as $key ) { if ( array_key_exists( $key, $item ) && is_null( $item[ $key ] ) ) { return true; } } return false; } /** * 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_id = $this->create_order( $request ); if ( is_wp_error( $order_id ) ) { return $order_id; } $post = get_post( $order_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; } /** * Update a single order. * * @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 ) ); } $order_id = $this->update_order( $request ); if ( is_wp_error( $order_id ) ) { return $order_id; } $post = get_post( $order_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() ) ); } } /** * Get order statuses without prefixes. * @return array */ protected 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; } /** * 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, ), 'parent_id' => array( 'description' => __( 'Parent order ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'status' => array( 'description' => __( 'Order status.', 'woocommerce' ), 'type' => 'string', 'default' => 'pending', 'enum' => $this->get_order_statuses(), 'context' => array( 'view', 'edit' ), ), 'order_key' => array( 'description' => __( 'Order key.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'number' => array( 'description' => __( 'Order number.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'currency' => array( 'description' => __( 'Currency the order was created with, in ISO format.', 'woocommerce' ), 'type' => 'string', 'default' => get_woocommerce_currency(), 'enum' => array_keys( get_woocommerce_currencies() ), 'context' => array( 'view', 'edit' ), ), 'version' => array( 'description' => __( 'Version of WooCommerce which last updated the order.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'prices_include_tax' => array( 'description' => __( 'True the prices included tax during checkout.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created' => array( 'description' => __( "The date the order was created, as GMT.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified' => array( 'description' => __( "The date the order was last modified, as GMT.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'customer_id' => array( 'description' => __( 'User ID who owns the order. 0 for guests.', 'woocommerce' ), 'type' => 'integer', 'default' => 0, 'context' => array( 'view', 'edit' ), ), 'discount_total' => array( 'description' => __( 'Total discount amount for the order.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'discount_tax' => array( 'description' => __( 'Total discount tax amount for the order.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_total' => array( 'description' => __( 'Total shipping amount for the order.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_tax' => array( 'description' => __( 'Total shipping tax amount for the order.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'cart_tax' => array( 'description' => __( 'Sum of line item taxes only.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Grand total.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total_tax' => array( 'description' => __( 'Sum of all taxes.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'billing' => array( 'description' => __( 'Billing address.', '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' => __( 'Country code in ISO 3166-1 alpha-2 format.', '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' => __( 'Shipping address.', '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' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'payment_method' => array( 'description' => __( 'Payment method ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'payment_method_title' => array( 'description' => __( 'Payment method title.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'set_paid' => array( 'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'edit' ), ), 'transaction_id' => array( 'description' => __( 'Unique transaction ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'customer_ip_address' => array( 'description' => __( "Customer's IP address.", 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'customer_user_agent' => array( 'description' => __( 'User agent of the customer.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'created_via' => array( 'description' => __( 'Shows where the order was created.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'customer_note' => array( 'description' => __( 'Note left by customer during checkout.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_completed' => array( 'description' => __( "The date the order was completed, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_paid' => array( 'description' => __( "The date the order was paid, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'cart_hash' => array( 'description' => __( 'MD5 hash of cart items to ensure orders are not modified.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'line_items' => array( 'description' => __( 'Line items data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), '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' ), ), 'variation_id' => array( 'description' => __( 'Variation ID, if applicable.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'quantity' => array( 'description' => __( 'Quantity ordered.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class of product.', 'woocommerce' ), 'type' => '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' ), ), 'subtotal_tax' => array( 'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'total' => array( 'description' => __( 'Line total (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'total_tax' => array( 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'taxes' => array( 'description' => __( 'Line 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, ), ), ), ), ), ), ), 'tax_lines' => array( 'description' => __( 'Tax lines 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, ), 'rate_code' => array( 'description' => __( 'Tax rate code.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'rate_id' => array( 'description' => __( 'Tax rate ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'label' => array( 'description' => __( 'Tax rate label.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'compound' => array( 'description' => __( 'Show if is a compound tax rate.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'tax_total' => array( 'description' => __( 'Tax total (not including shipping taxes).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_tax_total' => array( 'description' => __( 'Shipping tax total.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'shipping_lines' => array( 'description' => __( 'Shipping lines data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Item ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'method_title' => array( 'description' => __( 'Shipping method name.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'method_id' => array( 'description' => __( 'Shipping method ID.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'total' => array( 'description' => __( 'Line total (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'total_tax' => array( 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), '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, ), ), ), ), ), ), ), 'fee_lines' => array( 'description' => __( 'Fee lines data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Item ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'Fee name.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class of fee.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'tax_status' => array( 'description' => __( 'Tax status of fee.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'enum' => array( 'taxable', 'none' ), ), 'total' => array( 'description' => __( 'Line total (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'total_tax' => array( 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'taxes' => array( 'description' => __( 'Line 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, ), ), ), ), ), ), ), 'coupon_lines' => array( 'description' => __( 'Coupons line data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Item ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'code' => array( 'description' => __( 'Coupon code.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'discount' => array( 'description' => __( 'Discount total.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'discount_tax' => array( 'description' => __( 'Discount total tax.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'refunds' => array( 'description' => __( 'List of refunds.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'readonly' => true, 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Refund ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'reason' => array( 'description' => __( 'Refund reason.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Refund total.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), ), ); return $this->add_additional_fields_schema( $schema ); } /** * Get the query params for collections. * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); $params['status'] = array( 'default' => 'any', 'description' => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ), 'type' => 'string', 'enum' => array_merge( array( 'any' ), $this->get_order_statuses() ), 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); $params['customer'] = array( 'description' => __( 'Limit result set to orders assigned a specific customer.', 'woocommerce' ), 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ); $params['product'] = array( 'description' => __( 'Limit result set to orders assigned a specific product.', 'woocommerce' ), 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ); $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; } } PKo5\SOSO]woocommerce-rest-api/src/Controllers/Version1/class-wc-rest-product-reviews-v1-controller.phpnu[/reviews. * * @author WooThemes * @category API * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * REST API Product Reviews Controller Class. * * @package Automattic/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[\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[\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[\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[\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' ) ), ); } } PKo5\$<,,`woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-product-attributes-v2-controller.phpnu[namespace, '/' . $this->rest_base, array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), ), array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'create_item' ), 'permission_callback' => array( $this, 'create_item_permissions_check' ), 'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array( 'name' => array( 'required' => true, 'type' => 'string', 'description' => __( 'Shipping zone name.', 'woocommerce' ), ), ) ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( 'args' => array( 'id' => array( 'description' => __( 'Unique ID for the resource.', 'woocommerce' ), 'type' => 'integer', ), ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'update_items_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_items_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' ), ) ); } /** * Get a single Shipping Zone. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function get_item( $request ) { $zone = $this->get_zone( $request->get_param( 'id' ) ); if ( is_wp_error( $zone ) ) { return $zone; } $data = $zone->get_data(); $data = $this->prepare_item_for_response( $data, $request ); $data = $this->prepare_response_for_collection( $data ); return rest_ensure_response( $data ); } /** * Get all Shipping Zones. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response */ public function get_items( $request ) { $rest_of_the_world = WC_Shipping_Zones::get_zone_by( 'zone_id', 0 ); $zones = WC_Shipping_Zones::get_zones(); array_unshift( $zones, $rest_of_the_world->get_data() ); $data = array(); foreach ( $zones as $zone_obj ) { $zone = $this->prepare_item_for_response( $zone_obj, $request ); $zone = $this->prepare_response_for_collection( $zone ); $data[] = $zone; } return rest_ensure_response( $data ); } /** * Create a single Shipping Zone. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Request|WP_Error */ public function create_item( $request ) { $zone = new WC_Shipping_Zone( null ); if ( ! is_null( $request->get_param( 'name' ) ) ) { $zone->set_zone_name( $request->get_param( 'name' ) ); } if ( ! is_null( $request->get_param( 'order' ) ) ) { $zone->set_zone_order( $request->get_param( 'order' ) ); } $zone->save(); if ( $zone->get_id() !== 0 ) { $request->set_param( 'id', $zone->get_id() ); $response = $this->get_item( $request ); $response->set_status( 201 ); $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $zone->get_id() ) ) ); return $response; } else { return new WP_Error( 'woocommerce_rest_shipping_zone_not_created', __( "Resource cannot be created. Check to make sure 'order' and 'name' are present.", 'woocommerce' ), array( 'status' => 500 ) ); } } /** * Update a single Shipping Zone. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Request|WP_Error */ public function update_item( $request ) { $zone = $this->get_zone( $request->get_param( 'id' ) ); if ( is_wp_error( $zone ) ) { return $zone; } if ( 0 === $zone->get_id() ) { return new WP_Error( 'woocommerce_rest_shipping_zone_invalid_zone', __( 'The "locations not covered by your other zones" zone cannot be updated.', 'woocommerce' ), array( 'status' => 403 ) ); } $zone_changed = false; if ( ! is_null( $request->get_param( 'name' ) ) ) { $zone->set_zone_name( $request->get_param( 'name' ) ); $zone_changed = true; } if ( ! is_null( $request->get_param( 'order' ) ) ) { $zone->set_zone_order( $request->get_param( 'order' ) ); $zone_changed = true; } if ( $zone_changed ) { $zone->save(); } return $this->get_item( $request ); } /** * Delete a single Shipping Zone. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Request|WP_Error */ public function delete_item( $request ) { $zone = $this->get_zone( $request->get_param( 'id' ) ); if ( is_wp_error( $zone ) ) { return $zone; } $force = $request['force']; $response = $this->get_item( $request ); if ( $force ) { $zone->delete(); } else { return new WP_Error( 'rest_trash_not_supported', __( 'Shipping zones do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) ); } return $response; } /** * Prepare the Shipping Zone for the REST response. * * @param array $item Shipping Zone. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response */ public function prepare_item_for_response( $item, $request ) { $data = array( 'id' => (int) $item['id'], 'name' => $item['zone_name'], 'order' => (int) $item['zone_order'], ); $context = empty( $request['context'] ) ? 'view' : $request['context']; $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( $data['id'] ) ); return $response; } /** * Prepare links for the request. * * @param int $zone_id Given Shipping Zone ID. * @return array Links for the given Shipping Zone. */ protected function prepare_links( $zone_id ) { $base = '/' . $this->namespace . '/' . $this->rest_base; $links = array( 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $zone_id ), ), 'collection' => array( 'href' => rest_url( $base ), ), 'describedby' => array( 'href' => rest_url( trailingslashit( $base ) . $zone_id . '/locations' ), ), ); return $links; } /** * Get the Shipping Zones schema, conforming to JSON Schema * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'shipping_zone', '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 zone name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'order' => array( 'description' => __( 'Shipping zone order.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\29>Zwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-report-sales-v2-controller.phpnu[/deliveries endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Webhook Deliveries controller class. * * @deprecated 3.3.0 Webhooks deliveries logs now uses logging system. * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Webhook_Deliveries_V1_Controller */ class WC_REST_Webhook_Deliveries_V2_Controller extends WC_REST_Webhook_Deliveries_V1_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v2'; /** * 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 */ 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 ); } /** * 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, ), 'date_created_gmt' => array( 'description' => __( 'The date the webhook delivery was logged, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\B_LLawoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.phpnu[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[\w-]+)', array( 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), 'type' => 'string', ), ), 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_item_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Check whether a given request has permission to view system status tools. * * @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' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check whether a given request has permission to view a specific system status tool. * * @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 view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check whether a given request has permission to execute a specific system status tool. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function update_item_permissions_check( $request ) { if ( ! wc_rest_check_manager_permissions( 'system_status', 'edit' ) ) { return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * A list of available tools for use in the system status section. * 'button' becomes 'action' in the API. * * @return array */ public function get_tools() { $tools = array( 'clear_transients' => array( 'name' => __( 'WooCommerce transients', 'woocommerce' ), 'button' => __( 'Clear transients', 'woocommerce' ), 'desc' => __( 'This tool will clear the product/shop transients cache.', 'woocommerce' ), ), 'clear_expired_transients' => array( 'name' => __( 'Expired transients', 'woocommerce' ), 'button' => __( 'Clear transients', 'woocommerce' ), 'desc' => __( 'This tool will clear ALL expired transients from WordPress.', 'woocommerce' ), ), 'delete_orphaned_variations' => array( 'name' => __( 'Orphaned variations', 'woocommerce' ), 'button' => __( 'Delete orphaned variations', 'woocommerce' ), 'desc' => __( 'This tool will delete all variations which have no parent.', 'woocommerce' ), ), 'clear_expired_download_permissions' => array( 'name' => __( 'Used-up download permissions', 'woocommerce' ), 'button' => __( 'Clean up download permissions', 'woocommerce' ), 'desc' => __( 'This tool will delete expired download permissions and permissions with 0 remaining downloads.', 'woocommerce' ), ), 'regenerate_product_lookup_tables' => array( 'name' => __( 'Product lookup tables', 'woocommerce' ), 'button' => __( 'Regenerate', 'woocommerce' ), 'desc' => __( 'This tool will regenerate product lookup table data. This process may take a while.', 'woocommerce' ), ), 'recount_terms' => array( 'name' => __( 'Term counts', 'woocommerce' ), 'button' => __( 'Recount terms', 'woocommerce' ), 'desc' => __( 'This tool will recount product terms - useful when changing your settings in a way which hides products from the catalog.', 'woocommerce' ), ), 'reset_roles' => array( 'name' => __( 'Capabilities', 'woocommerce' ), 'button' => __( 'Reset capabilities', 'woocommerce' ), 'desc' => __( 'This tool will reset the admin, customer and shop_manager roles to default. Use this if your users cannot access all of the WooCommerce admin pages.', 'woocommerce' ), ), 'clear_sessions' => array( 'name' => __( 'Clear customer sessions', 'woocommerce' ), 'button' => __( 'Clear', 'woocommerce' ), 'desc' => sprintf( '%1$s %2$s', __( 'Note:', 'woocommerce' ), __( 'This tool will delete all customer session data from the database, including current carts and saved carts in the database.', 'woocommerce' ) ), ), 'install_pages' => array( 'name' => __( 'Create default WooCommerce pages', 'woocommerce' ), 'button' => __( 'Create pages', 'woocommerce' ), 'desc' => sprintf( '%1$s %2$s', __( 'Note:', 'woocommerce' ), __( 'This tool will install all the missing WooCommerce pages. Pages already defined and set up will not be replaced.', 'woocommerce' ) ), ), 'delete_taxes' => array( 'name' => __( 'Delete WooCommerce tax rates', 'woocommerce' ), 'button' => __( 'Delete tax rates', 'woocommerce' ), 'desc' => sprintf( '%1$s %2$s', __( 'Note:', 'woocommerce' ), __( 'This option will delete ALL of your tax rates, use with caution. This action cannot be reversed.', 'woocommerce' ) ), ), 'regenerate_thumbnails' => array( 'name' => __( 'Regenerate shop thumbnails', 'woocommerce' ), 'button' => __( 'Regenerate', 'woocommerce' ), 'desc' => __( 'This will regenerate all shop thumbnails to match your theme and/or image settings.', 'woocommerce' ), ), 'db_update_routine' => array( 'name' => __( 'Update database', 'woocommerce' ), 'button' => __( 'Update database', 'woocommerce' ), 'desc' => sprintf( '%1$s %2$s', __( 'Note:', 'woocommerce' ), __( 'This tool will update your WooCommerce database to the latest version. Please ensure you make sufficient backups before proceeding.', 'woocommerce' ) ), ), ); // Jetpack does the image resizing heavy lifting so you don't have to. if ( ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) ) || ! apply_filters( 'woocommerce_background_image_regeneration', true ) ) { unset( $tools['regenerate_thumbnails'] ); } return apply_filters( 'woocommerce_debug_tools', $tools ); } /** * Get a list of system status tools. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $tools = array(); foreach ( $this->get_tools() as $id => $tool ) { $tools[] = $this->prepare_response_for_collection( $this->prepare_item_for_response( array( 'id' => $id, 'name' => $tool['name'], 'action' => $tool['button'], 'description' => $tool['desc'], ), $request ) ); } $response = rest_ensure_response( $tools ); return $response; } /** * Return a single tool. * * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_item( $request ) { $tools = $this->get_tools(); if ( empty( $tools[ $request['id'] ] ) ) { return new WP_Error( 'woocommerce_rest_system_status_tool_invalid_id', __( 'Invalid tool ID.', 'woocommerce' ), array( 'status' => 404 ) ); } $tool = $tools[ $request['id'] ]; return rest_ensure_response( $this->prepare_item_for_response( array( 'id' => $request['id'], 'name' => $tool['name'], 'action' => $tool['button'], 'description' => $tool['desc'], ), $request ) ); } /** * Update (execute) a tool. * * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function update_item( $request ) { $tools = $this->get_tools(); if ( empty( $tools[ $request['id'] ] ) ) { return new WP_Error( 'woocommerce_rest_system_status_tool_invalid_id', __( 'Invalid tool ID.', 'woocommerce' ), array( 'status' => 404 ) ); } $tool = $tools[ $request['id'] ]; $tool = array( 'id' => $request['id'], 'name' => $tool['name'], 'action' => $tool['button'], 'description' => $tool['desc'], ); $execute_return = $this->execute_tool( $request['id'] ); $tool = array_merge( $tool, $execute_return ); /** * Fires after a WooCommerce REST system status tool has been executed. * * @param array $tool Details about the tool that has been executed. * @param WP_REST_Request $request The current WP_REST_Request object. */ do_action( 'woocommerce_rest_insert_system_status_tool', $tool, $request ); $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $tool, $request ); return rest_ensure_response( $response ); } /** * Prepare a tool item for serialization. * * @param array $item Object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response Response data. */ public function prepare_item_for_response( $item, $request ) { $context = empty( $request['context'] ) ? 'view' : $request['context']; $data = $this->add_additional_fields_to_object( $item, $request ); $data = $this->filter_response_by_context( $data, $context ); $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $item['id'] ) ); return $response; } /** * Get the system status tools schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'system_status_tool', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'A unique identifier for the tool.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_title', ), ), 'name' => array( 'description' => __( 'Tool name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'action' => array( 'description' => __( 'What running the tool will do.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'description' => array( 'description' => __( 'Tool description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'success' => array( 'description' => __( 'Did the tool run successfully?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'edit' ), ), 'message' => array( 'description' => __( 'Tool return message.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), ), ); return $this->add_additional_fields_schema( $schema ); } /** * Prepare links for the request. * * @param string $id ID. * @return array */ protected function prepare_links( $id ) { $base = '/' . $this->namespace . '/' . $this->rest_base; $links = array( 'item' => array( 'href' => rest_url( trailingslashit( $base ) . $id ), 'embeddable' => true, ), ); return $links; } /** * Get any query params needed. * * @return array */ public function get_collection_params() { return array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ); } /** * Actually executes a tool. * * @param string $tool Tool. * @return array */ public function execute_tool( $tool ) { global $wpdb; $ran = true; switch ( $tool ) { case 'clear_transients': wc_delete_product_transients(); wc_delete_shop_order_transients(); delete_transient( 'wc_count_comments' ); $attribute_taxonomies = wc_get_attribute_taxonomies(); if ( $attribute_taxonomies ) { foreach ( $attribute_taxonomies as $attribute ) { delete_transient( 'wc_layered_nav_counts_pa_' . $attribute->attribute_name ); } } WC_Cache_Helper::get_transient_version( 'shipping', true ); $message = __( 'Product transients cleared', 'woocommerce' ); break; case 'clear_expired_transients': /* translators: %d: amount of expired transients */ $message = sprintf( __( '%d transients rows cleared', 'woocommerce' ), wc_delete_expired_transients() ); break; case 'delete_orphaned_variations': // Delete orphans. $result = absint( $wpdb->query( "DELETE products FROM {$wpdb->posts} products LEFT JOIN {$wpdb->posts} wp ON wp.ID = products.post_parent WHERE wp.ID IS NULL AND products.post_type = 'product_variation';" ) ); /* translators: %d: amount of orphaned variations */ $message = sprintf( __( '%d orphaned variations deleted', 'woocommerce' ), $result ); break; case 'clear_expired_download_permissions': // Delete expired download permissions and ones with 0 downloads remaining. $result = absint( $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE ( downloads_remaining != '' AND downloads_remaining = 0 ) OR ( access_expires IS NOT NULL AND access_expires < %s )", date( 'Y-m-d', current_time( 'timestamp' ) ) ) ) ); /* translators: %d: amount of permissions */ $message = sprintf( __( '%d permissions deleted', 'woocommerce' ), $result ); break; case 'regenerate_product_lookup_tables': if ( ! wc_update_product_lookup_tables_is_running() ) { wc_update_product_lookup_tables(); } $message = __( 'Lookup tables are regenerating', 'woocommerce' ); break; case 'reset_roles': // Remove then re-add caps and roles. WC_Install::remove_roles(); WC_Install::create_roles(); $message = __( 'Roles successfully reset', 'woocommerce' ); break; case 'recount_terms': $product_cats = get_terms( 'product_cat', array( 'hide_empty' => false, 'fields' => 'id=>parent', ) ); _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), true, false ); $product_tags = get_terms( 'product_tag', array( 'hide_empty' => false, 'fields' => 'id=>parent', ) ); _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), true, false ); $message = __( 'Terms successfully recounted', 'woocommerce' ); break; case 'clear_sessions': $wpdb->query( "TRUNCATE {$wpdb->prefix}woocommerce_sessions" ); $result = absint( $wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key='_woocommerce_persistent_cart_" . get_current_blog_id() . "';" ) ); // WPCS: unprepared SQL ok. wp_cache_flush(); /* translators: %d: amount of sessions */ $message = sprintf( __( 'Deleted all active sessions, and %d saved carts.', 'woocommerce' ), absint( $result ) ); break; case 'install_pages': WC_Install::create_pages(); $message = __( 'All missing WooCommerce pages successfully installed', 'woocommerce' ); break; case 'delete_taxes': $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}woocommerce_tax_rates;" ); $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations;" ); if ( method_exists( 'WC_Cache_Helper', 'invalidate_cache_group' ) ) { WC_Cache_Helper::invalidate_cache_group( 'taxes' ); } else { WC_Cache_Helper::incr_cache_prefix( 'taxes' ); } $message = __( 'Tax rates successfully deleted', 'woocommerce' ); break; case 'regenerate_thumbnails': WC_Regenerate_Images::queue_image_regeneration(); $message = __( 'Thumbnail regeneration has been scheduled to run in the background.', 'woocommerce' ); break; case 'db_update_routine': $blog_id = get_current_blog_id(); // Used to fire an action added in WP_Background_Process::_construct() that calls WP_Background_Process::handle_cron_healthcheck(). // This method will make sure the database updates are executed even if cron is disabled. Nothing will happen if the updates are already running. do_action( 'wp_' . $blog_id . '_wc_updater_cron' ); $message = __( 'Database upgrade routine has been scheduled to run in the background.', 'woocommerce' ); break; default: $tools = $this->get_tools(); if ( isset( $tools[ $tool ]['callback'] ) ) { $callback = $tools[ $tool ]['callback']; $return = call_user_func( $callback ); if ( is_string( $return ) ) { $message = $return; } elseif ( false === $return ) { $callback_string = is_array( $callback ) ? get_class( $callback[0] ) . '::' . $callback[1] : $callback; $ran = false; /* translators: %s: callback string */ $message = sprintf( __( 'There was an error calling %s', 'woocommerce' ), $callback_string ); } else { $message = __( 'Tool ran.', 'woocommerce' ); } } else { $ran = false; $message = __( 'There was an error calling this tool. There is no callback present.', 'woocommerce' ); } break; } return array( 'success' => $ran, 'message' => $message, ); } } PKo5\Z]  Ywoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-order-notes-v2-controller.phpnu[/notes endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Order Notes controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Order_Notes_V1_Controller */ class WC_REST_Order_Notes_V2_Controller extends WC_REST_Order_Notes_V1_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v2'; /** * Get order notes from an order. * * @param WP_REST_Request $request Request data. * * @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', ); // Allow filter by order note type. if ( 'customer' === $request['type'] ) { $args['meta_query'] = array( // WPCS: slow query ok. array( 'key' => 'is_customer_note', 'value' => 1, 'compare' => '=', ), ); } elseif ( 'internal' === $request['type'] ) { $args['meta_query'] = array( // WPCS: slow query ok. array( 'key' => 'is_customer_note', 'compare' => 'NOT EXISTS', ), ); } 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 ); } /** * 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 ), 'date_created_gmt' => 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 ); } /** * 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, ), 'date_created_gmt' => array( 'description' => __( 'The date the order note was created, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'note' => array( 'description' => __( 'Order note content.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'customer_note' => array( 'description' => __( 'If true, the note will be shown to customers and they will be notified. If false, the note will be for admin reference only.', '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' ) ); $params['type'] = array( 'default' => 'any', 'description' => __( 'Limit result to customers or internal notes.', 'woocommerce' ), 'type' => 'string', 'enum' => array( 'any', 'customer', 'internal' ), 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); return $params; } } PKo5\F11Wwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-customers-v2-controller.phpnu[get_data(); $format_date = array( 'date_created', 'date_modified' ); // Format date values. foreach ( $format_date as $key ) { $datetime = 'date_created' === $key ? get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $data[ $key ]->getTimestamp() ) ) : $data[ $key ]; $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); } return array( 'id' => $object->get_id(), 'date_created' => $data['date_created'], 'date_created_gmt' => $data['date_created_gmt'], 'date_modified' => $data['date_modified'], 'date_modified_gmt' => $data['date_modified_gmt'], 'email' => $data['email'], 'first_name' => $data['first_name'], 'last_name' => $data['last_name'], 'role' => $data['role'], 'username' => $data['username'], 'billing' => $data['billing'], 'shipping' => $data['shipping'], 'is_paying_customer' => $data['is_paying_customer'], 'orders_count' => $object->get_order_count(), 'total_spent' => $object->get_total_spent(), 'avatar_url' => $object->get_avatar_url(), 'meta_data' => $data['meta_data'], ); } /** * 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 = $this->get_formatted_item_data( $customer ); $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 Customer data. * @param WP_REST_Request $request Request data. */ protected function update_customer_meta_fields( $customer, $request ) { parent::update_customer_meta_fields( $customer, $request ); // Meta data. if ( isset( $request['meta_data'] ) ) { if ( is_array( $request['meta_data'] ) ) { foreach ( $request['meta_data'] as $meta ) { $customer->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } } } } /** * Get the Customer's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'customer', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created' => array( 'description' => __( "The date the customer was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created_gmt' => 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, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified_gmt' => 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', ), ), 'role' => array( 'description' => __( 'Customer role.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), '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' ), ), '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' ), ), ), ), 'is_paying_customer' => array( 'description' => __( 'Is the customer a paying customer?', 'woocommerce' ), 'type' => 'bool', '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, ), '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 ); } } PKo5\M==^woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-payment-gateways-v2-controller.phpnu[ */ 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[\w-]+)', array( 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), 'type' => 'string', ), ), 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_items_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Check whether a given request has permission to view payment gateways. * * @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( 'payment_gateways', '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 payment gateway. * * @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( 'payment_gateways', '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 whether a given request has permission to edit payment gateways. * * @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( 'payment_gateways', '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; } /** * Get payment gateways. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $payment_gateways = WC()->payment_gateways->payment_gateways(); $response = array(); foreach ( $payment_gateways as $payment_gateway_id => $payment_gateway ) { $payment_gateway->id = $payment_gateway_id; $gateway = $this->prepare_item_for_response( $payment_gateway, $request ); $gateway = $this->prepare_response_for_collection( $gateway ); $response[] = $gateway; } return rest_ensure_response( $response ); } /** * Get a single payment gateway. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function get_item( $request ) { $gateway = $this->get_gateway( $request ); if ( is_null( $gateway ) ) { return new WP_Error( 'woocommerce_rest_payment_gateway_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); } $gateway = $this->prepare_item_for_response( $gateway, $request ); return rest_ensure_response( $gateway ); } /** * Update A Single Payment Method. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function update_item( $request ) { $gateway = $this->get_gateway( $request ); if ( is_null( $gateway ) ) { return new WP_Error( 'woocommerce_rest_payment_gateway_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); } // Get settings. $gateway->init_form_fields(); $settings = $gateway->settings; // Update settings. if ( isset( $request['settings'] ) ) { $errors_found = false; foreach ( $gateway->form_fields as $key => $field ) { if ( isset( $request['settings'][ $key ] ) ) { if ( is_callable( array( $this, 'validate_setting_' . $field['type'] . '_field' ) ) ) { $value = $this->{'validate_setting_' . $field['type'] . '_field'}( $request['settings'][ $key ], $field ); } else { $value = $this->validate_setting_text_field( $request['settings'][ $key ], $field ); } if ( is_wp_error( $value ) ) { $errors_found = true; break; } $settings[ $key ] = $value; } } if ( $errors_found ) { return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); } } // Update if this method is enabled or not. if ( isset( $request['enabled'] ) ) { $settings['enabled'] = wc_bool_to_string( $request['enabled'] ); $gateway->enabled = $settings['enabled']; } // Update title. if ( isset( $request['title'] ) ) { $settings['title'] = $request['title']; $gateway->title = $settings['title']; } // Update description. if ( isset( $request['description'] ) ) { $settings['description'] = $request['description']; $gateway->description = $settings['description']; } // Update options. $gateway->settings = $settings; update_option( $gateway->get_option_key(), apply_filters( 'woocommerce_gateway_' . $gateway->id . '_settings_values', $settings, $gateway ) ); // Update order. if ( isset( $request['order'] ) ) { $order = (array) get_option( 'woocommerce_gateway_order' ); $order[ $gateway->id ] = $request['order']; update_option( 'woocommerce_gateway_order', $order ); $gateway->order = absint( $request['order'] ); } $gateway = $this->prepare_item_for_response( $gateway, $request ); return rest_ensure_response( $gateway ); } /** * Get a gateway based on the current request object. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|null */ public function get_gateway( $request ) { $gateway = null; $payment_gateways = WC()->payment_gateways->payment_gateways(); foreach ( $payment_gateways as $payment_gateway_id => $payment_gateway ) { if ( $request['id'] !== $payment_gateway_id ) { continue; } $payment_gateway->id = $payment_gateway_id; $gateway = $payment_gateway; } return $gateway; } /** * Prepare a payment gateway for response. * * @param WC_Payment_Gateway $gateway Payment gateway object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response Response data. */ public function prepare_item_for_response( $gateway, $request ) { $order = (array) get_option( 'woocommerce_gateway_order' ); $item = array( 'id' => $gateway->id, 'title' => $gateway->title, 'description' => $gateway->description, 'order' => isset( $order[ $gateway->id ] ) ? $order[ $gateway->id ] : '', 'enabled' => ( 'yes' === $gateway->enabled ), 'method_title' => $gateway->get_method_title(), 'method_description' => $gateway->get_method_description(), 'settings' => $this->get_settings( $gateway ), ); $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $item, $request ); $data = $this->filter_response_by_context( $data, $context ); $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $gateway, $request ) ); /** * Filter payment gateway objects returned from the REST API. * * @param WP_REST_Response $response The response object. * @param WC_Payment_Gateway $gateway Payment gateway object. * @param WP_REST_Request $request Request object. */ return apply_filters( 'woocommerce_rest_prepare_payment_gateway', $response, $gateway, $request ); } /** * Return settings associated with this payment gateway. * * @param WC_Payment_Gateway $gateway Gateway data. * * @return array */ public function get_settings( $gateway ) { $settings = array(); $gateway->init_form_fields(); foreach ( $gateway->form_fields as $id => $field ) { // Make sure we at least have a title and type. if ( empty( $field['title'] ) || empty( $field['type'] ) ) { continue; } // Ignore 'title' settings/fields -- they are UI only. if ( 'title' === $field['type'] ) { continue; } // Ignore 'enabled' and 'description' which get included elsewhere. if ( in_array( $id, array( 'enabled', 'description' ), true ) ) { continue; } $data = array( 'id' => $id, 'label' => empty( $field['label'] ) ? $field['title'] : $field['label'], 'description' => empty( $field['description'] ) ? '' : $field['description'], 'type' => $field['type'], 'value' => empty( $gateway->settings[ $id ] ) ? '' : $gateway->settings[ $id ], 'default' => empty( $field['default'] ) ? '' : $field['default'], 'tip' => empty( $field['description'] ) ? '' : $field['description'], 'placeholder' => empty( $field['placeholder'] ) ? '' : $field['placeholder'], ); if ( ! empty( $field['options'] ) ) { $data['options'] = $field['options']; } $settings[ $id ] = $data; } return $settings; } /** * Prepare links for the request. * * @param WC_Payment_Gateway $gateway Payment gateway object. * @param WP_REST_Request $request Request object. * @return array */ protected function prepare_links( $gateway, $request ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $gateway->id ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Get the payment gateway schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'payment_gateway', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Payment gateway ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'title' => array( 'description' => __( 'Payment gateway title on checkout.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'description' => array( 'description' => __( 'Payment gateway description on checkout.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'order' => array( 'description' => __( 'Payment gateway sort order.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'absint', ), ), 'enabled' => array( 'description' => __( 'Payment gateway enabled status.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), ), 'method_title' => array( 'description' => __( 'Payment gateway method title.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'method_description' => array( 'description' => __( 'Payment gateway method description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'settings' => array( 'description' => __( 'Payment gateway settings.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'id' => array( 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'label' => array( 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'type' => array( 'description' => __( 'Type of setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox' ), 'readonly' => true, ), 'value' => array( 'description' => __( 'Setting value.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'default' => array( 'description' => __( 'Default value for the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'tip' => array( 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'placeholder' => array( 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), ); return $this->add_additional_fields_schema( $schema ); } /** * Get any query params needed. * * @return array */ public function get_collection_params() { return array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ); } } PKo5\aVwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-settings-v2-controller.phpnu[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 all settings groups items. * * @since 3.0.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $groups = apply_filters( 'woocommerce_settings_groups', array() ); if ( empty( $groups ) ) { return new WP_Error( 'rest_setting_groups_empty', __( 'No setting groups have been registered.', 'woocommerce' ), array( 'status' => 500 ) ); } $defaults = $this->group_defaults(); $filtered_groups = array(); foreach ( $groups as $group ) { $sub_groups = array(); foreach ( $groups as $_group ) { if ( ! empty( $_group['parent_id'] ) && $group['id'] === $_group['parent_id'] ) { $sub_groups[] = $_group['id']; } } $group['sub_groups'] = $sub_groups; $group = wp_parse_args( $group, $defaults ); if ( ! is_null( $group['id'] ) && ! is_null( $group['label'] ) ) { $group_obj = $this->filter_group( $group ); $group_data = $this->prepare_item_for_response( $group_obj, $request ); $group_data = $this->prepare_response_for_collection( $group_data ); $filtered_groups[] = $group_data; } } $response = rest_ensure_response( $filtered_groups ); return $response; } /** * Prepare links for the request. * * @param string $group_id Group ID. * @return array Links for the given group. */ protected function prepare_links( $group_id ) { $base = '/' . $this->namespace . '/' . $this->rest_base; $links = array( 'options' => array( 'href' => rest_url( trailingslashit( $base ) . $group_id ), ), ); return $links; } /** * Prepare a report sales object for serialization. * * @since 3.0.0 * @param array $item Group object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response Response data. */ public function prepare_item_for_response( $item, $request ) { $context = empty( $request['context'] ) ? 'view' : $request['context']; $data = $this->add_additional_fields_to_object( $item, $request ); $data = $this->filter_response_by_context( $data, $context ); $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $item['id'] ) ); return $response; } /** * Filters out bad values from the groups array/filter so we * only return known values via the API. * * @since 3.0.0 * @param array $group Group. * @return array */ public function filter_group( $group ) { return array_intersect_key( $group, array_flip( array_filter( array_keys( $group ), array( $this, 'allowed_group_keys' ) ) ) ); } /** * Callback for allowed keys for each group response. * * @since 3.0.0 * @param string $key Key to check. * @return boolean */ public function allowed_group_keys( $key ) { return in_array( $key, array( 'id', 'label', 'description', 'parent_id', 'sub_groups' ) ); } /** * Returns default settings for groups. null means the field is required. * * @since 3.0.0 * @return array */ protected function group_defaults() { return array( 'id' => null, 'label' => null, 'description' => '', 'parent_id' => '', 'sub_groups' => array(), ); } /** * Makes sure the current user has access to READ the settings APIs. * * @since 3.0.0 * @param WP_REST_Request $request Full data 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; } /** * Get the groups schema, conforming to JSON Schema. * * @since 3.0.0 * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'setting_group', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'A unique identifier that can be used to link settings together.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'label' => array( 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'parent_id' => array( 'description' => __( 'ID of parent grouping.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'sub_groups' => array( 'description' => __( 'IDs for settings sub groups.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\39YEYEcwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-shipping-zone-methods-v2-controller.phpnu[/methods endpoint. * * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Shipping Zone Methods class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Shipping_Zones_Controller_Base */ class WC_REST_Shipping_Zone_Methods_V2_Controller extends WC_REST_Shipping_Zones_Controller_Base { /** * Register the routes for Shipping Zone Methods. */ public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/methods', array( 'args' => array( 'zone_id' => array( 'description' => __( 'Unique ID for the zone.', 'woocommerce' ), 'type' => 'integer', ), ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), ), 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( 'method_id' => array( 'required' => true, 'readonly' => false, 'description' => __( 'Shipping method ID.', 'woocommerce' ), ), ) ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/methods/(?P[\d]+)', array( 'args' => array( 'zone_id' => array( 'description' => __( 'Unique ID for the zone.', 'woocommerce' ), 'type' => 'integer', ), 'instance_id' => array( 'description' => __( 'Unique ID for the instance.', 'woocommerce' ), 'type' => 'integer', ), ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'update_items_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_items_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' ), ) ); } /** * Get a single Shipping Zone Method. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function get_item( $request ) { $zone = $this->get_zone( $request['zone_id'] ); if ( is_wp_error( $zone ) ) { return $zone; } $instance_id = (int) $request['instance_id']; $methods = $zone->get_shipping_methods(); $method = false; foreach ( $methods as $method_obj ) { if ( $instance_id === $method_obj->instance_id ) { $method = $method_obj; break; } } if ( false === $method ) { return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); } $data = $this->prepare_item_for_response( $method, $request ); return rest_ensure_response( $data ); } /** * Get all Shipping Zone Methods. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function get_items( $request ) { $zone = $this->get_zone( $request['zone_id'] ); if ( is_wp_error( $zone ) ) { return $zone; } $methods = $zone->get_shipping_methods(); $data = array(); foreach ( $methods as $method_obj ) { $method = $this->prepare_item_for_response( $method_obj, $request ); $data[] = $method; } return rest_ensure_response( $data ); } /** * Create a new shipping zone method instance. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Request|WP_Error */ public function create_item( $request ) { $method_id = $request['method_id']; $zone = $this->get_zone( $request['zone_id'] ); if ( is_wp_error( $zone ) ) { return $zone; } $instance_id = $zone->add_shipping_method( $method_id ); $methods = $zone->get_shipping_methods(); $method = false; foreach ( $methods as $method_obj ) { if ( $instance_id === $method_obj->instance_id ) { $method = $method_obj; break; } } if ( false === $method ) { return new WP_Error( 'woocommerce_rest_shipping_zone_not_created', __( 'Resource cannot be created.', 'woocommerce' ), array( 'status' => 500 ) ); } $method = $this->update_fields( $instance_id, $method, $request ); if ( is_wp_error( $method ) ) { return $method; } $data = $this->prepare_item_for_response( $method, $request ); return rest_ensure_response( $data ); } /** * Delete a shipping method instance. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function delete_item( $request ) { $zone = $this->get_zone( $request['zone_id'] ); if ( is_wp_error( $zone ) ) { return $zone; } $instance_id = (int) $request['instance_id']; $force = $request['force']; $methods = $zone->get_shipping_methods(); $method = false; foreach ( $methods as $method_obj ) { if ( $instance_id === $method_obj->instance_id ) { $method = $method_obj; break; } } if ( false === $method ) { return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); } $method = $this->update_fields( $instance_id, $method, $request ); if ( is_wp_error( $method ) ) { return $method; } $request->set_param( 'context', 'view' ); $response = $this->prepare_item_for_response( $method, $request ); // Actually delete. if ( $force ) { $zone->delete_shipping_method( $instance_id ); } else { return new WP_Error( 'rest_trash_not_supported', __( 'Shipping methods do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) ); } /** * Fires after a product review is deleted via the REST API. * * @param object $method * @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', $method, $response, $request ); return $response; } /** * Update A Single Shipping Zone Method. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function update_item( $request ) { $zone = $this->get_zone( $request['zone_id'] ); if ( is_wp_error( $zone ) ) { return $zone; } $instance_id = (int) $request['instance_id']; $methods = $zone->get_shipping_methods(); $method = false; foreach ( $methods as $method_obj ) { if ( $instance_id === $method_obj->instance_id ) { $method = $method_obj; break; } } if ( false === $method ) { return new WP_Error( 'woocommerce_rest_shipping_zone_method_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); } $method = $this->update_fields( $instance_id, $method, $request ); if ( is_wp_error( $method ) ) { return $method; } $data = $this->prepare_item_for_response( $method, $request ); return rest_ensure_response( $data ); } /** * Updates settings, order, and enabled status on create. * * @param int $instance_id Instance ID. * @param WC_Shipping_Method $method Shipping method data. * @param WP_REST_Request $request Request data. * * @return WC_Shipping_Method */ public function update_fields( $instance_id, $method, $request ) { global $wpdb; // Update settings if present. if ( isset( $request['settings'] ) ) { $method->init_instance_settings(); $instance_settings = $method->instance_settings; $errors_found = false; foreach ( $method->get_instance_form_fields() as $key => $field ) { if ( isset( $request['settings'][ $key ] ) ) { if ( is_callable( array( $this, 'validate_setting_' . $field['type'] . '_field' ) ) ) { $value = $this->{'validate_setting_' . $field['type'] . '_field'}( $request['settings'][ $key ], $field ); } else { $value = $this->validate_setting_text_field( $request['settings'][ $key ], $field ); } if ( is_wp_error( $value ) ) { $errors_found = true; break; } $instance_settings[ $key ] = $value; } } if ( $errors_found ) { return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); } update_option( $method->get_instance_option_key(), apply_filters( 'woocommerce_shipping_' . $method->id . '_instance_settings_values', $instance_settings, $method ) ); } // Update order. if ( isset( $request['order'] ) ) { $wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'method_order' => absint( $request['order'] ) ), array( 'instance_id' => absint( $instance_id ) ) ); $method->method_order = absint( $request['order'] ); } // Update if this method is enabled or not. if ( isset( $request['enabled'] ) ) { if ( $wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'is_enabled' => $request['enabled'] ), array( 'instance_id' => absint( $instance_id ) ) ) ) { do_action( 'woocommerce_shipping_zone_method_status_toggled', $instance_id, $method->id, $request['zone_id'], $request['enabled'] ); $method->enabled = ( true === $request['enabled'] ? 'yes' : 'no' ); } } return $method; } /** * Prepare the Shipping Zone Method for the REST response. * * @param array $item Shipping Zone Method. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response */ public function prepare_item_for_response( $item, $request ) { $method = array( 'id' => $item->instance_id, 'instance_id' => $item->instance_id, 'title' => $item->instance_settings['title'], 'order' => $item->method_order, 'enabled' => ( 'yes' === $item->enabled ), 'method_id' => $item->id, 'method_title' => $item->method_title, 'method_description' => $item->method_description, 'settings' => $this->get_settings( $item ), ); $context = empty( $request['context'] ) ? 'view' : $request['context']; $data = $this->add_additional_fields_to_object( $method, $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( $request['zone_id'], $item->instance_id ) ); $response = $this->prepare_response_for_collection( $response ); return $response; } /** * Return settings associated with this shipping zone method instance. * * @param WC_Shipping_Method $item Shipping method data. * * @return array */ public function get_settings( $item ) { $item->init_instance_settings(); $settings = array(); foreach ( $item->get_instance_form_fields() as $id => $field ) { $data = array( 'id' => $id, 'label' => $field['title'], 'description' => empty( $field['description'] ) ? '' : $field['description'], 'type' => $field['type'], 'value' => $item->instance_settings[ $id ], 'default' => empty( $field['default'] ) ? '' : $field['default'], 'tip' => empty( $field['description'] ) ? '' : $field['description'], 'placeholder' => empty( $field['placeholder'] ) ? '' : $field['placeholder'], ); if ( ! empty( $field['options'] ) ) { $data['options'] = $field['options']; } $settings[ $id ] = $data; } return $settings; } /** * Prepare links for the request. * * @param int $zone_id Given Shipping Zone ID. * @param int $instance_id Given Shipping Zone Method Instance ID. * @return array Links for the given Shipping Zone Method. */ protected function prepare_links( $zone_id, $instance_id ) { $base = '/' . $this->namespace . '/' . $this->rest_base . '/' . $zone_id; $links = array( 'self' => array( 'href' => rest_url( $base . '/methods/' . $instance_id ), ), 'collection' => array( 'href' => rest_url( $base . '/methods' ), ), 'describes' => array( 'href' => rest_url( $base ), ), ); return $links; } /** * Get the Shipping Zone Methods schema, conforming to JSON Schema * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'shipping_zone_method', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Shipping method instance ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'instance_id' => array( 'description' => __( 'Shipping method instance ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'title' => array( 'description' => __( 'Shipping method customer facing title.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'order' => array( 'description' => __( 'Shipping method sort order.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'enabled' => array( 'description' => __( 'Shipping method enabled status.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), ), 'method_id' => array( 'description' => __( 'Shipping method ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'method_title' => array( 'description' => __( 'Shipping method title.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'method_description' => array( 'description' => __( 'Shipping method description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'settings' => array( 'description' => __( 'Shipping method settings.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'id' => array( 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'label' => array( 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'type' => array( 'description' => __( 'Type of setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox' ), 'readonly' => true, ), 'value' => array( 'description' => __( 'Setting value.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'default' => array( 'description' => __( 'Default value for the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'tip' => array( 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'placeholder' => array( 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\g\woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-network-orders-v2-controller.phpnu[namespace, '/' . $this->rest_base . '/network', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'network_orders' ), 'permission_callback' => array( $this, 'network_orders_permissions_check' ), 'args' => $this->get_collection_params(), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } } /** * Retrieves the item's schema for display / public consumption purposes. * * @return array Public item schema data. */ public function get_public_item_schema() { $schema = parent::get_public_item_schema(); $schema['properties']['blog'] = array( 'description' => __( 'Blog id of the record on the multisite.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ); $schema['properties']['edit_url'] = array( 'description' => __( 'URL to edit the order', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ); $schema['properties']['customer'][] = array( 'description' => __( 'Name of the customer for the order', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ); $schema['properties']['status_name'][] = array( 'description' => __( 'Order Status', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ); $schema['properties']['formatted_total'][] = array( 'description' => __( 'Order total formatted for locale', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ); return $schema; } /** * Does a permissions check for the proper requested blog * * @param WP_REST_Request $request Full details about the request. * * @return bool $permission */ public function network_orders_permissions_check( $request ) { $blog_id = $request->get_param( 'blog_id' ); $blog_id = ! empty( $blog_id ) ? $blog_id : get_current_blog_id(); switch_to_blog( $blog_id ); $permission = $this->get_items_permissions_check( $request ); restore_current_blog(); return $permission; } /** * Get a collection of orders from the requested blog id * * @param WP_REST_Request $request Full details about the request. * * @return WP_REST_Response */ public function network_orders( $request ) { $blog_id = $request->get_param( 'blog_id' ); $blog_id = ! empty( $blog_id ) ? $blog_id : get_current_blog_id(); $active_plugins = get_blog_option( $blog_id, 'active_plugins', array() ); $network_active_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) ); $plugins = array_merge( $active_plugins, $network_active_plugins ); $wc_active = false; foreach ( $plugins as $plugin ) { if ( substr_compare( $plugin, '/woocommerce.php', strlen( $plugin ) - strlen( '/woocommerce.php' ), strlen( '/woocommerce.php' ) ) === 0 ) { $wc_active = true; } } // If WooCommerce not active for site, return an empty response. if ( ! $wc_active ) { $response = rest_ensure_response( array() ); return $response; } switch_to_blog( $blog_id ); add_filter( 'woocommerce_rest_orders_prepare_object_query', array( $this, 'network_orders_filter_args' ) ); $items = $this->get_items( $request ); remove_filter( 'woocommerce_rest_orders_prepare_object_query', array( $this, 'network_orders_filter_args' ) ); foreach ( $items->data as &$current_order ) { $order = wc_get_order( $current_order['id'] ); $current_order['blog'] = get_blog_details( get_current_blog_id() ); $current_order['edit_url'] = get_admin_url( $blog_id, 'post.php?post=' . absint( $order->get_id() ) . '&action=edit' ); /* translators: 1: first name 2: last name */ $current_order['customer'] = trim( sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ), $order->get_billing_first_name(), $order->get_billing_last_name() ) ); $current_order['status_name'] = wc_get_order_status_name( $order->get_status() ); $current_order['formatted_total'] = $order->get_formatted_order_total(); } restore_current_blog(); return $items; } /** * Filters the post statuses to on hold and processing for the network order query. * * @param array $args Query args. * * @return array */ public function network_orders_filter_args( $args ) { $args['post_status'] = array( 'wc-on-hold', 'wc-processing', ); return $args; } } PKo5\ۙ|D&&Vwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-products-v2-controller.phpnu[post_type}_object", array( $this, 'clear_transients' ) ); } /** * Register the routes for products. */ 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[\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, 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ), 'type' => 'boolean', ), ), ), '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. * * @param int $id Object ID. * * @since 3.0.0 * @return WC_Data */ protected function get_object( $id ) { return wc_get_product( $id ); } /** * Prepare a single product output for response. * * @param WC_Data $object Object data. * @param WP_REST_Request $request Request object. * * @since 3.0.0 * @return WP_REST_Response */ public function prepare_object_for_response( $object, $request ) { $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->get_product_data( $object, $context ); // Add variations to variable products. if ( $object->is_type( 'variable' ) && $object->has_child() ) { $data['variations'] = $object->get_children(); } // Add grouped products data. if ( $object->is_type( 'grouped' ) && $object->has_child() ) { $data['grouped_products'] = $object->get_children(); } $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. * * @param WP_REST_Request $request Full details about the request. * * @since 3.0.0 * @return array */ protected function prepare_objects_query( $request ) { $args = parent::prepare_objects_query( $request ); // Set post_status. $args['post_status'] = $request['status']; // Taxonomy query to filter products by type, category, // tag, shipping class, and attribute. $tax_query = array(); // Map between taxonomy name and arg's key. $taxonomies = array( 'product_cat' => 'category', 'product_tag' => 'tag', 'product_shipping_class' => 'shipping_class', ); // Set tax_query for each passed arg. foreach ( $taxonomies as $taxonomy => $key ) { if ( ! empty( $request[ $key ] ) ) { $tax_query[] = array( 'taxonomy' => $taxonomy, 'field' => 'term_id', 'terms' => $request[ $key ], ); } } // Filter product type by slug. if ( ! empty( $request['type'] ) ) { $tax_query[] = array( 'taxonomy' => 'product_type', 'field' => 'slug', 'terms' => $request['type'], ); } // Filter by attribute and term. if ( ! empty( $request['attribute'] ) && ! empty( $request['attribute_term'] ) ) { if ( in_array( $request['attribute'], wc_get_attribute_taxonomy_names(), true ) ) { $tax_query[] = array( 'taxonomy' => $request['attribute'], 'field' => 'term_id', 'terms' => $request['attribute_term'], ); } } if ( ! empty( $tax_query ) ) { $args['tax_query'] = $tax_query; // WPCS: slow query ok. } // Filter featured. if ( is_bool( $request['featured'] ) ) { $args['tax_query'][] = array( 'taxonomy' => 'product_visibility', 'field' => 'name', 'terms' => 'featured', 'operator' => true === $request['featured'] ? 'IN' : 'NOT IN', ); } // Filter by sku. if ( ! empty( $request['sku'] ) ) { $skus = explode( ',', $request['sku'] ); // Include the current string as a SKU too. if ( 1 < count( $skus ) ) { $skus[] = $request['sku']; } $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_sku', 'value' => $skus, 'compare' => 'IN', ) ); } // Filter by tax class. if ( ! empty( $request['tax_class'] ) ) { $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_tax_class', 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', ) ); } // Price filter. if ( ! empty( $request['min_price'] ) || ! empty( $request['max_price'] ) ) { $args['meta_query'] = $this->add_meta_query( $args, wc_get_min_max_price_meta_query( $request ) ); // WPCS: slow query ok. } // Filter product in stock or out of stock. if ( is_bool( $request['in_stock'] ) ) { $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_stock_status', 'value' => true === $request['in_stock'] ? 'instock' : 'outofstock', ) ); } // Filter by on sale products. if ( is_bool( $request['on_sale'] ) ) { $on_sale_key = $request['on_sale'] ? 'post__in' : 'post__not_in'; $on_sale_ids = wc_get_product_ids_on_sale(); // Use 0 when there's no on sale products to avoid return all products. $on_sale_ids = empty( $on_sale_ids ) ? array( 0 ) : $on_sale_ids; $args[ $on_sale_key ] += $on_sale_ids; } // Force the post_type argument, since it's not a user input variable. if ( ! empty( $request['sku'] ) ) { $args['post_type'] = array( 'product', 'product_variation' ); } else { $args['post_type'] = $this->post_type; } return $args; } /** * Get the downloads for a product or product variation. * * @param WC_Product|WC_Product_Variation $product Product instance. * * @return array */ protected function get_downloads( $product ) { $downloads = array(); if ( $product->is_downloadable() ) { foreach ( $product->get_downloads() as $file_id => $file ) { $downloads[] = array( 'id' => $file_id, // MD5 hash. 'name' => $file['name'], 'file' => $file['file'], ); } } return $downloads; } /** * Get taxonomy terms. * * @param WC_Product $product Product instance. * @param string $taxonomy Taxonomy slug. * * @return array */ protected function get_taxonomy_terms( $product, $taxonomy = 'cat' ) { $terms = array(); foreach ( wc_get_object_terms( $product->get_id(), 'product_' . $taxonomy ) as $term ) { $terms[] = array( 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug, ); } return $terms; } /** * Get the images for a product or product variation. * * @param WC_Product|WC_Product_Variation $product Product instance. * * @return array */ protected function get_images( $product ) { $images = array(); $attachment_ids = array(); // Add featured image. if ( $product->get_image_id() ) { $attachment_ids[] = $product->get_image_id(); } // Add gallery images. $attachment_ids = array_merge( $attachment_ids, $product->get_gallery_image_ids() ); // Build image data. foreach ( $attachment_ids as $position => $attachment_id ) { $attachment_post = get_post( $attachment_id ); if ( is_null( $attachment_post ) ) { continue; } $attachment = wp_get_attachment_image_src( $attachment_id, 'full' ); if ( ! is_array( $attachment ) ) { continue; } $images[] = array( 'id' => (int) $attachment_id, 'date_created' => wc_rest_prepare_date_response( $attachment_post->post_date, false ), 'date_created_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ), 'date_modified' => wc_rest_prepare_date_response( $attachment_post->post_modified, false ), 'date_modified_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_modified_gmt ) ), 'src' => current( $attachment ), 'name' => get_the_title( $attachment_id ), 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), 'position' => (int) $position, ); } // Set a placeholder image if the product has no images set. if ( empty( $images ) ) { $images[] = array( 'id' => 0, 'date_created' => wc_rest_prepare_date_response( current_time( 'mysql' ), false ), // Default to now. 'date_created_gmt' => wc_rest_prepare_date_response( current_time( 'timestamp', true ) ), // Default to now. 'date_modified' => wc_rest_prepare_date_response( current_time( 'mysql' ), false ), 'date_modified_gmt' => wc_rest_prepare_date_response( current_time( 'timestamp', true ) ), 'src' => wc_placeholder_img_src(), 'name' => __( 'Placeholder', 'woocommerce' ), 'alt' => __( 'Placeholder', 'woocommerce' ), 'position' => 0, ); } return $images; } /** * Get attribute taxonomy label. * * @param string $name Taxonomy name. * * @deprecated 3.0.0 * @return string */ protected function get_attribute_taxonomy_label( $name ) { $tax = get_taxonomy( $name ); $labels = get_taxonomy_labels( $tax ); return $labels->singular_name; } /** * Get product attribute taxonomy name. * * @param string $slug Taxonomy name. * @param WC_Product $product Product data. * * @since 3.0.0 * @return string */ protected function get_attribute_taxonomy_name( $slug, $product ) { // Format slug so it matches attributes of the product. $slug = wc_attribute_taxonomy_slug( $slug ); $attributes = $product->get_attributes(); $attribute = false; // pa_ attributes. if ( isset( $attributes[ wc_attribute_taxonomy_name( $slug ) ] ) ) { $attribute = $attributes[ wc_attribute_taxonomy_name( $slug ) ]; } elseif ( isset( $attributes[ $slug ] ) ) { $attribute = $attributes[ $slug ]; } if ( ! $attribute ) { return $slug; } // Taxonomy attribute name. if ( $attribute->is_taxonomy() ) { $taxonomy = $attribute->get_taxonomy_object(); return $taxonomy->attribute_label; } // Custom product attribute name. return $attribute->get_name(); } /** * Get default attributes. * * @param WC_Product $product Product instance. * * @return array */ protected function get_default_attributes( $product ) { $default = array(); if ( $product->is_type( 'variable' ) ) { foreach ( array_filter( (array) $product->get_default_attributes(), 'strlen' ) as $key => $value ) { if ( 0 === strpos( $key, 'pa_' ) ) { $default[] = array( 'id' => wc_attribute_taxonomy_id_by_name( $key ), 'name' => $this->get_attribute_taxonomy_name( $key, $product ), 'option' => $value, ); } else { $default[] = array( 'id' => 0, 'name' => $this->get_attribute_taxonomy_name( $key, $product ), 'option' => $value, ); } } } return $default; } /** * Get attribute options. * * @param int $product_id Product ID. * @param array $attribute Attribute data. * * @return array */ protected function get_attribute_options( $product_id, $attribute ) { if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) { return wc_get_product_terms( $product_id, $attribute['name'], array( 'fields' => 'names', ) ); } elseif ( isset( $attribute['value'] ) ) { return array_map( 'trim', explode( '|', $attribute['value'] ) ); } return array(); } /** * Get the attributes for a product or product variation. * * @param WC_Product|WC_Product_Variation $product Product instance. * * @return array */ protected function get_attributes( $product ) { $attributes = array(); if ( $product->is_type( 'variation' ) ) { $_product = wc_get_product( $product->get_parent_id() ); foreach ( $product->get_variation_attributes() as $attribute_name => $attribute ) { $name = str_replace( 'attribute_', '', $attribute_name ); if ( empty( $attribute ) && '0' !== $attribute ) { continue; } // Taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_`. if ( 0 === strpos( $attribute_name, 'attribute_pa_' ) ) { $option_term = get_term_by( 'slug', $attribute, $name ); $attributes[] = array( 'id' => wc_attribute_taxonomy_id_by_name( $name ), 'name' => $this->get_attribute_taxonomy_name( $name, $_product ), 'option' => $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $attribute, ); } else { $attributes[] = array( 'id' => 0, 'name' => $this->get_attribute_taxonomy_name( $name, $_product ), 'option' => $attribute, ); } } } else { foreach ( $product->get_attributes() as $attribute ) { $attributes[] = array( 'id' => $attribute['is_taxonomy'] ? wc_attribute_taxonomy_id_by_name( $attribute['name'] ) : 0, 'name' => $this->get_attribute_taxonomy_name( $attribute['name'], $product ), 'position' => (int) $attribute['position'], 'visible' => (bool) $attribute['is_visible'], 'variation' => (bool) $attribute['is_variation'], 'options' => $this->get_attribute_options( $product->get_id(), $attribute ), ); } } return $attributes; } /** * Get product data. * * @param WC_Product $product Product instance. * @param string $context Request context. * Options: 'view' and 'edit'. * * @return array */ protected function get_product_data( $product, $context = 'view' ) { $data = array( 'id' => $product->get_id(), 'name' => $product->get_name( $context ), 'slug' => $product->get_slug( $context ), 'permalink' => $product->get_permalink(), 'date_created' => wc_rest_prepare_date_response( $product->get_date_created( $context ), false ), 'date_created_gmt' => wc_rest_prepare_date_response( $product->get_date_created( $context ) ), 'date_modified' => wc_rest_prepare_date_response( $product->get_date_modified( $context ), false ), 'date_modified_gmt' => wc_rest_prepare_date_response( $product->get_date_modified( $context ) ), 'type' => $product->get_type(), 'status' => $product->get_status( $context ), 'featured' => $product->is_featured(), 'catalog_visibility' => $product->get_catalog_visibility( $context ), 'description' => 'view' === $context ? wpautop( do_shortcode( $product->get_description() ) ) : $product->get_description( $context ), 'short_description' => 'view' === $context ? apply_filters( 'woocommerce_short_description', $product->get_short_description() ) : $product->get_short_description( $context ), 'sku' => $product->get_sku( $context ), 'price' => $product->get_price( $context ), 'regular_price' => $product->get_regular_price( $context ), 'sale_price' => $product->get_sale_price( $context ) ? $product->get_sale_price( $context ) : '', 'date_on_sale_from' => wc_rest_prepare_date_response( $product->get_date_on_sale_from( $context ), false ), 'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $product->get_date_on_sale_from( $context ) ), 'date_on_sale_to' => wc_rest_prepare_date_response( $product->get_date_on_sale_to( $context ), false ), 'date_on_sale_to_gmt' => wc_rest_prepare_date_response( $product->get_date_on_sale_to( $context ) ), 'price_html' => $product->get_price_html(), 'on_sale' => $product->is_on_sale( $context ), 'purchasable' => $product->is_purchasable(), 'total_sales' => $product->get_total_sales( $context ), 'virtual' => $product->is_virtual(), 'downloadable' => $product->is_downloadable(), 'downloads' => $this->get_downloads( $product ), 'download_limit' => $product->get_download_limit( $context ), 'download_expiry' => $product->get_download_expiry( $context ), 'external_url' => $product->is_type( 'external' ) ? $product->get_product_url( $context ) : '', 'button_text' => $product->is_type( 'external' ) ? $product->get_button_text( $context ) : '', 'tax_status' => $product->get_tax_status( $context ), 'tax_class' => $product->get_tax_class( $context ), 'manage_stock' => $product->managing_stock(), 'stock_quantity' => $product->get_stock_quantity( $context ), 'in_stock' => $product->is_in_stock(), 'backorders' => $product->get_backorders( $context ), 'backorders_allowed' => $product->backorders_allowed(), 'backordered' => $product->is_on_backorder(), 'sold_individually' => $product->is_sold_individually(), 'weight' => $product->get_weight( $context ), 'dimensions' => array( 'length' => $product->get_length( $context ), 'width' => $product->get_width( $context ), 'height' => $product->get_height( $context ), ), 'shipping_required' => $product->needs_shipping(), 'shipping_taxable' => $product->is_shipping_taxable(), 'shipping_class' => $product->get_shipping_class(), 'shipping_class_id' => $product->get_shipping_class_id( $context ), 'reviews_allowed' => $product->get_reviews_allowed( $context ), 'average_rating' => 'view' === $context ? wc_format_decimal( $product->get_average_rating(), 2 ) : $product->get_average_rating( $context ), 'rating_count' => $product->get_rating_count(), 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ), 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids( $context ) ), 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids( $context ) ), 'parent_id' => $product->get_parent_id( $context ), 'purchase_note' => 'view' === $context ? wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ) : $product->get_purchase_note( $context ), 'categories' => $this->get_taxonomy_terms( $product ), 'tags' => $this->get_taxonomy_terms( $product, 'tag' ), 'images' => $this->get_images( $product ), 'attributes' => $this->get_attributes( $product ), 'default_attributes' => $this->get_default_attributes( $product ), 'variations' => array(), 'grouped_products' => array(), 'menu_order' => $product->get_menu_order( $context ), 'meta_data' => $product->get_meta_data(), ); return $data; } /** * Prepare links for the request. * * @param WC_Data $object Object data. * @param WP_REST_Request $request Request object. * * @return array Links for the given post. */ protected function prepare_links( $object, $request ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ), // @codingStandardsIgnoreLine. ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), // @codingStandardsIgnoreLine. ), ); if ( $object->get_parent_id() ) { $links['up'] = array( 'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $object->get_parent_id() ) ), // @codingStandardsIgnoreLine. ); } return $links; } /** * Prepare a single product 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; // Type is the most important part here because we need to be using the correct class and methods. if ( isset( $request['type'] ) ) { $classname = WC_Product_Factory::get_classname_from_product_type( $request['type'] ); if ( ! class_exists( $classname ) ) { $classname = 'WC_Product_Simple'; } $product = new $classname( $id ); } elseif ( isset( $request['id'] ) ) { $product = wc_get_product( $id ); } else { $product = new WC_Product_Simple(); } if ( 'variation' === $product->get_type() ) { return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404, ) ); } // Post title. if ( isset( $request['name'] ) ) { $product->set_name( wp_filter_post_kses( $request['name'] ) ); } // Post content. if ( isset( $request['description'] ) ) { $product->set_description( wp_filter_post_kses( $request['description'] ) ); } // Post excerpt. if ( isset( $request['short_description'] ) ) { $product->set_short_description( wp_filter_post_kses( $request['short_description'] ) ); } // Post status. if ( isset( $request['status'] ) ) { $product->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' ); } // Post slug. if ( isset( $request['slug'] ) ) { $product->set_slug( $request['slug'] ); } // Menu order. if ( isset( $request['menu_order'] ) ) { $product->set_menu_order( $request['menu_order'] ); } // Comment status. if ( isset( $request['reviews_allowed'] ) ) { $product->set_reviews_allowed( $request['reviews_allowed'] ); } // Virtual. if ( isset( $request['virtual'] ) ) { $product->set_virtual( $request['virtual'] ); } // Tax status. if ( isset( $request['tax_status'] ) ) { $product->set_tax_status( $request['tax_status'] ); } // Tax Class. if ( isset( $request['tax_class'] ) ) { $product->set_tax_class( $request['tax_class'] ); } // Catalog Visibility. if ( isset( $request['catalog_visibility'] ) ) { $product->set_catalog_visibility( $request['catalog_visibility'] ); } // Purchase Note. if ( isset( $request['purchase_note'] ) ) { $product->set_purchase_note( wp_kses_post( wp_unslash( $request['purchase_note'] ) ) ); } // Featured Product. if ( isset( $request['featured'] ) ) { $product->set_featured( $request['featured'] ); } // Shipping data. $product = $this->save_product_shipping_data( $product, $request ); // SKU. if ( isset( $request['sku'] ) ) { $product->set_sku( wc_clean( $request['sku'] ) ); } // Attributes. if ( isset( $request['attributes'] ) ) { $attributes = array(); foreach ( $request['attributes'] as $attribute ) { $attribute_id = 0; $attribute_name = ''; // Check ID for global attributes or name for product attributes. if ( ! empty( $attribute['id'] ) ) { $attribute_id = absint( $attribute['id'] ); $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); } elseif ( ! empty( $attribute['name'] ) ) { $attribute_name = wc_clean( $attribute['name'] ); } if ( ! $attribute_id && ! $attribute_name ) { continue; } if ( $attribute_id ) { if ( isset( $attribute['options'] ) ) { $options = $attribute['options']; if ( ! is_array( $attribute['options'] ) ) { // Text based attributes - Posted values are term names. $options = explode( WC_DELIMITER, $options ); } $values = array_map( 'wc_sanitize_term_text_based', $options ); $values = array_filter( $values, 'strlen' ); } else { $values = array(); } if ( ! empty( $values ) ) { // Add attribute to array, but don't set values. $attribute_object = new WC_Product_Attribute(); $attribute_object->set_id( $attribute_id ); $attribute_object->set_name( $attribute_name ); $attribute_object->set_options( $values ); $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; } } elseif ( isset( $attribute['options'] ) ) { // Custom attribute - Add attribute to array and set the values. if ( is_array( $attribute['options'] ) ) { $values = $attribute['options']; } else { $values = explode( WC_DELIMITER, $attribute['options'] ); } $attribute_object = new WC_Product_Attribute(); $attribute_object->set_name( $attribute_name ); $attribute_object->set_options( $values ); $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; } } $product->set_attributes( $attributes ); } // Sales and prices. if ( in_array( $product->get_type(), array( 'variable', 'grouped' ), true ) ) { $product->set_regular_price( '' ); $product->set_sale_price( '' ); $product->set_date_on_sale_to( '' ); $product->set_date_on_sale_from( '' ); $product->set_price( '' ); } else { // Regular Price. if ( isset( $request['regular_price'] ) ) { $product->set_regular_price( $request['regular_price'] ); } // Sale Price. if ( isset( $request['sale_price'] ) ) { $product->set_sale_price( $request['sale_price'] ); } if ( isset( $request['date_on_sale_from'] ) ) { $product->set_date_on_sale_from( $request['date_on_sale_from'] ); } if ( isset( $request['date_on_sale_from_gmt'] ) ) { $product->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null ); } if ( isset( $request['date_on_sale_to'] ) ) { $product->set_date_on_sale_to( $request['date_on_sale_to'] ); } if ( isset( $request['date_on_sale_to_gmt'] ) ) { $product->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null ); } } // Product parent ID. if ( isset( $request['parent_id'] ) ) { $product->set_parent_id( $request['parent_id'] ); } // Sold individually. if ( isset( $request['sold_individually'] ) ) { $product->set_sold_individually( $request['sold_individually'] ); } // Stock status. if ( isset( $request['in_stock'] ) ) { $stock_status = true === $request['in_stock'] ? 'instock' : 'outofstock'; } else { $stock_status = $product->get_stock_status(); } // Stock data. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { // Manage stock. if ( isset( $request['manage_stock'] ) ) { $product->set_manage_stock( $request['manage_stock'] ); } // Backorders. if ( isset( $request['backorders'] ) ) { $product->set_backorders( $request['backorders'] ); } if ( $product->is_type( 'grouped' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } elseif ( $product->is_type( 'external' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( 'instock' ); } elseif ( $product->get_manage_stock() ) { // Stock status is always determined by children so sync later. if ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Stock quantity. if ( isset( $request['stock_quantity'] ) ) { $product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) ); } elseif ( isset( $request['inventory_delta'] ) ) { $stock_quantity = wc_stock_amount( $product->get_stock_quantity() ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); } } else { // Don't manage stock. $product->set_manage_stock( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } } elseif ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Upsells. if ( isset( $request['upsell_ids'] ) ) { $upsells = array(); $ids = $request['upsell_ids']; if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { if ( $id && $id > 0 ) { $upsells[] = $id; } } } $product->set_upsell_ids( $upsells ); } // Cross sells. if ( isset( $request['cross_sell_ids'] ) ) { $crosssells = array(); $ids = $request['cross_sell_ids']; if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { if ( $id && $id > 0 ) { $crosssells[] = $id; } } } $product->set_cross_sell_ids( $crosssells ); } // Product categories. if ( isset( $request['categories'] ) && is_array( $request['categories'] ) ) { $product = $this->save_taxonomy_terms( $product, $request['categories'] ); } // Product tags. if ( isset( $request['tags'] ) && is_array( $request['tags'] ) ) { $product = $this->save_taxonomy_terms( $product, $request['tags'], 'tag' ); } // Downloadable. if ( isset( $request['downloadable'] ) ) { $product->set_downloadable( $request['downloadable'] ); } // Downloadable options. if ( $product->get_downloadable() ) { // Downloadable files. if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { $product = $this->save_downloadable_files( $product, $request['downloads'] ); } // Download limit. if ( isset( $request['download_limit'] ) ) { $product->set_download_limit( $request['download_limit'] ); } // Download expiry. if ( isset( $request['download_expiry'] ) ) { $product->set_download_expiry( $request['download_expiry'] ); } } // Product url and button text for external products. if ( $product->is_type( 'external' ) ) { if ( isset( $request['external_url'] ) ) { $product->set_product_url( $request['external_url'] ); } if ( isset( $request['button_text'] ) ) { $product->set_button_text( $request['button_text'] ); } } // Save default attributes for variable products. if ( $product->is_type( 'variable' ) ) { $product = $this->save_default_attributes( $product, $request ); } // Set children for a grouped product. if ( $product->is_type( 'grouped' ) && isset( $request['grouped_products'] ) ) { $product->set_children( $request['grouped_products'] ); } // Check for featured/gallery images, upload it and set it. if ( isset( $request['images'] ) ) { $product = $this->set_product_images( $product, $request['images'] ); } // Allow set meta_data. if ( is_array( $request['meta_data'] ) ) { foreach ( $request['meta_data'] as $meta ) { $product->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } } /** * 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 $product 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", $product, $request, $creating ); } /** * Set product images. * * @param WC_Product $product Product instance. * @param array $images Images data. * * @throws WC_REST_Exception REST API exceptions. * @return WC_Product */ protected function set_product_images( $product, $images ) { $images = is_array( $images ) ? array_filter( $images ) : array(); if ( ! empty( $images ) ) { $gallery_positions = array(); foreach ( $images as $index => $image ) { $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; if ( 0 === $attachment_id && isset( $image['src'] ) ) { $upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) ); if ( is_wp_error( $upload ) ) { if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $product->get_id(), $images ) ) { throw new WC_REST_Exception( 'woocommerce_product_image_upload_error', $upload->get_error_message(), 400 ); } else { continue; } } $attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $product->get_id() ); } if ( ! wp_attachment_is_image( $attachment_id ) ) { /* translators: %s: attachment id */ throw new WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 ); } $gallery_positions[ $attachment_id ] = absint( isset( $image['position'] ) ? $image['position'] : $index ); // Set the image alt if present. if ( ! empty( $image['alt'] ) ) { update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) ); } // Set the image name if present. if ( ! empty( $image['name'] ) ) { wp_update_post( array( 'ID' => $attachment_id, 'post_title' => $image['name'], ) ); } // Set the image source if present, for future reference. if ( ! empty( $image['src'] ) ) { update_post_meta( $attachment_id, '_wc_attachment_source', esc_url_raw( $image['src'] ) ); } } // Sort images and get IDs in correct order. asort( $gallery_positions ); // Get gallery in correct order. $gallery = array_keys( $gallery_positions ); // Featured image is in position 0. $image_id = array_shift( $gallery ); // Set images. $product->set_image_id( $image_id ); $product->set_gallery_image_ids( $gallery ); } else { $product->set_image_id( '' ); $product->set_gallery_image_ids( array() ); } return $product; } /** * Save product shipping data. * * @param WC_Product $product Product instance. * @param array $data Shipping data. * * @return WC_Product */ protected function save_product_shipping_data( $product, $data ) { // Virtual. if ( isset( $data['virtual'] ) && true === $data['virtual'] ) { $product->set_weight( '' ); $product->set_height( '' ); $product->set_length( '' ); $product->set_width( '' ); } else { if ( isset( $data['weight'] ) ) { $product->set_weight( $data['weight'] ); } // Height. if ( isset( $data['dimensions']['height'] ) ) { $product->set_height( $data['dimensions']['height'] ); } // Width. if ( isset( $data['dimensions']['width'] ) ) { $product->set_width( $data['dimensions']['width'] ); } // Length. if ( isset( $data['dimensions']['length'] ) ) { $product->set_length( $data['dimensions']['length'] ); } } // Shipping class. if ( isset( $data['shipping_class'] ) ) { $data_store = $product->get_data_store(); $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data['shipping_class'] ) ); $product->set_shipping_class_id( $shipping_class_id ); } return $product; } /** * Save downloadable files. * * @param WC_Product $product Product instance. * @param array $downloads Downloads data. * @param int $deprecated Deprecated since 3.0. * * @return WC_Product */ protected function save_downloadable_files( $product, $downloads, $deprecated = 0 ) { if ( $deprecated ) { wc_deprecated_argument( 'variation_id', '3.0', 'save_downloadable_files() not requires a variation_id anymore.' ); } $files = array(); foreach ( $downloads as $key => $file ) { if ( empty( $file['file'] ) ) { continue; } $download = new WC_Product_Download(); $download->set_id( ! empty( $file['id'] ) ? $file['id'] : wp_generate_uuid4() ); $download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) ); $download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) ); $files[] = $download; } $product->set_downloads( $files ); return $product; } /** * Save taxonomy terms. * * @param WC_Product $product Product instance. * @param array $terms Terms data. * @param string $taxonomy Taxonomy name. * * @return WC_Product */ protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) { $term_ids = wp_list_pluck( $terms, 'id' ); if ( 'cat' === $taxonomy ) { $product->set_category_ids( $term_ids ); } elseif ( 'tag' === $taxonomy ) { $product->set_tag_ids( $term_ids ); } return $product; } /** * Save default attributes. * * @param WC_Product $product Product instance. * @param WP_REST_Request $request Request data. * * @since 3.0.0 * @return WC_Product */ protected function save_default_attributes( $product, $request ) { if ( isset( $request['default_attributes'] ) && is_array( $request['default_attributes'] ) ) { $attributes = $product->get_attributes(); $default_attributes = array(); foreach ( $request['default_attributes'] as $attribute ) { $attribute_id = 0; $attribute_name = ''; // Check ID for global attributes or name for product attributes. if ( ! empty( $attribute['id'] ) ) { $attribute_id = absint( $attribute['id'] ); $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); } elseif ( ! empty( $attribute['name'] ) ) { $attribute_name = sanitize_title( $attribute['name'] ); } if ( ! $attribute_id && ! $attribute_name ) { continue; } if ( isset( $attributes[ $attribute_name ] ) ) { $_attribute = $attributes[ $attribute_name ]; if ( $_attribute['is_variation'] ) { $value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; if ( ! empty( $_attribute['is_taxonomy'] ) ) { // If dealing with a taxonomy, we need to get the slug from the name posted to the API. $term = get_term_by( 'name', $value, $attribute_name ); if ( $term && ! is_wp_error( $term ) ) { $value = $term->slug; } else { $value = sanitize_title( $value ); } } if ( $value ) { $default_attributes[ $attribute_name ] = $value; } } } } $product->set_default_attributes( $default_attributes ); } return $product; } /** * Clear caches here so in sync with any new variations/children. * * @param WC_Data $object Object data. */ public function clear_transients( $object ) { wc_delete_product_transients( $object->get_id() ); wp_cache_delete( 'product-' . $object->get_id(), 'products' ); } /** * Delete a single item. * * @param WP_REST_Request $request Full details about the request. * * @return WP_REST_Response|WP_Error */ public function delete_item( $request ) { $id = (int) $request['id']; $force = (bool) $request['force']; $object = $this->get_object( (int) $request['id'] ); $result = false; if ( ! $object || 0 === $object->get_id() ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404, ) ); } if ( 'variation' === $object->get_type() ) { return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404, ) ); } $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); /** * Filter whether an object is trashable. * * Return false to disable trash support for the object. * * @param boolean $supports_trash Whether the object type support trashing. * @param WC_Data $object The object being considered for trashing support. */ $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { return new WP_Error( "woocommerce_rest_user_cannot_delete_{$this->post_type}", /* translators: %s: post type */ sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code(), ) ); } $request->set_param( 'context', 'edit' ); $response = $this->prepare_object_for_response( $object, $request ); // If we're forcing, then delete permanently. if ( $force ) { if ( $object->is_type( 'variable' ) ) { foreach ( $object->get_children() as $child_id ) { $child = wc_get_product( $child_id ); if ( ! empty( $child ) ) { $child->delete( true ); } } } else { // For other product types, if the product has children, remove the relationship. foreach ( $object->get_children() as $child_id ) { $child = wc_get_product( $child_id ); if ( ! empty( $child ) ) { $child->set_parent_id( 0 ); $child->save(); } } } $object->delete( true ); $result = 0 === $object->get_id(); } else { // If we don't support trashing for this type, error out. if ( ! $supports_trash ) { return new WP_Error( 'woocommerce_rest_trash_not_supported', /* translators: %s: post type */ sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501, ) ); } // Otherwise, only trash if we haven't already. if ( is_callable( array( $object, 'get_status' ) ) ) { if ( 'trash' === $object->get_status() ) { return new WP_Error( 'woocommerce_rest_already_trashed', /* translators: %s: post type */ sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410, ) ); } $object->delete(); $result = 'trash' === $object->get_status(); } } if ( ! $result ) { return new WP_Error( 'woocommerce_rest_cannot_delete', /* translators: %s: post type */ sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 500, ) ); } // Delete parent product transients. if ( 0 !== $object->get_parent_id() ) { wc_delete_product_transients( $object->get_parent_id() ); } /** * Fires after a single object is deleted or trashed via the REST API. * * @param WC_Data $object The deleted or trashed object. * @param WP_REST_Response $response The response data. * @param WP_REST_Request $request The request sent to the API. */ do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request ); return $response; } /** * Get the Product's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $weight_unit = get_option( 'woocommerce_weight_unit' ); $dimension_unit = get_option( 'woocommerce_dimension_unit' ); $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, ), 'name' => array( 'description' => __( 'Product name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'slug' => array( 'description' => __( 'Product slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'permalink' => array( 'description' => __( 'Product URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created' => array( 'description' => __( "The date the product 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 product was created, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified' => array( 'description' => __( "The date the product 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 product was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'type' => array( 'description' => __( 'Product type.', 'woocommerce' ), 'type' => 'string', 'default' => 'simple', 'enum' => array_keys( wc_get_product_types() ), 'context' => array( 'view', 'edit' ), ), 'status' => array( 'description' => __( 'Product status (post status).', 'woocommerce' ), 'type' => 'string', 'default' => 'publish', 'enum' => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ), 'context' => array( 'view', 'edit' ), ), 'featured' => array( 'description' => __( 'Featured product.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'catalog_visibility' => array( 'description' => __( 'Catalog visibility.', 'woocommerce' ), 'type' => 'string', 'default' => 'visible', 'enum' => array( 'visible', 'catalog', 'search', 'hidden' ), 'context' => array( 'view', 'edit' ), ), 'description' => array( 'description' => __( 'Product description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'short_description' => array( 'description' => __( 'Product short description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sku' => array( 'description' => __( 'Unique identifier.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'price' => array( 'description' => __( 'Current product price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'regular_price' => array( 'description' => __( 'Product regular price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sale_price' => array( 'description' => __( 'Product sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from' => array( 'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from_gmt' => array( 'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to' => array( 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to_gmt' => array( 'description' => __( 'End date of sale price, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'price_html' => array( 'description' => __( 'Price formatted in HTML.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'on_sale' => array( 'description' => __( 'Shows if the product is on sale.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'purchasable' => array( 'description' => __( 'Shows if the product can be bought.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total_sales' => array( 'description' => __( 'Amount of sales.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'virtual' => array( 'description' => __( 'If the product is virtual.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloadable' => array( 'description' => __( 'If the product is downloadable.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloads' => array( 'description' => __( 'List of downloadable files.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'File ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'File name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'file' => array( 'description' => __( 'File URL.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'download_limit' => array( 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'download_expiry' => array( 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'external_url' => array( 'description' => __( 'Product external URL. Only for external products.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), 'button_text' => array( 'description' => __( 'Product external button text. Only for external products.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'tax_status' => array( 'description' => __( 'Tax status.', 'woocommerce' ), 'type' => 'string', 'default' => 'taxable', 'enum' => array( 'taxable', 'shipping', 'none' ), 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'manage_stock' => array( 'description' => __( 'Stock management at product level.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'stock_quantity' => array( 'description' => __( 'Stock quantity.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'in_stock' => array( 'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ), 'type' => 'boolean', 'default' => true, 'context' => array( 'view', 'edit' ), ), 'backorders' => array( 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ), 'type' => 'string', 'default' => 'no', 'enum' => array( 'no', 'notify', 'yes' ), 'context' => array( 'view', 'edit' ), ), 'backorders_allowed' => array( 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'backordered' => array( 'description' => __( 'Shows if the product is on backordered.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'sold_individually' => array( 'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'weight' => array( /* translators: %s: weight unit */ 'description' => sprintf( __( 'Product weight (%s).', 'woocommerce' ), $weight_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'dimensions' => array( 'description' => __( 'Product dimensions.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'length' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product length (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'width' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product width (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'height' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product height (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'shipping_required' => array( 'description' => __( 'Shows if the product need to be shipped.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_taxable' => array( 'description' => __( 'Shows whether or not the product shipping is taxable.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_class' => array( 'description' => __( 'Shipping class slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'shipping_class_id' => array( 'description' => __( 'Shipping class ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'reviews_allowed' => array( 'description' => __( 'Allow reviews.', 'woocommerce' ), 'type' => 'boolean', 'default' => true, 'context' => array( 'view', 'edit' ), ), 'average_rating' => array( 'description' => __( 'Reviews average rating.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'rating_count' => array( 'description' => __( 'Amount of reviews that the product have.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'related_ids' => array( 'description' => __( 'List of related products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'upsell_ids' => array( 'description' => __( 'List of up-sell products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), ), 'cross_sell_ids' => array( 'description' => __( 'List of cross-sell products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), ), 'parent_id' => array( 'description' => __( 'Product parent ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'purchase_note' => array( 'description' => __( 'Optional note to send the customer after purchase.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'categories' => array( 'description' => __( 'List of categories.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Category ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Category name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'slug' => array( 'description' => __( 'Category slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'tags' => array( 'description' => __( 'List of tags.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Tag ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Tag name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'slug' => array( 'description' => __( 'Tag slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'images' => array( 'description' => __( 'List of images.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', '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_created_gmt' => array( 'description' => __( 'The date the image was created, as GMT.', '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, ), 'date_modified_gmt' => array( 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'src' => array( 'description' => __( 'Image URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Image name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'alt' => array( 'description' => __( 'Image alternative text.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'position' => array( 'description' => __( 'Image position. 0 means that the image is featured.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), ), ), ), 'attributes' => array( 'description' => __( 'List of attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'position' => array( 'description' => __( 'Attribute position.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'visible' => array( 'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'variation' => array( 'description' => __( 'Define if the attribute can be used as variation.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'options' => array( 'description' => __( 'List of available term names of the attribute.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'string', ), ), ), ), ), 'default_attributes' => array( 'description' => __( 'Defaults variation attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'option' => array( 'description' => __( 'Selected attribute term name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'variations' => array( 'description' => __( 'List of variations IDs.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'integer', ), 'readonly' => true, ), 'grouped_products' => array( 'description' => __( 'List of grouped products ID.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), ), 'menu_order' => array( 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), '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['orderby']['enum'] = array_merge( $params['orderby']['enum'], array( 'menu_order' ) ); $params['slug'] = array( 'description' => __( 'Limit result set to products with a specific slug.', 'woocommerce' ), 'type' => 'string', 'validate_callback' => 'rest_validate_request_arg', ); $params['status'] = array( 'default' => 'any', 'description' => __( 'Limit result set to products assigned a specific status.', 'woocommerce' ), 'type' => 'string', 'enum' => array_merge( array( 'any', 'future' ), array_keys( get_post_statuses() ) ), 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); $params['type'] = array( 'description' => __( 'Limit result set to products assigned a specific type.', 'woocommerce' ), 'type' => 'string', 'enum' => array_keys( wc_get_product_types() ), 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); $params['sku'] = array( 'description' => __( 'Limit result set to products with specific SKU(s). Use commas to separate.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); $params['featured'] = array( 'description' => __( 'Limit result set to featured products.', 'woocommerce' ), 'type' => 'boolean', 'sanitize_callback' => 'wc_string_to_bool', 'validate_callback' => 'rest_validate_request_arg', ); $params['category'] = array( 'description' => __( 'Limit result set to products assigned a specific category ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); $params['tag'] = array( 'description' => __( 'Limit result set to products assigned a specific tag ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); $params['shipping_class'] = array( 'description' => __( 'Limit result set to products assigned a specific shipping class ID.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); $params['attribute'] = array( 'description' => __( 'Limit result set to products with a specific attribute. Use the taxonomy name/attribute slug.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); $params['attribute_term'] = array( 'description' => __( 'Limit result set to products with a specific attribute term ID (required an assigned attribute).', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); if ( wc_tax_enabled() ) { $params['tax_class'] = array( 'description' => __( 'Limit result set to products with a specific tax class.', 'woocommerce' ), 'type' => 'string', 'enum' => array_merge( array( 'standard' ), WC_Tax::get_tax_class_slugs() ), 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); } $params['in_stock'] = array( 'description' => __( 'Limit result set to products in stock or out of stock.', 'woocommerce' ), 'type' => 'boolean', 'sanitize_callback' => 'wc_string_to_bool', 'validate_callback' => 'rest_validate_request_arg', ); $params['on_sale'] = array( 'description' => __( 'Limit result set to products on sale.', 'woocommerce' ), 'type' => 'boolean', 'sanitize_callback' => 'wc_string_to_bool', 'validate_callback' => 'rest_validate_request_arg', ); $params['min_price'] = array( 'description' => __( 'Limit result set to products based on a minimum price.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); $params['max_price'] = array( 'description' => __( 'Limit result set to products based on a maximum price.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); return $params; } } PKo5\>t]woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-product-reviews-v2-controller.phpnu[/reviews. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Product Reviews Controller Class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Product_Reviews_V1_Controller */ class WC_REST_Product_Reviews_V2_Controller extends WC_REST_Product_Reviews_V1_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v2'; /** * Route base. * * @var string */ protected $rest_base = 'products/(?P[\d]+)/reviews'; /** * Register the routes for product reviews. */ public function register_routes() { parent::register_routes(); register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array( 'args' => array( 'product_id' => array( 'description' => __( 'Unique identifier for the variable product.', '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' ), ) ); } /** * Check if a given request has access to batch manage product reviews. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function batch_items_permissions_check( $request ) { if ( ! wc_rest_check_post_permissions( 'product', 'batch' ) ) { return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to batch manipulate this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * 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 ), 'date_created_gmt' => 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 ); } /** * Bulk create, update and delete items. * * @since 3.0.0 * @param WP_REST_Request $request Full details about the request. * @return array Of WP_Error or WP_REST_Response. */ public function batch_items( $request ) { $items = array_filter( $request->get_params() ); $params = $request->get_url_params(); $product_id = $params['product_id']; $body_params = array(); foreach ( array( 'update', 'create', 'delete' ) as $batch_type ) { if ( ! empty( $items[ $batch_type ] ) ) { $injected_items = array(); foreach ( $items[ $batch_type ] as $item ) { $injected_items[] = is_array( $item ) ? array_merge( array( 'product_id' => $product_id ), $item ) : $item; } $body_params[ $batch_type ] = $injected_items; } } $request = new WP_REST_Request( $request->get_method() ); $request->set_body_params( $body_params ); return parent::batch_items( $request ); } /** * 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' ), ), 'date_created_gmt' => array( 'description' => __( 'The date the review was created, as GMT.', '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 ); } } PKo5\ DdSwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-taxes-v2-controller.phpnu[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[\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; } /** * Only return writable props from schema. * * @param array $schema Schema. * @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. * @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; } } PKo5\}] E E]woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-setting-options-v2-controller.phpnu[[\w-]+)'; /** * Register routes. * * @since 3.0.0 */ public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base, array( 'args' => array( 'group' => array( 'description' => __( 'Settings group ID.', 'woocommerce' ), 'type' => 'string', ), ), 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 . '/batch', array( 'args' => array( 'group' => array( 'description' => __( 'Settings group ID.', 'woocommerce' ), 'type' => 'string', ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'batch_items' ), 'permission_callback' => array( $this, 'update_items_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), 'schema' => array( $this, 'get_public_batch_schema' ), ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( 'args' => array( 'group' => array( 'description' => __( 'Settings group ID.', 'woocommerce' ), 'type' => 'string', ), 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), 'type' => 'string', ), ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'update_items_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Return a single setting. * * @since 3.0.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_item( $request ) { $setting = $this->get_setting( $request['group_id'], $request['id'] ); if ( is_wp_error( $setting ) ) { return $setting; } $response = $this->prepare_item_for_response( $setting, $request ); return rest_ensure_response( $response ); } /** * Return all settings in a group. * * @since 3.0.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $settings = $this->get_group_settings( $request['group_id'] ); if ( is_wp_error( $settings ) ) { return $settings; } $data = array(); foreach ( $settings as $setting_obj ) { $setting = $this->prepare_item_for_response( $setting_obj, $request ); $setting = $this->prepare_response_for_collection( $setting ); if ( $this->is_setting_type_valid( $setting['type'] ) ) { $data[] = $setting; } } return rest_ensure_response( $data ); } /** * Get all settings in a group. * * @since 3.0.0 * @param string $group_id Group ID. * @return array|WP_Error */ public function get_group_settings( $group_id ) { if ( empty( $group_id ) ) { return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'woocommerce' ), array( 'status' => 404 ) ); } $settings = apply_filters( 'woocommerce_settings-' . $group_id, array() ); if ( empty( $settings ) ) { return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'woocommerce' ), array( 'status' => 404 ) ); } $filtered_settings = array(); foreach ( $settings as $setting ) { $option_key = $setting['option_key']; $setting = $this->filter_setting( $setting ); $default = isset( $setting['default'] ) ? $setting['default'] : ''; // Get the option value. if ( is_array( $option_key ) ) { $option = get_option( $option_key[0] ); $setting['value'] = isset( $option[ $option_key[1] ] ) ? $option[ $option_key[1] ] : $default; } else { $admin_setting_value = WC_Admin_Settings::get_option( $option_key, $default ); $setting['value'] = $admin_setting_value; } if ( 'multi_select_countries' === $setting['type'] ) { $setting['options'] = WC()->countries->get_countries(); $setting['type'] = 'multiselect'; } elseif ( 'single_select_country' === $setting['type'] ) { $setting['type'] = 'select'; $setting['options'] = $this->get_countries_and_states(); } $filtered_settings[] = $setting; } return $filtered_settings; } /** * Returns a list of countries and states for use in the base location setting. * * @since 3.0.7 * @return array Array of states and countries. */ private function get_countries_and_states() { $countries = WC()->countries->get_countries(); if ( ! $countries ) { return array(); } $output = array(); foreach ( $countries as $key => $value ) { $states = WC()->countries->get_states( $key ); if ( $states ) { foreach ( $states as $state_key => $state_value ) { $output[ $key . ':' . $state_key ] = $value . ' - ' . $state_value; } } else { $output[ $key ] = $value; } } return $output; } /** * Get setting data. * * @since 3.0.0 * @param string $group_id Group ID. * @param string $setting_id Setting ID. * @return stdClass|WP_Error */ public function get_setting( $group_id, $setting_id ) { if ( empty( $setting_id ) ) { return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); } $settings = $this->get_group_settings( $group_id ); if ( is_wp_error( $settings ) ) { return $settings; } $array_key = array_keys( wp_list_pluck( $settings, 'id' ), $setting_id ); if ( empty( $array_key ) ) { return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); } $setting = $settings[ $array_key[0] ]; if ( ! $this->is_setting_type_valid( $setting['type'] ) ) { return new WP_Error( 'rest_setting_setting_invalid', __( 'Invalid setting.', 'woocommerce' ), array( 'status' => 404 ) ); } return $setting; } /** * Bulk create, update and delete items. * * @since 3.0.0 * @param WP_REST_Request $request Full details about the request. * @return array Of WP_Error or WP_REST_Response. */ public function batch_items( $request ) { // Get the request params. $items = array_filter( $request->get_params() ); /* * Since our batch settings update is group-specific and matches based on the route, * we inject the URL parameters (containing group) into the batch items */ if ( ! empty( $items['update'] ) ) { $to_update = array(); foreach ( $items['update'] as $item ) { $to_update[] = array_merge( $request->get_url_params(), $item ); } $request = new WP_REST_Request( $request->get_method() ); $request->set_body_params( array( 'update' => $to_update ) ); } return parent::batch_items( $request ); } /** * Update a single setting in a group. * * @since 3.0.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function update_item( $request ) { $setting = $this->get_setting( $request['group_id'], $request['id'] ); if ( is_wp_error( $setting ) ) { return $setting; } if ( is_callable( array( $this, 'validate_setting_' . $setting['type'] . '_field' ) ) ) { $value = $this->{'validate_setting_' . $setting['type'] . '_field'}( $request['value'], $setting ); } else { $value = $this->validate_setting_text_field( $request['value'], $setting ); } if ( is_wp_error( $value ) ) { return $value; } if ( is_array( $setting['option_key'] ) ) { $setting['value'] = $value; $option_key = $setting['option_key']; $prev = get_option( $option_key[0] ); $prev[ $option_key[1] ] = $request['value']; update_option( $option_key[0], $prev ); } else { $update_data = array(); $update_data[ $setting['option_key'] ] = $value; $setting['value'] = $value; WC_Admin_Settings::save_fields( array( $setting ), $update_data ); } $response = $this->prepare_item_for_response( $setting, $request ); return rest_ensure_response( $response ); } /** * Prepare a single setting object for response. * * @since 3.0.0 * @param object $item Setting object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response Response data. */ public function prepare_item_for_response( $item, $request ) { unset( $item['option_key'] ); $data = $this->filter_setting( $item ); $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, empty( $request['context'] ) ? 'view' : $request['context'] ); $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $data['id'], $request['group_id'] ) ); return $response; } /** * Prepare links for the request. * * @since 3.0.0 * @param string $setting_id Setting ID. * @param string $group_id Group ID. * @return array Links for the given setting. */ protected function prepare_links( $setting_id, $group_id ) { $base = str_replace( '(?P[\w-]+)', $group_id, $this->rest_base ); $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $base, $setting_id ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ), ), ); return $links; } /** * Makes sure the current user has access to READ the settings APIs. * * @since 3.0.0 * @param WP_REST_Request $request Full data 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; } /** * Makes sure the current user has access to WRITE the settings APIs. * * @since 3.0.0 * @param WP_REST_Request $request Full data 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_edit', __( 'Sorry, you cannot edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Filters out bad values from the settings array/filter so we * only return known values via the API. * * @since 3.0.0 * @param array $setting Settings. * @return array */ public function filter_setting( $setting ) { $setting = array_intersect_key( $setting, array_flip( array_filter( array_keys( $setting ), array( $this, 'allowed_setting_keys' ) ) ) ); if ( empty( $setting['options'] ) ) { unset( $setting['options'] ); } if ( 'image_width' === $setting['type'] ) { $setting = $this->cast_image_width( $setting ); } return $setting; } /** * For image_width, Crop can return "0" instead of false -- so we want * to make sure we return these consistently the same we accept them. * * @todo remove in 4.0 * @since 3.0.0 * @param array $setting Settings. * @return array */ public function cast_image_width( $setting ) { foreach ( array( 'default', 'value' ) as $key ) { if ( isset( $setting[ $key ] ) ) { $setting[ $key ]['width'] = intval( $setting[ $key ]['width'] ); $setting[ $key ]['height'] = intval( $setting[ $key ]['height'] ); $setting[ $key ]['crop'] = (bool) $setting[ $key ]['crop']; } } return $setting; } /** * Callback for allowed keys for each setting response. * * @since 3.0.0 * @param string $key Key to check. * @return boolean */ public function allowed_setting_keys( $key ) { return in_array( $key, array( 'id', 'label', 'description', 'default', 'tip', 'placeholder', 'type', 'options', 'value', 'option_key', ) ); } /** * Boolean for if a setting type is a valid supported setting type. * * @since 3.0.0 * @param string $type Type. * @return bool */ public function is_setting_type_valid( $type ) { return in_array( $type, array( 'text', // Validates with validate_setting_text_field. 'email', // Validates with validate_setting_text_field. 'number', // Validates with validate_setting_text_field. 'color', // Validates with validate_setting_text_field. 'password', // Validates with validate_setting_text_field. 'textarea', // Validates with validate_setting_textarea_field. 'select', // Validates with validate_setting_select_field. 'multiselect', // Validates with validate_setting_multiselect_field. 'radio', // Validates with validate_setting_radio_field (-> validate_setting_select_field). 'checkbox', // Validates with validate_setting_checkbox_field. 'image_width', // Validates with validate_setting_image_width_field. 'thumbnail_cropping', // Validates with validate_setting_text_field. ) ); } /** * Get the settings schema, conforming to JSON Schema. * * @since 3.0.0 * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'setting', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_title', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'label' => array( 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'value' => array( 'description' => __( 'Setting value.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'default' => array( 'description' => __( 'Default value for the setting.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'tip' => array( 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'placeholder' => array( 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'type' => array( 'description' => __( 'Type of setting.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox', 'thumbnail_cropping' ), 'readonly' => true, ), 'options' => array( 'description' => __( 'Array of options (key value pairs) for inputs such as select, multiselect, and radio buttons.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\?:ZZewoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-product-attribute-terms-v2-controller.phpnu[/terms endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Product Attribute Terms controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Product_Attribute_Terms_V1_Controller */ class WC_REST_Product_Attribute_Terms_V2_Controller extends WC_REST_Product_Attribute_Terms_V1_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v2'; } PKo5\zkoYwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-tax-classes-v2-controller.phpnu[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(), false ), 'date_created_gmt' => wc_rest_prepare_date_response( $webhook->get_date_created() ), 'date_modified' => wc_rest_prepare_date_response( $webhook->get_date_modified(), false ), 'date_modified_gmt' => 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(), $request ) ); /** * 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 ); } /** * Get the default REST API version. * * @since 3.0.0 * @return string */ protected function get_default_api_version() { return 'wp_api_v2'; } /** * 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_created_gmt' => array( 'description' => __( 'The date the webhook was created, as GMT.', '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, ), 'date_modified_gmt' => array( 'description' => __( 'The date the webhook was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\%Uwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-reports-v2-controller.phpnu[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 view system status. * * @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' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Get a system status info, by section. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $fields = $this->get_fields_for_response( $request ); $mappings = $this->get_item_mappings_per_fields( $fields ); $response = $this->prepare_item_for_response( $mappings, $request ); return rest_ensure_response( $response ); } /** * Get the system status schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'system_status', 'type' => 'object', 'properties' => array( 'environment' => array( 'description' => __( 'Environment.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( 'home_url' => array( 'description' => __( 'Home URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view' ), 'readonly' => true, ), 'site_url' => array( 'description' => __( 'Site URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view' ), 'readonly' => true, ), 'version' => array( 'description' => __( 'WooCommerce version.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'log_directory' => array( 'description' => __( 'Log directory.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'log_directory_writable' => array( 'description' => __( 'Is log directory writable?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'wp_version' => array( 'description' => __( 'WordPress version.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'wp_multisite' => array( 'description' => __( 'Is WordPress multisite?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'wp_memory_limit' => array( 'description' => __( 'WordPress memory limit.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), 'wp_debug_mode' => array( 'description' => __( 'Is WordPress debug mode active?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'wp_cron' => array( 'description' => __( 'Are WordPress cron jobs enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'language' => array( 'description' => __( 'WordPress language.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'server_info' => array( 'description' => __( 'Server info.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'php_version' => array( 'description' => __( 'PHP version.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'php_post_max_size' => array( 'description' => __( 'PHP post max size.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), 'php_max_execution_time' => array( 'description' => __( 'PHP max execution time.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), 'php_max_input_vars' => array( 'description' => __( 'PHP max input vars.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), 'curl_version' => array( 'description' => __( 'cURL version.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'suhosin_installed' => array( 'description' => __( 'Is SUHOSIN installed?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'max_upload_size' => array( 'description' => __( 'Max upload size.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), 'mysql_version' => array( 'description' => __( 'MySQL version.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'mysql_version_string' => array( 'description' => __( 'MySQL version string.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'default_timezone' => array( 'description' => __( 'Default timezone.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'fsockopen_or_curl_enabled' => array( 'description' => __( 'Is fsockopen/cURL enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'soapclient_enabled' => array( 'description' => __( 'Is SoapClient class enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'domdocument_enabled' => array( 'description' => __( 'Is DomDocument class enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'gzip_enabled' => array( 'description' => __( 'Is GZip enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'mbstring_enabled' => array( 'description' => __( 'Is mbstring enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'remote_post_successful' => array( 'description' => __( 'Remote POST successful?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'remote_post_response' => array( 'description' => __( 'Remote POST response.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'remote_get_successful' => array( 'description' => __( 'Remote GET successful?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'remote_get_response' => array( 'description' => __( 'Remote GET response.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ), 'database' => array( 'description' => __( 'Database.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( 'wc_database_version' => array( 'description' => __( 'WC database version.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'database_prefix' => array( 'description' => __( 'Database prefix.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'maxmind_geoip_database' => array( 'description' => __( 'MaxMind GeoIP database.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'database_tables' => array( 'description' => __( 'Database tables.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), ), ), 'active_plugins' => array( 'description' => __( 'Active plugins.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), 'inactive_plugins' => array( 'description' => __( 'Inactive plugins.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), 'dropins_mu_plugins' => array( 'description' => __( 'Dropins & MU plugins.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), 'theme' => array( 'description' => __( 'Theme.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( 'name' => array( 'description' => __( 'Theme name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'version' => array( 'description' => __( 'Theme version.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'version_latest' => array( 'description' => __( 'Latest version of theme.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'author_url' => array( 'description' => __( 'Theme author URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view' ), 'readonly' => true, ), 'is_child_theme' => array( 'description' => __( 'Is this theme a child theme?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'has_woocommerce_support' => array( 'description' => __( 'Does the theme declare WooCommerce support?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'has_woocommerce_file' => array( 'description' => __( 'Does the theme have a woocommerce.php file?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'has_outdated_templates' => array( 'description' => __( 'Does this theme have outdated templates?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'overrides' => array( 'description' => __( 'Template overrides.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), 'parent_name' => array( 'description' => __( 'Parent theme name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'parent_version' => array( 'description' => __( 'Parent theme version.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'parent_author_url' => array( 'description' => __( 'Parent theme author URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view' ), 'readonly' => true, ), ), ), 'settings' => array( 'description' => __( 'Settings.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( 'api_enabled' => array( 'description' => __( 'REST API enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'force_ssl' => array( 'description' => __( 'SSL forced?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'currency' => array( 'description' => __( 'Currency.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'currency_symbol' => array( 'description' => __( 'Currency symbol.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'currency_position' => array( 'description' => __( 'Currency position.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'thousand_separator' => array( 'description' => __( 'Thousand separator.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'decimal_separator' => array( 'description' => __( 'Decimal separator.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'number_of_decimals' => array( 'description' => __( 'Number of decimals.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), 'geolocation_enabled' => array( 'description' => __( 'Geolocation enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'taxonomies' => array( 'description' => __( 'Taxonomy terms for product/order statuses.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), 'product_visibility_terms' => array( 'description' => __( 'Terms in the product visibility taxonomy.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), ), ), 'security' => array( 'description' => __( 'Security.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( 'secure_connection' => array( 'description' => __( 'Is the connection to your store secure?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), 'hide_errors' => array( 'description' => __( 'Hide errors from visitors?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), ), ), 'pages' => array( 'description' => __( 'WooCommerce pages.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), 'post_type_counts' => array( 'description' => __( 'Total post count.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), ), ); return $this->add_additional_fields_schema( $schema ); } /** * Return an array of sections and the data associated with each. * * @deprecated 3.9.0 * @return array */ public function get_item_mappings() { return array( 'environment' => $this->get_environment_info(), 'database' => $this->get_database_info(), 'active_plugins' => $this->get_active_plugins(), 'inactive_plugins' => $this->get_inactive_plugins(), 'dropins_mu_plugins' => $this->get_dropins_mu_plugins(), 'theme' => $this->get_theme_info(), 'settings' => $this->get_settings(), 'security' => $this->get_security_info(), 'pages' => $this->get_pages(), 'post_type_counts' => $this->get_post_type_counts(), ); } /** * Return an array of sections and the data associated with each. * * @since 3.9.0 * @param array $fields List of fields to be included on the response. * @return array */ public function get_item_mappings_per_fields( $fields ) { return array( 'environment' => $this->get_environment_info_per_fields( $fields ), 'database' => $this->get_database_info(), 'active_plugins' => $this->get_active_plugins(), 'inactive_plugins' => $this->get_inactive_plugins(), 'dropins_mu_plugins' => $this->get_dropins_mu_plugins(), 'theme' => $this->get_theme_info(), 'settings' => $this->get_settings(), 'security' => $this->get_security_info(), 'pages' => $this->get_pages(), 'post_type_counts' => $this->get_post_type_counts(), ); } /** * Get array of environment information. Includes thing like software * versions, and various server settings. * * @deprecated 3.9.0 * @return array */ public function get_environment_info() { return $this->get_environment_info_per_fields( array( 'environment' ) ); } /** * Check if field item exists. * * @since 3.9.0 * @param string $section Fields section. * @param array $items List of items to check for. * @param array $fields List of fields to be included on the response. * @return bool */ private function check_if_field_item_exists( $section, $items, $fields ) { if ( ! in_array( $section, $fields, true ) ) { return false; } $exclude = array(); foreach ( $fields as $field ) { $values = explode( '.', $field ); if ( $section !== $values[0] || empty( $values[1] ) ) { continue; } $exclude[] = $values[1]; } return 0 <= count( array_intersect( $items, $exclude ) ); } /** * Get array of environment information. Includes thing like software * versions, and various server settings. * * @param array $fields List of fields to be included on the response. * @return array */ public function get_environment_info_per_fields( $fields ) { global $wpdb; $enable_remote_post = $this->check_if_field_item_exists( 'environment', array( 'remote_post_successful', 'remote_post_response' ), $fields ); $enable_remote_get = $this->check_if_field_item_exists( 'environment', array( 'remote_get_successful', 'remote_get_response' ), $fields ); // Figure out cURL version, if installed. $curl_version = ''; if ( function_exists( 'curl_version' ) ) { $curl_version = curl_version(); $curl_version = $curl_version['version'] . ', ' . $curl_version['ssl_version']; } elseif ( extension_loaded( 'curl' ) ) { $curl_version = __( 'cURL installed but unable to retrieve version.', 'woocommerce' ); } // WP memory limit. $wp_memory_limit = wc_let_to_num( WP_MEMORY_LIMIT ); if ( function_exists( 'memory_get_usage' ) ) { $wp_memory_limit = max( $wp_memory_limit, wc_let_to_num( @ini_get( 'memory_limit' ) ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } // Test POST requests. $post_response_successful = null; $post_response_code = null; if ( $enable_remote_post ) { $post_response_code = get_transient( 'woocommerce_test_remote_post' ); if ( false === $post_response_code || is_wp_error( $post_response_code ) ) { $response = wp_safe_remote_post( 'https://www.paypal.com/cgi-bin/webscr', array( 'timeout' => 10, 'user-agent' => 'WooCommerce/' . WC()->version, 'httpversion' => '1.1', 'body' => array( 'cmd' => '_notify-validate', ), ) ); if ( ! is_wp_error( $response ) ) { $post_response_code = $response['response']['code']; } set_transient( 'woocommerce_test_remote_post', $post_response_code, HOUR_IN_SECONDS ); } $post_response_successful = ! is_wp_error( $post_response_code ) && $post_response_code >= 200 && $post_response_code < 300; } // Test GET requests. $get_response_successful = null; $get_response_code = null; if ( $enable_remote_get ) { $get_response_code = get_transient( 'woocommerce_test_remote_get' ); if ( false === $get_response_code || is_wp_error( $get_response_code ) ) { $response = wp_safe_remote_get( 'https://woocommerce.com/wc-api/product-key-api?request=ping&network=' . ( is_multisite() ? '1' : '0' ) ); if ( ! is_wp_error( $response ) ) { $get_response_code = $response['response']['code']; } set_transient( 'woocommerce_test_remote_get', $get_response_code, HOUR_IN_SECONDS ); } $get_response_successful = ! is_wp_error( $get_response_code ) && $get_response_code >= 200 && $get_response_code < 300; } $database_version = wc_get_server_database_version(); // Return all environment info. Described by JSON Schema. return array( 'home_url' => get_option( 'home' ), 'site_url' => get_option( 'siteurl' ), 'version' => WC()->version, 'log_directory' => WC_LOG_DIR, 'log_directory_writable' => (bool) @fopen( WC_LOG_DIR . 'test-log.log', 'a' ), // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen 'wp_version' => get_bloginfo( 'version' ), 'wp_multisite' => is_multisite(), 'wp_memory_limit' => $wp_memory_limit, 'wp_debug_mode' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ), 'wp_cron' => ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ), 'language' => get_locale(), 'external_object_cache' => wp_using_ext_object_cache(), 'server_info' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? wc_clean( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '', 'php_version' => phpversion(), 'php_post_max_size' => wc_let_to_num( ini_get( 'post_max_size' ) ), 'php_max_execution_time' => (int) ini_get( 'max_execution_time' ), 'php_max_input_vars' => (int) ini_get( 'max_input_vars' ), 'curl_version' => $curl_version, 'suhosin_installed' => extension_loaded( 'suhosin' ), 'max_upload_size' => wp_max_upload_size(), 'mysql_version' => $database_version['number'], 'mysql_version_string' => $database_version['string'], 'default_timezone' => date_default_timezone_get(), 'fsockopen_or_curl_enabled' => ( function_exists( 'fsockopen' ) || function_exists( 'curl_init' ) ), 'soapclient_enabled' => class_exists( 'SoapClient' ), 'domdocument_enabled' => class_exists( 'DOMDocument' ), 'gzip_enabled' => is_callable( 'gzopen' ), 'mbstring_enabled' => extension_loaded( 'mbstring' ), 'remote_post_successful' => $post_response_successful, 'remote_post_response' => is_wp_error( $post_response_code ) ? $post_response_code->get_error_message() : $post_response_code, 'remote_get_successful' => $get_response_successful, 'remote_get_response' => is_wp_error( $get_response_code ) ? $get_response_code->get_error_message() : $get_response_code, ); } /** * Add prefix to table. * * @param string $table Table name. * @return stromg */ protected function add_db_table_prefix( $table ) { global $wpdb; return $wpdb->prefix . $table; } /** * Get array of database information. Version, prefix, and table existence. * * @return array */ public function get_database_info() { global $wpdb; $tables = array(); $database_size = array(); // It is not possible to get the database name from some classes that replace wpdb (e.g., HyperDB) // and that is why this if condition is needed. if ( defined( 'DB_NAME' ) ) { $database_table_information = $wpdb->get_results( $wpdb->prepare( "SELECT table_name AS 'name', engine AS 'engine', round( ( data_length / 1024 / 1024 ), 2 ) 'data', round( ( index_length / 1024 / 1024 ), 2 ) 'index' FROM information_schema.TABLES WHERE table_schema = %s ORDER BY name ASC;", DB_NAME ) ); // WC Core tables to check existence of. $core_tables = apply_filters( 'woocommerce_database_tables', array( 'woocommerce_sessions', 'woocommerce_api_keys', 'woocommerce_attribute_taxonomies', 'woocommerce_downloadable_product_permissions', 'woocommerce_order_items', 'woocommerce_order_itemmeta', 'woocommerce_tax_rates', 'woocommerce_tax_rate_locations', 'woocommerce_shipping_zones', 'woocommerce_shipping_zone_locations', 'woocommerce_shipping_zone_methods', 'woocommerce_payment_tokens', 'woocommerce_payment_tokenmeta', 'woocommerce_log', ) ); /** * Adding the prefix to the tables array, for backwards compatibility. * * If we changed the tables above to include the prefix, then any filters against that table could break. */ $core_tables = array_map( array( $this, 'add_db_table_prefix' ), $core_tables ); /** * Organize WooCommerce and non-WooCommerce tables separately for display purposes later. * * To ensure we include all WC tables, even if they do not exist, pre-populate the WC array with all the tables. */ $tables = array( 'woocommerce' => array_fill_keys( $core_tables, false ), 'other' => array(), ); $database_size = array( 'data' => 0, 'index' => 0, ); $site_tables_prefix = $wpdb->get_blog_prefix( get_current_blog_id() ); $global_tables = $wpdb->tables( 'global', true ); foreach ( $database_table_information as $table ) { // Only include tables matching the prefix of the current site, this is to prevent displaying all tables on a MS install not relating to the current. if ( is_multisite() && 0 !== strpos( $table->name, $site_tables_prefix ) && ! in_array( $table->name, $global_tables, true ) ) { continue; } $table_type = in_array( $table->name, $core_tables, true ) ? 'woocommerce' : 'other'; $tables[ $table_type ][ $table->name ] = array( 'data' => $table->data, 'index' => $table->index, 'engine' => $table->engine, ); $database_size['data'] += $table->data; $database_size['index'] += $table->index; } } // Return all database info. Described by JSON Schema. return array( 'wc_database_version' => get_option( 'woocommerce_db_version' ), 'database_prefix' => $wpdb->prefix, 'maxmind_geoip_database' => '', 'database_tables' => $tables, 'database_size' => $database_size, ); } /** * Get array of counts of objects. Orders, products, etc. * * @return array */ public function get_post_type_counts() { global $wpdb; $post_type_counts = $wpdb->get_results( "SELECT post_type AS 'type', count(1) AS 'count' FROM {$wpdb->posts} GROUP BY post_type;" ); return is_array( $post_type_counts ) ? $post_type_counts : array(); } /** * Get a list of plugins active on the site. * * @return array */ public function get_active_plugins() { require_once ABSPATH . 'wp-admin/includes/plugin.php'; if ( ! function_exists( 'get_plugin_data' ) ) { return array(); } $active_plugins = (array) get_option( 'active_plugins', array() ); if ( is_multisite() ) { $network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) ); $active_plugins = array_merge( $active_plugins, $network_activated_plugins ); } $active_plugins_data = array(); foreach ( $active_plugins as $plugin ) { $data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); $active_plugins_data[] = $this->format_plugin_data( $plugin, $data ); } return $active_plugins_data; } /** * Get a list of inplugins active on the site. * * @return array */ public function get_inactive_plugins() { require_once ABSPATH . 'wp-admin/includes/plugin.php'; if ( ! function_exists( 'get_plugins' ) ) { return array(); } $plugins = get_plugins(); $active_plugins = (array) get_option( 'active_plugins', array() ); if ( is_multisite() ) { $network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) ); $active_plugins = array_merge( $active_plugins, $network_activated_plugins ); } $plugins_data = array(); foreach ( $plugins as $plugin => $data ) { if ( in_array( $plugin, $active_plugins, true ) ) { continue; } $plugins_data[] = $this->format_plugin_data( $plugin, $data ); } return $plugins_data; } /** * Format plugin data, including data on updates, into a standard format. * * @since 3.6.0 * @param string $plugin Plugin directory/file. * @param array $data Plugin data from WP. * @return array Formatted data. */ protected function format_plugin_data( $plugin, $data ) { require_once ABSPATH . 'wp-admin/includes/update.php'; if ( ! function_exists( 'get_plugin_updates' ) ) { return array(); } // Use WP API to lookup latest updates for plugins. WC_Helper injects updates for premium plugins. if ( empty( $this->available_updates ) ) { $this->available_updates = get_plugin_updates(); } $version_latest = $data['Version']; // Find latest version. if ( isset( $this->available_updates[ $plugin ]->update->new_version ) ) { $version_latest = $this->available_updates[ $plugin ]->update->new_version; } return array( 'plugin' => $plugin, 'name' => $data['Name'], 'version' => $data['Version'], 'version_latest' => $version_latest, 'url' => $data['PluginURI'], 'author_name' => $data['AuthorName'], 'author_url' => esc_url_raw( $data['AuthorURI'] ), 'network_activated' => $data['Network'], ); } /** * Get a list of Dropins and MU plugins. * * @since 3.6.0 * @return array */ public function get_dropins_mu_plugins() { $dropins = get_dropins(); $plugins = array( 'dropins' => array(), 'mu_plugins' => array(), ); foreach ( $dropins as $key => $dropin ) { $plugins['dropins'][] = array( 'plugin' => $key, 'name' => $dropin['Name'], ); } $mu_plugins = get_mu_plugins(); foreach ( $mu_plugins as $plugin => $mu_plugin ) { $plugins['mu_plugins'][] = array( 'plugin' => $plugin, 'name' => $mu_plugin['Name'], 'version' => $mu_plugin['Version'], 'url' => $mu_plugin['PluginURI'], 'author_name' => $mu_plugin['AuthorName'], 'author_url' => esc_url_raw( $mu_plugin['AuthorURI'] ), ); } return $plugins; } /** * Get info on the current active theme, info on parent theme (if presnet) * and a list of template overrides. * * @return array */ public function get_theme_info() { $active_theme = wp_get_theme(); // Get parent theme info if this theme is a child theme, otherwise // pass empty info in the response. if ( is_child_theme() ) { $parent_theme = wp_get_theme( $active_theme->template ); $parent_theme_info = array( 'parent_name' => $parent_theme->name, 'parent_version' => $parent_theme->version, 'parent_version_latest' => WC_Admin_Status::get_latest_theme_version( $parent_theme ), 'parent_author_url' => $parent_theme->{'Author URI'}, ); } else { $parent_theme_info = array( 'parent_name' => '', 'parent_version' => '', 'parent_version_latest' => '', 'parent_author_url' => '', ); } /** * Scan the theme directory for all WC templates to see if our theme * overrides any of them. */ $override_files = array(); $outdated_templates = false; $scan_files = WC_Admin_Status::scan_template_files( WC()->plugin_path() . '/templates/' ); foreach ( $scan_files as $file ) { $located = apply_filters( 'wc_get_template', $file, $file, array(), WC()->template_path(), WC()->plugin_path() . '/templates/' ); if ( file_exists( $located ) ) { $theme_file = $located; } elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) { $theme_file = get_stylesheet_directory() . '/' . $file; } elseif ( file_exists( get_stylesheet_directory() . '/' . WC()->template_path() . $file ) ) { $theme_file = get_stylesheet_directory() . '/' . WC()->template_path() . $file; } elseif ( file_exists( get_template_directory() . '/' . $file ) ) { $theme_file = get_template_directory() . '/' . $file; } elseif ( file_exists( get_template_directory() . '/' . WC()->template_path() . $file ) ) { $theme_file = get_template_directory() . '/' . WC()->template_path() . $file; } else { $theme_file = false; } if ( ! empty( $theme_file ) ) { $core_version = WC_Admin_Status::get_file_version( WC()->plugin_path() . '/templates/' . $file ); $theme_version = WC_Admin_Status::get_file_version( $theme_file ); if ( $core_version && ( empty( $theme_version ) || version_compare( $theme_version, $core_version, '<' ) ) ) { if ( ! $outdated_templates ) { $outdated_templates = true; } } $override_files[] = array( 'file' => str_replace( WP_CONTENT_DIR . '/themes/', '', $theme_file ), 'version' => $theme_version, 'core_version' => $core_version, ); } } $active_theme_info = array( 'name' => $active_theme->name, 'version' => $active_theme->version, 'version_latest' => WC_Admin_Status::get_latest_theme_version( $active_theme ), 'author_url' => esc_url_raw( $active_theme->{'Author URI'} ), 'is_child_theme' => is_child_theme(), 'has_woocommerce_support' => current_theme_supports( 'woocommerce' ), 'has_woocommerce_file' => ( file_exists( get_stylesheet_directory() . '/woocommerce.php' ) || file_exists( get_template_directory() . '/woocommerce.php' ) ), 'has_outdated_templates' => $outdated_templates, 'overrides' => $override_files, ); return array_merge( $active_theme_info, $parent_theme_info ); } /** * Get some setting values for the site that are useful for debugging * purposes. For full settings access, use the settings api. * * @return array */ public function get_settings() { // Get a list of terms used for product/order taxonomies. $term_response = array(); $terms = get_terms( 'product_type', array( 'hide_empty' => 0 ) ); foreach ( $terms as $term ) { $term_response[ $term->slug ] = strtolower( $term->name ); } // Get a list of terms used for product visibility. $product_visibility_terms = array(); $terms = get_terms( 'product_visibility', array( 'hide_empty' => 0 ) ); foreach ( $terms as $term ) { $product_visibility_terms[ $term->slug ] = strtolower( $term->name ); } // Check if WooCommerce.com account is connected. $woo_com_connected = 'no'; $helper_options = get_option( 'woocommerce_helper_data', array() ); if ( array_key_exists( 'auth', $helper_options ) && ! empty( $helper_options['auth'] ) ) { $woo_com_connected = 'yes'; } // Return array of useful settings for debugging. return array( 'api_enabled' => 'yes' === get_option( 'woocommerce_api_enabled' ), 'force_ssl' => 'yes' === get_option( 'woocommerce_force_ssl_checkout' ), 'currency' => get_woocommerce_currency(), 'currency_symbol' => get_woocommerce_currency_symbol(), 'currency_position' => get_option( 'woocommerce_currency_pos' ), 'thousand_separator' => wc_get_price_thousand_separator(), 'decimal_separator' => wc_get_price_decimal_separator(), 'number_of_decimals' => wc_get_price_decimals(), 'geolocation_enabled' => in_array( get_option( 'woocommerce_default_customer_address' ), array( 'geolocation_ajax', 'geolocation' ), true ), 'taxonomies' => $term_response, 'product_visibility_terms' => $product_visibility_terms, 'woocommerce_com_connected' => $woo_com_connected, ); } /** * Returns security tips. * * @return array */ public function get_security_info() { $check_page = wc_get_page_permalink( 'shop' ); return array( 'secure_connection' => 'https' === substr( $check_page, 0, 5 ), 'hide_errors' => ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ), ); } /** * Returns a mini-report on WC pages and if they are configured correctly: * Present, visible, and including the correct shortcode. * * @return array */ public function get_pages() { // WC pages to check against. $check_pages = array( _x( 'Shop base', 'Page setting', 'woocommerce' ) => array( 'option' => 'woocommerce_shop_page_id', 'shortcode' => '', ), _x( 'Cart', 'Page setting', 'woocommerce' ) => array( 'option' => 'woocommerce_cart_page_id', 'shortcode' => '[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']', ), _x( 'Checkout', 'Page setting', 'woocommerce' ) => array( 'option' => 'woocommerce_checkout_page_id', 'shortcode' => '[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']', ), _x( 'My account', 'Page setting', 'woocommerce' ) => array( 'option' => 'woocommerce_myaccount_page_id', 'shortcode' => '[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']', ), _x( 'Terms and conditions', 'Page setting', 'woocommerce' ) => array( 'option' => 'woocommerce_terms_page_id', 'shortcode' => '', ), ); $pages_output = array(); foreach ( $check_pages as $page_name => $values ) { $page_id = get_option( $values['option'] ); $page_set = false; $page_exists = false; $page_visible = false; $shortcode_present = false; $shortcode_required = false; // Page checks. if ( $page_id ) { $page_set = true; } if ( get_post( $page_id ) ) { $page_exists = true; } if ( 'publish' === get_post_status( $page_id ) ) { $page_visible = true; } // Shortcode checks. if ( $values['shortcode'] && get_post( $page_id ) ) { $shortcode_required = true; $page = get_post( $page_id ); if ( strstr( $page->post_content, $values['shortcode'] ) ) { $shortcode_present = true; } } // Wrap up our findings into an output array. $pages_output[] = array( 'page_name' => $page_name, 'page_id' => $page_id, 'page_set' => $page_set, 'page_exists' => $page_exists, 'page_visible' => $page_visible, 'shortcode' => $values['shortcode'], 'shortcode_required' => $shortcode_required, 'shortcode_present' => $shortcode_present, ); } return $pages_output; } /** * Get any query params needed. * * @return array */ public function get_collection_params() { return array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ); } /** * Prepare the system status response * * @param array $system_status System status data. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function prepare_item_for_response( $system_status, $request ) { $data = $this->add_additional_fields_to_object( $system_status, $request ); $data = $this->filter_response_by_context( $data, 'view' ); $response = rest_ensure_response( $data ); /** * Filter the system status returned from the REST API. * * @param WP_REST_Response $response The response object. * @param mixed $system_status System status * @param WP_REST_Request $request Request object. */ return apply_filters( 'woocommerce_rest_prepare_system_status', $response, $system_status, $request ); } } PKo5\_!!`woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-report-top-sellers-v2-controller.phpnu[ */ 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[\w-]+)', array( 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), 'type' => 'string', ), ), 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 view shipping methods. * * @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( 'shipping_methods', '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 shipping method. * * @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( 'shipping_methods', '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 shipping methods. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $wc_shipping = WC_Shipping::instance(); $response = array(); foreach ( $wc_shipping->get_shipping_methods() as $id => $shipping_method ) { $method = $this->prepare_item_for_response( $shipping_method, $request ); $method = $this->prepare_response_for_collection( $method ); $response[] = $method; } return rest_ensure_response( $response ); } /** * Get a single Shipping Method. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function get_item( $request ) { $wc_shipping = WC_Shipping::instance(); $methods = $wc_shipping->get_shipping_methods(); if ( empty( $methods[ $request['id'] ] ) ) { return new WP_Error( 'woocommerce_rest_shipping_method_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); } $method = $methods[ $request['id'] ]; $response = $this->prepare_item_for_response( $method, $request ); return rest_ensure_response( $response ); } /** * Prepare a shipping method for response. * * @param WC_Shipping_Method $method Shipping method object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response Response data. */ public function prepare_item_for_response( $method, $request ) { $data = array( 'id' => $method->id, 'title' => $method->method_title, 'description' => $method->method_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( $this->prepare_links( $method, $request ) ); /** * Filter shipping methods object returned from the REST API. * * @param WP_REST_Response $response The response object. * @param WC_Shipping_Method $method Shipping method object used to create response. * @param WP_REST_Request $request Request object. */ return apply_filters( 'woocommerce_rest_prepare_shipping_method', $response, $method, $request ); } /** * Prepare links for the request. * * @param WC_Shipping_Method $method Shipping method object. * @param WP_REST_Request $request Request object. * @return array */ protected function prepare_links( $method, $request ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $method->id ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Get the shipping method schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'shipping_method', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Method ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'title' => array( 'description' => __( 'Shipping method title.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'Shipping method description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } /** * Get any query params needed. * * @return array */ public function get_collection_params() { return array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ); } } PKo5\CNPPfwoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-product-shipping-classes-v2-controller.phpnu[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[\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. Return false if object is not of required type. * * @since 3.0.0 * @param int $id Object ID. * @return WC_Data|bool */ protected function get_object( $id ) { $order = wc_get_order( $id ); // In case id is a refund's id (or it's not an order at all), don't expose it via /orders/ path. if ( ! $order || 'shop_order_refund' === $order->get_type() ) { return false; } return $order; } /** * Expands an order item to get its data. * * @param WC_Order_item $item Order item data. * @return array */ protected function get_order_item_data( $item ) { $data = $item->get_data(); $format_decimal = array( 'subtotal', 'subtotal_tax', 'total', 'total_tax', 'tax_total', 'shipping_tax_total' ); // Format decimal values. foreach ( $format_decimal as $key ) { if ( isset( $data[ $key ] ) ) { $data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] ); } } // Add SKU and PRICE to products. if ( is_callable( array( $item, 'get_product' ) ) ) { $data['sku'] = $item->get_product() ? $item->get_product()->get_sku() : null; $data['price'] = $item->get_quantity() ? $item->get_total() / $item->get_quantity() : 0; } // Format taxes. if ( ! empty( $data['taxes']['total'] ) ) { $taxes = array(); foreach ( $data['taxes']['total'] as $tax_rate_id => $tax ) { $taxes[] = array( 'id' => $tax_rate_id, 'total' => $tax, 'subtotal' => isset( $data['taxes']['subtotal'][ $tax_rate_id ] ) ? $data['taxes']['subtotal'][ $tax_rate_id ] : '', ); } $data['taxes'] = $taxes; } elseif ( isset( $data['taxes'] ) ) { $data['taxes'] = array(); } // Remove names for coupons, taxes and shipping. if ( isset( $data['code'] ) || isset( $data['rate_code'] ) || isset( $data['method_title'] ) ) { unset( $data['name'] ); } // Remove props we don't want to expose. unset( $data['order_id'] ); unset( $data['type'] ); return $data; } /** * 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( 'discount_total', 'discount_tax', 'shipping_total', 'shipping_tax', 'shipping_total', 'shipping_tax', 'cart_tax', 'total', 'total_tax' ); $format_date = array( 'date_created', 'date_modified', 'date_completed', 'date_paid' ); $format_line_items = array( 'line_items', 'tax_lines', 'shipping_lines', 'fee_lines', 'coupon_lines' ); // Format decimal values. foreach ( $format_decimal as $key ) { $data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] ); } // 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 the order status. $data['status'] = 'wc-' === substr( $data['status'], 0, 3 ) ? substr( $data['status'], 3 ) : $data['status']; // Format line items. foreach ( $format_line_items as $key ) { $data[ $key ] = array_values( array_map( array( $this, 'get_order_item_data' ), $data[ $key ] ) ); } // Refunds. $data['refunds'] = array(); foreach ( $object->get_refunds() as $refund ) { $data['refunds'][] = array( 'id' => $refund->get_id(), 'reason' => $refund->get_reason() ? $refund->get_reason() : '', 'total' => '-' . wc_format_decimal( $refund->get_amount(), $this->request['dp'] ), ); } return array( 'id' => $object->get_id(), 'parent_id' => $data['parent_id'], 'number' => $data['number'], 'order_key' => $data['order_key'], 'created_via' => $data['created_via'], 'version' => $data['version'], 'status' => $data['status'], 'currency' => $data['currency'], '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_total' => $data['discount_total'], 'discount_tax' => $data['discount_tax'], 'shipping_total' => $data['shipping_total'], 'shipping_tax' => $data['shipping_tax'], 'cart_tax' => $data['cart_tax'], 'total' => $data['total'], 'total_tax' => $data['total_tax'], 'prices_include_tax' => $data['prices_include_tax'], 'customer_id' => $data['customer_id'], 'customer_ip_address' => $data['customer_ip_address'], 'customer_user_agent' => $data['customer_user_agent'], 'customer_note' => $data['customer_note'], 'billing' => $data['billing'], 'shipping' => $data['shipping'], 'payment_method' => $data['payment_method'], 'payment_method_title' => $data['payment_method_title'], 'transaction_id' => $data['transaction_id'], 'date_paid' => $data['date_paid'], 'date_paid_gmt' => $data['date_paid_gmt'], 'date_completed' => $data['date_completed'], 'date_completed_gmt' => $data['date_completed_gmt'], 'cart_hash' => $data['cart_hash'], 'meta_data' => $data['meta_data'], 'line_items' => $data['line_items'], 'tax_lines' => $data['tax_lines'], 'shipping_lines' => $data['shipping_lines'], 'fee_lines' => $data['fee_lines'], 'coupon_lines' => $data['coupon_lines'], 'refunds' => $data['refunds'], ); } /** * Prepare a single order 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 ) { $this->request = $request; $this->request['dp'] = is_null( $this->request['dp'] ) ? wc_get_price_decimals() : absint( $this->request['dp'] ); $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 links for the request. * * @param WC_Data $object Object data. * @param WP_REST_Request $request Request object. * @return array Links for the given post. */ protected function prepare_links( $object, $request ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); if ( 0 !== (int) $object->get_customer_id() ) { $links['customer'] = array( 'href' => rest_url( sprintf( '/%s/customers/%d', $this->namespace, $object->get_customer_id() ) ), ); } if ( 0 !== (int) $object->get_parent_id() ) { $links['up'] = array( 'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $object->get_parent_id() ) ), ); } return $links; } /** * 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 ) { global $wpdb; $args = parent::prepare_objects_query( $request ); // Set post_status. if ( in_array( $request['status'], $this->get_order_statuses(), true ) ) { $args['post_status'] = 'wc-' . $request['status']; } elseif ( 'any' === $request['status'] ) { $args['post_status'] = 'any'; } else { $args['post_status'] = $request['status']; } if ( isset( $request['customer'] ) ) { if ( ! empty( $args['meta_query'] ) ) { $args['meta_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query } $args['meta_query'][] = array( 'key' => '_customer_user', 'value' => $request['customer'], 'type' => 'NUMERIC', ); } // Search by product. if ( ! empty( $request['product'] ) ) { $order_ids = $wpdb->get_col( $wpdb->prepare( "SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key = '_product_id' AND meta_value = %d ) AND order_item_type = 'line_item'", $request['product'] ) ); // Force WP_Query return empty if don't found any order. $order_ids = ! empty( $order_ids ) ? $order_ids : array( 0 ); $args['post__in'] = $order_ids; } // Search. if ( ! empty( $args['s'] ) ) { $order_ids = wc_order_search( $args['s'] ); if ( ! empty( $order_ids ) ) { unset( $args['s'] ); $args['post__in'] = array_merge( $order_ids, array( 0 ) ); } } /** * Filter the query arguments for a request. * * Enables adding extra arguments or setting defaults for an order 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_orders_prepare_object_query', $args, $request ); return $args; } /** * Only return writable props from schema. * * @param array $schema Schema. * @return bool */ protected function filter_writable_props( $schema ) { return empty( $schema['readonly'] ); } /** * Prepare a single order 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; $order = new WC_Order( $id ); $schema = $this->get_item_schema(); $data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) ); // Handle all writable props. foreach ( $data_keys as $key ) { $value = $request[ $key ]; if ( ! is_null( $value ) ) { switch ( $key ) { case 'status': // Status change should be done later so transitions have new data. break; case 'billing': case 'shipping': $this->update_address( $order, $value, $key ); break; case 'line_items': case 'shipping_lines': case 'fee_lines': case 'coupon_lines': if ( is_array( $value ) ) { foreach ( $value as $item ) { if ( is_array( $item ) ) { if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) { $order->remove_item( $item['id'] ); } else { $this->set_item( $order, $key, $item ); } } } } break; case 'meta_data': if ( is_array( $value ) ) { foreach ( $value as $meta ) { $order->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } } break; default: if ( is_callable( array( $order, "set_{$key}" ) ) ) { $order->{"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 $order 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", $order, $request, $creating ); } /** * Save an object data. * * @since 3.0.0 * @throws WC_REST_Exception But all errors are validated before returning any data. * @param WP_REST_Request $request Full details about the request. * @param bool $creating If is creating a new object. * @return WC_Data|WP_Error */ protected function save_object( $request, $creating = false ) { try { $object = $this->prepare_object_for_database( $request, $creating ); if ( is_wp_error( $object ) ) { return $object; } // Make sure gateways are loaded so hooks from gateways fire on save/create. WC()->payment_gateways(); if ( ! is_null( $request['customer_id'] ) && 0 !== $request['customer_id'] ) { // Make sure customer exists. if ( false === get_user_by( 'id', $request['customer_id'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id', __( 'Customer ID is invalid.', 'woocommerce' ), 400 ); } // Make sure customer is part of blog. if ( is_multisite() && ! is_user_member_of_blog( $request['customer_id'] ) ) { add_user_to_blog( get_current_blog_id(), $request['customer_id'], 'customer' ); } } if ( $creating ) { $object->set_created_via( 'rest-api' ); $object->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) ); $object->calculate_totals(); } else { // If items have changed, recalculate order totals. if ( isset( $request['billing'] ) || isset( $request['shipping'] ) || isset( $request['line_items'] ) || isset( $request['shipping_lines'] ) || isset( $request['fee_lines'] ) || isset( $request['coupon_lines'] ) ) { $object->calculate_totals( true ); } } // Set status. if ( ! empty( $request['status'] ) ) { $object->set_status( $request['status'] ); } $object->save(); // Actions for after the order is saved. if ( true === $request['set_paid'] ) { if ( $creating || $object->needs_payment() ) { $object->payment_complete( $request['transaction_id'] ); } } return $this->get_object( $object->get_id() ); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * Update address. * * @param WC_Order $order Order data. * @param array $posted Posted data. * @param string $type Address type. */ protected function update_address( $order, $posted, $type = 'billing' ) { foreach ( $posted as $key => $value ) { if ( is_callable( array( $order, "set_{$type}_{$key}" ) ) ) { $order->{"set_{$type}_{$key}"}( $value ); } } } /** * Gets the product ID from the SKU or posted ID. * * @throws WC_REST_Exception When SKU or ID is not valid. * @param array $posted Request data. * @param string $action 'create' to add line item or 'update' to update it. * @return int */ protected function get_product_id( $posted, $action = 'create' ) { if ( ! empty( $posted['sku'] ) ) { $product_id = (int) wc_get_product_id_by_sku( $posted['sku'] ); } elseif ( ! empty( $posted['product_id'] ) && empty( $posted['variation_id'] ) ) { $product_id = (int) $posted['product_id']; } elseif ( ! empty( $posted['variation_id'] ) ) { $product_id = (int) $posted['variation_id']; } elseif ( 'update' === $action ) { $product_id = 0; } else { throw new WC_REST_Exception( 'woocommerce_rest_required_product_reference', __( 'Product ID or SKU is required.', 'woocommerce' ), 400 ); } return $product_id; } /** * Maybe set an item prop if the value was posted. * * @param WC_Order_Item $item Order item. * @param string $prop Order property. * @param array $posted Request data. */ protected function maybe_set_item_prop( $item, $prop, $posted ) { if ( isset( $posted[ $prop ] ) ) { $item->{"set_$prop"}( $posted[ $prop ] ); } } /** * Maybe set item props if the values were posted. * * @param WC_Order_Item $item Order item data. * @param string[] $props Properties. * @param array $posted Request data. */ protected function maybe_set_item_props( $item, $props, $posted ) { foreach ( $props as $prop ) { $this->maybe_set_item_prop( $item, $prop, $posted ); } } /** * Maybe set item meta if posted. * * @param WC_Order_Item $item Order item data. * @param array $posted Request data. */ protected function maybe_set_item_meta_data( $item, $posted ) { if ( ! empty( $posted['meta_data'] ) && is_array( $posted['meta_data'] ) ) { foreach ( $posted['meta_data'] as $meta ) { if ( isset( $meta['key'] ) ) { $value = isset( $meta['value'] ) ? $meta['value'] : null; $item->update_meta_data( $meta['key'], $value, isset( $meta['id'] ) ? $meta['id'] : '' ); } } } } /** * Create or update a line item. * * @param array $posted Line item data. * @param string $action 'create' to add line item or 'update' to update it. * @param object $item Passed when updating an item. Null during creation. * @return WC_Order_Item_Product * @throws WC_REST_Exception Invalid data, server error. */ protected function prepare_line_items( $posted, $action = 'create', $item = null ) { $item = is_null( $item ) ? new WC_Order_Item_Product( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item; $product = wc_get_product( $this->get_product_id( $posted, $action ) ); if ( $product && $product !== $item->get_product() ) { $item->set_product( $product ); if ( 'create' === $action ) { $quantity = isset( $posted['quantity'] ) ? $posted['quantity'] : 1; $total = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) ); $item->set_total( $total ); $item->set_subtotal( $total ); } } $this->maybe_set_item_props( $item, array( 'name', 'quantity', 'total', 'subtotal', 'tax_class' ), $posted ); $this->maybe_set_item_meta_data( $item, $posted ); return $item; } /** * Create or update an order shipping method. * * @param array $posted $shipping Item data. * @param string $action 'create' to add shipping or 'update' to update it. * @param object $item Passed when updating an item. Null during creation. * @return WC_Order_Item_Shipping * @throws WC_REST_Exception Invalid data, server error. */ protected function prepare_shipping_lines( $posted, $action = 'create', $item = null ) { $item = is_null( $item ) ? new WC_Order_Item_Shipping( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item; if ( 'create' === $action ) { if ( empty( $posted['method_id'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_shipping_item', __( 'Shipping method ID is required.', 'woocommerce' ), 400 ); } } $this->maybe_set_item_props( $item, array( 'method_id', 'method_title', 'total', 'instance_id' ), $posted ); $this->maybe_set_item_meta_data( $item, $posted ); return $item; } /** * Create or update an order fee. * * @param array $posted Item data. * @param string $action 'create' to add fee or 'update' to update it. * @param object $item Passed when updating an item. Null during creation. * @return WC_Order_Item_Fee * @throws WC_REST_Exception Invalid data, server error. */ protected function prepare_fee_lines( $posted, $action = 'create', $item = null ) { $item = is_null( $item ) ? new WC_Order_Item_Fee( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item; if ( 'create' === $action ) { if ( empty( $posted['name'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_fee_item', __( 'Fee name is required.', 'woocommerce' ), 400 ); } } $this->maybe_set_item_props( $item, array( 'name', 'tax_class', 'tax_status', 'total' ), $posted ); $this->maybe_set_item_meta_data( $item, $posted ); return $item; } /** * Create or update an order coupon. * * @param array $posted Item data. * @param string $action 'create' to add coupon or 'update' to update it. * @param object $item Passed when updating an item. Null during creation. * @return WC_Order_Item_Coupon * @throws WC_REST_Exception Invalid data, server error. */ protected function prepare_coupon_lines( $posted, $action = 'create', $item = null ) { $item = is_null( $item ) ? new WC_Order_Item_Coupon( ! empty( $posted['id'] ) ? $posted['id'] : '' ) : $item; if ( 'create' === $action ) { if ( empty( $posted['code'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 ); } } $this->maybe_set_item_props( $item, array( 'code', 'discount' ), $posted ); $this->maybe_set_item_meta_data( $item, $posted ); return $item; } /** * Wrapper method to create/update order items. * When updating, the item ID provided is checked to ensure it is associated * with the order. * * @param WC_Order $order order object. * @param string $item_type The item type. * @param array $posted item provided in the request body. * @throws WC_REST_Exception If item ID is not associated with order. */ protected function set_item( $order, $item_type, $posted ) { global $wpdb; if ( ! empty( $posted['id'] ) ) { $action = 'update'; } else { $action = 'create'; } $method = 'prepare_' . $item_type; $item = null; // Verify provided line item ID is associated with order. if ( 'update' === $action ) { $item = $order->get_item( absint( $posted['id'] ), false ); if ( ! $item ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_item_id', __( 'Order item ID provided is not associated with order.', 'woocommerce' ), 400 ); } } // Prepare item data. $item = $this->$method( $posted, $action, $item ); do_action( 'woocommerce_rest_set_order_item', $item, $posted ); // If creating the order, add the item to it. if ( 'create' === $action ) { $order->add_item( $item ); } else { $item->save(); } } /** * Helper method to check if the resource ID associated with the provided item is null. * Items can be deleted by setting the resource ID to null. * * @param array $item Item provided in the request body. * @return bool True if the item resource ID is null, false otherwise. */ protected function item_is_null( $item ) { $keys = array( 'product_id', 'method_id', 'method_title', 'name', 'code' ); foreach ( $keys as $key ) { if ( array_key_exists( $key, $item ) && is_null( $item[ $key ] ) ) { return true; } } return false; } /** * Get order statuses without prefixes. * * @return array */ protected 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; } /** * 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, ), 'parent_id' => array( 'description' => __( 'Parent order ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'number' => array( 'description' => __( 'Order number.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'order_key' => array( 'description' => __( 'Order key.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'created_via' => array( 'description' => __( 'Shows where the order was created.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'version' => array( 'description' => __( 'Version of WooCommerce which last updated the order.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'status' => array( 'description' => __( 'Order status.', 'woocommerce' ), 'type' => 'string', 'default' => 'pending', 'enum' => $this->get_order_statuses(), 'context' => array( 'view', 'edit' ), ), 'currency' => array( 'description' => __( 'Currency the order was created with, in ISO format.', 'woocommerce' ), 'type' => 'string', 'default' => get_woocommerce_currency(), 'enum' => array_keys( get_woocommerce_currencies() ), 'context' => array( 'view', 'edit' ), ), 'date_created' => array( 'description' => __( "The date the order 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 order was created, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified' => array( 'description' => __( "The date the order 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 order was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'discount_total' => array( 'description' => __( 'Total discount amount for the order.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'discount_tax' => array( 'description' => __( 'Total discount tax amount for the order.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_total' => array( 'description' => __( 'Total shipping amount for the order.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_tax' => array( 'description' => __( 'Total shipping tax amount for the order.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'cart_tax' => array( 'description' => __( 'Sum of line item taxes only.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Grand total.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total_tax' => array( 'description' => __( 'Sum of all taxes.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'prices_include_tax' => array( 'description' => __( 'True the prices included tax during checkout.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'customer_id' => array( 'description' => __( 'User ID who owns the order. 0 for guests.', 'woocommerce' ), 'type' => 'integer', 'default' => 0, 'context' => array( 'view', 'edit' ), ), 'customer_ip_address' => array( 'description' => __( "Customer's IP address.", 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'customer_user_agent' => array( 'description' => __( 'User agent of the customer.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'customer_note' => array( 'description' => __( 'Note left by customer during checkout.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'billing' => array( 'description' => __( 'Billing address.', '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' => __( 'Country code in ISO 3166-1 alpha-2 format.', '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' => __( 'Shipping address.', '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' => __( 'Country code in ISO 3166-1 alpha-2 format.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'payment_method' => array( 'description' => __( 'Payment method ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'payment_method_title' => array( 'description' => __( 'Payment method title.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'transaction_id' => array( 'description' => __( 'Unique transaction ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_paid' => array( 'description' => __( "The date the order was paid, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_paid_gmt' => array( 'description' => __( 'The date the order was paid, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_completed' => array( 'description' => __( "The date the order was completed, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_completed_gmt' => array( 'description' => __( 'The date the order was completed, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'cart_hash' => array( 'description' => __( 'MD5 hash of cart items to ensure orders are not modified.', 'woocommerce' ), 'type' => 'string', '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' ), ), ), ), ), 'line_items' => array( 'description' => __( 'Line items data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), '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' ), ), 'product_id' => array( 'description' => __( 'Product ID.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'variation_id' => array( 'description' => __( 'Variation ID, if applicable.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'quantity' => array( 'description' => __( 'Quantity ordered.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class of product.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'subtotal' => array( 'description' => __( 'Line subtotal (before discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'subtotal_tax' => array( 'description' => __( 'Line subtotal tax (before discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Line total (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'total_tax' => array( 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), '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' ), ), 'total' => array( 'description' => __( 'Tax total.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'subtotal' => array( 'description' => __( 'Tax subtotal.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), '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' ), ), ), ), ), 'sku' => array( 'description' => __( 'Product SKU.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'price' => array( 'description' => __( 'Product price.', 'woocommerce' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'tax_lines' => array( 'description' => __( 'Tax lines 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, ), 'rate_code' => array( 'description' => __( 'Tax rate code.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'rate_id' => array( 'description' => __( 'Tax rate ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'label' => array( 'description' => __( 'Tax rate label.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'compound' => array( 'description' => __( 'Show if is a compound tax rate.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'tax_total' => array( 'description' => __( 'Tax total (not including shipping taxes).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_tax_total' => array( 'description' => __( 'Shipping tax total.', 'woocommerce' ), 'type' => 'string', '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' ), ), ), ), ), ), ), ), 'shipping_lines' => array( 'description' => __( 'Shipping lines data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Item ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'method_title' => array( 'description' => __( 'Shipping method name.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'method_id' => array( 'description' => __( 'Shipping method ID.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'instance_id' => array( 'description' => __( 'Shipping instance ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'total' => array( 'description' => __( 'Line total (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'total_tax' => array( 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), '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, ), ), ), ), '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' ), ), ), ), ), ), ), ), 'fee_lines' => array( 'description' => __( 'Fee lines data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Item ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'Fee name.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class of fee.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'tax_status' => array( 'description' => __( 'Tax status of fee.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'enum' => array( 'taxable', 'none' ), ), 'total' => array( 'description' => __( 'Line total (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'total_tax' => array( 'description' => __( 'Line total tax (after discounts).', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), '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_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' ), ), ), ), ), ), ), ), 'coupon_lines' => array( 'description' => __( 'Coupons line data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Item ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'code' => array( 'description' => __( 'Coupon code.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'discount' => array( 'description' => __( 'Discount total.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'discount_tax' => array( 'description' => __( 'Discount total tax.', 'woocommerce' ), 'type' => 'string', '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' ), ), ), ), ), ), ), ), 'refunds' => array( 'description' => __( 'List of refunds.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'readonly' => true, 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Refund ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'reason' => array( 'description' => __( 'Refund reason.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Refund total.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'set_paid' => array( 'description' => __( 'Define if the order is paid. It will set the status to processing and reduce stock items.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'edit' ), ), ), ); return $this->add_additional_fields_schema( $schema ); } /** * Get the query params for collections. * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); $params['status'] = array( 'default' => 'any', 'description' => __( 'Limit result set to orders assigned a specific status.', 'woocommerce' ), 'type' => 'string', 'enum' => array_merge( array( 'any', 'trash' ), $this->get_order_statuses() ), 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); $params['customer'] = array( 'description' => __( 'Limit result set to orders assigned a specific customer.', 'woocommerce' ), 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ); $params['product'] = array( 'description' => __( 'Limit result set to orders assigned a specific product.', 'woocommerce' ), 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ); $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; } } PKo5\ !c`woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-product-categories-v2-controller.phpnu[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 ), 'date_created_gmt' => wc_rest_prepare_date_response( $attachment->post_date_gmt ), 'date_modified' => wc_rest_prepare_date_response( $attachment->post_modified ), 'date_modified_gmt' => 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 ); } /** * 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_created_gmt' => array( 'description' => __( 'The date the image was created, as GMT.', '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, ), 'date_modified_gmt' => array( 'description' => __( 'The date the image was last modified, as GMT.', '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 ); } } PKo5\9a~~~`woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-product-variations-v2-controller.phpnu[/variations endpoints. * * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ defined( 'ABSPATH' ) || exit; /** * REST API variations controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Products_V2_Controller */ class WC_REST_Product_Variations_V2_Controller extends WC_REST_Products_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v2'; /** * Route base. * * @var string */ protected $rest_base = 'products/(?P[\d]+)/variations'; /** * Post type. * * @var string */ protected $post_type = 'product_variation'; /** * Register the routes for products. */ 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', ), ), 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[\d]+)', 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_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( 'args' => array( 'product_id' => array( 'description' => __( 'Unique identifier for the variable product.', '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' ), ) ); } /** * Get object. * * @since 3.0.0 * @param int $id Object ID. * @return WC_Data */ protected function get_object( $id ) { return wc_get_product( $id ); } /** * Check if a given request has access to update an item. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function update_item_permissions_check( $request ) { $object = $this->get_object( (int) $request['id'] ); if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'edit', $object->get_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() ) ); } // Check if variation belongs to the correct parent product. if ( $object && 0 !== $object->get_parent_id() && absint( $request['product_id'] ) !== $object->get_parent_id() ) { return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Parent product does not match current variation.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Prepare a single variation 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 = array( 'id' => $object->get_id(), 'date_created' => wc_rest_prepare_date_response( $object->get_date_created(), false ), 'date_created_gmt' => wc_rest_prepare_date_response( $object->get_date_created() ), 'date_modified' => wc_rest_prepare_date_response( $object->get_date_modified(), false ), 'date_modified_gmt' => wc_rest_prepare_date_response( $object->get_date_modified() ), 'description' => wc_format_content( $object->get_description() ), 'permalink' => $object->get_permalink(), 'sku' => $object->get_sku(), 'price' => $object->get_price(), 'regular_price' => $object->get_regular_price(), 'sale_price' => $object->get_sale_price(), 'date_on_sale_from' => wc_rest_prepare_date_response( $object->get_date_on_sale_from(), false ), 'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_from() ), 'date_on_sale_to' => wc_rest_prepare_date_response( $object->get_date_on_sale_to(), false ), 'date_on_sale_to_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_to() ), 'on_sale' => $object->is_on_sale(), 'visible' => $object->is_visible(), 'purchasable' => $object->is_purchasable(), 'virtual' => $object->is_virtual(), 'downloadable' => $object->is_downloadable(), 'downloads' => $this->get_downloads( $object ), 'download_limit' => '' !== $object->get_download_limit() ? (int) $object->get_download_limit() : -1, 'download_expiry' => '' !== $object->get_download_expiry() ? (int) $object->get_download_expiry() : -1, 'tax_status' => $object->get_tax_status(), 'tax_class' => $object->get_tax_class(), 'manage_stock' => $object->managing_stock(), 'stock_quantity' => $object->get_stock_quantity(), 'in_stock' => $object->is_in_stock(), 'backorders' => $object->get_backorders(), 'backorders_allowed' => $object->backorders_allowed(), 'backordered' => $object->is_on_backorder(), 'weight' => $object->get_weight(), 'dimensions' => array( 'length' => $object->get_length(), 'width' => $object->get_width(), 'height' => $object->get_height(), ), 'shipping_class' => $object->get_shipping_class(), 'shipping_class_id' => $object->get_shipping_class_id(), 'image' => current( $this->get_images( $object ) ), 'attributes' => $this->get_attributes( $object ), 'menu_order' => $object->get_menu_order(), 'meta_data' => $object->get_meta_data(), ); $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 ); $args['post_parent'] = $request['product_id']; return $args; } /** * Prepare a single variation 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 ) { if ( isset( $request['id'] ) ) { $variation = wc_get_product( absint( $request['id'] ) ); } else { $variation = new WC_Product_Variation(); } // Update parent ID just once. if ( 0 === $variation->get_parent_id() ) { $variation->set_parent_id( absint( $request['product_id'] ) ); } // Status. if ( isset( $request['visible'] ) ) { $variation->set_status( false === $request['visible'] ? 'private' : 'publish' ); } // SKU. if ( isset( $request['sku'] ) ) { $variation->set_sku( wc_clean( $request['sku'] ) ); } // Thumbnail. if ( isset( $request['image'] ) ) { if ( is_array( $request['image'] ) && ! empty( $request['image'] ) ) { $image = $request['image']; if ( is_array( $image ) ) { $image['position'] = 0; } $variation = $this->set_product_images( $variation, array( $image ) ); } else { $variation->set_image_id( '' ); } } // Virtual variation. if ( isset( $request['virtual'] ) ) { $variation->set_virtual( $request['virtual'] ); } // Downloadable variation. if ( isset( $request['downloadable'] ) ) { $variation->set_downloadable( $request['downloadable'] ); } // Downloads. if ( $variation->get_downloadable() ) { // Downloadable files. if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { $variation = $this->save_downloadable_files( $variation, $request['downloads'] ); } // Download limit. if ( isset( $request['download_limit'] ) ) { $variation->set_download_limit( $request['download_limit'] ); } // Download expiry. if ( isset( $request['download_expiry'] ) ) { $variation->set_download_expiry( $request['download_expiry'] ); } } // Shipping data. $variation = $this->save_product_shipping_data( $variation, $request ); // Stock handling. if ( isset( $request['manage_stock'] ) ) { if ( 'parent' === $request['manage_stock'] ) { $variation->set_manage_stock( false ); // This just indicates the variation does not manage stock, but the parent does. } else { $variation->set_manage_stock( wc_string_to_bool( $request['manage_stock'] ) ); } } if ( isset( $request['in_stock'] ) ) { $variation->set_stock_status( true === $request['in_stock'] ? 'instock' : 'outofstock' ); } if ( isset( $request['backorders'] ) ) { $variation->set_backorders( $request['backorders'] ); } if ( $variation->get_manage_stock() ) { if ( isset( $request['stock_quantity'] ) ) { $variation->set_stock_quantity( $request['stock_quantity'] ); } elseif ( isset( $request['inventory_delta'] ) ) { $stock_quantity = wc_stock_amount( $variation->get_stock_quantity() ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); $variation->set_stock_quantity( $stock_quantity ); } } else { $variation->set_backorders( 'no' ); $variation->set_stock_quantity( '' ); } // Regular Price. if ( isset( $request['regular_price'] ) ) { $variation->set_regular_price( $request['regular_price'] ); } // Sale Price. if ( isset( $request['sale_price'] ) ) { $variation->set_sale_price( $request['sale_price'] ); } if ( isset( $request['date_on_sale_from'] ) ) { $variation->set_date_on_sale_from( $request['date_on_sale_from'] ); } if ( isset( $request['date_on_sale_from_gmt'] ) ) { $variation->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null ); } if ( isset( $request['date_on_sale_to'] ) ) { $variation->set_date_on_sale_to( $request['date_on_sale_to'] ); } if ( isset( $request['date_on_sale_to_gmt'] ) ) { $variation->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null ); } // Tax class. if ( isset( $request['tax_class'] ) ) { $variation->set_tax_class( $request['tax_class'] ); } // Description. if ( isset( $request['description'] ) ) { $variation->set_description( wp_kses_post( $request['description'] ) ); } // Update taxonomies. if ( isset( $request['attributes'] ) ) { $attributes = array(); $parent = wc_get_product( $variation->get_parent_id() ); $parent_attributes = $parent->get_attributes(); foreach ( $request['attributes'] as $attribute ) { $attribute_id = 0; $attribute_name = ''; // Check ID for global attributes or name for product attributes. if ( ! empty( $attribute['id'] ) ) { $attribute_id = absint( $attribute['id'] ); $raw_attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); } elseif ( ! empty( $attribute['name'] ) ) { $raw_attribute_name = sanitize_title( $attribute['name'] ); } if ( ! $attribute_id && ! $raw_attribute_name ) { continue; } $attribute_name = sanitize_title( $raw_attribute_name ); if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) { continue; } $attribute_key = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() ); $attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) { // If dealing with a taxonomy, we need to get the slug from the name posted to the API. $term = get_term_by( 'name', $attribute_value, $raw_attribute_name ); // @codingStandardsIgnoreLine if ( $term && ! is_wp_error( $term ) ) { $attribute_value = $term->slug; } else { $attribute_value = sanitize_title( $attribute_value ); } } $attributes[ $attribute_key ] = $attribute_value; } $variation->set_attributes( $attributes ); } // Menu order. if ( $request['menu_order'] ) { $variation->set_menu_order( $request['menu_order'] ); } // Meta data. if ( is_array( $request['meta_data'] ) ) { foreach ( $request['meta_data'] as $meta ) { $variation->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } } /** * 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 $variation 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", $variation, $request, $creating ); } /** * Clear caches here so in sync with any new variations. * * @param WC_Data $object Object data. */ public function clear_transients( $object ) { wc_delete_product_transients( $object->get_parent_id() ); wp_cache_delete( 'product-' . $object->get_parent_id(), 'products' ); } /** * Delete a variation. * * @param WP_REST_Request $request Full details about the request. * * @return bool|WP_Error|WP_REST_Response */ public function delete_item( $request ) { $force = (bool) $request['force']; $object = $this->get_object( (int) $request['id'] ); $result = false; if ( ! $object || 0 === $object->get_id() ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404, ) ); } $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); /** * Filter whether an object is trashable. * * Return false to disable trash support for the object. * * @param boolean $supports_trash Whether the object type support trashing. * @param WC_Data $object The object being considered for trashing support. */ $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { return new WP_Error( /* translators: %s: post type */ "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code(), ) ); } $request->set_param( 'context', 'edit' ); $response = $this->prepare_object_for_response( $object, $request ); // If we're forcing, then delete permanently. if ( $force ) { $object->delete( true ); $result = 0 === $object->get_id(); } else { // If we don't support trashing for this type, error out. if ( ! $supports_trash ) { return new WP_Error( /* translators: %s: post type */ 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501, ) ); } // Otherwise, only trash if we haven't already. if ( is_callable( array( $object, 'get_status' ) ) ) { if ( 'trash' === $object->get_status() ) { return new WP_Error( /* translators: %s: post type */ 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410, ) ); } $object->delete(); $result = 'trash' === $object->get_status(); } } if ( ! $result ) { return new WP_Error( /* translators: %s: post type */ 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 500, ) ); } // Delete parent product transients. if ( 0 !== $object->get_parent_id() ) { wc_delete_product_transients( $object->get_parent_id() ); } /** * Fires after a single object is deleted or trashed via the REST API. * * @param WC_Data $object The deleted or trashed object. * @param WP_REST_Response $response The response data. * @param WP_REST_Request $request The request sent to the API. */ do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request ); return $response; } /** * Bulk create, update and delete items. * * @since 3.0.0 * @param WP_REST_Request $request Full details about the request. * @return array Of WP_Error or WP_REST_Response. */ public function batch_items( $request ) { $items = array_filter( $request->get_params() ); $params = $request->get_url_params(); $product_id = $params['product_id']; $body_params = array(); foreach ( array( 'update', 'create', 'delete' ) as $batch_type ) { if ( ! empty( $items[ $batch_type ] ) ) { $injected_items = array(); foreach ( $items[ $batch_type ] as $item ) { $injected_items[] = is_array( $item ) ? array_merge( array( 'product_id' => $product_id, ), $item ) : $item; } $body_params[ $batch_type ] = $injected_items; } } $request = new WP_REST_Request( $request->get_method() ); $request->set_body_params( $body_params ); return parent::batch_items( $request ); } /** * Prepare links for the request. * * @param WC_Data $object Object data. * @param WP_REST_Request $request Request object. * @return array Links for the given post. */ protected function prepare_links( $object, $request ) { $product_id = (int) $request['product_id']; $base = str_replace( '(?P[\d]+)', $product_id, $this->rest_base ); $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $object->get_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 Variation's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $weight_unit = get_option( 'woocommerce_weight_unit' ); $dimension_unit = get_option( 'woocommerce_dimension_unit' ); $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 variation was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified' => array( 'description' => __( "The date the variation was last modified, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'Variation description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'permalink' => array( 'description' => __( 'Variation URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'sku' => array( 'description' => __( 'Unique identifier.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'price' => array( 'description' => __( 'Current variation price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'regular_price' => array( 'description' => __( 'Variation regular price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sale_price' => array( 'description' => __( 'Variation sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from' => array( 'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from_gmt' => array( 'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to' => array( 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to_gmt' => array( 'description' => __( 'End date of sale price, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'on_sale' => array( 'description' => __( 'Shows if the variation is on sale.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'visible' => array( 'description' => __( "Define if the variation is visible on the product's page.", 'woocommerce' ), 'type' => 'boolean', 'default' => true, 'context' => array( 'view', 'edit' ), ), 'purchasable' => array( 'description' => __( 'Shows if the variation can be bought.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'virtual' => array( 'description' => __( 'If the variation is virtual.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloadable' => array( 'description' => __( 'If the variation is downloadable.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloads' => array( 'description' => __( 'List of downloadable files.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'File ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'File name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'file' => array( 'description' => __( 'File URL.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'download_limit' => array( 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'download_expiry' => array( 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'tax_status' => array( 'description' => __( 'Tax status.', 'woocommerce' ), 'type' => 'string', 'default' => 'taxable', 'enum' => array( 'taxable', 'shipping', 'none' ), 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'manage_stock' => array( 'description' => __( 'Stock management at variation level.', 'woocommerce' ), 'type' => 'mixed', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'stock_quantity' => array( 'description' => __( 'Stock quantity.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'in_stock' => array( 'description' => __( 'Controls whether or not the variation is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ), 'type' => 'boolean', 'default' => true, 'context' => array( 'view', 'edit' ), ), 'backorders' => array( 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ), 'type' => 'string', 'default' => 'no', 'enum' => array( 'no', 'notify', 'yes' ), 'context' => array( 'view', 'edit' ), ), 'backorders_allowed' => array( 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'backordered' => array( 'description' => __( 'Shows if the variation is on backordered.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'weight' => array( /* translators: %s: weight unit */ 'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), $weight_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'dimensions' => array( 'description' => __( 'Variation dimensions.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'length' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation length (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'width' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation width (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'height' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation height (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'shipping_class' => array( 'description' => __( 'Shipping class slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'shipping_class_id' => array( 'description' => __( 'Shipping class ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'image' => array( 'description' => __( 'Variation 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_created_gmt' => array( 'description' => __( 'The date the image was created, as GMT.', '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, ), 'date_modified_gmt' => array( 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'src' => array( 'description' => __( 'Image URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Image name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'alt' => array( 'description' => __( 'Image alternative text.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'position' => array( 'description' => __( 'Image position. 0 means that the image is featured.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), ), ), 'attributes' => array( 'description' => __( 'List of attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'option' => array( 'description' => __( 'Selected attribute term name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'menu_order' => array( 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), '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 ); } } PKo5\>Iewoocommerce-rest-api/src/Controllers/Version2/class-wc-rest-shipping-zone-locations-v2-controller.phpnu[/locations endpoint. * * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Shipping Zone Locations class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Shipping_Zones_Controller_Base */ class WC_REST_Shipping_Zone_Locations_V2_Controller extends WC_REST_Shipping_Zones_Controller_Base { /** * Register the routes for Shipping Zone Locations. */ public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/locations', array( 'args' => array( 'id' => array( 'description' => __( 'Unique ID 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' ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_items' ), 'permission_callback' => array( $this, 'update_items_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Get all Shipping Zone Locations. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function get_items( $request ) { $zone = $this->get_zone( (int) $request['id'] ); if ( is_wp_error( $zone ) ) { return $zone; } $locations = $zone->get_zone_locations(); $data = array(); foreach ( $locations as $location_obj ) { $location = $this->prepare_item_for_response( $location_obj, $request ); $location = $this->prepare_response_for_collection( $location ); $data[] = $location; } return rest_ensure_response( $data ); } /** * Update all Shipping Zone Locations. * * @param WP_REST_Request $request Request data. * @return WP_REST_Response|WP_Error */ public function update_items( $request ) { $zone = $this->get_zone( (int) $request['id'] ); if ( is_wp_error( $zone ) ) { return $zone; } if ( 0 === $zone->get_id() ) { return new WP_Error( 'woocommerce_rest_shipping_zone_locations_invalid_zone', __( 'The "locations not covered by your other zones" zone cannot be updated.', 'woocommerce' ), array( 'status' => 403 ) ); } $raw_locations = $request->get_json_params(); $locations = array(); foreach ( (array) $raw_locations as $raw_location ) { if ( empty( $raw_location['code'] ) ) { continue; } $type = ! empty( $raw_location['type'] ) ? sanitize_text_field( $raw_location['type'] ) : 'country'; if ( ! in_array( $type, array( 'postcode', 'state', 'country', 'continent' ), true ) ) { continue; } $locations[] = array( 'code' => sanitize_text_field( $raw_location['code'] ), 'type' => sanitize_text_field( $type ), ); } $zone->set_locations( $locations ); $zone->save(); return $this->get_items( $request ); } /** * Prepare the Shipping Zone Location for the REST response. * * @param array $item Shipping Zone Location. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response */ public function prepare_item_for_response( $item, $request ) { $context = empty( $request['context'] ) ? 'view' : $request['context']; $data = $this->add_additional_fields_to_object( $item, $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( (int) $request['id'] ) ); return $response; } /** * Prepare links for the request. * * @param int $zone_id Given Shipping Zone ID. * @return array Links for the given Shipping Zone Location. */ protected function prepare_links( $zone_id ) { $base = '/' . $this->namespace . '/' . $this->rest_base . '/' . $zone_id; $links = array( 'collection' => array( 'href' => rest_url( $base . '/locations' ), ), 'describes' => array( 'href' => rest_url( $base ), ), ); return $links; } /** * Get the Shipping Zone Locations schema, conforming to JSON Schema * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'shipping_zone_location', 'type' => 'object', 'properties' => array( 'code' => array( 'description' => __( 'Shipping zone location code.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'type' => array( 'description' => __( 'Shipping zone location type.', 'woocommerce' ), 'type' => 'string', 'default' => 'country', 'enum' => array( 'postcode', 'state', 'country', 'continent', ), 'context' => array( 'view', 'edit' ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\f/PJPJ[woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-order-refunds-v2-controller.phpnu[/refunds endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Order Refunds controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Orders_V2_Controller */ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v2'; /** * Route base. * * @var string */ protected $rest_base = 'orders/(?P[\d]+)/refunds'; /** * Post type. * * @var string */ protected $post_type = 'shop_order_refund'; /** * Stores the request. * * @var array */ protected $request = array(); /** * Order refunds actions. */ public function __construct() { add_filter( "woocommerce_rest_{$this->post_type}_object_trashable", '__return_false' ); } /** * 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[\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' ), ) ); } /** * Get object. * * @since 3.0.0 * @param int $id Object ID. * @return WC_Data */ protected function get_object( $id ) { return wc_get_order( $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' ); $format_date = array( 'date_created' ); $format_line_items = array( 'line_items' ); // Format decimal values. foreach ( $format_decimal as $key ) { $data[ $key ] = wc_format_decimal( $data[ $key ], $this->request['dp'] ); } // 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 line items. foreach ( $format_line_items as $key ) { $data[ $key ] = array_values( array_map( array( $this, 'get_order_item_data' ), $data[ $key ] ) ); } return array( 'id' => $object->get_id(), 'date_created' => $data['date_created'], 'date_created_gmt' => $data['date_created_gmt'], 'amount' => $data['amount'], 'reason' => $data['reason'], 'refunded_by' => $data['refunded_by'], 'refunded_payment' => $data['refunded_payment'], 'meta_data' => $data['meta_data'], 'line_items' => $data['line_items'], ); } /** * Prepare a single order output for response. * * @since 3.0.0 * * @param WC_Data $object Object data. * @param WP_REST_Request $request Request object. * * @return WP_Error|WP_REST_Response */ public function prepare_object_for_response( $object, $request ) { $this->request = $request; $this->request['dp'] = is_null( $this->request['dp'] ) ? wc_get_price_decimals() : absint( $this->request['dp'] ); $order = wc_get_order( (int) $request['order_id'] ); if ( ! $order ) { return new WP_Error( 'woocommerce_rest_invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), 404 ); } if ( ! $object || $object->get_parent_id() !== $order->get_id() ) { return new WP_Error( 'woocommerce_rest_invalid_order_refund_id', __( 'Invalid order refund ID.', 'woocommerce' ), 404 ); } $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 ); // Wrap the data in a response object. $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 links for the request. * * @param WC_Data $object Object data. * @param WP_REST_Request $request Request object. * @return array Links for the given post. */ protected function prepare_links( $object, $request ) { $base = str_replace( '(?P[\d]+)', $object->get_parent_id(), $this->rest_base ); $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $object->get_id() ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ), ), 'up' => array( 'href' => rest_url( sprintf( '/%s/orders/%d', $this->namespace, $object->get_parent_id() ) ), ), ); return $links; } /** * 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 ); $args['post_status'] = array_keys( wc_get_order_statuses() ); $args['post_parent__in'] = array( absint( $request['order_id'] ) ); return $args; } /** * Prepares one object for create or update operation. * * @since 3.0.0 * @param WP_REST_Request $request Request object. * @param bool $creating If is creating a new object. * @return WP_Error|WC_Data The prepared item, or WP_Error object on failure. */ protected function prepare_object_for_database( $request, $creating = false ) { $order = wc_get_order( (int) $request['order_id'] ); if ( ! $order ) { return new WP_Error( 'woocommerce_rest_invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), 404 ); } 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->get_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 ); } if ( ! empty( $request['meta_data'] ) && is_array( $request['meta_data'] ) ) { foreach ( $request['meta_data'] as $meta ) { $refund->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } $refund->save_meta_data(); } /** * 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", $refund, $request, $creating ); } /** * Save an object data. * * @since 3.0.0 * @param WP_REST_Request $request Full details about the request. * @param bool $creating If is creating a new object. * @return WC_Data|WP_Error */ protected function save_object( $request, $creating = false ) { try { $object = $this->prepare_object_for_database( $request, $creating ); if ( is_wp_error( $object ) ) { return $object; } return $this->get_object( $object->get_id() ); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * 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, ), 'date_created_gmt' => array( 'description' => __( 'The date the order refund was created, as GMT.', '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' ), ), 'refunded_by' => array( 'description' => __( 'User ID of user who created the refund.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'refunded_payment' => array( 'description' => __( 'If the payment was refunded via the API.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), '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' ), ), ), ), ), '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, ), '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, ), '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_data' => array( 'description' => __( 'Meta data.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'readonly' => true, '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' ), 'readonly' => true, ), 'value' => array( 'description' => __( 'Meta value.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'sku' => array( 'description' => __( 'Product SKU.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'price' => array( 'description' => __( 'Product price.', 'woocommerce' ), 'type' => 'number', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'api_refund' => array( 'description' => __( 'When true, the payment gateway API is used to generate the refund.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'edit' ), 'default' => 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(); unset( $params['status'], $params['customer'], $params['product'] ); return $params; } } PKo5\_`woocommerce-rest-api/src/Controllers/Version2/class-wc-rest-customer-downloads-v2-controller.phpnu[/downloads endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Customers controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Customer_Downloads_V1_Controller */ class WC_REST_Customer_Downloads_V2_Controller extends WC_REST_Customer_Downloads_V1_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v2'; /** * Prepare a single download output for response. * * @param stdClass $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_id' => $download->download_id, 'download_url' => $download->download_url, 'product_id' => $download->product_id, 'product_name' => $download->product_name, 'download_name' => $download->download_name, 'order_id' => $download->order_id, 'order_key' => $download->order_key, 'downloads_remaining' => '' === $download->downloads_remaining ? 'unlimited' : $download->downloads_remaining, 'access_expires' => $download->access_expires ? wc_rest_prepare_date_response( $download->access_expires ) : 'never', 'access_expires_gmt' => $download->access_expires ? wc_rest_prepare_date_response( get_gmt_from_date( $download->access_expires ) ) : 'never', 'file' => $download->file, ); $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 stdClass $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 ); } /** * 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_id' => array( 'description' => __( 'Download ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'download_url' => array( 'description' => __( 'Download file URL.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'product_id' => array( 'description' => __( 'Downloadable product ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), 'product_name' => array( 'description' => __( 'Product name.', 'woocommerce' ), 'type' => 'string', '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, ), 'access_expires_gmt' => array( 'description' => __( 'The date when download access expires, as GMT.', '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 ); } } PKo5\W$Xc+c+Zwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-data-continents-controller.phpnu[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 . '/(?P[\w-]+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), 'args' => array( 'continent' => array( 'description' => __( '2 character continent code.', 'woocommerce' ), 'type' => 'string', ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Return the list of countries and states for a given continent. * * @since 3.5.0 * @param string $continent_code Continent code. * @param WP_REST_Request $request Request data. * @return array|mixed Response data, ready for insertion into collection data. */ public function get_continent( $continent_code = false, $request ) { $continents = WC()->countries->get_continents(); $countries = WC()->countries->get_countries(); $states = WC()->countries->get_states(); $locale_info = include WC()->plugin_path() . '/i18n/locale-info.php'; $data = array(); if ( ! array_key_exists( $continent_code, $continents ) ) { return false; } $continent_list = $continents[ $continent_code ]; $continent = array( 'code' => $continent_code, 'name' => $continent_list['name'], ); $local_countries = array(); foreach ( $continent_list['countries'] as $country_code ) { if ( isset( $countries[ $country_code ] ) ) { $country = array( 'code' => $country_code, 'name' => $countries[ $country_code ], ); // If we have detailed locale information include that in the response. if ( array_key_exists( $country_code, $locale_info ) ) { // Defensive programming against unexpected changes in locale-info.php. $country_data = wp_parse_args( $locale_info[ $country_code ], array( 'currency_code' => 'USD', 'currency_pos' => 'left', 'decimal_sep' => '.', 'dimension_unit' => 'in', 'num_decimals' => 2, 'thousand_sep' => ',', 'weight_unit' => 'lbs', ) ); $country = array_merge( $country, $country_data ); } $local_states = array(); if ( isset( $states[ $country_code ] ) ) { foreach ( $states[ $country_code ] as $state_code => $state_name ) { $local_states[] = array( 'code' => $state_code, 'name' => $state_name, ); } } $country['states'] = $local_states; // Allow only desired keys (e.g. filter out tax rates). $allowed = array( 'code', 'currency_code', 'currency_pos', 'decimal_sep', 'dimension_unit', 'name', 'num_decimals', 'states', 'thousand_sep', 'weight_unit', ); $country = array_intersect_key( $country, array_flip( $allowed ) ); $local_countries[] = $country; } } $continent['countries'] = $local_countries; return $continent; } /** * Return the list of states for all continents. * * @since 3.5.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $continents = WC()->countries->get_continents(); $data = array(); foreach ( array_keys( $continents ) as $continent_code ) { $continent = $this->get_continent( $continent_code, $request ); $response = $this->prepare_item_for_response( $continent, $request ); $data[] = $this->prepare_response_for_collection( $response ); } return rest_ensure_response( $data ); } /** * Return the list of locations for a given continent. * * @since 3.5.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_item( $request ) { $data = $this->get_continent( strtoupper( $request['location'] ), $request ); if ( empty( $data ) ) { return new WP_Error( 'woocommerce_rest_data_invalid_location', __( 'There are no locations matching these parameters.', 'woocommerce' ), array( 'status' => 404 ) ); } return $this->prepare_item_for_response( $data, $request ); } /** * 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 location list returned from the API. * * Allows modification of the loction data right before it is returned. * * @param WP_REST_Response $response The response object. * @param array $item The original list of continent(s), countries, and states. * @param WP_REST_Request $request Request used to generate the response. */ return apply_filters( 'woocommerce_rest_prepare_data_continent', $response, $item, $request ); } /** * Prepare links for the request. * * @param object $item Data object. * @return array Links for the given continent. */ protected function prepare_links( $item ) { $continent_code = strtolower( $item['code'] ); $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $continent_code ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Get the location schema, conforming to JSON Schema. * * @since 3.5.0 * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'data_continents', 'type' => 'object', 'properties' => array( 'code' => array( 'type' => 'string', 'description' => __( '2 character continent code.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'type' => 'string', 'description' => __( 'Full name of continent.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'countries' => array( 'type' => 'array', 'description' => __( 'List of countries on this continent.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'object', 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( 'code' => array( 'type' => 'string', 'description' => __( 'ISO3166 alpha-2 country code.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'currency_code' => array( 'type' => 'string', 'description' => __( 'Default ISO4127 alpha-3 currency code for the country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'currency_pos' => array( 'type' => 'string', 'description' => __( 'Currency symbol position for this country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'decimal_sep' => array( 'type' => 'string', 'description' => __( 'Decimal separator for displayed prices for this country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'dimension_unit' => array( 'type' => 'string', 'description' => __( 'The unit lengths are defined in for this country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'type' => 'string', 'description' => __( 'Full name of country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'num_decimals' => array( 'type' => 'integer', 'description' => __( 'Number of decimal points shown in displayed prices for this country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'states' => array( 'type' => 'array', 'description' => __( 'List of states in this country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'object', 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( 'code' => array( 'type' => 'string', 'description' => __( 'State code.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'type' => 'string', 'description' => __( 'Full name of state.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), ), ), ), 'thousand_sep' => array( 'type' => 'string', 'description' => __( 'Thousands separator for displayed prices in this country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'weight_unit' => array( 'type' => 'string', 'description' => __( 'The unit weights are defined in for this country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), ), ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\ͰqVwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-order-notes-controller.phpnu[/notes endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Order Notes controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Order_Notes_V2_Controller */ class WC_REST_Order_Notes_Controller extends WC_REST_Order_Notes_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v3'; /** * 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, 'author' => __( 'woocommerce', 'woocommerce' ) === $note->comment_author ? 'system' : $note->comment_author, 'date_created' => wc_rest_prepare_date_response( $note->comment_date ), 'date_created_gmt' => 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 ); } /** * 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'], $request['added_by_user'] ); 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[\d]+)', $order->get_id(), $this->rest_base ), $note_id ) ) ); return $response; } /** * 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, ), 'author' => array( 'description' => __( 'Order note author.', 'woocommerce' ), 'type' => 'string', '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, ), 'date_created_gmt' => array( 'description' => __( 'The date the order note was created, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'note' => array( 'description' => __( 'Order note content.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'customer_note' => array( 'description' => __( 'If true, the note will be shown to customers and they will be notified. If false, the note will be for admin reference only.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'added_by_user' => array( 'description' => __( 'If true, this note will be attributed to the current user. If false, the note will be attributed to the system.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'edit' ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\)~hhZwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-setting-options-controller.phpnu[ 404 ) ); } $settings = apply_filters( 'woocommerce_settings-' . $group_id, array() ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores if ( empty( $settings ) ) { return new WP_Error( 'rest_setting_setting_group_invalid', __( 'Invalid setting group.', 'woocommerce' ), array( 'status' => 404 ) ); } $filtered_settings = array(); foreach ( $settings as $setting ) { $option_key = $setting['option_key']; $setting = $this->filter_setting( $setting ); $default = isset( $setting['default'] ) ? $setting['default'] : ''; // Get the option value. if ( is_array( $option_key ) ) { $option = get_option( $option_key[0] ); $setting['value'] = isset( $option[ $option_key[1] ] ) ? $option[ $option_key[1] ] : $default; } else { $admin_setting_value = WC_Admin_Settings::get_option( $option_key, $default ); $setting['value'] = $admin_setting_value; } if ( 'multi_select_countries' === $setting['type'] ) { $setting['options'] = WC()->countries->get_countries(); $setting['type'] = 'multiselect'; } elseif ( 'single_select_country' === $setting['type'] ) { $setting['type'] = 'select'; $setting['options'] = $this->get_countries_and_states(); } elseif ( 'single_select_page' === $setting['type'] ) { $pages = get_pages( array( 'sort_column' => 'menu_order', 'sort_order' => 'ASC', 'hierarchical' => 0, ) ); $options = array(); foreach ( $pages as $page ) { $options[ $page->ID ] = ! empty( $page->post_title ) ? $page->post_title : '#' . $page->ID; } $setting['type'] = 'select'; $setting['options'] = $options; } $filtered_settings[] = $setting; } return $filtered_settings; } /** * Returns a list of countries and states for use in the base location setting. * * @since 3.0.7 * @return array Array of states and countries. */ private function get_countries_and_states() { $countries = WC()->countries->get_countries(); if ( ! $countries ) { return array(); } $output = array(); foreach ( $countries as $key => $value ) { $states = WC()->countries->get_states( $key ); if ( $states ) { foreach ( $states as $state_key => $state_value ) { $output[ $key . ':' . $state_key ] = $value . ' - ' . $state_value; } } else { $output[ $key ] = $value; } } return $output; } /** * Get the settings schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'setting', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_title', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'group_id' => array( 'description' => __( 'An identifier for the group this setting belongs to.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_title', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'label' => array( 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'value' => array( 'description' => __( 'Setting value.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), ), 'default' => array( 'description' => __( 'Default value for the setting.', 'woocommerce' ), 'type' => 'mixed', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'tip' => array( 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'placeholder' => array( 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'type' => array( 'description' => __( 'Type of setting.', 'woocommerce' ), 'type' => 'string', 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'context' => array( 'view', 'edit' ), 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox' ), 'readonly' => true, ), 'options' => array( 'description' => __( 'Array of options (key value pairs) for inputs such as select, multiselect, and radio buttons.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\M7&&^woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-system-status-tools-controller.phpnu[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 . '/current', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_current_item' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]{3})', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ), 'args' => array( 'location' => array( 'description' => __( 'ISO4217 currency code.', 'woocommerce' ), 'type' => 'string', ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Get currency information. * * @param string $code Currency code. * @param WP_REST_Request $request Request data. * @return array|mixed Response data, ready for insertion into collection data. */ public function get_currency( $code = false, $request ) { $currencies = get_woocommerce_currencies(); $data = array(); if ( ! array_key_exists( $code, $currencies ) ) { return false; } $currency = array( 'code' => $code, 'name' => $currencies[ $code ], 'symbol' => get_woocommerce_currency_symbol( $code ), ); return $currency; } /** * Return the list of currencies. * * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $currencies = get_woocommerce_currencies(); foreach ( array_keys( $currencies ) as $code ) { $currency = $this->get_currency( $code, $request ); $response = $this->prepare_item_for_response( $currency, $request ); $data[] = $this->prepare_response_for_collection( $response ); } return rest_ensure_response( $data ); } /** * Return information for a specific currency. * * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_item( $request ) { $data = $this->get_currency( strtoupper( $request['currency'] ), $request ); if ( empty( $data ) ) { return new WP_Error( 'woocommerce_rest_data_invalid_currency', __( 'There are no currencies matching these parameters.', 'woocommerce' ), array( 'status' => 404 ) ); } return $this->prepare_item_for_response( $data, $request ); } /** * Return information for the current site currency. * * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_current_item( $request ) { $currency = get_option( 'woocommerce_currency' ); return $this->prepare_item_for_response( $this->get_currency( $currency, $request ), $request ); } /** * 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 currency returned from the API. * * @param WP_REST_Response $response The response object. * @param array $item Currency data. * @param WP_REST_Request $request Request used to generate the response. */ return apply_filters( 'woocommerce_rest_prepare_data_currency', $response, $item, $request ); } /** * Prepare links for the request. * * @param object $item Data object. * @return array Links for the given currency. */ protected function prepare_links( $item ) { $code = strtoupper( $item['code'] ); $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $code ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Get the currency schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'data_currencies', 'type' => 'object', 'properties' => array( 'code' => array( 'type' => 'string', 'description' => __( 'ISO4217 currency code.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'type' => 'string', 'description' => __( 'Full name of currency.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'symbol' => array( 'type' => 'string', 'description' => __( 'Currency symbol.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\gYwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-network-orders-controller.phpnu[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 ), 'date_created_gmt' => wc_rest_prepare_date_response( $attachment->post_date_gmt ), 'date_modified' => wc_rest_prepare_date_response( $attachment->post_modified ), 'date_modified_gmt' => wc_rest_prepare_date_response( $attachment->post_modified_gmt ), 'src' => wp_get_attachment_url( $image_id ), 'name' => 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 ); } /** * 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_created_gmt' => array( 'description' => __( 'The date the image was created, as GMT.', '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, ), 'date_modified_gmt' => array( 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'src' => array( 'description' => __( 'Image URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), 'name' => 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 ); } /** * Update term meta fields. * * @param WP_Term $term Term object. * @param WP_REST_Request $request Request instance. * @return bool|WP_Error * * @since 3.5.5 */ 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']['name'] ) ) { wp_update_post( array( 'ID' => $image_id, 'post_title' => wc_clean( $request['image']['name'] ), ) ); } } else { delete_term_meta( $id, 'thumbnail_id' ); } } return true; } } PKo5\ Swoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-settings-controller.phpnu[namespace, '/' . $this->rest_base . '/batch', array( array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'batch_items' ), 'permission_callback' => array( $this, 'update_items_permissions_check' ), ), 'schema' => array( $this, 'get_public_batch_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 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' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Update a setting. * * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function update_item( $request ) { $options_controller = new WC_REST_Setting_Options_Controller(); $response = $options_controller->update_item( $request ); return $response; } /** * Get the groups schema, conforming to JSON Schema. * * @since 3.0.0 * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'setting_group', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'A unique identifier that can be used to link settings together.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'label' => array( 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'description' => array( 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'parent_id' => array( 'description' => __( 'ID of parent grouping.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sub_groups' => array( 'description' => __( 'IDs for settings sub groups.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\H?Ywoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-shipping-zones-controller.phpnu[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 . '/(?P[\w-]+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), 'args' => array( 'location' => array( 'description' => __( 'ISO3166 alpha-2 country code.', 'woocommerce' ), 'type' => 'string', ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Get a list of countries and states. * * @param string $country_code Country code. * @param WP_REST_Request $request Request data. * @return array|mixed Response data, ready for insertion into collection data. */ public function get_country( $country_code = false, $request ) { $countries = WC()->countries->get_countries(); $states = WC()->countries->get_states(); $data = array(); if ( ! array_key_exists( $country_code, $countries ) ) { return false; } $country = array( 'code' => $country_code, 'name' => $countries[ $country_code ], ); $local_states = array(); if ( isset( $states[ $country_code ] ) ) { foreach ( $states[ $country_code ] as $state_code => $state_name ) { $local_states[] = array( 'code' => $state_code, 'name' => $state_name, ); } } $country['states'] = $local_states; return $country; } /** * Return the list of states for all countries. * * @since 3.5.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $countries = WC()->countries->get_countries(); $data = array(); foreach ( array_keys( $countries ) as $country_code ) { $country = $this->get_country( $country_code, $request ); $response = $this->prepare_item_for_response( $country, $request ); $data[] = $this->prepare_response_for_collection( $response ); } return rest_ensure_response( $data ); } /** * Return the list of states for a given country. * * @since 3.5.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_item( $request ) { $data = $this->get_country( strtoupper( $request['location'] ), $request ); if ( empty( $data ) ) { return new WP_Error( 'woocommerce_rest_data_invalid_location', __( 'There are no locations matching these parameters.', 'woocommerce' ), array( 'status' => 404 ) ); } return $this->prepare_item_for_response( $data, $request ); } /** * 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 states list for a country returned from the API. * * Allows modification of the loction data right before it is returned. * * @param WP_REST_Response $response The response object. * @param array $data The original country's states list. * @param WP_REST_Request $request Request used to generate the response. */ return apply_filters( 'woocommerce_rest_prepare_data_country', $response, $item, $request ); } /** * Prepare links for the request. * * @param object $item Data object. * @return array Links for the given country. */ protected function prepare_links( $item ) { $country_code = strtolower( $item['code'] ); $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $country_code ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Get the location schema, conforming to JSON Schema. * * @since 3.5.0 * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'data_countries', 'type' => 'object', 'properties' => array( 'code' => array( 'type' => 'string', 'description' => __( 'ISO3166 alpha-2 country code.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'type' => 'string', 'description' => __( 'Full name of country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'states' => array( 'type' => 'array', 'description' => __( 'List of states in this country.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, 'items' => array( 'type' => 'object', 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( 'code' => array( 'type' => 'string', 'description' => __( 'State code.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'type' => 'string', 'description' => __( 'Full name of state.', 'woocommerce' ), 'context' => array( 'view' ), 'readonly' => true, ), ), ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\<1@Rwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-coupons-controller.phpnu[ $name ) { $results = $wpdb->get_results( $wpdb->prepare( " SELECT count(meta_id) AS total FROM $wpdb->postmeta WHERE meta_key = 'discount_type' AND meta_value = %s ", $slug ) ); $total = isset( $results[0] ) ? (int) $results[0]->total : 0; $data[] = array( 'slug' => $slug, 'name' => $name, 'total' => $total, ); } set_transient( 'rest_api_coupons_type_count', $data, YEAR_IN_SECONDS ); return $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, 'name' => $report->name, 'total' => $report->total, ); $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_count', $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_coupon_total', 'type' => 'object', 'properties' => array( 'slug' => array( 'description' => __( 'An alphanumeric identifier for the resource.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'Coupon type name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Amount of coupons.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\ޫ]woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-report-top-sellers-controller.phpnu[get_image_id() ) { $attachment_ids[] = $product->get_image_id(); } // Add gallery images. $attachment_ids = array_merge( $attachment_ids, $product->get_gallery_image_ids() ); // Build image data. foreach ( $attachment_ids as $attachment_id ) { $attachment_post = get_post( $attachment_id ); if ( is_null( $attachment_post ) ) { continue; } $attachment = wp_get_attachment_image_src( $attachment_id, 'full' ); if ( ! is_array( $attachment ) ) { continue; } $images[] = array( 'id' => (int) $attachment_id, 'date_created' => wc_rest_prepare_date_response( $attachment_post->post_date, false ), 'date_created_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ), 'date_modified' => wc_rest_prepare_date_response( $attachment_post->post_modified, false ), 'date_modified_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_modified_gmt ) ), 'src' => current( $attachment ), 'name' => get_the_title( $attachment_id ), 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), ); } return $images; } /** * Make extra product orderby features supported by WooCommerce available to the WC API. * This includes 'price', 'popularity', and 'rating'. * * @param WP_REST_Request $request Request data. * @return array */ protected function prepare_objects_query( $request ) { $args = WC_REST_CRUD_Controller::prepare_objects_query( $request ); // Set post_status. $args['post_status'] = $request['status']; // Taxonomy query to filter products by type, category, // tag, shipping class, and attribute. $tax_query = array(); // Map between taxonomy name and arg's key. $taxonomies = array( 'product_cat' => 'category', 'product_tag' => 'tag', 'product_shipping_class' => 'shipping_class', ); // Set tax_query for each passed arg. foreach ( $taxonomies as $taxonomy => $key ) { if ( ! empty( $request[ $key ] ) ) { $tax_query[] = array( 'taxonomy' => $taxonomy, 'field' => 'term_id', 'terms' => $request[ $key ], ); } } // Filter product type by slug. if ( ! empty( $request['type'] ) ) { $tax_query[] = array( 'taxonomy' => 'product_type', 'field' => 'slug', 'terms' => $request['type'], ); } // Filter by attribute and term. if ( ! empty( $request['attribute'] ) && ! empty( $request['attribute_term'] ) ) { if ( in_array( $request['attribute'], wc_get_attribute_taxonomy_names(), true ) ) { $tax_query[] = array( 'taxonomy' => $request['attribute'], 'field' => 'term_id', 'terms' => $request['attribute_term'], ); } } // Build tax_query if taxonomies are set. if ( ! empty( $tax_query ) ) { if ( ! empty( $args['tax_query'] ) ) { $args['tax_query'] = array_merge( $tax_query, $args['tax_query'] ); // WPCS: slow query ok. } else { $args['tax_query'] = $tax_query; // WPCS: slow query ok. } } // Filter featured. if ( is_bool( $request['featured'] ) ) { $args['tax_query'][] = array( 'taxonomy' => 'product_visibility', 'field' => 'name', 'terms' => 'featured', 'operator' => true === $request['featured'] ? 'IN' : 'NOT IN', ); } // Filter by sku. if ( ! empty( $request['sku'] ) ) { $skus = explode( ',', $request['sku'] ); // Include the current string as a SKU too. if ( 1 < count( $skus ) ) { $skus[] = $request['sku']; } $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_sku', 'value' => $skus, 'compare' => 'IN', ) ); } // Filter by tax class. if ( ! empty( $request['tax_class'] ) ) { $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_tax_class', 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', ) ); } // Price filter. if ( ! empty( $request['min_price'] ) || ! empty( $request['max_price'] ) ) { $args['meta_query'] = $this->add_meta_query( $args, wc_get_min_max_price_meta_query( $request ) ); // WPCS: slow query ok. } // Filter product by stock_status. if ( ! empty( $request['stock_status'] ) ) { $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_stock_status', 'value' => $request['stock_status'], ) ); } // Filter by on sale products. if ( is_bool( $request['on_sale'] ) ) { $on_sale_key = $request['on_sale'] ? 'post__in' : 'post__not_in'; $on_sale_ids = wc_get_product_ids_on_sale(); // Use 0 when there's no on sale products to avoid return all products. $on_sale_ids = empty( $on_sale_ids ) ? array( 0 ) : $on_sale_ids; $args[ $on_sale_key ] += $on_sale_ids; } // Force the post_type argument, since it's not a user input variable. if ( ! empty( $request['sku'] ) ) { $args['post_type'] = array( 'product', 'product_variation' ); } else { $args['post_type'] = $this->post_type; } $orderby = $request->get_param( 'orderby' ); $order = $request->get_param( 'order' ); $ordering_args = WC()->query->get_catalog_ordering_args( $orderby, $order ); $args['orderby'] = $ordering_args['orderby']; $args['order'] = $ordering_args['order']; if ( $ordering_args['meta_key'] ) { $args['meta_key'] = $ordering_args['meta_key']; // WPCS: slow query ok. } return $args; } /** * Set product images. * * @throws WC_REST_Exception REST API exceptions. * @param WC_Product $product Product instance. * @param array $images Images data. * @return WC_Product */ protected function set_product_images( $product, $images ) { $images = is_array( $images ) ? array_filter( $images ) : array(); if ( ! empty( $images ) ) { $gallery = array(); foreach ( $images as $index => $image ) { $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; if ( 0 === $attachment_id && isset( $image['src'] ) ) { $upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) ); if ( is_wp_error( $upload ) ) { if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $product->get_id(), $images ) ) { throw new WC_REST_Exception( 'woocommerce_product_image_upload_error', $upload->get_error_message(), 400 ); } else { continue; } } $attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $product->get_id() ); } if ( ! wp_attachment_is_image( $attachment_id ) ) { /* translators: %s: image ID */ throw new WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 ); } $featured_image = $product->get_image_id(); if ( 0 === $index ) { $product->set_image_id( $attachment_id ); } else { $gallery[] = $attachment_id; } // Set the image alt if present. if ( ! empty( $image['alt'] ) ) { update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) ); } // Set the image name if present. if ( ! empty( $image['name'] ) ) { wp_update_post( array( 'ID' => $attachment_id, 'post_title' => $image['name'], ) ); } } $product->set_gallery_image_ids( $gallery ); } else { $product->set_image_id( '' ); $product->set_gallery_image_ids( array() ); } return $product; } /** * Prepare a single product 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; // Type is the most important part here because we need to be using the correct class and methods. if ( isset( $request['type'] ) ) { $classname = WC_Product_Factory::get_classname_from_product_type( $request['type'] ); if ( ! class_exists( $classname ) ) { $classname = 'WC_Product_Simple'; } $product = new $classname( $id ); } elseif ( isset( $request['id'] ) ) { $product = wc_get_product( $id ); } else { $product = new WC_Product_Simple(); } if ( 'variation' === $product->get_type() ) { return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404, ) ); } // Post title. if ( isset( $request['name'] ) ) { $product->set_name( wp_filter_post_kses( $request['name'] ) ); } // Post content. if ( isset( $request['description'] ) ) { $product->set_description( wp_filter_post_kses( $request['description'] ) ); } // Post excerpt. if ( isset( $request['short_description'] ) ) { $product->set_short_description( wp_filter_post_kses( $request['short_description'] ) ); } // Post status. if ( isset( $request['status'] ) ) { $product->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' ); } // Post slug. if ( isset( $request['slug'] ) ) { $product->set_slug( $request['slug'] ); } // Menu order. if ( isset( $request['menu_order'] ) ) { $product->set_menu_order( $request['menu_order'] ); } // Comment status. if ( isset( $request['reviews_allowed'] ) ) { $product->set_reviews_allowed( $request['reviews_allowed'] ); } // Virtual. if ( isset( $request['virtual'] ) ) { $product->set_virtual( $request['virtual'] ); } // Tax status. if ( isset( $request['tax_status'] ) ) { $product->set_tax_status( $request['tax_status'] ); } // Tax Class. if ( isset( $request['tax_class'] ) ) { $product->set_tax_class( $request['tax_class'] ); } // Catalog Visibility. if ( isset( $request['catalog_visibility'] ) ) { $product->set_catalog_visibility( $request['catalog_visibility'] ); } // Purchase Note. if ( isset( $request['purchase_note'] ) ) { $product->set_purchase_note( wp_kses_post( wp_unslash( $request['purchase_note'] ) ) ); } // Featured Product. if ( isset( $request['featured'] ) ) { $product->set_featured( $request['featured'] ); } // Shipping data. $product = $this->save_product_shipping_data( $product, $request ); // SKU. if ( isset( $request['sku'] ) ) { $product->set_sku( wc_clean( $request['sku'] ) ); } // Attributes. if ( isset( $request['attributes'] ) ) { $attributes = array(); foreach ( $request['attributes'] as $attribute ) { $attribute_id = 0; $attribute_name = ''; // Check ID for global attributes or name for product attributes. if ( ! empty( $attribute['id'] ) ) { $attribute_id = absint( $attribute['id'] ); $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); } elseif ( ! empty( $attribute['name'] ) ) { $attribute_name = wc_clean( $attribute['name'] ); } if ( ! $attribute_id && ! $attribute_name ) { continue; } if ( $attribute_id ) { if ( isset( $attribute['options'] ) ) { $options = $attribute['options']; if ( ! is_array( $attribute['options'] ) ) { // Text based attributes - Posted values are term names. $options = explode( WC_DELIMITER, $options ); } $values = array_map( 'wc_sanitize_term_text_based', $options ); $values = array_filter( $values, 'strlen' ); } else { $values = array(); } if ( ! empty( $values ) ) { // Add attribute to array, but don't set values. $attribute_object = new WC_Product_Attribute(); $attribute_object->set_id( $attribute_id ); $attribute_object->set_name( $attribute_name ); $attribute_object->set_options( $values ); $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; } } elseif ( isset( $attribute['options'] ) ) { // Custom attribute - Add attribute to array and set the values. if ( is_array( $attribute['options'] ) ) { $values = $attribute['options']; } else { $values = explode( WC_DELIMITER, $attribute['options'] ); } $attribute_object = new WC_Product_Attribute(); $attribute_object->set_name( $attribute_name ); $attribute_object->set_options( $values ); $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); $attributes[] = $attribute_object; } } $product->set_attributes( $attributes ); } // Sales and prices. if ( in_array( $product->get_type(), array( 'variable', 'grouped' ), true ) ) { $product->set_regular_price( '' ); $product->set_sale_price( '' ); $product->set_date_on_sale_to( '' ); $product->set_date_on_sale_from( '' ); $product->set_price( '' ); } else { // Regular Price. if ( isset( $request['regular_price'] ) ) { $product->set_regular_price( $request['regular_price'] ); } // Sale Price. if ( isset( $request['sale_price'] ) ) { $product->set_sale_price( $request['sale_price'] ); } if ( isset( $request['date_on_sale_from'] ) ) { $product->set_date_on_sale_from( $request['date_on_sale_from'] ); } if ( isset( $request['date_on_sale_from_gmt'] ) ) { $product->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null ); } if ( isset( $request['date_on_sale_to'] ) ) { $product->set_date_on_sale_to( $request['date_on_sale_to'] ); } if ( isset( $request['date_on_sale_to_gmt'] ) ) { $product->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null ); } } // Product parent ID. if ( isset( $request['parent_id'] ) ) { $product->set_parent_id( $request['parent_id'] ); } // Sold individually. if ( isset( $request['sold_individually'] ) ) { $product->set_sold_individually( $request['sold_individually'] ); } // Stock status; stock_status has priority over in_stock. if ( isset( $request['stock_status'] ) ) { $stock_status = $request['stock_status']; } else { $stock_status = $product->get_stock_status(); } // Stock data. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { // Manage stock. if ( isset( $request['manage_stock'] ) ) { $product->set_manage_stock( $request['manage_stock'] ); } // Backorders. if ( isset( $request['backorders'] ) ) { $product->set_backorders( $request['backorders'] ); } if ( $product->is_type( 'grouped' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } elseif ( $product->is_type( 'external' ) ) { $product->set_manage_stock( 'no' ); $product->set_backorders( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( 'instock' ); } elseif ( $product->get_manage_stock() ) { // Stock status is always determined by children so sync later. if ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Stock quantity. if ( isset( $request['stock_quantity'] ) ) { $product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) ); } elseif ( isset( $request['inventory_delta'] ) ) { $stock_quantity = wc_stock_amount( $product->get_stock_quantity() ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); } } else { // Don't manage stock. $product->set_manage_stock( 'no' ); $product->set_stock_quantity( '' ); $product->set_stock_status( $stock_status ); } } elseif ( ! $product->is_type( 'variable' ) ) { $product->set_stock_status( $stock_status ); } // Upsells. if ( isset( $request['upsell_ids'] ) ) { $upsells = array(); $ids = $request['upsell_ids']; if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { if ( $id && $id > 0 ) { $upsells[] = $id; } } } $product->set_upsell_ids( $upsells ); } // Cross sells. if ( isset( $request['cross_sell_ids'] ) ) { $crosssells = array(); $ids = $request['cross_sell_ids']; if ( ! empty( $ids ) ) { foreach ( $ids as $id ) { if ( $id && $id > 0 ) { $crosssells[] = $id; } } } $product->set_cross_sell_ids( $crosssells ); } // Product categories. if ( isset( $request['categories'] ) && is_array( $request['categories'] ) ) { $product = $this->save_taxonomy_terms( $product, $request['categories'] ); } // Product tags. if ( isset( $request['tags'] ) && is_array( $request['tags'] ) ) { $product = $this->save_taxonomy_terms( $product, $request['tags'], 'tag' ); } // Downloadable. if ( isset( $request['downloadable'] ) ) { $product->set_downloadable( $request['downloadable'] ); } // Downloadable options. if ( $product->get_downloadable() ) { // Downloadable files. if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { $product = $this->save_downloadable_files( $product, $request['downloads'] ); } // Download limit. if ( isset( $request['download_limit'] ) ) { $product->set_download_limit( $request['download_limit'] ); } // Download expiry. if ( isset( $request['download_expiry'] ) ) { $product->set_download_expiry( $request['download_expiry'] ); } } // Product url and button text for external products. if ( $product->is_type( 'external' ) ) { if ( isset( $request['external_url'] ) ) { $product->set_product_url( $request['external_url'] ); } if ( isset( $request['button_text'] ) ) { $product->set_button_text( $request['button_text'] ); } } // Save default attributes for variable products. if ( $product->is_type( 'variable' ) ) { $product = $this->save_default_attributes( $product, $request ); } // Set children for a grouped product. if ( $product->is_type( 'grouped' ) && isset( $request['grouped_products'] ) ) { $product->set_children( $request['grouped_products'] ); } // Check for featured/gallery images, upload it and set it. if ( isset( $request['images'] ) ) { $product = $this->set_product_images( $product, $request['images'] ); } // Allow set meta_data. if ( is_array( $request['meta_data'] ) ) { foreach ( $request['meta_data'] as $meta ) { $product->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } } if ( ! empty( $request['date_created'] ) ) { $date = rest_parse_date( $request['date_created'] ); if ( $date ) { $product->set_date_created( $date ); } } if ( ! empty( $request['date_created_gmt'] ) ) { $date = rest_parse_date( $request['date_created_gmt'], true ); if ( $date ) { $product->set_date_created( $date ); } } /** * 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 $product 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", $product, $request, $creating ); } /** * Get the Product's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $weight_unit = get_option( 'woocommerce_weight_unit' ); $dimension_unit = get_option( 'woocommerce_dimension_unit' ); $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, ), 'name' => array( 'description' => __( 'Product name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'slug' => array( 'description' => __( 'Product slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'permalink' => array( 'description' => __( 'Product URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created' => array( 'description' => __( "The date the product was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_created_gmt' => array( 'description' => __( 'The date the product was created, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_modified' => array( 'description' => __( "The date the product 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 product was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'type' => array( 'description' => __( 'Product type.', 'woocommerce' ), 'type' => 'string', 'default' => 'simple', 'enum' => array_keys( wc_get_product_types() ), 'context' => array( 'view', 'edit' ), ), 'status' => array( 'description' => __( 'Product status (post status).', 'woocommerce' ), 'type' => 'string', 'default' => 'publish', 'enum' => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ), 'context' => array( 'view', 'edit' ), ), 'featured' => array( 'description' => __( 'Featured product.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'catalog_visibility' => array( 'description' => __( 'Catalog visibility.', 'woocommerce' ), 'type' => 'string', 'default' => 'visible', 'enum' => array( 'visible', 'catalog', 'search', 'hidden' ), 'context' => array( 'view', 'edit' ), ), 'description' => array( 'description' => __( 'Product description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'short_description' => array( 'description' => __( 'Product short description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sku' => array( 'description' => __( 'Unique identifier.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'price' => array( 'description' => __( 'Current product price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'regular_price' => array( 'description' => __( 'Product regular price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sale_price' => array( 'description' => __( 'Product sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from' => array( 'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from_gmt' => array( 'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to' => array( 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to_gmt' => array( 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'price_html' => array( 'description' => __( 'Price formatted in HTML.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'on_sale' => array( 'description' => __( 'Shows if the product is on sale.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'purchasable' => array( 'description' => __( 'Shows if the product can be bought.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'total_sales' => array( 'description' => __( 'Amount of sales.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'virtual' => array( 'description' => __( 'If the product is virtual.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloadable' => array( 'description' => __( 'If the product is downloadable.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloads' => array( 'description' => __( 'List of downloadable files.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'File ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'File name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'file' => array( 'description' => __( 'File URL.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'download_limit' => array( 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'download_expiry' => array( 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'external_url' => array( 'description' => __( 'Product external URL. Only for external products.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), 'button_text' => array( 'description' => __( 'Product external button text. Only for external products.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'tax_status' => array( 'description' => __( 'Tax status.', 'woocommerce' ), 'type' => 'string', 'default' => 'taxable', 'enum' => array( 'taxable', 'shipping', 'none' ), 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'manage_stock' => array( 'description' => __( 'Stock management at product level.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'stock_quantity' => array( 'description' => __( 'Stock quantity.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'stock_status' => array( 'description' => __( 'Controls the stock status of the product.', 'woocommerce' ), 'type' => 'string', 'default' => 'instock', 'enum' => array_keys( wc_get_product_stock_status_options() ), 'context' => array( 'view', 'edit' ), ), 'backorders' => array( 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ), 'type' => 'string', 'default' => 'no', 'enum' => array( 'no', 'notify', 'yes' ), 'context' => array( 'view', 'edit' ), ), 'backorders_allowed' => array( 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'backordered' => array( 'description' => __( 'Shows if the product is on backordered.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'sold_individually' => array( 'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'weight' => array( /* translators: %s: weight unit */ 'description' => sprintf( __( 'Product weight (%s).', 'woocommerce' ), $weight_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'dimensions' => array( 'description' => __( 'Product dimensions.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'length' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product length (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'width' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product width (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'height' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Product height (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'shipping_required' => array( 'description' => __( 'Shows if the product need to be shipped.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_taxable' => array( 'description' => __( 'Shows whether or not the product shipping is taxable.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'shipping_class' => array( 'description' => __( 'Shipping class slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'shipping_class_id' => array( 'description' => __( 'Shipping class ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'reviews_allowed' => array( 'description' => __( 'Allow reviews.', 'woocommerce' ), 'type' => 'boolean', 'default' => true, 'context' => array( 'view', 'edit' ), ), 'average_rating' => array( 'description' => __( 'Reviews average rating.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'rating_count' => array( 'description' => __( 'Amount of reviews that the product have.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'related_ids' => array( 'description' => __( 'List of related products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'upsell_ids' => array( 'description' => __( 'List of up-sell products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), ), 'cross_sell_ids' => array( 'description' => __( 'List of cross-sell products IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), ), 'parent_id' => array( 'description' => __( 'Product parent ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'purchase_note' => array( 'description' => __( 'Optional note to send the customer after purchase.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'categories' => array( 'description' => __( 'List of categories.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Category ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Category name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'slug' => array( 'description' => __( 'Category slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'tags' => array( 'description' => __( 'List of tags.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Tag ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Tag name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'slug' => array( 'description' => __( 'Tag slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), 'images' => array( 'description' => __( 'List of images.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', '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_created_gmt' => array( 'description' => __( 'The date the image was created, as GMT.', '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, ), 'date_modified_gmt' => array( 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'src' => array( 'description' => __( 'Image URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Image name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'alt' => array( 'description' => __( 'Image alternative text.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'attributes' => array( 'description' => __( 'List of attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'position' => array( 'description' => __( 'Attribute position.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'visible' => array( 'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'variation' => array( 'description' => __( 'Define if the attribute can be used as variation.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'options' => array( 'description' => __( 'List of available term names of the attribute.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'string', ), 'context' => array( 'view', 'edit' ), ), ), ), ), 'default_attributes' => array( 'description' => __( 'Defaults variation attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'option' => array( 'description' => __( 'Selected attribute term name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'variations' => array( 'description' => __( 'List of variations IDs.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'integer', ), 'readonly' => true, ), 'grouped_products' => array( 'description' => __( 'List of grouped products ID.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'menu_order' => array( 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), '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 ); } /** * Add new options for 'orderby' to the collection params. * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); $params['orderby']['enum'] = array_merge( $params['orderby']['enum'], array( 'price', 'popularity', 'rating' ) ); unset( $params['in_stock'] ); $params['stock_status'] = array( 'description' => __( 'Limit result set to products with specified stock status.', 'woocommerce' ), 'type' => 'string', 'enum' => array_keys( wc_get_product_stock_status_options() ), 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); return $params; } /** * Get product data. * * @param WC_Product $product Product instance. * @param string $context Request context. * Options: 'view' and 'edit'. * @return array */ protected function get_product_data( $product, $context = 'view' ) { $data = parent::get_product_data( $product, $context ); // Replace in_stock with stock_status. $pos = array_search( 'in_stock', array_keys( $data ), true ); $array_section_1 = array_slice( $data, 0, $pos, true ); $array_section_2 = array_slice( $data, $pos + 1, null, true ); return $array_section_1 + array( 'stock_status' => $product->get_stock_status( $context ) ) + $array_section_2; } } PKo5\1eQwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-orders-controller.phpnu[get_items( 'coupon' ) as $coupon ) { $order->remove_coupon( $coupon->get_code() ); } foreach ( $request['coupon_lines'] as $item ) { if ( is_array( $item ) ) { if ( empty( $item['id'] ) ) { if ( empty( $item['code'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_coupon', __( 'Coupon code is required.', 'woocommerce' ), 400 ); } $results = $order->apply_coupon( wc_clean( $item['code'] ) ); if ( is_wp_error( $results ) ) { throw new WC_REST_Exception( 'woocommerce_rest_' . $results->get_error_code(), $results->get_error_message(), 400 ); } } } } return true; } /** * Prepare a single order for create or update. * * @throws WC_REST_Exception When fails to set any item. * @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; $order = new WC_Order( $id ); $schema = $this->get_item_schema(); $data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) ); // Handle all writable props. foreach ( $data_keys as $key ) { $value = $request[ $key ]; if ( ! is_null( $value ) ) { switch ( $key ) { case 'coupon_lines': case 'status': // Change should be done later so transitions have new data. break; case 'billing': case 'shipping': $this->update_address( $order, $value, $key ); break; case 'line_items': case 'shipping_lines': case 'fee_lines': if ( is_array( $value ) ) { foreach ( $value as $item ) { if ( is_array( $item ) ) { if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) { $order->remove_item( $item['id'] ); } else { $this->set_item( $order, $key, $item ); } } } } break; case 'meta_data': if ( is_array( $value ) ) { foreach ( $value as $meta ) { $order->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } } break; default: if ( is_callable( array( $order, "set_{$key}" ) ) ) { $order->{"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 $order 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", $order, $request, $creating ); } /** * Save an object data. * * @since 3.0.0 * @throws WC_REST_Exception But all errors are validated before returning any data. * @param WP_REST_Request $request Full details about the request. * @param bool $creating If is creating a new object. * @return WC_Data|WP_Error */ protected function save_object( $request, $creating = false ) { try { $object = $this->prepare_object_for_database( $request, $creating ); if ( is_wp_error( $object ) ) { return $object; } // Make sure gateways are loaded so hooks from gateways fire on save/create. WC()->payment_gateways(); if ( ! is_null( $request['customer_id'] ) && 0 !== $request['customer_id'] ) { // Make sure customer exists. if ( false === get_user_by( 'id', $request['customer_id'] ) ) { throw new WC_REST_Exception( 'woocommerce_rest_invalid_customer_id', __( 'Customer ID is invalid.', 'woocommerce' ), 400 ); } // Make sure customer is part of blog. if ( is_multisite() && ! is_user_member_of_blog( $request['customer_id'] ) ) { add_user_to_blog( get_current_blog_id(), $request['customer_id'], 'customer' ); } } if ( $creating ) { $object->set_created_via( 'rest-api' ); $object->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) ); $object->calculate_totals(); } else { // If items have changed, recalculate order totals. if ( isset( $request['billing'] ) || isset( $request['shipping'] ) || isset( $request['line_items'] ) || isset( $request['shipping_lines'] ) || isset( $request['fee_lines'] ) || isset( $request['coupon_lines'] ) ) { $object->calculate_totals( true ); } } // Set coupons. $this->calculate_coupons( $request, $object ); // Set status. if ( ! empty( $request['status'] ) ) { $object->set_status( $request['status'] ); } $object->save(); // Actions for after the order is saved. if ( true === $request['set_paid'] ) { if ( $creating || $object->needs_payment() ) { $object->payment_complete( $request['transaction_id'] ); } } return $this->get_object( $object->get_id() ); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * 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 ) { // This is needed to get around an array to string notice in WC_REST_Orders_V2_Controller::prepare_objects_query. $statuses = $request['status']; unset( $request['status'] ); $args = parent::prepare_objects_query( $request ); $args['post_status'] = array(); foreach ( $statuses as $status ) { if ( in_array( $status, $this->get_order_statuses(), true ) ) { $args['post_status'][] = 'wc-' . $status; } elseif ( 'any' === $status ) { // Set status to "any" and short-circuit out. $args['post_status'] = 'any'; break; } else { $args['post_status'][] = $status; } } // Put the statuses back for further processing (next/prev links, etc). $request['status'] = $statuses; return $args; } /** * Get the Order's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = parent::get_item_schema(); $schema['properties']['coupon_lines']['items']['properties']['discount']['readonly'] = true; return $schema; } /** * Get the query params for collections. * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); $params['status'] = array( 'default' => 'any', 'description' => __( 'Limit result set to orders which have specific statuses.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'string', 'enum' => array_merge( array( 'any', 'trash' ), $this->get_order_statuses() ), ), 'validate_callback' => 'rest_validate_request_arg', ); return $params; } } PKo5\f) xE)E)Twoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-customers-controller.phpnu[get_data(); $format_date = array( 'date_created', 'date_modified' ); // Format date values. foreach ( $format_date as $key ) { // Date created is stored UTC, date modified is stored WP local time. $datetime = 'date_created' === $key ? get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $data[ $key ]->getTimestamp() ) ) : $data[ $key ]; $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); } return array( 'id' => $object->get_id(), 'date_created' => $data['date_created'], 'date_created_gmt' => $data['date_created_gmt'], 'date_modified' => $data['date_modified'], 'date_modified_gmt' => $data['date_modified_gmt'], 'email' => $data['email'], 'first_name' => $data['first_name'], 'last_name' => $data['last_name'], 'role' => $data['role'], 'username' => $data['username'], 'billing' => $data['billing'], 'shipping' => $data['shipping'], 'is_paying_customer' => $data['is_paying_customer'], 'avatar_url' => $object->get_avatar_url(), 'meta_data' => $data['meta_data'], ); } /** * Get the Customer's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'customer', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created' => array( 'description' => __( "The date the customer was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created_gmt' => 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, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified_gmt' => 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', ), ), 'role' => array( 'description' => __( 'Customer role.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), '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' ), ), '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' ), ), ), ), 'is_paying_customer' => array( 'description' => __( 'Is the customer a paying customer?', 'woocommerce' ), 'type' => 'bool', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'avatar_url' => array( 'description' => __( 'Avatar URL.', 'woocommerce' ), 'type' => 'string', '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 ); } } PKo5\5Owoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-data-controller.phpnu[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' ), ) ); } /** * Check whether a given request has permission to read site 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' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check whether a given request has permission to read site settings. * * @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; } /** * Return the list of data resources. * * @since 3.5.0 * @param WP_REST_Request $request Request data. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $data = array(); $resources = array( array( 'slug' => 'continents', 'description' => __( 'List of supported continents, countries, and states.', 'woocommerce' ), ), array( 'slug' => 'countries', 'description' => __( 'List of supported states in a given country.', 'woocommerce' ), ), array( 'slug' => 'currencies', 'description' => __( 'List of supported currencies.', 'woocommerce' ), ), ); foreach ( $resources as $resource ) { $item = $this->prepare_item_for_response( (object) $resource, $request ); $data[] = $this->prepare_response_for_collection( $item ); } return rest_ensure_response( $data ); } /** * Prepare a data resource object for serialization. * * @param stdClass $resource Resource data. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response Response data. */ public function prepare_item_for_response( $resource, $request ) { $data = array( 'slug' => $resource->slug, 'description' => $resource->description, ); $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, 'view' ); // Wrap the data in a response object. $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $resource ) ); return $response; } /** * Prepare links for the request. * * @param object $item Data object. * @return array Links for the given country. */ protected function prepare_links( $item ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $item->slug ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Get the data index schema, conforming to JSON Schema. * * @since 3.5.0 * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'data_index', 'type' => 'object', 'properties' => array( 'slug' => array( 'description' => __( 'Data resource ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'Data resource description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\  awoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-report-products-totals-controller.phpnu[ 'product_type', 'hide_empty' => false, ) ); $data = array(); foreach ( $terms as $product_type ) { if ( ! isset( $types[ $product_type->name ] ) ) { continue; } $data[] = array( 'slug' => $product_type->name, 'name' => $types[ $product_type->name ], 'total' => (int) $product_type->count, ); } return $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, 'name' => $report->name, 'total' => $report->total, ); $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_count', $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_product_total', 'type' => 'object', 'properties' => array( 'slug' => array( 'description' => __( 'An alphanumeric identifier for the resource.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'Product type name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Amount of products.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\*FwFw]woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-product-variations-controller.phpnu[/variations endpoints. * * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ defined( 'ABSPATH' ) || exit; /** * REST API variations controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Product_Variations_V2_Controller */ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v3'; /** * 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 ) { $data = array( 'id' => $object->get_id(), 'date_created' => wc_rest_prepare_date_response( $object->get_date_created(), false ), 'date_created_gmt' => wc_rest_prepare_date_response( $object->get_date_created() ), 'date_modified' => wc_rest_prepare_date_response( $object->get_date_modified(), false ), 'date_modified_gmt' => wc_rest_prepare_date_response( $object->get_date_modified() ), 'description' => wc_format_content( $object->get_description() ), 'permalink' => $object->get_permalink(), 'sku' => $object->get_sku(), 'price' => $object->get_price(), 'regular_price' => $object->get_regular_price(), 'sale_price' => $object->get_sale_price(), 'date_on_sale_from' => wc_rest_prepare_date_response( $object->get_date_on_sale_from(), false ), 'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_from() ), 'date_on_sale_to' => wc_rest_prepare_date_response( $object->get_date_on_sale_to(), false ), 'date_on_sale_to_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_to() ), 'on_sale' => $object->is_on_sale(), 'status' => $object->get_status(), 'purchasable' => $object->is_purchasable(), 'virtual' => $object->is_virtual(), 'downloadable' => $object->is_downloadable(), 'downloads' => $this->get_downloads( $object ), 'download_limit' => '' !== $object->get_download_limit() ? (int) $object->get_download_limit() : -1, 'download_expiry' => '' !== $object->get_download_expiry() ? (int) $object->get_download_expiry() : -1, 'tax_status' => $object->get_tax_status(), 'tax_class' => $object->get_tax_class(), 'manage_stock' => $object->managing_stock(), 'stock_quantity' => $object->get_stock_quantity(), 'stock_status' => $object->get_stock_status(), 'backorders' => $object->get_backorders(), 'backorders_allowed' => $object->backorders_allowed(), 'backordered' => $object->is_on_backorder(), 'weight' => $object->get_weight(), 'dimensions' => array( 'length' => $object->get_length(), 'width' => $object->get_width(), 'height' => $object->get_height(), ), 'shipping_class' => $object->get_shipping_class(), 'shipping_class_id' => $object->get_shipping_class_id(), 'image' => $this->get_image( $object ), 'attributes' => $this->get_attributes( $object ), 'menu_order' => $object->get_menu_order(), 'meta_data' => $object->get_meta_data(), ); $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 a single variation 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 ) { if ( isset( $request['id'] ) ) { $variation = wc_get_product( absint( $request['id'] ) ); } else { $variation = new WC_Product_Variation(); } $variation->set_parent_id( absint( $request['product_id'] ) ); // Status. if ( isset( $request['status'] ) ) { $variation->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' ); } // SKU. if ( isset( $request['sku'] ) ) { $variation->set_sku( wc_clean( $request['sku'] ) ); } // Thumbnail. if ( isset( $request['image'] ) ) { if ( is_array( $request['image'] ) ) { $variation = $this->set_variation_image( $variation, $request['image'] ); } else { $variation->set_image_id( '' ); } } // Virtual variation. if ( isset( $request['virtual'] ) ) { $variation->set_virtual( $request['virtual'] ); } // Downloadable variation. if ( isset( $request['downloadable'] ) ) { $variation->set_downloadable( $request['downloadable'] ); } // Downloads. if ( $variation->get_downloadable() ) { // Downloadable files. if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { $variation = $this->save_downloadable_files( $variation, $request['downloads'] ); } // Download limit. if ( isset( $request['download_limit'] ) ) { $variation->set_download_limit( $request['download_limit'] ); } // Download expiry. if ( isset( $request['download_expiry'] ) ) { $variation->set_download_expiry( $request['download_expiry'] ); } } // Shipping data. $variation = $this->save_product_shipping_data( $variation, $request ); // Stock handling. if ( isset( $request['manage_stock'] ) ) { $variation->set_manage_stock( $request['manage_stock'] ); } if ( isset( $request['stock_status'] ) ) { $variation->set_stock_status( $request['stock_status'] ); } if ( isset( $request['backorders'] ) ) { $variation->set_backorders( $request['backorders'] ); } if ( $variation->get_manage_stock() ) { if ( isset( $request['stock_quantity'] ) ) { $variation->set_stock_quantity( $request['stock_quantity'] ); } elseif ( isset( $request['inventory_delta'] ) ) { $stock_quantity = wc_stock_amount( $variation->get_stock_quantity() ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); $variation->set_stock_quantity( $stock_quantity ); } } else { $variation->set_backorders( 'no' ); $variation->set_stock_quantity( '' ); } // Regular Price. if ( isset( $request['regular_price'] ) ) { $variation->set_regular_price( $request['regular_price'] ); } // Sale Price. if ( isset( $request['sale_price'] ) ) { $variation->set_sale_price( $request['sale_price'] ); } if ( isset( $request['date_on_sale_from'] ) ) { $variation->set_date_on_sale_from( $request['date_on_sale_from'] ); } if ( isset( $request['date_on_sale_from_gmt'] ) ) { $variation->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null ); } if ( isset( $request['date_on_sale_to'] ) ) { $variation->set_date_on_sale_to( $request['date_on_sale_to'] ); } if ( isset( $request['date_on_sale_to_gmt'] ) ) { $variation->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null ); } // Tax class. if ( isset( $request['tax_class'] ) ) { $variation->set_tax_class( $request['tax_class'] ); } // Description. if ( isset( $request['description'] ) ) { $variation->set_description( wp_kses_post( $request['description'] ) ); } // Update taxonomies. if ( isset( $request['attributes'] ) ) { $attributes = array(); $parent = wc_get_product( $variation->get_parent_id() ); if ( ! $parent ) { return new WP_Error( // Translators: %d parent ID. "woocommerce_rest_{$this->post_type}_invalid_parent", sprintf( __( 'Cannot set attributes due to invalid parent product.', 'woocommerce' ), $variation->get_parent_id() ), array( 'status' => 404, ) ); } $parent_attributes = $parent->get_attributes(); foreach ( $request['attributes'] as $attribute ) { $attribute_id = 0; $attribute_name = ''; // Check ID for global attributes or name for product attributes. if ( ! empty( $attribute['id'] ) ) { $attribute_id = absint( $attribute['id'] ); $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); } elseif ( ! empty( $attribute['name'] ) ) { $attribute_name = sanitize_title( $attribute['name'] ); } if ( ! $attribute_id && ! $attribute_name ) { continue; } if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) { continue; } $attribute_key = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() ); $attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) { // If dealing with a taxonomy, we need to get the slug from the name posted to the API. $term = get_term_by( 'name', $attribute_value, $attribute_name ); if ( $term && ! is_wp_error( $term ) ) { $attribute_value = $term->slug; } else { $attribute_value = sanitize_title( $attribute_value ); } } $attributes[ $attribute_key ] = $attribute_value; } $variation->set_attributes( $attributes ); } // Menu order. if ( $request['menu_order'] ) { $variation->set_menu_order( $request['menu_order'] ); } // Meta data. if ( is_array( $request['meta_data'] ) ) { foreach ( $request['meta_data'] as $meta ) { $variation->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } } /** * 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 $variation 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", $variation, $request, $creating ); } /** * Get the image for a product variation. * * @param WC_Product_Variation $variation Variation data. * @return array */ protected function get_image( $variation ) { if ( ! $variation->get_image_id() ) { return; } $attachment_id = $variation->get_image_id(); $attachment_post = get_post( $attachment_id ); if ( is_null( $attachment_post ) ) { return; } $attachment = wp_get_attachment_image_src( $attachment_id, 'full' ); if ( ! is_array( $attachment ) ) { return; } if ( ! isset( $image ) ) { return array( 'id' => (int) $attachment_id, 'date_created' => wc_rest_prepare_date_response( $attachment_post->post_date, false ), 'date_created_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ), 'date_modified' => wc_rest_prepare_date_response( $attachment_post->post_modified, false ), 'date_modified_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_modified_gmt ) ), 'src' => current( $attachment ), 'name' => get_the_title( $attachment_id ), 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), ); } } /** * Set variation image. * * @throws WC_REST_Exception REST API exceptions. * @param WC_Product_Variation $variation Variation instance. * @param array $image Image data. * @return WC_Product_Variation */ protected function set_variation_image( $variation, $image ) { $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; if ( 0 === $attachment_id && isset( $image['src'] ) ) { $upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) ); if ( is_wp_error( $upload ) ) { if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $variation->get_id(), array( $image ) ) ) { throw new WC_REST_Exception( 'woocommerce_variation_image_upload_error', $upload->get_error_message(), 400 ); } } $attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $variation->get_id() ); } if ( ! wp_attachment_is_image( $attachment_id ) ) { /* translators: %s: attachment ID */ throw new WC_REST_Exception( 'woocommerce_variation_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 ); } $variation->set_image_id( $attachment_id ); // Set the image alt if present. if ( ! empty( $image['alt'] ) ) { update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) ); } // Set the image name if present. if ( ! empty( $image['name'] ) ) { wp_update_post( array( 'ID' => $attachment_id, 'post_title' => $image['name'], ) ); } return $variation; } /** * Get the Variation's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $weight_unit = get_option( 'woocommerce_weight_unit' ); $dimension_unit = get_option( 'woocommerce_dimension_unit' ); $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 variation was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_modified' => array( 'description' => __( "The date the variation was last modified, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'Variation description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'permalink' => array( 'description' => __( 'Variation URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'sku' => array( 'description' => __( 'Unique identifier.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'price' => array( 'description' => __( 'Current variation price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'regular_price' => array( 'description' => __( 'Variation regular price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'sale_price' => array( 'description' => __( 'Variation sale price.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from' => array( 'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_from_gmt' => array( 'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to' => array( 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'date_on_sale_to_gmt' => array( 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'on_sale' => array( 'description' => __( 'Shows if the variation is on sale.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'status' => array( 'description' => __( 'Variation status.', 'woocommerce' ), 'type' => 'string', 'default' => 'publish', 'enum' => array_keys( get_post_statuses() ), 'context' => array( 'view', 'edit' ), ), 'purchasable' => array( 'description' => __( 'Shows if the variation can be bought.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'virtual' => array( 'description' => __( 'If the variation is virtual.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloadable' => array( 'description' => __( 'If the variation is downloadable.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'downloads' => array( 'description' => __( 'List of downloadable files.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'File ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'File name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'file' => array( 'description' => __( 'File URL.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'download_limit' => array( 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'download_expiry' => array( 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ), 'type' => 'integer', 'default' => -1, 'context' => array( 'view', 'edit' ), ), 'tax_status' => array( 'description' => __( 'Tax status.', 'woocommerce' ), 'type' => 'string', 'default' => 'taxable', 'enum' => array( 'taxable', 'shipping', 'none' ), 'context' => array( 'view', 'edit' ), ), 'tax_class' => array( 'description' => __( 'Tax class.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'manage_stock' => array( 'description' => __( 'Stock management at variation level.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'context' => array( 'view', 'edit' ), ), 'stock_quantity' => array( 'description' => __( 'Stock quantity.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'stock_status' => array( 'description' => __( 'Controls the stock status of the product.', 'woocommerce' ), 'type' => 'string', 'default' => 'instock', 'enum' => array_keys( wc_get_product_stock_status_options() ), 'context' => array( 'view', 'edit' ), ), 'backorders' => array( 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ), 'type' => 'string', 'default' => 'no', 'enum' => array( 'no', 'notify', 'yes' ), 'context' => array( 'view', 'edit' ), ), 'backorders_allowed' => array( 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'backordered' => array( 'description' => __( 'Shows if the variation is on backordered.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'weight' => array( /* translators: %s: weight unit */ 'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), $weight_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'dimensions' => array( 'description' => __( 'Variation dimensions.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'length' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation length (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'width' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation width (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'height' => array( /* translators: %s: dimension unit */ 'description' => sprintf( __( 'Variation height (%s).', 'woocommerce' ), $dimension_unit ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'shipping_class' => array( 'description' => __( 'Shipping class slug.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'shipping_class_id' => array( 'description' => __( 'Shipping class ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'image' => array( 'description' => __( 'Variation 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_created_gmt' => array( 'description' => __( 'The date the image was created, as GMT.', '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, ), 'date_modified_gmt' => array( 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'src' => array( 'description' => __( 'Image URL.', 'woocommerce' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Image name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'alt' => array( 'description' => __( 'Image alternative text.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), 'attributes' => array( 'description' => __( 'List of attributes.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Attribute ID.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'name' => array( 'description' => __( 'Attribute name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'option' => array( 'description' => __( 'Selected attribute term name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), ), ), ), 'menu_order' => array( 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), '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 ); } /** * 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 = WC_REST_CRUD_Controller::prepare_objects_query( $request ); // Set post_status. $args['post_status'] = $request['status']; // Filter by sku. if ( ! empty( $request['sku'] ) ) { $skus = explode( ',', $request['sku'] ); // Include the current string as a SKU too. if ( 1 < count( $skus ) ) { $skus[] = $request['sku']; } $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_sku', 'value' => $skus, 'compare' => 'IN', ) ); } // Filter by tax class. if ( ! empty( $request['tax_class'] ) ) { $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_tax_class', 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', ) ); } // Price filter. if ( ! empty( $request['min_price'] ) || ! empty( $request['max_price'] ) ) { $args['meta_query'] = $this->add_meta_query( $args, wc_get_min_max_price_meta_query( $request ) ); // WPCS: slow query ok. } // Filter product based on stock_status. if ( ! empty( $request['stock_status'] ) ) { $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok. $args, array( 'key' => '_stock_status', 'value' => $request['stock_status'], ) ); } // Filter by on sale products. if ( is_bool( $request['on_sale'] ) ) { $on_sale_key = $request['on_sale'] ? 'post__in' : 'post__not_in'; $on_sale_ids = wc_get_product_ids_on_sale(); // Use 0 when there's no on sale products to avoid return all products. $on_sale_ids = empty( $on_sale_ids ) ? array( 0 ) : $on_sale_ids; $args[ $on_sale_key ] += $on_sale_ids; } // Force the post_type argument, since it's not a user input variable. if ( ! empty( $request['sku'] ) ) { $args['post_type'] = array( 'product', 'product_variation' ); } else { $args['post_type'] = $this->post_type; } $args['post_parent'] = $request['product_id']; return $args; } /** * Get the query params for collections of attachments. * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); unset( $params['in_stock'], $params['type'], $params['featured'], $params['category'], $params['tag'], $params['shipping_class'], $params['attribute'], $params['attribute_term'] ); $params['stock_status'] = array( 'description' => __( 'Limit result set to products with specified stock status.', 'woocommerce' ), 'type' => 'string', 'enum' => array_keys( wc_get_product_stock_status_options() ), 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); return $params; } } PKo5\//]woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-customer-downloads-controller.phpnu[/downloads endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Customers controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Customer_Downloads_V2_Controller */ class WC_REST_Customer_Downloads_Controller extends WC_REST_Customer_Downloads_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v3'; } PKo5\ N N `woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-report-reviews-totals-controller.phpnu[ true, 'post_type' => 'product', 'meta_key' => 'rating', // WPCS: slow query ok. 'meta_value' => '', // WPCS: slow query ok. ); for ( $i = 1; $i <= 5; $i++ ) { $query_data['meta_value'] = $i; $data[] = array( 'slug' => 'rated_' . $i . '_out_of_5', /* translators: %s: average rating */ 'name' => sprintf( __( 'Rated %s out of 5', 'woocommerce' ), $i ), 'total' => (int) get_comments( $query_data ), ); } return $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, 'name' => $report->name, 'total' => $report->total, ); $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_reviews_count', $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_review_total', 'type' => 'object', 'properties' => array( 'slug' => array( 'description' => __( 'An alphanumeric identifier for the resource.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'Review type name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Amount of reviews.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\Z))]woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-product-attributes-controller.phpnu[/locations endpoint. * * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Shipping Zone Locations class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Shipping_Zone_Locations_V2_Controller */ class WC_REST_Shipping_Zone_Locations_Controller extends WC_REST_Shipping_Zone_Locations_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v3'; } PKo5\} eePwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-terms-controller.phpnu[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( '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[\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 if a given request has access to read the terms. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function get_items_permissions_check( $request ) { $permissions = $this->check_permissions( $request, 'read' ); if ( is_wp_error( $permissions ) ) { return $permissions; } if ( ! $permissions ) { return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access to create a term. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function create_item_permissions_check( $request ) { $permissions = $this->check_permissions( $request, 'create' ); if ( is_wp_error( $permissions ) ) { return $permissions; } if ( ! $permissions ) { return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you 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 term. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function get_item_permissions_check( $request ) { $permissions = $this->check_permissions( $request, 'read' ); if ( is_wp_error( $permissions ) ) { return $permissions; } if ( ! $permissions ) { return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access to update a term. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function update_item_permissions_check( $request ) { $permissions = $this->check_permissions( $request, 'edit' ); if ( is_wp_error( $permissions ) ) { return $permissions; } if ( ! $permissions ) { return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access to delete a term. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function delete_item_permissions_check( $request ) { $permissions = $this->check_permissions( $request, 'delete' ); if ( is_wp_error( $permissions ) ) { return $permissions; } if ( ! $permissions ) { return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you 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 boolean|WP_Error */ public function batch_items_permissions_check( $request ) { $permissions = $this->check_permissions( $request, 'batch' ); if ( is_wp_error( $permissions ) ) { return $permissions; } if ( ! $permissions ) { 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; } /** * Check permissions. * * @param WP_REST_Request $request Full details about the request. * @param string $context Request context. * @return bool|WP_Error */ protected function check_permissions( $request, $context = 'read' ) { // Get taxonomy. $taxonomy = $this->get_taxonomy( $request ); if ( ! $taxonomy || ! taxonomy_exists( $taxonomy ) ) { return new WP_Error( 'woocommerce_rest_taxonomy_invalid', __( 'Taxonomy does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); } // Check permissions for a single term. $id = intval( $request['id'] ); if ( $id ) { $term = get_term( $id, $taxonomy ); if ( is_wp_error( $term ) || ! $term || $term->taxonomy !== $taxonomy ) { return new WP_Error( 'woocommerce_rest_term_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); } return wc_rest_check_product_term_permissions( $taxonomy, $context, $term->term_id ); } return wc_rest_check_product_term_permissions( $taxonomy, $context ); } /** * Get terms associated with a taxonomy. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error */ public function get_items( $request ) { $taxonomy = $this->get_taxonomy( $request ); $prepared_args = array( 'exclude' => $request['exclude'], 'include' => $request['include'], 'order' => $request['order'], 'orderby' => $request['orderby'], 'product' => $request['product'], 'hide_empty' => $request['hide_empty'], 'number' => $request['per_page'], 'search' => $request['search'], 'slug' => $request['slug'], ); if ( ! empty( $request['offset'] ) ) { $prepared_args['offset'] = $request['offset']; } else { $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; } $taxonomy_obj = get_taxonomy( $taxonomy ); if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) { if ( 0 === $request['parent'] ) { // Only query top-level terms. $prepared_args['parent'] = 0; } else { if ( $request['parent'] ) { $prepared_args['parent'] = $request['parent']; } } } /** * Filter the query arguments, before passing them to `get_terms()`. * * Enables adding extra arguments or setting defaults for a terms * collection request. * * @see https://developer.wordpress.org/reference/functions/get_terms/ * * @param array $prepared_args Array of arguments to be * passed to get_terms. * @param WP_REST_Request $request The current request. */ $prepared_args = apply_filters( "woocommerce_rest_{$taxonomy}_query", $prepared_args, $request ); if ( ! empty( $prepared_args['product'] ) ) { $query_result = $this->get_terms_for_product( $prepared_args, $request ); $total_terms = $this->total_terms; } else { $query_result = get_terms( $taxonomy, $prepared_args ); $count_args = $prepared_args; unset( $count_args['number'] ); unset( $count_args['offset'] ); $total_terms = wp_count_terms( $taxonomy, $count_args ); // Ensure we don't return results when offset is out of bounds. // See https://core.trac.wordpress.org/ticket/35935. if ( $prepared_args['offset'] && $prepared_args['offset'] >= $total_terms ) { $query_result = array(); } // wp_count_terms can return a falsy value when the term has no children. if ( ! $total_terms ) { $total_terms = 0; } } $response = array(); foreach ( $query_result as $term ) { $data = $this->prepare_item_for_response( $term, $request ); $response[] = $this->prepare_response_for_collection( $data ); } $response = rest_ensure_response( $response ); // Store pagination values for headers then unset for count query. $per_page = (int) $prepared_args['number']; $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); $response->header( 'X-WP-Total', (int) $total_terms ); $max_pages = ceil( $total_terms / $per_page ); $response->header( 'X-WP-TotalPages', (int) $max_pages ); $base = str_replace( '(?P[\d]+)', $request['attribute_id'], $this->rest_base ); $base = add_query_arg( $request->get_query_params(), rest_url( '/' . $this->namespace . '/' . $base ) ); if ( $page > 1 ) { $prev_page = $page - 1; if ( $prev_page > $max_pages ) { $prev_page = $max_pages; } $prev_link = add_query_arg( 'page', $prev_page, $base ); $response->link_header( 'prev', $prev_link ); } if ( $max_pages > $page ) { $next_page = $page + 1; $next_link = add_query_arg( 'page', $next_page, $base ); $response->link_header( 'next', $next_link ); } return $response; } /** * Create a single term for a taxonomy. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Request|WP_Error */ public function create_item( $request ) { $taxonomy = $this->get_taxonomy( $request ); $name = $request['name']; $args = array(); $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) { $args['description'] = $request['description']; } if ( isset( $request['slug'] ) ) { $args['slug'] = $request['slug']; } if ( isset( $request['parent'] ) ) { if ( ! is_taxonomy_hierarchical( $taxonomy ) ) { return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) ); } $args['parent'] = $request['parent']; } $term = wp_insert_term( $name, $taxonomy, $args ); if ( is_wp_error( $term ) ) { $error_data = array( 'status' => 400 ); // If we're going to inform the client that the term exists, // give them the identifier they can actually use. $term_id = $term->get_error_data( 'term_exists' ); if ( $term_id ) { $error_data['resource_id'] = $term_id; } return new WP_Error( $term->get_error_code(), $term->get_error_message(), $error_data ); } $term = get_term( $term['term_id'], $taxonomy ); $this->update_additional_fields_for_object( $term, $request ); // Add term data. $meta_fields = $this->update_term_meta_fields( $term, $request ); if ( is_wp_error( $meta_fields ) ) { wp_delete_term( $term->term_id, $taxonomy ); return $meta_fields; } /** * Fires after a single term is created or updated via the REST API. * * @param WP_Term $term Inserted Term object. * @param WP_REST_Request $request Request object. * @param boolean $creating True when creating term, false when updating. */ do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, true ); $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $term, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); $base = '/' . $this->namespace . '/' . $this->rest_base; if ( ! empty( $request['attribute_id'] ) ) { $base = str_replace( '(?P[\d]+)', (int) $request['attribute_id'], $base ); } $response->header( 'Location', rest_url( $base . '/' . $term->term_id ) ); return $response; } /** * Get a single term from a taxonomy. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Request|WP_Error */ public function get_item( $request ) { $taxonomy = $this->get_taxonomy( $request ); $term = get_term( (int) $request['id'], $taxonomy ); if ( is_wp_error( $term ) ) { return $term; } $response = $this->prepare_item_for_response( $term, $request ); return rest_ensure_response( $response ); } /** * Update a single term from a taxonomy. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Request|WP_Error */ public function update_item( $request ) { $taxonomy = $this->get_taxonomy( $request ); $term = get_term( (int) $request['id'], $taxonomy ); $schema = $this->get_item_schema(); $prepared_args = array(); if ( isset( $request['name'] ) ) { $prepared_args['name'] = $request['name']; } if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) { $prepared_args['description'] = $request['description']; } if ( isset( $request['slug'] ) ) { $prepared_args['slug'] = $request['slug']; } if ( isset( $request['parent'] ) ) { if ( ! is_taxonomy_hierarchical( $taxonomy ) ) { return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) ); } $prepared_args['parent'] = $request['parent']; } // Only update the term if we haz something to update. if ( ! empty( $prepared_args ) ) { $update = wp_update_term( $term->term_id, $term->taxonomy, $prepared_args ); if ( is_wp_error( $update ) ) { return $update; } } $term = get_term( (int) $request['id'], $taxonomy ); $this->update_additional_fields_for_object( $term, $request ); // Update term data. $meta_fields = $this->update_term_meta_fields( $term, $request ); if ( is_wp_error( $meta_fields ) ) { return $meta_fields; } /** * Fires after a single term is created or updated via the REST API. * * @param WP_Term $term Inserted Term object. * @param WP_REST_Request $request Request object. * @param boolean $creating True when creating term, false when updating. */ do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, false ); $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $term, $request ); return rest_ensure_response( $response ); } /** * Delete a single term from a taxonomy. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error */ public function delete_item( $request ) { $taxonomy = $this->get_taxonomy( $request ); $force = isset( $request['force'] ) ? (bool) $request['force'] : false; // We don't support trashing for this type, error out. if ( ! $force ) { return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) ); } $term = get_term( (int) $request['id'], $taxonomy ); // Get default category id. $default_category_id = absint( get_option( 'default_product_cat', 0 ) ); // Prevent deleting the default product category. if ( $default_category_id === (int) $request['id'] ) { return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Default product category cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) ); } $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $term, $request ); $retval = wp_delete_term( $term->term_id, $term->taxonomy ); if ( ! $retval ) { return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) ); } /** * Fires after a single term is deleted via the REST API. * * @param WP_Term $term The deleted term. * @param WP_REST_Response $response The response data. * @param WP_REST_Request $request The request sent to the API. */ do_action( "woocommerce_rest_delete_{$taxonomy}", $term, $response, $request ); return $response; } /** * Prepare links for the request. * * @param object $term Term object. * @param WP_REST_Request $request Full details about the request. * @return array Links for the given term. */ protected function prepare_links( $term, $request ) { $base = '/' . $this->namespace . '/' . $this->rest_base; if ( ! empty( $request['attribute_id'] ) ) { $base = str_replace( '(?P[\d]+)', (int) $request['attribute_id'], $base ); } $links = array( 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $term->term_id ), ), 'collection' => array( 'href' => rest_url( $base ), ), ); if ( $term->parent ) { $parent_term = get_term( (int) $term->parent, $term->taxonomy ); if ( $parent_term ) { $links['up'] = array( 'href' => rest_url( trailingslashit( $base ) . $parent_term->term_id ), ); } } return $links; } /** * Update term meta fields. * * @param WP_Term $term Term object. * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error */ protected function update_term_meta_fields( $term, $request ) { return true; } /** * Get the terms attached to a product. * * This is an alternative to `get_terms()` that uses `get_the_terms()` * instead, which hits the object cache. There are a few things not * supported, notably `include`, `exclude`. In `self::get_items()` these * are instead treated as a full query. * * @param array $prepared_args Arguments for `get_terms()`. * @param WP_REST_Request $request Full details about the request. * @return array List of term objects. (Total count in `$this->total_terms`). */ protected function get_terms_for_product( $prepared_args, $request ) { $taxonomy = $this->get_taxonomy( $request ); $query_result = get_the_terms( $prepared_args['product'], $taxonomy ); if ( empty( $query_result ) ) { $this->total_terms = 0; return array(); } // get_items() verifies that we don't have `include` set, and default. // ordering is by `name`. if ( ! in_array( $prepared_args['orderby'], array( 'name', 'none', 'include' ), true ) ) { switch ( $prepared_args['orderby'] ) { case 'id': $this->sort_column = 'term_id'; break; case 'slug': case 'term_group': case 'description': case 'count': $this->sort_column = $prepared_args['orderby']; break; } usort( $query_result, array( $this, 'compare_terms' ) ); } if ( strtolower( $prepared_args['order'] ) !== 'asc' ) { $query_result = array_reverse( $query_result ); } // Pagination. $this->total_terms = count( $query_result ); $query_result = array_slice( $query_result, $prepared_args['offset'], $prepared_args['number'] ); return $query_result; } /** * Comparison function for sorting terms by a column. * * Uses `$this->sort_column` to determine field to sort by. * * @param stdClass $left Term object. * @param stdClass $right Term object. * @return int <0 if left is higher "priority" than right, 0 if equal, >0 if right is higher "priority" than left. */ protected function compare_terms( $left, $right ) { $col = $this->sort_column; $left_val = $left->$col; $right_val = $right->$col; if ( is_int( $left_val ) && is_int( $right_val ) ) { return $left_val - $right_val; } return strcmp( $left_val, $right_val ); } /** * Get the query params for collections * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); if ( '' !== $this->taxonomy && taxonomy_exists( $this->taxonomy ) ) { $taxonomy = get_taxonomy( $this->taxonomy ); } else { $taxonomy = new stdClass(); $taxonomy->hierarchical = true; } $params['context']['default'] = 'view'; $params['exclude'] = array( 'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ), 'type' => 'array', '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', ); if ( ! $taxonomy->hierarchical ) { $params['offset'] = array( 'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ), 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ); } $params['order'] = array( 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_key', 'default' => 'asc', 'enum' => array( 'asc', 'desc', ), 'validate_callback' => 'rest_validate_request_arg', ); $params['orderby'] = array( 'description' => __( 'Sort collection by resource attribute.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_key', 'default' => 'name', 'enum' => array( 'id', 'include', 'name', 'slug', 'term_group', 'description', 'count', ), 'validate_callback' => 'rest_validate_request_arg', ); $params['hide_empty'] = array( 'description' => __( 'Whether to hide resources not assigned to any products.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'validate_callback' => 'rest_validate_request_arg', ); if ( $taxonomy->hierarchical ) { $params['parent'] = array( 'description' => __( 'Limit result set to resources assigned to a specific parent.', 'woocommerce' ), 'type' => 'integer', 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ); } $params['product'] = array( 'description' => __( 'Limit result set to resources assigned to a specific product.', 'woocommerce' ), 'type' => 'integer', 'default' => null, 'validate_callback' => 'rest_validate_request_arg', ); $params['slug'] = array( 'description' => __( 'Limit result set to resources with a specific slug.', 'woocommerce' ), 'type' => 'string', 'validate_callback' => 'rest_validate_request_arg', ); return $params; } /** * Get taxonomy. * * @param WP_REST_Request $request Full details about the request. * @return int|WP_Error */ protected function get_taxonomy( $request ) { // Check if taxonomy is defined. // Prevents check for attribute taxonomy more than one time for each query. if ( '' !== $this->taxonomy ) { return $this->taxonomy; } if ( ! empty( $request['attribute_id'] ) ) { $taxonomy = wc_attribute_taxonomy_name_by_id( (int) $request['attribute_id'] ); $this->taxonomy = $taxonomy; } return $this->taxonomy; } } PKo5\W%Zwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-product-reviews-controller.phpnu[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( 'product_id' => array( 'required' => true, 'description' => __( 'Unique identifier for the product.', 'woocommerce' ), 'type' => 'integer', ), 'review' => array( 'required' => true, 'type' => 'string', 'description' => __( 'Review content.', 'woocommerce' ), ), 'reviewer' => array( 'required' => true, 'type' => 'string', 'description' => __( 'Name of the reviewer.', 'woocommerce' ), ), 'reviewer_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[\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' ), ) ); } /** * 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_product_reviews_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 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 ) { $id = (int) $request['id']; $review = get_comment( $id ); if ( $review && ! wc_rest_check_product_reviews_permissions( 'read', $review->comment_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 ) { if ( ! wc_rest_check_product_reviews_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 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 ) { $id = (int) $request['id']; $review = get_comment( $id ); if ( $review && ! wc_rest_check_product_reviews_permissions( 'edit', $review->comment_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 ) { $id = (int) $request['id']; $review = get_comment( $id ); if ( $review && ! wc_rest_check_product_reviews_permissions( 'delete', $review->comment_ID ) ) { return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you cannot 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 boolean|WP_Error */ public function batch_items_permissions_check( $request ) { if ( ! wc_rest_check_product_reviews_permissions( 'create' ) ) { 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 reviews. * * @param WP_REST_Request $request Full details about the request. * @return array|WP_Error */ public function get_items( $request ) { // Retrieve the list of registered collection query parameters. $registered = $this->get_collection_params(); /* * This array defines mappings between public API query parameters whose * values are accepted as-passed, and their internal WP_Query parameter * name equivalents (some are the same). Only values which are also * present in $registered will be set. */ $parameter_mappings = array( 'reviewer' => 'author__in', 'reviewer_email' => 'author_email', 'reviewer_exclude' => 'author__not_in', 'exclude' => 'comment__not_in', 'include' => 'comment__in', 'offset' => 'offset', 'order' => 'order', 'per_page' => 'number', 'product' => 'post__in', 'search' => 'search', 'status' => 'status', ); $prepared_args = array(); /* * For each known parameter which is both registered and present in the request, * set the parameter's value on the query $prepared_args. */ foreach ( $parameter_mappings as $api_param => $wp_param ) { if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { $prepared_args[ $wp_param ] = $request[ $api_param ]; } } // Ensure certain parameter values default to empty strings. foreach ( array( 'author_email', 'search' ) as $param ) { if ( ! isset( $prepared_args[ $param ] ) ) { $prepared_args[ $param ] = ''; } } if ( isset( $registered['orderby'] ) ) { $prepared_args['orderby'] = $this->normalize_query_param( $request['orderby'] ); } if ( isset( $prepared_args['status'] ) ) { $prepared_args['status'] = 'approved' === $prepared_args['status'] ? 'approve' : $prepared_args['status']; } $prepared_args['no_found_rows'] = false; $prepared_args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. if ( isset( $registered['before'], $request['before'] ) ) { $prepared_args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. if ( isset( $registered['after'], $request['after'] ) ) { $prepared_args['date_query'][0]['after'] = $request['after']; } if ( isset( $registered['page'] ) && empty( $request['offset'] ) ) { $prepared_args['offset'] = $prepared_args['number'] * ( absint( $request['page'] ) - 1 ); } /** * Filters arguments, before passing to WP_Comment_Query, when querying reviews via the REST API. * * @since 3.5.0 * @link https://developer.wordpress.org/reference/classes/wp_comment_query/ * @param array $prepared_args Array of arguments for WP_Comment_Query. * @param WP_REST_Request $request The current request. */ $prepared_args = apply_filters( 'woocommerce_rest_product_review_query', $prepared_args, $request ); // Make sure that returns only reviews. $prepared_args['type'] = 'review'; // Query reviews. $query = new WP_Comment_Query(); $query_result = $query->query( $prepared_args ); $reviews = array(); foreach ( $query_result as $review ) { if ( ! wc_rest_check_product_reviews_permissions( 'read', $review->comment_ID ) ) { continue; } $data = $this->prepare_item_for_response( $review, $request ); $reviews[] = $this->prepare_response_for_collection( $data ); } $total_reviews = (int) $query->found_comments; $max_pages = (int) $query->max_num_pages; if ( $total_reviews < 1 ) { // Out-of-bounds, run the query again without LIMIT for total count. unset( $prepared_args['number'], $prepared_args['offset'] ); $query = new WP_Comment_Query(); $prepared_args['count'] = true; $total_reviews = $query->query( $prepared_args ); $max_pages = ceil( $total_reviews / $request['per_page'] ); } $response = rest_ensure_response( $reviews ); $response->header( 'X-WP-Total', $total_reviews ); $response->header( 'X-WP-TotalPages', $max_pages ); $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); if ( $request['page'] > 1 ) { $prev_page = $request['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 > $request['page'] ) { $next_page = $request['page'] + 1; $next_link = add_query_arg( 'page', $next_page, $base ); $response->link_header( 'next', $next_link ); } return $response; } /** * Create a single review. * * @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_review_exists', __( 'Cannot create existing product review.', 'woocommerce' ), array( 'status' => 400 ) ); } $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 ); if ( is_wp_error( $prepared_review ) ) { return $prepared_review; } $prepared_review['comment_type'] = 'review'; /* * Do not allow a comment to be created with missing or empty comment_content. See wp_handle_comment_submission(). */ if ( empty( $prepared_review['comment_content'] ) ) { return new WP_Error( 'woocommerce_rest_review_content_invalid', __( 'Invalid review content.', 'woocommerce' ), array( 'status' => 400 ) ); } // Setting remaining values before wp_insert_comment so we can use wp_allow_comment(). if ( ! isset( $prepared_review['comment_date_gmt'] ) ) { $prepared_review['comment_date_gmt'] = current_time( 'mysql', true ); } if ( ! empty( $_SERVER['REMOTE_ADDR'] ) && rest_is_ip_address( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) ) { // WPCS: input var ok, sanitization ok. $prepared_review['comment_author_IP'] = wc_clean( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); // WPCS: input var ok. } else { $prepared_review['comment_author_IP'] = '127.0.0.1'; } if ( ! empty( $request['author_user_agent'] ) ) { $prepared_review['comment_agent'] = $request['author_user_agent']; } elseif ( $request->get_header( 'user_agent' ) ) { $prepared_review['comment_agent'] = $request->get_header( 'user_agent' ); } else { $prepared_review['comment_agent'] = ''; } $check_comment_lengths = wp_check_comment_data_max_lengths( $prepared_review ); if ( is_wp_error( $check_comment_lengths ) ) { $error_code = str_replace( array( 'comment_author', 'comment_content' ), array( 'reviewer', 'review_content' ), $check_comment_lengths->get_error_code() ); return new WP_Error( 'woocommerce_rest_' . $error_code, __( 'Product review field exceeds maximum length allowed.', 'woocommerce' ), array( 'status' => 400 ) ); } $prepared_review['comment_parent'] = 0; $prepared_review['comment_author_url'] = ''; $prepared_review['comment_approved'] = wp_allow_comment( $prepared_review, true ); if ( is_wp_error( $prepared_review['comment_approved'] ) ) { $error_code = $prepared_review['comment_approved']->get_error_code(); $error_message = $prepared_review['comment_approved']->get_error_message(); if ( 'comment_duplicate' === $error_code ) { return new WP_Error( 'woocommerce_rest_' . $error_code, $error_message, array( 'status' => 409 ) ); } if ( 'comment_flood' === $error_code ) { return new WP_Error( 'woocommerce_rest_' . $error_code, $error_message, array( 'status' => 400 ) ); } return $prepared_review['comment_approved']; } /** * Filters a review before it is inserted via the REST API. * * Allows modification of the review right before it is inserted via wp_insert_comment(). * Returning a WP_Error value from the filter will shortcircuit insertion and allow * skipping further processing. * * @since 3.5.0 * @param array|WP_Error $prepared_review The prepared review data for wp_insert_comment(). * @param WP_REST_Request $request Request used to insert the review. */ $prepared_review = apply_filters( 'woocommerce_rest_pre_insert_product_review', $prepared_review, $request ); if ( is_wp_error( $prepared_review ) ) { return $prepared_review; } $review_id = wp_insert_comment( wp_filter_comment( wp_slash( (array) $prepared_review ) ) ); if ( ! $review_id ) { return new WP_Error( 'woocommerce_rest_review_failed_create', __( 'Creating product review failed.', 'woocommerce' ), array( 'status' => 500 ) ); } if ( isset( $request['status'] ) ) { $this->handle_status_param( $request['status'], $review_id ); } update_comment_meta( $review_id, 'rating', ! empty( $request['rating'] ) ? $request['rating'] : '0' ); $review = get_comment( $review_id ); /** * Fires after a comment is created or updated via the REST API. * * @param WP_Comment $review Inserted or updated comment object. * @param WP_REST_Request $request Request object. * @param bool $creating True when creating a comment, false when updating. */ do_action( 'woocommerce_rest_insert_product_review', $review, $request, true ); $fields_update = $this->update_additional_fields_for_object( $review, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $context = current_user_can( 'moderate_comments' ) ? 'edit' : 'view'; $request->set_param( 'context', $context ); $response = $this->prepare_item_for_response( $review, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $review_id ) ) ); return $response; } /** * 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 ) { $review = $this->get_review( $request['id'] ); if ( is_wp_error( $review ) ) { return $review; } $data = $this->prepare_item_for_response( $review, $request ); $response = rest_ensure_response( $data ); return $response; } /** * Updates a review. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response Response object on success, or error object on failure. */ public function update_item( $request ) { $review = $this->get_review( $request['id'] ); if ( is_wp_error( $review ) ) { return $review; } $id = (int) $review->comment_ID; if ( isset( $request['type'] ) && 'review' !== get_comment_type( $id ) ) { return new WP_Error( 'woocommerce_rest_review_invalid_type', __( 'Sorry, you are not allowed to change the comment type.', 'woocommerce' ), array( 'status' => 404 ) ); } $prepared_args = $this->prepare_item_for_database( $request ); if ( is_wp_error( $prepared_args ) ) { return $prepared_args; } if ( ! empty( $prepared_args['comment_post_ID'] ) ) { if ( 'product' !== get_post_type( (int) $prepared_args['comment_post_ID'] ) ) { return new WP_Error( 'woocommerce_rest_product_invalid_id', __( 'Invalid product ID.', 'woocommerce' ), array( 'status' => 404 ) ); } } if ( empty( $prepared_args ) && isset( $request['status'] ) ) { // Only the comment status is being changed. $change = $this->handle_status_param( $request['status'], $id ); if ( ! $change ) { return new WP_Error( 'woocommerce_rest_review_failed_edit', __( 'Updating review status failed.', 'woocommerce' ), array( 'status' => 500 ) ); } } elseif ( ! empty( $prepared_args ) ) { if ( is_wp_error( $prepared_args ) ) { return $prepared_args; } if ( isset( $prepared_args['comment_content'] ) && empty( $prepared_args['comment_content'] ) ) { return new WP_Error( 'woocommerce_rest_review_content_invalid', __( 'Invalid review content.', 'woocommerce' ), array( 'status' => 400 ) ); } $prepared_args['comment_ID'] = $id; $check_comment_lengths = wp_check_comment_data_max_lengths( $prepared_args ); if ( is_wp_error( $check_comment_lengths ) ) { $error_code = str_replace( array( 'comment_author', 'comment_content' ), array( 'reviewer', 'review_content' ), $check_comment_lengths->get_error_code() ); return new WP_Error( 'woocommerce_rest_' . $error_code, __( 'Product review field exceeds maximum length allowed.', 'woocommerce' ), array( 'status' => 400 ) ); } $updated = wp_update_comment( wp_slash( (array) $prepared_args ) ); if ( false === $updated ) { return new WP_Error( 'woocommerce_rest_comment_failed_edit', __( 'Updating review failed.', 'woocommerce' ), array( 'status' => 500 ) ); } if ( isset( $request['status'] ) ) { $this->handle_status_param( $request['status'], $id ); } } if ( ! empty( $request['rating'] ) ) { update_comment_meta( $id, 'rating', $request['rating'] ); } $review = get_comment( $id ); /** This action is documented in includes/api/class-wc-rest-product-reviews-controller.php */ do_action( 'woocommerce_rest_insert_product_review', $review, $request, false ); $fields_update = $this->update_additional_fields_for_object( $review, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $review, $request ); return rest_ensure_response( $response ); } /** * Deletes a review. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response Response object on success, or error object on failure. */ public function delete_item( $request ) { $review = $this->get_review( $request['id'] ); if ( is_wp_error( $review ) ) { return $review; } $force = isset( $request['force'] ) ? (bool) $request['force'] : false; /** * Filters whether a review can be trashed. * * Return false to disable trash support for the post. * * @since 3.5.0 * @param bool $supports_trash Whether the post type support trashing. * @param WP_Comment $review The review object being considered for trashing support. */ $supports_trash = apply_filters( 'woocommerce_rest_product_review_trashable', ( EMPTY_TRASH_DAYS > 0 ), $review ); $request->set_param( 'context', 'edit' ); if ( $force ) { $previous = $this->prepare_item_for_response( $review, $request ); $result = wp_delete_comment( $review->comment_ID, true ); $response = new WP_REST_Response(); $response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data(), ) ); } else { // If this type doesn't support trashing, error out. if ( ! $supports_trash ) { /* translators: %s: force=true */ return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( "The object does not support trashing. Set '%s' to delete.", 'woocommerce' ), 'force=true' ), array( 'status' => 501 ) ); } if ( 'trash' === $review->comment_approved ) { return new WP_Error( 'woocommerce_rest_already_trashed', __( 'The object has already been trashed.', 'woocommerce' ), array( 'status' => 410 ) ); } $result = wp_trash_comment( $review->comment_ID ); $review = get_comment( $review->comment_ID ); $response = $this->prepare_item_for_response( $review, $request ); } if ( ! $result ) { return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The object cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) ); } /** * Fires after a review is deleted via the REST API. * * @param WP_Comment $review The deleted review 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_review', $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 ) { $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $fields = $this->get_fields_for_response( $request ); $data = array(); if ( in_array( 'id', $fields, true ) ) { $data['id'] = (int) $review->comment_ID; } if ( in_array( 'date_created', $fields, true ) ) { $data['date_created'] = wc_rest_prepare_date_response( $review->comment_date ); } if ( in_array( 'date_created_gmt', $fields, true ) ) { $data['date_created_gmt'] = wc_rest_prepare_date_response( $review->comment_date_gmt ); } if ( in_array( 'product_id', $fields, true ) ) { $data['product_id'] = (int) $review->comment_post_ID; } if ( in_array( 'status', $fields, true ) ) { $data['status'] = $this->prepare_status_response( (string) $review->comment_approved ); } if ( in_array( 'reviewer', $fields, true ) ) { $data['reviewer'] = $review->comment_author; } if ( in_array( 'reviewer_email', $fields, true ) ) { $data['reviewer_email'] = $review->comment_author_email; } if ( in_array( 'review', $fields, true ) ) { $data['review'] = 'view' === $context ? wpautop( $review->comment_content ) : $review->comment_content; } if ( in_array( 'rating', $fields, true ) ) { $data['rating'] = (int) get_comment_meta( $review->comment_ID, 'rating', true ); } if ( in_array( 'verified', $fields, true ) ) { $data['verified'] = wc_review_is_from_verified_owner( $review->comment_ID ); } if ( in_array( 'reviewer_avatar_urls', $fields, true ) ) { $data['reviewer_avatar_urls'] = rest_get_avatar_urls( $review->comment_author_email ); } $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 ) ); /** * 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 ) { 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['reviewer'] ) ) { $prepared_review['comment_author'] = $request['reviewer']; } if ( isset( $request['reviewer_email'] ) ) { $prepared_review['comment_author_email'] = $request['reviewer_email']; } if ( ! empty( $request['date_created'] ) ) { $date_data = rest_get_date_with_gmt( $request['date_created'] ); if ( ! empty( $date_data ) ) { list( $prepared_review['comment_date'], $prepared_review['comment_date_gmt'] ) = $date_data; } } elseif ( ! empty( $request['date_created_gmt'] ) ) { $date_data = rest_get_date_with_gmt( $request['date_created_gmt'], true ); if ( ! empty( $date_data ) ) { list( $prepared_review['comment_date'], $prepared_review['comment_date_gmt'] ) = $date_data; } } /** * Filters a review after it is prepared for the database. * * Allows modification of the review right after it is prepared for the database. * * @since 3.5.0 * @param array $prepared_review The prepared review data for `wp_insert_comment`. * @param WP_REST_Request $request The current request. */ return apply_filters( 'woocommerce_rest_preprocess_product_review', $prepared_review, $request ); } /** * 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 ) ), ); } if ( 0 !== (int) $review->user_id ) { $links['reviewer'] = array( 'href' => rest_url( 'wp/v2/users/' . $review->user_id ), 'embeddable' => true, ); } 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, ), 'date_created' => array( 'description' => __( "The date the review was created, in the site's timezone.", 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'date_created_gmt' => array( 'description' => __( 'The date the review was created, as GMT.', 'woocommerce' ), 'type' => 'date-time', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'product_id' => array( 'description' => __( 'Unique identifier for the product that the review belongs to.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ), 'status' => array( 'description' => __( 'Status of the review.', 'woocommerce' ), 'type' => 'string', 'default' => 'approved', 'enum' => array( 'approved', 'hold', 'spam', 'unspam', 'trash', 'untrash' ), 'context' => array( 'view', 'edit' ), ), 'reviewer' => array( 'description' => __( 'Reviewer name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'reviewer_email' => array( 'description' => __( 'Reviewer email.', 'woocommerce' ), 'type' => 'string', 'format' => 'email', 'context' => array( 'view', 'edit' ), ), 'review' => array( 'description' => __( 'The content of the review.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'wp_filter_post_kses', ), ), 'rating' => array( 'description' => __( 'Review rating (0 to 5).', 'woocommerce' ), 'type' => 'integer', '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, ), ), ); if ( get_option( 'show_avatars' ) ) { $avatar_properties = array(); $avatar_sizes = rest_get_avatar_sizes(); foreach ( $avatar_sizes as $size ) { $avatar_properties[ $size ] = array( /* translators: %d: avatar image size in pixels */ 'description' => sprintf( __( 'Avatar URL with image size of %d pixels.', 'woocommerce' ), $size ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'embed', 'view', 'edit' ), ); } $schema['properties']['reviewer_avatar_urls'] = array( 'description' => __( 'Avatar URLs for the object reviewer.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'readonly' => true, 'properties' => $avatar_properties, ); } return $this->add_additional_fields_schema( $schema ); } /** * Get the query params for collections. * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); $params['context']['default'] = 'view'; $params['after'] = array( 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), 'type' => 'string', 'format' => 'date-time', ); $params['before'] = array( 'description' => __( 'Limit response to reviews published before a given ISO8601 compliant date.', 'woocommerce' ), 'type' => 'string', 'format' => 'date-time', ); $params['exclude'] = array( 'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'default' => array(), ); $params['include'] = array( 'description' => __( 'Limit result set to specific IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'default' => array(), ); $params['offset'] = array( 'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ), 'type' => 'integer', ); $params['order'] = array( 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), 'type' => 'string', 'default' => 'desc', 'enum' => array( 'asc', 'desc', ), ); $params['orderby'] = array( 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), 'type' => 'string', 'default' => 'date_gmt', 'enum' => array( 'date', 'date_gmt', 'id', 'include', 'product', ), ); $params['reviewer'] = array( 'description' => __( 'Limit result set to reviews assigned to specific user IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), ); $params['reviewer_exclude'] = array( 'description' => __( 'Ensure result set excludes reviews assigned to specific user IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), ); $params['reviewer_email'] = array( 'default' => null, 'description' => __( 'Limit result set to that from a specific author email.', 'woocommerce' ), 'format' => 'email', 'type' => 'string', ); $params['product'] = array( 'default' => array(), 'description' => __( 'Limit result set to reviews assigned to specific product IDs.', 'woocommerce' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), ); $params['status'] = array( 'default' => 'approved', 'description' => __( 'Limit result set to reviews assigned a specific status.', 'woocommerce' ), 'sanitize_callback' => 'sanitize_key', 'type' => 'string', 'enum' => array( 'all', 'hold', 'approved', 'spam', 'trash', ), ); /** * Filter collection parameters for the reviews controller. * * This filter registers the collection parameter, but does not map the * collection parameter to an internal WP_Comment_Query parameter. Use the * `wc_rest_review_query` filter to set WP_Comment_Query parameters. * * @since 3.5.0 * @param array $params JSON Schema-formatted collection parameters. */ return apply_filters( 'woocommerce_rest_product_review_collection_params', $params ); } /** * Get the reivew, if the ID is valid. * * @since 3.5.0 * @param int $id Supplied ID. * @return WP_Comment|WP_Error Comment object if ID is valid, WP_Error otherwise. */ protected function get_review( $id ) { $id = (int) $id; $error = new WP_Error( 'woocommerce_rest_review_invalid_id', __( 'Invalid review ID.', 'woocommerce' ), array( 'status' => 404 ) ); if ( 0 >= $id ) { return $error; } $review = get_comment( $id ); if ( empty( $review ) ) { return $error; } if ( ! empty( $review->comment_post_ID ) ) { $post = get_post( (int) $review->comment_post_ID ); if ( 'product' !== get_post_type( (int) $review->comment_post_ID ) ) { return new WP_Error( 'woocommerce_rest_product_invalid_id', __( 'Invalid product ID.', 'woocommerce' ), array( 'status' => 404 ) ); } } return $review; } /** * Prepends internal property prefix to query parameters to match our response fields. * * @since 3.5.0 * @param string $query_param Query parameter. * @return string */ protected function normalize_query_param( $query_param ) { $prefix = 'comment_'; switch ( $query_param ) { case 'id': $normalized = $prefix . 'ID'; break; case 'product': $normalized = $prefix . 'post_ID'; break; case 'include': $normalized = 'comment__in'; break; default: $normalized = $prefix . $query_param; break; } return $normalized; } /** * Checks comment_approved to set comment status for single comment output. * * @since 3.5.0 * @param string|int $comment_approved comment status. * @return string Comment status. */ protected function prepare_status_response( $comment_approved ) { switch ( $comment_approved ) { case 'hold': case '0': $status = 'hold'; break; case 'approve': case '1': $status = 'approved'; break; case 'spam': case 'trash': default: $status = $comment_approved; break; } return $status; } /** * Sets the comment_status of a given review object when creating or updating a review. * * @since 3.5.0 * @param string|int $new_status New review status. * @param int $id Review ID. * @return bool Whether the status was changed. */ protected function handle_status_param( $new_status, $id ) { $old_status = wp_get_comment_status( $id ); if ( $new_status === $old_status ) { return false; } switch ( $new_status ) { case 'approved': case 'approve': case '1': $changed = wp_set_comment_status( $id, 'approve' ); break; case 'hold': case '0': $changed = wp_set_comment_status( $id, 'hold' ); break; case 'spam': $changed = wp_spam_comment( $id ); break; case 'unspam': $changed = wp_unspam_comment( $id ); break; case 'trash': $changed = wp_trash_comment( $id ); break; case 'untrash': $changed = wp_untrash_comment( $id ); break; default: $changed = false; break; } return $changed; } } PKo5\'^woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-shipping-zones-controller-base.phpnu[ 404 ) ); } return $zone; } /** * Check whether a given request has permission to read Shipping Zones. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function get_items_permissions_check( $request ) { if ( ! wc_shipping_enabled() ) { return new WP_Error( 'rest_no_route', __( 'Shipping is disabled.', 'woocommerce' ), array( 'status' => 404 ) ); } 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 to create Shipping Zones. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function create_item_permissions_check( $request ) { if ( ! wc_shipping_enabled() ) { return new WP_Error( 'rest_no_route', __( 'Shipping is disabled.', 'woocommerce' ), array( 'status' => 404 ) ); } if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) { 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 whether a given request has permission to edit Shipping Zones. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function update_items_permissions_check( $request ) { if ( ! wc_shipping_enabled() ) { return new WP_Error( 'rest_no_route', __( 'Shipping is disabled.', 'woocommerce' ), array( 'status' => 404 ) ); } 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 whether a given request has permission to delete Shipping Zones. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function delete_items_permissions_check( $request ) { if ( ! wc_shipping_enabled() ) { return new WP_Error( 'rest_no_route', __( 'Shipping is disabled.', 'woocommerce' ), array( 'status' => 404 ) ); } if ( ! wc_rest_check_manager_permissions( 'settings', 'delete' ) ) { return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } } PKo5\J_<Swoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-webhooks-controller.phpnu[/refunds endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Order Refunds controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Order_Refunds_V2_Controller */ class WC_REST_Order_Refunds_Controller extends WC_REST_Order_Refunds_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v3'; /** * Prepares one object for create or update operation. * * @since 3.0.0 * @param WP_REST_Request $request Request object. * @param bool $creating If is creating a new object. * @return WP_Error|WC_Data The prepared item, or WP_Error object on failure. */ protected function prepare_object_for_database( $request, $creating = false ) { $order = wc_get_order( (int) $request['order_id'] ); if ( ! $order ) { return new WP_Error( 'woocommerce_rest_invalid_order_id', __( 'Invalid order ID.', 'woocommerce' ), 404 ); } 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->get_id(), 'amount' => $request['amount'], 'reason' => empty( $request['reason'] ) ? null : $request['reason'], 'line_items' => empty( $request['line_items'] ) ? array() : $request['line_items'], '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 ); } if ( ! empty( $request['meta_data'] ) && is_array( $request['meta_data'] ) ) { foreach ( $request['meta_data'] as $meta ) { $refund->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); } $refund->save_meta_data(); } /** * 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", $refund, $request, $creating ); } } PKo5\MJ66`woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-shipping-zone-methods-controller.phpnu[/methods endpoint. * * @package Automattic/WooCommerce/RestApi * @since 3.0.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Shipping Zone Methods class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Shipping_Zone_Methods_V2_Controller */ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zone_Methods_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v3'; } PKo5\NN5Pwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-taxes-controller.phpnu[ 405 ) ); } /** * Check if a given request has access to read an item. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function get_item_permissions_check( $request ) { $object = $this->get_object( (int) $request['id'] ); if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'read', $object->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 to update an item. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function update_item_permissions_check( $request ) { $object = $this->get_object( (int) $request['id'] ); if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'edit', $object->get_id() ) ) { return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access to delete an item. * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error */ public function delete_item_permissions_check( $request ) { $object = $this->get_object( (int) $request['id'] ); if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->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 object permalink. * * @param object $object Object. * @return string */ protected function get_permalink( $object ) { return ''; } /** * Prepares the object for the REST response. * * @since 3.0.0 * @param WC_Data $object Object data. * @param WP_REST_Request $request Request object. * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. */ protected function prepare_object_for_response( $object, $request ) { // translators: %s: Class method name. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); } /** * Prepares one object for create or update operation. * * @since 3.0.0 * @param WP_REST_Request $request Request object. * @param bool $creating If is creating a new object. * @return WP_Error|WC_Data The prepared item, or WP_Error object on failure. */ protected function prepare_object_for_database( $request, $creating = false ) { // translators: %s: Class method name. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); } /** * 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 ) { $object = $this->get_object( (int) $request['id'] ); if ( ! $object || 0 === $object->get_id() ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); } $data = $this->prepare_object_for_response( $object, $request ); $response = rest_ensure_response( $data ); if ( $this->public ) { $response->link_header( 'alternate', $this->get_permalink( $object ), array( 'type' => 'text/html' ) ); } return $response; } /** * Save an object data. * * @since 3.0.0 * @param WP_REST_Request $request Full details about the request. * @param bool $creating If is creating a new object. * @return WC_Data|WP_Error */ protected function save_object( $request, $creating = false ) { try { $object = $this->prepare_object_for_database( $request, $creating ); if ( is_wp_error( $object ) ) { return $object; } $object->save(); return $this->get_object( $object->get_id() ); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } } /** * 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 ) ); } $object = $this->save_object( $request, true ); if ( is_wp_error( $object ) ) { return $object; } try { $this->update_additional_fields_for_object( $object, $request ); /** * Fires after a single object is created or updated via the REST API. * * @param WC_Data $object Inserted object. * @param WP_REST_Request $request Request object. * @param boolean $creating True when creating object, false when updating. */ do_action( "woocommerce_rest_insert_{$this->post_type}_object", $object, $request, true ); } catch ( WC_Data_Exception $e ) { $object->delete(); return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { $object->delete(); return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } $request->set_param( 'context', 'edit' ); $response = $this->prepare_object_for_response( $object, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ) ); return $response; } /** * Update a single post. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function update_item( $request ) { $object = $this->get_object( (int) $request['id'] ); if ( ! $object || 0 === $object->get_id() ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 400 ) ); } $object = $this->save_object( $request, false ); if ( is_wp_error( $object ) ) { return $object; } try { $this->update_additional_fields_for_object( $object, $request ); /** * Fires after a single object is created or updated via the REST API. * * @param WC_Data $object Inserted object. * @param WP_REST_Request $request Request object. * @param boolean $creating True when creating object, false when updating. */ do_action( "woocommerce_rest_insert_{$this->post_type}_object", $object, $request, false ); } catch ( WC_Data_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } $request->set_param( 'context', 'edit' ); $response = $this->prepare_object_for_response( $object, $request ); return rest_ensure_response( $response ); } /** * 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 = array(); $args['offset'] = $request['offset']; $args['order'] = $request['order']; $args['orderby'] = $request['orderby']; $args['paged'] = $request['page']; $args['post__in'] = $request['include']; $args['post__not_in'] = $request['exclude']; $args['posts_per_page'] = $request['per_page']; $args['name'] = $request['slug']; $args['post_parent__in'] = $request['parent']; $args['post_parent__not_in'] = $request['parent_exclude']; $args['s'] = $request['search']; if ( 'date' === $args['orderby'] ) { $args['orderby'] = 'date ID'; } $args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. if ( isset( $request['before'] ) ) { $args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. if ( isset( $request['after'] ) ) { $args['date_query'][0]['after'] = $request['after']; } // Force the post_type argument, since it's not a user input variable. $args['post_type'] = $this->post_type; /** * Filter the query arguments for a request. * * Enables adding extra arguments or setting defaults for a post * collection request. * * @param array $args Key value array of query var to query value. * @param WP_REST_Request $request The request used. */ $args = apply_filters( "woocommerce_rest_{$this->post_type}_object_query", $args, $request ); return $this->prepare_items_query( $args, $request ); } /** * Get objects. * * @since 3.0.0 * @param array $query_args Query args. * @return array */ protected function get_objects( $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_filter( array_map( array( $this, 'get_object' ), $result ) ), 'total' => (int) $total_posts, 'pages' => (int) ceil( $total_posts / (int) $query->query_vars['posts_per_page'] ), ); } /** * Get a collection of posts. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $query_args = $this->prepare_objects_query( $request ); $query_results = $this->get_objects( $query_args ); $objects = array(); foreach ( $query_results['objects'] as $object ) { if ( ! wc_rest_check_post_permissions( $this->post_type, 'read', $object->get_id() ) ) { continue; } $data = $this->prepare_object_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 = $this->rest_base; $attrib_prefix = '(?P<'; if ( strpos( $base, $attrib_prefix ) !== false ) { $attrib_names = array(); preg_match( '/\(\?P<[^>]+>.*\)/', $base, $attrib_names, PREG_OFFSET_CAPTURE ); foreach ( $attrib_names as $attrib_name_match ) { $beginning_offset = strlen( $attrib_prefix ); $attrib_name_end = strpos( $attrib_name_match[0], '>', $attrib_name_match[1] ); $attrib_name = substr( $attrib_name_match[0], $beginning_offset, $attrib_name_end - $beginning_offset ); if ( isset( $request[ $attrib_name ] ) ) { $base = str_replace( "(?P<$attrib_name>[\d]+)", $request[ $attrib_name ], $base ); } } } $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ) ); if ( $page > 1 ) { $prev_page = $page - 1; if ( $prev_page > $max_pages ) { $prev_page = $max_pages; } $prev_link = add_query_arg( 'page', $prev_page, $base ); $response->link_header( 'prev', $prev_link ); } if ( $max_pages > $page ) { $next_page = $page + 1; $next_link = add_query_arg( 'page', $next_page, $base ); $response->link_header( 'next', $next_link ); } return $response; } /** * Delete a single item. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error */ public function delete_item( $request ) { $force = (bool) $request['force']; $object = $this->get_object( (int) $request['id'] ); $result = false; if ( ! $object || 0 === $object->get_id() ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); } $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); /** * Filter whether an object is trashable. * * Return false to disable trash support for the object. * * @param boolean $supports_trash Whether the object type support trashing. * @param WC_Data $object The object being considered for trashing support. */ $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { /* translators: %s: post type */ return new WP_Error( "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code() ) ); } $request->set_param( 'context', 'edit' ); $response = $this->prepare_object_for_response( $object, $request ); // If we're forcing, then delete permanently. if ( $force ) { $object->delete( true ); $result = 0 === $object->get_id(); } else { // If we don't support trashing for this type, error out. if ( ! $supports_trash ) { /* translators: %s: post type */ return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501 ) ); } // Otherwise, only trash if we haven't already. if ( is_callable( array( $object, 'get_status' ) ) ) { if ( 'trash' === $object->get_status() ) { /* translators: %s: post type */ return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410 ) ); } $object->delete(); $result = 'trash' === $object->get_status(); } } 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 object is deleted or trashed via the REST API. * * @param WC_Data $object The deleted or trashed object. * @param WP_REST_Response $response The response data. * @param WP_REST_Request $request The request sent to the API. */ do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request ); return $response; } /** * Prepare links for the request. * * @param WC_Data $object Object data. * @param WP_REST_Request $request Request object. * @return array Links for the given post. */ protected function prepare_links( $object, $request ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Get the query params for collections of attachments. * * @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['search'] = array( 'description' => __( 'Limit results to those matching a string.', 'woocommerce' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ); $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', 'include', 'title', 'slug', ), 'validate_callback' => 'rest_validate_request_arg', ); if ( $this->hierarchical ) { $params['parent'] = array( 'description' => __( 'Limit result set to those of particular parent IDs.', 'woocommerce' ), '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' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'sanitize_callback' => 'wp_parse_id_list', 'default' => array(), ); } /** * Filter collection parameters for the posts controller. * * The dynamic part of the filter `$this->post_type` refers to the post * type slug for the controller. * * This filter registers the collection parameter, but does not map the * collection parameter to an internal WP_Query parameter. Use the * `rest_{$this->post_type}_query` filter to set WP_Query parameters. * * @param array $query_params JSON Schema-formatted collection parameters. * @param WP_Post_Type $post_type Post type object. */ return apply_filters( "rest_{$this->post_type}_collection_params", $params, $this->post_type ); } } PKo5\: 55[woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-payment-gateways-controller.phpnu[ $gateway->id, 'title' => $gateway->title, 'description' => $gateway->description, 'order' => isset( $order[ $gateway->id ] ) ? $order[ $gateway->id ] : '', 'enabled' => ( 'yes' === $gateway->enabled ), 'method_title' => $gateway->get_method_title(), 'method_description' => $gateway->get_method_description(), 'method_supports' => $gateway->supports, 'settings' => $this->get_settings( $gateway ), ); $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $item, $request ); $data = $this->filter_response_by_context( $data, $context ); $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $gateway, $request ) ); /** * Filter payment gateway objects returned from the REST API. * * @param WP_REST_Response $response The response object. * @param WC_Payment_Gateway $gateway Payment gateway object. * @param WP_REST_Request $request Request object. */ return apply_filters( 'woocommerce_rest_prepare_payment_gateway', $response, $gateway, $request ); } /** * Return settings associated with this payment gateway. * * @param WC_Payment_Gateway $gateway Gateway instance. * * @return array */ public function get_settings( $gateway ) { $settings = array(); $gateway->init_form_fields(); foreach ( $gateway->form_fields as $id => $field ) { // Make sure we at least have a title and type. if ( empty( $field['title'] ) || empty( $field['type'] ) ) { continue; } // Ignore 'enabled' and 'description' which get included elsewhere. if ( in_array( $id, array( 'enabled', 'description' ), true ) ) { continue; } $data = array( 'id' => $id, 'label' => empty( $field['label'] ) ? $field['title'] : $field['label'], 'description' => empty( $field['description'] ) ? '' : $field['description'], 'type' => $field['type'], 'value' => empty( $gateway->settings[ $id ] ) ? '' : $gateway->settings[ $id ], 'default' => empty( $field['default'] ) ? '' : $field['default'], 'tip' => empty( $field['description'] ) ? '' : $field['description'], 'placeholder' => empty( $field['placeholder'] ) ? '' : $field['placeholder'], ); if ( ! empty( $field['options'] ) ) { $data['options'] = $field['options']; } $settings[ $id ] = $data; } return $settings; } /** * Get the payment gateway schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'payment_gateway', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Payment gateway ID.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'title' => array( 'description' => __( 'Payment gateway title on checkout.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'description' => array( 'description' => __( 'Payment gateway description on checkout.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'order' => array( 'description' => __( 'Payment gateway sort order.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'absint', ), ), 'enabled' => array( 'description' => __( 'Payment gateway enabled status.', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), ), 'method_title' => array( 'description' => __( 'Payment gateway method title.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'method_description' => array( 'description' => __( 'Payment gateway method description.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'method_supports' => array( 'description' => __( 'Supported features for this payment gateway.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'readonly' => true, 'items' => array( 'type' => 'string', ), ), 'settings' => array( 'description' => __( 'Payment gateway settings.', 'woocommerce' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array( 'id' => array( 'description' => __( 'A unique identifier for the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'label' => array( 'description' => __( 'A human readable label for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'A human readable description for the setting used in interfaces.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'type' => array( 'description' => __( 'Type of setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'enum' => array( 'text', 'email', 'number', 'color', 'password', 'textarea', 'select', 'multiselect', 'radio', 'image_width', 'checkbox' ), 'readonly' => true, ), 'value' => array( 'description' => __( 'Setting value.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'default' => array( 'description' => __( 'Default value for the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'tip' => array( 'description' => __( 'Additional help text shown to the user about the setting.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'placeholder' => array( 'description' => __( 'Placeholder text to be displayed in text inputs.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\*XRwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-reports-controller.phpnu[ 'orders/totals', 'description' => __( 'Orders totals.', 'woocommerce' ), ); $reports[] = array( 'slug' => 'products/totals', 'description' => __( 'Products totals.', 'woocommerce' ), ); $reports[] = array( 'slug' => 'customers/totals', 'description' => __( 'Customers totals.', 'woocommerce' ), ); $reports[] = array( 'slug' => 'coupons/totals', 'description' => __( 'Coupons totals.', 'woocommerce' ), ); $reports[] = array( 'slug' => 'reviews/totals', 'description' => __( 'Reviews totals.', 'woocommerce' ), ); $reports[] = array( 'slug' => 'categories/totals', 'description' => __( 'Categories totals.', 'woocommerce' ), ); $reports[] = array( 'slug' => 'tags/totals', 'description' => __( 'Tags totals.', 'woocommerce' ), ); $reports[] = array( 'slug' => 'attributes/totals', 'description' => __( 'Attributes totals.', 'woocommerce' ), ); return $reports; } } PKo5\MMcwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-product-shipping-classes-controller.phpnu[/terms endpoint. * * @package Automattic/WooCommerce/RestApi * @since 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * REST API Product Attribute Terms controller class. * * @package Automattic/WooCommerce/RestApi * @extends WC_REST_Product_Attribute_Terms_V2_Controller */ class WC_REST_Product_Attribute_Terms_Controller extends WC_REST_Product_Attribute_Terms_V2_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v3'; } PKo5\~ _woocommerce-rest-api/src/Controllers/Version3/class-wc-rest-report-orders-totals-controller.phpnu[ $name ) { if ( ! isset( $totals->$slug ) ) { continue; } $data[] = array( 'slug' => str_replace( 'wc-', '', $slug ), 'name' => $name, 'total' => (int) $totals->$slug, ); } return $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, 'name' => $report->name, 'total' => $report->total, ); $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_count', $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_order_total', 'type' => 'object', 'properties' => array( 'slug' => array( 'description' => __( 'An alphanumeric identifier for the resource.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'Order status name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Amount of orders.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\Xwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-system-status-controller.phpnu[ $total ) { if ( in_array( $role, array( 'administrator', 'shop_manager' ), true ) ) { continue; } $total_customers += (int) $total; } $customers_query = new WP_User_Query( array( 'role__not_in' => array( 'administrator', 'shop_manager' ), 'number' => 0, 'fields' => 'ID', 'count_total' => true, 'meta_query' => array( // WPCS: slow query ok. array( 'key' => 'paying_customer', 'value' => 1, 'compare' => '=', ), ), ) ); $total_paying = (int) $customers_query->get_total(); $data = array( array( 'slug' => 'paying', 'name' => __( 'Paying customer', 'woocommerce' ), 'total' => $total_paying, ), array( 'slug' => 'non_paying', 'name' => __( 'Non-paying customer', 'woocommerce' ), 'total' => $total_customers - $total_paying, ), ); return $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, 'name' => $report->name, 'total' => $report->total, ); $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_count', $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_customer_total', 'type' => 'object', 'properties' => array( 'slug' => array( 'description' => __( 'An alphanumeric identifier for the resource.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'Customer type name.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), 'total' => array( 'description' => __( 'Amount of customers.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), ), ); return $this->add_additional_fields_schema( $schema ); } } PKo5\c44Jwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-controller.phpnu[ * * NOTE THAT ONLY CODE RELEVANT FOR MOST ENDPOINTS SHOULD BE INCLUDED INTO THIS CLASS. * If necessary extend this class and create new abstract classes like `WC_REST_CRUD_Controller` or `WC_REST_Terms_Controller`. * * @class WC_REST_Controller * @package Automattic/WooCommerce/RestApi * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/controller-classes/ */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Abstract Rest Controller Class * * @package Automattic/WooCommerce/RestApi * @extends WP_REST_Controller * @version 2.6.0 */ abstract class WC_REST_Controller extends WP_REST_Controller { /** * Endpoint namespace. * * @var string */ protected $namespace = 'wc/v1'; /** * Route base. * * @var string */ protected $rest_base = ''; /** * Add the schema from additional fields to an schema array. * * The type of object is inferred from the passed schema. * * @param array $schema Schema array. * * @return array */ protected function add_additional_fields_schema( $schema ) { if ( empty( $schema['title'] ) ) { return $schema; } /** * Can't use $this->get_object_type otherwise we cause an inf loop. */ $object_type = $schema['title']; $additional_fields = $this->get_additional_fields( $object_type ); foreach ( $additional_fields as $field_name => $field_options ) { if ( ! $field_options['schema'] ) { continue; } $schema['properties'][ $field_name ] = $field_options['schema']; } $schema['properties'] = apply_filters( 'woocommerce_rest_' . $object_type . '_schema', $schema['properties'] ); return $schema; } /** * Get normalized rest base. * * @return string */ protected function get_normalized_rest_base() { return preg_replace( '/\(.*\)\//i', '', $this->rest_base ); } /** * Check batch limit. * * @param array $items Request items. * @return bool|WP_Error */ protected function check_batch_limit( $items ) { $limit = apply_filters( 'woocommerce_rest_batch_items_limit', 100, $this->get_normalized_rest_base() ); $total = 0; if ( ! empty( $items['create'] ) ) { $total += count( $items['create'] ); } if ( ! empty( $items['update'] ) ) { $total += count( $items['update'] ); } if ( ! empty( $items['delete'] ) ) { $total += count( $items['delete'] ); } if ( $total > $limit ) { /* translators: %s: items limit */ return new WP_Error( 'woocommerce_rest_request_entity_too_large', sprintf( __( 'Unable to accept more than %s items for this request.', 'woocommerce' ), $limit ), array( 'status' => 413 ) ); } return true; } /** * Bulk create, update and delete items. * * @param WP_REST_Request $request Full details about the request. * @return array Of WP_Error or WP_REST_Response. */ public function batch_items( $request ) { /** * REST Server * * @var WP_REST_Server $wp_rest_server */ global $wp_rest_server; // Get the request params. $items = array_filter( $request->get_params() ); $query = $request->get_query_params(); $response = array(); // Check batch limit. $limit = $this->check_batch_limit( $items ); if ( is_wp_error( $limit ) ) { return $limit; } if ( ! empty( $items['create'] ) ) { foreach ( $items['create'] as $item ) { $_item = new WP_REST_Request( 'POST' ); // Default parameters. $defaults = array(); $schema = $this->get_public_item_schema(); foreach ( $schema['properties'] as $arg => $options ) { if ( isset( $options['default'] ) ) { $defaults[ $arg ] = $options['default']; } } $_item->set_default_params( $defaults ); // Set request parameters. $_item->set_body_params( $item ); // Set query (GET) parameters. $_item->set_query_params( $query ); $_response = $this->create_item( $_item ); if ( is_wp_error( $_response ) ) { $response['create'][] = array( 'id' => 0, 'error' => array( 'code' => $_response->get_error_code(), 'message' => $_response->get_error_message(), 'data' => $_response->get_error_data(), ), ); } else { $response['create'][] = $wp_rest_server->response_to_data( $_response, '' ); } } } if ( ! empty( $items['update'] ) ) { foreach ( $items['update'] as $item ) { $_item = new WP_REST_Request( 'PUT' ); $_item->set_body_params( $item ); $_response = $this->update_item( $_item ); if ( is_wp_error( $_response ) ) { $response['update'][] = array( 'id' => $item['id'], 'error' => array( 'code' => $_response->get_error_code(), 'message' => $_response->get_error_message(), 'data' => $_response->get_error_data(), ), ); } else { $response['update'][] = $wp_rest_server->response_to_data( $_response, '' ); } } } if ( ! empty( $items['delete'] ) ) { foreach ( $items['delete'] as $id ) { $id = (int) $id; if ( 0 === $id ) { continue; } $_item = new WP_REST_Request( 'DELETE' ); $_item->set_query_params( array( 'id' => $id, 'force' => true, ) ); $_response = $this->delete_item( $_item ); if ( is_wp_error( $_response ) ) { $response['delete'][] = array( 'id' => $id, 'error' => array( 'code' => $_response->get_error_code(), 'message' => $_response->get_error_message(), 'data' => $_response->get_error_data(), ), ); } else { $response['delete'][] = $wp_rest_server->response_to_data( $_response, '' ); } } } return $response; } /** * Validate a text value for a text based setting. * * @since 3.0.0 * @param string $value Value. * @param array $setting Setting. * @return string */ public function validate_setting_text_field( $value, $setting ) { $value = is_null( $value ) ? '' : $value; return wp_kses_post( trim( stripslashes( $value ) ) ); } /** * Validate select based settings. * * @since 3.0.0 * @param string $value Value. * @param array $setting Setting. * @return string|WP_Error */ public function validate_setting_select_field( $value, $setting ) { if ( array_key_exists( $value, $setting['options'] ) ) { return $value; } else { return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); } } /** * Validate multiselect based settings. * * @since 3.0.0 * @param array $values Values. * @param array $setting Setting. * @return array|WP_Error */ public function validate_setting_multiselect_field( $values, $setting ) { if ( empty( $values ) ) { return array(); } if ( ! is_array( $values ) ) { return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); } $final_values = array(); foreach ( $values as $value ) { if ( array_key_exists( $value, $setting['options'] ) ) { $final_values[] = $value; } } return $final_values; } /** * Validate image_width based settings. * * @since 3.0.0 * @param array $values Values. * @param array $setting Setting. * @return string|WP_Error */ public function validate_setting_image_width_field( $values, $setting ) { if ( ! is_array( $values ) ) { return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); } $current = $setting['value']; if ( isset( $values['width'] ) ) { $current['width'] = intval( $values['width'] ); } if ( isset( $values['height'] ) ) { $current['height'] = intval( $values['height'] ); } if ( isset( $values['crop'] ) ) { $current['crop'] = (bool) $values['crop']; } return $current; } /** * Validate radio based settings. * * @since 3.0.0 * @param string $value Value. * @param array $setting Setting. * @return string|WP_Error */ public function validate_setting_radio_field( $value, $setting ) { return $this->validate_setting_select_field( $value, $setting ); } /** * Validate checkbox based settings. * * @since 3.0.0 * @param string $value Value. * @param array $setting Setting. * @return string|WP_Error */ public function validate_setting_checkbox_field( $value, $setting ) { if ( in_array( $value, array( 'yes', 'no' ) ) ) { return $value; } elseif ( empty( $value ) ) { $value = isset( $setting['default'] ) ? $setting['default'] : 'no'; return $value; } else { return new WP_Error( 'rest_setting_value_invalid', __( 'An invalid setting value was passed.', 'woocommerce' ), array( 'status' => 400 ) ); } } /** * Validate textarea based settings. * * @since 3.0.0 * @param string $value Value. * @param array $setting Setting. * @return string */ public function validate_setting_textarea_field( $value, $setting ) { $value = is_null( $value ) ? '' : $value; return wp_kses( trim( stripslashes( $value ) ), array_merge( array( 'iframe' => array( 'src' => true, 'style' => true, 'id' => true, 'class' => true, ), ), wp_kses_allowed_html( 'post' ) ) ); } /** * Add meta query. * * @since 3.0.0 * @param array $args Query args. * @param array $meta_query Meta query. * @return array */ protected function add_meta_query( $args, $meta_query ) { if ( empty( $args['meta_query'] ) ) { $args['meta_query'] = array(); } $args['meta_query'][] = $meta_query; return $args['meta_query']; } /** * Get the batch schema, conforming to JSON Schema. * * @return array */ public function get_public_batch_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'batch', 'type' => 'object', 'properties' => array( 'create' => array( 'description' => __( 'List of created resources.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', ), ), 'update' => array( 'description' => __( 'List of updated resources.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'object', ), ), 'delete' => array( 'description' => __( 'List of delete resources.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'items' => array( 'type' => 'integer', ), ), ), ); return $schema; } /** * Gets an array of fields to be included on the response. * * Included fields are based on item schema and `_fields=` request argument. * Updated from WordPress 5.3, included into this class to support old versions. * * @since 3.5.0 * @param WP_REST_Request $request Full details about the request. * @return array Fields to be included in the response. */ public function get_fields_for_response( $request ) { $schema = $this->get_item_schema(); $properties = isset( $schema['properties'] ) ? $schema['properties'] : array(); $additional_fields = $this->get_additional_fields(); foreach ( $additional_fields as $field_name => $field_options ) { // For back-compat, include any field with an empty schema // because it won't be present in $this->get_item_schema(). if ( is_null( $field_options['schema'] ) ) { $properties[ $field_name ] = $field_options; } } // Exclude fields that specify a different context than the request context. $context = $request['context']; if ( $context ) { foreach ( $properties as $name => $options ) { if ( ! empty( $options['context'] ) && ! in_array( $context, $options['context'], true ) ) { unset( $properties[ $name ] ); } } } $fields = array_keys( $properties ); if ( ! isset( $request['_fields'] ) ) { return $fields; } $requested_fields = wp_parse_list( $request['_fields'] ); if ( 0 === count( $requested_fields ) ) { return $fields; } // Trim off outside whitespace from the comma delimited list. $requested_fields = array_map( 'trim', $requested_fields ); // Always persist 'id', because it can be needed for add_additional_fields_to_object(). if ( in_array( 'id', $fields, true ) ) { $requested_fields[] = 'id'; } // Return the list of all requested fields which appear in the schema. return array_reduce( $requested_fields, function( $response_fields, $field ) use ( $fields ) { if ( in_array( $field, $fields, true ) ) { $response_fields[] = $field; return $response_fields; } // Check for nested fields if $field is not a direct match. $nested_fields = explode( '.', $field ); // A nested field is included so long as its top-level property is // present in the schema. if ( in_array( $nested_fields[0], $fields, true ) ) { $response_fields[] = $field; } return $response_fields; }, array() ); } } PKo5\LZ}Vwoocommerce-rest-api/src/Controllers/Version3/class-wc-rest-tax-classes-controller.phpnu[post_type, 'read' ) ) { return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access to create an item. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function create_item_permissions_check( $request ) { if ( ! wc_rest_check_post_permissions( $this->post_type, 'create' ) ) { return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access to read an item. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function get_item_permissions_check( $request ) { $post = get_post( (int) $request['id'] ); if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'read', $post->ID ) ) { return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access to update an item. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function update_item_permissions_check( $request ) { $post = get_post( (int) $request['id'] ); if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'edit', $post->ID ) ) { return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access to delete an item. * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error */ public function delete_item_permissions_check( $request ) { $post = get_post( (int) $request['id'] ); if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'delete', $post->ID ) ) { return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Check if a given request has access batch create, update and delete items. * * @param WP_REST_Request $request Full details about the request. * * @return boolean|WP_Error */ public function batch_items_permissions_check( $request ) { if ( ! wc_rest_check_post_permissions( $this->post_type, '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 a single item. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function get_item( $request ) { $id = (int) $request['id']; $post = get_post( $id ); if ( ! empty( $post->post_type ) && 'product_variation' === $post->post_type && 'product' === $this->post_type ) { return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404 ) ); } elseif ( empty( $id ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) { return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); } $data = $this->prepare_item_for_response( $post, $request ); $response = rest_ensure_response( $data ); if ( $this->public ) { $response->link_header( 'alternate', get_permalink( $id ), array( 'type' => 'text/html' ) ); } return $response; } /** * Create a single item. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function create_item( $request ) { if ( ! empty( $request['id'] ) ) { /* 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 ) ); } $post = $this->prepare_item_for_database( $request ); if ( is_wp_error( $post ) ) { return $post; } $post->post_type = $this->post_type; $post_id = wp_insert_post( $post, true ); if ( is_wp_error( $post_id ) ) { if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ) ) ) { $post_id->add_data( array( 'status' => 500 ) ); } else { $post_id->add_data( array( 'status' => 400 ) ); } return $post_id; } $post->ID = $post_id; $post = get_post( $post_id ); $this->update_additional_fields_for_object( $post, $request ); // Add meta fields. $meta_fields = $this->add_post_meta_fields( $post, $request ); if ( is_wp_error( $meta_fields ) ) { // Remove post. $this->delete_post( $post ); return $meta_fields; } /** * Fires after a single item is created or updated via the REST API. * * @param 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; } /** * Add post meta fields. * * @param WP_Post $post Post Object. * @param WP_REST_Request $request WP_REST_Request Object. * @return bool|WP_Error */ protected function add_post_meta_fields( $post, $request ) { return true; } /** * Delete post. * * @param WP_Post $post Post object. */ protected function delete_post( $post ) { wp_delete_post( $post->ID, true ); } /** * Update a single post. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function update_item( $request ) { $id = (int) $request['id']; $post = get_post( $id ); if ( ! empty( $post->post_type ) && 'product_variation' === $post->post_type && 'product' === $this->post_type ) { return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404 ) ); } elseif ( empty( $id ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) ); } $post = $this->prepare_item_for_database( $request ); if ( is_wp_error( $post ) ) { return $post; } // Convert the post object to an array, otherwise wp_update_post will expect non-escaped input. $post_id = wp_update_post( (array) $post, true ); if ( is_wp_error( $post_id ) ) { if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ) ) ) { $post_id->add_data( array( 'status' => 500 ) ); } else { $post_id->add_data( array( 'status' => 400 ) ); } return $post_id; } $post = get_post( $post_id ); $this->update_additional_fields_for_object( $post, $request ); // Update meta fields. $meta_fields = $this->update_post_meta_fields( $post, $request ); if ( is_wp_error( $meta_fields ) ) { return $meta_fields; } /** * Fires after a single item is created or updated via the REST API. * * @param 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 ); } /** * Get a collection of posts. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { $args = array(); $args['offset'] = $request['offset']; $args['order'] = $request['order']; $args['orderby'] = $request['orderby']; $args['paged'] = $request['page']; $args['post__in'] = $request['include']; $args['post__not_in'] = $request['exclude']; $args['posts_per_page'] = $request['per_page']; $args['name'] = $request['slug']; $args['post_parent__in'] = $request['parent']; $args['post_parent__not_in'] = $request['parent_exclude']; $args['s'] = $request['search']; $args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. if ( isset( $request['before'] ) ) { $args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. if ( isset( $request['after'] ) ) { $args['date_query'][0]['after'] = $request['after']; } if ( 'wc/v1' === $this->namespace ) { if ( is_array( $request['filter'] ) ) { $args = array_merge( $args, $request['filter'] ); unset( $args['filter'] ); } } // Force the post_type argument, since it's not a user input variable. $args['post_type'] = $this->post_type; /** * Filter the query arguments for a request. * * Enables adding extra arguments or setting defaults for a post * collection request. * * @param array $args Key value array of query var to query value. * @param WP_REST_Request $request The request used. */ $args = apply_filters( "woocommerce_rest_{$this->post_type}_query", $args, $request ); $query_args = $this->prepare_items_query( $args, $request ); $posts_query = new WP_Query(); $query_result = $posts_query->query( $query_args ); $posts = array(); foreach ( $query_result as $post ) { if ( ! wc_rest_check_post_permissions( $this->post_type, 'read', $post->ID ) ) { continue; } $data = $this->prepare_item_for_response( $post, $request ); $posts[] = $this->prepare_response_for_collection( $data ); } $page = (int) $query_args['paged']; $total_posts = $posts_query->found_posts; if ( $total_posts < 1 ) { // Out-of-bounds, run the query again without LIMIT for total count. unset( $query_args['paged'] ); $count_query = new WP_Query(); $count_query->query( $query_args ); $total_posts = $count_query->found_posts; } $max_pages = ceil( $total_posts / (int) $query_args['posts_per_page'] ); $response = rest_ensure_response( $posts ); $response->header( 'X-WP-Total', (int) $total_posts ); $response->header( 'X-WP-TotalPages', (int) $max_pages ); $request_params = $request->get_query_params(); if ( ! empty( $request_params['filter'] ) ) { // Normalize the pagination params. unset( $request_params['filter']['posts_per_page'] ); unset( $request_params['filter']['paged'] ); } $base = add_query_arg( $request_params, rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); if ( $page > 1 ) { $prev_page = $page - 1; if ( $prev_page > $max_pages ) { $prev_page = $max_pages; } $prev_link = add_query_arg( 'page', $prev_page, $base ); $response->link_header( 'prev', $prev_link ); } if ( $max_pages > $page ) { $next_page = $page + 1; $next_link = add_query_arg( 'page', $next_page, $base ); $response->link_header( 'next', $next_link ); } return $response; } /** * Delete a single item. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error */ public function delete_item( $request ) { $id = (int) $request['id']; $force = (bool) $request['force']; $post = get_post( $id ); if ( empty( $id ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) { return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 404 ) ); } $supports_trash = EMPTY_TRASH_DAYS > 0; /** * Filter whether an item is trashable. * * Return false to disable trash support for the item. * * @param boolean $supports_trash Whether the item type support trashing. * @param WP_Post $post The Post object being considered for trashing support. */ $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_trashable", $supports_trash, $post ); if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $post->ID ) ) { /* translators: %s: post type */ return new WP_Error( "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code() ) ); } $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $post, $request ); // If we're forcing, then delete permanently. if ( $force ) { $result = wp_delete_post( $id, true ); } else { // If we don't support trashing for this type, error out. if ( ! $supports_trash ) { /* translators: %s: post type */ return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501 ) ); } // Otherwise, only trash if we haven't already. if ( 'trash' === $post->post_status ) { /* translators: %s: post type */ return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410 ) ); } // (Note that internally this falls through to `wp_delete_post` if // the trash is disabled.) $result = wp_trash_post( $id ); } if ( ! $result ) { /* 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 object $post The deleted or trashed item. * @param WP_REST_Response $response The response data. * @param WP_REST_Request $request The request sent to the API. */ do_action( "woocommerce_rest_delete_{$this->post_type}", $post, $response, $request ); return $response; } /** * Prepare links for the request. * * @param WP_Post $post Post object. * @param WP_REST_Request $request Request object. * @return array Links for the given post. */ protected function prepare_links( $post, $request ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Determine the allowed query_vars for a get_items() response and * prepare for WP_Query. * * @param array $prepared_args Prepared arguments. * @param WP_REST_Request $request Request object. * @return array $query_args */ protected function prepare_items_query( $prepared_args = array(), $request = null ) { $valid_vars = array_flip( $this->get_allowed_query_vars() ); $query_args = array(); foreach ( $valid_vars as $var => $index ) { if ( isset( $prepared_args[ $var ] ) ) { /** * Filter the query_vars used in `get_items` for the constructed query. * * The dynamic portion of the hook name, $var, refers to the query_var key. * * @param mixed $prepared_args[ $var ] The query_var value. */ $query_args[ $var ] = apply_filters( "woocommerce_rest_query_var-{$var}", $prepared_args[ $var ] ); } } $query_args['ignore_sticky_posts'] = true; if ( 'include' === $query_args['orderby'] ) { $query_args['orderby'] = 'post__in'; } elseif ( 'id' === $query_args['orderby'] ) { $query_args['orderby'] = 'ID'; // ID must be capitalized. } elseif ( 'slug' === $query_args['orderby'] ) { $query_args['orderby'] = 'name'; } return $query_args; } /** * Get all the WP Query vars that are allowed for the API request. * * @return array */ protected function get_allowed_query_vars() { global $wp; /** * Filter the publicly allowed query vars. * * Allows adjusting of the default query vars that are made public. * * @param array Array of allowed WP_Query query vars. */ $valid_vars = apply_filters( 'query_vars', $wp->public_query_vars ); $post_type_obj = get_post_type_object( $this->post_type ); if ( current_user_can( $post_type_obj->cap->edit_posts ) ) { /** * Filter the allowed 'private' query vars for authorized users. * * If the user has the `edit_posts` capability, we also allow use of * private query parameters, which are only undesirable on the * frontend, but are safe for use in query strings. * * To disable anyway, use * `add_filter( 'woocommerce_rest_private_query_vars', '__return_empty_array' );` * * @param array $private_query_vars Array of allowed query vars for authorized users. * } */ $private = apply_filters( 'woocommerce_rest_private_query_vars', $wp->private_query_vars ); $valid_vars = array_merge( $valid_vars, $private ); } // Define our own in addition to WP's normal vars. $rest_valid = array( 'date_query', 'ignore_sticky_posts', 'offset', 'post__in', 'post__not_in', 'post_parent', 'post_parent__in', 'post_parent__not_in', 'posts_per_page', 'meta_query', 'tax_query', 'meta_key', 'meta_value', 'meta_compare', 'meta_value_num', ); $valid_vars = array_merge( $valid_vars, $rest_valid ); /** * Filter allowed query vars for the REST API. * * This filter allows you to add or remove query vars from the final allowed * list for all requests, including unauthenticated ones. To alter the * vars for editors only. * * @param array { * Array of allowed WP_Query query vars. * * @param string $allowed_query_var The query var to allow. * } */ $valid_vars = apply_filters( 'woocommerce_rest_query_vars', $valid_vars ); return $valid_vars; } /** * Get the query params for collections of attachments. * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); $params['context']['default'] = 'view'; $params['after'] = array( 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), 'type' => 'string', 'format' => 'date-time', 'validate_callback' => 'rest_validate_request_arg', ); $params['before'] = array( 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), 'type' => 'string', 'format' => 'date-time', 'validate_callback' => 'rest_validate_request_arg', ); $params['exclude'] = array( 'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ), 'type' => 'array', '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', 'include', 'title', 'slug', ), 'validate_callback' => 'rest_validate_request_arg', ); $post_type_obj = get_post_type_object( $this->post_type ); if ( isset( $post_type_obj->hierarchical ) && $post_type_obj->hierarchical ) { $params['parent'] = array( 'description' => __( 'Limit result set to those of particular parent IDs.', 'woocommerce' ), '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' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'sanitize_callback' => 'wp_parse_id_list', 'default' => array(), ); } if ( 'wc/v1' === $this->namespace ) { $params['filter'] = array( 'type' => 'object', 'description' => __( 'Use WP Query arguments to modify the response; private query vars require appropriate authorization.', 'woocommerce' ), ); } return $params; } /** * Update post meta fields. * * @param WP_Post $post Post object. * @param WP_REST_Request $request Request object. * @return bool|WP_Error */ protected function update_post_meta_fields( $post, $request ) { return true; } } PKo5\AWW-woocommerce-rest-api/woocommerce-rest-api.phpnu[

composer install', '' . esc_html( str_replace( ABSPATH, '', __DIR__ ) ) . '' ); ?>

` and `