Merge branch 'master' into fix/18674

This commit is contained in:
Mike Jolley 2018-01-31 16:29:08 +00:00
commit 40c59d31f2
10 changed files with 274 additions and 232 deletions

View File

@ -101,6 +101,10 @@ class WC_Admin_Post_Types {
new WC_Admin_List_Table_Products(); new WC_Admin_List_Table_Products();
break; break;
} }
// Ensure the table handler is only loaded once. Prevents multiple loads if a plugin calls check_ajax_referer many times.
remove_action( 'current_screen', array( $this, 'setup_screen' ) );
remove_action( 'check_ajax_referer', array( $this, 'setup_screen' ) );
} }
/** /**

View File

@ -674,7 +674,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Products_Controller
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
), ),
'date_on_sale_to_gmt' => array( 'date_on_sale_to_gmt' => array(
'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ), 'description' => __( 'End date of sale price, as GMT.', 'woocommerce' ),
'type' => 'date-time', 'type' => 'date-time',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
), ),

View File

@ -4,8 +4,6 @@
* *
* Handles requests to the /products endpoint. * Handles requests to the /products endpoint.
* *
* @author WooThemes
* @category API
* @package WooCommerce/API * @package WooCommerce/API
* @since 2.6.0 * @since 2.6.0
*/ */
@ -61,7 +59,8 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
* Register the routes for products. * Register the routes for products.
*/ */
public function register_routes() { public function register_routes() {
register_rest_route( $this->namespace, '/' . $this->rest_base, array( register_rest_route(
$this->namespace, '/' . $this->rest_base, array(
array( array(
'methods' => WP_REST_Server::READABLE, 'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ), 'callback' => array( $this, 'get_items' ),
@ -75,9 +74,11 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
), ),
'schema' => array( $this, 'get_public_item_schema' ), 'schema' => array( $this, 'get_public_item_schema' ),
) ); )
);
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array( register_rest_route(
$this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
'args' => array( 'args' => array(
'id' => array( 'id' => array(
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
@ -89,9 +90,11 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
'callback' => array( $this, 'get_item' ), 'callback' => array( $this, 'get_item' ),
'permission_callback' => array( $this, 'get_item_permissions_check' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ),
'args' => array( 'args' => array(
'context' => $this->get_context_param( array( 'context' => $this->get_context_param(
array(
'default' => 'view', 'default' => 'view',
) ), )
),
), ),
), ),
array( array(
@ -113,9 +116,11 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
), ),
), ),
'schema' => array( $this, 'get_public_item_schema' ), 'schema' => array( $this, 'get_public_item_schema' ),
) ); )
);
register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array( register_rest_route(
$this->namespace, '/' . $this->rest_base . '/batch', array(
array( array(
'methods' => WP_REST_Server::EDITABLE, 'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'batch_items' ), 'callback' => array( $this, 'batch_items' ),
@ -123,7 +128,8 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
), ),
'schema' => array( $this, 'get_public_batch_schema' ), 'schema' => array( $this, 'get_public_batch_schema' ),
) ); )
);
} }
/** /**
@ -253,19 +259,23 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
$skus[] = $request['sku']; $skus[] = $request['sku'];
} }
$args['meta_query'] = $this->add_meta_query( $args, array( // WPCS: slow query ok. $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
$args, array(
'key' => '_sku', 'key' => '_sku',
'value' => $skus, 'value' => $skus,
'compare' => 'IN', 'compare' => 'IN',
) ); )
);
} }
// Filter by tax class. // Filter by tax class.
if ( ! empty( $request['tax_class'] ) ) { if ( ! empty( $request['tax_class'] ) ) {
$args['meta_query'] = $this->add_meta_query( $args, array( // WPCS: slow query ok. $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
$args, array(
'key' => '_tax_class', 'key' => '_tax_class',
'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '', 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '',
) ); )
);
} }
// Price filter. // Price filter.
@ -275,10 +285,12 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
// Filter product in stock or out of stock. // Filter product in stock or out of stock.
if ( is_bool( $request['in_stock'] ) ) { if ( is_bool( $request['in_stock'] ) ) {
$args['meta_query'] = $this->add_meta_query( $args, array( // WPCS: slow query ok. $args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
$args, array(
'key' => '_stock_status', 'key' => '_stock_status',
'value' => true === $request['in_stock'] ? 'instock' : 'outofstock', 'value' => true === $request['in_stock'] ? 'instock' : 'outofstock',
) ); )
);
} }
// Filter by on sale products. // Filter by on sale products.
@ -487,9 +499,11 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
*/ */
protected function get_attribute_options( $product_id, $attribute ) { protected function get_attribute_options( $product_id, $attribute ) {
if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) { if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) {
return wc_get_product_terms( $product_id, $attribute['name'], array( return wc_get_product_terms(
$product_id, $attribute['name'], array(
'fields' => 'names', 'fields' => 'names',
) ); )
);
} elseif ( isset( $attribute['value'] ) ) { } elseif ( isset( $attribute['value'] ) ) {
return array_map( 'trim', explode( '|', $attribute['value'] ) ); return array_map( 'trim', explode( '|', $attribute['value'] ) );
} }
@ -683,9 +697,11 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
} }
if ( 'variation' === $product->get_type() ) { 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/&lt;product_id&gt;/variations/&lt;id&gt; endpoint.', 'woocommerce' ), array( return new WP_Error(
"woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/&lt;product_id&gt;/variations/&lt;id&gt; endpoint.', 'woocommerce' ), array(
'status' => 404, 'status' => 404,
) ); )
);
} }
// Post title. // Post title.
@ -1086,10 +1102,12 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
// Set the image name if present. // Set the image name if present.
if ( ! empty( $image['name'] ) ) { if ( ! empty( $image['name'] ) ) {
wp_update_post( array( wp_update_post(
array(
'ID' => $attachment_id, 'ID' => $attachment_id,
'post_title' => $image['name'], 'post_title' => $image['name'],
) ); )
);
} }
// Set the image source if present, for future reference. // Set the image source if present, for future reference.
@ -1286,15 +1304,19 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
$result = false; $result = false;
if ( ! $object || 0 === $object->get_id() ) { if ( ! $object || 0 === $object->get_id() ) {
return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( return new WP_Error(
"woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array(
'status' => 404, 'status' => 404,
) ); )
);
} }
if ( 'variation' === $object->get_type() ) { 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/&lt;product_id&gt;/variations/&lt;id&gt; endpoint.', 'woocommerce' ), array( return new WP_Error(
"woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/&lt;product_id&gt;/variations/&lt;id&gt; endpoint.', 'woocommerce' ), array(
'status' => 404, 'status' => 404,
) ); )
);
} }
$supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) );
@ -1310,10 +1332,12 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
$supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); $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() ) ) { if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) {
return new WP_Error(
/* translators: %s: post type */ /* 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( "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(), 'status' => rest_authorization_required_code(),
) ); )
);
} }
$request->set_param( 'context', 'edit' ); $request->set_param( 'context', 'edit' );
@ -1339,19 +1363,23 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
} else { } else {
// If we don't support trashing for this type, error out. // If we don't support trashing for this type, error out.
if ( ! $supports_trash ) { if ( ! $supports_trash ) {
return new WP_Error(
/* translators: %s: post type */ /* 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( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array(
'status' => 501, 'status' => 501,
) ); )
);
} }
// Otherwise, only trash if we haven't already. // Otherwise, only trash if we haven't already.
if ( is_callable( array( $object, 'get_status' ) ) ) { if ( is_callable( array( $object, 'get_status' ) ) ) {
if ( 'trash' === $object->get_status() ) { if ( 'trash' === $object->get_status() ) {
return new WP_Error(
/* translators: %s: post type */ /* translators: %s: post type */
return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array(
'status' => 410, 'status' => 410,
) ); )
);
} }
$object->delete(); $object->delete();
@ -1360,10 +1388,12 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
} }
if ( ! $result ) { if ( ! $result ) {
return new WP_Error(
/* translators: %s: post type */ /* translators: %s: post type */
return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array(
'status' => 500, 'status' => 500,
) ); )
);
} }
// Delete parent product transients. // Delete parent product transients.
@ -1823,7 +1853,7 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
), ),
'images' => array( 'images' => array(
'description' => __( 'List of images.', 'woocommerce' ), 'description' => __( 'List of images.', 'woocommerce' ),
'type' => 'object', 'type' => 'array',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
'items' => array( 'items' => array(
'type' => 'object', 'type' => 'object',

View File

@ -4,8 +4,6 @@
* *
* Handles requests to the /webhooks endpoint. * Handles requests to the /webhooks endpoint.
* *
* @author WooThemes
* @category API
* @package WooCommerce/API * @package WooCommerce/API
* @since 2.6.0 * @since 2.6.0
*/ */
@ -38,6 +36,11 @@ class WC_REST_Webhooks_Controller extends WC_REST_Webhooks_V1_Controller {
*/ */
public function prepare_item_for_response( $id, $request ) { public function prepare_item_for_response( $id, $request ) {
$webhook = wc_get_webhook( $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 ) );
}
$data = array( $data = array(
'id' => $webhook->get_id(), 'id' => $webhook->get_id(),
'name' => $webhook->get_name(), 'name' => $webhook->get_name(),
@ -148,7 +151,7 @@ class WC_REST_Webhooks_Controller extends WC_REST_Webhooks_V1_Controller {
'readonly' => true, 'readonly' => true,
), ),
'secret' => array( 'secret' => array(
'description' => __( "Secret key used to generate a hash of the delivered webhook and provided in the request headers. This will default is a MD5 hash from the current user's ID|username if not provided.", 'woocommerce' ), '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', 'type' => 'string',
'context' => array( 'edit' ), 'context' => array( 'edit' ),
), ),

View File

@ -4,8 +4,6 @@
* *
* Handles requests to the /webhooks endpoint. * Handles requests to the /webhooks endpoint.
* *
* @author WooThemes
* @category API
* @package WooCommerce/API * @package WooCommerce/API
* @since 3.0.0 * @since 3.0.0
*/ */
@ -362,7 +360,7 @@ class WC_REST_Webhooks_V1_Controller extends WC_REST_Controller {
$id = (int) $request['id']; $id = (int) $request['id'];
$webhook = wc_get_webhook( $id ); $webhook = wc_get_webhook( $id );
if ( empty( $id ) || is_null( $webhook->get_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 ) ); return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) );
} }
@ -521,12 +519,17 @@ class WC_REST_Webhooks_V1_Controller extends WC_REST_Controller {
/** /**
* Prepare a single webhook output for response. * Prepare a single webhook output for response.
* *
* @param int $id Webhook ID. * @param int $id Webhook ID or object.
* @param WP_REST_Request $request Request object. * @param WP_REST_Request $request Request object.
* @return WP_REST_Response $response Response data. * @return WP_REST_Response $response Response data.
*/ */
public function prepare_item_for_response( $id, $request ) { public function prepare_item_for_response( $id, $request ) {
$webhook = wc_get_webhook( (int) $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 ) );
}
$data = array( $data = array(
'id' => $webhook->get_id(), 'id' => $webhook->get_id(),
'name' => $webhook->get_name(), 'name' => $webhook->get_name(),
@ -644,7 +647,7 @@ class WC_REST_Webhooks_V1_Controller extends WC_REST_Controller {
'readonly' => true, 'readonly' => true,
), ),
'secret' => array( 'secret' => array(
'description' => __( "Secret key used to generate a hash of the delivered webhook and provided in the request headers. This will default is a MD5 hash from the current user's ID|username if not provided.", 'woocommerce' ), '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', 'type' => 'string',
'context' => array( 'edit' ), 'context' => array( 'edit' ),
), ),

View File

@ -390,7 +390,7 @@ class WC_Structured_Data {
continue; continue;
} }
$product = apply_filters( 'woocommerce_order_item_product', $order->get_product_from_item( $item ), $item ); $product = $order->get_product_from_item( $item );
$product_exists = is_object( $product ); $product_exists = is_object( $product );
$is_visible = $product_exists && $product->is_visible(); $is_visible = $product_exists && $product->is_visible();

View File

@ -357,7 +357,7 @@ class WC_Shop_Customizer {
'woocommerce_default_catalog_orderby', 'woocommerce_default_catalog_orderby',
array( array(
'label' => __( 'Default product sorting', 'woocommerce' ), 'label' => __( 'Default product sorting', 'woocommerce' ),
'description' => __( 'How should products by sorted in the catalog by default?', 'woocommerce' ), 'description' => __( 'How should products be sorted in the catalog by default?', 'woocommerce' ),
'section' => 'woocommerce_product_catalog', 'section' => 'woocommerce_product_catalog',
'settings' => 'woocommerce_default_catalog_orderby', 'settings' => 'woocommerce_default_catalog_orderby',
'type' => 'select', 'type' => 'select',

View File

@ -204,6 +204,8 @@ add_action( 'woocommerce_after_shop_loop', 'woocommerce_reset_loop', 999 );
* @return mixed * @return mixed
*/ */
function wc_get_loop_prop( $prop, $default = '' ) { function wc_get_loop_prop( $prop, $default = '' ) {
wc_setup_loop(); // Ensure shop loop is setup.
return isset( $GLOBALS['woocommerce_loop'], $GLOBALS['woocommerce_loop'][ $prop ] ) ? $GLOBALS['woocommerce_loop'][ $prop ] : $default; return isset( $GLOBALS['woocommerce_loop'], $GLOBALS['woocommerce_loop'][ $prop ] ) ? $GLOBALS['woocommerce_loop'][ $prop ] : $default;
} }
@ -323,7 +325,9 @@ function wc_get_default_products_per_row() {
$columns = apply_filters( 'loop_shop_columns', $columns ); $columns = apply_filters( 'loop_shop_columns', $columns );
} }
return absint( $columns ); $columns = absint( $columns );
return max( 1, $columns );
} }
/** /**

View File

@ -2,8 +2,6 @@
/** /**
* WooCommerce Webhook functions * WooCommerce Webhook functions
* *
* @author Automattic
* @category Core
* @package WooCommerce/Functions * @package WooCommerce/Functions
* @version 3.3.0 * @version 3.3.0
*/ */
@ -117,11 +115,11 @@ function wc_load_webhooks() {
/** /**
* Get webhook. * Get webhook.
* *
* @param int $id Webhook ID. * @param int|WC_Webhook $id Webhook ID or object.
* @return WC_Webhook|null * @return WC_Webhook|null
*/ */
function wc_get_webhook( $id ) { function wc_get_webhook( $id ) {
$webhook = new WC_Webhook( (int) $id ); $webhook = new WC_Webhook( $id );
return 0 !== $webhook->get_id() ? $webhook : null; return 0 !== $webhook->get_id() ? $webhook : null;
} }

View File

@ -52,7 +52,7 @@ if ( $show_downloads ) {
do_action( 'woocommerce_order_details_before_order_table_items', $order ); do_action( 'woocommerce_order_details_before_order_table_items', $order );
foreach ( $order_items as $item_id => $item ) { foreach ( $order_items as $item_id => $item ) {
$product = apply_filters( 'woocommerce_order_item_product', $item->get_product(), $item ); $product = $item->get_product();
wc_get_template( 'order/order-details-item.php', array( wc_get_template( 'order/order-details-item.php', array(
'order' => $order, 'order' => $order,