2427 lines
69 KiB
PHP
2427 lines
69 KiB
PHP
<?php
|
|
/**
|
|
* Easy Digital Downloads API
|
|
*
|
|
* This class provides a front-facing JSON/XML API that makes it possible to
|
|
* query data from the shop.
|
|
*
|
|
* The primary purpose of this class is for external sales / earnings tracking
|
|
* systems, such as mobile. This class is also used in the EDD iOS App.
|
|
*
|
|
* @package EDD
|
|
* @subpackage Classes/API
|
|
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
|
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
|
* @since 1.5
|
|
* @since 3.0.4 Refactored to use the new stats API, returns same formatting as 2.x API.
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
use EDD\Stats;
|
|
use EDD\Reports;
|
|
|
|
/**
|
|
* EDD_API Class
|
|
*
|
|
* Renders API returns as a JSON/XML array
|
|
*
|
|
* @since 1.5
|
|
*/
|
|
class EDD_API {
|
|
|
|
/**
|
|
* Latest API Version
|
|
*/
|
|
const VERSION = 2;
|
|
|
|
/**
|
|
* Pretty Print?
|
|
*
|
|
* @var bool
|
|
* @access private
|
|
* @since 1.5
|
|
*/
|
|
private $pretty_print = false;
|
|
|
|
/**
|
|
* Log API requests?
|
|
*
|
|
* @var bool
|
|
* @access private
|
|
* @since 1.5
|
|
*/
|
|
public $log_requests = true;
|
|
|
|
/**
|
|
* Is this a valid request?
|
|
*
|
|
* @var bool
|
|
* @access private
|
|
* @since 1.5
|
|
*/
|
|
private $is_valid_request = false;
|
|
|
|
/**
|
|
* User ID Performing the API Request
|
|
*
|
|
* @var int
|
|
* @access private
|
|
* @since 1.5.1
|
|
*/
|
|
public $user_id = 0;
|
|
|
|
/**
|
|
* Instance of EDD Stats class
|
|
*
|
|
* @var object
|
|
* @access private
|
|
* @since 1.7
|
|
*/
|
|
private $stats;
|
|
|
|
/**
|
|
* Response data to return
|
|
*
|
|
* @var array
|
|
* @access private
|
|
* @since 1.5.2
|
|
*/
|
|
private $data = array();
|
|
|
|
/**
|
|
*
|
|
* @var bool
|
|
* @access private
|
|
* @since 1.7
|
|
*/
|
|
public $override = true;
|
|
|
|
/**
|
|
* Version of the API queried
|
|
*
|
|
* @var string
|
|
* @since 2.4
|
|
*/
|
|
private $queried_version;
|
|
|
|
/**
|
|
* All versions of the API
|
|
*
|
|
* @var string
|
|
* @since 2.4
|
|
*/
|
|
protected $versions = array();
|
|
|
|
/**
|
|
* Queried endpoint
|
|
*
|
|
* @var string
|
|
* @since 2.4
|
|
*/
|
|
private $endpoint;
|
|
|
|
/**
|
|
* Endpoints routes
|
|
*
|
|
* @var object
|
|
* @since 2.4
|
|
*/
|
|
private $routes;
|
|
|
|
/**
|
|
* Setup the EDD API
|
|
*
|
|
* @author Daniel J Griffiths
|
|
* @since 1.5
|
|
*/
|
|
public function __construct() {
|
|
|
|
$this->versions = array(
|
|
'v1' => 'EDD_API_V1',
|
|
'v2' => 'EDD_API_V2',
|
|
);
|
|
|
|
foreach ( $this->get_versions() as $version => $class ) {
|
|
require_once EDD_PLUGIN_DIR . 'includes/api/class-edd-api-' . $version . '.php';
|
|
}
|
|
|
|
add_action( 'init', array( $this, 'add_endpoint' ) );
|
|
add_action( 'wp', array( $this, 'process_query' ), - 1 );
|
|
add_filter( 'query_vars', array( $this, 'query_vars' ) );
|
|
add_action( 'edd_process_api_key', array( $this, 'process_api_key' ) );
|
|
|
|
// Setup a backwards compatibility check for user API Keys
|
|
add_filter( 'get_user_metadata', array( $this, 'api_key_backwards_copmat' ), 10, 4 );
|
|
|
|
// Determine if JSON_PRETTY_PRINT is available
|
|
$this->pretty_print = defined( 'JSON_PRETTY_PRINT' ) ? JSON_PRETTY_PRINT : null;
|
|
|
|
// Setup EDD_Stats instance
|
|
$this->stats = new EDD_Payment_Stats;
|
|
}
|
|
|
|
/**
|
|
* Registers a new rewrite endpoint for accessing the API
|
|
*
|
|
* @author Daniel J Griffiths
|
|
*
|
|
* @param array $rewrite_rules WordPress Rewrite Rules
|
|
*
|
|
* @since 1.5
|
|
*/
|
|
public function add_endpoint( $rewrite_rules ) {
|
|
add_rewrite_endpoint( 'edd-api', EP_ALL );
|
|
}
|
|
|
|
/**
|
|
* Registers query vars for API access
|
|
*
|
|
* @since 1.5
|
|
* @author Daniel J Griffiths
|
|
*
|
|
* @param array $vars Query vars
|
|
*
|
|
* @return string[] $vars New query vars
|
|
*/
|
|
public function query_vars( $vars ) {
|
|
|
|
$vars[] = 'token';
|
|
$vars[] = 'key';
|
|
$vars[] = 'query';
|
|
$vars[] = 'type';
|
|
$vars[] = 'product';
|
|
$vars[] = 'category';
|
|
$vars[] = 'tag';
|
|
$vars[] = 'term_relation';
|
|
$vars[] = 'number';
|
|
$vars[] = 'date';
|
|
$vars[] = 'startdate';
|
|
$vars[] = 'enddate';
|
|
$vars[] = 'customer';
|
|
$vars[] = 'discount';
|
|
$vars[] = 'format';
|
|
$vars[] = 'id';
|
|
$vars[] = 'purchasekey';
|
|
$vars[] = 'email';
|
|
$vars[] = 'info';
|
|
$vars[] = 'include_tax';
|
|
|
|
return $vars;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the API versions
|
|
*
|
|
* @since 2.4
|
|
* @return array
|
|
*/
|
|
public function get_versions() {
|
|
return $this->versions;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the API version that was queried
|
|
*
|
|
* @since 2.4
|
|
* @return string
|
|
*/
|
|
public function get_queried_version() {
|
|
return $this->queried_version;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the default version of the API to use
|
|
*
|
|
* @access private
|
|
* @since 2.4
|
|
* @return string
|
|
*/
|
|
public function get_default_version() {
|
|
|
|
$version = get_option( 'edd_default_api_version' );
|
|
|
|
if ( defined( 'EDD_API_VERSION' ) ) {
|
|
$version = EDD_API_VERSION;
|
|
} elseif ( ! $version ) {
|
|
$version = 'v1';
|
|
}
|
|
|
|
return $version;
|
|
}
|
|
|
|
/**
|
|
* Sets the version of the API that was queried.
|
|
*
|
|
* Falls back to the default version if no version is specified
|
|
*
|
|
* @access private
|
|
* @since 2.4
|
|
*/
|
|
private function set_queried_version() {
|
|
|
|
global $wp_query;
|
|
|
|
$version = $wp_query->query_vars['edd-api'];
|
|
|
|
if ( strpos( $version, '/' ) ) {
|
|
|
|
$version = explode( '/', $version );
|
|
$version = strtolower( $version[0] );
|
|
|
|
$wp_query->query_vars['edd-api'] = str_replace( $version . '/', '', $wp_query->query_vars['edd-api'] );
|
|
|
|
if ( array_key_exists( $version, $this->versions ) ) {
|
|
|
|
$this->queried_version = $version;
|
|
|
|
} else {
|
|
|
|
$this->is_valid_request = false;
|
|
$this->invalid_version();
|
|
}
|
|
|
|
} else {
|
|
|
|
$this->queried_version = $this->get_default_version();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Validate the API request
|
|
*
|
|
* Checks for the user's public key and token against the secret key
|
|
*
|
|
* @access private
|
|
* @global object $wp_query WordPress Query
|
|
* @uses EDD_API::get_user()
|
|
* @uses EDD_API::invalid_key()
|
|
* @uses EDD_API::invalid_auth()
|
|
* @since 1.5
|
|
* @return bool
|
|
*/
|
|
private function validate_request() {
|
|
global $wp_query;
|
|
|
|
$this->override = false;
|
|
|
|
// Make sure we have both user and api key
|
|
if ( ! empty( $wp_query->query_vars['edd-api'] ) && ( ! $this->is_public_query() || ! empty( $wp_query->query_vars['token'] ) ) ) {
|
|
|
|
if ( empty( $wp_query->query_vars['token'] ) || empty( $wp_query->query_vars['key'] ) ) {
|
|
$this->missing_auth();
|
|
|
|
return false;
|
|
}
|
|
|
|
// Auth was provided, include the upgrade routine so we can use the fallback api checks
|
|
require_once EDD_PLUGIN_DIR . 'includes/admin/upgrades/upgrade-functions.php';
|
|
|
|
// Retrieve the user by public API key and ensure they exist
|
|
if ( ! ( $user = $this->get_user( $wp_query->query_vars['key'] ) ) ) {
|
|
|
|
$this->invalid_key();
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
$token = urldecode( $wp_query->query_vars['token'] );
|
|
$secret = $this->get_user_secret_key( $user );
|
|
$public = urldecode( $wp_query->query_vars['key'] );
|
|
|
|
// Verify that if user has secret key or not.
|
|
if ( ! $secret ) {
|
|
$this->invalid_auth();
|
|
}
|
|
|
|
$valid = $this->check_keys( $secret, $public, $token );
|
|
if ( $valid ) {
|
|
$this->is_valid_request = true;
|
|
} else {
|
|
$this->invalid_auth();
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|
|
} elseif ( ! empty( $wp_query->query_vars['edd-api'] ) && $this->is_public_query() ) {
|
|
$this->is_valid_request = true;
|
|
$wp_query->set( 'key', 'public' );
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Return whether this is a public query.
|
|
*
|
|
* @access private
|
|
* @global object $wp_query WordPress Query
|
|
* @since 2.6
|
|
* @return boolean
|
|
*/
|
|
private function is_public_query() {
|
|
global $wp_query;
|
|
|
|
$public_modes = apply_filters( 'edd_api_public_query_modes', array(
|
|
'products',
|
|
) );
|
|
|
|
return in_array( $wp_query->query_vars['edd-api'], $public_modes );
|
|
}
|
|
|
|
/**
|
|
* Retrieve the user ID based on the public key provided
|
|
*
|
|
* @since 1.5.1
|
|
* @global object $wpdb Used to query the database using the WordPress
|
|
* Database API
|
|
*
|
|
* @param string $key Public Key
|
|
*
|
|
* @return bool if user ID is found, false otherwise
|
|
*/
|
|
public function get_user( $key = '' ) {
|
|
global $wpdb, $wp_query;
|
|
|
|
if ( empty( $key ) ) {
|
|
$key = urldecode( $wp_query->query_vars['key'] );
|
|
}
|
|
|
|
if ( empty( $key ) ) {
|
|
return false;
|
|
}
|
|
|
|
$user = get_transient( md5( 'edd_api_user_' . $key ) );
|
|
|
|
if ( false === $user ) {
|
|
$user = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s LIMIT 1", $key ) );
|
|
set_transient( md5( 'edd_api_user_' . $key ), $user, DAY_IN_SECONDS );
|
|
}
|
|
|
|
if ( $user != null ) {
|
|
$this->user_id = $user;
|
|
|
|
return $user;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function get_user_public_key( $user_id = 0 ) {
|
|
global $wpdb;
|
|
|
|
if ( empty( $user_id ) ) {
|
|
return '';
|
|
}
|
|
|
|
$cache_key = md5( 'edd_api_user_public_key' . $user_id );
|
|
$user_public_key = get_transient( $cache_key );
|
|
|
|
if ( empty( $user_public_key ) ) {
|
|
$user_public_key = $wpdb->get_var( $wpdb->prepare( "SELECT meta_key FROM $wpdb->usermeta WHERE meta_value = 'edd_user_public_key' AND user_id = %d", $user_id ) );
|
|
set_transient( $cache_key, $user_public_key, HOUR_IN_SECONDS );
|
|
}
|
|
|
|
return $user_public_key;
|
|
}
|
|
|
|
public function get_user_secret_key( $user_id = 0 ) {
|
|
global $wpdb;
|
|
|
|
if ( empty( $user_id ) ) {
|
|
return '';
|
|
}
|
|
|
|
$cache_key = md5( 'edd_api_user_secret_key' . $user_id );
|
|
$user_secret_key = get_transient( $cache_key );
|
|
|
|
if ( empty( $user_secret_key ) ) {
|
|
$user_secret_key = $wpdb->get_var( $wpdb->prepare( "SELECT meta_key FROM $wpdb->usermeta WHERE meta_value = 'edd_user_secret_key' AND user_id = %d", $user_id ) );
|
|
set_transient( $cache_key, $user_secret_key, HOUR_IN_SECONDS );
|
|
}
|
|
|
|
return $user_secret_key;
|
|
}
|
|
|
|
/**
|
|
* Displays a missing authentication error if all the parameters aren't
|
|
* provided
|
|
*
|
|
* @access private
|
|
* @author Daniel J Griffiths
|
|
* @uses EDD_API::output()
|
|
* @since 1.5
|
|
*/
|
|
private function missing_auth() {
|
|
$error = array();
|
|
$error['error'] = __( 'You must specify both a token and API key!', 'easy-digital-downloads' );
|
|
|
|
$this->data = $error;
|
|
$this->output( 401 );
|
|
}
|
|
|
|
/**
|
|
* Displays an authentication failed error if the user failed to provide valid
|
|
* credentials
|
|
*
|
|
* @access private
|
|
* @since 1.5
|
|
* @uses EDD_API::output()
|
|
* @return void
|
|
*/
|
|
private function invalid_auth() {
|
|
$error = array();
|
|
$error['error'] = __( 'Your request could not be authenticated!', 'easy-digital-downloads' );
|
|
|
|
$this->data = $error;
|
|
$this->output( 403 );
|
|
}
|
|
|
|
/**
|
|
* Displays an invalid API key error if the API key provided couldn't be
|
|
* validated
|
|
*
|
|
* @access private
|
|
* @author Daniel J Griffiths
|
|
* @since 1.5
|
|
* @uses EDD_API::output()
|
|
* @return void
|
|
*/
|
|
private function invalid_key() {
|
|
$error = array();
|
|
$error['error'] = __( 'Invalid API key!', 'easy-digital-downloads' );
|
|
|
|
$this->data = $error;
|
|
$this->output( 403 );
|
|
}
|
|
|
|
/**
|
|
* Displays an invalid version error if the version number passed isn't valid
|
|
*
|
|
* @access private
|
|
* @since 2.4
|
|
* @uses EDD_API::output()
|
|
* @return void
|
|
*/
|
|
private function invalid_version() {
|
|
$error = array();
|
|
$error['error'] = __( 'Invalid API version!', 'easy-digital-downloads' );
|
|
|
|
$this->data = $error;
|
|
$this->output( 404 );
|
|
}
|
|
|
|
/**
|
|
* Listens for the API and then processes the API requests
|
|
*
|
|
* @global $wp_query
|
|
* @since 1.5
|
|
* @return void
|
|
*/
|
|
public function process_query() {
|
|
|
|
global $wp_query;
|
|
|
|
// Start logging how long the request takes for logging
|
|
$before = microtime( true );
|
|
|
|
// Check for edd-api var. Get out if not present
|
|
if ( empty( $wp_query->query_vars['edd-api'] ) ) {
|
|
return;
|
|
}
|
|
|
|
// Determine which version was queried
|
|
$this->set_queried_version();
|
|
|
|
// Determine the kind of query
|
|
$this->set_query_mode();
|
|
|
|
// Check for a valid user and set errors if necessary
|
|
$this->validate_request();
|
|
|
|
// Only proceed if no errors have been noted
|
|
if ( ! $this->is_valid_request ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! defined( 'EDD_DOING_API' ) ) {
|
|
define( 'EDD_DOING_API', true );
|
|
}
|
|
|
|
$data = array();
|
|
$version = $this->get_queried_version();
|
|
$this->routes = new $this->versions[ $version ];
|
|
$this->routes->validate_request();
|
|
|
|
switch ( $this->endpoint ) :
|
|
|
|
case 'stats' :
|
|
$data = $this->routes->get_stats( array(
|
|
'type' => isset( $wp_query->query_vars['type'] ) ? $wp_query->query_vars['type'] : null,
|
|
'product' => isset( $wp_query->query_vars['product'] ) ? $wp_query->query_vars['product'] : null,
|
|
'date' => isset( $wp_query->query_vars['date'] ) ? $wp_query->query_vars['date'] : null,
|
|
'startdate' => isset( $wp_query->query_vars['startdate'] ) ? $wp_query->query_vars['startdate'] : null,
|
|
'enddate' => isset( $wp_query->query_vars['enddate'] ) ? $wp_query->query_vars['enddate'] : null,
|
|
'include_tax' => isset( $wp_query->query_vars['include_tax'] ) ? filter_var( $wp_query->query_vars['include_tax'], FILTER_VALIDATE_BOOLEAN ) : true,
|
|
) );
|
|
|
|
break;
|
|
|
|
case 'products' :
|
|
|
|
$args = array(
|
|
'product' => isset( $wp_query->query_vars['product'] ) ? absint( $wp_query->query_vars['product'] ) : null,
|
|
'category' => isset( $wp_query->query_vars['category'] ) ? $this->sanitize_request_term( $wp_query->query_vars['category'] ) : null,
|
|
'tag' => isset( $wp_query->query_vars['tag'] ) ? $this->sanitize_request_term( $wp_query->query_vars['tag'] ) : null,
|
|
'term_relation' => isset( $wp_query->query_vars['term_relation'] ) ? $this->sanitize_request_term( $wp_query->query_vars['term_relation'] ) : null,
|
|
's' => isset( $wp_query->query_vars['s'] ) ? sanitize_text_field( $wp_query->query_vars['s'] ) : null,
|
|
'order' => isset( $wp_query->query_vars['order'] ) ? $wp_query->query_vars['order'] : 'DESC',
|
|
'orderby' => isset( $wp_query->query_vars['orderby'] ) ? $wp_query->query_vars['orderby'] : 'date',
|
|
);
|
|
|
|
$data = $this->routes->get_products( $args );
|
|
|
|
break;
|
|
|
|
case 'customers' :
|
|
|
|
$args = array(
|
|
'customer' => isset( $wp_query->query_vars['customer'] ) ? $wp_query->query_vars['customer'] : null,
|
|
'date' => isset( $wp_query->query_vars['date'] ) ? $wp_query->query_vars['date'] : null,
|
|
'startdate' => isset( $wp_query->query_vars['startdate'] ) ? $wp_query->query_vars['startdate'] : null,
|
|
'enddate' => isset( $wp_query->query_vars['enddate'] ) ? $wp_query->query_vars['enddate'] : null,
|
|
);
|
|
|
|
$data = $this->routes->get_customers( $args );
|
|
|
|
break;
|
|
|
|
case 'sales' :
|
|
|
|
$data = $this->routes->get_recent_sales();
|
|
|
|
break;
|
|
|
|
case 'discounts' :
|
|
|
|
$discount = isset( $wp_query->query_vars['discount'] ) ? $wp_query->query_vars['discount'] : null;
|
|
|
|
$data = $this->routes->get_discounts( $discount );
|
|
|
|
break;
|
|
|
|
case 'file-download-logs' :
|
|
|
|
$customer = isset( $wp_query->query_vars['customer'] ) ? $wp_query->query_vars['customer'] : null;
|
|
|
|
$data = $this->get_download_logs( $customer );
|
|
|
|
break;
|
|
|
|
case 'info' :
|
|
|
|
$data = $this->routes->get_info();
|
|
|
|
break;
|
|
|
|
endswitch;
|
|
|
|
// Allow extensions to setup their own return data
|
|
$this->data = apply_filters( 'edd_api_output_data', $data, $this->endpoint, $this );
|
|
|
|
$after = microtime( true );
|
|
$request_time = ( $after - $before );
|
|
$this->data['request_speed'] = $request_time;
|
|
|
|
// Log this API request, if enabled. We log it here because we have access to errors.
|
|
$this->log_request( $this->data );
|
|
|
|
// Send out data to the output function
|
|
$this->output();
|
|
}
|
|
|
|
/**
|
|
* Returns the API endpoint requested
|
|
*
|
|
* @access private
|
|
* @since 1.5
|
|
* @return string $query Query mode
|
|
*/
|
|
public function get_query_mode() {
|
|
|
|
return $this->endpoint;
|
|
}
|
|
|
|
/**
|
|
* Determines the kind of query requested and also ensure it is a valid query
|
|
*
|
|
* @access private
|
|
* @since 2.4
|
|
* @global $wp_query
|
|
*/
|
|
public function set_query_mode() {
|
|
|
|
global $wp_query;
|
|
|
|
// Whitelist our query options
|
|
$accepted = apply_filters( 'edd_api_valid_query_modes', array(
|
|
'stats',
|
|
'products',
|
|
'customers',
|
|
'sales',
|
|
'discounts',
|
|
'file-download-logs',
|
|
'info',
|
|
) );
|
|
|
|
$query = isset( $wp_query->query_vars['edd-api'] ) ? $wp_query->query_vars['edd-api'] : null;
|
|
$query = str_replace( $this->queried_version . '/', '', $query );
|
|
|
|
$error = array();
|
|
|
|
// Make sure our query is valid
|
|
if ( ! in_array( $query, $accepted ) ) {
|
|
$error['error'] = __( 'Invalid query!', 'easy-digital-downloads' );
|
|
|
|
$this->data = $error;
|
|
// 400 is Bad Request
|
|
$this->output( 400 );
|
|
}
|
|
|
|
$this->endpoint = $query;
|
|
}
|
|
|
|
/**
|
|
* Get page number
|
|
*
|
|
* @access private
|
|
* @since 1.5
|
|
* @global $wp_query
|
|
* @return int $wp_query->query_vars['page'] if page number returned (default: 1)
|
|
*/
|
|
public function get_paged() {
|
|
global $wp_query;
|
|
|
|
return isset( $wp_query->query_vars['page'] ) ? $wp_query->query_vars['page'] : 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Number of results to display per page
|
|
*
|
|
* @access private
|
|
* @since 1.5
|
|
* @global $wp_query
|
|
* @return int $per_page Results to display per page (default: 10)
|
|
*/
|
|
public function per_page() {
|
|
global $wp_query;
|
|
|
|
$per_page = isset( $wp_query->query_vars['number'] ) ? $wp_query->query_vars['number'] : 10;
|
|
|
|
if ( $per_page < 0 && $this->get_query_mode() == 'customers' ) {
|
|
$per_page = 99999999; // Customers query doesn't support -1
|
|
}
|
|
|
|
return apply_filters( 'edd_api_results_per_page', $per_page );
|
|
}
|
|
|
|
/**
|
|
* Sets up the dates used to retrieve earnings/sales
|
|
*
|
|
* @since 1.5.1
|
|
*
|
|
* @param array $args Arguments to override defaults
|
|
*
|
|
* @return array $dates
|
|
*/
|
|
public function get_dates( $args = array() ) {
|
|
$dates = array();
|
|
|
|
$defaults = array(
|
|
'type' => '',
|
|
'product' => null,
|
|
'date' => null,
|
|
'startdate' => null,
|
|
'enddate' => null,
|
|
);
|
|
|
|
$args = wp_parse_args( $args, $defaults );
|
|
|
|
$current_time = current_time( 'timestamp' );
|
|
|
|
if ( 'range' === $args['date'] ) {
|
|
$startdate = strtotime( $args['startdate'] );
|
|
$enddate = strtotime( $args['enddate'] );
|
|
$dates['day_start'] = date( 'd', $startdate );
|
|
$dates['day_end'] = date( 'd', $enddate );
|
|
$dates['m_start'] = date( 'n', $startdate );
|
|
$dates['m_end'] = date( 'n', $enddate );
|
|
$dates['year'] = date( 'Y', $startdate );
|
|
$dates['year_end'] = date( 'Y', $enddate );
|
|
} else {
|
|
// Modify dates based on predefined ranges
|
|
switch ( $args['date'] ) :
|
|
|
|
case 'this_month' :
|
|
$dates['day'] = 1;
|
|
$dates['day_end'] = date( 't', $current_time );
|
|
$dates['m_start'] = date( 'n', $current_time );
|
|
$dates['m_end'] = date( 'n', $current_time );
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
break;
|
|
|
|
case 'last_month' :
|
|
$dates['day'] = 1;
|
|
$dates['m_start'] = date( 'n', $current_time ) == 1 ? 12 : date( 'n', $current_time ) - 1;
|
|
$dates['m_end'] = $dates['m_start'];
|
|
$dates['year'] = date( 'n', $current_time ) == 1 ? date( 'Y', $current_time ) - 1 : date( 'Y', $current_time );
|
|
$dates['day_end'] = date( 't', strtotime( $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day'] ) );
|
|
break;
|
|
|
|
case 'today' :
|
|
$dates['day'] = date( 'd', $current_time );
|
|
$dates['day_end'] = date( 'd', $current_time );
|
|
$dates['m_start'] = date( 'n', $current_time );
|
|
$dates['m_end'] = date( 'n', $current_time );
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
break;
|
|
|
|
case 'yesterday' :
|
|
|
|
$year = date( 'Y', $current_time );
|
|
$month = date( 'n', $current_time );
|
|
$day = date( 'd', $current_time );
|
|
|
|
if ( $month == 1 && $day == 1 ) {
|
|
|
|
$year -= 1;
|
|
$month = 12;
|
|
$day = cal_days_in_month( CAL_GREGORIAN, $month, $year );
|
|
|
|
} elseif ( $month > 1 && $day == 1 ) {
|
|
|
|
$month -= 1;
|
|
$day = cal_days_in_month( CAL_GREGORIAN, $month, $year );
|
|
|
|
} else {
|
|
|
|
$day -= 1;
|
|
|
|
}
|
|
|
|
$dates['day'] = $day;
|
|
$dates['day_end'] = $day;
|
|
$dates['m_start'] = $month;
|
|
$dates['m_end'] = $month;
|
|
$dates['year'] = $year;
|
|
|
|
break;
|
|
|
|
case 'this_quarter' :
|
|
$month_now = date( 'n', $current_time );
|
|
|
|
$dates['day'] = 1;
|
|
|
|
if ( $month_now <= 3 ) {
|
|
|
|
$dates['m_start'] = 1;
|
|
$dates['m_end'] = 3;
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
|
|
} else if ( $month_now <= 6 ) {
|
|
|
|
$dates['m_start'] = 4;
|
|
$dates['m_end'] = 6;
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
|
|
} else if ( $month_now <= 9 ) {
|
|
|
|
$dates['m_start'] = 7;
|
|
$dates['m_end'] = 9;
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
|
|
} else {
|
|
|
|
$dates['m_start'] = 10;
|
|
$dates['m_end'] = 12;
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
|
|
}
|
|
|
|
$dates['day_end'] = date( 't', strtotime( $dates['year'] . '-' . $dates['m_end'] ) );
|
|
|
|
break;
|
|
|
|
case 'last_quarter' :
|
|
$month_now = date( 'n', $current_time );
|
|
|
|
$dates['day'] = 1;
|
|
|
|
if ( $month_now <= 3 ) {
|
|
|
|
$dates['m_start'] = 10;
|
|
$dates['m_end'] = 12;
|
|
$dates['year'] = date( 'Y', $current_time ) - 1; // Previous year
|
|
|
|
} else if ( $month_now <= 6 ) {
|
|
|
|
$dates['m_start'] = 1;
|
|
$dates['m_end'] = 3;
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
|
|
} else if ( $month_now <= 9 ) {
|
|
|
|
$dates['m_start'] = 4;
|
|
$dates['m_end'] = 6;
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
|
|
} else {
|
|
|
|
$dates['m_start'] = 7;
|
|
$dates['m_end'] = 9;
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
|
|
}
|
|
|
|
$dates['day_end'] = date( 't', strtotime( $dates['year'] . '-' . $dates['m_end'] ) );
|
|
break;
|
|
|
|
case 'this_year' :
|
|
$dates['day'] = 1;
|
|
$dates['m_start'] = 1;
|
|
$dates['m_end'] = 12;
|
|
$dates['day_end'] = 31;
|
|
$dates['year'] = date( 'Y', $current_time );
|
|
break;
|
|
|
|
case 'last_year' :
|
|
$dates['day'] = 1;
|
|
$dates['m_start'] = 1;
|
|
$dates['m_end'] = 12;
|
|
$dates['day_end'] = 31;
|
|
$dates['year'] = date( 'Y', $current_time ) - 1;
|
|
break;
|
|
|
|
case 'this_week' :
|
|
case 'last_week' :
|
|
$start_of_week = get_option( 'start_of_week' );
|
|
|
|
if ( 'last_week' === $args['date'] ) {
|
|
$today = date( 'd', $current_time - WEEK_IN_SECONDS );
|
|
} else {
|
|
$today = date( 'd', $current_time );
|
|
}
|
|
|
|
$day_of_the_week = date( 'w', $current_time );
|
|
$month = date( 'n', $current_time );
|
|
$year = date( 'Y', $current_time );
|
|
|
|
// Account for a week the spans a month change (including if that week spans over a break in the year).
|
|
if ( ( $today - $day_of_the_week ) < 1 ) {
|
|
$start_date = date( 'd', strtotime( $year . '-' . $month . '-' . $today . ' -' . $day_of_the_week . ' days' ) );
|
|
$month = $month > 1 ? $month -- : 12;
|
|
$adjusted_month = true;
|
|
} else {
|
|
$start_date = $today - $day_of_the_week;
|
|
$adjusted_month = false;
|
|
}
|
|
|
|
// Account for the WordPress Start of Week setting.
|
|
$adjusted_start_date = date( 'd', strtotime( $year . '-' . $month . '-' . $start_date . ' +' . $start_of_week . 'days' ) );
|
|
|
|
/**
|
|
* Account for when the base start of the week is the end of one month, but the WordPress Start of Week setting
|
|
* Jumps it to the following month.
|
|
*/
|
|
if ( $adjusted_start_date < $start_date ) {
|
|
if ( 12 === $month ) {
|
|
$month = 1;
|
|
$year ++;
|
|
} else {
|
|
$month ++;
|
|
}
|
|
}
|
|
|
|
$dates['day'] = $adjusted_start_date;
|
|
$dates['m_start'] = $month;
|
|
$dates['year'] = $month === 12 && $adjusted_month ? $year - 1 : $year;
|
|
|
|
$base_start_date = $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day'];
|
|
$base_start_timestamp = strtotime( $base_start_date . ' +6 days' );
|
|
$dates['m_end'] = date( 'n', $base_start_timestamp );
|
|
$dates['day_end'] = date( 'd', $base_start_timestamp );
|
|
$dates['year_end'] = date( 'Y', $base_start_timestamp );
|
|
break;
|
|
|
|
endswitch;
|
|
}
|
|
|
|
/**
|
|
* Returns the filters for the dates used to retreive earnings/sales
|
|
*
|
|
* @since 1.5.1
|
|
*
|
|
* @param object $dates The dates used for retreiving earnings/sales
|
|
*/
|
|
|
|
return apply_filters( 'edd_api_stat_dates', $dates );
|
|
}
|
|
|
|
/**
|
|
* Process Get Customers API Request
|
|
*
|
|
* @since 1.5
|
|
* @author Daniel J Griffiths
|
|
*
|
|
* @param int $customer Customer ID
|
|
*
|
|
* @return array $customers Multidimensional array of the customers
|
|
*/
|
|
public function get_customers( $customer = null ) {
|
|
|
|
$customer = is_array( $customer ) ? $customer['customer'] : $customer;
|
|
$customers = array();
|
|
$error = array();
|
|
|
|
if ( ! user_can( $this->user_id, 'view_shop_sensitive_data' ) && ! $this->override ) {
|
|
return $customers;
|
|
}
|
|
|
|
$paged = $this->get_paged();
|
|
$per_page = $this->per_page();
|
|
$offset = $per_page * ( $paged - 1 );
|
|
|
|
if ( is_numeric( $customer ) ) {
|
|
$field = 'id';
|
|
} elseif ( is_array( $customer ) ) {
|
|
// Checking if search is being done by id, email, user_id fields.
|
|
if ( array_key_exists( 'id', $customer ) ) {
|
|
$field = 'id';
|
|
} elseif ( array_key_exists( 'email', $customer ) ) {
|
|
$field = 'email';
|
|
} elseif ( array_key_exists( 'user_id', $customer ) ) {
|
|
$field = 'user_id';
|
|
}
|
|
|
|
$customer = $customer[ $field ];
|
|
} else {
|
|
$field = 'email';
|
|
}
|
|
|
|
$args = array(
|
|
'number' => $per_page,
|
|
'offset' => $offset
|
|
);
|
|
|
|
if ( ! is_null( $customer ) ) {
|
|
$args[ $field ] = $customer;
|
|
}
|
|
|
|
$customer_query = edd_get_customers( $args );
|
|
$customer_count = 0;
|
|
|
|
if ( $customer_query ) {
|
|
|
|
foreach ( $customer_query as $customer_obj ) {
|
|
|
|
$names = explode( ' ', $customer_obj->name );
|
|
$first_name = ! empty( $names[0] ) ? $names[0] : '';
|
|
$last_name = '';
|
|
if ( ! empty( $names[1] ) ) {
|
|
unset( $names[0] );
|
|
$last_name = implode( ' ', $names );
|
|
}
|
|
|
|
$customers['customers'][ $customer_count ]['info']['id'] = '';
|
|
$customers['customers'][ $customer_count ]['info']['user_id'] = '';
|
|
$customers['customers'][ $customer_count ]['info']['username'] = '';
|
|
$customers['customers'][ $customer_count ]['info']['display_name'] = '';
|
|
$customers['customers'][ $customer_count ]['info']['customer_id'] = $customer_obj->id;
|
|
$customers['customers'][ $customer_count ]['info']['first_name'] = $first_name;
|
|
$customers['customers'][ $customer_count ]['info']['last_name'] = $last_name;
|
|
$customers['customers'][ $customer_count ]['info']['email'] = $customer_obj->email;
|
|
|
|
if ( ! empty( $customer_obj->user_id ) && $customer_obj->user_id > 0 ) {
|
|
|
|
$user_data = get_userdata( $customer_obj->user_id );
|
|
|
|
// Customer with registered account
|
|
|
|
// id is going to get deprecated in the future, user user_id or customer_id instead
|
|
$customers['customers'][ $customer_count ]['info']['id'] = $customer_obj->user_id;
|
|
$customers['customers'][ $customer_count ]['info']['user_id'] = $customer_obj->user_id;
|
|
$customers['customers'][ $customer_count ]['info']['username'] = $user_data->user_login;
|
|
$customers['customers'][ $customer_count ]['info']['display_name'] = $user_data->display_name;
|
|
|
|
}
|
|
|
|
$customers['customers'][ $customer_count ]['stats']['total_purchases'] = $customer_obj->purchase_count;
|
|
$customers['customers'][ $customer_count ]['stats']['total_spent'] = $customer_obj->purchase_value;
|
|
$customers['customers'][ $customer_count ]['stats']['total_downloads'] = edd_count_file_downloads_of_customer( $customer_obj->id );
|
|
|
|
$customer_count ++;
|
|
|
|
}
|
|
|
|
} elseif ( $customer ) {
|
|
|
|
$error['error'] = sprintf( __( 'Customer %s not found!', 'easy-digital-downloads' ), $customer );
|
|
|
|
return $error;
|
|
|
|
} else {
|
|
|
|
$error['error'] = __( 'No customers found!', 'easy-digital-downloads' );
|
|
|
|
return $error;
|
|
|
|
}
|
|
|
|
return apply_filters( 'edd_api_customers', $customers, $this );
|
|
}
|
|
|
|
/**
|
|
* Process Get Products API Request
|
|
*
|
|
* @author Daniel J Griffiths
|
|
* @since 1.5
|
|
*
|
|
* @param array $args
|
|
*
|
|
* @return array $customers Multidimensional array of the products
|
|
*/
|
|
public function get_products( $args = array() ) {
|
|
$products = array();
|
|
$error = array();
|
|
|
|
if ( empty( $args['product'] ) ) {
|
|
$products['products'] = array();
|
|
|
|
$parameters = array(
|
|
'post_type' => 'download',
|
|
'posts_per_page' => $this->per_page(),
|
|
'suppress_filters' => true,
|
|
'paged' => $this->get_paged(),
|
|
);
|
|
|
|
if ( isset( $args['s'] ) && ! empty( $args['s'] ) ) {
|
|
$parameters['s'] = $args['s'];
|
|
}
|
|
|
|
$product_list = get_posts( $parameters );
|
|
|
|
if ( $product_list ) {
|
|
$i = 0;
|
|
foreach ( $product_list as $product_info ) {
|
|
$products['products'][ $i ] = $this->get_product_data( $product_info );
|
|
$i ++;
|
|
}
|
|
}
|
|
} else {
|
|
if ( get_post_type( $args['product'] ) == 'download' ) {
|
|
$product_info = get_post( $args['product'] );
|
|
|
|
$products['products'][0] = $this->get_product_data( $product_info );
|
|
} else {
|
|
$error['error'] = sprintf( __( 'Product %s not found!', 'easy-digital-downloads' ), $args['product'] );
|
|
|
|
return $error;
|
|
}
|
|
}
|
|
|
|
return apply_filters( 'edd_api_products', $products, $this );
|
|
}
|
|
|
|
/**
|
|
* Given a download post object, generate the data for the API output
|
|
*
|
|
* @since 2.3.9
|
|
*
|
|
* @param object $product_info The Download Post Object
|
|
*
|
|
* @return array Array of post data to return back in the API
|
|
*/
|
|
public function get_product_data( $product_info ) {
|
|
|
|
$product = array();
|
|
|
|
$product['info']['id'] = $product_info->ID;
|
|
$product['info']['slug'] = $product_info->post_name;
|
|
$product['info']['title'] = $product_info->post_title;
|
|
$product['info']['create_date'] = $product_info->post_date;
|
|
$product['info']['modified_date'] = $product_info->post_modified;
|
|
$product['info']['status'] = $product_info->post_status;
|
|
$product['info']['link'] = html_entity_decode( $product_info->guid );
|
|
$product['info']['permalink'] = html_entity_decode( get_permalink( $product_info->ID ) );
|
|
$product['info']['content'] = $product_info->post_content;
|
|
$product['info']['excerpt'] = $product_info->post_excerpt;
|
|
$product['info']['thumbnail'] = wp_get_attachment_url( get_post_thumbnail_id( $product_info->ID ) );
|
|
$product['info']['category'] = get_the_terms( $product_info, 'download_category' );
|
|
$product['info']['tags'] = get_the_terms( $product_info, 'download_tag' );
|
|
|
|
if ( user_can( $this->user_id, 'view_shop_reports' ) || $this->override ) {
|
|
$product['stats']['total']['sales'] = edd_get_download_sales_stats( $product_info->ID );
|
|
$product['stats']['total']['earnings'] = edd_get_download_earnings_stats( $product_info->ID );
|
|
$product['stats']['monthly_average']['sales'] = edd_get_average_monthly_download_sales( $product_info->ID );
|
|
$product['stats']['monthly_average']['earnings'] = edd_get_average_monthly_download_earnings( $product_info->ID );
|
|
}
|
|
|
|
if ( edd_has_variable_prices( $product_info->ID ) ) {
|
|
foreach ( edd_get_variable_prices( $product_info->ID ) as $price ) {
|
|
$product['pricing'][ sanitize_key( $price['name'] ) ] = $price['amount'];
|
|
}
|
|
} else {
|
|
$product['pricing']['amount'] = edd_get_download_price( $product_info->ID );
|
|
}
|
|
|
|
if ( user_can( $this->user_id, 'view_shop_sensitive_data' ) || $this->override ) {
|
|
foreach ( edd_get_download_files( $product_info->ID ) as $file ) {
|
|
$product['files'][] = $file;
|
|
}
|
|
$product['notes'] = edd_get_product_notes( $product_info->ID );
|
|
}
|
|
|
|
return apply_filters( 'edd_api_products_product', $product );
|
|
|
|
}
|
|
|
|
/**
|
|
* Process Get Stats API Request
|
|
*
|
|
* @author Daniel J Griffiths
|
|
* @since 1.5
|
|
*
|
|
* @global object $wpdb Used to query the database using the WordPress
|
|
*
|
|
* @param array $args Arguments provided by API Request
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_stats( $args = array() ) {
|
|
|
|
// Parse args
|
|
$args = wp_parse_args( $args, array(
|
|
'type' => null,
|
|
'product' => null,
|
|
'date' => null,
|
|
'startdate' => null,
|
|
'enddate' => null,
|
|
'include_tax' => true,
|
|
) );
|
|
|
|
$dates = $this->get_dates( $args );
|
|
|
|
$stats = array();
|
|
$earnings = array(
|
|
'earnings' => array(),
|
|
);
|
|
$sales = array(
|
|
'sales' => array(),
|
|
);
|
|
$error = array();
|
|
|
|
if ( ! user_can( $this->user_id, 'view_shop_reports' ) && ! $this->override ) {
|
|
return $stats;
|
|
}
|
|
|
|
if ( 'sales' === $args['type'] ) {
|
|
if ( null === $args['product'] ) {
|
|
if ( null === $args['date'] ) {
|
|
$sales = $this->get_default_sales_stats();
|
|
} elseif ( 'range' === $args['date'] ) {
|
|
// Ensure the end date is later than the start date.
|
|
if ( $args['enddate'] < $args['startdate'] ) {
|
|
$error['error'] = __( 'The end date must be later than the start date!', 'easy-digital-downloads' );
|
|
}
|
|
|
|
// Ensure both the start and end date are specified.
|
|
if ( empty( $args['startdate'] ) || empty( $args['enddate'] ) ) {
|
|
$error['error'] = __( 'Invalid or no date range specified!', 'easy-digital-downloads' );
|
|
}
|
|
|
|
$start_date = EDD()->utils->date( $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day_start'], edd_get_timezone_id(), false )->startOfDay();
|
|
$end_date = EDD()->utils->date( $dates['year_end'] . '-' . $dates['m_end'] . '-' . $dates['day_end'], edd_get_timezone_id(), false )->endOfDay();
|
|
|
|
// Force the data for the reports API.
|
|
$_GET['filter_from'] = $start_date->format( 'Y-m-d' );
|
|
$_GET['filter_to'] = $end_date->format( 'Y-m-d' );
|
|
$_GET['range'] = 'other';
|
|
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'revenue_type' => 'net',
|
|
)
|
|
);
|
|
|
|
// Get UTC dates for selected date range.
|
|
$utc_dates = EDD\Reports\parse_dates_for_range();
|
|
|
|
// Get total sales.
|
|
$sales['totals'] = $stats->get_order_count(
|
|
array(
|
|
'start' => $utc_dates['start']->format( 'Y-m-d H:i:s' ),
|
|
'end' => $utc_dates['end']->format( 'Y-m-d H:i:s' ),
|
|
)
|
|
);
|
|
|
|
// Get sales for each day in the requested date range.
|
|
$current_date = $start_date->copy();
|
|
while ( $current_date->lte( $end_date ) ) {
|
|
// Force the data for the reports API.
|
|
$_GET['filter_from'] = $current_date->format( 'Y-m-d' );
|
|
$_GET['filter_to'] = $current_date->format( 'Y-m-d' );
|
|
$_GET['range'] = 'other';
|
|
|
|
$date_key = str_replace( '-', '', $current_date->format( 'Y-m-d' ) );
|
|
$utc_dates = EDD\Reports\parse_dates_for_range();
|
|
|
|
if ( ! isset( $sales['sales'][ $date_key ] ) ) {
|
|
$sales['sales'][ $date_key ] = $stats->get_order_count(
|
|
array(
|
|
'start' => $utc_dates['start']->format( 'Y-m-d H:i:s' ),
|
|
'end' => $utc_dates['end']->format( 'Y-m-d H:i:s' ),
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
$current_date->addDay();
|
|
}
|
|
|
|
ksort( $sales['sales'] );
|
|
} else {
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'range' => $args['date'],
|
|
'revenue_type' => 'net',
|
|
)
|
|
);
|
|
|
|
$sales['sales'][ $args['date'] ] = $stats->get_order_count();
|
|
}
|
|
} elseif ( 'all' === $args['product'] ) {
|
|
$products = get_posts( array( 'post_type' => 'download', 'nopaging' => true ) );
|
|
$i = 0;
|
|
|
|
$stats = new EDD\Stats();
|
|
foreach ( $products as $product_info ) {
|
|
$product_order_count = $stats->get_order_item_count(
|
|
array(
|
|
'product_id' => $product_info->ID,
|
|
)
|
|
);
|
|
|
|
$sales['sales'][ $i ] = array(
|
|
$product_info->post_name => $product_order_count,
|
|
);
|
|
$i ++;
|
|
}
|
|
} else {
|
|
if ( 'download' === get_post_type( $args['product'] ) ) {
|
|
$stats = new EDD\Stats();
|
|
$product_info = get_post( $args['product'] );
|
|
$order_item_count = $stats->get_order_item_count(
|
|
array(
|
|
'product_id' => $args['product'],
|
|
)
|
|
);
|
|
|
|
$sales['sales'][0] = array(
|
|
$product_info->post_name => $order_item_count,
|
|
);
|
|
} else {
|
|
$error['error'] = sprintf( __( 'Product %s not found!', 'easy-digital-downloads' ), $args['product'] );
|
|
}
|
|
}
|
|
|
|
if ( ! empty( $error ) ) {
|
|
return $error;
|
|
}
|
|
|
|
return apply_filters( 'edd_api_stats_sales', $sales, $this );
|
|
} elseif ( 'earnings' === $args['type'] ) {
|
|
if ( null === $args['product'] ) {
|
|
if ( null === $args['date'] ) {
|
|
$earnings = $this->get_default_earnings_stats( $args );
|
|
} elseif ( 'range' === $args['date'] ) {
|
|
// Ensure the end date is later than the start date.
|
|
if ( $args['enddate'] < $args['startdate'] ) {
|
|
$error['error'] = __( 'The end date must be later than the start date!', 'easy-digital-downloads' );
|
|
}
|
|
|
|
// Ensure both the start and end date are specified.
|
|
if ( empty( $args['startdate'] ) || empty( $args['enddate'] ) ) {
|
|
$error['error'] = __( 'Invalid or no date range specified!', 'easy-digital-downloads' );
|
|
}
|
|
|
|
$start_date = EDD()->utils->date( $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day_start'], edd_get_timezone_id(), false )->startOfDay();
|
|
$end_date = EDD()->utils->date( $dates['year_end'] . '-' . $dates['m_end'] . '-' . $dates['day_end'], edd_get_timezone_id(), false )->endOfDay();
|
|
|
|
// Force the data for the reports API.
|
|
$_GET['filter_from'] = $start_date->format( 'Y-m-d' );
|
|
$_GET['filter_to'] = $end_date->format( 'Y-m-d' );
|
|
$_GET['range'] = 'other';
|
|
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'revenue_type' => 'net',
|
|
'exclude_taxes' => ! $args['include_tax'],
|
|
'output' => 'typed',
|
|
)
|
|
);
|
|
|
|
// Get UTC dates for selected date range.
|
|
$utc_dates = EDD\Reports\parse_dates_for_range();
|
|
|
|
// Get total earnings.
|
|
$earnings['totals'] = $stats->get_order_earnings(
|
|
array(
|
|
'start' => $utc_dates['start']->format( 'Y-m-d H:i:s' ),
|
|
'end' => $utc_dates['end']->format( 'Y-m-d H:i:s' ),
|
|
)
|
|
);
|
|
|
|
// Get earnings for each day in the requested date range.
|
|
$current_date = $start_date->copy();
|
|
while ( $current_date->lte( $end_date ) ) {
|
|
// Force the data for the reports API.
|
|
$_GET['filter_from'] = $current_date->format( 'Y-m-d' );
|
|
$_GET['filter_to'] = $current_date->format( 'Y-m-d' );
|
|
$_GET['range'] = 'other';
|
|
|
|
$date_key = str_replace( '-', '', $current_date->format( 'Y-m-d' ) );
|
|
$utc_dates = EDD\Reports\parse_dates_for_range();
|
|
|
|
if ( ! isset( $sales['earnings'][ $date_key ] ) ) {
|
|
$earnings['earnings'][ $date_key ] = $stats->get_order_earnings(
|
|
array(
|
|
'start' => $utc_dates['start']->format( 'Y-m-d H:i:s' ),
|
|
'end' => $utc_dates['end']->format( 'Y-m-d H:i:s' ),
|
|
)
|
|
);
|
|
}
|
|
|
|
$current_date->addDay();
|
|
}
|
|
|
|
ksort( $earnings['earnings'] );
|
|
} else {
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'range' => $args['date'],
|
|
'revenue_type' => 'net',
|
|
'exclude_tax' => ! $args['include_tax'],
|
|
'output' => 'typed',
|
|
)
|
|
);
|
|
$earnings['earnings'][ $args['date'] ] = $stats->get_order_earnings();
|
|
}
|
|
} elseif ( 'all' === $args['product'] ) {
|
|
$products = get_posts( array( 'post_type' => 'download', 'nopaging' => true ) );
|
|
$i = 0;
|
|
|
|
$stats = new EDD\Stats();
|
|
foreach ( $products as $product_info ) {
|
|
$product_earnings = $stats->get_order_item_earnings(
|
|
array(
|
|
'product_id' => $product_info->ID,
|
|
'output' => 'typed',
|
|
)
|
|
);
|
|
|
|
$earnings['earnings'][ $i ] = array(
|
|
$product_info->post_name => $product_earnings,
|
|
);
|
|
$i ++;
|
|
}
|
|
} else {
|
|
if ( 'download' === get_post_type( $args['product'] ) ) {
|
|
$stats = new EDD\Stats();
|
|
$product_info = get_post( $args['product'] );
|
|
$order_item_earnings = $stats->get_order_item_earnings(
|
|
array(
|
|
'product_id' => $args['product'],
|
|
'output' => 'typed',
|
|
)
|
|
);
|
|
|
|
$earnings['earnings'][0] = array(
|
|
$product_info->post_name => $order_item_earnings,
|
|
);
|
|
} else {
|
|
$error['error'] = sprintf( __( 'Product %s not found!', 'easy-digital-downloads' ), $args['product'] );
|
|
}
|
|
}
|
|
|
|
if ( ! empty( $error ) ) {
|
|
return $error;
|
|
}
|
|
|
|
return apply_filters( 'edd_api_stats_earnings', $earnings, $this );
|
|
} elseif ( 'customers' === $args['type'] ) {
|
|
$stats['customers']['total_customers'] = edd_count_customers();
|
|
|
|
return apply_filters( 'edd_api_stats_customers', $stats, $this );
|
|
} elseif ( empty( $args['type'] ) ) {
|
|
$stats = array_merge( $stats, $this->get_default_sales_stats() );
|
|
$stats = array_merge( $stats, $this->get_default_earnings_stats( $args ) );
|
|
|
|
return apply_filters( 'edd_api_stats', array( 'stats' => $stats, $this ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves Recent Sales
|
|
*
|
|
* @since 1.5
|
|
* @return array
|
|
*/
|
|
public function get_recent_sales() {
|
|
global $wp_query;
|
|
|
|
$sales = array();
|
|
|
|
if ( ! user_can( $this->user_id, 'view_shop_reports' ) && ! $this->override ) {
|
|
return $sales;
|
|
}
|
|
|
|
if ( isset( $wp_query->query_vars['id'] ) ) {
|
|
$query = array();
|
|
$query[] = new EDD_Payment( $wp_query->query_vars['id'] );
|
|
} elseif ( isset( $wp_query->query_vars['purchasekey'] ) ) {
|
|
$query = array();
|
|
$query[] = edd_get_payment_by( 'key', $wp_query->query_vars['purchasekey'] );
|
|
} elseif ( isset( $wp_query->query_vars['email'] ) ) {
|
|
$query = edd_get_payments( array(
|
|
'fields' => 'ids',
|
|
'meta_key' => '_edd_payment_user_email',
|
|
'meta_value' => $wp_query->query_vars['email'],
|
|
'number' => $this->per_page(),
|
|
'page' => $this->get_paged(),
|
|
'status' => 'complete',
|
|
) );
|
|
} else {
|
|
$query = edd_get_payments( array(
|
|
'fields' => 'ids',
|
|
'number' => $this->per_page(),
|
|
'page' => $this->get_paged(),
|
|
'status' => 'complete',
|
|
) );
|
|
}
|
|
|
|
if ( $query ) {
|
|
$i = 0;
|
|
foreach ( $query as $payment ) {
|
|
if ( is_numeric( $payment ) ) {
|
|
$payment = new EDD_Payment( $payment );
|
|
}
|
|
|
|
if ( empty( $payment ) ) {
|
|
continue;
|
|
}
|
|
|
|
$user_info = $payment->user_info;
|
|
|
|
$sales['sales'][ $i ]['ID'] = $payment->number;
|
|
$sales['sales'][ $i ]['transaction_id'] = $payment->transaction_id;
|
|
$sales['sales'][ $i ]['key'] = $payment->key;
|
|
$sales['sales'][ $i ]['discount'] = ! empty( $payment->discounts ) ? explode( ',', $payment->discounts ) : array();
|
|
$sales['sales'][ $i ]['subtotal'] = $payment->subtotal;
|
|
$sales['sales'][ $i ]['tax'] = $payment->tax;
|
|
$sales['sales'][ $i ]['fees'] = $payment->fees;
|
|
$sales['sales'][ $i ]['total'] = $payment->total;
|
|
$sales['sales'][ $i ]['gateway'] = $payment->gateway;
|
|
$sales['sales'][ $i ]['email'] = $payment->email;
|
|
$sales['sales'][ $i ]['user_id'] = $payment->user_id;
|
|
$sales['sales'][ $i ]['customer_id'] = $payment->customer_id;
|
|
$sales['sales'][ $i ]['date'] = $payment->date;
|
|
$sales['sales'][ $i ]['products'] = array();
|
|
|
|
$c = 0;
|
|
|
|
if ( ! empty( $payment->cart_details ) && is_array( $payment->cart_details ) ) {
|
|
foreach ( $payment->cart_details as $key => $item ) {
|
|
|
|
$item_id = isset( $item['id'] ) ? $item['id'] : $item;
|
|
$price = isset( $item['price'] ) ? $item['price'] : false;
|
|
$price_id = isset( $item['item_number']['options']['price_id'] ) ? $item['item_number']['options']['price_id'] : null;
|
|
$quantity = isset( $item['quantity'] ) && $item['quantity'] > 0 ? $item['quantity'] : 1;
|
|
|
|
if ( ! $price ) {
|
|
// This function is only used on payments with near 1.0 cart data structure
|
|
$price = edd_get_download_final_price( $item_id, $user_info, null );
|
|
}
|
|
|
|
$price_name = '';
|
|
if ( isset( $item['item_number'] ) && isset( $item['item_number']['options'] ) ) {
|
|
$price_options = $item['item_number']['options'];
|
|
if ( isset( $price_options['price_id'] ) ) {
|
|
$price_name = edd_get_price_option_name( $item_id, $price_options['price_id'], $payment->ID );
|
|
}
|
|
}
|
|
|
|
$sales['sales'][ $i ]['products'][ $c ]['id'] = $item_id;
|
|
$sales['sales'][ $i ]['products'][ $c ]['quantity'] = $quantity;
|
|
$sales['sales'][ $i ]['products'][ $c ]['name'] = get_the_title( $item_id );
|
|
$sales['sales'][ $i ]['products'][ $c ]['price'] = $price;
|
|
$sales['sales'][ $i ]['products'][ $c ]['price_name'] = $price_name;
|
|
$c ++;
|
|
}
|
|
}
|
|
|
|
$i ++;
|
|
}
|
|
}
|
|
|
|
return apply_filters( 'edd_api_sales', $sales, $this );
|
|
}
|
|
|
|
/**
|
|
* Process Get Discounts API Request
|
|
*
|
|
* @since 1.6
|
|
* @global object $wpdb Used to query the database using the WordPress
|
|
* Database API
|
|
*
|
|
* @param int $discount Discount ID
|
|
*
|
|
* @return array $discounts Multidimensional array of the discounts
|
|
*/
|
|
public function get_discounts( $discount = null ) {
|
|
|
|
$discount_list = $error = array();
|
|
|
|
if ( ! user_can( $this->user_id, 'manage_shop_discounts' ) && ! $this->override ) {
|
|
return $discount_list;
|
|
}
|
|
|
|
if ( empty( $discount ) ) {
|
|
$count = 0;
|
|
$paged = $this->get_paged();
|
|
$per_page = $this->per_page();
|
|
$discounts = edd_get_discounts( array(
|
|
'number' => $per_page,
|
|
'offset' => ( absint( $paged ) - 1 ) * $per_page,
|
|
) );
|
|
|
|
if ( empty( $discounts ) ) {
|
|
$error['error'] = __( 'No discounts found!', 'easy-digital-downloads' );
|
|
|
|
return $error;
|
|
}
|
|
|
|
foreach ( $discounts as $discount ) {
|
|
$discount_list['discounts'][ $count ]['ID'] = $discount->id;
|
|
$discount_list['discounts'][ $count ]['name'] = $discount->name;
|
|
$discount_list['discounts'][ $count ]['code'] = $discount->code;
|
|
$discount_list['discounts'][ $count ]['amount'] = $discount->amount;
|
|
$discount_list['discounts'][ $count ]['min_price'] = $discount->min_charge_amount;
|
|
$discount_list['discounts'][ $count ]['type'] = $discount->amount_type;
|
|
$discount_list['discounts'][ $count ]['uses'] = $discount->use_count;
|
|
$discount_list['discounts'][ $count ]['max_uses'] = $discount->max_uses;
|
|
$discount_list['discounts'][ $count ]['start_date'] = $discount->start_date;
|
|
$discount_list['discounts'][ $count ]['exp_date'] = $discount->end_date;
|
|
$discount_list['discounts'][ $count ]['status'] = $discount->status;
|
|
$discount_list['discounts'][ $count ]['product_requirements'] = $discount->product_reqs;
|
|
$discount_list['discounts'][ $count ]['requirement_condition'] = $discount->product_condition;
|
|
$discount_list['discounts'][ $count ]['global_discount'] = 'global' === $discount->scope;
|
|
$discount_list['discounts'][ $count ]['excluded_products'] = $discount->excluded_products;
|
|
$discount_list['discounts'][ $count ]['single_use'] = $discount->once_per_customer;
|
|
|
|
$count ++;
|
|
}
|
|
|
|
} else {
|
|
$discount_object = edd_get_discount( $discount );
|
|
if ( is_numeric( $discount ) && $discount_object ) {
|
|
$discount_list['discounts'][0]['ID'] = $discount_object->id;
|
|
$discount_list['discounts'][0]['name'] = $discount_object->name;
|
|
$discount_list['discounts'][0]['code'] = $discount_object->code;
|
|
$discount_list['discounts'][0]['amount'] = $discount_object->amount;
|
|
$discount_list['discounts'][0]['min_price'] = $discount_object->min_charge_amount;
|
|
$discount_list['discounts'][0]['type'] = $discount_object->amount_type;
|
|
$discount_list['discounts'][0]['uses'] = $discount_object->use_count;
|
|
$discount_list['discounts'][0]['max_uses'] = $discount_object->max_uses;
|
|
$discount_list['discounts'][0]['start_date'] = $discount_object->start_date;
|
|
$discount_list['discounts'][0]['exp_date'] = $discount_object->end_date;
|
|
$discount_list['discounts'][0]['status'] = $discount_object->status;
|
|
$discount_list['discounts'][0]['product_requirements'] = $discount_object->product_reqs;
|
|
$discount_list['discounts'][0]['requirement_condition'] = $discount_object->product_condition;
|
|
$discount_list['discounts'][0]['global_discount'] = 'global' === $discount_object->scope;
|
|
$discount_list['discounts'][0]['excluded_products'] = $discount_object->excluded_products;
|
|
$discount_list['discounts'][0]['single_use'] = $discount_object->once_per_customer;
|
|
} else {
|
|
$error['error'] = sprintf( __( 'Discount %s not found!', 'easy-digital-downloads' ), $discount );
|
|
|
|
return $error;
|
|
}
|
|
}
|
|
|
|
return apply_filters( 'edd_api_discounts', $discount_list, $this );
|
|
}
|
|
|
|
/**
|
|
* Process Get Downloads API Request to retrieve download logs
|
|
*
|
|
* @since 2.5
|
|
* @author Daniel J Griffiths
|
|
*
|
|
* @param int $customer_id The customer ID you wish to retrieve download logs for
|
|
*
|
|
* @return array Multidimensional array of the download logs
|
|
*/
|
|
public function get_download_logs( $customer_id = 0 ) {
|
|
$edd_logs = EDD()->debug_log;
|
|
|
|
$downloads = array();
|
|
$invalid_customer = false;
|
|
|
|
$paged = $this->get_paged();
|
|
$per_page = $this->per_page();
|
|
$offset = $per_page * ( $paged - 1 );
|
|
|
|
$meta_query = array();
|
|
if ( ! empty( $customer_id ) ) {
|
|
|
|
$customer = new EDD_Customer( $customer_id );
|
|
|
|
if ( $customer->id > 0 ) {
|
|
$meta_query['relation'] = 'OR';
|
|
|
|
if ( $customer->id > 0 ) {
|
|
// Based on customer->user_id
|
|
$meta_query[] = array(
|
|
'key' => '_edd_log_user_id',
|
|
'value' => $customer->user_id,
|
|
);
|
|
}
|
|
|
|
// Based on customer->email
|
|
$meta_query[] = array(
|
|
'key' => '_edd_log_user_info',
|
|
'value' => $customer->email,
|
|
'compare' => 'LIKE',
|
|
);
|
|
} else {
|
|
$invalid_customer = true;
|
|
}
|
|
}
|
|
|
|
$query = array(
|
|
'log_type' => 'file_download',
|
|
'paged' => $paged,
|
|
'meta_query' => $meta_query,
|
|
'posts_per_page' => $per_page,
|
|
'update_post_meta_cache' => false,
|
|
'update_post_term_cache' => false,
|
|
);
|
|
|
|
$logs = array();
|
|
if ( ! $invalid_customer ) {
|
|
$logs = $edd_logs->get_connected_logs( $query );
|
|
}
|
|
|
|
if ( empty( $logs ) ) {
|
|
$error['error'] = __( 'No download logs found!', 'easy-digital-downloads' );
|
|
|
|
return $error;
|
|
}
|
|
|
|
foreach ( $logs as $log ) {
|
|
|
|
$log_meta = get_post_custom( $log->ID );
|
|
$payment_id = isset( $log_meta['_edd_log_payment_id'] ) ? $log_meta['_edd_log_payment_id'][0] : false;
|
|
|
|
$payment_customer_id = edd_get_payment_customer_id( $payment_id );
|
|
$payment_customer = new EDD_Customer( $payment_customer_id );
|
|
$user_id = ( $payment_customer->user_id > 0 ) ? $payment_customer->user_id : false;
|
|
$ip = $log_meta['_edd_log_ip'][0];
|
|
$files = edd_get_payment_meta_downloads( $payment_id );
|
|
$files = edd_get_download_files( $files[0]['id'] );
|
|
$file_id = (int) $log_meta['_edd_log_file_id'][0];
|
|
$file_id = $file_id !== false ? $file_id : 0;
|
|
$file_name = isset( $files[ $file_id ]['name'] ) ? $files[ $file_id ]['name'] : null;
|
|
|
|
$item = (array) apply_filters( 'edd_api_download_log_item', array(
|
|
'ID' => $log->ID,
|
|
'user_id' => $user_id,
|
|
'product_id' => $log->post_parent,
|
|
'product_name' => get_the_title( $log->post_parent ),
|
|
'customer_id' => $payment_customer_id,
|
|
'payment_id' => $payment_id,
|
|
'file' => $file_name,
|
|
'ip' => $ip,
|
|
'date' => $log->post_date,
|
|
), $log, $log_meta );
|
|
|
|
$downloads['download_logs'][] = $item;
|
|
|
|
}
|
|
|
|
return apply_filters( 'edd_api_download_logs', $downloads, $this );
|
|
}
|
|
|
|
/**
|
|
* Process Get Info API Request
|
|
*
|
|
* @param array $args Arguments provided by API Request
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_info() {
|
|
$data = array();
|
|
|
|
// plugin.php required to use is_plugin_active()
|
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
|
|
|
// Integrations
|
|
if ( is_plugin_active( 'edd-commissions/edd-commissions.php' ) ) {
|
|
$data['info']['integrations']['commissions'] = true;
|
|
}
|
|
|
|
if ( class_exists( 'EDD_Software_Licensing' ) ) {
|
|
$data['info']['integrations']['software_licensing'] = true;
|
|
}
|
|
|
|
if ( class_exists( 'EDD_Front_End_Submissions' ) ) {
|
|
$data['info']['integrations']['fes'] = true;
|
|
}
|
|
|
|
if ( class_exists( 'EDD_Reviews' ) ) {
|
|
$data['info']['integrations']['reviews'] = true;
|
|
}
|
|
|
|
if ( class_exists( 'EDD_Recurring' ) ) {
|
|
$data['info']['integrations']['recurring'] = true;
|
|
}
|
|
|
|
// Permissions
|
|
if ( user_can( $this->user_id, 'view_shop_reports' ) ) {
|
|
$data['info']['permissions']['view_shop_reports'] = true;
|
|
}
|
|
|
|
if ( user_can( $this->user_id, 'view_shop_sensitive_data' ) ) {
|
|
$data['info']['permissions']['view_shop_sensitive_data'] = true;
|
|
}
|
|
|
|
if ( user_can( $this->user_id, 'manage_shop_discounts' ) ) {
|
|
$data['info']['permissions']['manage_shop_discounts'] = true;
|
|
}
|
|
|
|
// Site Information
|
|
if ( user_can( $this->user_id, 'view_shop_sensitive_data' ) ) {
|
|
$data['info']['site']['wp_version'] = get_bloginfo( 'version' );
|
|
$data['info']['site']['edd_version'] = EDD_VERSION;
|
|
}
|
|
|
|
$data['info']['site']['currency'] = edd_get_currency();
|
|
$data['info']['site']['currency_position'] = edd_get_option( 'currency_position', 'before' );
|
|
$data['info']['site']['decimal_separator'] = edd_get_option( 'decimal_separator', '.' );
|
|
$data['info']['site']['thousands_separator'] = edd_get_option( 'thousands_separator', ',' );
|
|
|
|
return apply_filters( 'edd_api_info', $data, $this );
|
|
}
|
|
|
|
/**
|
|
* Retrieve the output format
|
|
*
|
|
* Determines whether results should be displayed in XML or JSON
|
|
*
|
|
* @since 1.5
|
|
*
|
|
* @return mixed|void
|
|
*/
|
|
public function get_output_format() {
|
|
global $wp_query;
|
|
|
|
$format = isset( $wp_query->query_vars['format'] ) ? $wp_query->query_vars['format'] : 'json';
|
|
|
|
return apply_filters( 'edd_api_output_format', $format );
|
|
}
|
|
|
|
|
|
/**
|
|
* Log each API request, if enabled
|
|
*
|
|
* @access private
|
|
* @since 1.5
|
|
* @global $edd_logs
|
|
* @global $wp_query
|
|
*
|
|
* @param array $data
|
|
*
|
|
* @return void
|
|
*/
|
|
private function log_request( $data = array() ) {
|
|
if ( ! $this->log_requests() ) {
|
|
return;
|
|
}
|
|
|
|
global $wp_query;
|
|
|
|
$query = array(
|
|
'edd-api' => $wp_query->query_vars['edd-api'],
|
|
'key' => isset( $wp_query->query_vars['key'] ) ? $wp_query->query_vars['key'] : null,
|
|
'token' => isset( $wp_query->query_vars['token'] ) ? $wp_query->query_vars['token'] : null,
|
|
'query' => isset( $wp_query->query_vars['query'] ) ? $wp_query->query_vars['query'] : null,
|
|
'type' => isset( $wp_query->query_vars['type'] ) ? $wp_query->query_vars['type'] : null,
|
|
'product' => isset( $wp_query->query_vars['product'] ) ? $wp_query->query_vars['product'] : null,
|
|
'customer' => isset( $wp_query->query_vars['customer'] ) ? $wp_query->query_vars['customer'] : null,
|
|
'date' => isset( $wp_query->query_vars['date'] ) ? $wp_query->query_vars['date'] : null,
|
|
'startdate' => isset( $wp_query->query_vars['startdate'] ) ? $wp_query->query_vars['startdate'] : null,
|
|
'enddate' => isset( $wp_query->query_vars['enddate'] ) ? $wp_query->query_vars['enddate'] : null,
|
|
'id' => isset( $wp_query->query_vars['id'] ) ? $wp_query->query_vars['id'] : null,
|
|
'purchasekey' => isset( $wp_query->query_vars['purchasekey'] ) ? $wp_query->query_vars['purchasekey'] : null,
|
|
'email' => isset( $wp_query->query_vars['email'] ) ? $wp_query->query_vars['email'] : null,
|
|
);
|
|
|
|
$data = array(
|
|
'user_id' => $this->user_id,
|
|
'api_key' => isset( $wp_query->query_vars['key'] ) ? $wp_query->query_vars['key'] : 'public',
|
|
'token' => isset( $wp_query->query_vars['token'] ) ? $wp_query->query_vars['token'] : 'public',
|
|
'version' => $this->get_queried_version(),
|
|
'request' => http_build_query( $query ),
|
|
'error' => ! empty( $data['error'] ) ? $data['error'] : '',
|
|
'ip' => edd_get_ip(),
|
|
'time' => $data['request_speed'],
|
|
);
|
|
|
|
edd_add_api_request_log( $data );
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve the output data
|
|
*
|
|
* @since 1.5.2
|
|
* @return array
|
|
*/
|
|
public function get_output() {
|
|
return $this->data;
|
|
}
|
|
|
|
/**
|
|
* Output Query in either JSON/XML. The query data is outputted as JSON
|
|
* by default
|
|
*
|
|
* @author Daniel J Griffiths
|
|
* @since 1.5
|
|
* @global $wp_query
|
|
*
|
|
* @param int $status_code
|
|
*/
|
|
public function output( $status_code = 200 ) {
|
|
$format = $this->get_output_format();
|
|
|
|
status_header( $status_code );
|
|
|
|
do_action( 'edd_api_output_before', $this->data, $this, $format );
|
|
|
|
switch ( $format ) :
|
|
|
|
case 'xml' :
|
|
|
|
require_once EDD_PLUGIN_DIR . 'includes/libraries/class-ArrayToXML.php';
|
|
$arraytoxml = new ArrayToXML();
|
|
$xml = $arraytoxml->buildXML( $this->data, 'edd' );
|
|
|
|
echo $xml;
|
|
|
|
break;
|
|
|
|
case 'json' :
|
|
|
|
header( 'Content-Type: application/json' );
|
|
if ( ! empty( $this->pretty_print ) ) {
|
|
echo json_encode( $this->data, $this->pretty_print );
|
|
} else {
|
|
echo json_encode( $this->data );
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
default :
|
|
|
|
// Allow other formats to be added via extensions
|
|
do_action( 'edd_api_output_' . $format, $this->data, $this );
|
|
|
|
break;
|
|
|
|
endswitch;
|
|
|
|
do_action( 'edd_api_output_after', $this->data, $this, $format );
|
|
|
|
if ( defined( 'EDD_DOING_TESTS' ) && EDD_DOING_TESTS ) {
|
|
edd_die();
|
|
} else {
|
|
die();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Modify User Profile
|
|
*
|
|
* Modifies the output of profile.php to add key generation/revocation
|
|
*
|
|
* @author Daniel J Griffiths
|
|
* @since 1.5
|
|
*
|
|
* @param object $user Current user info
|
|
*
|
|
* @return void
|
|
*/
|
|
function user_key_field( $user ) {
|
|
if ( ( edd_get_option( 'api_allow_user_keys', false ) || current_user_can( 'manage_shop_settings' ) ) && current_user_can( 'edit_user', $user->ID ) ) {
|
|
$user = get_userdata( $user->ID );
|
|
?>
|
|
<table class="form-table">
|
|
<tbody>
|
|
<tr>
|
|
<th><?php _e( 'Downloads API Keys', 'easy-digital-downloads' ); ?></th>
|
|
<td>
|
|
<?php
|
|
$public_key = $this->get_user_public_key( $user->ID );
|
|
$secret_key = $this->get_user_secret_key( $user->ID );
|
|
|
|
if ( empty( $user->edd_user_public_key ) ) { ?>
|
|
<p class="description">
|
|
<label>
|
|
<input name="edd_set_api_key" type="checkbox" id="edd_set_api_key" value="0"/>
|
|
<?php _e( 'Generate API Key', 'easy-digital-downloads' ); ?>
|
|
</label>
|
|
</p>
|
|
<?php } else { ?>
|
|
<strong style="display:inline-block; width: 125px;"><?php _e( 'Public key:', 'easy-digital-downloads' ); ?> </strong>
|
|
<input type="text" disabled="disabled" class="regular-text" id="publickey" value="<?php echo esc_attr( $public_key ); ?>"/>
|
|
<br/>
|
|
<strong style="display:inline-block; width: 125px;"><?php _e( 'Secret key:', 'easy-digital-downloads' ); ?> </strong>
|
|
<input type="text" disabled="disabled" class="regular-text" id="privatekey" value="<?php echo esc_attr( $secret_key ); ?>"/>
|
|
<br/>
|
|
<strong style="display:inline-block; width: 125px;"><?php _e( 'Token:', 'easy-digital-downloads' ); ?> </strong>
|
|
<input type="text" disabled="disabled" class="regular-text" id="token" value="<?php echo esc_attr( $this->get_token( $user->ID ) ); ?>"/>
|
|
<br/>
|
|
<input name="edd_set_api_key" type="checkbox" id="edd_set_api_key" value="0"/>
|
|
<span class="description"><label for="edd_set_api_key"><?php _e( 'Revoke API Keys', 'easy-digital-downloads' ); ?></label></span>
|
|
<?php } ?>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<?php }
|
|
}
|
|
|
|
/**
|
|
* Process an API key generation/revocation
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param array $args
|
|
*
|
|
* @return void
|
|
*/
|
|
public function process_api_key( $args ) {
|
|
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'edd-api-nonce' ) ) {
|
|
wp_die( __( 'Nonce verification failed', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
|
|
}
|
|
|
|
if ( empty( $args['user_id'] ) ) {
|
|
wp_die( __( 'User ID Required', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 401 ) );
|
|
}
|
|
|
|
if ( is_numeric( $args['user_id'] ) ) {
|
|
$user_id = isset( $args['user_id'] ) ? absint( $args['user_id'] ) : get_current_user_id();
|
|
} else {
|
|
$userdata = get_user_by( 'login', $args['user_id'] );
|
|
$user_id = $userdata->ID;
|
|
}
|
|
$process = isset( $args['edd_api_process'] ) ? strtolower( $args['edd_api_process'] ) : false;
|
|
|
|
if ( $user_id == get_current_user_id() && ! edd_get_option( 'allow_user_api_keys' ) && ! current_user_can( 'manage_shop_settings' ) ) {
|
|
wp_die( sprintf( __( 'You do not have permission to %s API keys for this user', 'easy-digital-downloads' ), $process ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
|
|
} elseif ( ! current_user_can( 'manage_shop_settings' ) ) {
|
|
wp_die( sprintf( __( 'You do not have permission to %s API keys for this user', 'easy-digital-downloads' ), $process ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
|
|
}
|
|
|
|
switch ( $process ) {
|
|
case 'generate':
|
|
if ( $this->generate_api_key( $user_id ) ) {
|
|
delete_transient( 'edd-total-api-keys' );
|
|
edd_redirect(
|
|
edd_get_admin_url(
|
|
array(
|
|
'page' => 'edd-tools',
|
|
'tab' => 'api_keys',
|
|
'edd-message' => 'api-key-generated',
|
|
)
|
|
)
|
|
);
|
|
} else {
|
|
edd_redirect(
|
|
edd_get_admin_url(
|
|
array(
|
|
'page' => 'edd-tools',
|
|
'tab' => 'api_keys',
|
|
'edd-message' => 'api-key-failed',
|
|
)
|
|
)
|
|
);
|
|
}
|
|
break;
|
|
case 'regenerate':
|
|
$this->generate_api_key( $user_id, true );
|
|
delete_transient( 'edd-total-api-keys' );
|
|
edd_redirect(
|
|
edd_get_admin_url(
|
|
array(
|
|
'page' => 'edd-tools',
|
|
'tab' => 'api_keys',
|
|
'edd-message' => 'api-key-regenerated',
|
|
)
|
|
)
|
|
);
|
|
break;
|
|
case 'revoke':
|
|
$this->revoke_api_key( $user_id );
|
|
delete_transient( 'edd-total-api-keys' );
|
|
edd_redirect(
|
|
edd_get_admin_url(
|
|
array(
|
|
'page' => 'edd-tools',
|
|
'tab' => 'api_keys',
|
|
'edd-message' => 'api-key-revoked',
|
|
)
|
|
)
|
|
);
|
|
break;
|
|
default;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate new API keys for a user
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int $user_id User ID the key is being generated for
|
|
* @param boolean $regenerate Regenerate the key for the user
|
|
*
|
|
* @return boolean True if (re)generated successfully, false otherwise.
|
|
*/
|
|
public function generate_api_key( $user_id = 0, $regenerate = false ) {
|
|
if ( empty( $user_id ) ) {
|
|
return false;
|
|
}
|
|
|
|
$user = get_userdata( $user_id );
|
|
|
|
if ( ! $user ) {
|
|
return false;
|
|
}
|
|
|
|
$public_key = $this->get_user_public_key( $user_id );
|
|
|
|
if ( empty( $public_key ) || $regenerate == true ) {
|
|
$new_public_key = $this->generate_public_key( $user->user_email );
|
|
$new_secret_key = $this->generate_private_key( $user->ID );
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if ( $regenerate == true ) {
|
|
$this->revoke_api_key( $user->ID );
|
|
}
|
|
|
|
update_user_meta( $user_id, $new_public_key, 'edd_user_public_key' );
|
|
update_user_meta( $user_id, $new_secret_key, 'edd_user_secret_key' );
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Revoke a users API keys
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int $user_id User ID of user to revoke key for
|
|
*
|
|
* @return string
|
|
*/
|
|
public function revoke_api_key( $user_id = 0 ) {
|
|
if ( empty( $user_id ) ) {
|
|
return false;
|
|
}
|
|
|
|
$user = get_userdata( $user_id );
|
|
|
|
if ( ! $user ) {
|
|
return false;
|
|
}
|
|
|
|
$public_key = $this->get_user_public_key( $user_id );
|
|
$secret_key = $this->get_user_secret_key( $user_id );
|
|
if ( ! empty( $public_key ) ) {
|
|
delete_transient( md5( 'edd_api_user_' . $public_key ) );
|
|
delete_transient( md5( 'edd_api_user_public_key' . $user_id ) );
|
|
delete_transient( md5( 'edd_api_user_secret_key' . $user_id ) );
|
|
delete_user_meta( $user_id, $public_key );
|
|
delete_user_meta( $user_id, $secret_key );
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function get_version() {
|
|
return self::VERSION;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate and Save API key
|
|
*
|
|
* Generates the key requested by user_key_field and stores it in the database
|
|
*
|
|
* @author Daniel J Griffiths
|
|
* @since 1.5
|
|
*
|
|
* @param int $user_id
|
|
*
|
|
* @return void
|
|
*/
|
|
public function update_key( $user_id ) {
|
|
edd_update_user_api_key( $user_id );
|
|
}
|
|
|
|
/**
|
|
* Generate the public key for a user
|
|
*
|
|
* @access private
|
|
* @since 1.9.9
|
|
*
|
|
* @param string $user_email
|
|
*
|
|
* @return string
|
|
*/
|
|
public function generate_public_key( $user_email = '' ) {
|
|
$auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
|
|
$public = hash( 'md5', $user_email . $auth_key . date( 'U' ) );
|
|
|
|
return $public;
|
|
}
|
|
|
|
/**
|
|
* Generate the secret key for a user
|
|
*
|
|
* @access private
|
|
* @since 1.9.9
|
|
*
|
|
* @param int $user_id
|
|
*
|
|
* @return string
|
|
*/
|
|
public function generate_private_key( $user_id = 0 ) {
|
|
$auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
|
|
$secret = hash( 'md5', $user_id . $auth_key . date( 'U' ) );
|
|
|
|
return $secret;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the user's token
|
|
*
|
|
* @access private
|
|
* @since 1.9.9
|
|
*
|
|
* @param int $user_id
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_token( $user_id = 0 ) {
|
|
return hash( 'md5', $this->get_user_secret_key( $user_id ) . $this->get_user_public_key( $user_id ) );
|
|
}
|
|
|
|
/**
|
|
* Generate the default sales stats returned by the 'stats' endpoint
|
|
*
|
|
* @access private
|
|
* @since 1.5.3
|
|
* @return array default sales statistics
|
|
*/
|
|
private function get_default_sales_stats() {
|
|
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'range' => 'today',
|
|
'revenue_type' => 'net',
|
|
)
|
|
);
|
|
$sales['sales']['today'] = $stats->get_order_count();
|
|
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'range' => 'this_month',
|
|
'revenue_type' => 'net',
|
|
)
|
|
);
|
|
$sales['sales']['current_month'] = $stats->get_order_count();
|
|
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'range' => 'last_month',
|
|
'revenue_type' => 'net',
|
|
)
|
|
);
|
|
$sales['sales']['last_month'] = $stats->get_order_count();
|
|
|
|
$sales['sales']['totals'] = edd_get_total_sales();
|
|
|
|
return $sales;
|
|
}
|
|
|
|
/**
|
|
* Generate the default earnings stats returned by the 'stats' endpoint
|
|
*
|
|
* @access private
|
|
* @since 1.5.3
|
|
* @return array default earnings statistics
|
|
*/
|
|
private function get_default_earnings_stats( $args ) {
|
|
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'range' => 'today',
|
|
'exclude_taxes' => ! $args['include_tax'],
|
|
'revenue_type' => 'net',
|
|
'output' => 'typed',
|
|
)
|
|
);
|
|
$earnings['earnings']['today'] = $stats->get_order_earnings();
|
|
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'range' => 'this_month',
|
|
'exclude_taxes' => ! $args['include_tax'],
|
|
'revenue_type' => 'net',
|
|
'output' => 'typed',
|
|
)
|
|
);
|
|
$earnings['earnings']['current_month'] = $stats->get_order_earnings();
|
|
|
|
$stats = new EDD\Stats(
|
|
array(
|
|
'range' => 'last_month',
|
|
'exclude_taxes' => ! $args['include_tax'],
|
|
'revenue_type' => 'net',
|
|
'output' => 'typed',
|
|
)
|
|
);
|
|
$earnings['earnings']['last_month'] = $stats->get_order_earnings();
|
|
|
|
$earnings['earnings']['totals'] = edd_get_total_earnings( $args['include_tax'] );
|
|
|
|
return $earnings;
|
|
}
|
|
|
|
/**
|
|
* A Backwards Compatibility call for the change of meta_key/value for users API Keys
|
|
*
|
|
* @since 2.4
|
|
*
|
|
* @param string $check Wether to check the cache or not
|
|
* @param int $object_id The User ID being passed
|
|
* @param string $meta_key The user meta key
|
|
* @param bool $single If it should return a single value or array
|
|
*
|
|
* @return string The API key/secret for the user supplied
|
|
*/
|
|
public function api_key_backwards_copmat( $check, $object_id, $meta_key, $single ) {
|
|
|
|
if ( $meta_key !== 'edd_user_public_key' && $meta_key !== 'edd_user_secret_key' ) {
|
|
return $check;
|
|
}
|
|
|
|
$return = $check;
|
|
|
|
switch ( $meta_key ) {
|
|
case 'edd_user_public_key':
|
|
$return = EDD()->api->get_user_public_key( $object_id );
|
|
break;
|
|
case 'edd_user_secret_key':
|
|
$return = EDD()->api->get_user_secret_key( $object_id );
|
|
break;
|
|
}
|
|
|
|
if ( ! $single ) {
|
|
$return = array( $return );
|
|
}
|
|
|
|
return $return;
|
|
|
|
}
|
|
|
|
/**
|
|
* Sanitizes category and tag terms
|
|
*
|
|
* @access private
|
|
* @since 2.6
|
|
*
|
|
* @param mixed $term Request variable
|
|
*
|
|
* @return mixed Sanitized term/s
|
|
*/
|
|
public function sanitize_request_term( $term ) {
|
|
if ( is_array( $term ) ) {
|
|
$term = array_map( 'sanitize_text_field', $term );
|
|
} else if ( is_int( $term ) ) {
|
|
$term = absint( $term );
|
|
} else {
|
|
$term = sanitize_text_field( $term );
|
|
}
|
|
|
|
return $term;
|
|
}
|
|
|
|
/**
|
|
* Disable request logging
|
|
*
|
|
* @since 2.7
|
|
*/
|
|
public function log_requests() {
|
|
return apply_filters( 'edd_api_log_requests', true );
|
|
}
|
|
|
|
/**
|
|
* Check API keys vs token
|
|
*
|
|
* @since 2.8.2
|
|
*
|
|
* @param string $secret Secret key
|
|
* @param string $public Public key
|
|
* @param string $token Token used in API request
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function check_keys( $secret, $public, $token ) {
|
|
return hash_equals( md5( $secret . $public ), $token );
|
|
}
|
|
|
|
/**
|
|
* Flush previous API output. This is for unit tests only as we use a static fixture.
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
public function flush_api_output() {
|
|
if ( defined( 'EDD_UNIT_TESTS' ) ) {
|
|
$this->data = array();
|
|
}
|
|
}
|
|
}
|