2022-11-27 15:03:07 +00:00
< ? php
/**
* Download Functions
*
* @ package EDD
* @ subpackage Functions
* @ copyright Copyright ( c ) 2018 , Easy Digital Downloads , LLC
* @ license http :// opensource . org / licenses / gpl - 2.0 . php GNU Public License
* @ since 1.0
*/
// Exit if accessed directly
defined ( 'ABSPATH' ) || exit ;
/**
* Retrieve a download by a given field .
*
* @ since 2.0
*
* @ param string $field Field to retrieve the download with .
* @ param mixed $value Value of the row .
*
* @ return WP_Post | false WP_Post object if download found , false otherwise .
*/
function edd_get_download_by ( $field = '' , $value = '' ) {
// Bail if empty values passed.
if ( empty ( $field ) || empty ( $value ) ) {
return false ;
}
switch ( strtolower ( $field ) ) {
case 'id' :
$download = get_post ( $value );
if ( 'download' !== get_post_type ( $download ) ) {
return false ;
}
break ;
case 'slug' :
case 'name' :
$download = get_posts ( array (
'post_type' => 'download' ,
'name' => $value ,
'posts_per_page' => 1 ,
'post_status' => 'any' ,
) );
if ( $download ) {
$download = $download [ 0 ];
}
break ;
case 'sku' :
$download = get_posts ( array (
'post_type' => 'download' ,
'meta_key' => 'edd_sku' ,
'meta_value' => $value ,
'posts_per_page' => 1 ,
'post_status' => 'any' ,
) );
if ( $download ) {
$download = $download [ 0 ];
}
break ;
default :
return false ;
}
return $download ? : false ;
}
/**
* Retrieves a download post object by ID or slug .
*
* @ since 1.0
* @ since 2.9 - Return an EDD_Download object .
*
* @ param int $download_id Download ID .
* @ return EDD_Download | null EDD_Download object if found , null otherwise .
*/
function edd_get_download ( $download_id = 0 ) {
$download = null ;
if ( is_numeric ( $download_id ) ) {
$found_download = new EDD_Download ( $download_id );
if ( ! empty ( $found_download -> ID ) ) {
$download = $found_download ;
}
// Fetch download by name.
} else {
$args = array (
'post_type' => 'download' ,
'name' => $download_id ,
'post_per_page' => 1 ,
'fields' => 'ids' ,
);
$downloads = new WP_Query ( $args );
if ( is_array ( $downloads -> posts ) && ! empty ( $downloads -> posts ) ) {
$download_id = $downloads -> posts [ 0 ];
$download = new EDD_Download ( $download_id );
}
}
return $download ;
}
/**
* Checks whether or not a download is free .
*
* @ since 2.1
*
* @ param int $download_id Download ID .
* @ param int $price_id Optional . Price ID .
* @ return bool $is_free True if the product is free , false if the product is not free or the check fails
*/
function edd_is_free_download ( $download_id = 0 , $price_id = false ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> is_free ( $price_id )
: false ;
}
/**
* Return the name of a download .
*
* Pass a price ID to append the specific price variation name .
*
* @ since 3.0
*
* @ param int $download_id
* @ param int | null $price_id
*
* @ return false | string
*/
function edd_get_download_name ( $download_id = 0 , $price_id = null ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) || ! is_numeric ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
// Bail if the download cannot be retrieved.
if ( ! $download instanceof EDD_Download ) {
return false ;
}
// Get the download title
$retval = $download -> get_name ();
// Check for variable pricing
if ( $download -> has_variable_prices () && is_numeric ( $price_id ) ) {
// Check for price option name
$price_name = edd_get_price_option_name ( $download_id , $price_id );
// Product has prices
if ( ! empty ( $price_name ) ) {
$retval .= ' — ' . $price_name ;
}
}
/**
* Override the download name .
*
* @ since 3.0
*
* @ param string $retval The download name .
* @ param int $id The download ID .
* @ param int $price_id The price ID , if any .
*/
return apply_filters ( 'edd_get_download_name' , $retval , $download_id , $price_id );
}
/**
* Returns the price of a download , but only for non - variable priced downloads .
*
* @ since 1.0
*
* @ param int $download_id Download ID .
* @ return string | int Price of the download .
*/
function edd_get_download_price ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> get_price ()
: 0 ;
}
/**
* Displays a formatted price for a download .
*
* @ since 1.0
*
* @ param int $download_id Download ID .
* @ param bool $echo Optional . Whether to echo or return the result . Default true .
* @ param int $price_id Optional . Price ID .
*
* @ return string Download price if $echo set to false .
*/
function edd_price ( $download_id = 0 , $echo = true , $price_id = false ) {
// Attempt to get the ID of the current item in the WordPress loop.
if ( empty ( $download_id ) || ! is_numeric ( $download_id ) ) {
$download_id = get_the_ID ();
}
// Variable prices
if ( edd_has_variable_prices ( $download_id ) ) {
// Get the price variations
$prices = edd_get_variable_prices ( $download_id );
// Use the amount for the price ID
if ( is_numeric ( $price_id ) && isset ( $prices [ $price_id ] ) ) {
$price = edd_get_price_option_amount ( $download_id , $price_id );
// Maybe use the default variable price
} elseif ( $default = edd_get_default_variable_price ( $download_id ) ) {
$price = edd_get_price_option_amount ( $download_id , $default );
// Maybe guess the lowest price
} else {
$price = edd_get_lowest_price_option ( $download_id );
}
// Single price (not variable)
} else {
$price = edd_get_download_price ( $download_id );
}
// Filter the price (already sanitized)
$price = apply_filters ( 'edd_download_price' , $price , $download_id , $price_id );
// Format the price (do not escape $price)
$formatted_price = '<span class="edd_price" id="edd_price_' . esc_attr ( $download_id ) . '">' . $price . '</span>' ;
$formatted_price = apply_filters ( 'edd_download_price_after_html' , $formatted_price , $download_id , $price , $price_id );
// Echo or return
if ( ! empty ( $echo ) ) {
echo $formatted_price ; // WPCS: XSS ok.
} else {
return $formatted_price ;
}
}
add_filter ( 'edd_download_price' , 'edd_format_amount' , 10 );
add_filter ( 'edd_download_price' , 'edd_currency_filter' , 20 );
/**
* Retrieves the final price of a downloadable product after purchase .
* This price includes any necessary discounts that were applied
*
* @ since 1.0
* @ param int $download_id ID of the download
* @ param array $user_purchase_info - an array of all information for the payment
* @ param string $amount_override a custom amount that over rides the 'edd_price' meta , used for variable prices
* @ return string - the price of the download
*/
function edd_get_download_final_price ( $download_id , $user_purchase_info , $amount_override = null ) {
if ( is_null ( $amount_override ) ) {
$original_price = get_post_meta ( $download_id , 'edd_price' , true );
} else {
$original_price = $amount_override ;
}
if ( isset ( $user_purchase_info [ 'discount' ] ) && 'none' !== $user_purchase_info [ 'discount' ] ) {
// If the discount was a percentage, we modify the amount.
// Flat rate discounts are ignored
if ( EDD_Discount :: FLAT !== edd_get_discount_type ( edd_get_discount_id_by_code ( $user_purchase_info [ 'discount' ] ) ) ) {
$price = edd_get_discounted_amount ( $user_purchase_info [ 'discount' ], $original_price );
} else {
$price = $original_price ;
}
} else {
$price = $original_price ;
}
// Filter & return.
return apply_filters ( 'edd_final_price' , $price , $download_id , $user_purchase_info );
}
/**
* Retrieves the variable prices for a download .
*
* @ since 1.2
*
* @ param int $download_id Download ID .
* @ return array | false Variable prices if found , false otherwise .
*/
function edd_get_variable_prices ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> get_prices ()
: false ;
}
/**
* Checks to see if a download has variable prices enabled .
*
* @ since 1.0 . 7
*
* @ param int $download_id Download ID .
* @ return bool True if the download has variable prices , false otherwise .
*/
function edd_has_variable_prices ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = new EDD_Download ( $download_id );
return $download
? $download -> has_variable_prices ()
: false ;
}
/**
* Returns the default price ID for variable pricing , or the first price if
* none set .
*
* @ since 2.2
*
* @ param int $download_id Download ID .
* @ return int Price ID .
*/
function edd_get_default_variable_price ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
// Bail if download has no variable prices.
if ( ! edd_has_variable_prices ( $download_id ) ) {
return false ;
}
$prices = edd_get_variable_prices ( $download_id );
$default_price_id = get_post_meta ( $download_id , '_edd_default_price_id' , true );
if ( '' === $default_price_id || ! isset ( $prices [ $default_price_id ] ) ) {
$default_price_id = current ( array_keys ( $prices ) );
}
// Filter & return.
return apply_filters ( 'edd_variable_default_price_id' , absint ( $default_price_id ), $download_id );
}
/**
* Retrieves the name of a variable price option .
*
* @ since 1.0 . 9
* @ since 3.0 Renamed $payment_id parameter to $order_id .
*
* @ param int $download_id Download ID .
* @ param int $price_id Price ID .
* @ param int $order_id Optional . Order ID for use in filters .
*
* @ return string $price_name Name of the price option .
*/
function edd_get_price_option_name ( $download_id = 0 , $price_id = 0 , $order_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
// Fetch variable prices.
$prices = edd_get_variable_prices ( $download_id );
$price_name = '' ;
if ( $prices && is_array ( $prices ) ) {
if ( isset ( $prices [ $price_id ] ) ) {
$price_name = $prices [ $price_id ][ 'name' ];
}
}
return apply_filters ( 'edd_get_price_option_name' , $price_name , $download_id , $order_id , $price_id );
}
/**
* Retrieves the amount for a variable price option .
*
* @ since 1.8 . 2
*
* @ param int $download_id Download ID .
* @ param int $price_id Price ID .
*
* @ return float $amount Price option amount .
*/
function edd_get_price_option_amount ( $download_id = 0 , $price_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
// Fetch variable prices.
$prices = edd_get_variable_prices ( $download_id );
// Set default prices.
$amount = 0.00 ;
if ( $prices && is_array ( $prices ) ) {
if ( isset ( $prices [ $price_id ] ) ) {
$amount = $prices [ $price_id ][ 'amount' ];
}
}
// Filter & return.
return apply_filters ( 'edd_get_price_option_amount' , edd_sanitize_amount ( $amount ), $download_id , $price_id );
}
/**
* Retrieve the lowest price option of a variable priced download .
*
* @ since 1.4 . 4
*
* @ param int $download_id Download ID .
* @ return float Amount of the lowest price option .
*/
function edd_get_lowest_price_option ( $download_id = 0 ) {
// Attempt to get the ID of the current item in the WordPress loop.
if ( empty ( $download_id ) ) {
$download_id = get_the_ID ();
}
// Bail if download ID is still empty.
if ( empty ( $download_id ) ) {
return false ;
}
// Return download price if variable prices do not exist for download.
if ( ! edd_has_variable_prices ( $download_id ) ) {
return edd_get_download_price ( $download_id );
}
// Fetch variables prices.
$prices = edd_get_variable_prices ( $download_id );
// Set lowest to 0.
$lowest = 0.00 ;
// Loop through all the prices.
if ( ! empty ( $prices ) ) {
foreach ( $prices as $key => $price ) {
// Skip if amount doesn't exist.
if ( empty ( $price [ 'amount' ] ) ) {
continue ;
}
if ( ! isset ( $min ) ) {
$min = $price [ 'amount' ];
} else {
$min = min ( $min , $price [ 'amount' ] );
}
if ( $price [ 'amount' ] == $min ) {
$min_id = $key ;
}
}
$lowest = $prices [ $min_id ][ 'amount' ];
}
return edd_sanitize_amount ( $lowest );
}
/**
* Retrieves the ID for the cheapest price option of a variable priced download .
*
* @ since 2.2
*
* @ param int $download_id Download ID .
* @ return int | false ID of the lowest price , false if download does not exist .
*/
function edd_get_lowest_price_id ( $download_id = 0 ) {
// Attempt to get the ID of the current item in the WordPress loop.
if ( empty ( $download_id ) ) {
$download_id = get_the_ID ();
}
// Bail if download ID is still empty.
if ( empty ( $download_id ) ) {
return false ;
}
// Return download price if variable prices do not exist for download.
if ( ! edd_has_variable_prices ( $download_id ) ) {
return edd_get_download_price ( $download_id );
}
// Fetch variable prices.
$prices = edd_get_variable_prices ( $download_id );
// Loop through all the prices.
if ( ! empty ( $prices ) ) {
foreach ( $prices as $key => $price ) {
// Skip if amount doesn't exist.
if ( empty ( $price [ 'amount' ] ) ) {
continue ;
}
if ( ! isset ( $min ) ) {
$min = $price [ 'amount' ];
} else {
$min = min ( $min , $price [ 'amount' ] );
}
if ( $price [ 'amount' ] == $min ) {
$min_id = $key ;
}
}
}
return absint ( $min_id );
}
/**
* Retrieves most expensive price option of a variable priced download
*
* @ since 1.4 . 4
* @ param int $download_id ID of the download
* @ return float Amount of the highest price
*/
function edd_get_highest_price_option ( $download_id = 0 ) {
// Attempt to get the ID of the current item in the WordPress loop.
if ( empty ( $download_id ) ) {
$download_id = get_the_ID ();
}
// Bail if download ID is still empty.
if ( empty ( $download_id ) ) {
return false ;
}
// Return download price if variable prices do not exist for download.
if ( ! edd_has_variable_prices ( $download_id ) ) {
return edd_get_download_price ( $download_id );
}
// Fetch variables prices.
$prices = edd_get_variable_prices ( $download_id );
// Set highest to 0.
$highest = 0.00 ;
// Loop through all the prices.
if ( ! empty ( $prices ) ) {
$max = 0 ;
foreach ( $prices as $key => $price ) {
// Skip if amount doesn't exist.
if ( empty ( $price [ 'amount' ] ) ) {
continue ;
}
$max = max ( $max , $price [ 'amount' ] );
if ( $price [ 'amount' ] == $max ) {
$max_id = $key ;
}
}
$highest = $prices [ $max_id ][ 'amount' ];
}
return edd_sanitize_amount ( $highest );
}
/**
* Retrieves a price from from low to high of a variable priced download .
*
* @ since 1.4 . 4
*
* @ param int $download_id Download ID .
* @ return string $range A fully formatted price range .
*/
function edd_price_range ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$low = edd_get_lowest_price_option ( $download_id );
$high = edd_get_highest_price_option ( $download_id );
$range = '<span class="edd_price edd_price_range_low" id="edd_price_low_' . $download_id . '">' . edd_currency_filter ( edd_format_amount ( $low ) ) . '</span>' ;
$range .= '<span class="edd_price_range_sep"> – </span>' ;
$range .= '<span class="edd_price edd_price_range_high" id="edd_price_high_' . $download_id . '">' . edd_currency_filter ( edd_format_amount ( $high ) ) . '</span>' ;
return apply_filters ( 'edd_price_range' , $range , $download_id , $low , $high );
}
/**
* Checks to see if multiple price options can be purchased at once .
*
* @ since 1.4 . 2
*
* @ param int $download_id Download ID .
* @ return bool True if multiple price options can be purchased at once , false otherwise .
*/
function edd_single_price_option_mode ( $download_id = 0 ) {
// Attempt to get the ID of the current item in the WordPress loop.
if ( empty ( $download_id ) ) {
$download_id = get_the_ID ();
}
// Bail if download ID is still empty.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> is_single_price_mode ()
: false ;
}
/**
* Get product types .
*
* @ since 1.8
*
* @ return array $types Download types .
*/
function edd_get_download_types () {
$types = array (
'0' => __ ( 'Default' , 'easy-digital-downloads' ),
'bundle' => __ ( 'Bundle' , 'easy-digital-downloads' ),
);
return apply_filters ( 'edd_download_types' , $types );
}
/**
* Get the download type : either `default` or `bundled` .
*
* @ since 1.6
*
* @ param int $download_id Download ID .
* @ return string $type Download type .
*/
function edd_get_download_type ( $download_id = 0 ) {
$download = edd_get_download ( $download_id );
return $download
? $download -> type
: false ;
}
/**
* Determines if a product is a bundle .
*
* @ since 1.6
*
* @ param int $download_id Download ID .
* @ return bool True if a bundle , false otherwise .
*/
function edd_is_bundled_product ( $download_id = 0 ) {
$download = edd_get_download ( $download_id );
return $download
? $download -> is_bundled_download ()
: false ;
}
/**
* Retrieves the product IDs of bundled products .
*
* @ since 1.6
* @ since 2.7 Added $price_id parameter .
*
* @ param int $download_id Download ID .
* @ param int $price_id Optional . Price ID . Default null .
*
* @ return array | false Products in the bundle , false if download does not exist .
*/
function edd_get_bundled_products ( $download_id = 0 , $price_id = null ) {
$download = edd_get_download ( $download_id );
// Bail if download does not exist.
if ( ! $download ) {
return false ;
}
if ( null !== $price_id ) {
return $download -> get_variable_priced_bundled_downloads ( $price_id );
} else {
return $download -> bundled_downloads ;
}
}
/**
* Returns the total earnings for a download .
*
* @ since 1.0
*
* @ param int $download_id Download ID .
* @ return float | false $earnings Download earnings , false if download not found .
*/
function edd_get_download_earnings_stats ( $download_id = 0 ) {
$download = edd_get_download ( $download_id );
return $download
? $download -> earnings
: false ;
}
/**
* Return the sales number for a download .
*
* @ since 1.0
*
* @ param int $download_id Download ID .
* @ return int | false Number of sales , false if download was not found .
*/
function edd_get_download_sales_stats ( $download_id = 0 ) {
$download = edd_get_download ( $download_id );
return $download
? $download -> sales
: false ;
}
/**
* Record a file download .
*
* @ since 1.0
* @ since 3.0 Refactored to use new query methods .
*
* @ param int $download_id Download ID .
* @ param int $file_id File ID .
* @ param array $user_info User information ( deprecated ) .
* @ param string $ip User IP .
* @ param int $order_id Order ID .
* @ param int $price_id Optional . Price ID ,
* @ return void
*/
function edd_record_download_in_log ( $download_id = 0 , $file_id = 0 , $user_info = array (), $ip = '' , $order_id = 0 , $price_id = 0 ) {
$order = edd_get_order ( $order_id );
if ( ! class_exists ( 'Browser' ) ) {
require_once EDD_PLUGIN_DIR . 'includes/libraries/browser.php' ;
}
$browser = new Browser ();
$user_agent = $browser -> getBrowser () . ' ' . $browser -> getVersion () . '/' . $browser -> getPlatform ();
edd_add_file_download_log ( array (
'product_id' => absint ( $download_id ),
'file_id' => absint ( $file_id ),
'order_id' => absint ( $order_id ),
'price_id' => absint ( $price_id ),
'customer_id' => $order -> customer_id ,
'ip' => sanitize_text_field ( $ip ),
'user_agent' => $user_agent ,
) );
}
/**
* Delete log entries when deleting downloads .
*
* Removes all related log entries when a download is completely deleted .
* ( Does not run when a download is trashed )
*
* @ since 1.3 . 4
* @ since 3.0 Updated to use new query methods .
*
* @ param int $download_id Download ID .
*/
function edd_remove_download_logs_on_delete ( $download_id = 0 ) {
global $wpdb ;
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return ;
}
// Ensure download ID is an integer.
$download_id = absint ( $download_id );
// Bail if the post type is not `download`.
if ( 'download' !== get_post_type ( $download_id ) ) {
return ;
}
// Delete file download logs.
$wpdb -> delete ( $wpdb -> edd_logs_file_downloads , array (
'product_id' => $download_id ,
), array ( '%d' ) );
// Delete logs.
$wpdb -> delete ( $wpdb -> edd_logs , array (
'object_id' => $download_id ,
'object_type' => 'download' ,
), array ( '%d' , '%s' ) );
}
add_action ( 'delete_post' , 'edd_remove_download_logs_on_delete' );
/**
* Recalculates both the net and gross sales and earnings for a download .
*
* @ since 3.0
* @ param int $download_id
* @ return void
*/
function edd_recalculate_download_sales_earnings ( $download_id ) {
$download = edd_get_download ( $download_id );
if ( ! $download instanceof \EDD_Download ) {
return ;
}
$download -> recalculate_net_sales_earnings ();
$download -> recalculate_gross_sales_earnings ();
}
/**
* Retrieves the average monthly earnings for a specific download .
*
* @ since 1.3
*
* @ param int $download_id Download ID .
* @ return float $earnings Average monthly earnings .
*/
function edd_get_average_monthly_download_earnings ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return 0 ;
}
$earnings = edd_get_download_earnings_stats ( $download_id );
$release_date = get_post_field ( 'post_date' , $download_id );
$diff = abs ( current_time ( 'timestamp' ) - strtotime ( $release_date ) );
// Number of months since publication
$months = floor ( $diff / ( 30 * 60 * 60 * 24 ) );
if ( $months > 0 ) {
$earnings = ( $earnings / $months );
}
return $earnings < 0
? 0
: $earnings ;
}
/**
* Retrieves the average monthly sales for a specific download .
*
* @ since 1.3
*
* @ param int $download_id Download ID .
* @ return float $sales Average monthly sales .
*/
function edd_get_average_monthly_download_sales ( $download_id = 0 ) {
$sales = edd_get_download_sales_stats ( $download_id );
$release_date = get_post_field ( 'post_date' , $download_id );
$diff = abs ( current_time ( 'timestamp' ) - strtotime ( $release_date ) );
// Number of months since publication
$months = floor ( $diff / ( 30 * 60 * 60 * 24 ) );
if ( $months > 0 ) {
$sales = ( $sales / $months );
}
return $sales ;
}
/**
* Gets all download files for a product .
*
* @ since 1.0
* @ since 3.0 Renamed $variable_price_id parameter to $price ) id for consistency .
*
* @ param int $download_id Download ID .
* @ param int $price_id Optional . Price ID . Default null .
*
* @ return array | false Download files , false if invalid data was passed .
*/
function edd_get_download_files ( $download_id = 0 , $price_id = null ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> get_files ( $price_id )
: false ;
}
/**
* Retrieves a file name for a file attached to a download . Defaults to the
* file 's actual name if no ' name ' key is present .
*
* @ since 1.6
*
* @ param array $file File information .
* @ return string Filename .
*/
function edd_get_file_name ( $file = array () ) {
// Bail if no data was passed.
if ( empty ( $file ) || ! is_array ( $file ) ) {
return false ;
}
$name = ! empty ( $file [ 'name' ] )
? esc_html ( $file [ 'name' ] )
: basename ( $file [ 'file' ] );
return $name ;
}
/**
* Gets the number of times a file has been downloaded for a specific order .
*
* @ since 1.6
* @ since 3.0 Renamed parameters for consistency across new query methods
* introduced .
* Refactored to use new query methods .
*
* @ param int $download_id Download ID .
* @ param int $file_id File ID .
* @ param int $order_id Order ID .
*
* @ return int Number of times the file has been downloaded for the order .
*/
function edd_get_file_downloaded_count ( $download_id = 0 , $file_id = 0 , $order_id = 0 ) {
// Bail if no download ID or order ID was passed.
if ( empty ( $download_id ) || empty ( $order_id ) ) {
return false ;
}
// Ensure arguments passed are valid.
$download_id = absint ( $download_id );
$file_id = absint ( $file_id );
$order_id = absint ( $order_id );
return edd_count_file_download_logs ( array (
'product_id' => $download_id ,
'order_id' => $order_id ,
'file_id' => $file_id ,
) );
}
/**
* Gets the file download file limit for a particular download . This limit refers
* to the maximum number of times files connected to a product can be downloaded .
*
* @ since 1.3 . 1
*
* @ param int $download_id Download ID .
* @ return int | false File download limit , false if invalid download ID passed .
*/
function edd_get_file_download_limit ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> get_file_download_limit ()
: false ;
}
/**
* Gets the file refund window for a particular download
*
* This window refers to the maximum number of days it can be refunded after
* it has been purchased .
*
* @ since 3.0
*
* @ param int $download_id Download ID .
* @ return int Refund window .
*/
function edd_get_download_refund_window ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> get_refund_window ()
: false ;
}
/**
* Get the refundability status for a download .
*
* @ since 3.0
*
* @ param int $download_id Download ID .
* @ return string `refundable` or `nonrefundable` .
*/
function edd_get_download_refundability ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> get_refundability ()
: false ;
}
/**
* Gets the file download file limit override for a particular download .
* The override allows the main file download limit to be bypassed .
*
* @ since 1.3 . 2
* @ since 3.0 Renamed $payment_id parameter to $order_id .
*
* @ param int $download_id Download ID .
* @ param int $order_id Order ID .
*
* @ return int | false New file download limit , false if invalid download ID passed .
*/
function edd_get_file_download_limit_override ( $download_id = 0 , $order_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$limit_override = get_post_meta ( $download_id , '_edd_download_limit_override_' . $order_id , true );
return $limit_override
? absint ( $limit_override )
: 0 ;
}
/**
* Sets the file download file limit override for a particular download .
*
* The override allows the main file download limit to be bypassed .
* If no override is set yet , the override is set to the main limit + 1.
* If the override is already set , then it is simply incremented by 1.
*
* @ since 1.3 . 2
* @ since 3.0 Renamed $payment_id parameter to $order_id .
*
* @ param int $download_id Download ID .
* @ param int $order_id Order ID .
*
* @ return false False if invalid download ID or order ID was passed .
*/
function edd_set_file_download_limit_override ( $download_id = 0 , $order_id = 0 ) {
// Bail if no download ID or order ID was passed.
if ( empty ( $download_id ) || empty ( $order_id ) ) {
return false ;
}
$override = edd_get_file_download_limit_override ( $download_id , $order_id );
$limit = edd_get_file_download_limit ( $download_id );
if ( ! empty ( $override ) ) {
$override = $override += 1 ;
} else {
$override = $limit += 1 ;
}
update_post_meta ( $download_id , '_edd_download_limit_override_' . $order_id , $override );
}
/**
* Checks if a file is at its download limit
*
* This limit refers to the maximum number of times files connected to a product
* can be downloaded .
*
* @ since 1.3 . 1
* @ since 3.0 Refactored to use new query methods .
* Renamed $payment_id parameter to $order_id .
* Set default value of $price_id to 0.
*
* @ param int $download_id Download ID .
* @ param int $order_id Order ID .
* @ param int $file_id File ID .
* @ param int $price_id Price ID .
*
* @ return bool True if at limit , false otherwise .
*/
function edd_is_file_at_download_limit ( $download_id = 0 , $order_id = 0 , $file_id = 0 , $price_id = 0 ) {
// Bail if invalid data was passed.
if ( empty ( $download_id ) || empty ( $order_id ) ) {
return false ;
}
// Sanitize parameters.
$download_id = absint ( $download_id );
$order_id = absint ( $order_id );
$file_id = absint ( $file_id );
$price_id = absint ( $price_id );
// Default to false.
$ret = false ;
$download_limit = edd_get_file_download_limit ( $download_id );
if ( ! empty ( $download_limit ) ) {
$unlimited_purchase = edd_payment_has_unlimited_downloads ( $order_id );
if ( empty ( $unlimited_purchase ) ) {
// Retrieve the file download count.
$download_count = edd_count_file_download_logs ( array (
'product_id' => $download_id ,
'file_id' => $file_id ,
'order_id' => $order_id ,
'price_id' => $price_id ,
) );
if ( $download_count >= $download_limit ) {
$ret = true ;
// Check to make sure the limit isn't overwritten.
// A limit is overwritten when purchase receipt is resent.
$limit_override = edd_get_file_download_limit_override ( $download_id , $order_id );
if ( ! empty ( $limit_override ) && $download_count < $limit_override ) {
$ret = false ;
}
}
}
}
/**
* Filters whether or not a file is at its download limit .
*
* @ param bool $ret
* @ param int $download_id
* @ param int $payment_id
* @ param int $file_id
* @ param int $price_id
*
* @ since 2.10 Added `$price_id` parameter .
*/
return ( bool ) apply_filters ( 'edd_is_file_at_download_limit' , $ret , $download_id , $order_id , $file_id , $price_id );
}
/**
* Retrieve the price option that has access to the specified file .
*
* @ since 1.0 . 9
*
* @ param int $download_id Download ID .
* @ param string $file_key File key .
*
* @ return string | false Price ID if restricted , " all " otherwise , false if no download ID was passed .
*/
function edd_get_file_price_condition ( $download_id = 0 , $file_key = '' ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> get_file_price_condition ( $file_key )
: false ;
}
/**
* Constructs a secure file download url for a specific file .
*
* @ since 1.0
* @ since 3.0 Updated to use new query methods .
*
2023-03-17 22:34:04 +00:00
* @ param string $order_or_key The order object or payment key . Using the payment key will eventually be deprecated .
* @ param string $email Customer email address . Use edd_get_payment_user_email () to get user email .
* @ param int $filekey Index of array of files returned by edd_get_download_files () that this download link is for .
* @ param int $download_id Optional . ID of download this download link is for . Default is 0.
* @ param bool | int $price_id Optional . Price ID when using variable prices . Default is false .
2022-11-27 15:03:07 +00:00
*
* @ return string Secure download URL .
*/
2023-03-17 22:34:04 +00:00
function edd_get_download_file_url ( $order_or_key , $email , $filekey , $download_id = 0 , $price_id = false ) {
2022-11-27 15:03:07 +00:00
$hours = absint ( edd_get_option ( 'download_link_expiration' , 24 ) );
if ( ! ( $date = strtotime ( '+' . $hours . 'hours' , current_time ( 'timestamp' ) ) ) ) {
$date = 2147472000 ; // Highest possible date, January 19, 2038
}
2023-03-17 22:34:04 +00:00
// Fetch order.
if ( $order_or_key instanceof EDD\Orders\Order ) {
$order = $order_or_key ;
$key = $order -> payment_key ;
} else {
$key = $order_or_key ;
$order = edd_get_order_by ( 'payment_key' , $key );
}
2022-11-27 15:03:07 +00:00
// Leaving in this array and the filter for backwards compatibility now
$old_args = array (
'download_key' => rawurlencode ( $key ),
'email' => rawurlencode ( $email ),
'file' => rawurlencode ( $filekey ),
'price_id' => ( int ) $price_id ,
'download_id' => $download_id ,
'expire' => rawurlencode ( $date ),
);
$params = apply_filters ( 'edd_download_file_url_args' , $old_args );
// Bail if order wasn't found.
if ( ! $order ) {
return false ;
}
// Get the array of parameters in the same order in which they will be validated.
$args = array_fill_keys ( edd_get_url_token_parameters (), '' );
// Simply the URL by concatenating required data using a colon as a delimiter.
if ( ! is_numeric ( $price_id ) ) {
$eddfile = sprintf ( '%d:%d:%d' , $order -> id , $params [ 'download_id' ], $params [ 'file' ] );
} else {
$eddfile = sprintf ( '%d:%d:%d:%d' , $order -> id , $params [ 'download_id' ], $params [ 'file' ], $price_id );
}
$args [ 'eddfile' ] = rawurlencode ( $eddfile );
if ( isset ( $params [ 'expire' ] ) ) {
$args [ 'ttl' ] = $params [ 'expire' ];
}
// Ensure all custom args registered with extensions through edd_download_file_url_args get added to the URL, but without adding all the old args
$args = array_merge ( $args , array_diff_key ( $params , $old_args ) );
2023-03-17 22:34:04 +00:00
/**
* Allow the file download args to be filtered .
*
* @ since 3.1 . 1 Includes the order object as the fourth parameter .
* @ param array $args The full array of parameters .
* @ param int $order_id The order ID .
* @ param array $params The original array of parameters .
* @ param EDD\Orders\Order $order The order object .
*/
$args = apply_filters ( 'edd_get_download_file_url_args' , $args , $order -> id , $params , $order );
2022-11-27 15:03:07 +00:00
$args [ 'file' ] = $params [ 'file' ];
$args [ 'token' ] = edd_get_download_token ( add_query_arg ( array_filter ( $args ), untrailingslashit ( site_url () ) ) );
return add_query_arg ( array_filter ( $args ), site_url ( 'index.php' ) );
}
/**
* Gets the array of parameters to be used for the URL token generation and validation .
* Used by `edd_get_download_file_url` and `edd_validate_url_token` so that their parameters are ordered the same .
*
* @ since 2.11 . 4
* @ return array
*/
function edd_get_url_token_parameters () {
return apply_filters (
'edd_url_token_allowed_params' ,
array (
'eddfile' ,
'ttl' ,
'file' ,
'token' ,
)
);
}
/**
* Get product notes .
*
* @ since 1.2 . 1
*
* @ param int $download_id Download ID .
* @ return string | false Product notes , false if invalid data was passed .
*/
function edd_get_product_notes ( $download_id = 0 ) {
// Bail if download ID was not passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> notes
: false ;
}
/**
* Retrieves a download SKU by ID .
*
* @ since 1.6
*
* @ param int $download_id Download ID .
* @ return string | false Download SKU , false if invalid data was passed .
*/
function edd_get_download_sku ( $download_id = 0 ) {
// Bail if download ID was not passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> sku
: false ;
}
/**
* Retrieve the download button behavior : either add to cart or direct .
*
* @ since 1.7
*
* @ param int $download_id Download ID .
* @ return string | false `add_to_cart` or `direct` , false if invalid data was passed .
*/
function edd_get_download_button_behavior ( $download_id = 0 ) {
// Bail if download ID was not passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> button_behavior
: false ;
}
/**
* Is quantity input disabled on this product ?
*
* @ since 2.7
*
* @ param int $download_id Download ID .
* @ return bool
*/
function edd_download_quantities_disabled ( $download_id = 0 ) {
// Bail if download ID was not passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> quantities_disabled ()
: false ;
}
/**
* Get the file download method .
*
* @ since 1.6
*
* @ return string File download method .
*/
function edd_get_file_download_method () {
$method = edd_get_option ( 'download_method' , 'direct' );
return apply_filters ( 'edd_file_download_method' , $method );
}
/**
* Returns a random download .
*
* @ since 1.7
*
* @ param bool $post_ids Optional . True for of download IDs , false for WP_Post
* objects . Default true .
* @ return array Download IDs / WP_Post objects .
*/
function edd_get_random_download ( $post_ids = true ) {
return edd_get_random_downloads ( 1 , $post_ids );
}
/**
* Returns random downloads .
*
* @ since 1.7
*
* @ param int $num Number of downloads to return . Default 3.
* @ param bool $post_ids Optional . True for array of WP_Post objects , else
* array of IDs . Default true .
*
* @ return array Download IDs / WP_Post objects .
*/
function edd_get_random_downloads ( $num = 3 , $post_ids = true ) {
$args = array (
'post_type' => 'download' ,
'orderby' => 'rand' ,
'numberposts' => $num ,
);
if ( $post_ids ) {
$args [ 'fields' ] = 'ids' ;
}
$args = apply_filters ( 'edd_get_random_downloads' , $args );
return get_posts ( $args );
}
/**
* Generates a token for a given URL .
*
* An 'o' query parameter on a URL can include optional variables to test
* against when verifying a token without passing those variables around in
* the URL . For example , downloads can be limited to the IP that the URL was
* generated for by adding 'o=ip' to the query string .
*
* Or suppose when WordPress requested a URL for automatic updates , the user
* agent could be tested to ensure the URL is only valid for requests from
* that user agent .
*
* @ since 2.3
*
* @ param string $url URL to generate a token for .
* @ return string Token for the URL .
*/
function edd_get_download_token ( $url = '' ) {
$args = array ();
$hash = apply_filters ( 'edd_get_url_token_algorithm' , 'sha256' );
$secret = apply_filters ( 'edd_get_url_token_secret' , hash ( $hash , wp_salt () ) );
/*
* Add additional args to the URL for generating the token .
* Allows for restricting access to IP and / or user agent .
*/
$parts = wp_parse_url ( $url );
$options = array ();
if ( isset ( $parts [ 'query' ] ) ) {
wp_parse_str ( $parts [ 'query' ], $query_args );
// o = option checks (ip, user agent).
if ( ! empty ( $query_args [ 'o' ] ) ) {
// Multiple options can be checked by separating them with a colon in the query parameter.
$options = explode ( ':' , rawurldecode ( $query_args [ 'o' ] ) );
if ( in_array ( 'ip' , $options , true ) ) {
$args [ 'ip' ] = edd_get_ip ();
}
if ( in_array ( 'ua' , $options , true ) ) {
$ua = isset ( $_SERVER [ 'HTTP_USER_AGENT' ] )
? $_SERVER [ 'HTTP_USER_AGENT' ]
: '' ;
$args [ 'user_agent' ] = rawurlencode ( $ua );
}
}
}
/*
* Filter to modify arguments and allow custom options to be tested .
* Be sure to rawurlencode any custom options for consistent results .
*/
$args = apply_filters ( 'edd_get_url_token_args' , $args , $url , $options );
$args [ 'secret' ] = $secret ;
$args [ 'token' ] = false ; // Removes a token if present.
$url = add_query_arg ( $args , $url );
$parts = wp_parse_url ( $url );
// In the event there isn't a path, set an empty one so we can MD5 the token
if ( ! isset ( $parts [ 'path' ] ) ) {
$parts [ 'path' ] = '' ;
}
$token = hash_hmac ( 'sha256' , $parts [ 'path' ] . '?' . $parts [ 'query' ], wp_salt ( 'edd_file_download_link' ) );
return $token ;
}
/**
* Generate a token for a URL and match it against the existing token to make
* sure the URL hasn ' t been tampered with .
*
* @ since 2.3
*
* @ param string $url URL to test .
* @ return bool
*/
function edd_validate_url_token ( $url = '' ) {
$ret = false ;
$parts = parse_url ( $url );
$query_args = array ();
$original_url = $url ;
if ( isset ( $parts [ 'query' ] ) ) {
wp_parse_str ( $parts [ 'query' ], $query_args );
// If the TTL is in the past, die out before we go any further.
if ( isset ( $query_args [ 'ttl' ] ) && current_time ( 'timestamp' ) > $query_args [ 'ttl' ] ) {
wp_die ( apply_filters ( 'edd_download_link_expired_text' , esc_html__ ( 'Sorry but your download link has expired.' , 'easy-digital-downloads' ) ), esc_html__ ( 'Error' , 'easy-digital-downloads' ), array ( 'response' => 403 ) );
}
// These are the only URL parameters that are allowed to affect the token validation.
$allowed_args = edd_get_url_token_parameters ();
// Collect the allowed tags in proper order, remove all tags, and re-add only the allowed ones.
$validated_query_args = array ();
foreach ( $allowed_args as $key ) {
if ( true === array_key_exists ( $key , $query_args ) ) {
$validated_query_args [ $key ] = $query_args [ $key ];
}
}
// strtok allows a quick clearing of existing query string parameters, so we can re-add the allowed ones.
$url = add_query_arg ( $validated_query_args , strtok ( $url , '?' ) );
if ( isset ( $query_args [ 'token' ] ) && hash_equals ( $query_args [ 'token' ], edd_get_download_token ( $url ) ) ) {
$ret = true ;
}
}
/**
* Filters the URL token validation .
*
* @ param bool $ret Whether the URL has validated or not .
* @ param string $url The URL used for validation .
* @ param array $query_args The array of query parameters .
* @ param string $original_url The original URL ( added 2.11 . 3 ) .
*/
return apply_filters ( 'edd_validate_url_token' , $ret , $url , $query_args , $original_url );
}
/**
* Allows parsing of the values saved by the product drop down .
*
* @ since 2.6 . 9
*
* @ param array $values Parse the values from the product dropdown into a readable array .
* @ return array A parsed set of values for download_id and price_id .
*/
function edd_parse_product_dropdown_values ( $values = array () ) {
$parsed_values = array ();
if ( is_array ( $values ) ) {
foreach ( $values as $value ) {
$value = edd_parse_product_dropdown_value ( $value );
$parsed_values [] = array (
'download_id' => $value [ 'download_id' ],
'price_id' => $value [ 'price_id' ],
);
}
} else {
$value = edd_parse_product_dropdown_value ( $values );
$parsed_values [] = array (
'download_id' => $value [ 'download_id' ],
'price_id' => $value [ 'price_id' ],
);
}
return $parsed_values ;
}
/**
* Given a value from the product dropdown array , parse its parts .
*
* @ since 2.6 . 9
*
* @ param string $value A value saved in a product dropdown array
* @ return array A parsed set of values for download_id and price_id .
*/
function edd_parse_product_dropdown_value ( $value ) {
$parts = explode ( '_' , $value );
$download_id = $parts [ 0 ];
$price_id = isset ( $parts [ 1 ] )
? $parts [ 1 ]
: false ;
return array (
'download_id' => $download_id ,
'price_id' => $price_id ,
);
}
/**
* Get bundle pricing variations
*
* @ since 2.7
*
* @ param int $download_id Download ID .
* @ return array | false Bundle pricing variations , false if invalid data was passed .
*/
function edd_get_bundle_pricing_variations ( $download_id = 0 ) {
// Bail if no download ID was passed.
if ( empty ( $download_id ) ) {
return false ;
}
$download = edd_get_download ( $download_id );
return $download
? $download -> get_bundle_pricing_variations ()
: false ;
}