EDD_API

EDD_API Class.

Defined (2)

The class is defined in the following location(s).

/includes/api/class-edd-api.php  
  1. class EDD_API { 
  2.  
  3. /** 
  4. * Latest API Version 
  5. */ 
  6. const VERSION = 2; 
  7.  
  8. /** 
  9. * Pretty Print? 
  10. * @var bool 
  11. * @access private 
  12. * @since 1.5 
  13. */ 
  14. private $pretty_print = false; 
  15.  
  16. /** 
  17. * Log API requests? 
  18. * @var bool 
  19. * @access private 
  20. * @since 1.5 
  21. */ 
  22. public $log_requests = true; 
  23.  
  24. /** 
  25. * Is this a valid request? 
  26. * @var bool 
  27. * @access private 
  28. * @since 1.5 
  29. */ 
  30. private $is_valid_request = false; 
  31.  
  32. /** 
  33. * User ID Performing the API Request 
  34. * @var int 
  35. * @access private 
  36. * @since 1.5.1 
  37. */ 
  38. public $user_id = 0; 
  39.  
  40. /** 
  41. * Instance of EDD Stats class 
  42. * @var object 
  43. * @access private 
  44. * @since 1.7 
  45. */ 
  46. private $stats; 
  47.  
  48. /** 
  49. * Response data to return 
  50. * @var array 
  51. * @access private 
  52. * @since 1.5.2 
  53. */ 
  54. private $data = array(); 
  55.  
  56. /** 
  57. * @var bool 
  58. * @access private 
  59. * @since 1.7 
  60. */ 
  61. public $override = true; 
  62.  
  63. /** 
  64. * Version of the API queried 
  65. * @var string 
  66. * @access public 
  67. * @since 2.4 
  68. */ 
  69. private $queried_version; 
  70.  
  71. /** 
  72. * All versions of the API 
  73. * @var string 
  74. * @access public 
  75. * @since 2.4 
  76. */ 
  77. protected $versions = array(); 
  78.  
  79. /** 
  80. * Queried endpoint 
  81. * @var string 
  82. * @access public 
  83. * @since 2.4 
  84. */ 
  85. private $endpoint; 
  86.  
  87. /** 
  88. * Endpoints routes 
  89. * @var object 
  90. * @access public 
  91. * @since 2.4 
  92. */ 
  93. private $routes; 
  94.  
  95. /** 
  96. * Setup the EDD API 
  97. * @author Daniel J Griffiths 
  98. * @since 1.5 
  99. */ 
  100. public function __construct() { 
  101.  
  102. $this->versions = array( 
  103. 'v1' => 'EDD_API_V1',  
  104. 'v2' => 'EDD_API_V2' 
  105. ); 
  106.  
  107. foreach( $this->get_versions() as $version => $class ) { 
  108. require_once EDD_PLUGIN_DIR . 'includes/api/class-edd-api-' . $version . '.php'; 
  109.  
  110. add_action( 'init', array( $this, 'add_endpoint' ) ); 
  111. add_action( 'wp', array( $this, 'process_query' ), -1 ); 
  112. add_filter( 'query_vars', array( $this, 'query_vars' ) ); 
  113. add_action( 'edd_process_api_key', array( $this, 'process_api_key' ) ); 
  114.  
  115. // Setup a backwards compatibility check for user API Keys 
  116. add_filter( 'get_user_metadata', array( $this, 'api_key_backwards_copmat' ), 10, 4 ); 
  117.  
  118. // Determine if JSON_PRETTY_PRINT is available 
  119. $this->pretty_print = defined( 'JSON_PRETTY_PRINT' ) ? JSON_PRETTY_PRINT : null; 
  120.  
  121. // Setup EDD_Stats instance 
  122. $this->stats = new EDD_Payment_Stats; 
  123.  
  124.  
  125. /** 
  126. * Registers a new rewrite endpoint for accessing the API 
  127. * @access public 
  128. * @author Daniel J Griffiths 
  129. * @param array $rewrite_rules WordPress Rewrite Rules 
  130. * @since 1.5 
  131. */ 
  132. public function add_endpoint( $rewrite_rules ) { 
  133. add_rewrite_endpoint( 'edd-api', EP_ALL ); 
  134.  
  135. /** 
  136. * Registers query vars for API access 
  137. * @access public 
  138. * @since 1.5 
  139. * @author Daniel J Griffiths 
  140. * @param array $vars Query vars 
  141. * @return string[] $vars New query vars 
  142. */ 
  143. public function query_vars( $vars ) { 
  144.  
  145. $vars[] = 'token'; 
  146. $vars[] = 'key'; 
  147. $vars[] = 'query'; 
  148. $vars[] = 'type'; 
  149. $vars[] = 'product'; 
  150. $vars[] = 'category'; 
  151. $vars[] = 'tag'; 
  152. $vars[] = 'term_relation'; 
  153. $vars[] = 'number'; 
  154. $vars[] = 'date'; 
  155. $vars[] = 'startdate'; 
  156. $vars[] = 'enddate'; 
  157. $vars[] = 'customer'; 
  158. $vars[] = 'discount'; 
  159. $vars[] = 'format'; 
  160. $vars[] = 'id'; 
  161. $vars[] = 'purchasekey'; 
  162. $vars[] = 'email'; 
  163. $vars[] = 'info'; 
  164.  
  165. return $vars; 
  166.  
  167. /** 
  168. * Retrieve the API versions 
  169. * @access public 
  170. * @since 2.4 
  171. * @return array 
  172. */ 
  173. public function get_versions() { 
  174. return $this->versions; 
  175.  
  176. /** 
  177. * Retrieve the API version that was queried 
  178. * @access public 
  179. * @since 2.4 
  180. * @return string 
  181. */ 
  182. public function get_queried_version() { 
  183. return $this->queried_version; 
  184.  
  185. /** 
  186. * Retrieves the default version of the API to use 
  187. * @access private 
  188. * @since 2.4 
  189. * @return string 
  190. */ 
  191. public function get_default_version() { 
  192.  
  193. $version = get_option( 'edd_default_api_version' ); 
  194.  
  195. if( defined( 'EDD_API_VERSION' ) ) { 
  196. $version = EDD_API_VERSION; 
  197. } elseif( ! $version ) { 
  198. $version = 'v1'; 
  199.  
  200. return $version; 
  201.  
  202. /** 
  203. * Sets the version of the API that was queried. 
  204. * Falls back to the default version if no version is specified 
  205. * @access private 
  206. * @since 2.4 
  207. */ 
  208. private function set_queried_version() { 
  209.  
  210. global $wp_query; 
  211.  
  212. $version = $wp_query->query_vars['edd-api']; 
  213.  
  214. if( strpos( $version, '/' ) ) { 
  215.  
  216. $version = explode( '/', $version ); 
  217. $version = strtolower( $version[0] ); 
  218.  
  219. $wp_query->query_vars['edd-api'] = str_replace( $version . '/', '', $wp_query->query_vars['edd-api'] ); 
  220.  
  221. if( array_key_exists( $version, $this->versions ) ) { 
  222.  
  223. $this->queried_version = $version; 
  224.  
  225. } else { 
  226.  
  227. $this->is_valid_request = false; 
  228. $this->invalid_version(); 
  229.  
  230. } else { 
  231.  
  232. $this->queried_version = $this->get_default_version(); 
  233.  
  234.  
  235.  
  236. /** 
  237. * Validate the API request 
  238. * Checks for the user's public key and token against the secret key 
  239. * @access private 
  240. * @global object $wp_query WordPress Query 
  241. * @uses EDD_API::get_user() 
  242. * @uses EDD_API::invalid_key() 
  243. * @uses EDD_API::invalid_auth() 
  244. * @since 1.5 
  245. * @return void 
  246. */ 
  247. private function validate_request() { 
  248. global $wp_query; 
  249.  
  250. $this->override = false; 
  251.  
  252. // Make sure we have both user and api key 
  253. if ( ! empty( $wp_query->query_vars['edd-api'] ) && ( ! $this->is_public_query() || ! empty( $wp_query->query_vars['token'] ) ) ) { 
  254.  
  255. if ( empty( $wp_query->query_vars['token'] ) || empty( $wp_query->query_vars['key'] ) ) { 
  256. $this->missing_auth(); 
  257.  
  258. // Auth was provided, include the upgrade routine so we can use the fallback api checks 
  259. require_once EDD_PLUGIN_DIR . 'includes/admin/upgrades/upgrade-functions.php'; 
  260.  
  261. // Retrieve the user by public API key and ensure they exist 
  262. if ( ! ( $user = $this->get_user( $wp_query->query_vars['key'] ) ) ) { 
  263.  
  264. $this->invalid_key(); 
  265.  
  266. } else { 
  267.  
  268. $token = urldecode( $wp_query->query_vars['token'] ); 
  269. $secret = $this->get_user_secret_key( $user ); 
  270. $public = urldecode( $wp_query->query_vars['key'] ); 
  271.  
  272. if ( hash_equals( md5( $secret . $public ), $token ) ) { 
  273. $this->is_valid_request = true; 
  274. } else { 
  275. $this->invalid_auth(); 
  276. } elseif ( ! empty( $wp_query->query_vars['edd-api'] ) && $this->is_public_query() ) { 
  277. $this->is_valid_request = true; 
  278. $wp_query->set( 'key', 'public' ); 
  279.  
  280.  
  281. /** 
  282. * Return whether this is a public query. 
  283. * @access private 
  284. * @global object $wp_query WordPress Query 
  285. * @since 2.6 
  286. * @return boolean 
  287. */ 
  288. private function is_public_query() { 
  289. global $wp_query; 
  290.  
  291. $public_modes = apply_filters( 'edd_api_public_query_modes', array( 
  292. 'products' 
  293. ) ); 
  294.  
  295. return in_array( $wp_query->query_vars['edd-api'], $public_modes ); 
  296.  
  297. /** 
  298. * Retrieve the user ID based on the public key provided 
  299. * @access public 
  300. * @since 1.5.1 
  301. * @global object $wpdb Used to query the database using the WordPress 
  302. * Database API 
  303. * @param string $key Public Key 
  304. * @return bool if user ID is found, false otherwise 
  305. */ 
  306. public function get_user( $key = '' ) { 
  307. global $wpdb, $wp_query; 
  308.  
  309. if( empty( $key ) ) { 
  310. $key = urldecode( $wp_query->query_vars['key'] ); 
  311.  
  312. if ( empty( $key ) ) { 
  313. return false; 
  314.  
  315. $user = get_transient( md5( 'edd_api_user_' . $key ) ); 
  316.  
  317. if ( false === $user ) { 
  318. if ( edd_has_upgrade_completed( 'upgrade_user_api_keys' ) ) { 
  319. $user = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s LIMIT 1", $key ) ); 
  320. } else { 
  321. $user = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = 'edd_user_public_key' AND meta_value = %s LIMIT 1", $key ) ); 
  322. set_transient( md5( 'edd_api_user_' . $key ) , $user, DAY_IN_SECONDS ); 
  323.  
  324. if ( $user != NULL ) { 
  325. $this->user_id = $user; 
  326. return $user; 
  327.  
  328. return false; 
  329.  
  330. public function get_user_public_key( $user_id = 0 ) { 
  331. global $wpdb; 
  332.  
  333. if ( empty( $user_id ) ) { 
  334. return ''; 
  335.  
  336. $cache_key = md5( 'edd_api_user_public_key' . $user_id ); 
  337. $user_public_key = get_transient( $cache_key ); 
  338.  
  339. if ( empty( $user_public_key ) ) { 
  340. if ( edd_has_upgrade_completed( 'upgrade_user_api_keys' ) ) { 
  341. $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 ) ); 
  342. } else { 
  343. $user_public_key = $wpdb->get_var( $wpdb->prepare( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = 'edd_user_public_key' AND user_id = %d", $user_id ) ); 
  344. set_transient( $cache_key, $user_public_key, HOUR_IN_SECONDS ); 
  345.  
  346. return $user_public_key; 
  347.  
  348. public function get_user_secret_key( $user_id = 0 ) { 
  349. global $wpdb; 
  350.  
  351. if ( empty( $user_id ) ) { 
  352. return ''; 
  353.  
  354. $cache_key = md5( 'edd_api_user_secret_key' . $user_id ); 
  355. $user_secret_key = get_transient( $cache_key ); 
  356.  
  357. if ( empty( $user_secret_key ) ) { 
  358. if ( edd_has_upgrade_completed( 'upgrade_user_api_keys' ) ) { 
  359. $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 ) ); 
  360. } else { 
  361. $user_secret_key = $wpdb->get_var( $wpdb->prepare( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = 'edd_user_secret_key' AND user_id = %d", $user_id ) ); 
  362. set_transient( $cache_key, $user_secret_key, HOUR_IN_SECONDS ); 
  363.  
  364. return $user_secret_key; 
  365.  
  366. /** 
  367. * Displays a missing authentication error if all the parameters aren't 
  368. * provided 
  369. * @access private 
  370. * @author Daniel J Griffiths 
  371. * @uses EDD_API::output() 
  372. * @since 1.5 
  373. */ 
  374. private function missing_auth() { 
  375. $error = array(); 
  376. $error['error'] = __( 'You must specify both a token and API key!', 'easy-digital-downloads' ); 
  377.  
  378. $this->data = $error; 
  379. $this->output( 401 ); 
  380.  
  381. /** 
  382. * Displays an authentication failed error if the user failed to provide valid 
  383. * credentials 
  384. * @access private 
  385. * @since 1.5 
  386. * @uses EDD_API::output() 
  387. * @return void 
  388. */ 
  389. private function invalid_auth() { 
  390. $error = array(); 
  391. $error['error'] = __( 'Your request could not be authenticated!', 'easy-digital-downloads' ); 
  392.  
  393. $this->data = $error; 
  394. $this->output( 403 ); 
  395.  
  396. /** 
  397. * Displays an invalid API key error if the API key provided couldn't be 
  398. * validated 
  399. * @access private 
  400. * @author Daniel J Griffiths 
  401. * @since 1.5 
  402. * @uses EDD_API::output() 
  403. * @return void 
  404. */ 
  405. private function invalid_key() { 
  406. $error = array(); 
  407. $error['error'] = __( 'Invalid API key!', 'easy-digital-downloads' ); 
  408.  
  409. $this->data = $error; 
  410. $this->output( 403 ); 
  411.  
  412. /** 
  413. * Displays an invalid version error if the version number passed isn't valid 
  414. * @access private 
  415. * @since 2.4 
  416. * @uses EDD_API::output() 
  417. * @return void 
  418. */ 
  419. private function invalid_version() { 
  420. $error = array(); 
  421. $error['error'] = __( 'Invalid API version!', 'easy-digital-downloads' ); 
  422.  
  423. $this->data = $error; 
  424. $this->output( 404 ); 
  425.  
  426. /** 
  427. * Listens for the API and then processes the API requests 
  428. * @access public 
  429. * @global $wp_query 
  430. * @since 1.5 
  431. * @return void 
  432. */ 
  433. public function process_query() { 
  434.  
  435. global $wp_query; 
  436.  
  437. // Start logging how long the request takes for logging 
  438. $before = microtime( true ); 
  439.  
  440. // Check for edd-api var. Get out if not present 
  441. if ( empty( $wp_query->query_vars['edd-api'] ) ) { 
  442. return; 
  443.  
  444. // Determine which version was queried 
  445. $this->set_queried_version(); 
  446.  
  447. // Determine the kind of query 
  448. $this->set_query_mode(); 
  449.  
  450. // Check for a valid user and set errors if necessary 
  451. $this->validate_request(); 
  452.  
  453. // Only proceed if no errors have been noted 
  454. if( ! $this->is_valid_request ) { 
  455. return; 
  456.  
  457. if( ! defined( 'EDD_DOING_API' ) ) { 
  458. define( 'EDD_DOING_API', true ); 
  459.  
  460. $data = array(); 
  461. $this->routes = new $this->versions[ $this->get_queried_version() ]; 
  462. $this->routes->validate_request(); 
  463.  
  464. switch( $this->endpoint ) : 
  465.  
  466. case 'stats' : 
  467.  
  468. $data = $this->routes->get_stats( array( 
  469. 'type' => isset( $wp_query->query_vars['type'] ) ? $wp_query->query_vars['type'] : null,  
  470. 'product' => isset( $wp_query->query_vars['product'] ) ? $wp_query->query_vars['product'] : null,  
  471. 'date' => isset( $wp_query->query_vars['date'] ) ? $wp_query->query_vars['date'] : null,  
  472. 'startdate' => isset( $wp_query->query_vars['startdate'] ) ? $wp_query->query_vars['startdate'] : null,  
  473. 'enddate' => isset( $wp_query->query_vars['enddate'] ) ? $wp_query->query_vars['enddate'] : null,  
  474. ) ); 
  475.  
  476. break; 
  477.  
  478. case 'products' : 
  479.  
  480. $args = array( 
  481. 'product' => isset( $wp_query->query_vars['product'] ) ? absint( $wp_query->query_vars['product'] ) : null,  
  482. 'category' => isset( $wp_query->query_vars['category'] ) ? $this->sanitize_request_term( $wp_query->query_vars['category'] ) : null,  
  483. 'tag' => isset( $wp_query->query_vars['tag'] ) ? $this->sanitize_request_term( $wp_query->query_vars['tag'] ) : null,  
  484. 'term_relation' => isset( $wp_query->query_vars['term_relation'] ) ? $this->sanitize_request_term( $wp_query->query_vars['term_relation'] ) : null,  
  485. 's' => isset( $wp_query->query_vars['s'] ) ? sanitize_text_field( $wp_query->query_vars['s'] ) : null,  
  486. ); 
  487.  
  488. $data = $this->routes->get_products( $args ); 
  489.  
  490. break; 
  491.  
  492. case 'customers' : 
  493.  
  494. $args = array( 
  495. 'customer' => isset( $wp_query->query_vars['customer'] ) ? $wp_query->query_vars['customer'] : null,  
  496. 'date' => isset( $wp_query->query_vars['date'] ) ? $wp_query->query_vars['date'] : null,  
  497. 'startdate' => isset( $wp_query->query_vars['startdate'] ) ? $wp_query->query_vars['startdate'] : null,  
  498. 'enddate' => isset( $wp_query->query_vars['enddate'] ) ? $wp_query->query_vars['enddate'] : null,  
  499. ); 
  500.  
  501. $data = $this->routes->get_customers( $args ); 
  502.  
  503. break; 
  504.  
  505. case 'sales' : 
  506.  
  507. $data = $this->routes->get_recent_sales(); 
  508.  
  509. break; 
  510.  
  511. case 'discounts' : 
  512.  
  513. $discount = isset( $wp_query->query_vars['discount'] ) ? $wp_query->query_vars['discount'] : null; 
  514.  
  515. $data = $this->routes->get_discounts( $discount ); 
  516.  
  517. break; 
  518.  
  519. case 'file-download-logs' : 
  520.  
  521. $customer = isset( $wp_query->query_vars['customer'] ) ? $wp_query->query_vars['customer'] : null; 
  522.  
  523. $data = $this->get_download_logs( $customer ); 
  524.  
  525. break; 
  526.  
  527. case 'info' : 
  528.  
  529. $data = $this->routes->get_info(); 
  530.  
  531. break; 
  532.  
  533. endswitch; 
  534.  
  535. // Allow extensions to setup their own return data 
  536. $this->data = apply_filters( 'edd_api_output_data', $data, $this->endpoint, $this ); 
  537.  
  538. $after = microtime( true ); 
  539. $request_time = ( $after - $before ); 
  540. $this->data['request_speed'] = $request_time; 
  541.  
  542. // Log this API request, if enabled. We log it here because we have access to errors. 
  543. $this->log_request( $this->data ); 
  544.  
  545. // Send out data to the output function 
  546. $this->output(); 
  547.  
  548. /** 
  549. * Returns the API endpoint requested 
  550. * @access private 
  551. * @since 1.5 
  552. * @return string $query Query mode 
  553. */ 
  554. public function get_query_mode() { 
  555.  
  556. return $this->endpoint; 
  557.  
  558. /** 
  559. * Determines the kind of query requested and also ensure it is a valid query 
  560. * @access private 
  561. * @since 2.4 
  562. * @global $wp_query 
  563. */ 
  564. public function set_query_mode() { 
  565.  
  566. global $wp_query; 
  567.  
  568. // Whitelist our query options 
  569. $accepted = apply_filters( 'edd_api_valid_query_modes', array( 
  570. 'stats',  
  571. 'products',  
  572. 'customers',  
  573. 'sales',  
  574. 'discounts',  
  575. 'file-download-logs',  
  576. 'info' 
  577. ) ); 
  578.  
  579. $query = isset( $wp_query->query_vars['edd-api'] ) ? $wp_query->query_vars['edd-api'] : null; 
  580. $query = str_replace( $this->queried_version . '/', '', $query ); 
  581.  
  582. $error = array(); 
  583.  
  584. // Make sure our query is valid 
  585. if ( ! in_array( $query, $accepted ) ) { 
  586. $error['error'] = __( 'Invalid query!', 'easy-digital-downloads' ); 
  587.  
  588. $this->data = $error; 
  589. // 400 is Bad Request 
  590. $this->output( 400 ); 
  591.  
  592. $this->endpoint = $query; 
  593.  
  594. /** 
  595. * Get page number 
  596. * @access private 
  597. * @since 1.5 
  598. * @global $wp_query 
  599. * @return int $wp_query->query_vars['page'] if page number returned (default: 1) 
  600. */ 
  601. public function get_paged() { 
  602. global $wp_query; 
  603.  
  604. return isset( $wp_query->query_vars['page'] ) ? $wp_query->query_vars['page'] : 1; 
  605.  
  606.  
  607. /** 
  608. * Number of results to display per page 
  609. * @access private 
  610. * @since 1.5 
  611. * @global $wp_query 
  612. * @return int $per_page Results to display per page (default: 10) 
  613. */ 
  614. public function per_page() { 
  615. global $wp_query; 
  616.  
  617. $per_page = isset( $wp_query->query_vars['number'] ) ? $wp_query->query_vars['number'] : 10; 
  618.  
  619. if( $per_page < 0 && $this->get_query_mode() == 'customers' ) { 
  620. $per_page = 99999999; // Customers query doesn't support -1 
  621.  
  622. return apply_filters( 'edd_api_results_per_page', $per_page ); 
  623.  
  624. /** 
  625. * Sets up the dates used to retrieve earnings/sales 
  626. * @access public 
  627. * @since 1.5.1 
  628. * @param array $args Arguments to override defaults 
  629. * @return array $dates 
  630. */ 
  631. public function get_dates( $args = array() ) { 
  632. $dates = array(); 
  633.  
  634. $defaults = array( 
  635. 'type' => '',  
  636. 'product' => null,  
  637. 'date' => null,  
  638. 'startdate' => null,  
  639. 'enddate' => null,  
  640. ); 
  641.  
  642. $args = wp_parse_args( $args, $defaults ); 
  643.  
  644. $current_time = current_time( 'timestamp' ); 
  645.  
  646. if ( 'range' === $args['date'] ) { 
  647. $startdate = strtotime( $args['startdate'] ); 
  648. $enddate = strtotime( $args['enddate'] ); 
  649. $dates['day_start'] = date( 'd', $startdate ); 
  650. $dates['day_end'] = date( 'd', $enddate ); 
  651. $dates['m_start'] = date( 'n', $startdate ); 
  652. $dates['m_end'] = date( 'n', $enddate ); 
  653. $dates['year'] = date( 'Y', $startdate ); 
  654. $dates['year_end'] = date( 'Y', $enddate ); 
  655. } else { 
  656. // Modify dates based on predefined ranges 
  657. switch ( $args['date'] ) : 
  658.  
  659. case 'this_month' : 
  660. $dates['day'] = null; 
  661. $dates['m_start'] = date( 'n', $current_time ); 
  662. $dates['m_end'] = date( 'n', $current_time ); 
  663. $dates['year'] = date( 'Y', $current_time ); 
  664. break; 
  665.  
  666. case 'last_month' : 
  667. $dates['day'] = null; 
  668. $dates['m_start'] = date( 'n', $current_time ) == 1 ? 12 : date( 'n', $current_time ) - 1; 
  669. $dates['m_end'] = $dates['m_start']; 
  670. $dates['year'] = date( 'n', $current_time ) == 1 ? date( 'Y', $current_time ) - 1 : date( 'Y', $current_time ); 
  671. break; 
  672.  
  673. case 'today' : 
  674. $dates['day'] = date( 'd', $current_time ); 
  675. $dates['m_start'] = date( 'n', $current_time ); 
  676. $dates['m_end'] = date( 'n', $current_time ); 
  677. $dates['year'] = date( 'Y', $current_time ); 
  678. break; 
  679.  
  680. case 'yesterday' : 
  681.  
  682. $year = date( 'Y', $current_time ); 
  683. $month = date( 'n', $current_time ); 
  684. $day = date( 'd', $current_time ); 
  685.  
  686. if ( $month == 1 && $day == 1 ) { 
  687.  
  688. $year -= 1; 
  689. $month = 12; 
  690. $day = cal_days_in_month( CAL_GREGORIAN, $month, $year ); 
  691.  
  692. } elseif ( $month > 1 && $day == 1 ) { 
  693.  
  694. $month -= 1; 
  695. $day = cal_days_in_month( CAL_GREGORIAN, $month, $year ); 
  696.  
  697. } else { 
  698.  
  699. $day -= 1; 
  700.  
  701.  
  702. $dates['day'] = $day; 
  703. $dates['m_start'] = $month; 
  704. $dates['m_end'] = $month; 
  705. $dates['year'] = $year; 
  706.  
  707. break; 
  708.  
  709. case 'this_quarter' : 
  710. $month_now = date( 'n', $current_time ); 
  711.  
  712. $dates['day'] = null; 
  713.  
  714. if ( $month_now <= 3 ) { 
  715.  
  716. $dates['m_start'] = 1; 
  717. $dates['m_end'] = 3; 
  718. $dates['year'] = date( 'Y', $current_time ); 
  719.  
  720. } else if ( $month_now <= 6 ) { 
  721.  
  722. $dates['m_start'] = 4; 
  723. $dates['m_end'] = 6; 
  724. $dates['year'] = date( 'Y', $current_time ); 
  725.  
  726. } else if ( $month_now <= 9 ) { 
  727.  
  728. $dates['m_start'] = 7; 
  729. $dates['m_end'] = 9; 
  730. $dates['year'] = date( 'Y', $current_time ); 
  731.  
  732. } else { 
  733.  
  734. $dates['m_start'] = 10; 
  735. $dates['m_end'] = 12; 
  736. $dates['year'] = date( 'Y', $current_time ); 
  737.  
  738. break; 
  739.  
  740. case 'last_quarter' : 
  741. $month_now = date( 'n', $current_time ); 
  742.  
  743. $dates['day'] = null; 
  744.  
  745. if ( $month_now <= 3 ) { 
  746.  
  747. $dates['m_start'] = 10; 
  748. $dates['m_end'] = 12; 
  749. $dates['year'] = date( 'Y', $current_time ) - 1; // Previous year 
  750.  
  751. } else if ( $month_now <= 6 ) { 
  752.  
  753. $dates['m_start'] = 1; 
  754. $dates['m_end'] = 3; 
  755. $dates['year'] = date( 'Y', $current_time ); 
  756.  
  757. } else if ( $month_now <= 9 ) { 
  758.  
  759. $dates['m_start'] = 4; 
  760. $dates['m_end'] = 6; 
  761. $dates['year'] = date( 'Y', $current_time ); 
  762.  
  763. } else { 
  764.  
  765. $dates['m_start'] = 7; 
  766. $dates['m_end'] = 9; 
  767. $dates['year'] = date( 'Y', $current_time ); 
  768.  
  769. break; 
  770.  
  771. case 'this_year' : 
  772. $dates['day'] = null; 
  773. $dates['m_start'] = null; 
  774. $dates['m_end'] = null; 
  775. $dates['year'] = date( 'Y', $current_time ); 
  776. break; 
  777.  
  778. case 'last_year' : 
  779. $dates['day'] = null; 
  780. $dates['m_start'] = null; 
  781. $dates['m_end'] = null; 
  782. $dates['year'] = date( 'Y', $current_time ) - 1; 
  783. break; 
  784.  
  785. endswitch; 
  786.  
  787. /** 
  788. * Returns the filters for the dates used to retreive earnings/sales 
  789. * @since 1.5.1 
  790. * @param object $dates The dates used for retreiving earnings/sales 
  791. */ 
  792.  
  793. return apply_filters( 'edd_api_stat_dates', $dates ); 
  794.  
  795. /** 
  796. * Process Get Customers API Request 
  797. * @access public 
  798. * @since 1.5 
  799. * @author Daniel J Griffiths 
  800. * @global object $wpdb Used to query the database using the WordPress 
  801. * Database API 
  802. * @param int $customer Customer ID 
  803. * @return array $customers Multidimensional array of the customers 
  804. */ 
  805. public function get_customers( $customer = null ) { 
  806.  
  807. $customer = is_array( $customer ) ? $customer['customer'] : $customer; 
  808. $customers = array(); 
  809. $error = array(); 
  810.  
  811. if( ! user_can( $this->user_id, 'view_shop_sensitive_data' ) && ! $this->override ) { 
  812. return $customers; 
  813.  
  814. global $wpdb; 
  815.  
  816. $paged = $this->get_paged(); 
  817. $per_page = $this->per_page(); 
  818. $offset = $per_page * ( $paged - 1 ); 
  819.  
  820. if( is_numeric( $customer ) ) { 
  821. $field = 'id'; 
  822. } elseif ( is_array( $customer ) ) { 
  823. // Checking if search is being done by id, email, user_id fields. 
  824. if ( array_key_exists( 'id', $customer ) ) { 
  825. $field = 'id'; 
  826. } elseif ( array_key_exists( 'email', $customer ) ) { 
  827. $field = 'email'; 
  828. } elseif ( array_key_exists( 'user_id', $customer ) ) { 
  829. $field = 'user_id'; 
  830.  
  831. $customer = $customer[ $field ]; 
  832. } else { 
  833. $field = 'email'; 
  834.  
  835. $customer_query = EDD()->customers->get_customers( array( 'number' => $per_page, 'offset' => $offset, $field => $customer ) ); 
  836. $customer_count = 0; 
  837.  
  838. if( $customer_query ) { 
  839.  
  840. foreach ( $customer_query as $customer_obj ) { 
  841.  
  842. $names = explode( ' ', $customer_obj->name ); 
  843. $first_name = ! empty( $names[0] ) ? $names[0] : ''; 
  844. $last_name = ''; 
  845. if( ! empty( $names[1] ) ) { 
  846. unset( $names[0] ); 
  847. $last_name = implode( ' ', $names ); 
  848.  
  849. $customers['customers'][$customer_count]['info']['id'] = ''; 
  850. $customers['customers'][$customer_count]['info']['user_id'] = ''; 
  851. $customers['customers'][$customer_count]['info']['username'] = ''; 
  852. $customers['customers'][$customer_count]['info']['display_name'] = ''; 
  853. $customers['customers'][$customer_count]['info']['customer_id'] = $customer_obj->id; 
  854. $customers['customers'][$customer_count]['info']['first_name'] = $first_name; 
  855. $customers['customers'][$customer_count]['info']['last_name'] = $last_name; 
  856. $customers['customers'][$customer_count]['info']['email'] = $customer_obj->email; 
  857.  
  858. if ( ! empty( $customer_obj->user_id ) && $customer_obj->user_id > 0 ) { 
  859.  
  860. $user_data = get_userdata( $customer_obj->user_id ); 
  861.  
  862. // Customer with registered account 
  863.  
  864. // id is going to get deprecated in the future, user user_id or customer_id instead 
  865. $customers['customers'][$customer_count]['info']['id'] = $customer_obj->user_id; 
  866. $customers['customers'][$customer_count]['info']['user_id'] = $customer_obj->user_id; 
  867. $customers['customers'][$customer_count]['info']['username'] = $user_data->user_login; 
  868. $customers['customers'][$customer_count]['info']['display_name'] = $user_data->display_name; 
  869.  
  870.  
  871. $customers['customers'][$customer_count]['stats']['total_purchases'] = $customer_obj->purchase_count; 
  872. $customers['customers'][$customer_count]['stats']['total_spent'] = $customer_obj->purchase_value; 
  873. $customers['customers'][$customer_count]['stats']['total_downloads'] = edd_count_file_downloads_of_user( $customer_obj->email ); 
  874.  
  875. $customer_count++; 
  876.  
  877.  
  878. } elseif( $customer ) { 
  879.  
  880. $error['error'] = sprintf( __( 'Customer %s not found!', 'easy-digital-downloads' ), $customer ); 
  881. return $error; 
  882.  
  883. } else { 
  884.  
  885. $error['error'] = __( 'No customers found!', 'easy-digital-downloads' ); 
  886. return $error; 
  887.  
  888.  
  889. return apply_filters( 'edd_api_customers', $customers, $this ); 
  890.  
  891. /** 
  892. * Process Get Products API Request 
  893. * @access public 
  894. * @author Daniel J Griffiths 
  895. * @since 1.5 
  896. * @param int $product Product (Download) ID 
  897. * @return array $customers Multidimensional array of the products 
  898. */ 
  899. public function get_products( $args = array() ) { 
  900.  
  901. $products = array(); 
  902. $error = array(); 
  903.  
  904. if ( empty( $args['product'] ) ) { 
  905.  
  906. $products['products'] = array(); 
  907.  
  908. $parameters = array( 
  909. 'post_type' => 'download',  
  910. 'posts_per_page' => $this->per_page(),  
  911. 'suppress_filters' => true,  
  912. 'paged' => $this->get_paged(),  
  913. ); 
  914.  
  915. if ( isset( $args['s'] ) && !empty( $args['s'] ) ) { 
  916. $parameters['s'] = $args['s']; 
  917.  
  918. $product_list = get_posts( $parameters ); 
  919.  
  920. if ( $product_list ) { 
  921. $i = 0; 
  922. foreach ( $product_list as $product_info ) { 
  923. $products['products'][$i] = $this->get_product_data( $product_info ); 
  924. $i++; 
  925. } else { 
  926.  
  927. if ( get_post_type( $args['product'] ) == 'download' ) { 
  928. $product_info = get_post( $args['product'] ); 
  929.  
  930. $products['products'][0] = $this->get_product_data( $product_info ); 
  931.  
  932. } else { 
  933. $error['error'] = sprintf( __( 'Product %s not found!', 'easy-digital-downloads' ), $args['product'] ); 
  934. return $error; 
  935.  
  936. return apply_filters( 'edd_api_products', $products, $this ); 
  937.  
  938. /** 
  939. * Given a download post object, generate the data for the API output 
  940. * @since 2.3.9 
  941. * @param object $product_info The Download Post Object 
  942. * @return array Array of post data to return back in the API 
  943. */ 
  944. public function get_product_data( $product_info ) { 
  945.  
  946. $product = array(); 
  947.  
  948. $product['info']['id'] = $product_info->ID; 
  949. $product['info']['slug'] = $product_info->post_name; 
  950. $product['info']['title'] = $product_info->post_title; 
  951. $product['info']['create_date'] = $product_info->post_date; 
  952. $product['info']['modified_date'] = $product_info->post_modified; 
  953. $product['info']['status'] = $product_info->post_status; 
  954. $product['info']['link'] = html_entity_decode( $product_info->guid ); 
  955. $product['info']['content'] = $product_info->post_content; 
  956. $product['info']['excerpt'] = $product_info->post_excerpt; 
  957. $product['info']['thumbnail'] = wp_get_attachment_url( get_post_thumbnail_id( $product_info->ID ) ); 
  958. $product['info']['category'] = get_the_terms( $product_info, 'download_category' ); 
  959. $product['info']['tags'] = get_the_terms( $product_info, 'download_tag' ); 
  960.  
  961. if( user_can( $this->user_id, 'view_shop_reports' ) || $this->override ) { 
  962. $product['stats']['total']['sales'] = edd_get_download_sales_stats( $product_info->ID ); 
  963. $product['stats']['total']['earnings'] = edd_get_download_earnings_stats( $product_info->ID ); 
  964. $product['stats']['monthly_average']['sales'] = edd_get_average_monthly_download_sales( $product_info->ID ); 
  965. $product['stats']['monthly_average']['earnings'] = edd_get_average_monthly_download_earnings( $product_info->ID ); 
  966.  
  967. if ( edd_has_variable_prices( $product_info->ID ) ) { 
  968. foreach ( edd_get_variable_prices( $product_info->ID ) as $price ) { 
  969. $product['pricing'][ sanitize_key( $price['name'] ) ] = $price['amount']; 
  970. } else { 
  971. $product['pricing']['amount'] = edd_get_download_price( $product_info->ID ); 
  972.  
  973. if( user_can( $this->user_id, 'view_shop_sensitive_data' ) || $this->override ) { 
  974. foreach ( edd_get_download_files( $product_info->ID ) as $file ) { 
  975. $product['files'][] = $file; 
  976. $product['notes'] = edd_get_product_notes( $product_info->ID ); 
  977.  
  978. return apply_filters( 'edd_api_products_product', $product ); 
  979.  
  980.  
  981. /** 
  982. * Process Get Stats API Request 
  983. * @author Daniel J Griffiths 
  984. * @since 1.5 
  985. * @global object $wpdb Used to query the database using the WordPress 
  986. * @param array $args Arguments provided by API Request 
  987. * @return array 
  988. */ 
  989. public function get_stats( $args = array() ) { 
  990. $defaults = array( 
  991. 'type' => null,  
  992. 'product' => null,  
  993. 'date' => null,  
  994. 'startdate' => null,  
  995. 'enddate' => null 
  996. ); 
  997.  
  998. $args = wp_parse_args( $args, $defaults ); 
  999.  
  1000. $dates = $this->get_dates( $args ); 
  1001.  
  1002. $stats = array(); 
  1003. $earnings = array( 
  1004. 'earnings' => array() 
  1005. ); 
  1006. $sales = array( 
  1007. 'sales' => array() 
  1008. ); 
  1009. $error = array(); 
  1010.  
  1011. if( ! user_can( $this->user_id, 'view_shop_reports' ) && ! $this->override ) { 
  1012. return $stats; 
  1013.  
  1014. if ( $args['type'] == 'sales' ) { 
  1015. if ( $args['product'] == null ) { 
  1016. if ( $args['date'] == null ) { 
  1017. $sales = $this->get_default_sales_stats(); 
  1018. } elseif( $args['date'] === 'range' ) { 
  1019. // Return sales for a date range 
  1020.  
  1021. // Ensure the end date is later than the start date 
  1022. if( $args['enddate'] < $args['startdate'] ) { 
  1023. $error['error'] = __( 'The end date must be later than the start date!', 'easy-digital-downloads' ); 
  1024.  
  1025. // Ensure both the start and end date are specified 
  1026. if ( empty( $args['startdate'] ) || empty( $args['enddate'] ) ) { 
  1027. $error['error'] = __( 'Invalid or no date range specified!', 'easy-digital-downloads' ); 
  1028.  
  1029. $start_date = $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day_start']; 
  1030. $end_date = $dates['year_end'] . '-' . $dates['m_end'] . '-' . $dates['day_end']; 
  1031.  
  1032. $stats = EDD()->payment_stats->get_sales_by_range( 'other', true, $start_date, $end_date ); 
  1033.  
  1034. foreach ( $stats as $sale ) { 
  1035. $key = $sale['y'] . $sale['m'] . $sale['d']; 
  1036. $sales['sales'][ $key ] = (int) $sale['count']; 
  1037.  
  1038. $start_date = date( 'Y-m-d', strtotime( $start_date ) ); 
  1039. $end_date = date( 'Y-m-d', strtotime( $end_date ) ); 
  1040.  
  1041. while ( strtotime( $start_date ) <= strtotime( $end_date ) ) { 
  1042. $d = date( 'd', strtotime( $start_date ) ); 
  1043. $m = date( 'm', strtotime( $start_date ) ); 
  1044. $y = date( 'Y', strtotime( $start_date ) ); 
  1045.  
  1046. $key = $y . $m . $d; 
  1047.  
  1048. if ( ! isset( $sales['sales'][ $key ] ) ) { 
  1049. $sales['sales'][ $key ] = 0; 
  1050.  
  1051. $start_date = date( 'Y-m-d', strtotime( '+1 day', strtotime( $start_date ) ) ); 
  1052.  
  1053. ksort( $sales['sales'] ); 
  1054.  
  1055. $sales['totals'] = array_sum( $sales['sales'] ); 
  1056. } else { 
  1057. if( $args['date'] == 'this_quarter' || $args['date'] == 'last_quarter' ) { 
  1058. $start_date = $dates['year'] . '-' . $dates['m_start'] . '-01'; 
  1059. $end_date = $dates['year'] . '-' . $dates['m_end'] . '-' . cal_days_in_month( CAL_GREGORIAN, $dates['m_end'], $dates['year'] ); 
  1060.  
  1061. $start_date = date( 'Y-m-d', strtotime( $start_date ) ); 
  1062. $end_date = date( 'Y-m-d', strtotime( $end_date ) ); 
  1063.  
  1064. $stats = EDD()->payment_stats->get_sales_by_range( $args['date'], false, $start_date, $end_date ); 
  1065.  
  1066. if ( empty( $stats ) ) { 
  1067. $sales['sales'][ $args['date'] ] = 0; 
  1068. } else { 
  1069. $sales['sales'][ $args['date'] ] = $stats[0]['count']; 
  1070. } else { 
  1071. $start_date = $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day']; 
  1072. $start_date = date( 'Y-m-d', strtotime( $start_date ) ); 
  1073. $end_date = date( 'Y-m-d', strtotime( '+1 day', strtotime( $start_date ) ) ); 
  1074.  
  1075. $stats = EDD()->payment_stats->get_sales_by_range( 'yesterday', true, $start_date, $end_date ); 
  1076.  
  1077. if ( empty( $stats ) ) { 
  1078. $sales['sales'][ $args['date'] ] = 0; 
  1079. } else { 
  1080. $sales['sales'][ $args['date'] ] = (int) $stats[0]['count']; 
  1081. } elseif ( $args['product'] == 'all' ) { 
  1082. $products = get_posts( array( 'post_type' => 'download', 'nopaging' => true ) ); 
  1083. $i = 0; 
  1084. foreach ( $products as $product_info ) { 
  1085. $sales['sales'][$i] = array( $product_info->post_name => edd_get_download_sales_stats( $product_info->ID ) ); 
  1086. $i++; 
  1087. } else { 
  1088. if ( get_post_type( $args['product'] ) == 'download' ) { 
  1089. $product_info = get_post( $args['product'] ); 
  1090. $sales['sales'][0] = array( $product_info->post_name => edd_get_download_sales_stats( $args['product'] ) ); 
  1091. } else { 
  1092. $error['error'] = sprintf( __( 'Product %s not found!', 'easy-digital-downloads' ), $args['product'] ); 
  1093.  
  1094. if ( ! empty( $error ) ) 
  1095. return $error; 
  1096.  
  1097. return apply_filters( 'edd_api_stats_sales', $sales, $this ); 
  1098. } elseif ( $args['type'] == 'earnings' ) { 
  1099. if ( $args['product'] == null ) { 
  1100. if ( $args['date'] == null ) { 
  1101. $earnings = $this->get_default_earnings_stats(); 
  1102. } elseif ( $args['date'] === 'range' ) { 
  1103. // Return sales for a date range 
  1104.  
  1105. // Ensure the end date is later than the start date 
  1106. if ( $args['enddate'] < $args['startdate'] ) { 
  1107. $error['error'] = __( 'The end date must be later than the start date!', 'easy-digital-downloads' ); 
  1108.  
  1109. // Ensure both the start and end date are specified 
  1110. if ( empty( $args['startdate'] ) || empty( $args['enddate'] ) ) { 
  1111. $error['error'] = __( 'Invalid or no date range specified!', 'easy-digital-downloads' ); 
  1112.  
  1113. $total = (float) 0.00; 
  1114.  
  1115. // Loop through the years 
  1116. if ( ! isset( $earnings['earnings'] ) ) { 
  1117. $earnings['earnings'] = array(); 
  1118.  
  1119. if ( cal_days_in_month( CAL_GREGORIAN, $dates['m_start'], $dates['year'] ) < $dates['day_start'] ) { 
  1120. $next_day = mktime( 0, 0, 0, $dates['m_start'] + 1, 1, $dates['year'] ); 
  1121. $day = date( 'd', $next_day ); 
  1122. $month = date( 'm', $next_day ); 
  1123. $year = date( 'Y', $next_day ); 
  1124. $date_start = $year . '-' . $month . '-' . $day; 
  1125. } else { 
  1126. $date_start = $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day_start']; 
  1127.  
  1128. if ( cal_days_in_month( CAL_GREGORIAN, $dates['m_end'], $dates['year'] ) < $dates['day_end'] ) { 
  1129. $date_end = $dates['year_end'] . '-' . $dates['m_end'] . '-' . cal_days_in_month( CAL_GREGORIAN, $dates['m_end'], $dates['year'] ); 
  1130. } else { 
  1131. $date_end = $dates['year_end'] . '-' . $dates['m_end'] . '-' . $dates['day_end']; 
  1132.  
  1133. $earnings = EDD()->payment_stats->get_earnings_by_range( 'other', true, $date_start, $date_end ); 
  1134.  
  1135. $total = 0; 
  1136.  
  1137. foreach ( $earnings as $earning ) { 
  1138. $temp_data['earnings'][ $earning['y'] . $earning['m'] . $earning['d'] ] = (float) $earning['total']; 
  1139. $total += (float) $earning['total']; 
  1140.  
  1141. $date_start = date( 'Y-m-d', strtotime( $date_start ) ); 
  1142. $date_end = date( 'Y-m-d', strtotime( $date_end ) ); 
  1143.  
  1144. while ( strtotime( $date_start ) <= strtotime( $date_end ) ) { 
  1145. $d = date( 'd', strtotime( $date_start ) ); 
  1146. $m = date( 'm', strtotime( $date_start ) ); 
  1147. $y = date( 'Y', strtotime( $date_start ) ); 
  1148.  
  1149. $key = $y . $m . $d; 
  1150.  
  1151. if ( ! isset( $temp_data['earnings'][ $key ] ) ) { 
  1152. $temp_data['earnings'][ $key ] = 0; 
  1153.  
  1154. $date_start = date( 'Y-m-d', strtotime( '+1 day', strtotime( $date_start ) ) ); 
  1155.  
  1156. ksort($temp_data['earnings']); 
  1157.  
  1158. $earnings = $temp_data; 
  1159.  
  1160. $earnings['totals'] = $total; 
  1161. } else { 
  1162. if ( $args['date'] == 'this_quarter' || $args['date'] == 'last_quarter' ) { 
  1163. $current_time = current_time( 'timestamp' ); 
  1164.  
  1165. if ( 'this_quarter' == $args['date'] ) { 
  1166. $month_now = date( 'n', $current_time ); 
  1167. $dates['year'] = date( 'Y', $current_time ); 
  1168. $dates['year_end'] = $dates['year']; 
  1169.  
  1170. if ( $month_now <= 3 ) { 
  1171. $dates['m_start'] = 1; 
  1172. $dates['m_end'] = 3; 
  1173. } else if ( $month_now <= 6 ) { 
  1174. $dates['m_start'] = 4; 
  1175. $dates['m_end'] = 6; 
  1176. } else if ( $month_now <= 9 ) { 
  1177. $dates['m_start'] = 7; 
  1178. $dates['m_end'] = 9; 
  1179. } else { 
  1180. $dates['m_start'] = 10; 
  1181. $dates['m_end'] = 12; 
  1182.  
  1183. $dates['day_end'] = cal_days_in_month( CAL_GREGORIAN, $dates['m_end'], $dates['year'] ); 
  1184. } else { 
  1185. $month_now = date( 'n' ); 
  1186.  
  1187. if ( $month_now <= 3 ) { 
  1188. $dates['m_start'] = 10; 
  1189. $dates['m_end'] = 12; 
  1190. $dates['year'] = date( 'Y', $current_time ) - 1; // Previous year 
  1191. } else if ( $month_now <= 6 ) { 
  1192. $dates['m_start'] = 1; 
  1193. $dates['m_end'] = 3; 
  1194. $dates['year'] = date( 'Y', $current_time ); 
  1195. } else if ( $month_now <= 9 ) { 
  1196. $dates['m_start'] = 4; 
  1197. $dates['m_end'] = 6; 
  1198. $dates['year'] = date( 'Y', $current_time ); 
  1199. } else { 
  1200. $dates['m_start'] = 7; 
  1201. $dates['m_end'] = 9; 
  1202. $dates['year'] = date( 'Y', $current_time ); 
  1203.  
  1204. $dates['day_end'] = cal_days_in_month( CAL_GREGORIAN, $dates['m_end'], $dates['year'] ); 
  1205. $dates['year_end'] = $dates['year']; 
  1206.  
  1207. $dates['day_start'] = 1; 
  1208.  
  1209. if ( cal_days_in_month( CAL_GREGORIAN, $dates['m_start'], $dates['year'] ) < $dates['day_start'] ) { 
  1210. $next_day = mktime( 0, 0, 0, $dates['m_start'] + 1, 1, $dates['year'] ); 
  1211. $day = date( 'd', $next_day ); 
  1212. $month = date( 'm', $next_day ); 
  1213. $year = date( 'Y', $next_day ); 
  1214. $date_start = $year . '-' . $month . '-' . $day; 
  1215. } else { 
  1216. $date_start = $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day_start']; 
  1217.  
  1218. if ( cal_days_in_month( CAL_GREGORIAN, $dates['m_end'], $dates['year'] ) < $dates['day_end'] ) { 
  1219. $date_end = $dates['year_end'] . '-' . $dates['m_end'] . '-' . cal_days_in_month( CAL_GREGORIAN, $dates['m_end'], $dates['year'] ); 
  1220. } else { 
  1221. $date_end = $dates['year_end'] . '-' . $dates['m_end'] . '-' . $dates['day_end']; 
  1222.  
  1223. $results = EDD()->payment_stats->get_earnings_by_range( $args['date'], false, $date_start, $date_end ); 
  1224.  
  1225. $total = (float) 0.00; 
  1226.  
  1227. foreach ( $results as $result ) { 
  1228. $total += (float) $result['total']; 
  1229.  
  1230. $earnings['earnings'][ $args['date'] ] = (float) $total; 
  1231. } else { 
  1232. $date_start = $dates['year'] . '-' . $dates['m_start'] . '-' . $dates['day']; 
  1233. $date_end = date( 'Y-m-d', strtotime( '+1 day', strtotime( $date_start ) ) ); 
  1234.  
  1235. $results = EDD()->payment_stats->get_earnings_by_range( $args['date'], false, $date_start, $date_end ); 
  1236. if ($args['date'] == 'yesterday' || $args['date'] == 'today') { 
  1237. foreach ($results as $result) { 
  1238. $earnings['earnings'][ $args['date'] ] += $result['total']; 
  1239. } else { 
  1240. $earnings['earnings'][ $args['date'] ] = $results[0]['total']; 
  1241. } elseif ( $args['product'] == 'all' ) { 
  1242. $products = get_posts( array( 'post_type' => 'download', 'nopaging' => true ) ); 
  1243.  
  1244. $i = 0; 
  1245. foreach ( $products as $product_info ) { 
  1246. $earnings['earnings'][ $i ] = array( $product_info->post_name => edd_get_download_earnings_stats( $product_info->ID ) ); 
  1247. $i++; 
  1248. } else { 
  1249. if ( get_post_type( $args['product'] ) == 'download' ) { 
  1250. $product_info = get_post( $args['product'] ); 
  1251. $earnings['earnings'][0] = array( $product_info->post_name => edd_get_download_earnings_stats( $args['product'] ) ); 
  1252. } else { 
  1253. $error['error'] = sprintf( __( 'Product %s not found!', 'easy-digital-downloads' ), $args['product'] ); 
  1254.  
  1255. if ( ! empty( $error ) ) 
  1256. return $error; 
  1257.  
  1258. return apply_filters( 'edd_api_stats_earnings', $earnings, $this ); 
  1259. } elseif ( $args['type'] == 'customers' ) { 
  1260. if ( version_compare( $edd_version, '2.3', '<' ) || ! edd_has_upgrade_completed( 'upgrade_customer_payments_association' ) ) { 
  1261. global $wpdb; 
  1262. $stats = array(); 
  1263. $count = $wpdb->get_col( "SELECT COUNT(DISTINCT meta_value) FROM $wpdb->postmeta WHERE meta_key = '_edd_payment_user_email'" ); 
  1264. $stats['customers']['total_customers'] = $count[0]; 
  1265. return apply_filters( 'edd_api_stats_customers', $stats, $this ); 
  1266. } else { 
  1267. $customers = new EDD_DB_Customers(); 
  1268. $stats['customers']['total_customers'] = $customers->count(); 
  1269. return apply_filters( 'edd_api_stats_customers', $stats, $this ); 
  1270. } elseif ( empty( $args['type'] ) ) { 
  1271. $stats = array_merge( $stats, $this->get_default_sales_stats() ); 
  1272. $stats = array_merge ( $stats, $this->get_default_earnings_stats() ); 
  1273.  
  1274. return apply_filters( 'edd_api_stats', array( 'stats' => $stats, $this ) ); 
  1275.  
  1276. /** 
  1277. * Retrieves Recent Sales 
  1278. * @access public 
  1279. * @since 1.5 
  1280. * @return array 
  1281. */ 
  1282. public function get_recent_sales() { 
  1283. global $wp_query; 
  1284.  
  1285. $sales = array(); 
  1286.  
  1287. if( ! user_can( $this->user_id, 'view_shop_reports' ) && ! $this->override ) { 
  1288. return $sales; 
  1289.  
  1290. if( isset( $wp_query->query_vars['id'] ) ) { 
  1291. $query = array(); 
  1292. $query[] = new EDD_Payment( $wp_query->query_vars['id'] ); 
  1293. } elseif( isset( $wp_query->query_vars['purchasekey'] ) ) { 
  1294. $query = array(); 
  1295. $query[] = edd_get_payment_by( 'key', $wp_query->query_vars['purchasekey'] ); 
  1296. } elseif( isset( $wp_query->query_vars['email'] ) ) { 
  1297. $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' => 'publish' ) ); 
  1298. } else { 
  1299. $query = edd_get_payments( array( 'fields' => 'ids', 'number' => $this->per_page(), 'page' => $this->get_paged(), 'status' => 'publish' ) ); 
  1300.  
  1301. if ( $query ) { 
  1302. $i = 0; 
  1303. foreach ( $query as $payment ) { 
  1304. if ( is_numeric( $payment ) ) { 
  1305. $payment = new EDD_Payment( $payment ); 
  1306.  
  1307. $payment_meta = $payment->get_meta(); 
  1308. $user_info = $payment->user_info; 
  1309.  
  1310. $sales['sales'][ $i ]['ID'] = $payment->number; 
  1311. $sales['sales'][ $i ]['transaction_id'] = $payment->transaction_id; 
  1312. $sales['sales'][ $i ]['key'] = $payment->key; 
  1313. $sales['sales'][ $i ]['discount'] = ! empty( $payment->discounts ) ? explode( ', ', $payment->discounts ) : array(); 
  1314. $sales['sales'][ $i ]['subtotal'] = $payment->subtotal; 
  1315. $sales['sales'][ $i ]['tax'] = $payment->tax; 
  1316. $sales['sales'][ $i ]['fees'] = $payment->fees; 
  1317. $sales['sales'][ $i ]['total'] = $payment->total; 
  1318. $sales['sales'][ $i ]['gateway'] = $payment->gateway; 
  1319. $sales['sales'][ $i ]['email'] = $payment->email; 
  1320. $sales['sales'][ $i ]['user_id'] = $payment->user_id; 
  1321. $sales['sales'][ $i ]['customer_id'] = $payment->customer_id; 
  1322. $sales['sales'][ $i ]['date'] = $payment->date; 
  1323. $sales['sales'][ $i ]['products'] = array(); 
  1324.  
  1325. $c = 0; 
  1326.  
  1327. foreach ( $payment->cart_details as $key => $item ) { 
  1328.  
  1329. $item_id = isset( $item['id'] ) ? $item['id'] : $item; 
  1330. $price = isset( $item['price'] ) ? $item['price'] : false; 
  1331. $price_id = isset( $item['item_number']['options']['price_id'] ) ? $item['item_number']['options']['price_id'] : null; 
  1332. $quantity = isset( $item['quantity'] ) && $item['quantity'] > 0 ? $item['quantity'] : 1; 
  1333.  
  1334. if( ! $price ) { 
  1335. // This function is only used on payments with near 1.0 cart data structure 
  1336. $price = edd_get_download_final_price( $item_id, $user_info, null ); 
  1337.  
  1338. $price_name = ''; 
  1339. if ( isset( $item['item_number'] ) && isset( $item['item_number']['options'] ) ) { 
  1340. $price_options = $item['item_number']['options']; 
  1341. if ( isset( $price_options['price_id'] ) ) { 
  1342. $price_name = edd_get_price_option_name( $item_id, $price_options['price_id'], $payment->ID ); 
  1343.  
  1344. $sales['sales'][ $i ]['products'][ $c ]['id'] = $item_id; 
  1345. $sales['sales'][ $i ]['products'][ $c ]['quantity'] = $quantity; 
  1346. $sales['sales'][ $i ]['products'][ $c ]['name'] = get_the_title( $item_id ); 
  1347. $sales['sales'][ $i ]['products'][ $c ]['price'] = $price; 
  1348. $sales['sales'][ $i ]['products'][ $c ]['price_name'] = $price_name; 
  1349. $c++; 
  1350.  
  1351. $i++; 
  1352. return apply_filters( 'edd_api_sales', $sales, $this ); 
  1353.  
  1354. /** 
  1355. * Process Get Discounts API Request 
  1356. * @access public 
  1357. * @since 1.6 
  1358. * @global object $wpdb Used to query the database using the WordPress 
  1359. * Database API 
  1360. * @param int $discount Discount ID 
  1361. * @return array $discounts Multidimensional array of the discounts 
  1362. */ 
  1363. public function get_discounts( $discount = null ) { 
  1364.  
  1365. $discount_list = array(); 
  1366.  
  1367. if( ! user_can( $this->user_id, 'manage_shop_discounts' ) && ! $this->override ) { 
  1368. return $discount_list; 
  1369. $error = array(); 
  1370.  
  1371. if ( empty( $discount ) ) { 
  1372.  
  1373. global $wpdb; 
  1374.  
  1375. $paged = $this->get_paged(); 
  1376. $per_page = $this->per_page(); 
  1377. $discounts = edd_get_discounts( array( 'posts_per_page' => $per_page, 'paged' => $paged ) ); 
  1378. $count = 0; 
  1379.  
  1380. if ( empty( $discounts ) ) { 
  1381. $error['error'] = __( 'No discounts found!', 'easy-digital-downloads' ); 
  1382. return $error; 
  1383.  
  1384. foreach ( $discounts as $discount ) { 
  1385.  
  1386. $discount_list['discounts'][$count]['ID'] = $discount->ID; 
  1387. $discount_list['discounts'][$count]['name'] = $discount->post_title; 
  1388. $discount_list['discounts'][$count]['code'] = edd_get_discount_code( $discount->ID ); 
  1389. $discount_list['discounts'][$count]['amount'] = edd_get_discount_amount( $discount->ID ); 
  1390. $discount_list['discounts'][$count]['min_price'] = edd_get_discount_min_price( $discount->ID ); 
  1391. $discount_list['discounts'][$count]['type'] = edd_get_discount_type( $discount->ID ); 
  1392. $discount_list['discounts'][$count]['uses'] = edd_get_discount_uses( $discount->ID ); 
  1393. $discount_list['discounts'][$count]['max_uses'] = edd_get_discount_max_uses( $discount->ID ); 
  1394. $discount_list['discounts'][$count]['start_date'] = edd_get_discount_start_date( $discount->ID ); 
  1395. $discount_list['discounts'][$count]['exp_date'] = edd_get_discount_expiration( $discount->ID ); 
  1396. $discount_list['discounts'][$count]['status'] = $discount->post_status; 
  1397. $discount_list['discounts'][$count]['product_requirements'] = edd_get_discount_product_reqs( $discount->ID ); 
  1398. $discount_list['discounts'][$count]['requirement_condition'] = edd_get_discount_product_condition( $discount->ID ); 
  1399. $discount_list['discounts'][$count]['global_discount'] = edd_is_discount_not_global( $discount->ID ); 
  1400. $discount_list['discounts'][$count]['single_use'] = edd_discount_is_single_use( $discount->ID ); 
  1401.  
  1402. $count++; 
  1403.  
  1404. } else { 
  1405.  
  1406. if ( is_numeric( $discount ) && get_post( $discount ) ) { 
  1407.  
  1408. $discount_list['discounts'][0]['ID'] = $discount; 
  1409. $discount_list['discounts'][0]['name'] = get_post_field( 'post_title', $discount ); 
  1410. $discount_list['discounts'][0]['code'] = edd_get_discount_code( $discount ); 
  1411. $discount_list['discounts'][0]['amount'] = edd_get_discount_amount( $discount ); 
  1412. $discount_list['discounts'][0]['min_price'] = edd_get_discount_min_price( $discount ); 
  1413. $discount_list['discounts'][0]['type'] = edd_get_discount_type( $discount ); 
  1414. $discount_list['discounts'][0]['uses'] = edd_get_discount_uses( $discount ); 
  1415. $discount_list['discounts'][0]['max_uses'] = edd_get_discount_max_uses( $discount ); 
  1416. $discount_list['discounts'][0]['start_date'] = edd_get_discount_start_date( $discount ); 
  1417. $discount_list['discounts'][0]['exp_date'] = edd_get_discount_expiration( $discount ); 
  1418. $discount_list['discounts'][0]['status'] = get_post_field( 'post_status', $discount ); 
  1419. $discount_list['discounts'][0]['product_requirements'] = edd_get_discount_product_reqs( $discount ); 
  1420. $discount_list['discounts'][0]['requirement_condition'] = edd_get_discount_product_condition( $discount ); 
  1421. $discount_list['discounts'][0]['global_discount'] = edd_is_discount_not_global( $discount ); 
  1422. $discount_list['discounts'][0]['single_use'] = edd_discount_is_single_use( $discount ); 
  1423.  
  1424. } else { 
  1425.  
  1426. $error['error'] = sprintf( __( 'Discount %s not found!', 'easy-digital-downloads' ), $discount ); 
  1427. return $error; 
  1428.  
  1429.  
  1430.  
  1431. return apply_filters( 'edd_api_discounts', $discount_list, $this ); 
  1432.  
  1433. /** 
  1434. * Process Get Downloads API Request to retrieve download logs 
  1435. * @access public 
  1436. * @since 2.5 
  1437. * @author Daniel J Griffiths 
  1438. * @param int $customer_id The customer ID you wish to retrieve download logs for 
  1439. * @return array Multidimensional array of the download logs 
  1440. */ 
  1441. public function get_download_logs( $customer_id = 0 ) { 
  1442. global $edd_logs; 
  1443.  
  1444. $downloads = array(); 
  1445. $errors = array(); 
  1446. $invalid_customer = false; 
  1447.  
  1448. $paged = $this->get_paged(); 
  1449. $per_page = $this->per_page(); 
  1450. $offset = $per_page * ( $paged - 1 ); 
  1451.  
  1452. $meta_query = array(); 
  1453. if ( ! empty( $customer_id ) ) { 
  1454.  
  1455. $customer = new EDD_Customer( $customer_id ); 
  1456.  
  1457. if ( $customer->id > 0 ) { 
  1458. $meta_query['relation'] = 'OR'; 
  1459.  
  1460. if ( $customer->id > 0 ) { 
  1461. // Based on customer->user_id 
  1462. $meta_query[] = array( 
  1463. 'key' => '_edd_log_user_id',  
  1464. 'value' => $customer->user_id,  
  1465. ); 
  1466.  
  1467. // Based on customer->email 
  1468. $meta_query[] = array( 
  1469. 'key' => '_edd_log_user_info',  
  1470. 'value' => $customer->email,  
  1471. 'compare'=> 'LIKE',  
  1472. ); 
  1473. } else { 
  1474. $invalid_customer = true; 
  1475.  
  1476. $query = array( 
  1477. 'log_type' => 'file_download',  
  1478. 'paged' => $paged,  
  1479. 'meta_query' => $meta_query,  
  1480. 'posts_per_page' => $per_page,  
  1481. 'update_post_meta_cache' => false,  
  1482. 'update_post_term_cache' => false,  
  1483. ); 
  1484.  
  1485. $logs = array(); 
  1486. if ( ! $invalid_customer ) { 
  1487. $logs = $edd_logs->get_connected_logs( $query ); 
  1488.  
  1489. if ( empty( $logs ) ) { 
  1490. $error['error'] = __( 'No download logs found!', 'easy-digital-downloads' ); 
  1491. return $error; 
  1492.  
  1493. foreach( $logs as $log ) { 
  1494. $item = array(); 
  1495.  
  1496. $log_meta = get_post_custom( $log->ID ); 
  1497. $user_info = isset( $log_meta['_edd_log_user_info'] ) ? maybe_unserialize( $log_meta['_edd_log_user_info'][0] ) : array(); 
  1498. $payment_id = isset( $log_meta['_edd_log_payment_id'] ) ? $log_meta['_edd_log_payment_id'][0] : false; 
  1499.  
  1500. $payment_customer_id = edd_get_payment_customer_id( $payment_id ); 
  1501. $payment_customer = new EDD_Customer( $payment_customer_id ); 
  1502. $user_id = ( $payment_customer->user_id > 0 ) ? $payment_customer->user_id : false; 
  1503. $ip = $log_meta['_edd_log_ip'][0]; 
  1504. $files = edd_get_payment_meta_downloads( $payment_id ); 
  1505. $files = edd_get_download_files( $files[0]['id'] ); 
  1506. $file_id = (int) $log_meta['_edd_log_file_id'][0]; 
  1507. $file_id = $file_id !== false ? $file_id : 0; 
  1508. $file_name = isset( $files[ $file_id ]['name'] ) ? $files[ $file_id ]['name'] : null; 
  1509.  
  1510. $item = array( 
  1511. 'ID' => $log->ID,  
  1512. 'user_id' => $user_id,  
  1513. 'product_id' => $log->post_parent,  
  1514. 'product_name' => get_the_title( $log->post_parent ),  
  1515. 'customer_id' => $payment_customer_id,  
  1516. 'payment_id' => $payment_id,  
  1517. 'file' => $file_name,  
  1518. 'ip' => $ip,  
  1519. 'date' => $log->post_date,  
  1520. ); 
  1521.  
  1522. $item = apply_filters( 'edd_api_download_log_item', $item, $log, $log_meta ); 
  1523.  
  1524. $downloads['download_logs'][] = $item; 
  1525.  
  1526.  
  1527. return apply_filters( 'edd_api_download_logs', $downloads, $this ); 
  1528.  
  1529. /** 
  1530. * Process Get Info API Request 
  1531. * @param array $args Arguments provided by API Request 
  1532. * @return array 
  1533. */ 
  1534. public function get_info() { 
  1535. $data = array(); 
  1536.  
  1537. // plugin.php required to use is_plugin_active() 
  1538. require_once ABSPATH . 'wp-admin/includes/plugin.php'; 
  1539.  
  1540. // Integrations 
  1541. if ( is_plugin_active( 'edd-commissions/edd-commissions.php' ) ) { 
  1542. $data['info']['integrations']['commissions'] = true; 
  1543.  
  1544. if ( class_exists( 'EDD_Software_Licensing' ) ) { 
  1545. $data['info']['integrations']['software_licensing'] = true; 
  1546.  
  1547. if ( class_exists( 'EDD_Front_End_Submissions' ) ) { 
  1548. $data['info']['integrations']['fes'] = true; 
  1549.  
  1550. if ( class_exists( 'EDD_Reviews' ) ) { 
  1551. $data['info']['integrations']['reviews'] = true; 
  1552.  
  1553. if ( class_exists( 'EDD_Recurring' ) ) { 
  1554. $data['info']['integrations']['recurring'] = true; 
  1555.  
  1556. // Permissions 
  1557. if ( user_can( $this->user_id, 'view_shop_reports' ) ) { 
  1558. $data['info']['permissions']['view_shop_reports'] = true; 
  1559.  
  1560. if ( user_can( $this->user_id, 'view_shop_sensitive_data' ) ) { 
  1561. $data['info']['permissions']['view_shop_sensitive_data'] = true; 
  1562.  
  1563. if ( user_can( $this->user_id, 'manage_shop_discounts' ) ) { 
  1564. $data['info']['permissions']['manage_shop_discounts'] = true; 
  1565.  
  1566. // Site Information 
  1567. if ( user_can( $this->user_id, 'view_shop_sensitive_data' ) ) { 
  1568. $data['info']['site']['wp_version'] = get_bloginfo( 'version' ); 
  1569. $data['info']['site']['edd_version'] = EDD_VERSION; 
  1570.  
  1571. $data['info']['site']['currency'] = edd_get_currency(); 
  1572. $data['info']['site']['currency_position'] = edd_get_option( 'currency_position', 'before' ); 
  1573. $data['info']['site']['decimal_separator'] = edd_get_option( 'decimal_separator', '.' ); 
  1574. $data['info']['site']['thousands_separator'] = edd_get_option( 'thousands_separator', ', ' ); 
  1575.  
  1576. return apply_filters( 'edd_api_info', $data, $this ); 
  1577.  
  1578. /** 
  1579. * Retrieve the output format 
  1580. * Determines whether results should be displayed in XML or JSON 
  1581. * @since 1.5 
  1582. * @return mixed|void 
  1583. */ 
  1584. public function get_output_format() { 
  1585. global $wp_query; 
  1586.  
  1587. $format = isset( $wp_query->query_vars['format'] ) ? $wp_query->query_vars['format'] : 'json'; 
  1588.  
  1589. return apply_filters( 'edd_api_output_format', $format ); 
  1590.  
  1591.  
  1592. /** 
  1593. * Log each API request, if enabled 
  1594. * @access private 
  1595. * @since 1.5 
  1596. * @global $edd_logs 
  1597. * @global $wp_query 
  1598. * @param array $data 
  1599. * @return void 
  1600. */ 
  1601. private function log_request( $data = array() ) { 
  1602. if ( ! $this->log_requests() ) { 
  1603. return; 
  1604.  
  1605. global $edd_logs, $wp_query; 
  1606.  
  1607. $query = array( 
  1608. 'edd-api' => $wp_query->query_vars['edd-api'],  
  1609. 'key' => isset( $wp_query->query_vars['key'] ) ? $wp_query->query_vars['key'] : null,  
  1610. 'token' => isset( $wp_query->query_vars['token'] ) ? $wp_query->query_vars['token'] : null,  
  1611. 'query' => isset( $wp_query->query_vars['query'] ) ? $wp_query->query_vars['query'] : null,  
  1612. 'type' => isset( $wp_query->query_vars['type'] ) ? $wp_query->query_vars['type'] : null,  
  1613. 'product' => isset( $wp_query->query_vars['product'] ) ? $wp_query->query_vars['product'] : null,  
  1614. 'customer' => isset( $wp_query->query_vars['customer'] ) ? $wp_query->query_vars['customer'] : null,  
  1615. 'date' => isset( $wp_query->query_vars['date'] ) ? $wp_query->query_vars['date'] : null,  
  1616. 'startdate' => isset( $wp_query->query_vars['startdate'] ) ? $wp_query->query_vars['startdate'] : null,  
  1617. 'enddate' => isset( $wp_query->query_vars['enddate'] ) ? $wp_query->query_vars['enddate'] : null,  
  1618. 'id' => isset( $wp_query->query_vars['id'] ) ? $wp_query->query_vars['id'] : null,  
  1619. 'purchasekey' => isset( $wp_query->query_vars['purchasekey'] ) ? $wp_query->query_vars['purchasekey'] : null,  
  1620. 'email' => isset( $wp_query->query_vars['email'] ) ? $wp_query->query_vars['email'] : null,  
  1621. ); 
  1622.  
  1623. $log_data = array( 
  1624. 'log_type' => 'api_request',  
  1625. 'post_excerpt' => http_build_query( $query ),  
  1626. 'post_content' => ! empty( $data['error'] ) ? $data['error'] : '',  
  1627. ); 
  1628.  
  1629. $log_meta = array( 
  1630. 'request_ip' => edd_get_ip(),  
  1631. 'user' => $this->user_id,  
  1632. 'key' => isset( $wp_query->query_vars['key'] ) ? $wp_query->query_vars['key'] : null,  
  1633. 'token' => isset( $wp_query->query_vars['token'] ) ? $wp_query->query_vars['token'] : null,  
  1634. 'time' => $data['request_speed'],  
  1635. 'version' => $this->get_queried_version() 
  1636. ); 
  1637.  
  1638. $edd_logs->insert_log( $log_data, $log_meta ); 
  1639.  
  1640.  
  1641. /** 
  1642. * Retrieve the output data 
  1643. * @access public 
  1644. * @since 1.5.2 
  1645. * @return array 
  1646. */ 
  1647. public function get_output() { 
  1648. return $this->data; 
  1649.  
  1650. /** 
  1651. * Output Query in either JSON/XML. The query data is outputted as JSON 
  1652. * by default 
  1653. * @author Daniel J Griffiths 
  1654. * @since 1.5 
  1655. * @global $wp_query 
  1656. * @param int $status_code 
  1657. */ 
  1658. public function output( $status_code = 200 ) { 
  1659. global $wp_query; 
  1660.  
  1661. $format = $this->get_output_format(); 
  1662.  
  1663. status_header( $status_code ); 
  1664.  
  1665. do_action( 'edd_api_output_before', $this->data, $this, $format ); 
  1666.  
  1667. switch ( $format ) : 
  1668.  
  1669. case 'xml' : 
  1670.  
  1671. require_once EDD_PLUGIN_DIR . 'includes/libraries/array2xml.php'; 
  1672. $xml = Array2XML::createXML( 'edd', $this->data ); 
  1673. echo $xml->saveXML(); 
  1674.  
  1675. break; 
  1676.  
  1677. case 'json' : 
  1678.  
  1679. header( 'Content-Type: application/json' ); 
  1680. if ( ! empty( $this->pretty_print ) ) 
  1681. echo json_encode( $this->data, $this->pretty_print ); 
  1682. else 
  1683. echo json_encode( $this->data ); 
  1684.  
  1685. break; 
  1686.  
  1687.  
  1688. default : 
  1689.  
  1690. // Allow other formats to be added via extensions 
  1691. do_action( 'edd_api_output_' . $format, $this->data, $this ); 
  1692.  
  1693. break; 
  1694.  
  1695. endswitch; 
  1696.  
  1697. do_action( 'edd_api_output_after', $this->data, $this, $format ); 
  1698.  
  1699. edd_die(); 
  1700.  
  1701. /** 
  1702. * Modify User Profile 
  1703. * Modifies the output of profile.php to add key generation/revocation 
  1704. * @access public 
  1705. * @author Daniel J Griffiths 
  1706. * @since 1.5 
  1707. * @param object $user Current user info 
  1708. * @return void 
  1709. */ 
  1710. function user_key_field( $user ) { 
  1711. if ( ( edd_get_option( 'api_allow_user_keys', false ) || current_user_can( 'manage_shop_settings' ) ) && current_user_can( 'edit_user', $user->ID ) ) { 
  1712. $user = get_userdata( $user->ID ); 
  1713. ?> 
  1714. <table class="form-table"> 
  1715. <tbody> 
  1716. <tr> 
  1717. <th> 
  1718. <?php _e( 'Easy Digital Downloads API Keys', 'easy-digital-downloads' ); ?> 
  1719. </th> 
  1720. <td> 
  1721. <?php 
  1722. $public_key = $this->get_user_public_key( $user->ID ); 
  1723. $secret_key = $this->get_user_secret_key( $user->ID ); 
  1724. ?> 
  1725. <?php if ( empty( $user->edd_user_public_key ) ) { ?> 
  1726. <input name="edd_set_api_key" type="checkbox" id="edd_set_api_key" value="0" /> 
  1727. <span class="description"><?php _e( 'Generate API Key', 'easy-digital-downloads' ); ?></span> 
  1728. <?php } else { ?> 
  1729. <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/> 
  1730. <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/> 
  1731. <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/> 
  1732. <input name="edd_set_api_key" type="checkbox" id="edd_set_api_key" value="0" /> 
  1733. <span class="description"><label for="edd_set_api_key"><?php _e( 'Revoke API Keys', 'easy-digital-downloads' ); ?></label></span> 
  1734. <?php } ?> 
  1735. </td> 
  1736. </tr> 
  1737. </tbody> 
  1738. </table> 
  1739. <?php } 
  1740.  
  1741. /** 
  1742. * Process an API key generation/revocation 
  1743. * @access public 
  1744. * @since 2.0.0 
  1745. * @param array $args 
  1746. * @return void 
  1747. */ 
  1748. public function process_api_key( $args ) { 
  1749.  
  1750. if( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'edd-api-nonce' ) ) { 
  1751.  
  1752. wp_die( __( 'Nonce verification failed', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) ); 
  1753.  
  1754.  
  1755. if ( empty( $args['user_id'] ) ) { 
  1756. wp_die( sprintf( __( 'User ID Required', 'easy-digital-downloads' ), $process ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 401 ) ); 
  1757.  
  1758. if( is_numeric( $args['user_id'] ) ) { 
  1759. $user_id = isset( $args['user_id'] ) ? absint( $args['user_id'] ) : get_current_user_id(); 
  1760. } else { 
  1761. $userdata = get_user_by( 'login', $args['user_id'] ); 
  1762. $user_id = $userdata->ID; 
  1763. $process = isset( $args['edd_api_process'] ) ? strtolower( $args['edd_api_process'] ) : false; 
  1764.  
  1765. if( $user_id == get_current_user_id() && ! edd_get_option( 'allow_user_api_keys' ) && ! current_user_can( 'manage_shop_settings' ) ) { 
  1766. 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 ) ); 
  1767. } elseif( ! current_user_can( 'manage_shop_settings' ) ) { 
  1768. 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 ) ); 
  1769.  
  1770. switch( $process ) { 
  1771. case 'generate': 
  1772. if( $this->generate_api_key( $user_id ) ) { 
  1773. delete_transient( 'edd-total-api-keys' ); 
  1774. wp_redirect( add_query_arg( 'edd-message', 'api-key-generated', 'edit.php?post_type=download&page=edd-tools&tab=api_keys' ) ); exit(); 
  1775. } else { 
  1776. wp_redirect( add_query_arg( 'edd-message', 'api-key-failed', 'edit.php?post_type=download&page=edd-tools&tab=api_keys' ) ); exit(); 
  1777. break; 
  1778. case 'regenerate': 
  1779. $this->generate_api_key( $user_id, true ); 
  1780. delete_transient( 'edd-total-api-keys' ); 
  1781. wp_redirect( add_query_arg( 'edd-message', 'api-key-regenerated', 'edit.php?post_type=download&page=edd-tools&tab=api_keys' ) ); exit(); 
  1782. break; 
  1783. case 'revoke': 
  1784. $this->revoke_api_key( $user_id ); 
  1785. delete_transient( 'edd-total-api-keys' ); 
  1786. wp_redirect( add_query_arg( 'edd-message', 'api-key-revoked', 'edit.php?post_type=download&page=edd-tools&tab=api_keys' ) ); exit(); 
  1787. break; 
  1788. default; 
  1789. break; 
  1790.  
  1791. /** 
  1792. * Generate new API keys for a user 
  1793. * @access public 
  1794. * @since 2.0.0 
  1795. * @param int $user_id User ID the key is being generated for 
  1796. * @param boolean $regenerate Regenerate the key for the user 
  1797. * @return boolean True if (re)generated successfully, false otherwise. 
  1798. */ 
  1799. public function generate_api_key( $user_id = 0, $regenerate = false ) { 
  1800.  
  1801. if( empty( $user_id ) ) { 
  1802. return false; 
  1803.  
  1804. $user = get_userdata( $user_id ); 
  1805.  
  1806. if( ! $user ) { 
  1807. return false; 
  1808.  
  1809. $public_key = $this->get_user_public_key( $user_id ); 
  1810. $secret_key = $this->get_user_secret_key( $user_id ); 
  1811.  
  1812. if ( empty( $public_key ) || $regenerate == true ) { 
  1813. $new_public_key = $this->generate_public_key( $user->user_email ); 
  1814. $new_secret_key = $this->generate_private_key( $user->ID ); 
  1815. } else { 
  1816. return false; 
  1817.  
  1818. if ( $regenerate == true ) { 
  1819. $this->revoke_api_key( $user->ID ); 
  1820.  
  1821. update_user_meta( $user_id, $new_public_key, 'edd_user_public_key' ); 
  1822. update_user_meta( $user_id, $new_secret_key, 'edd_user_secret_key' ); 
  1823.  
  1824. return true; 
  1825.  
  1826. /** 
  1827. * Revoke a users API keys 
  1828. * @access public 
  1829. * @since 2.0.0 
  1830. * @param int $user_id User ID of user to revoke key for 
  1831. * @return string 
  1832. */ 
  1833. public function revoke_api_key( $user_id = 0 ) { 
  1834.  
  1835. if( empty( $user_id ) ) { 
  1836. return false; 
  1837.  
  1838. $user = get_userdata( $user_id ); 
  1839.  
  1840. if( ! $user ) { 
  1841. return false; 
  1842.  
  1843. $public_key = $this->get_user_public_key( $user_id ); 
  1844. $secret_key = $this->get_user_secret_key( $user_id ); 
  1845. if ( ! empty( $public_key ) ) { 
  1846. delete_transient( md5( 'edd_api_user_' . $public_key ) ); 
  1847. delete_transient( md5('edd_api_user_public_key' . $user_id ) ); 
  1848. delete_transient( md5('edd_api_user_secret_key' . $user_id ) ); 
  1849. delete_user_meta( $user_id, $public_key ); 
  1850. delete_user_meta( $user_id, $secret_key ); 
  1851. } else { 
  1852. return false; 
  1853.  
  1854. return true; 
  1855.  
  1856. public function get_version() { 
  1857. return self::VERSION; 
  1858.  
  1859.  
  1860. /** 
  1861. * Generate and Save API key 
  1862. * Generates the key requested by user_key_field and stores it in the database 
  1863. * @access public 
  1864. * @author Daniel J Griffiths 
  1865. * @since 1.5 
  1866. * @param int $user_id 
  1867. * @return void 
  1868. */ 
  1869. public function update_key( $user_id ) { 
  1870. edd_update_user_api_key( $user_id ); 
  1871.  
  1872. /** 
  1873. * Generate the public key for a user 
  1874. * @access private 
  1875. * @since 1.9.9 
  1876. * @param string $user_email 
  1877. * @return string 
  1878. */ 
  1879. public function generate_public_key( $user_email = '' ) { 
  1880. $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : ''; 
  1881. $public = hash( 'md5', $user_email . $auth_key . date( 'U' ) ); 
  1882. return $public; 
  1883.  
  1884. /** 
  1885. * Generate the secret key for a user 
  1886. * @access private 
  1887. * @since 1.9.9 
  1888. * @param int $user_id 
  1889. * @return string 
  1890. */ 
  1891. public function generate_private_key( $user_id = 0 ) { 
  1892. $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : ''; 
  1893. $secret = hash( 'md5', $user_id . $auth_key . date( 'U' ) ); 
  1894. return $secret; 
  1895.  
  1896. /** 
  1897. * Retrieve the user's token 
  1898. * @access private 
  1899. * @since 1.9.9 
  1900. * @param int $user_id 
  1901. * @return string 
  1902. */ 
  1903. public function get_token( $user_id = 0 ) { 
  1904. return hash( 'md5', $this->get_user_secret_key( $user_id ) . $this->get_user_public_key( $user_id ) ); 
  1905.  
  1906. /** 
  1907. * Generate the default sales stats returned by the 'stats' endpoint 
  1908. * @access private 
  1909. * @since 1.5.3 
  1910. * @return array default sales statistics 
  1911. */ 
  1912. private function get_default_sales_stats() { 
  1913.  
  1914. // Default sales return 
  1915. $sales = array(); 
  1916. $sales['sales']['today'] = $this->stats->get_sales( 0, 'today' ); 
  1917. $sales['sales']['current_month'] = $this->stats->get_sales( 0, 'this_month' ); 
  1918. $sales['sales']['last_month'] = $this->stats->get_sales( 0, 'last_month' ); 
  1919. $sales['sales']['totals'] = edd_get_total_sales(); 
  1920.  
  1921. return $sales; 
  1922.  
  1923. /** 
  1924. * Generate the default earnings stats returned by the 'stats' endpoint 
  1925. * @access private 
  1926. * @since 1.5.3 
  1927. * @return array default earnings statistics 
  1928. */ 
  1929. private function get_default_earnings_stats() { 
  1930.  
  1931. // Default earnings return 
  1932. $earnings = array(); 
  1933. $earnings['earnings']['today'] = $this->stats->get_earnings( 0, 'today' ); 
  1934. $earnings['earnings']['current_month'] = $this->stats->get_earnings( 0, 'this_month' ); 
  1935. $earnings['earnings']['last_month'] = $this->stats->get_earnings( 0, 'last_month' ); 
  1936. $earnings['earnings']['totals'] = edd_get_total_earnings(); 
  1937.  
  1938. return $earnings; 
  1939.  
  1940. /** 
  1941. * A Backwards Compatibility call for the change of meta_key/value for users API Keys 
  1942. * @since 2.4 
  1943. * @param string $check Wether to check the cache or not 
  1944. * @param int $object_id The User ID being passed 
  1945. * @param string $meta_key The user meta key 
  1946. * @param bool $single If it should return a single value or array 
  1947. * @return string The API key/secret for the user supplied 
  1948. */ 
  1949. public function api_key_backwards_copmat( $check, $object_id, $meta_key, $single ) { 
  1950.  
  1951. if ( $meta_key !== 'edd_user_public_key' && $meta_key !== 'edd_user_secret_key' ) { 
  1952. return $check; 
  1953.  
  1954. $return = $check; 
  1955.  
  1956. switch( $meta_key ) { 
  1957. case 'edd_user_public_key': 
  1958. $return = EDD()->api->get_user_public_key( $object_id ); 
  1959. break; 
  1960. case 'edd_user_secret_key': 
  1961. $return = EDD()->api->get_user_secret_key( $object_id ); 
  1962. break; 
  1963.  
  1964. if ( ! $single ) { 
  1965. $return = array( $return ); 
  1966.  
  1967. return $return; 
  1968.  
  1969.  
  1970. /** 
  1971. * Sanitizes category and tag terms 
  1972. * @access private 
  1973. * @since 2.6 
  1974. * @param mixed $term Request variable 
  1975. * @return mixed Sanitized term/s 
  1976. */ 
  1977. public function sanitize_request_term( $term ) { 
  1978.  
  1979. if( is_array( $term ) ) { 
  1980. $term = array_map( 'sanitize_text_field', $term ); 
  1981. } else if( is_int( $term ) ) { 
  1982. $term = absint( $term ); 
  1983. } else { 
  1984. $term = sanitize_text_field( $term ); 
  1985.  
  1986. return $term; 
  1987.  
  1988.  
  1989. /** 
  1990. * Disable request logging 
  1991. * @access public 
  1992. * @since 2.7 
  1993. */ 
  1994. public function log_requests() { 
  1995. return apply_filters( 'edd_api_log_requests', true ); 
  1996.  
/includes/class-edd-api.php  
  1. class EDD_API { 
  2.  
  3. /** 
  4. * API Version 
  5. */ 
  6. const VERSION = '1.3'; 
  7.  
  8. /** 
  9. * Pretty Print? 
  10. * @var bool 
  11. * @access private 
  12. * @since 1.5 
  13. */ 
  14. private $pretty_print = false; 
  15.  
  16. /** 
  17. * Log API requests? 
  18. * @var bool 
  19. * @access private 
  20. * @since 1.5 
  21. */ 
  22. public $log_requests = true; 
  23.  
  24. /** 
  25. * Is this a valid request? 
  26. * @var bool 
  27. * @access private 
  28. * @since 1.5 
  29. */ 
  30. private $is_valid_request = false; 
  31.  
  32. /** 
  33. * User ID Performing the API Request 
  34. * @var int 
  35. * @access private 
  36. * @since 1.5.1 
  37. */ 
  38. private $user_id = 0; 
  39.  
  40. /** 
  41. * Instance of EDD Stats class 
  42. * @var object 
  43. * @access private 
  44. * @since 1.7 
  45. */ 
  46. private $stats; 
  47.  
  48. /** 
  49. * Response data to return 
  50. * @var array 
  51. * @access private 
  52. * @since 1.5.2 
  53. */ 
  54. private $data = array(); 
  55.  
  56. /** 
  57. * @var bool 
  58. * @access private 
  59. * @since 1.7 
  60. */ 
  61. private $override = true; 
  62.  
  63. /** 
  64. * Setup the EDD API 
  65. * @author Daniel J Griffiths 
  66. * @since 1.5 
  67. */ 
  68. public function __construct() { 
  69. add_action( 'init', array( $this, 'add_endpoint' ) ); 
  70. add_action( 'template_redirect', array( $this, 'process_query' ), -1 ); 
  71. add_filter( 'query_vars', array( $this, 'query_vars' ) ); 
  72. add_action( 'show_user_profile', array( $this, 'user_key_field' ) ); 
  73. add_action( 'edit_user_profile', array( $this, 'user_key_field' ) ); 
  74. add_action( 'personal_options_update', array( $this, 'update_key' ) ); 
  75. add_action( 'edit_user_profile_update', array( $this, 'update_key' ) ); 
  76. add_action( 'edd_process_api_key', array( $this, 'process_api_key' ) ); 
  77.  
  78. // Determine if JSON_PRETTY_PRINT is available 
  79. $this->pretty_print = defined( 'JSON_PRETTY_PRINT' ) ? JSON_PRETTY_PRINT : null; 
  80.  
  81. // Allow API request logging to be turned off 
  82. $this->log_requests = apply_filters( 'edd_api_log_requests', $this->log_requests ); 
  83.  
  84. // Setup EDD_Stats instance 
  85. $this->stats = new EDD_Payment_Stats; 
  86.  
  87.  
  88. /** 
  89. * Registers a new rewrite endpoint for accessing the API 
  90. * @access public 
  91. * @author Daniel J Griffiths 
  92. * @param array $rewrite_rules WordPress Rewrite Rules 
  93. * @since 1.5 
  94. */ 
  95. public function add_endpoint( $rewrite_rules ) { 
  96. add_rewrite_endpoint( 'edd-api', EP_ALL ); 
  97.  
  98. /** 
  99. * Registers query vars for API access 
  100. * @access public 
  101. * @since 1.5 
  102. * @author Daniel J Griffiths 
  103. * @param array $vars Query vars 
  104. * @return string[] $vars New query vars 
  105. */ 
  106. public function query_vars( $vars ) { 
  107. $vars[] = 'token'; 
  108. $vars[] = 'key'; 
  109. $vars[] = 'query'; 
  110. $vars[] = 'type'; 
  111. $vars[] = 'product'; 
  112. $vars[] = 'number'; 
  113. $vars[] = 'date'; 
  114. $vars[] = 'startdate'; 
  115. $vars[] = 'enddate'; 
  116. $vars[] = 'customer'; 
  117. $vars[] = 'discount'; 
  118. $vars[] = 'format'; 
  119. $vars[] = 'id'; 
  120. $vars[] = 'purchasekey'; 
  121. $vars[] = 'email'; 
  122.  
  123. return $vars; 
  124.  
  125. /** 
  126. * Validate the API request 
  127. * Checks for the user's public key and token against the secret key 
  128. * @access private 
  129. * @global object $wp_query WordPress Query 
  130. * @uses EDD_API::get_user() 
  131. * @uses EDD_API::invalid_key() 
  132. * @uses EDD_API::invalid_auth() 
  133. * @since 1.5 
  134. * @return void 
  135. */ 
  136. private function validate_request() { 
  137. global $wp_query; 
  138.  
  139. $this->override = false; 
  140.  
  141. // Make sure we have both user and api key 
  142. if ( ! empty( $wp_query->query_vars['edd-api'] ) && ( $wp_query->query_vars['edd-api'] != 'products' || ! empty( $wp_query->query_vars['token'] ) ) ) { 
  143. if ( empty( $wp_query->query_vars['token'] ) || empty( $wp_query->query_vars['key'] ) ) 
  144. $this->missing_auth(); 
  145.  
  146. // Retrieve the user by public API key and ensure they exist 
  147. if ( ! ( $user = $this->get_user( $wp_query->query_vars['key'] ) ) ) : 
  148. $this->invalid_key(); 
  149. else : 
  150. $token = urldecode( $wp_query->query_vars['token'] ); 
  151. $secret = get_user_meta( $user, 'edd_user_secret_key', true ); 
  152. $public = urldecode( $wp_query->query_vars['key'] ); 
  153.  
  154. if ( hash_equals( md5( $secret . $public ), $token ) ) 
  155. $this->is_valid_request = true; 
  156. else 
  157. $this->invalid_auth(); 
  158. endif; 
  159. } elseif ( !empty( $wp_query->query_vars['edd-api'] ) && $wp_query->query_vars['edd-api'] == 'products' ) { 
  160. $this->is_valid_request = true; 
  161. $wp_query->set( 'key', 'public' ); 
  162.  
  163. /** 
  164. * Retrieve the user ID based on the public key provided 
  165. * @access public 
  166. * @since 1.5.1 
  167. * @global object $wpdb Used to query the database using the WordPress 
  168. * Database API 
  169. * @param string $key Public Key 
  170. * @return bool if user ID is found, false otherwise 
  171. */ 
  172. public function get_user( $key = '' ) { 
  173. global $wpdb, $wp_query; 
  174.  
  175. if( empty( $key ) ) 
  176. $key = urldecode( $wp_query->query_vars['key'] ); 
  177.  
  178. if ( empty( $key ) ) { 
  179. return false; 
  180.  
  181. $user = get_transient( md5( 'edd_api_user_' . $key ) ); 
  182.  
  183. if ( false === $user ) { 
  184. $user = $wpdb->get_var( $wpdb->prepare( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = 'edd_user_public_key' AND meta_value = %s LIMIT 1", $key ) ); 
  185. set_transient( md5( 'edd_api_user_' . $key ) , $user, DAY_IN_SECONDS ); 
  186.  
  187. if ( $user != NULL ) { 
  188. $this->user_id = $user; 
  189. return $user; 
  190.  
  191. return false; 
  192.  
  193. /** 
  194. * Displays a missing authentication error if all the parameters aren't 
  195. * provided 
  196. * @access private 
  197. * @author Daniel J Griffiths 
  198. * @uses EDD_API::output() 
  199. * @since 1.5 
  200. */ 
  201. private function missing_auth() { 
  202. $error = array(); 
  203. $error['error'] = __( 'You must specify both a token and API key!', 'edd' ); 
  204.  
  205. $this->data = $error; 
  206. $this->output( 401 ); 
  207.  
  208. /** 
  209. * Displays an authentication failed error if the user failed to provide valid 
  210. * credentials 
  211. * @access private 
  212. * @since 1.5 
  213. * @uses EDD_API::output() 
  214. * @return void 
  215. */ 
  216. private function invalid_auth() { 
  217. $error = array(); 
  218. $error['error'] = __( 'Your request could not be authenticated!', 'edd' ); 
  219.  
  220. $this->data = $error; 
  221. $this->output( 401 ); 
  222.  
  223. /** 
  224. * Displays an invalid API key error if the API key provided couldn't be 
  225. * validated 
  226. * @access private 
  227. * @author Daniel J Griffiths 
  228. * @since 1.5 
  229. * @uses EDD_API::output() 
  230. * @return void 
  231. */ 
  232. private function invalid_key() { 
  233. $error = array(); 
  234. $error['error'] = __( 'Invalid API key!', 'edd' ); 
  235.  
  236. $this->data = $error; 
  237. $this->output( 401 ); 
  238.  
  239.  
  240. /** 
  241. * Listens for the API and then processes the API requests 
  242. * @access public 
  243. * @author Daniel J Griffiths 
  244. * @global $wp_query 
  245. * @since 1.5 
  246. * @return void 
  247. */ 
  248. public function process_query() { 
  249. global $wp_query; 
  250.  
  251. // Check for edd-api var. Get out if not present 
  252. if ( ! isset( $wp_query->query_vars['edd-api'] ) ) 
  253. return; 
  254.  
  255. // Check for a valid user and set errors if necessary 
  256. $this->validate_request(); 
  257.  
  258. // Only proceed if no errors have been noted 
  259. if( ! $this->is_valid_request ) 
  260. return; 
  261.  
  262. if( ! defined( 'EDD_DOING_API' ) ) { 
  263. define( 'EDD_DOING_API', true ); 
  264.  
  265. // Determine the kind of query 
  266. $query_mode = $this->get_query_mode(); 
  267.  
  268. $data = array(); 
  269.  
  270. switch( $query_mode ) : 
  271.  
  272. case 'stats' : 
  273.  
  274. $data = $this->get_stats( array( 
  275. 'type' => isset( $wp_query->query_vars['type'] ) ? $wp_query->query_vars['type'] : null,  
  276. 'product' => isset( $wp_query->query_vars['product'] ) ? $wp_query->query_vars['product'] : null,  
  277. 'date' => isset( $wp_query->query_vars['date'] ) ? $wp_query->query_vars['date'] : null,  
  278. 'startdate' => isset( $wp_query->query_vars['startdate'] ) ? $wp_query->query_vars['startdate'] : null,  
  279. 'enddate' => isset( $wp_query->query_vars['enddate'] ) ? $wp_query->query_vars['enddate'] : null 
  280. ) ); 
  281.  
  282. break; 
  283.  
  284. case 'products' : 
  285.  
  286. $product = isset( $wp_query->query_vars['product'] ) ? $wp_query->query_vars['product'] : null; 
  287.  
  288. $data = $this->get_products( $product ); 
  289.  
  290. break; 
  291.  
  292. case 'customers' : 
  293.  
  294. $customer = isset( $wp_query->query_vars['customer'] ) ? $wp_query->query_vars['customer'] : null; 
  295.  
  296. $data = $this->get_customers( $customer ); 
  297.  
  298. break; 
  299.  
  300. case 'sales' : 
  301.  
  302. $data = $this->get_recent_sales(); 
  303.  
  304. break; 
  305.  
  306. case 'discounts' : 
  307.  
  308. $discount = isset( $wp_query->query_vars['discount'] ) ? $wp_query->query_vars['discount'] : null; 
  309.  
  310. $data = $this->get_discounts( $discount ); 
  311.  
  312. break; 
  313.  
  314. endswitch; 
  315.  
  316. // Allow extensions to setup their own return data 
  317. $this->data = apply_filters( 'edd_api_output_data', $data, $query_mode, $this ); 
  318.  
  319. // Log this API request, if enabled. We log it here because we have access to errors. 
  320. $this->log_request( $this->data ); 
  321.  
  322. // Send out data to the output function 
  323. $this->output(); 
  324.  
  325. /** 
  326. * Determines the kind of query requested and also ensure it is a valid query 
  327. * @access private 
  328. * @since 1.5 
  329. * @global $wp_query 
  330. * @return string $query Query mode 
  331. */ 
  332. public function get_query_mode() { 
  333. global $wp_query; 
  334.  
  335. // Whitelist our query options 
  336. $accepted = apply_filters( 'edd_api_valid_query_modes', array( 
  337. 'stats',  
  338. 'products',  
  339. 'customers',  
  340. 'sales',  
  341. 'discounts' 
  342. ) ); 
  343.  
  344. $query = isset( $wp_query->query_vars['edd-api'] ) ? $wp_query->query_vars['edd-api'] : null; 
  345. $error = array(); 
  346. // Make sure our query is valid 
  347. if ( ! in_array( $query, $accepted ) ) { 
  348. $error['error'] = __( 'Invalid query!', 'edd' ); 
  349.  
  350. $this->data = $error; 
  351. $this->output(); 
  352.  
  353. return $query; 
  354.  
  355. /** 
  356. * Get page number 
  357. * @access private 
  358. * @since 1.5 
  359. * @global $wp_query 
  360. * @return int $wp_query->query_vars['page'] if page number returned (default: 1) 
  361. */ 
  362. public function get_paged() { 
  363. global $wp_query; 
  364.  
  365. return isset( $wp_query->query_vars['page'] ) ? $wp_query->query_vars['page'] : 1; 
  366.  
  367.  
  368. /** 
  369. * Number of results to display per page 
  370. * @access private 
  371. * @since 1.5 
  372. * @global $wp_query 
  373. * @return int $per_page Results to display per page (default: 10) 
  374. */ 
  375. public function per_page() { 
  376. global $wp_query; 
  377.  
  378. $per_page = isset( $wp_query->query_vars['number'] ) ? $wp_query->query_vars['number'] : 10; 
  379.  
  380. if( $per_page < 0 && $this->get_query_mode() == 'customers' ) 
  381. $per_page = 99999999; // Customers query doesn't support -1 
  382.  
  383. return apply_filters( 'edd_api_results_per_page', $per_page ); 
  384.  
  385. /** 
  386. * Retrieve the output format 
  387. * Determines whether results should be displayed in XML or JSON 
  388. * @since 1.5 
  389. * @return mixed|void 
  390. */ 
  391. public function get_output_format() { 
  392. global $wp_query; 
  393.  
  394. $format = isset( $wp_query->query_vars['format'] ) ? $wp_query->query_vars['format'] : 'json'; 
  395.  
  396. return apply_filters( 'edd_api_output_format', $format ); 
  397.  
  398. /** 
  399. * Sets up the dates used to retrieve earnings/sales 
  400. * @access public 
  401. * @since 1.5.1 
  402. * @param array $args Arguments to override defaults 
  403. * @return array $dates 
  404. */ 
  405. public function get_dates( $args = array() ) { 
  406. $dates = array(); 
  407.  
  408. $defaults = array( 
  409. 'type' => '',  
  410. 'product' => null,  
  411. 'date' => null,  
  412. 'startdate' => null,  
  413. 'enddate' => null 
  414. ); 
  415.  
  416. $args = wp_parse_args( $args, $defaults ); 
  417.  
  418. $current_time = current_time( 'timestamp' ); 
  419.  
  420. if ( 'range' === $args['date'] ) { 
  421. $startdate = strtotime( $args['startdate'] ); 
  422. $enddate = strtotime( $args['enddate'] ); 
  423. $dates['day_start'] = date( 'd', $startdate ); 
  424. $dates['day_end'] = date( 'd', $enddate ); 
  425. $dates['m_start'] = date( 'n', $startdate ); 
  426. $dates['m_end'] = date( 'n', $enddate ); 
  427. $dates['year'] = date( 'Y', $startdate ); 
  428. $dates['year_end'] = date( 'Y', $enddate ); 
  429. } else { 
  430. // Modify dates based on predefined ranges 
  431. switch ( $args['date'] ) : 
  432.  
  433. case 'this_month' : 
  434. $dates['day'] = null; 
  435. $dates['m_start'] = date( 'n', $current_time ); 
  436. $dates['m_end'] = date( 'n', $current_time ); 
  437. $dates['year'] = date( 'Y', $current_time ); 
  438. break; 
  439.  
  440. case 'last_month' : 
  441. $dates['day'] = null; 
  442. $dates['m_start'] = date( 'n', $current_time ) == 1 ? 12 : date( 'n', $current_time ) - 1; 
  443. $dates['m_end'] = $dates['m_start']; 
  444. $dates['year'] = date( 'n', $current_time ) == 1 ? date( 'Y', $current_time ) - 1 : date( 'Y', $current_time ); 
  445. break; 
  446.  
  447. case 'today' : 
  448. $dates['day'] = date( 'd', $current_time ); 
  449. $dates['m_start'] = date( 'n', $current_time ); 
  450. $dates['m_end'] = date( 'n', $current_time ); 
  451. $dates['year'] = date( 'Y', $current_time ); 
  452. break; 
  453.  
  454. case 'yesterday' : 
  455.  
  456. $year = date( 'Y', $current_time ); 
  457. $month = date( 'n', $current_time ); 
  458. $day = date( 'd', $current_time ); 
  459.  
  460. if ( $month == 1 && $day == 1 ) { 
  461.  
  462. $year -= 1; 
  463. $month = 12; 
  464. $day = cal_days_in_month( CAL_GREGORIAN, $month, $year ); 
  465.  
  466. } elseif ( $month > 1 && $day == 1 ) { 
  467.  
  468. $month -= 1; 
  469. $day = cal_days_in_month( CAL_GREGORIAN, $month, $year ); 
  470.  
  471. } else { 
  472.  
  473. $day -= 1; 
  474.  
  475.  
  476. $dates['day'] = $day; 
  477. $dates['m_start'] = $month; 
  478. $dates['m_end'] = $month; 
  479. $dates['year'] = $year; 
  480.  
  481. break; 
  482.  
  483. case 'this_quarter' : 
  484. $month_now = date( 'n', $current_time ); 
  485.  
  486. $dates['day'] = null; 
  487.  
  488. if ( $month_now <= 3 ) { 
  489.  
  490. $dates['m_start'] = 1; 
  491. $dates['m_end'] = 3; 
  492. $dates['year'] = date( 'Y', $current_time ); 
  493.  
  494. } else if ( $month_now <= 6 ) { 
  495.  
  496. $dates['m_start'] = 4; 
  497. $dates['m_end'] = 6; 
  498. $dates['year'] = date( 'Y', $current_time ); 
  499.  
  500. } else if ( $month_now <= 9 ) { 
  501.  
  502. $dates['m_start'] = 7; 
  503. $dates['m_end'] = 9; 
  504. $dates['year'] = date( 'Y', $current_time ); 
  505.  
  506. } else { 
  507.  
  508. $dates['m_start'] = 10; 
  509. $dates['m_end'] = 12; 
  510. $dates['year'] = date( 'Y', $current_time ); 
  511.  
  512. break; 
  513.  
  514. case 'last_quarter' : 
  515. $month_now = date( 'n', $current_time ); 
  516.  
  517. $dates['day'] = null; 
  518.  
  519. if ( $month_now <= 3 ) { 
  520.  
  521. $dates['m_start'] = 10; 
  522. $dates['m_end'] = 12; 
  523. $dates['year'] = date( 'Y', $current_time ) - 1; // Previous year 
  524.  
  525. } else if ( $month_now <= 6 ) { 
  526.  
  527. $dates['m_start'] = 1; 
  528. $dates['m_end'] = 3; 
  529. $dates['year'] = date( 'Y', $current_time ); 
  530.  
  531. } else if ( $month_now <= 9 ) { 
  532.  
  533. $dates['m_start'] = 4; 
  534. $dates['m_end'] = 6; 
  535. $dates['year'] = date( 'Y', $current_time ); 
  536.  
  537. } else { 
  538.  
  539. $dates['m_start'] = 7; 
  540. $dates['m_end'] = 9; 
  541. $dates['year'] = date( 'Y', $current_time ); 
  542.  
  543. break; 
  544.  
  545. case 'this_year' : 
  546. $dates['day'] = null; 
  547. $dates['m_start'] = null; 
  548. $dates['m_end'] = null; 
  549. $dates['year'] = date( 'Y', $current_time ); 
  550. break; 
  551.  
  552. case 'last_year' : 
  553. $dates['day'] = null; 
  554. $dates['m_start'] = null; 
  555. $dates['m_end'] = null; 
  556. $dates['year'] = date( 'Y', $current_time ) - 1; 
  557. break; 
  558.  
  559. endswitch; 
  560.  
  561. /** 
  562. * Returns the filters for the dates used to retreive earnings/sales 
  563. * @since 1.5.1 
  564. * @param object $dates The dates used for retreiving earnings/sales 
  565. */ 
  566.  
  567. return apply_filters( 'edd_api_stat_dates', $dates ); 
  568.  
  569. /** 
  570. * Process Get Customers API Request 
  571. * @access public 
  572. * @since 1.5 
  573. * @author Daniel J Griffiths 
  574. * @global object $wpdb Used to query the database using the WordPress 
  575. * Database API 
  576. * @param int $customer Customer ID 
  577. * @return array $customers Multidimensional array of the customers 
  578. */ 
  579. public function get_customers( $customer = null ) { 
  580.  
  581. $customers = array(); 
  582. $error = array(); 
  583. if( ! user_can( $this->user_id, 'view_shop_sensitive_data' ) && ! $this->override ) { 
  584. return $customers; 
  585.  
  586. global $wpdb; 
  587.  
  588. $paged = $this->get_paged(); 
  589. $per_page = $this->per_page(); 
  590. $offset = $per_page * ( $paged - 1 ); 
  591.  
  592. if( is_numeric( $customer ) ) { 
  593. $field = 'id'; 
  594. } else { 
  595. $field = 'email'; 
  596.  
  597. $customer_query = EDD()->customers->get_customers( array( 'number' => $per_page, 'offset' => $offset, $field => $customer ) ); 
  598. $customer_count = 0; 
  599.  
  600. if( $customer_query ) { 
  601.  
  602. foreach ( $customer_query as $customer_obj ) { 
  603.  
  604. $names = explode( ' ', $customer_obj->name ); 
  605. $first_name = ! empty( $names[0] ) ? $names[0] : ''; 
  606. $last_name = ''; 
  607. if( ! empty( $names[1] ) ) { 
  608. unset( $names[0] ); 
  609. $last_name = implode( ' ', $names ); 
  610.  
  611. $customers['customers'][$customer_count]['info']['id'] = ''; 
  612. $customers['customers'][$customer_count]['info']['user_id'] = ''; 
  613. $customers['customers'][$customer_count]['info']['username'] = ''; 
  614. $customers['customers'][$customer_count]['info']['display_name'] = ''; 
  615. $customers['customers'][$customer_count]['info']['customer_id'] = $customer_obj->id; 
  616. $customers['customers'][$customer_count]['info']['first_name'] = $first_name; 
  617. $customers['customers'][$customer_count]['info']['last_name'] = $last_name; 
  618. $customers['customers'][$customer_count]['info']['email'] = $customer_obj->email; 
  619.  
  620. if ( ! empty( $customer_obj->user_id ) ) { 
  621.  
  622. $user_data = get_userdata( $customer_obj->user_id ); 
  623.  
  624. // Customer with registered account 
  625.  
  626. // id is going to get deprecated in the future, user user_id or customer_id instead 
  627. $customers['customers'][$customer_count]['info']['id'] = $customer_obj->user_id; 
  628. $customers['customers'][$customer_count]['info']['user_id'] = $customer_obj->user_id; 
  629. $customers['customers'][$customer_count]['info']['username'] = $user_data->user_login; 
  630. $customers['customers'][$customer_count]['info']['display_name'] = $user_data->display_name; 
  631.  
  632.  
  633. $customers['customers'][$customer_count]['stats']['total_purchases'] = $customer_obj->purchase_count; 
  634. $customers['customers'][$customer_count]['stats']['total_spent'] = $customer_obj->purchase_value; 
  635. $customers['customers'][$customer_count]['stats']['total_downloads'] = edd_count_file_downloads_of_user( $customer_obj->email ); 
  636.  
  637. $customer_count++; 
  638.  
  639.  
  640. } elseif( $customer ) { 
  641.  
  642. $error['error'] = sprintf( __( 'Customer %s not found!', 'edd' ), $customer ); 
  643. return $error; 
  644.  
  645. } else { 
  646.  
  647. $error['error'] = __( 'No customers found!', 'edd' ); 
  648. return $error; 
  649.  
  650.  
  651. return $customers; 
  652.  
  653. /** 
  654. * Process Get Products API Request 
  655. * @access public 
  656. * @author Daniel J Griffiths 
  657. * @since 1.5 
  658. * @param int $product Product (Download) ID 
  659. * @return array $customers Multidimensional array of the products 
  660. */ 
  661. public function get_products( $product = null ) { 
  662.  
  663. $products = array(); 
  664. $error = array(); 
  665.  
  666. if ( $product == null ) { 
  667. $products['products'] = array(); 
  668.  
  669. $product_list = get_posts( array( 
  670. 'post_type' => 'download',  
  671. 'posts_per_page' => $this->per_page(),  
  672. 'suppress_filters' => true,  
  673. 'paged' => $this->get_paged() 
  674. ) ); 
  675.  
  676. if ( $product_list ) { 
  677. $i = 0; 
  678. foreach ( $product_list as $product_info ) { 
  679. $products['products'][$i] = $this->get_product_data( $product_info ); 
  680. $i++; 
  681. } else { 
  682. if ( get_post_type( $product ) == 'download' ) { 
  683. $product_info = get_post( $product ); 
  684.  
  685. $products['products'][0] = $this->get_product_data( $product_info ); 
  686.  
  687. } else { 
  688. $error['error'] = sprintf( __( 'Product %s not found!', 'edd' ), $product ); 
  689. return $error; 
  690.  
  691. return $products; 
  692.  
  693. /** 
  694. * Given a download post object, generate the data for the API output 
  695. * @since 2.3.9 
  696. * @param object $product_info The Download Post Object 
  697. * @return array Array of post data to return back in the API 
  698. */ 
  699. private function get_product_data( $product_info ) { 
  700.  
  701. $product = array(); 
  702.  
  703. $product['info']['id'] = $product_info->ID; 
  704. $product['info']['slug'] = $product_info->post_name; 
  705. $product['info']['title'] = $product_info->post_title; 
  706. $product['info']['create_date'] = $product_info->post_date; 
  707. $product['info']['modified_date'] = $product_info->post_modified; 
  708. $product['info']['status'] = $product_info->post_status; 
  709. $product['info']['link'] = html_entity_decode( $product_info->guid ); 
  710. $product['info']['content'] = $product_info->post_content; 
  711. $product['info']['thumbnail'] = wp_get_attachment_url( get_post_thumbnail_id( $product_info->ID ) ); 
  712. $product['info']['category'] = get_the_terms( $product_info, 'download_category' ); 
  713. $product['info']['tags'] = get_the_terms( $product_info, 'download_tag' ); 
  714.  
  715. if( user_can( $this->user_id, 'view_shop_reports' ) || $this->override ) { 
  716. $product['stats']['total']['sales'] = edd_get_download_sales_stats( $product_info->ID ); 
  717. $product['stats']['total']['earnings'] = edd_get_download_earnings_stats( $product_info->ID ); 
  718. $product['stats']['monthly_average']['sales'] = edd_get_average_monthly_download_sales( $product_info->ID ); 
  719. $product['stats']['monthly_average']['earnings'] = edd_get_average_monthly_download_earnings( $product_info->ID ); 
  720.  
  721. if ( edd_has_variable_prices( $product_info->ID ) ) { 
  722. foreach ( edd_get_variable_prices( $product_info->ID ) as $price ) { 
  723. $product['pricing'][ sanitize_key( $price['name'] ) ] = $price['amount']; 
  724. } else { 
  725. $product['pricing']['amount'] = edd_get_download_price( $product_info->ID ); 
  726.  
  727. if( user_can( $this->user_id, 'view_shop_sensitive_data' ) || $this->override ) { 
  728. foreach ( edd_get_download_files( $product_info->ID ) as $file ) { 
  729. $product['files'][] = $file; 
  730. $product['notes'] = edd_get_product_notes( $product_info->ID ); 
  731.  
  732. return apply_filters( 'edd_api_products_product', $product ); 
  733.  
  734.  
  735. /** 
  736. * Process Get Stats API Request 
  737. * @author Daniel J Griffiths 
  738. * @since 1.5 
  739. * @global object $wpdb Used to query the database using the WordPress 
  740. * @param array $args Arguments provided by API Request 
  741. * @return array 
  742. */ 
  743. public function get_stats( $args = array() ) { 
  744. $defaults = array( 
  745. 'type' => null,  
  746. 'product' => null,  
  747. 'date' => null,  
  748. 'startdate' => null,  
  749. 'enddate' => null 
  750. ); 
  751.  
  752. $args = wp_parse_args( $args, $defaults ); 
  753.  
  754. $dates = $this->get_dates( $args ); 
  755.  
  756. $stats = array(); 
  757. $earnings = array( 
  758. 'earnings' => array() 
  759. ); 
  760. $sales = array( 
  761. 'sales' => array() 
  762. ); 
  763. $error = array(); 
  764.  
  765. if( ! user_can( $this->user_id, 'view_shop_reports' ) && ! $this->override ) { 
  766. return $stats; 
  767.  
  768. if ( $args['type'] == 'sales' ) { 
  769. if ( $args['product'] == null ) { 
  770. if ( $args['date'] == null ) { 
  771. $sales = $this->get_default_sales_stats(); 
  772. } elseif( $args['date'] === 'range' ) { 
  773. // Return sales for a date range 
  774.  
  775. // Ensure the end date is later than the start date 
  776. if( $args['enddate'] < $args['startdate'] ) { 
  777. $error['error'] = __( 'The end date must be later than the start date!', 'edd' ); 
  778.  
  779. // Ensure both the start and end date are specified 
  780. if ( empty( $args['startdate'] ) || empty( $args['enddate'] ) ) { 
  781. $error['error'] = __( 'Invalid or no date range specified!', 'edd' ); 
  782.  
  783. $total = 0; 
  784.  
  785. // Loop through the years 
  786. $y = $dates['year']; 
  787. while( $y <= $dates['year_end'] ) : 
  788.  
  789. if( $dates['year'] == $dates['year_end'] ) { 
  790. $month_start = $dates['m_start']; 
  791. $month_end = $dates['m_end']; 
  792. } elseif( $y == $dates['year'] && $dates['year_end'] > $dates['year'] ) { 
  793. $month_start = $dates['m_start']; 
  794. $month_end = 12; 
  795. } elseif( $y == $dates['year_end'] ) { 
  796. $month_start = 1; 
  797. $month_end = $dates['m_end']; 
  798. } else { 
  799. $month_start = 1; 
  800. $month_end = 12; 
  801.  
  802. $i = $month_start; 
  803. while ( $i <= $month_end ) : 
  804.  
  805. if( $i == $dates['m_start'] ) { 
  806. $d = $dates['day_start']; 
  807. } else { 
  808. $d = 1; 
  809.  
  810. if( $i == $dates['m_end'] ) { 
  811. $num_of_days = $dates['day_end']; 
  812. } else { 
  813. $num_of_days = cal_days_in_month( CAL_GREGORIAN, $i, $y ); 
  814.  
  815. while ( $d <= $num_of_days ) : 
  816. $sale_count = edd_get_sales_by_date( $d, $i, $y ); 
  817. $date_key = date( 'Ymd', strtotime( $y . '/' . $i . '/' . $d ) ); 
  818. if ( ! isset( $sales['sales'][ $date_key ] ) ) { 
  819. $sales['sales'][ $date_key ] = 0; 
  820. $sales['sales'][ $date_key ] += $sale_count; 
  821. $total += $sale_count; 
  822. $d++; 
  823. endwhile; 
  824. $i++; 
  825. endwhile; 
  826.  
  827. $y++; 
  828. endwhile; 
  829.  
  830. $sales['totals'] = $total; 
  831. } else { 
  832. if( $args['date'] == 'this_quarter' || $args['date'] == 'last_quarter' ) { 
  833. $sales_count = 0; 
  834.  
  835. // Loop through the months 
  836. $month = $dates['m_start']; 
  837.  
  838. while( $month <= $dates['m_end'] ) : 
  839. $sales_count += edd_get_sales_by_date( null, $month, $dates['year'] ); 
  840. $month++; 
  841. endwhile; 
  842.  
  843. $sales['sales'][ $args['date'] ] = $sales_count; 
  844. } else { 
  845. $sales['sales'][ $args['date'] ] = edd_get_sales_by_date( $dates['day'], $dates['m_start'], $dates['year'] ); 
  846. } elseif ( $args['product'] == 'all' ) { 
  847. $products = get_posts( array( 'post_type' => 'download', 'nopaging' => true ) ); 
  848. $i = 0; 
  849. foreach ( $products as $product_info ) { 
  850. $sales['sales'][$i] = array( $product_info->post_name => edd_get_download_sales_stats( $product_info->ID ) ); 
  851. $i++; 
  852. } else { 
  853. if ( get_post_type( $args['product'] ) == 'download' ) { 
  854. $product_info = get_post( $args['product'] ); 
  855. $sales['sales'][0] = array( $product_info->post_name => edd_get_download_sales_stats( $args['product'] ) ); 
  856. } else { 
  857. $error['error'] = sprintf( __( 'Product %s not found!', 'edd' ), $args['product'] ); 
  858.  
  859. if ( ! empty( $error ) ) 
  860. return $error; 
  861.  
  862. return $sales; 
  863. } elseif ( $args['type'] == 'earnings' ) { 
  864. if ( $args['product'] == null ) { 
  865. if ( $args['date'] == null ) { 
  866. $earnings = $this->get_default_earnings_stats(); 
  867. } elseif ( $args['date'] === 'range' ) { 
  868. // Return sales for a date range 
  869.  
  870. // Ensure the end date is later than the start date 
  871. if ( $args['enddate'] < $args['startdate'] ) { 
  872. $error['error'] = __( 'The end date must be later than the start date!', 'edd' ); 
  873.  
  874. // Ensure both the start and end date are specified 
  875. if ( empty( $args['startdate'] ) || empty( $args['enddate'] ) ) { 
  876. $error['error'] = __( 'Invalid or no date range specified!', 'edd' ); 
  877.  
  878. $total = (float) 0.00; 
  879.  
  880. // Loop through the years 
  881. $y = $dates['year']; 
  882. if ( ! isset( $earnings['earnings'] ) ) { 
  883. $earnings['earnings'] = array(); 
  884. while( $y <= $dates['year_end'] ) : 
  885.  
  886. if( $dates['year'] == $dates['year_end'] ) { 
  887. $month_start = $dates['m_start']; 
  888. $month_end = $dates['m_end']; 
  889. } elseif( $y == $dates['year'] && $dates['year_end'] > $dates['year'] ) { 
  890. $month_start = $dates['m_start']; 
  891. $month_end = 12; 
  892. } elseif( $y == $dates['year_end'] ) { 
  893. $month_start = 1; 
  894. $month_end = $dates['m_end']; 
  895. } else { 
  896. $month_start = 1; 
  897. $month_end = 12; 
  898.  
  899. $i = $month_start; 
  900. while ( $i <= $month_end ) : 
  901.  
  902. if( $i == $dates['m_start'] ) 
  903. $d = $dates['day_start']; 
  904. else 
  905. $d = 1; 
  906.  
  907. if( $i == $dates['m_end'] ) { 
  908. $num_of_days = $dates['day_end']; 
  909. } else { 
  910. $num_of_days = cal_days_in_month( CAL_GREGORIAN, $i, $y ); 
  911.  
  912. while ( $d <= $num_of_days ) : 
  913. $earnings_stat = edd_get_earnings_by_date( $d, $i, $y ); 
  914. $date_key = date( 'Ymd', strtotime( $y . '/' . $i . '/' . $d ) ); 
  915. if ( ! isset( $earnings['earnings'][ $date_key ] ) ) { 
  916. $earnings['earnings'][ $date_key ] = 0; 
  917. $earnings['earnings'][ $date_key ] += $earnings_stat; 
  918. $total += $earnings_stat; 
  919. $d++; 
  920. endwhile; 
  921.  
  922. $i++; 
  923. endwhile; 
  924.  
  925. $y++; 
  926. endwhile; 
  927.  
  928. $earnings['totals'] = $total; 
  929. } else { 
  930. if ( $args['date'] == 'this_quarter' || $args['date'] == 'last_quarter' ) { 
  931. $earnings_count = (float) 0.00; 
  932.  
  933. // Loop through the months 
  934. $month = $dates['m_start']; 
  935.  
  936. while ( $month <= $dates['m_end'] ) : 
  937. $earnings_count += edd_get_earnings_by_date( null, $month, $dates['year'] ); 
  938. $month++; 
  939. endwhile; 
  940.  
  941. $earnings['earnings'][ $args['date'] ] = $earnings_count; 
  942. } else { 
  943. $earnings['earnings'][ $args['date'] ] = edd_get_earnings_by_date( $dates['day'], $dates['m_start'], $dates['year'] ); 
  944. } elseif ( $args['product'] == 'all' ) { 
  945. $products = get_posts( array( 'post_type' => 'download', 'nopaging' => true ) ); 
  946.  
  947. $i = 0; 
  948. foreach ( $products as $product_info ) { 
  949. $earnings['earnings'][ $i ] = array( $product_info->post_name => edd_get_download_earnings_stats( $product_info->ID ) ); 
  950. $i++; 
  951. } else { 
  952. if ( get_post_type( $args['product'] ) == 'download' ) { 
  953. $product_info = get_post( $args['product'] ); 
  954. $earnings['earnings'][0] = array( $product_info->post_name => edd_get_download_earnings_stats( $args['product'] ) ); 
  955. } else { 
  956. $error['error'] = sprintf( __( 'Product %s not found!', 'edd' ), $args['product'] ); 
  957.  
  958. if ( ! empty( $error ) ) 
  959. return $error; 
  960.  
  961. return $earnings; 
  962. } elseif ( $args['type'] == 'customers' ) { 
  963. global $wpdb; 
  964.  
  965. $stats = array(); 
  966.  
  967. $count = $wpdb->get_col( "SELECT COUNT(DISTINCT meta_value) FROM $wpdb->postmeta WHERE meta_key = '_edd_payment_user_email'" ); 
  968.  
  969. $stats['customers']['total_customers'] = $count[0]; 
  970.  
  971. return $stats; 
  972. } elseif ( empty( $args['type'] ) ) { 
  973. $stats = array_merge( $stats, $this->get_default_sales_stats() ); 
  974. $stats = array_merge ( $stats, $this->get_default_earnings_stats() ); 
  975.  
  976. return array( 'stats' => $stats ); 
  977.  
  978. /** 
  979. * Retrieves Recent Sales 
  980. * @access public 
  981. * @since 1.5 
  982. * @return array 
  983. */ 
  984. public function get_recent_sales() { 
  985. global $wp_query; 
  986.  
  987. $sales = array(); 
  988.  
  989. if( ! user_can( $this->user_id, 'view_shop_reports' ) && ! $this->override ) { 
  990. return $sales; 
  991.  
  992. if( isset( $wp_query->query_vars['id'] ) ) { 
  993. $query = array(); 
  994. $query[] = edd_get_payment_by( 'id', $wp_query->query_vars['id'] ); 
  995. } elseif( isset( $wp_query->query_vars['purchasekey'] ) ) { 
  996. $query = array(); 
  997. $query[] = edd_get_payment_by( 'key', $wp_query->query_vars['purchasekey'] ); 
  998. } elseif( isset( $wp_query->query_vars['email'] ) ) { 
  999. $query = edd_get_payments( array( 'meta_key' => '_edd_payment_user_email', 'meta_value' => $wp_query->query_vars['email'], 'number' => $this->per_page(), 'page' => $this->get_paged(), 'status' => 'publish' ) ); 
  1000. } else { 
  1001. $query = edd_get_payments( array( 'number' => $this->per_page(), 'page' => $this->get_paged(), 'status' => 'publish' ) ); 
  1002.  
  1003. if ( $query ) { 
  1004. $i = 0; 
  1005. foreach ( $query as $payment ) { 
  1006. $payment_meta = edd_get_payment_meta( $payment->ID ); 
  1007. $user_info = edd_get_payment_meta_user_info( $payment->ID ); 
  1008. $cart_items = edd_get_payment_meta_cart_details( $payment->ID ); 
  1009.  
  1010. $sales['sales'][ $i ]['ID'] = edd_get_payment_number( $payment->ID ); 
  1011. $sales['sales'][ $i ]['transaction_id'] = edd_get_payment_transaction_id( $payment->ID ); 
  1012. $sales['sales'][ $i ]['key'] = edd_get_payment_key( $payment->ID ); 
  1013. $sales['sales'][ $i ]['discount'] = isset( $user_info['discount'] ) && $user_info['discount'] != 'none' ? explode( ', ', $user_info['discount'] ) : array(); 
  1014. $sales['sales'][ $i ]['subtotal'] = edd_get_payment_subtotal( $payment->ID ); 
  1015. $sales['sales'][ $i ]['tax'] = edd_get_payment_tax( $payment->ID ); 
  1016. $sales['sales'][ $i ]['fees'] = edd_get_payment_fees( $payment->ID ); 
  1017. $sales['sales'][ $i ]['total'] = edd_get_payment_amount( $payment->ID ); 
  1018. $sales['sales'][ $i ]['gateway'] = edd_get_payment_gateway( $payment->ID ); 
  1019. $sales['sales'][ $i ]['email'] = edd_get_payment_user_email( $payment->ID ); 
  1020. $sales['sales'][ $i ]['date'] = $payment->post_date; 
  1021. $sales['sales'][ $i ]['products'] = array(); 
  1022.  
  1023. $c = 0; 
  1024.  
  1025. foreach ( $cart_items as $key => $item ) { 
  1026.  
  1027. $item_id = isset( $item['id'] ) ? $item['id'] : $item; 
  1028. $price = isset( $item['price'] ) ? $item['price'] : false; 
  1029. $price_id = isset( $item['item_number']['options']['price_id'] ) ? $item['item_number']['options']['price_id'] : null; 
  1030. $quantity = isset( $item['quantity'] ) && $item['quantity'] > 0 ? $item['quantity'] : 1; 
  1031.  
  1032. if( ! $price ) { 
  1033. // This function is only used on payments with near 1.0 cart data structure 
  1034. $price = edd_get_download_final_price( $item_id, $user_info, null ); 
  1035.  
  1036. $price_name = ''; 
  1037. if ( isset( $item['item_number'] ) && isset( $item['item_number']['options'] ) ) { 
  1038. $price_options = $item['item_number']['options']; 
  1039. if ( isset( $price_options['price_id'] ) ) { 
  1040. $price_name = edd_get_price_option_name( $item['id'], $price_options['price_id'], $payment->ID ); 
  1041.  
  1042. $sales['sales'][ $i ]['products'][ $c ]['quantity'] = $quantity; 
  1043. $sales['sales'][ $i ]['products'][ $c ]['name'] = get_the_title( $item['id'] ); 
  1044. $sales['sales'][ $i ]['products'][ $c ]['price'] = $price; 
  1045. $sales['sales'][ $i ]['products'][ $c ]['price_name'] = $price_name; 
  1046. $c++; 
  1047.  
  1048. $i++; 
  1049. return $sales; 
  1050.  
  1051. /** 
  1052. * Process Get Discounts API Request 
  1053. * @access public 
  1054. * @since 1.6 
  1055. * @global object $wpdb Used to query the database using the WordPress 
  1056. * Database API 
  1057. * @param int $discount Discount ID 
  1058. * @return array $discounts Multidimensional array of the discounts 
  1059. */ 
  1060. public function get_discounts( $discount = null ) { 
  1061.  
  1062. $discount_list = array(); 
  1063.  
  1064. if( ! user_can( $this->user_id, 'manage_shop_discounts' ) && ! $this->override ) { 
  1065. return $discount_list; 
  1066. $error = array(); 
  1067.  
  1068. if ( empty( $discount ) ) { 
  1069.  
  1070. global $wpdb; 
  1071.  
  1072. $paged = $this->get_paged(); 
  1073. $per_page = $this->per_page(); 
  1074. $discounts = edd_get_discounts( array( 'posts_per_page' => $per_page, 'paged' => $paged ) ); 
  1075. $count = 0; 
  1076.  
  1077. if ( empty( $discounts ) ) { 
  1078. $error['error'] = __( 'No discounts found!', 'edd' ); 
  1079. return $error; 
  1080.  
  1081. foreach ( $discounts as $discount ) { 
  1082.  
  1083. $discount_list['discounts'][$count]['ID'] = $discount->ID; 
  1084. $discount_list['discounts'][$count]['name'] = $discount->post_title; 
  1085. $discount_list['discounts'][$count]['code'] = edd_get_discount_code( $discount->ID ); 
  1086. $discount_list['discounts'][$count]['amount'] = edd_get_discount_amount( $discount->ID ); 
  1087. $discount_list['discounts'][$count]['min_price'] = edd_get_discount_min_price( $discount->ID ); 
  1088. $discount_list['discounts'][$count]['type'] = edd_get_discount_type( $discount->ID ); 
  1089. $discount_list['discounts'][$count]['uses'] = edd_get_discount_uses( $discount->ID ); 
  1090. $discount_list['discounts'][$count]['max_uses'] = edd_get_discount_max_uses( $discount->ID ); 
  1091. $discount_list['discounts'][$count]['start_date'] = edd_get_discount_start_date( $discount->ID ); 
  1092. $discount_list['discounts'][$count]['exp_date'] = edd_get_discount_expiration( $discount->ID ); 
  1093. $discount_list['discounts'][$count]['status'] = $discount->post_status; 
  1094. $discount_list['discounts'][$count]['product_requirements'] = edd_get_discount_product_reqs( $discount->ID ); 
  1095. $discount_list['discounts'][$count]['requirement_condition'] = edd_get_discount_product_condition( $discount->ID ); 
  1096. $discount_list['discounts'][$count]['global_discount'] = edd_is_discount_not_global( $discount->ID ); 
  1097. $discount_list['discounts'][$count]['single_use'] = edd_discount_is_single_use( $discount->ID ); 
  1098.  
  1099. $count++; 
  1100.  
  1101. } else { 
  1102.  
  1103. if ( is_numeric( $discount ) && get_post( $discount ) ) { 
  1104.  
  1105. $discount_list['discounts'][0]['ID'] = $discount; 
  1106. $discount_list['discounts'][0]['name'] = get_post_field( 'post_title', $discount ); 
  1107. $discount_list['discounts'][0]['code'] = edd_get_discount_code( $discount ); 
  1108. $discount_list['discounts'][0]['amount'] = edd_get_discount_amount( $discount ); 
  1109. $discount_list['discounts'][0]['min_price'] = edd_get_discount_min_price( $discount ); 
  1110. $discount_list['discounts'][0]['type'] = edd_get_discount_type( $discount ); 
  1111. $discount_list['discounts'][0]['uses'] = edd_get_discount_uses( $discount ); 
  1112. $discount_list['discounts'][0]['max_uses'] = edd_get_discount_max_uses( $discount ); 
  1113. $discount_list['discounts'][0]['start_date'] = edd_get_discount_start_date( $discount ); 
  1114. $discount_list['discounts'][0]['exp_date'] = edd_get_discount_expiration( $discount ); 
  1115. $discount_list['discounts'][0]['status'] = get_post_field( 'post_status', $discount ); 
  1116. $discount_list['discounts'][0]['product_requirements'] = edd_get_discount_product_reqs( $discount ); 
  1117. $discount_list['discounts'][0]['requirement_condition'] = edd_get_discount_product_condition( $discount ); 
  1118. $discount_list['discounts'][0]['global_discount'] = edd_is_discount_not_global( $discount ); 
  1119. $discount_list['discounts'][0]['single_use'] = edd_discount_is_single_use( $discount ); 
  1120.  
  1121. } else { 
  1122.  
  1123. $error['error'] = sprintf( __( 'Discount %s not found!', 'edd' ), $discount ); 
  1124. return $error; 
  1125.  
  1126.  
  1127.  
  1128. return $discount_list; 
  1129.  
  1130.  
  1131. /** 
  1132. * Log each API request, if enabled 
  1133. * @access private 
  1134. * @since 1.5 
  1135. * @global $edd_logs 
  1136. * @global $wp_query 
  1137. * @param array $data 
  1138. * @return void 
  1139. */ 
  1140. private function log_request( $data = array() ) { 
  1141. if ( ! $this->log_requests ) 
  1142. return; 
  1143.  
  1144. global $edd_logs, $wp_query; 
  1145.  
  1146. $query = array( 
  1147. 'edd-api' => $wp_query->query_vars['edd-api'],  
  1148. 'key' => $wp_query->query_vars['key'],  
  1149. 'token' => $wp_query->query_vars['token'],  
  1150. 'query' => isset( $wp_query->query_vars['query'] ) ? $wp_query->query_vars['query'] : null,  
  1151. 'type' => isset( $wp_query->query_vars['type'] ) ? $wp_query->query_vars['type'] : null,  
  1152. 'product' => isset( $wp_query->query_vars['product'] ) ? $wp_query->query_vars['product'] : null,  
  1153. 'customer' => isset( $wp_query->query_vars['customer'] ) ? $wp_query->query_vars['customer'] : null,  
  1154. 'date' => isset( $wp_query->query_vars['date'] ) ? $wp_query->query_vars['date'] : null,  
  1155. 'startdate' => isset( $wp_query->query_vars['startdate'] ) ? $wp_query->query_vars['startdate'] : null,  
  1156. 'enddate' => isset( $wp_query->query_vars['enddate'] ) ? $wp_query->query_vars['enddate'] : null,  
  1157. 'id' => isset( $wp_query->query_vars['id'] ) ? $wp_query->query_vars['id'] : null,  
  1158. 'purchasekey' => isset( $wp_query->query_vars['purchasekey'] ) ? $wp_query->query_vars['purchasekey'] : null,  
  1159. 'email' => isset( $wp_query->query_vars['email'] ) ? $wp_query->query_vars['email'] : null,  
  1160. ); 
  1161.  
  1162. $log_data = array( 
  1163. 'log_type' => 'api_request',  
  1164. 'post_excerpt' => http_build_query( $query ),  
  1165. 'post_content' => ! empty( $data['error'] ) ? $data['error'] : '',  
  1166. ); 
  1167.  
  1168. $log_meta = array( 
  1169. 'request_ip' => edd_get_ip(),  
  1170. 'user' => $this->user_id,  
  1171. 'key' => $wp_query->query_vars['key'],  
  1172. 'token' => $wp_query->query_vars['token'] 
  1173. ); 
  1174.  
  1175. $edd_logs->insert_log( $log_data, $log_meta ); 
  1176.  
  1177.  
  1178. /** 
  1179. * Retrieve the output data 
  1180. * @access public 
  1181. * @since 1.5.2 
  1182. * @return array 
  1183. */ 
  1184. public function get_output() { 
  1185. return $this->data; 
  1186.  
  1187. /** 
  1188. * Output Query in either JSON/XML. The query data is outputted as JSON 
  1189. * by default 
  1190. * @author Daniel J Griffiths 
  1191. * @since 1.5 
  1192. * @global $wp_query 
  1193. * @param int $status_code 
  1194. */ 
  1195. public function output( $status_code = 200 ) { 
  1196. global $wp_query; 
  1197.  
  1198. $format = $this->get_output_format(); 
  1199.  
  1200. status_header( $status_code ); 
  1201.  
  1202. do_action( 'edd_api_output_before', $this->data, $this, $format ); 
  1203.  
  1204. switch ( $format ) : 
  1205.  
  1206. case 'xml' : 
  1207.  
  1208. require_once EDD_PLUGIN_DIR . 'includes/libraries/array2xml.php'; 
  1209. $xml = Array2XML::createXML( 'edd', $this->data ); 
  1210. echo $xml->saveXML(); 
  1211.  
  1212. break; 
  1213.  
  1214. case 'json' : 
  1215.  
  1216. header( 'Content-Type: application/json' ); 
  1217. if ( ! empty( $this->pretty_print ) ) 
  1218. echo json_encode( $this->data, $this->pretty_print ); 
  1219. else 
  1220. echo json_encode( $this->data ); 
  1221.  
  1222. break; 
  1223.  
  1224.  
  1225. default : 
  1226.  
  1227. // Allow other formats to be added via extensions 
  1228. do_action( 'edd_api_output_' . $format, $this->data, $this ); 
  1229.  
  1230. break; 
  1231.  
  1232. endswitch; 
  1233.  
  1234. do_action( 'edd_api_output_after', $this->data, $this, $format ); 
  1235.  
  1236. edd_die(); 
  1237.  
  1238. /** 
  1239. * Modify User Profile 
  1240. * Modifies the output of profile.php to add key generation/revocation 
  1241. * @access public 
  1242. * @author Daniel J Griffiths 
  1243. * @since 1.5 
  1244. * @param object $user Current user info 
  1245. * @return void 
  1246. */ 
  1247. function user_key_field( $user ) { 
  1248. if ( ( edd_get_option( 'api_allow_user_keys', false ) || current_user_can( 'manage_shop_settings' ) ) && current_user_can( 'edit_user', $user->ID ) ) { 
  1249. $user = get_userdata( $user->ID ); 
  1250. ?> 
  1251. <table class="form-table"> 
  1252. <tbody> 
  1253. <tr> 
  1254. <th> 
  1255. <label for="edd_set_api_key"><?php _e( 'Easy Digital Downloads API Keys', 'edd' ); ?></label> 
  1256. </th> 
  1257. <td> 
  1258. <?php if ( empty( $user->edd_user_public_key ) ) { ?> 
  1259. <input name="edd_set_api_key" type="checkbox" id="edd_set_api_key" value="0" /> 
  1260. <span class="description"><?php _e( 'Generate API Key', 'edd' ); ?></span> 
  1261. <?php } else { ?> 
  1262. <strong><?php _e( 'Public key:', 'edd' ); ?> </strong><span id="publickey"><?php echo $user->edd_user_public_key; ?></span><br/> 
  1263. <strong><?php _e( 'Secret key:', 'edd' ); ?> </strong><span id="privatekey"><?php echo $user->edd_user_secret_key; ?></span><br/> 
  1264. <strong><?php _e( 'Token:', 'edd' ); ?> </strong><span id="token"><?php echo $this->get_token( $user->ID ); ?></span><br/> 
  1265. <input name="edd_set_api_key" type="checkbox" id="edd_set_api_key" value="0" /> 
  1266. <span class="description"><?php _e( 'Revoke API Keys', 'edd' ); ?></span> 
  1267. <?php } ?> 
  1268. </td> 
  1269. </tr> 
  1270. </tbody> 
  1271. </table> 
  1272. <?php } 
  1273.  
  1274. /** 
  1275. * Process an API key generation/revocation 
  1276. * @access public 
  1277. * @since 2.0.0 
  1278. * @param array $args 
  1279. * @return void 
  1280. */ 
  1281. public function process_api_key( $args ) { 
  1282.  
  1283. if( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'edd-api-nonce' ) ) { 
  1284.  
  1285. wp_die( __( 'Nonce verification failed', 'edd' ), __( 'Error', 'edd' ), array( 'response' => 403 ) ); 
  1286.  
  1287.  
  1288. if( is_numeric( $args['user_id'] ) ) { 
  1289. $user_id = isset( $args['user_id'] ) ? absint( $args['user_id'] ) : get_current_user_id(); 
  1290. } else { 
  1291. $userdata = get_user_by( 'login', $args['user_id'] ); 
  1292. $user_id = $userdata->ID; 
  1293. $process = isset( $args['edd_api_process'] ) ? strtolower( $args['edd_api_process'] ) : false; 
  1294.  
  1295. if( $user_id == get_current_user_id() && ! edd_get_option( 'allow_user_api_keys' ) && ! current_user_can( 'manage_shop_settings' ) ) { 
  1296. wp_die( sprintf( __( 'You do not have permission to %s API keys for this user', 'edd' ), $process ), __( 'Error', 'edd' ), array( 'response' => 403 ) ); 
  1297. } elseif( ! current_user_can( 'manage_shop_settings' ) ) { 
  1298. wp_die( sprintf( __( 'You do not have permission to %s API keys for this user', 'edd' ), $process ), __( 'Error', 'edd' ), array( 'response' => 403 ) ); 
  1299.  
  1300. switch( $process ) { 
  1301. case 'generate': 
  1302. if( $this->generate_api_key( $user_id ) ) { 
  1303. delete_transient( 'edd-total-api-keys' ); 
  1304. wp_redirect( add_query_arg( 'edd-message', 'api-key-generated', 'edit.php?post_type=download&page=edd-tools&tab=api_keys' ) ); exit(); 
  1305. } else { 
  1306. wp_redirect( add_query_arg( 'edd-message', 'api-key-failed', 'edit.php?post_type=download&page=edd-tools&tab=api_keys' ) ); exit(); 
  1307. break; 
  1308. case 'regenerate': 
  1309. $this->generate_api_key( $user_id, true ); 
  1310. delete_transient( 'edd-total-api-keys' ); 
  1311. wp_redirect( add_query_arg( 'edd-message', 'api-key-regenerated', 'edit.php?post_type=download&page=edd-tools&tab=api_keys' ) ); exit(); 
  1312. break; 
  1313. case 'revoke': 
  1314. $this->revoke_api_key( $user_id ); 
  1315. delete_transient( 'edd-total-api-keys' ); 
  1316. wp_redirect( add_query_arg( 'edd-message', 'api-key-revoked', 'edit.php?post_type=download&page=edd-tools&tab=api_keys' ) ); exit(); 
  1317. break; 
  1318. default; 
  1319. break; 
  1320.  
  1321. /** 
  1322. * Generate new API keys for a user 
  1323. * @access public 
  1324. * @since 2.0.0 
  1325. * @param int $user_id User ID the key is being generated for 
  1326. * @param boolean $regenerate Regenerate the key for the user 
  1327. * @return boolean True if (re)generated succesfully, false otherwise. 
  1328. */ 
  1329. public function generate_api_key( $user_id = 0, $regenerate = false ) { 
  1330.  
  1331. if( empty( $user_id ) ) { 
  1332. return false; 
  1333.  
  1334. $user = get_userdata( $user_id ); 
  1335.  
  1336. if( ! $user ) { 
  1337. return false; 
  1338.  
  1339. if ( empty( $user->edd_user_public_key ) ) { 
  1340. update_user_meta( $user_id, 'edd_user_public_key', $this->generate_public_key( $user->user_email ) ); 
  1341. update_user_meta( $user_id, 'edd_user_secret_key', $this->generate_private_key( $user->ID ) ); 
  1342. } elseif( $regenerate == true ) { 
  1343. $this->revoke_api_key( $user->ID ); 
  1344. update_user_meta( $user_id, 'edd_user_public_key', $this->generate_public_key( $user->user_email ) ); 
  1345. update_user_meta( $user_id, 'edd_user_secret_key', $this->generate_private_key( $user->ID ) ); 
  1346. } else { 
  1347. return false; 
  1348.  
  1349. return true; 
  1350.  
  1351. /** 
  1352. * Revoke a users API keys 
  1353. * @access public 
  1354. * @since 2.0.0 
  1355. * @param int $user_id User ID of user to revoke key for 
  1356. * @return string 
  1357. */ 
  1358. public function revoke_api_key( $user_id = 0 ) { 
  1359.  
  1360. if( empty( $user_id ) ) { 
  1361. return false; 
  1362.  
  1363. $user = get_userdata( $user_id ); 
  1364.  
  1365. if( ! $user ) { 
  1366. return false; 
  1367.  
  1368. if ( ! empty( $user->edd_user_public_key ) ) { 
  1369. delete_transient( md5( 'edd_api_user_' . $user->edd_user_public_key ) ); 
  1370. delete_user_meta( $user_id, 'edd_user_public_key' ); 
  1371. delete_user_meta( $user_id, 'edd_user_secret_key' ); 
  1372. } else { 
  1373. return false; 
  1374.  
  1375. return true; 
  1376.  
  1377.  
  1378. /** 
  1379. * Generate and Save API key 
  1380. * Generates the key requested by user_key_field and stores it in the database 
  1381. * @access public 
  1382. * @author Daniel J Griffiths 
  1383. * @since 1.5 
  1384. * @param int $user_id 
  1385. * @return void 
  1386. */ 
  1387. public function update_key( $user_id ) { 
  1388. if ( current_user_can( 'edit_user', $user_id ) && isset( $_POST['edd_set_api_key'] ) ) { 
  1389.  
  1390. $user = get_userdata( $user_id ); 
  1391.  
  1392. if ( empty( $user->edd_user_public_key ) ) { 
  1393. update_user_meta( $user_id, 'edd_user_public_key', $this->generate_public_key( $user->user_email ) ); 
  1394. update_user_meta( $user_id, 'edd_user_secret_key', $this->generate_private_key( $user->ID ) ); 
  1395. } else { 
  1396. $this->revoke_api_key( $user_id ); 
  1397.  
  1398. /** 
  1399. * Generate the public key for a user 
  1400. * @access private 
  1401. * @since 1.9.9 
  1402. * @param string $user_email 
  1403. * @return string 
  1404. */ 
  1405. private function generate_public_key( $user_email = '' ) { 
  1406. $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : ''; 
  1407. $public = hash( 'md5', $user_email . $auth_key . date( 'U' ) ); 
  1408. return $public; 
  1409.  
  1410. /** 
  1411. * Generate the secret key for a user 
  1412. * @access private 
  1413. * @since 1.9.9 
  1414. * @param int $user_id 
  1415. * @return string 
  1416. */ 
  1417. private function generate_private_key( $user_id = 0 ) { 
  1418. $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : ''; 
  1419. $secret = hash( 'md5', $user_id . $auth_key . date( 'U' ) ); 
  1420. return $secret; 
  1421.  
  1422. /** 
  1423. * Retrieve the user's token 
  1424. * @access private 
  1425. * @since 1.9.9 
  1426. * @param int $user_id 
  1427. * @return string 
  1428. */ 
  1429. private function get_token( $user_id = 0 ) { 
  1430. $user = get_userdata( $user_id ); 
  1431. return hash( 'md5', $user->edd_user_secret_key . $user->edd_user_public_key ); 
  1432.  
  1433. /** 
  1434. * Generate the default sales stats returned by the 'stats' endpoint 
  1435. * @access private 
  1436. * @since 1.5.3 
  1437. * @return array default sales statistics 
  1438. */ 
  1439. private function get_default_sales_stats() { 
  1440.  
  1441. // Default sales return 
  1442. $sales = array(); 
  1443. $sales['sales']['today'] = $this->stats->get_sales( 0, 'today' ); 
  1444. $sales['sales']['current_month'] = $this->stats->get_sales( 0, 'this_month' ); 
  1445. $sales['sales']['last_month'] = $this->stats->get_sales( 0, 'last_month' ); 
  1446. $sales['sales']['totals'] = edd_get_total_sales(); 
  1447.  
  1448. return $sales; 
  1449.  
  1450. /** 
  1451. * Generate the default earnings stats returned by the 'stats' endpoint 
  1452. * @access private 
  1453. * @since 1.5.3 
  1454. * @return array default earnings statistics 
  1455. */ 
  1456. private function get_default_earnings_stats() { 
  1457.  
  1458. // Default earnings return 
  1459. $earnings = array(); 
  1460. $earnings['earnings']['today'] = $this->stats->get_earnings( 0, 'today' ); 
  1461. $earnings['earnings']['current_month'] = $this->stats->get_earnings( 0, 'this_month' ); 
  1462. $earnings['earnings']['last_month'] = $this->stats->get_earnings( 0, 'last_month' ); 
  1463. $earnings['earnings']['totals'] = edd_get_total_earnings(); 
  1464.  
  1465. return $earnings;