Download update routine and interface

This commit is contained in:
Mike Jolley 2016-11-18 19:29:37 +00:00
parent 1b6d7acd24
commit b1565cf0d6
20 changed files with 303 additions and 140 deletions

View File

@ -1196,6 +1196,9 @@ class WC_Product extends WC_Abstract_Legacy_Product {
$download_object->set_id( md5( $download['file'] ) );
$download_object->set_name( $download['name'] );
$download_object->set_file( $download['file'] );
if ( isset( $download['previous_hash'] ) ) {
$download_object->set_previous_hash( $download['previous_hash'] );
}
}
// Validate the file extension

View File

@ -88,9 +88,6 @@ class WC_Admin_Post_Types {
// Meta-Box Class
include_once( dirname( __FILE__ ) . '/class-wc-admin-meta-boxes.php' );
// Download permissions
add_action( 'woocommerce_process_product_file_download_paths', array( $this, 'process_product_file_download_paths' ), 10, 3 );
// Disable DFW feature pointer
add_action( 'admin_footer', array( $this, 'disable_dfw_feature_pointer' ) );
@ -2057,58 +2054,10 @@ class WC_Admin_Post_Types {
* @param int $product_id product identifier
* @param int $variation_id optional product variation identifier
* @param array $downloadable_files newly set files
* @deprecated and moved to post-data class.
*/
public function process_product_file_download_paths( $product_id, $variation_id, $downloadable_files ) {
if ( $variation_id ) {
$product_id = $variation_id;
}
$product = wc_get_product( $product_id );
$existing_download_ids = array_keys( (array) $product->get_downloads() );
$updated_download_ids = array_keys( (array) $downloadable_files );
$new_download_ids = array_filter( array_diff( $updated_download_ids, $existing_download_ids ) );
$removed_download_ids = array_filter( array_diff( $existing_download_ids, $updated_download_ids ) );
$data_store = WC_Data_Store::load( 'customer-download' );
if ( ! empty( $new_download_ids ) || ! empty( $removed_download_ids ) ) {
// determine whether downloadable file access has been granted via the typical order completion, or via the admin ajax method
$order_ids = wc_list_pluck( $data_store->get_downloads( array(
'product_id' => $product_id,
) ), 'get_order_id' );
if ( $order_ids ) {
foreach ( $order_ids as $order_id ) {
$order = wc_get_order( $order_id );
if ( $order->get_id() ) {
// Remove permissions
if ( ! empty( $removed_download_ids ) ) {
foreach ( $removed_download_ids as $download_id ) {
if ( apply_filters( 'woocommerce_process_product_file_download_paths_remove_access_to_old_file', true, $download_id, $product_id, $order ) ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE order_id = %d AND product_id = %d AND download_id = %s", $order->get_id(), $product_id, $download_id ) );
}
}
}
// Add permissions
if ( ! empty( $new_download_ids ) ) {
foreach ( $new_download_ids as $download_id ) {
if ( apply_filters( 'woocommerce_process_product_file_download_paths_grant_access_to_new_file', true, $download_id, $product_id, $order ) ) {
// grant permission if it doesn't already exist
if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT 1=1 FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE order_id = %d AND product_id = %d AND download_id = %s", $order->get_id(), $product_id, $download_id ) ) ) {
wc_downloadable_file_permission( $download_id, $product, $order );
}
}
}
}
}
}
}
}
WC_Post_Data::process_product_file_download_paths( $product_id, $variation_id, $downloadable_files );
}
/**

View File

@ -41,7 +41,7 @@ class WC_Meta_Box_Order_Downloads {
if ( $download_permissions && sizeof( $download_permissions ) > 0 ) {
foreach ( $download_permissions as $download ) {
if ( ! $product || $product->get_id() !== $download->get_product_id() ) {
$product = wc_get_product( absint( $download->get_product_id() ) );
$product = wc_get_product( $download->get_product_id() );
$file_counter = 1;
}
@ -89,36 +89,16 @@ class WC_Meta_Box_Order_Downloads {
$downloads_remaining = $_POST['downloads_remaining'];
$access_expires = $_POST['access_expires'];
$product_ids_max = max( array_keys( $product_ids ) );
$data_store = WC_Data_Store::load( 'customer-download' );
for ( $i = 0; $i <= $product_ids_max; $i ++ ) {
if ( ! isset( $product_ids[ $i ] ) ) {
continue;
}
// @todo $download = new WC_Customer_Download( $data );
$data = array(
'downloads_remaining' => wc_clean( $downloads_remaining[ $i ] ),
);
$format = array(
'%s',
);
$expiry = ( array_key_exists( $i, $access_expires ) && '' != $access_expires[ $i ] ) ? date_i18n( 'Y-m-d', strtotime( $access_expires[ $i ] ) ) : null;
$data['access_expires'] = $expiry;
$format[] = '%s';
$wpdb->update( $wpdb->prefix . "woocommerce_downloadable_product_permissions",
$data,
array(
'order_id' => $post_id,
'product_id' => absint( $product_ids[ $i ] ),
'download_id' => wc_clean( $download_ids[ $i ] ),
),
$format, array( '%d', '%d', '%s' )
);
$download = $data_store::get_download_from_order( $post_id, absint( $product_ids[ $i ] ), $download_ids[ $i ] );
$download->set_downloads_remaining( wc_clean( $downloads_remaining[ $i ] ) );
$download->set_access_expires( array_key_exists( $i, $access_expires ) && '' !== $access_expires[ $i ] ? strtotime( $access_expires[ $i ] ) : '' );
$download->save();
}
}
}

View File

@ -142,7 +142,7 @@ class WC_Meta_Box_Product_Data {
* Prepare downloads for save.
* @return array
*/
private static function prepare_downloads( $file_names, $file_urls ) {
private static function prepare_downloads( $file_names, $file_urls, $file_hashes ) {
$downloads = array();
if ( ! empty( $file_urls ) ) {
@ -153,6 +153,7 @@ class WC_Meta_Box_Product_Data {
$downloads[] = array(
'name' => wc_clean( $file_names[ $i ] ),
'file' => wp_unslash( trim( $file_urls[ $i ] ) ),
'previous_hash' => wc_clean( $file_hashes[ $i ] ),
);
}
}
@ -286,7 +287,11 @@ class WC_Meta_Box_Product_Data {
'stock_quantity' => wc_stock_amount( $_POST['_stock'] ),
'download_limit' => '' === $_POST['_download_limit'] ? '' : absint( $_POST['_download_limit'] ),
'download_expiry' => '' === $_POST['_download_expiry'] ? '' : absint( $_POST['_download_expiry'] ),
'downloads' => self::prepare_downloads( isset( $_POST['_wc_file_names'] ) ? $_POST['_wc_file_names'] : array(), isset( $_POST['_wc_file_urls'] ) ? $_POST['_wc_file_urls'] : array() ),
'downloads' => self::prepare_downloads(
isset( $_POST['_wc_file_names'] ) ? $_POST['_wc_file_names'] : array(),
isset( $_POST['_wc_file_urls'] ) ? $_POST['_wc_file_urls'] : array(),
isset( $_POST['_wc_file_hashes'] ) ? $_POST['_wc_file_hashes'] : array()
),
'product_url' => esc_url_raw( $_POST['_product_url'] ),
'button_text' => wc_clean( $_POST['_button_text'] ),
'children' => 'grouped' === $product_type ? self::prepare_children() : null,
@ -334,7 +339,11 @@ class WC_Meta_Box_Product_Data {
'description' => wp_kses_post( wc_sanitize_textarea( $_POST['variable_description'][ $i ] ) ),
'download_limit' => wc_clean( $_POST['variable_download_limit'][ $i ] ),
'download_expiry' => wc_clean( $_POST['variable_download_expiry'][ $i ] ),
'downloads' => self::prepare_downloads( isset( $_POST['_wc_variation_file_names'][ $variation_id ] ) ? $_POST['_wc_variation_file_names'][ $variation_id ] : array(), isset( $_POST['_wc_variation_file_urls'][ $variation_id ] ) ? $_POST['_wc_variation_file_urls'][ $variation_id ] : array() ),
'downloads' => self::prepare_downloads(
isset( $_POST['_wc_variation_file_names'][ $variation_id ] ) ? $_POST['_wc_variation_file_names'][ $variation_id ] : array(),
isset( $_POST['_wc_variation_file_urls'][ $variation_id ] ) ? $_POST['_wc_variation_file_urls'][ $variation_id ] : array(),
isset( $_POST['_wc_variation_file_hashes'][ $variation_id ] ) ? $_POST['_wc_variation_file_hashes'][ $variation_id ] : array()
),
'manage_stock' => isset( $_POST['variable_manage_stock'][ $i ] ),
'stock_quantity' => wc_clean( $_POST['variable_stock'][ $i ] ),
'backorders' => wc_clean( $_POST['variable_backorders'][ $i ] ),

View File

@ -1,6 +1,9 @@
<tr>
<td class="sort"></td>
<td class="file_name"><input type="text" class="input_text" placeholder="<?php esc_attr_e( 'File name', 'woocommerce' ); ?>" name="_wc_file_names[]" value="<?php echo esc_attr( $file['name'] ); ?>" /></td>
<td class="file_name">
<input type="text" class="input_text" placeholder="<?php esc_attr_e( 'File name', 'woocommerce' ); ?>" name="_wc_file_names[]" value="<?php echo esc_attr( $file['name'] ); ?>" />
<input type="hidden" name="_wc_file_hashes[]" value="<?php echo esc_attr( $key ); ?>" />
</td>
<td class="file_url"><input type="text" class="input_text" placeholder="<?php esc_attr_e( "http://", 'woocommerce' ); ?>" name="_wc_file_urls[]" value="<?php echo esc_attr( $file['file'] ); ?>" /></td>
<td class="file_url_choose" width="1%"><a href="#" class="button upload_file_button" data-choose="<?php esc_attr_e( 'Choose file', 'woocommerce' ); ?>" data-update="<?php esc_attr_e( 'Insert file URL', 'woocommerce' ); ?>"><?php echo str_replace( ' ', '&nbsp;', __( 'Choose file', 'woocommerce' ) ); ?></a></td>
<td width="1%"><a href="#" class="delete"><?php _e( 'Delete', 'woocommerce' ); ?></a></td>

View File

@ -1,5 +1,8 @@
<tr>
<td class="file_name"><input type="text" class="input_text" placeholder="<?php esc_attr_e( 'File name', 'woocommerce' ); ?>" name="_wc_variation_file_names[<?php echo $variation_id; ?>][]" value="<?php echo esc_attr( $file['name'] ); ?>" /></td>
<td class="file_name">
<input type="text" class="input_text" placeholder="<?php esc_attr_e( 'File name', 'woocommerce' ); ?>" name="_wc_variation_file_names[<?php echo $variation_id; ?>][]" value="<?php echo esc_attr( $file['name'] ); ?>" />
<input type="hidden" name="_wc_variation_file_hashes[]" value="<?php echo esc_attr( $key ); ?>" />
</td>
<td class="file_url"><input type="text" class="input_text" placeholder="<?php esc_attr_e( "http://", 'woocommerce' ); ?>" name="_wc_variation_file_urls[<?php echo $variation_id; ?>][]" value="<?php echo esc_attr( $file['file'] ); ?>" /></td>
<td class="file_url_choose" width="1%"><a href="#" class="button upload_file_button" data-choose="<?php esc_attr_e( 'Choose file', 'woocommerce' ); ?>" data-update="<?php esc_attr_e( 'Insert file URL', 'woocommerce' ); ?>"><?php echo str_replace( ' ', '&nbsp;', __( 'Choose file', 'woocommerce' ) ); ?></a></td>
<td width="1%"><a href="#" class="delete"><?php _e( 'Delete', 'woocommerce' ); ?></a></td>

View File

@ -936,7 +936,7 @@ class WC_Cart {
if ( $product_data->managing_stock() ) {
$products_qty_in_cart = $this->get_cart_item_quantities();
if ( ! $product_data->has_enough_stock( $products_qty_in_cart[ $product_data->get_stock_managed_by_id() ] + $quantity ) ) {
if ( isset( $products_qty_in_cart[ $product_data->get_stock_managed_by_id() ] ) && ! $product_data->has_enough_stock( $products_qty_in_cart[ $product_data->get_stock_managed_by_id() ] + $quantity ) ) {
throw new Exception( sprintf(
'<a href="%s" class="button wc-forward">%s</a> %s',
wc_get_cart_url(),

View File

@ -38,15 +38,15 @@ class WC_Customer_Download extends WC_Data implements ArrayAccess {
* @param int|object|array $download
*/
public function __construct( $download = 0 ) {
parent::__construct( $order );
parent::__construct( $download );
if ( is_numeric( $download ) && $download > 0 ) {
$this->set_id( $download );
} elseif ( $download instanceof self ) {
$this->set_id( absint( $download->get_id() ) );
} elseif ( is_array( $download ) && $download['permission_id'] ) {
$this->set_id( absint( $download['permission_id'] ) );
$this->set_props( $download );
} elseif ( is_object( $download ) && ! empty( $download->permission_id ) ) {
$this->set_id( $download->permission_id );
$this->set_props( (array) $download );
$this->set_object_read( true );
} else {
$this->set_object_read( true );

View File

@ -30,7 +30,7 @@ class WC_Data_Store {
private $stores = array(
'coupon' => 'WC_Coupon_Data_Store_CPT',
'customer' => 'WC_Customer_Data_Store',
'customer-download' => 'WC_Customer_Download_Data_Store_Session',
'customer-download' => 'WC_Customer_Download_Data_Store',
'customer-session' => 'WC_Customer_Data_Store_Session',
'order' => 'WC_Order_Data_Store_CPT',
'order-refund' => 'WC_Order_Refund_Data_Store_CPT',

View File

@ -41,7 +41,7 @@ class WC_Download_Handler {
self::download_error( __( 'Invalid download link.', 'woocommerce' ) );
}
$download_ids = $data_store->get_download_ids( array(
$download_ids = $data_store->get_downloads( array(
'user_email' => sanitize_email( str_replace( ' ', '+', $_GET['email'] ) ),
'order_key' => wc_clean( $_GET['order'] ),
'product_id' => $product_id,
@ -49,6 +49,7 @@ class WC_Download_Handler {
'orderby' => 'downloads_remaining',
'order' => 'DESC',
'limit' => 1,
'return' => 'ids',
) );
if ( empty( $download_ids ) ) {

View File

@ -188,10 +188,11 @@ class WC_Order_Item_Product extends WC_Order_Item {
if ( $product && $order && $product->is_downloadable() && $order->is_download_permitted() ) {
$data_store = WC_Data_Store::load( 'customer-download' );
$download_ids = $data_store->get_download_ids( array(
$download_ids = $data_store->get_downloads( array(
'user_email' => $order->get_billing_email(),
'order_key' => $order->get_order_key(),
'product_id' => $this->get_variation_id() ? $this->get_variation_id() : $this->get_product_id(),
'return' => 'ids',
) );
foreach ( $download_ids as $download_id ) {

View File

@ -47,6 +47,9 @@ class WC_Post_Data {
add_action( 'untrashed_post', array( __CLASS__, 'untrash_post' ) );
add_action( 'before_delete_post', array( __CLASS__, 'delete_order_items' ) );
add_action( 'before_delete_post', array( __CLASS__, 'delete_order_downloadable_permissions' ) );
// Download permissions
add_action( 'woocommerce_process_product_file_download_paths', array( __CLASS__, 'process_product_file_download_paths' ), 10, 3 );
}
/**
@ -375,6 +378,32 @@ class WC_Post_Data {
do_action( 'woocommerce_deleted_order_downloadable_permissions', $postid );
}
}
/**
* Update changed downloads.
*
* @param int $product_id product identifier
* @param int $variation_id optional product variation identifier
* @param array $downloads newly set files
*/
public static function process_product_file_download_paths( $product_id, $variation_id, $downloads ) {
if ( $variation_id ) {
$product_id = $variation_id;
}
$product = wc_get_product( $product_id );
$data_store = WC_Data_Store::load( 'customer-download' );
if ( $downloads ) {
foreach ( $downloads as $download ) {
$new_hash = md5( $download->get_file() );
if ( $download->get_previous_hash() && $download->get_previous_hash() !== $new_hash ) {
// Update permissions.
$data_store->update_download_id( $product_id, $download->get_previous_hash(), $new_hash );
}
}
}
}
}
WC_Post_Data::init();

View File

@ -22,6 +22,7 @@ class WC_Product_Download implements ArrayAccess {
'id' => '',
'name' => '',
'file' => '',
'previous_hash' => '',
);
/**
@ -122,6 +123,14 @@ class WC_Product_Download implements ArrayAccess {
$this->data['name'] = wc_clean( $value );
}
/**
* Set previous_hash.
* @param string $value
*/
public function set_previous_hash( $value ) {
$this->data['previous_hash'] = wc_clean( $value );
}
/**
* Set file.
* @param string $value
@ -159,6 +168,14 @@ class WC_Product_Download implements ArrayAccess {
return $this->data['name'];
}
/**
* Get previous_hash.
* @return string
*/
public function get_previous_hash() {
return $this->data['previous_hash'];
}
/**
* Get file.
* @return string

View File

@ -182,22 +182,40 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
return new WC_Customer_Download( $data );
}
/**
* Get a download object from an order.
*
* @param int $order_id
* @param int $product_id
* @param string $download_id
* @return WC_Customer_Download
*/
private function get_download_from_order( $order_id, $product_id, $download_id ) {
global $wpdb;
$raw_download = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE order_id = %d AND product_id = %d AND download_id = %s", $order_id, $product_id, $download_id ) );
return $raw_download ? new WC_Customer_Download( $raw_download ) : false;
}
/**
* Get array of download ids by specified args.
*
* @param array $args
* @return array of WC_Customer_Download
* @return array
*/
public function get_downloads( $args = array() ) {
global $wpdb;
$args = wp_parse_args( $args, array(
'user_email' => '',
'order_id' => '',
'order_key' => '',
'product_id' => '',
'orderby' => 'permission_id',
'order' => 'DESC',
'limit' => -1,
'return' => 'objects',
) );
extract( $args );
@ -209,6 +227,10 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
$query[] = $wpdb->prepare( "AND user_email = %s", $user_email );
}
if ( $order_id ) {
$query[] = $wpdb->prepare( "AND order_id = %d", $order_id );
}
if ( $order_key ) {
$query[] = $wpdb->prepare( "AND order_key = %s", $order_key );
}
@ -221,23 +243,45 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
$order = esc_sql( $order );
$query[] = "ORDER BY {$orderby} {$order}";
if ( $limit ) {
if ( 0 < $limit ) {
$query[] = $wpdb->prepare( "LIMIT %d", $limit );
}
$raw_downloads = $wpdb->get_results( implode( ' ', $query ) );
switch ( $return ) {
case 'ids' :
return wp_list_pluck( $raw_downloads, 'permission_id' );
default :
return array_map( array( $this, 'get_download' ), $raw_downloads );
}
}
/**
* Update download ids if the hash changes.
*
* @param int $product_id
* @param string $old_id
* @param string $new_idn
*/
public function update_download_id( $product_id, $old_id, $new_id ) {
global $wpdb;
//@todo get_download_ids
$wpdb->update(
$wpdb->prefix . 'woocommerce_downloadable_product_permissions',
array(
'download_id' => $new_id,
),
array(
'download_id' => $old_id,
'product_id' => $product_id,
)
);
}
/**
* Get a customers downloads.
*
* @param int $customer_id
* @return array
*/
@ -265,9 +309,16 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
$customer_id,
date( 'Y-m-d', current_time( 'timestamp' ) )
)
)
);
}
/**
* Update user prop for downloads based on order id.
*
* @param int $order_id
* @param int $customer_id
* @param string $email
*/
public function update_user_by_order_id( $order_id, $customer_id, $email ) {
global $wpdb;
$wpdb->update( $wpdb->prefix . 'woocommerce_downloadable_product_permissions',
@ -287,7 +338,4 @@ class WC_Customer_Download_Data_Store implements WC_Customer_Download_Data_Store
)
);
}
}

View File

@ -249,6 +249,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
/**
* Return count of orders with type.
*
* @param string $type
* @return int
*/
@ -324,6 +325,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
/**
* Generate meta query for wc_get_orders.
*
* @param array $values
* @param string $relation
* @return array
@ -367,6 +369,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
/**
* Get unpaid orders after a certain date,
*
* @param int timestamp $date
* @return array
*/
@ -386,6 +389,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
/**
* Search order data for a term and return ids.
*
* @param string $term
* @return array of ids
*/
@ -433,55 +437,121 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
/**
* Gets information about whether permissions were generated yet.
* @param WC_Order $order
*
* @param WC_Order|int $order
* @return boolet
*/
public function get_download_permissions_granted( $order ) {
return wc_string_to_bool( get_post_meta( $order->get_id(), '_download_permissions_granted', true ) );
if ( is_a( $order, 'WC_Order' ) ) {
$order_id = $order->get_id();
} else {
$order_id = $order;
}
return wc_string_to_bool( get_post_meta( $order_id, '_download_permissions_granted', true ) );
}
/**
* Stores information about whether permissions were generated yet.
* @param WC_Order $order
*
* @param WC_Order|int $order
* @param bool $set
*/
public function set_download_permissions_granted( $order, $set ) {
update_post_meta( $order->get_id(), '_download_permissions_granted', wc_bool_to_string( $set ) );
if ( is_a( $order, 'WC_Order' ) ) {
$order_id = $order->get_id();
} else {
$order_id = $order;
}
update_post_meta( $order_id, '_download_permissions_granted', wc_bool_to_string( $set ) );
}
/**
* Gets information about whether sales were recorded.
* @param WC_Order $order
*
* @param WC_Order|int $order
* @return boolet
*/
public function get_recorded_sales( $order ) {
return wc_string_to_bool( get_post_meta( $order->get_id(), '_recorded_sales', true ) );
if ( is_a( $order, 'WC_Order' ) ) {
$order_id = $order->get_id();
} else {
$order_id = $order;
}
return wc_string_to_bool( get_post_meta( $order_id, '_recorded_sales', true ) );
}
/**
* Stores information about whether sales were recorded.
* @param WC_Order $order
*
* @param WC_Order|int $order
* @param bool $set
*/
public function set_recorded_sales( $order, $set ) {
update_post_meta( $order->get_id(), '_recorded_sales', wc_bool_to_string( $set ) );
if ( is_a( $order, 'WC_Order' ) ) {
$order_id = $order->get_id();
} else {
$order_id = $order;
}
update_post_meta( $order_id, '_recorded_sales', wc_bool_to_string( $set ) );
}
/**
* Gets information about whether coupon counts were updated.
* @param WC_Order $order
*
* @param WC_Order|int $order
* @return boolet
*/
public function get_recorded_coupon_usage_counts( $order ) {
return wc_string_to_bool( get_post_meta( $order->get_id(), '_recorded_sales', true ) );
if ( is_a( $order, 'WC_Order' ) ) {
$order_id = $order->get_id();
} else {
$order_id = $order;
}
return wc_string_to_bool( get_post_meta( $order_id, '_recorded_sales', true ) );
}
/**
* Stores information about whether coupon counts were updated.
* @param WC_Order $order
*
* @param WC_Order|int $order
* @param bool $set
*/
public function set_recorded_coupon_usage_counts( $order, $set ) {
update_post_meta( $order->get_id(), '_recorded_sales', wc_bool_to_string( $set ) );
if ( is_a( $order, 'WC_Order' ) ) {
$order_id = $order->get_id();
} else {
$order_id = $order;
}
update_post_meta( $order_id, '_recorded_sales', wc_bool_to_string( $set ) );
}
/**
* Gets information about whether stock was reduced.
*
* @param WC_Order|int $order
* @return boolet
*/
public function get_stock_reduced( $order ) {
if ( is_a( $order, 'WC_Order' ) ) {
$order_id = $order->get_id();
} else {
$order_id = $order;
}
return wc_string_to_bool( get_post_meta( $order_id, '_order_stock_reduced', true ) );
}
/**
* Stores information about whether stock was reduced.
*
* @param WC_Order|int $order
* @param bool $set
*/
public function set_stock_reduced( $order, $set ) {
if ( is_a( $order, 'WC_Order' ) ) {
$order_id = $order->get_id();
} else {
$order_id = $order;
}
update_post_meta( $order_id, '_order_stock_reduced', wc_bool_to_string( $set ) );
}
}

View File

@ -320,7 +320,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_D
'_download_expiry' => 'download_expiry',
'_featured' => 'featured',
'_thumbnail_id' => 'image_id',
'_downloadable_files' => 'downloads',
'_stock' => 'stock_quantity',
'_stock_status' => 'stock_status',
'_wc_average_rating' => 'average_rating',
@ -344,15 +343,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_D
case 'gallery_image_ids' :
$updated = update_post_meta( $product->get_id(), $meta_key, implode( ',', $value ) );
break;
case 'downloads' :
// grant permission to any newly added files on any existing orders for this product prior to saving.
if ( $product->is_type( 'variation' ) ) {
do_action( 'woocommerce_process_product_file_download_paths', $product->get_parent_id(), $product->get_id(), $value );
} else {
do_action( 'woocommerce_process_product_file_download_paths', $product->get_id(), 0, $value );
}
$updated = update_post_meta( $product->get_id(), $meta_key, $value );
break;
case 'image_id' :
if ( ! empty( $value ) ) {
set_post_thumbnail( $product->get_id(), $value );
@ -477,6 +467,13 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_D
$meta_values[ $key ] = $download->get_data();
}
}
if ( $product->is_type( 'variation' ) ) {
do_action( 'woocommerce_process_product_file_download_paths', $product->get_parent_id(), $product->get_id(), $downloads );
} else {
do_action( 'woocommerce_process_product_file_download_paths', $product->get_id(), 0, $downloads );
}
update_post_meta( $product->get_id(), '_downloadable_files', $meta_values );
}

View File

@ -11,4 +11,59 @@ if ( ! defined( 'ABSPATH' ) ) {
* @author WooThemes
*/
interface WC_Customer_Download_Data_Store_Interface {
/**
* Method to delete a download permission from the database by ID.
*
* @param int $id
*/
public function delete_by_id( $id );
/**
* Method to delete a download permission from the database by order ID.
*
* @param int $id
*/
public function delete_by_order_id( $id );
/**
* Method to delete a download permission from the database by download ID.
*
* @param int $id
*/
public function delete_by_download_id( $id );
/**
* Get array of download ids by specified args.
*
* @param array $args
* @return array of WC_Customer_Download
*/
public function get_downloads( $args = array() );
/**
* Update download ids if the hash changes.
*
* @param int $product_id
* @param string $old_id
* @param string $new_idn
*/
public function update_download_id( $product_id, $old_id, $new_id );
/**
* Get a customers downloads.
*
* @param int $customer_id
* @return array
*/
public function get_downloads_for_customer( $customer_id );
/**
* Update user prop for downloads based on order id.
*
* @param int $order_id
* @param int $customer_id
* @param string $email
*/
public function update_user_by_order_id( $order_id, $customer_id, $email );
}

View File

@ -364,7 +364,7 @@ function wc_downloadable_file_permission( $download_id, $product, $order, $qty =
$download->set_product_id( $product->get_id() );
$download->set_user_id( $order->get_customer_id() );
$download->set_order_id( $order->get_id() );
$download->set_user_email();
$download->set_user_email( $order->get_billing_email() );
$download->set_order_key( $order->get_order_key() );
$download->set_downloads_remaining( 0 > $product->get_download_limit() ? '' : $product->get_download_limit() * $qty );
$download->set_access_granted( current_time( 'timestamp' ) );

View File

@ -29,15 +29,13 @@ function wc_update_product_stock( $product, $stock_quantity = null, $operation =
if ( ! is_null( $stock_quantity ) && $product->managing_stock() ) {
// Some products (variations) can have their stock managed by their parent. Get the correct ID to reduce here.
$product_id_with_stock = $product->get_stock_managed_by_id();
$product_with_stock = $product->get_id() !== $product_id_with_stock ? wc_get_product( $product_id_with_stock ) : $product;
$data_store = WC_Data_Store::load( 'product' );
$data_store->update_product_stock( $product_id_with_stock, $stock_quantity, $operation );
delete_transient( 'wc_low_stock_count' );
delete_transient( 'wc_outofstock_count' );
// Re-read product data after updating stock, then have stock status calculated and saved.
$product_with_stock = wc_get_product( $product_with_stock->get_id() );
$product_with_stock = wc_get_product( $product_id_with_stock );
$product_with_stock->set_stock_status();
$product_with_stock->save();
@ -68,9 +66,10 @@ function wc_update_product_stock_status( $product_id, $status ) {
* @param int $order_id
*/
function wc_maybe_reduce_stock_levels( $order_id ) {
if ( apply_filters( 'woocommerce_payment_complete_reduce_order_stock', ! get_post_meta( $order_id, '_order_stock_reduced', true ), $order_id ) ) {
$data_store = WC_Data_Store::load( 'order' );
if ( apply_filters( 'woocommerce_payment_complete_reduce_order_stock', ! $data_store->get_stock_reduced( $order_id ), $order_id ) ) {
wc_reduce_stock_levels( $order_id );
add_post_meta( $order_id, '_order_stock_reduced', '1', true );
$data_store->set_stock_reduced( $order_id, true );
}
}
add_action( 'woocommerce_payment_complete', 'wc_maybe_reduce_stock_levels' );
@ -88,7 +87,7 @@ function wc_reduce_stock_levels( $order_id ) {
if ( $item->is_type( 'line_item' ) && ( $product = $item->get_product() ) && $product->managing_stock() ) {
$qty = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $order, $item );
$item_name = $product->get_formatted_name();
$new_stock = wc_update_product_stock( $product, $qty, 'subtract' );
$new_stock = wc_update_product_stock( $product, $qty, 'decrease' );
if ( ! is_wp_error( $new_stock ) ) {
/* translators: 1: item name 2: old stock quantity 3: new stock quantity */

View File

@ -189,8 +189,7 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woocommerce/wo
* CRUD - Optimised variable product sync. Upper/lower price meta is no longer stored, just the main prices, if a child has weight, and if a child has dimensions.
* CRUD - Removed WP_Query from up-sells.php and related.php and replaced with PHP foreach loop (since we already have the product IDs).
* CRUD - Optimised variable product sync. Upper/lower price meta is no longer stored, just the main prices, if a child has weight, and if a child has dimensions.
* CRUD - Removed WP_Query from up-sells.php and related.php and replaced with PHP foreach loop (since we already have the product IDs).
* Performance - Removed the feature where old orders get access to new downloads on product edit. Looping potentially thousands of orders to do this is too much of a performance burden for stores and this can sometimes be unexpected behavior too. This does however updates *edited* downloads.
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/CHANGELOG.txt).