1674 lines
43 KiB
PHP
1674 lines
43 KiB
PHP
<?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.
|
|
*
|
|
* @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.
|
|
*
|
|
* @return string Secure download URL.
|
|
*/
|
|
function edd_get_download_file_url( $order_or_key, $email, $filekey, $download_id = 0, $price_id = false ) {
|
|
$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
|
|
}
|
|
|
|
// 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 );
|
|
}
|
|
|
|
// 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 ) );
|
|
|
|
/**
|
|
* 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 );
|
|
|
|
$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;
|
|
}
|