diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 2d1279a053a..2aa01d358b7 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -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 diff --git a/includes/admin/class-wc-admin-post-types.php b/includes/admin/class-wc-admin-post-types.php index 562e4ddd30c..75300bd9420 100644 --- a/includes/admin/class-wc-admin-post-types.php +++ b/includes/admin/class-wc-admin-post-types.php @@ -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 ); } /** diff --git a/includes/admin/meta-boxes/class-wc-meta-box-order-downloads.php b/includes/admin/meta-boxes/class-wc-meta-box-order-downloads.php index 5ab96baf399..a1eef217a1f 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-order-downloads.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-order-downloads.php @@ -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(); } } } diff --git a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php index d346d5cab58..a9952cf236a 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-product-data.php @@ -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 ) ) { @@ -151,8 +151,9 @@ class WC_Meta_Box_Product_Data { for ( $i = 0; $i < $file_url_size; $i ++ ) { if ( ! empty( $file_urls[ $i ] ) ) { $downloads[] = array( - 'name' => wc_clean( $file_names[ $i ] ), - 'file' => wp_unslash( trim( $file_urls[ $i ] ) ), + '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 ] ), diff --git a/includes/admin/meta-boxes/views/html-product-download.php b/includes/admin/meta-boxes/views/html-product-download.php index 5bbcc023c06..1e7cade40c3 100644 --- a/includes/admin/meta-boxes/views/html-product-download.php +++ b/includes/admin/meta-boxes/views/html-product-download.php @@ -1,6 +1,9 @@ - + + + + " name="_wc_file_urls[]" value="" /> diff --git a/includes/admin/meta-boxes/views/html-product-variation-download.php b/includes/admin/meta-boxes/views/html-product-variation-download.php index 18cb2de00b9..ca8a0516b19 100644 --- a/includes/admin/meta-boxes/views/html-product-variation-download.php +++ b/includes/admin/meta-boxes/views/html-product-variation-download.php @@ -1,5 +1,8 @@ - + + + + " name="_wc_variation_file_urls[][]" value="" /> diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index 59c051f9aba..eaf30e5d914 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -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( '%s %s', wc_get_cart_url(), diff --git a/includes/class-wc-customer-download.php b/includes/class-wc-customer-download.php index 9420c7abf24..60802632377 100644 --- a/includes/class-wc-customer-download.php +++ b/includes/class-wc-customer-download.php @@ -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 ); diff --git a/includes/class-wc-data-store.php b/includes/class-wc-data-store.php index a74c8efeb4e..7753d3ba951 100644 --- a/includes/class-wc-data-store.php +++ b/includes/class-wc-data-store.php @@ -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', diff --git a/includes/class-wc-download-handler.php b/includes/class-wc-download-handler.php index f734b9aa924..3db7caa5176 100644 --- a/includes/class-wc-download-handler.php +++ b/includes/class-wc-download-handler.php @@ -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 ) ) { diff --git a/includes/class-wc-order-item-product.php b/includes/class-wc-order-item-product.php index 6b1805a2a6a..6f62bb1a456 100644 --- a/includes/class-wc-order-item-product.php +++ b/includes/class-wc-order-item-product.php @@ -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 ) { diff --git a/includes/class-wc-post-data.php b/includes/class-wc-post-data.php index b2527197d59..7d869cdfb75 100644 --- a/includes/class-wc-post-data.php +++ b/includes/class-wc-post-data.php @@ -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(); diff --git a/includes/class-wc-product-download.php b/includes/class-wc-product-download.php index 552e8e7acf4..c05dc07f32c 100644 --- a/includes/class-wc-product-download.php +++ b/includes/class-wc-product-download.php @@ -19,9 +19,10 @@ class WC_Product_Download implements ArrayAccess { * @var array */ protected $data = array( - 'id' => '', - 'name' => '', - 'file' => '', + '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 diff --git a/includes/data-stores/class-wc-customer-download-data-store.php b/includes/data-stores/class-wc-customer-download-data-store.php index dbd22e1dd92..bc69539edf9 100644 --- a/includes/data-stores/class-wc-customer-download-data-store.php +++ b/includes/data-stores/class-wc-customer-download-data-store.php @@ -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 ) ); - return array_map( array( $this, 'get_download' ), $raw_downloads ); + 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 ) ); } - - - } diff --git a/includes/data-stores/class-wc-order-data-store-cpt.php b/includes/data-stores/class-wc-order-data-store-cpt.php index 8b5635295fb..3362fbab41c 100644 --- a/includes/data-stores/class-wc-order-data-store-cpt.php +++ b/includes/data-stores/class-wc-order-data-store-cpt.php @@ -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 ) ); } } diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index 825ec2dbffb..cb128db41d6 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -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 ); } diff --git a/includes/data-stores/interfaces/wc-customer-download-data-store-interface.php b/includes/data-stores/interfaces/wc-customer-download-data-store-interface.php index 6c402281da2..010da8f1878 100644 --- a/includes/data-stores/interfaces/wc-customer-download-data-store-interface.php +++ b/includes/data-stores/interfaces/wc-customer-download-data-store-interface.php @@ -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 ); } diff --git a/includes/wc-order-functions.php b/includes/wc-order-functions.php index 38321e5e4d5..2c243b9b2c8 100644 --- a/includes/wc-order-functions.php +++ b/includes/wc-order-functions.php @@ -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' ) ); diff --git a/includes/wc-stock-functions.php b/includes/wc-stock-functions.php index 8798aa4824d..ee7025dc43d 100644 --- a/includes/wc-stock-functions.php +++ b/includes/wc-stock-functions.php @@ -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 = 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 */ diff --git a/readme.txt b/readme.txt index 2db2dc5bed9..66193c8b739 100644 --- a/readme.txt +++ b/readme.txt @@ -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).