laipower/wp-content/plugins/easy-digital-downloads/includes/ajax-functions.php

1561 lines
44 KiB
PHP

<?php
/**
* AJAX Functions
*
* Process the front-end AJAX actions.
*
* @package EDD
* @subpackage Functions/AJAX
* @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;
/**
* Checks whether AJAX is enabled.
*
* This will be deprecated soon in favor of edd_is_ajax_disabled()
*
* @since 1.0
* @return bool True when EDD AJAX is enabled (for the cart), false otherwise.
*/
function edd_is_ajax_enabled() {
$retval = ! edd_is_ajax_disabled();
return apply_filters( 'edd_is_ajax_enabled', $retval );
}
/**
* Checks whether AJAX is disabled.
*
* @since 2.0
* @since 2.7 Setting to disable AJAX was removed. See https://github.com/easydigitaldownloads/easy-digital-downloads/issues/4758
* @return bool True when EDD AJAX is disabled (for the cart), false otherwise.
*/
function edd_is_ajax_disabled() {
return apply_filters( 'edd_is_ajax_disabled', false );
}
/**
* Check if AJAX works as expected
*
* @since 2.2
* @return bool True if AJAX works, false otherwise
*/
function edd_test_ajax_works() {
// Check if the Airplane Mode plugin is installed
if ( class_exists( 'Airplane_Mode_Core' ) ) {
$airplane = Airplane_Mode_Core::getInstance();
if ( method_exists( $airplane, 'enabled' ) ) {
if ( $airplane->enabled() ) {
return true;
}
} else {
if ( $airplane->check_status() == 'on' ) {
return true;
}
}
}
add_filter( 'block_local_requests', '__return_false' );
if ( get_transient( '_edd_ajax_works' ) ) {
return true;
}
$works = true;
$ajax = wp_safe_remote_post( esc_url_raw( edd_get_ajax_url() ), array(
'sslverify' => false,
'timeout' => 30,
'body' => array(
'action' => 'edd_test_ajax'
)
) );
if ( is_wp_error( $ajax ) ) {
$works = false;
} else {
if ( empty( $ajax['response'] ) ) {
$works = false;
}
if ( empty( $ajax['response']['code'] ) || 200 !== (int) $ajax['response']['code'] ) {
$works = false;
}
if ( empty( $ajax['response']['message'] ) || 'OK' !== $ajax['response']['message'] ) {
$works = false;
}
if ( ! isset( $ajax['body'] ) || 0 !== (int) $ajax['body'] ) {
$works = false;
}
}
if ( $works ) {
set_transient( '_edd_ajax_works', '1', DAY_IN_SECONDS );
}
return $works;
}
/**
* Get AJAX URL
*
* @since 1.3
* @return string URL to the AJAX file to call during AJAX requests.
*/
function edd_get_ajax_url() {
$scheme = defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN ? 'https' : 'admin';
$current_url = edd_get_current_page_url();
$ajax_url = admin_url( 'admin-ajax.php', $scheme );
if ( preg_match( '/^https/', $current_url ) && ! preg_match( '/^https/', $ajax_url ) ) {
$ajax_url = preg_replace( '/^http/', 'https', $ajax_url );
}
return apply_filters( 'edd_ajax_url', $ajax_url );
}
/**
* Removes item from cart via AJAX.
*
* @since 1.0
* @return void
*/
function edd_ajax_remove_from_cart() {
if ( ! isset( $_POST['nonce'] ) ) {
edd_debug_log( __( 'Missing nonce when removing an item from the cart. Please read the following for more information: https://easydigitaldownloads.com/development/2018/07/05/important-update-to-ajax-requests-in-easy-digital-downloads-2-9-4', 'easy-digital-downloads' ), true );
}
if ( isset( $_POST['cart_item'] ) && isset( $_POST['nonce'] ) ) {
$cart_item = absint( $_POST['cart_item'] );
$nonce = sanitize_text_field( $_POST['nonce'] );
$nonce_verified = wp_verify_nonce( $nonce, 'edd-remove-cart-widget-item' );
if ( false === $nonce_verified ) {
$return = array( 'removed' => 0 );
} else {
edd_remove_from_cart( $cart_item );
$return = array(
'removed' => 1,
'subtotal' => html_entity_decode( edd_currency_filter( edd_format_amount( edd_get_cart_subtotal() ) ), ENT_COMPAT, 'UTF-8' ),
'total' => html_entity_decode( edd_currency_filter( edd_format_amount( edd_get_cart_total() ) ), ENT_COMPAT, 'UTF-8' ),
'cart_quantity' => html_entity_decode( edd_get_cart_quantity() ),
);
if ( edd_use_taxes() ) {
$cart_tax = (float) edd_get_cart_tax();
$return['tax'] = html_entity_decode( edd_currency_filter( edd_format_amount( $cart_tax ) ), ENT_COMPAT, 'UTF-8' );
}
}
$return = apply_filters( 'edd_ajax_remove_from_cart_response', $return );
echo json_encode( $return );
}
edd_die();
}
add_action( 'wp_ajax_edd_remove_from_cart', 'edd_ajax_remove_from_cart' );
add_action( 'wp_ajax_nopriv_edd_remove_from_cart', 'edd_ajax_remove_from_cart' );
/**
* Adds item to the cart via AJAX.
*
* @since 1.0
* @return void
*/
function edd_ajax_add_to_cart() {
if ( ! isset( $_POST['download_id'] ) ) {
edd_die();
}
$download_id = absint( $_POST['download_id'] );
$request_validated = false;
if ( isset( $_POST['timestamp'] ) && isset( $_POST['token'] ) && EDD\Utils\Tokenizer::is_token_valid( $_POST['token'], $_POST['timestamp'] ) ) {
$request_validated = true;
} elseif ( isset( $_POST['nonce'] ) && wp_verify_nonce( $_POST['nonce'], 'edd-add-to-cart-' . $download_id ) ) {
$request_validated = true;
}
if ( ! $request_validated ) {
edd_debug_log( __( 'Missing nonce when adding an item to the cart. Please read the following for more information: https://easydigitaldownloads.com/development/2018/07/05/important-update-to-ajax-requests-in-easy-digital-downloads-2-9-4', 'easy-digital-downloads' ), true );
edd_die( '', '', 403 );
}
$to_add = array();
if ( isset( $_POST['price_ids'] ) && is_array( $_POST['price_ids'] ) ) {
foreach ( $_POST['price_ids'] as $price ) {
$to_add[] = array( 'price_id' => $price );
}
}
$items = '';
if ( isset( $_POST['post_data'] ) ) {
parse_str( $_POST['post_data'], $post_data );
} else {
$post_data = array();
}
foreach ( $to_add as $options ) {
if ( $_POST['download_id'] == $options['price_id'] ) {
$options = array();
}
if ( isset( $options['price_id'] ) && isset( $post_data['edd_download_quantity_' . $options['price_id'] ] ) ) {
$options['quantity'] = absint( $post_data['edd_download_quantity_' . $options['price_id'] ] );
} else {
$options['quantity'] = isset( $post_data['edd_download_quantity'] ) ? absint( $post_data['edd_download_quantity'] ) : 1;
}
$key = edd_add_to_cart( $_POST['download_id'], $options );
$item = array(
'id' => $_POST['download_id'],
'options' => $options,
);
$item = apply_filters( 'edd_ajax_pre_cart_item_template', $item );
$items .= html_entity_decode( edd_get_cart_item_template( $key, $item, true ), ENT_COMPAT, 'UTF-8' );
}
$return = array(
'subtotal' => html_entity_decode( edd_currency_filter( edd_format_amount( edd_get_cart_subtotal() ) ), ENT_COMPAT, 'UTF-8' ),
'total' => html_entity_decode( edd_currency_filter( edd_format_amount( edd_get_cart_total() ) ), ENT_COMPAT, 'UTF-8' ),
'cart_item' => $items,
'cart_quantity' => html_entity_decode( edd_get_cart_quantity() )
);
if ( edd_use_taxes() ) {
$cart_tax = (float) edd_get_cart_tax();
$return['tax'] = html_entity_decode( edd_currency_filter( edd_format_amount( $cart_tax ) ), ENT_COMPAT, 'UTF-8' );
}
$return = apply_filters( 'edd_ajax_add_to_cart_response', $return );
echo json_encode( $return );
edd_die();
}
add_action( 'wp_ajax_edd_add_to_cart', 'edd_ajax_add_to_cart' );
add_action( 'wp_ajax_nopriv_edd_add_to_cart', 'edd_ajax_add_to_cart' );
/**
* Gets the cart's subtotal via AJAX.
*
* @since 1.0
* @return void
*/
function edd_ajax_get_subtotal() {
echo edd_currency_filter( edd_get_cart_subtotal() );
edd_die();
}
add_action( 'wp_ajax_edd_get_subtotal', 'edd_ajax_get_subtotal' );
add_action( 'wp_ajax_nopriv_edd_get_subtotal', 'edd_ajax_get_subtotal' );
/**
* Validates the supplied discount sent via AJAX.
*
* @since 1.0
* @return void
*/
function edd_ajax_apply_discount() {
if ( isset( $_POST['code'] ) ) { // WPCS: CSRF ok.
$discount_code = sanitize_text_field( $_POST['code'] );
$return = array(
'msg' => '',
'code' => $discount_code,
);
$user = '';
if ( is_user_logged_in() ) {
$user = get_current_user_id();
} else {
parse_str( $_POST['form'], $form ); // WPCS: CSRF ok.
if ( ! empty( $form['edd_email'] ) ) {
$user = urldecode( $form['edd_email'] );
}
}
if ( edd_is_discount_valid( $discount_code, $user ) ) {
$discount = edd_get_discount_by( 'code', $discount_code );
$amount = edd_format_discount_rate( edd_get_discount_type( $discount->id ), edd_get_discount_amount( $discount->id ) );
$discounts = edd_set_cart_discount( $discount_code );
$total = edd_get_cart_total( $discounts );
$return = array(
'msg' => 'valid',
'amount' => $amount,
'total_plain' => $total,
'total' => html_entity_decode( edd_currency_filter( edd_format_amount( $total ) ), ENT_COMPAT, 'UTF-8' ),
'code' => $discount_code,
'html' => edd_get_cart_discounts_html( $discounts ),
);
} else {
$errors = edd_get_errors();
$return['msg'] = $errors['edd-discount-error'];
edd_unset_error( 'edd-discount-error' );
}
// Allow for custom discount code handling
$return = apply_filters( 'edd_ajax_discount_response', $return );
echo wp_json_encode( $return );
}
edd_die();
}
add_action( 'wp_ajax_edd_apply_discount', 'edd_ajax_apply_discount' );
add_action( 'wp_ajax_nopriv_edd_apply_discount', 'edd_ajax_apply_discount' );
/**
* Validates the supplied discount sent via AJAX.
*
* @since 1.0
* @return void
*/
function edd_ajax_update_cart_item_quantity() {
if ( ! empty( $_POST['quantity'] ) && ! empty( $_POST['download_id'] ) ) {
$download_id = absint( $_POST['download_id'] );
$quantity = absint( $_POST['quantity'] );
$options = json_decode( stripslashes( $_POST['options'] ), true );
EDD()->cart->set_item_quantity( $download_id, $quantity, $options );
$return = array(
'download_id' => $download_id,
'quantity' => EDD()->cart->get_item_quantity( $download_id, $options ),
'subtotal' => html_entity_decode( edd_currency_filter( edd_format_amount( EDD()->cart->get_subtotal() ) ), ENT_COMPAT, 'UTF-8' ),
'taxes' => html_entity_decode( edd_currency_filter( edd_format_amount( EDD()->cart->get_tax() ) ), ENT_COMPAT, 'UTF-8' ),
'total' => html_entity_decode( edd_currency_filter( edd_format_amount( EDD()->cart->get_total() ) ), ENT_COMPAT, 'UTF-8' )
);
// Allow for custom cart item quantity handling
$return = apply_filters( 'edd_ajax_cart_item_quantity_response', $return );
echo json_encode($return);
}
edd_die();
}
add_action( 'wp_ajax_edd_update_quantity', 'edd_ajax_update_cart_item_quantity' );
add_action( 'wp_ajax_nopriv_edd_update_quantity', 'edd_ajax_update_cart_item_quantity' );
/**
* Removes a discount code from the cart via ajax
*
* @since 1.7
* @return void
*/
function edd_ajax_remove_discount() {
if ( isset( $_POST['code'] ) ) {
edd_unset_cart_discount( urldecode( $_POST['code'] ) );
$total = edd_get_cart_total();
$return = array(
'total_plain' => $total,
'total' => html_entity_decode( edd_currency_filter( edd_format_amount( $total ) ), ENT_COMPAT, 'UTF-8' ),
'code' => sanitize_text_field( $_POST['code'] ),
'discounts' => edd_get_cart_discounts(),
'html' => edd_get_cart_discounts_html()
);
/**
* Allow for custom remove discount code handling.
*
* @since 2.11.4
*/
$return = apply_filters( 'edd_ajax_remove_discount_response', $return );
wp_send_json( $return );
}
edd_die();
}
add_action( 'wp_ajax_edd_remove_discount', 'edd_ajax_remove_discount' );
add_action( 'wp_ajax_nopriv_edd_remove_discount', 'edd_ajax_remove_discount' );
/**
* Loads Checkout Login Fields the via AJAX
*
* @since 1.0
* @return void
*/
function edd_load_checkout_login_fields() {
$action = sanitize_text_field( $_POST['action'] );
$nonce = sanitize_text_field( $_POST['nonce'] );
$nonce_verified = wp_verify_nonce( $nonce, 'edd_' . $action );
if ( $nonce_verified ) {
do_action( 'edd_purchase_form_login_fields' );
}
edd_die();
}
add_action('wp_ajax_nopriv_checkout_login', 'edd_load_checkout_login_fields');
/**
* Load Checkout Register Fields via AJAX
*
* @since 1.0
* @return void
*/
function edd_load_checkout_register_fields() {
$action = sanitize_text_field( $_POST['action'] );
$nonce = sanitize_text_field( $_POST['nonce'] );
$nonce_verified = wp_verify_nonce( $nonce, 'edd_' . $action );
if ( $nonce_verified ) {
do_action( 'edd_purchase_form_register_fields' );
}
edd_die();
}
add_action('wp_ajax_nopriv_checkout_register', 'edd_load_checkout_register_fields');
/**
* Get Download Title via AJAX
*
* @since 1.0
* @since 2.8 Restrict to just the download post type
* @return void
*/
function edd_ajax_get_download_title() {
if ( isset( $_POST['download_id'] ) ) {
$post_id = absint( $_POST['download_id'] );
$post_type = get_post_type( $post_id );
$title = 'fail';
if ( 'download' === $post_type ) {
$post_title = get_the_title( $_POST['download_id'] );
if ( $post_title ) {
echo $title = $post_title;
}
}
echo $title;
}
edd_die();
}
add_action( 'wp_ajax_edd_get_download_title', 'edd_ajax_get_download_title' );
add_action( 'wp_ajax_nopriv_edd_get_download_title', 'edd_ajax_get_download_title' );
/**
* Recalculate cart taxes
*
* @since 1.6
* @return void
*/
function edd_ajax_recalculate_taxes() {
if ( ! isset( $_POST['nonce'] ) ) {
edd_debug_log( __( 'Missing nonce when recalculating taxes. Please read the following for more information: https://easydigitaldownloads.com/development/2018/07/05/important-update-to-ajax-requests-in-easy-digital-downloads-2-9-4', 'easy-digital-downloads' ), true );
}
$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( $_POST['nonce'] ) : '';
$nonce_verified = wp_verify_nonce( $nonce, 'edd-checkout-address-fields' );
if ( false === $nonce_verified ) {
return false;
}
if ( ! edd_get_cart_contents() ) {
return false;
}
if ( empty( $_POST['billing_country'] ) ) {
$_POST['billing_country'] = edd_get_shop_country();
}
ob_start();
edd_checkout_cart();
/**
* Allows the cart content to be filtered.
* @since 3.1
*/
$cart = apply_filters( 'edd_get_checkout_cart', ob_get_clean() );
$response = array(
'html' => $cart,
'tax_raw' => edd_get_cart_tax(),
'tax' => html_entity_decode( edd_cart_tax( false ), ENT_COMPAT, 'UTF-8' ),
'tax_rate_raw' => edd_get_tax_rate(),
'tax_rate' => html_entity_decode( edd_get_formatted_tax_rate(), ENT_COMPAT, 'UTF-8' ),
'total' => html_entity_decode( edd_cart_total( false ), ENT_COMPAT, 'UTF-8' ),
'total_raw' => edd_get_cart_total(),
);
echo json_encode( $response );
edd_die();
}
add_action( 'wp_ajax_edd_recalculate_taxes', 'edd_ajax_recalculate_taxes' );
add_action( 'wp_ajax_nopriv_edd_recalculate_taxes', 'edd_ajax_recalculate_taxes' );
/**
* Retrieve a states drop down
*
* @since 1.6
* @since 2.9.4 Added nonce verification.
* @since 3.0 Updated listbox with placeholder values.
*/
function edd_ajax_get_states_field() {
// Check a nonce was sent.
if ( empty( $_POST['nonce'] ) ) {
edd_debug_log( __( 'Missing nonce when retrieving state list. Please read the following for more information: https://easydigitaldownloads.com/development/2018/07/05/important-update-to-ajax-requests-in-easy-digital-downloads-2-9-4', 'easy-digital-downloads' ), true );
}
$nonce = ! empty( $_POST['nonce'] )
? sanitize_text_field( $_POST['nonce'] )
: '';
$nonce_verified = wp_verify_nonce( $nonce, 'edd-country-field-nonce' );
// Bail if nonce verification failed.
if ( false === $nonce_verified ) {
edd_die();
}
// Get country.
$country = ! empty( $_POST['country'] )
? sanitize_text_field( $_POST['country'] ) // Exactly matched
: edd_get_shop_country();
// Get states for country.
$states = edd_get_shop_states( $country );
// Chosen
$chosen = ! isset( $_POST['chosen'] ) || ( 'true' === $_POST['chosen'] )
? true
: false;
// Maybe setup the new listbox.
if ( ! empty( $states ) ) {
$field_name = isset( $_POST['field_name'] )
? sanitize_text_field( $_POST['field_name'] )
: 'edd-state-select';
$field_id = isset( $_POST['field_id'] )
? sanitize_text_field( $_POST['field_id'] )
: $field_name;
$response = EDD()->html->region_select(
array(
'name' => $field_name,
'id' => $field_id,
'class' => $field_name . ' edd-select',
'options' => $states,
'chosen' => $chosen,
'show_option_all' => false,
)
);
} else {
$response = 'nostates';
}
echo $response;
edd_die();
}
add_action( 'wp_ajax_edd_get_shop_states', 'edd_ajax_get_states_field' );
add_action( 'wp_ajax_nopriv_edd_get_shop_states', 'edd_ajax_get_states_field' );
/**
* Retrieve a downloads drop down
*
* @since 1.6
* @since 3.0 Use `get_posts()` instead of multiple direct queries (yay caching)
*
* @return void
*/
function edd_ajax_download_search() {
// We store the last search in a transient for 30 seconds. This _might_
// result in a race condition if 2 users are looking at the exact same time,
// but we'll worry about that later if that situation ever happens.
$args = get_transient( 'edd_download_search' );
// Parse args
$search = wp_parse_args( (array) $args, array(
'text' => '',
'results' => array()
) );
// Get the search string
$new_search = isset( $_GET['s'] )
? sanitize_text_field( $_GET['s'] )
: '';
// Bail early if the search text has not changed
if ( $search['text'] === $new_search ) {
echo json_encode( $search['results'] );
edd_die();
}
// Set the local static search variable
$search['text'] = $new_search;
// Are we excluding the current ID?
$excludes = isset( $_GET['current_id'] )
? array_unique( array_map( 'absint', (array) $_GET['current_id'] ) )
: array();
// Are we excluding bundles?
$no_bundles = isset( $_GET['no_bundles'] )
? filter_var( $_GET['no_bundles'], FILTER_VALIDATE_BOOLEAN )
: false;
// Are we including variations?
$variations = isset( $_GET['variations'] )
? filter_var( $_GET['variations'], FILTER_VALIDATE_BOOLEAN )
: false;
$variations_only = isset( $_GET['variations_only'] )
? filter_var( $_GET['variations_only'], FILTER_VALIDATE_BOOLEAN )
: false;
// Are we including all statuses, or only public ones?
$status = ! current_user_can( 'edit_products' )
? apply_filters( 'edd_product_dropdown_status_nopriv', array( 'publish' ) )
: apply_filters( 'edd_product_dropdown_status', array( 'publish', 'draft', 'private', 'future' ) );
// Default query arguments
$args = array(
'orderby' => 'title',
'order' => 'ASC',
'post_type' => 'download',
'posts_per_page' => 50,
'post_status' => implode( ',', $status ), // String
'post__not_in' => $excludes, // Array
'edd_search' => $new_search, // String
'suppress_filters' => false,
);
// Maybe exclude bundles.
if ( true === $no_bundles ) {
$args['meta_query'] = array(
'relation' => 'OR',
array(
'key' => '_edd_product_type',
'value' => 'bundle',
'compare' => '!=',
),
array(
'key' => '_edd_product_type',
'value' => 'bundle',
'compare' => 'NOT EXISTS',
),
);
}
add_filter( 'posts_where', 'edd_ajax_filter_download_where', 10, 2 );
// Get downloads
$items = get_posts( $args );
remove_filter( 'posts_where', 'edd_ajax_filter_download_where', 10, 2 );
// Pluck title & ID
if ( ! empty( $items ) ) {
$items = wp_list_pluck( $items, 'post_title', 'ID' );
// Loop through all items...
foreach ( $items as $post_id => $title ) {
$product_title = $title;
// Look for variable pricing
$prices = edd_get_variable_prices( $post_id );
if ( ! empty( $prices ) && ( false === $variations|| ! $variations_only ) ) {
$title .= ' (' . __( 'All Price Options', 'easy-digital-downloads' ) . ')';
}
if ( empty( $prices ) || ! $variations_only ) {
// Add item to results array
$search['results'][] = array(
'id' => $post_id,
'name' => $title,
);
}
// Maybe include variable pricing
if ( ! empty( $variations ) && ! empty( $prices ) ) {
foreach ( $prices as $key => $value ) {
$name = ! empty( $value['name'] ) ? $value['name'] : '';
if ( ! empty( $name ) ) {
$search['results'][] = array(
'id' => $post_id . '_' . $key,
'name' => esc_html( $product_title . ': ' . $name ),
);
}
}
}
}
// Empty the results array
} else {
$search['results'] = array();
}
// Update the transient
set_transient( 'edd_download_search', $search, 30 );
// Output the results
echo json_encode( $search['results'] );
// Done!
edd_die();
}
add_action( 'wp_ajax_edd_download_search', 'edd_ajax_download_search' );
add_action( 'wp_ajax_nopriv_edd_download_search', 'edd_ajax_download_search' );
/**
* Filters the WHERE SQL query for the edd_download_search.
* This searches the download titles only, not the excerpt/content.
*
* @since 3.1.0.2
* @param string $where
* @param WP_Query $wp_query
* @return string
*/
function edd_ajax_filter_download_where( $where, $wp_query ) {
$search = $wp_query->get( 'edd_search' );
if ( $search ) {
global $wpdb;
$search = $wpdb->esc_like( $search );
$where .= " AND {$wpdb->posts}.post_title LIKE '%{$search}%'";
}
return $where;
}
/**
* Search the customers database via AJAX
*
* @since 2.2
* @return void
*/
function edd_ajax_customer_search() {
global $wpdb;
$search = esc_sql( sanitize_text_field( $_GET['s'] ) );
$results = array();
$customer_view_role = apply_filters( 'edd_view_customers_role', 'view_shop_reports' );
if ( ! current_user_can( $customer_view_role ) ) {
$customers = array();
} else {
$select = "SELECT id, name, email FROM {$wpdb->prefix}edd_customers ";
if ( is_numeric( $search ) ) {
$where = "WHERE `id` LIKE '%$search%' OR `user_id` LIKE '%$search%' ";
} else {
$where = "WHERE `name` LIKE '%$search%' OR `email` LIKE '%$search%' ";
}
$limit = "LIMIT 50";
$customers = $wpdb->get_results( $select . $where . $limit );
}
if ( $customers ) {
foreach( $customers as $customer ) {
$results[] = array(
'id' => $customer->id,
'name' => $customer->name . '(' . $customer->email . ')'
);
}
} else {
$customers[] = array(
'id' => 0,
'name' => __( 'No results found', 'easy-digital-downloads' )
);
}
echo json_encode( $results );
edd_die();
}
add_action( 'wp_ajax_edd_customer_search', 'edd_ajax_customer_search' );
/**
* Search the users database via AJAX
*
* @since 2.6.9
* @return void
*/
function edd_ajax_user_search() {
// Default results
$results = array(
'id' => 0,
'name' => __( 'No users found', 'easy-digital-downloads' )
);
// Default user role
$user_view_role = apply_filters( 'edd_view_users_role', 'view_shop_reports' );
// User can view users
if ( current_user_can( $user_view_role ) ) {
$search = esc_sql( sanitize_text_field( $_GET['s'] ) );
$users = array();
// Searching
if ( ! empty( $search ) ) {
$users = get_users( array(
'search' => '*' . $search . '*',
'number' => 50
) );
}
// Setup results based on users
if ( ! empty( $users ) ) {
$results = array();
foreach( $users as $user ) {
$results[] = array(
'id' => $user->ID,
'name' => $user->display_name,
);
}
}
}
echo json_encode( $results );
edd_die();
}
add_action( 'wp_ajax_edd_user_search', 'edd_ajax_user_search' );
/**
* Check for Download Price Variations via AJAX (this function can only be used
* in WordPress Admin). This function is used for the Edit Payment screen when downloads
* are added to the purchase. When each download is chosen, an AJAX call is fired
* to this function which will check if variable prices exist for that download.
* If they do, it will output a dropdown of all the variable prices available for
* that download.
*
* @author Sunny Ratilal
* @since 1.5
* @return void
*/
function edd_check_for_download_price_variations() {
if ( ! current_user_can( 'edit_products' ) ) {
die( '-1' );
}
$download_id = intval( $_POST['download_id'] );
$download = get_post( $download_id );
if ( 'download' != $download->post_type ) {
die( '-2' );
}
if ( edd_has_variable_prices( $download_id ) ) {
$variable_prices = edd_get_variable_prices( $download_id );
if ( ! empty( $variable_prices ) ) {
$ajax_response = '<select class="edd_price_options_select edd-select edd-select" name="edd_price_option">';
if ( isset( $_POST['all_prices'] ) ) {
$ajax_response .= '<option value="">' . __( 'All Prices', 'easy-digital-downloads' ) . '</option>';
}
foreach ( $variable_prices as $key => $price ) {
$ajax_response .= '<option value="' . esc_attr( $key ) . '">' . esc_html( $price['name'] ) . '</option>';
}
$ajax_response .= '</select>';
echo $ajax_response;
}
}
edd_die();
}
add_action( 'wp_ajax_edd_check_for_download_price_variations', 'edd_check_for_download_price_variations' );
/**
* Searches for users via ajax and returns a list of results
*
* @since 2.0
* @return void
*/
function edd_ajax_search_users() {
// Bail if user cannot manage shop settings
if ( ! current_user_can( 'manage_shop_settings' ) ) {
die();
}
// To search for
$search_query = ! empty( $_POST['user_name'] )
? trim( $_POST['user_name'] )
: '';
// To exclude
$exclude = ! empty( $_POST['exclude'] )
? trim( $_POST['exclude'] )
: '';
// Default args
$defaults = array(
'number' => 50,
'search' => $search_query . '*'
);
// Maybe exclude users
if ( ! empty( $exclude ) ) {
$exclude_array = explode( ',', $exclude );
$defaults['exclude'] = $exclude_array;
}
// Filter query args
$get_users_args = apply_filters( 'edd_search_users_args', $defaults );
// Maybe get users
$users = ! empty( $get_users_args ) && ! empty( $search_query )
? get_users( $get_users_args )
: array();
// Filter users
$found_users = apply_filters( 'edd_ajax_found_users', $users, $search_query );
// Put together the results string
$user_list = '<ul>';
if ( ! empty( $found_users ) ) {
foreach( $found_users as $user ) {
$user_list .= '<li><a href="#" data-userid="' . esc_attr( $user->ID ) . '" data-login="' . esc_attr( $user->user_login ) . '">' . esc_html( $user->user_login ) . '</a></li>';
}
} else {
$user_list .= '<li class="no-users">' . __( 'No users found', 'easy-digital-downloads' ) . '</li>';
}
$user_list .= '</ul>';
echo json_encode( array( 'results' => $user_list ) );
edd_die();
}
add_action( 'wp_ajax_edd_search_users', 'edd_ajax_search_users' );
/**
* Search for download, build, and return HTML.
*
* This is used in the Admin for Adding items to an order.
*
* @since 3.0
*/
function edd_ajax_add_order_item() {
// Bail if user cannot manage shop settings.
if ( ! current_user_can( 'manage_shop_settings' ) ) {
wp_send_json_error();
}
// Set up parameters.
$nonce = isset( $_POST['nonce'] )
? sanitize_text_field( $_POST['nonce'] )
: '';
$download = isset( $_POST['download'] )
? edd_parse_product_dropdown_value( sanitize_text_field( $_POST['download'] ) )
: array();
$country = isset( $_POST['country'] )
? sanitize_text_field( $_POST['country'] )
: '';
$region = isset( $_POST['region'] )
? sanitize_text_field( $_POST['region'] )
: '';
$editable = 1 !== absint( $_POST['editable'] );
// Bail if missing any data.
if ( empty( $nonce ) || empty( $download ) ) {
wp_send_json_error();
}
// Bail if nonce fails.
if ( ! wp_verify_nonce( $nonce, 'edd_add_order_nonce' ) ) {
wp_send_json_error();
}
$response = array();
$d = edd_get_download( $download['download_id'] );
if ( $d ) {
$name = $d->get_name();
if ( ! $d->has_variable_prices() ) {
$amount = floatval( $d->get_price() );
} else {
$prices = $d->get_prices();
if ( isset( $prices[ $download['price_id'] ] ) ) {
$price = $prices[ $download['price_id'] ];
$amount = floatval( $price['amount'] );
$name .= ' &mdash; ' . esc_html( $price['name'] );
}
}
$quantity = edd_item_quantities_enabled() && isset( $_POST['quantity'] )
? absint( $_POST['quantity'] )
: 1;
$response['name'] = $name;
$response['discount'] = 0.00;
$response['tax'] = edd_calculate_tax( $amount * $quantity, $country, $region );
$response['quantity'] = $quantity;
$response['amount'] = $amount;
$response['total'] = floatval( ( $amount * $quantity ) + $response['tax'] );
static $symbol = null;
if ( null === $symbol ) {
$symbol = edd_currency_symbol( edd_get_currency() );
}
ob_start(); ?>
<tr class="edd-add-order-item" data-key="0">
<td class="name column-name column-primary"><a class="row-title" href=""><?php echo esc_html( $response['name'] ); ?></a></td>
<td class="overridable amount column-amount" data-type="amount">
<?php echo esc_html( $symbol ); ?>
<input type="text" class="download-amount" name="downloads[0][amount]" value="<?php echo esc_attr( edd_format_amount( $response['amount'] ) ); ?>" <?php readonly( $editable ); ?> />
</td>
<?php if ( edd_item_quantities_enabled() ) : ?>
<td class="overridable quantity column-quantity" data-type="quantity">
<input type="text" class="download-quantity" name="downloads[0][quantity]" value="<?php echo esc_attr( $quantity ); ?>" <?php readonly( $editable ); ?> />
</td>
<?php endif; ?>
<?php if ( edd_use_taxes() ) : ?>
<td class="overridable tax column-tax" data-type="tax">
<?php echo esc_html( $symbol ); ?>
<input type="text" class="download-tax" name="downloads[0][tax]" value="<?php echo esc_attr( edd_format_amount( $response['tax'] ) ); ?>" <?php readonly( $editable ); ?> />
</td>
<?php endif; ?>
<td class="overridable total column-total" data-type="total">
<?php echo esc_html( $symbol ); ?>
<input type="text" class="download-total" name="downloads[0][total]" value="<?php echo esc_attr( edd_format_amount( $response['total'] ) ); ?>" <?php readonly( $editable ); ?> />
</td>
<th scope="row" class="check-column"><a href="#" class="remove-item"><span class="dashicons dashicons-no"></span></a></th>
<input type="hidden" class="download-id" name="downloads[0][id]" value="<?php echo esc_attr( $download['download_id'] ); ?>" />
<input type="hidden" class="download-price-id" name="downloads[0][price_id]" value="<?php echo esc_attr( $download['price_id'] ); // WPCS: XSS ok. ?>" />
</tr>
<?php
$html = ob_get_contents();
ob_end_clean();
$response['html'] = $html;
}
return wp_send_json_success( $response );
}
add_action( 'wp_ajax_edd_add_order_item', 'edd_ajax_add_order_item' );
function edd_ajax_add_adjustment_to_order() {
// Bail if user cannot manage shop settings.
if ( ! current_user_can( 'manage_shop_settings' ) ) {
wp_send_json_error();
}
// Set up parameters.
$nonce = isset( $_POST['nonce'] )
? sanitize_text_field( $_POST['nonce'] )
: '';
$type = isset( $_POST['type'] )
? sanitize_text_field( $_POST['type'] )
: '';
// Bail if missing any data.
if ( empty( $nonce ) || empty( $type ) ) {
edd_die( '-1' );
}
// Bail if nonce fails.
if ( ! wp_verify_nonce( $nonce, 'edd_add_order_nonce' ) ) {
wp_send_json_error();
}
$response = array();
$valid_types = array( 'fee', 'discount', 'credit' );
// Bail if an invalid type is passed.
if ( ! in_array( $type, $valid_types, true ) ) {
wp_send_json_error();
}
static $symbol = null;
if ( null === $symbol ) {
$symbol = edd_currency_symbol( edd_get_currency() );
}
switch ( $type ) {
case 'discount':
$discount = isset( $_POST['adjustment_data']['discount'] )
? absint( $_POST['adjustment_data']['discount'] )
: 0;
// Bail if no discount ID passed.
if ( empty( $discount ) ) {
wp_send_json_error();
}
$discount = edd_get_discount( $discount );
// Bail if discount not found.
if ( ! $discount ) {
wp_send_json_error();
}
ob_start(); ?>
<tr data-key="0" data-adjustment="discount">
<td class="name column-name column-primary"><a class="row-title" href=""><?php echo esc_html( $discount->name ) ?></a></td>
<td class="type column-type"><?php esc_html_e( 'Discount', 'easy-digital-downloads' ); ?></td>
<td class="description column-description"><code><?php echo esc_html( $discount->code ); ?></code></span></td>
<td class="amount column-amount"><span class="value"><?php echo edd_format_discount_rate( $discount->type, $discount->amount ); ?></span></td>
<th scope="row" class="check-column"><a href="#" class="remove-item"><span class="dashicons dashicons-no"></span></a></th>
<input type="hidden" class="discount-id" name="adjustments[discount][0][id]" value="<?php echo $discount->id; // WPCS: XSS ok. ?>" />
<input type="hidden" class="discount-amount" name="adjustments[discount][0][amount]" value="<?php echo $discount->amount; // WPCS: XSS ok. ?>" />
<input type="hidden" class="discount-type" name="adjustments[discount][0][type]" value="<?php echo $discount->type; // WPCS: XSS ok. ?>" />
</tr>
<?php
$html = ob_get_contents();
ob_end_clean();
$response['html'] = $html;
$response['amount'] = $discount->amount;
$response['type'] = $discount->type;
break;
// We just need to generate HTML if credit is being applied.
case 'credit':
$amount = isset( $_POST['adjustment_data']['credit']['amount'] )
? floatval( $_POST['adjustment_data']['credit']['amount'] )
: 0.00;
$description = isset( $_POST['adjustment_data']['credit']['description'] )
? esc_html( $_POST['adjustment_data']['credit']['description'] )
: '';
ob_start(); ?>
<tr data-key="0" data-adjustment="credit">
<td class="name column-name column-primary"><a class="row-title" href=""><?php esc_html_e( 'Order Credit', 'easy-digital-downloads' ); ?></a></td>
<td class="type column-type"><?php esc_html_e( 'Credit', 'easy-digital-downloads' ); ?></td>
<td class="description column-description"><?php echo $description; // WPCS: XSS ok. ?></span></td>
<td class="amount column-amount"><?php echo esc_html( $symbol ); ?><span class="value"><?php echo esc_html( edd_format_amount( $amount ) ); ?></span></td>
<th scope="row" class="check-column"><a href="#" class="remove-item"><span class="dashicons dashicons-no"></span></a></th>
<input type="hidden" class="credit-description" name="adjustments[credit][0][description]" value="<?php echo $description; // WPCS: XSS ok. ?>" />
<input type="hidden" class="credit-amount" name="adjustments[credit][0][amount]" value="<?php echo $amount; // WPCS: XSS ok. ?>" />
</tr>
<?php
$html = ob_get_contents();
ob_end_clean();
$response['amount'] = $amount;
$response['html'] = $html;
break;
}
return wp_send_json_success( $response );
}
add_action( 'wp_ajax_edd_add_adjustment_to_order', 'edd_ajax_add_adjustment_to_order' );
/**
* Search for customer addresses and return a list.
*
* @since 3.0
* @return array Custom address data.
*/
function edd_ajax_customer_addresses() {
// Bail if user cannot manage shop settings.
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return wp_send_json_error();
}
// Set up parameters.
$nonce = isset( $_POST['nonce'] )
? sanitize_text_field( $_POST['nonce'] )
: '';
$customer_id = isset( $_POST['customer_id'] )
? absint( $_POST['customer_id'] )
: 0;
// Bail if missing any data.
if ( empty( $nonce ) || empty( $customer_id ) ) {
return wp_send_json_error();
}
$response = array();
// Fetch customer.
$customer = edd_get_customer( $customer_id );
if ( $customer ) {
// Fetch customer addresses.
$addresses = $customer->get_addresses();
if ( $addresses ) {
$response['addresses'] = array();
$options = array();
foreach ( $addresses as $address ) {
// Convert EDD\Customer\Customer_Address object to array.
$a = $address->to_array();
// Pass array back as response.
$response['addresses'][ $address->id ] = $a;
$address_keys = array_flip( array( 'address', 'address2', 'city', 'region', 'country', 'postal_code' ) );
$a = array_filter( array_intersect_key( $a, $address_keys ) );
if ( isset( $a['region'] ) && isset( $a['country'] ) ) {
$a['region'] = edd_get_state_name( $a['country'], $a['region'] );
}
if ( isset( $a['country'] ) ) {
$a['country'] = edd_get_country_name( $a['country'] );
}
$a = implode( ', ', $a );
$response['formatted'][ $address->id ] = $a;
$options[ $address->id ] = $a;
}
// Fetch the select
if ( ! empty( $options ) ) {
$html = '<select id="edd_customer_existing_addresses" data-nonce="' . wp_create_nonce( 'edd-country-field-nonce' ) . '" data-placeholder="Select a previously used address" class="add-order-customer-address-select edd-form-group__input">';
$html .= '<option data-key="0" value="0"></option>';
foreach ( $options as $key => $value ) {
$html .= '<option data-key="' . esc_attr( $key ) . '" value="' . esc_attr( $key ) . '">' . esc_attr( $value ). '</option>';
}
$html .= '</select>';
$response['html'] = $html;
}
}
}
return wp_send_json_success( $response );
}
add_action( 'wp_ajax_edd_customer_addresses', 'edd_ajax_customer_addresses' );
/**
* Returns details about a Customer.
*
* @since 3.0
*/
function edd_ajax_customer_details() {
// Bail if user cannot manage shop settings.
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return wp_send_json_error();
}
// Set up parameters.
$nonce = isset( $_POST['nonce'] )
? sanitize_text_field( $_POST['nonce'] )
: '';
$customer_id = isset( $_POST['customer_id'] )
? absint( $_POST['customer_id'] )
: 0;
// Bail if missing any data.
if ( empty( $nonce ) || empty( $customer_id ) ) {
return wp_send_json_error();
}
// Bail if nonce verification failed.
if ( ! wp_verify_nonce( $nonce, 'edd_customer_details_nonce' ) ) {
return wp_send_json_error();
}
// Fetch customer.
$customer = edd_get_customer( $customer_id );
if ( ! $customer ) {
return wp_send_json_error();
}
$response = array(
'id' => esc_html( $customer->id ),
'name' => esc_html( $customer->name ),
'email' => esc_html( $customer->email ),
'avatar' => get_avatar( $customer->email, 50 ),
'date_created' => esc_html( $customer->date_created ),
'date_created_i18n' => esc_html( edd_date_i18n( $customer->date_created ) ),
'_links' => array(
'self' => esc_url_raw( admin_url( 'edit.php?post_type=download&page=edd-customers&view=overview&id=' . absint( $customer->id ) ) ),
),
);
return wp_send_json_success( $response );
}
add_action( 'wp_ajax_edd_customer_details', 'edd_ajax_customer_details' );
/**
* Recalculates taxes when adding a new order and the country/region field is changed.
*
* @since 3.0
*/
function edd_ajax_get_tax_rate() {
// Bail if user cannot manage shop settings.
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return wp_send_json_error();
}
// Set up parameters.
$nonce = isset( $_POST['nonce'] )
? sanitize_text_field( $_POST['nonce'] )
: '';
$country = isset( $_POST['country'] )
? sanitize_text_field( $_POST['country'] )
: '';
$region = isset( $_POST['region'] )
? sanitize_text_field( $_POST['region'] )
: '';
// Bail if missing any data.
if ( empty( $nonce ) ) {
return wp_send_json_error();
}
// Bail if nonce verification failed.
if ( ! wp_verify_nonce( $nonce, 'edd_get_tax_rate_nonce' ) ) {
return wp_send_json_error();
}
$response = array();
$rate = edd_get_tax_rate( $country, $region, $fallback = false );
$response['tax_rate'] = $rate;
$response['prices_include_tax'] = (bool) edd_prices_include_tax();
return wp_send_json_success( $response );
}
add_action( 'wp_ajax_edd_get_tax_rate', 'edd_ajax_get_tax_rate' );
/**
* Retrieves a potential Order Item's amounts.
*
* @since 3.0
*/
function edd_admin_order_get_item_amounts() {
// Set up parameters.
$nonce = isset( $_POST['nonce'] )
? sanitize_text_field( $_POST['nonce'] )
: '';
// Bail if missing any data.
if ( empty( $nonce ) ) {
return wp_send_json_error( array(
'message' => esc_html__( 'Unable to verify action. Please refresh the page and try again.', 'easy-digital-downloads' ),
) );
}
// Bail if nonce verification failed.
if ( ! wp_verify_nonce( $nonce, 'edd_admin_order_get_item_amounts' ) ) {
return wp_send_json_error( array(
'message' => esc_html__( 'Unable to verify action. Please refresh the page and try again.', 'easy-digital-downloads' ),
) );
}
$is_adjusting_manually = isset( $_POST['_isAdjustingManually'] ) && false !== $_POST['_isAdjustingManually'];
$product_id = isset( $_POST['productId'] )
? intval( sanitize_text_field( $_POST['productId'] ) )
: 0;
$price_id = isset( $_POST['priceId'] )
? intval( sanitize_text_field( $_POST['priceId'] ) )
: 0;
$quantity = isset( $_POST['quantity'] )
? intval( sanitize_text_field( $_POST['quantity'] ) )
: 0;
$country = isset( $_POST['country'] )
? sanitize_text_field( $_POST['country'] )
: '';
$region = isset( $_POST['region'] )
? sanitize_text_field( $_POST['region'] )
: '';
$products = isset( $_POST['products'] )
? $_POST['products']
: array();
$discounts = isset( $_POST['discounts'] )
? array_unique( array_map( 'intval', $_POST['discounts'] ) )
: array();
$download = edd_get_download( $product_id );
// Bail if no Download is found.
if ( ! $download ) {
return wp_send_json_error( array(
'message' => esc_html__( 'Unable to find download. Please refresh the page and try again.', 'easy-digital-downloads' ),
) );
}
// Use base Amount if sent.
if ( isset( $_POST['amount'] ) && '0' !== $_POST['amount'] ) {
$amount = edd_sanitize_amount( sanitize_text_field( $_POST['amount'] ) );
// Determine amount from Download record.
} else {
if ( ! $download->has_variable_prices() ) {
$amount = floatval( $download->get_price() );
} else {
$prices = $download->get_prices();
if ( isset( $prices[ $price_id ] ) ) {
$price = $prices[ $price_id ];
$amount = floatval( $price['amount'] );
}
}
}
// Use base Subtotal if sent.
if ( isset( $_POST['subtotal'] ) && '0' !== $_POST['subtotal'] ) {
$subtotal = edd_sanitize_amount( sanitize_text_field( $_POST['subtotal'] ) );
} else {
$subtotal = $amount * $quantity;
}
$discount = 0;
// Track how much of each Discount is applied to an `OrderItem`.
// There is not currently API support for `OrderItem`-level `OrderAdjustment`s.
$adjustments = array();
global $edd_flat_discount_total;
foreach ( $discounts as $discount_id ) {
$edd_flat_discount_total = 0;
$d = edd_get_discount( $discount_id );
if ( ! $d ) {
continue;
}
// Retrieve total flat rate amount.
if ( 'flat' === $d->get_type() ) {
foreach ( $products as $product ) {
// This incremements the `$edd_flat_discount_total` global.
edd_get_item_discount_amount( $product, $products, array( $d ) );
}
}
// Store total discount and reset global.
$total_discount = $edd_flat_discount_total;
$item = array(
'id' => $download->id,
'quantity' => $quantity,
'options' => array(
'price_id' => $price_id,
),
);
$discount_amount = edd_get_item_discount_amount( $item, $products, array( $d ) );
if (
0 !== $discount_amount &&
'flat' === $d->get_type() &&
$item['id'] == end( $products )['id']
) {
if ( $total_discount < $d->get_amount() ) {
$adjustment = ( $d->get_amount() - $total_discount );
$discount_amount += $adjustment;
} else if ( $total_discount > $d->get_amount() ) {
$adjustment = ( $total_discount - $d->get_amount() );
$discount_amount -= $adjustment;
}
}
$adjustments[] = array(
'objectType' => 'order_item',
'type' => 'discount',
'typeId' => $d->id,
'description' => $d->code,
'subtotal' => $discount_amount,
'total' => $discount_amount,
);
$discount += $discount_amount;
}
if (
true === edd_use_taxes() &&
false === edd_download_is_tax_exclusive( $product_id )
) {
$tax = edd_calculate_tax( floatval( $subtotal - $discount ), $country, $region, false );
} else {
$tax = 0;
}
wp_send_json_success( array(
'amount' => $amount,
'subtotal' => $subtotal,
'discount' => $discount,
'tax' => $tax,
'total' => $subtotal + $tax,
'adjustments' => $adjustments,
) );
}
add_action( 'wp_ajax_edd-admin-order-get-item-amounts', 'edd_admin_order_get_item_amounts' );