Nenhuma Descrição

class-wc-admin-dashboard.php 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. <?php
  2. /**
  3. * Admin Dashboard
  4. *
  5. * @package WooCommerce\Admin
  6. * @version 2.1.0
  7. */
  8. use Automattic\Jetpack\Constants;
  9. if ( ! defined( 'ABSPATH' ) ) {
  10. exit; // Exit if accessed directly.
  11. }
  12. if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
  13. /**
  14. * WC_Admin_Dashboard Class.
  15. */
  16. class WC_Admin_Dashboard {
  17. /**
  18. * Hook in tabs.
  19. */
  20. public function __construct() {
  21. // Only hook in admin parts if the user has admin access.
  22. if ( $this->should_display_widget() ) {
  23. // If on network admin, only load the widget that works in that context and skip the rest.
  24. if ( is_multisite() && is_network_admin() ) {
  25. add_action( 'wp_network_dashboard_setup', array( $this, 'register_network_order_widget' ) );
  26. } else {
  27. add_action( 'wp_dashboard_setup', array( $this, 'init' ) );
  28. }
  29. }
  30. }
  31. /**
  32. * Init dashboard widgets.
  33. */
  34. public function init() {
  35. // Reviews Widget.
  36. if ( current_user_can( 'publish_shop_orders' ) && post_type_supports( 'product', 'comments' ) ) {
  37. wp_add_dashboard_widget( 'woocommerce_dashboard_recent_reviews', __( 'WooCommerce Recent Reviews', 'woocommerce' ), array( $this, 'recent_reviews' ) );
  38. }
  39. wp_add_dashboard_widget( 'woocommerce_dashboard_status', __( 'WooCommerce Status', 'woocommerce' ), array( $this, 'status_widget' ) );
  40. // Network Order Widget.
  41. if ( is_multisite() && is_main_site() ) {
  42. $this->register_network_order_widget();
  43. }
  44. }
  45. /**
  46. * Register the network order dashboard widget.
  47. */
  48. public function register_network_order_widget() {
  49. wp_add_dashboard_widget( 'woocommerce_network_orders', __( 'WooCommerce Network Orders', 'woocommerce' ), array( $this, 'network_orders' ) );
  50. }
  51. /**
  52. * Check to see if we should display the widget.
  53. *
  54. * @return bool
  55. */
  56. private function should_display_widget() {
  57. if ( ! WC()->is_wc_admin_active() ) {
  58. return false;
  59. }
  60. $has_permission = current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'manage_woocommerce' ) || current_user_can( 'publish_shop_orders' );
  61. $task_completed_or_hidden = 'yes' === get_option( 'woocommerce_task_list_complete' ) || 'yes' === get_option( 'woocommerce_task_list_hidden' );
  62. return $task_completed_or_hidden && $has_permission;
  63. }
  64. /**
  65. * Get top seller from DB.
  66. *
  67. * @return object
  68. */
  69. private function get_top_seller() {
  70. global $wpdb;
  71. $query = array();
  72. $query['fields'] = "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id
  73. FROM {$wpdb->posts} as posts";
  74. $query['join'] = "INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id ";
  75. $query['join'] .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id ";
  76. $query['join'] .= "INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id ";
  77. $query['where'] = "WHERE posts.post_type IN ( '" . implode( "','", wc_get_order_types( 'order-count' ) ) . "' ) ";
  78. $query['where'] .= "AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) ";
  79. $query['where'] .= "AND order_item_meta.meta_key = '_qty' ";
  80. $query['where'] .= "AND order_item_meta_2.meta_key = '_product_id' ";
  81. $query['where'] .= "AND posts.post_date >= '" . gmdate( 'Y-m-01', current_time( 'timestamp' ) ) . "' ";
  82. $query['where'] .= "AND posts.post_date <= '" . gmdate( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) . "' ";
  83. $query['groupby'] = 'GROUP BY product_id';
  84. $query['orderby'] = 'ORDER BY qty DESC';
  85. $query['limits'] = 'LIMIT 1';
  86. return $wpdb->get_row( implode( ' ', apply_filters( 'woocommerce_dashboard_status_widget_top_seller_query', $query ) ) ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  87. }
  88. /**
  89. * Get sales report data.
  90. *
  91. * @return object
  92. */
  93. private function get_sales_report_data() {
  94. include_once dirname( __FILE__ ) . '/reports/class-wc-report-sales-by-date.php';
  95. $sales_by_date = new WC_Report_Sales_By_Date();
  96. $sales_by_date->start_date = strtotime( gmdate( 'Y-m-01', current_time( 'timestamp' ) ) );
  97. $sales_by_date->end_date = strtotime( gmdate( 'Y-m-d', current_time( 'timestamp' ) ) );
  98. $sales_by_date->chart_groupby = 'day';
  99. $sales_by_date->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)';
  100. return $sales_by_date->get_report_data();
  101. }
  102. /**
  103. * Show status widget.
  104. */
  105. public function status_widget() {
  106. $suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
  107. $version = Constants::get_constant( 'WC_VERSION' );
  108. wp_enqueue_script( 'wc-status-widget', WC()->plugin_url() . '/assets/js/admin/wc-status-widget' . $suffix . '.js', array( 'jquery' ), $version, true );
  109. include_once dirname( __FILE__ ) . '/reports/class-wc-admin-report.php';
  110. $is_wc_admin_disabled = apply_filters( 'woocommerce_admin_disabled', false );
  111. $reports = new WC_Admin_Report();
  112. $net_sales_link = 'admin.php?page=wc-reports&tab=orders&range=month';
  113. $top_seller_link = 'admin.php?page=wc-reports&tab=orders&report=sales_by_product&range=month&product_ids=';
  114. $report_data = $is_wc_admin_disabled ? $this->get_sales_report_data() : $this->get_wc_admin_performance_data();
  115. if ( ! $is_wc_admin_disabled ) {
  116. $net_sales_link = 'admin.php?page=wc-admin&path=%2Fanalytics%2Frevenue&chart=net_revenue&orderby=net_revenue&period=month&compare=previous_period';
  117. $top_seller_link = 'admin.php?page=wc-admin&filter=single_product&path=%2Fanalytics%2Fproducts&products=';
  118. }
  119. echo '<ul class="wc_status_list">';
  120. if ( current_user_can( 'view_woocommerce_reports' ) ) {
  121. if ( $report_data ) {
  122. ?>
  123. <li class="sales-this-month">
  124. <a href="<?php echo esc_url( admin_url( $net_sales_link ) ); ?>">
  125. <?php echo $this->sales_sparkline( $reports, $is_wc_admin_disabled, '' ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
  126. <?php
  127. printf(
  128. /* translators: %s: net sales */
  129. esc_html__( '%s net sales this month', 'woocommerce' ),
  130. '<strong>' . wc_price( $report_data->net_sales ) . '</strong>'
  131. ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
  132. ?>
  133. </a>
  134. </li>
  135. <?php
  136. }
  137. $top_seller = $this->get_top_seller();
  138. if ( $top_seller && $top_seller->qty ) {
  139. ?>
  140. <li class="best-seller-this-month">
  141. <a href="<?php echo esc_url( admin_url( $top_seller_link . $top_seller->product_id ) ); ?>">
  142. <?php echo $this->sales_sparkline( $reports, $is_wc_admin_disabled, $top_seller->product_id, 'count' ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
  143. <?php
  144. printf(
  145. /* translators: 1: top seller product title 2: top seller quantity */
  146. esc_html__( '%1$s top seller this month (sold %2$d)', 'woocommerce' ),
  147. '<strong>' . get_the_title( $top_seller->product_id ) . '</strong>',
  148. $top_seller->qty
  149. ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
  150. ?>
  151. </a>
  152. </li>
  153. <?php
  154. }
  155. }
  156. $this->status_widget_order_rows();
  157. $this->status_widget_stock_rows( $is_wc_admin_disabled );
  158. do_action( 'woocommerce_after_dashboard_status_widget', $reports );
  159. echo '</ul>';
  160. }
  161. /**
  162. * Show order data is status widget.
  163. */
  164. private function status_widget_order_rows() {
  165. if ( ! current_user_can( 'edit_shop_orders' ) ) {
  166. return;
  167. }
  168. $on_hold_count = 0;
  169. $processing_count = 0;
  170. foreach ( wc_get_order_types( 'order-count' ) as $type ) {
  171. $counts = (array) wp_count_posts( $type );
  172. $on_hold_count += isset( $counts['wc-on-hold'] ) ? $counts['wc-on-hold'] : 0;
  173. $processing_count += isset( $counts['wc-processing'] ) ? $counts['wc-processing'] : 0;
  174. }
  175. ?>
  176. <li class="processing-orders">
  177. <a href="<?php echo esc_url( admin_url( 'edit.php?post_status=wc-processing&post_type=shop_order' ) ); ?>">
  178. <?php
  179. printf(
  180. /* translators: %s: order count */
  181. _n( '<strong>%s order</strong> awaiting processing', '<strong>%s orders</strong> awaiting processing', $processing_count, 'woocommerce' ),
  182. $processing_count
  183. ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
  184. ?>
  185. </a>
  186. </li>
  187. <li class="on-hold-orders">
  188. <a href="<?php echo esc_url( admin_url( 'edit.php?post_status=wc-on-hold&post_type=shop_order' ) ); ?>">
  189. <?php
  190. printf(
  191. /* translators: %s: order count */
  192. _n( '<strong>%s order</strong> on-hold', '<strong>%s orders</strong> on-hold', $on_hold_count, 'woocommerce' ),
  193. $on_hold_count
  194. ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
  195. ?>
  196. </a>
  197. </li>
  198. <?php
  199. }
  200. /**
  201. * Show stock data is status widget.
  202. *
  203. * @param bool $is_wc_admin_disabled if woocommerce admin is disabled.
  204. */
  205. private function status_widget_stock_rows( $is_wc_admin_disabled ) {
  206. global $wpdb;
  207. // Requires lookup table added in 3.6.
  208. if ( version_compare( get_option( 'woocommerce_db_version', null ), '3.6', '<' ) ) {
  209. return;
  210. }
  211. $stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
  212. $nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
  213. $transient_name = 'wc_low_stock_count';
  214. $lowinstock_count = get_transient( $transient_name );
  215. if ( false === $lowinstock_count ) {
  216. /**
  217. * Status widget low in stock count pre query.
  218. *
  219. * @since 4.3.0
  220. * @param null|string $low_in_stock_count Low in stock count, by default null.
  221. * @param int $stock Low stock amount.
  222. * @param int $nostock No stock amount
  223. */
  224. $lowinstock_count = apply_filters( 'woocommerce_status_widget_low_in_stock_count_pre_query', null, $stock, $nostock );
  225. if ( is_null( $lowinstock_count ) ) {
  226. $lowinstock_count = $wpdb->get_var(
  227. $wpdb->prepare(
  228. "SELECT COUNT( product_id )
  229. FROM {$wpdb->wc_product_meta_lookup} AS lookup
  230. INNER JOIN {$wpdb->posts} as posts ON lookup.product_id = posts.ID
  231. WHERE stock_quantity <= %d
  232. AND stock_quantity > %d
  233. AND posts.post_status = 'publish'",
  234. $stock,
  235. $nostock
  236. )
  237. );
  238. }
  239. set_transient( $transient_name, (int) $lowinstock_count, DAY_IN_SECONDS * 30 );
  240. }
  241. $transient_name = 'wc_outofstock_count';
  242. $outofstock_count = get_transient( $transient_name );
  243. $lowstock_link = 'admin.php?page=wc-reports&tab=stock&report=low_in_stock';
  244. $outofstock_link = 'admin.php?page=wc-reports&tab=stock&report=out_of_stock';
  245. if ( false === $is_wc_admin_disabled ) {
  246. $lowstock_link = 'admin.php?page=wc-admin&type=lowstock&path=%2Fanalytics%2Fstock';
  247. $outofstock_link = 'admin.php?page=wc-admin&type=outofstock&path=%2Fanalytics%2Fstock';
  248. }
  249. if ( false === $outofstock_count ) {
  250. /**
  251. * Status widget out of stock count pre query.
  252. *
  253. * @since 4.3.0
  254. * @param null|string $outofstock_count Out of stock count, by default null.
  255. * @param int $nostock No stock amount
  256. */
  257. $outofstock_count = apply_filters( 'woocommerce_status_widget_out_of_stock_count_pre_query', null, $nostock );
  258. if ( is_null( $outofstock_count ) ) {
  259. $outofstock_count = (int) $wpdb->get_var(
  260. $wpdb->prepare(
  261. "SELECT COUNT( product_id )
  262. FROM {$wpdb->wc_product_meta_lookup} AS lookup
  263. INNER JOIN {$wpdb->posts} as posts ON lookup.product_id = posts.ID
  264. WHERE stock_quantity <= %d
  265. AND posts.post_status = 'publish'",
  266. $nostock
  267. )
  268. );
  269. }
  270. set_transient( $transient_name, (int) $outofstock_count, DAY_IN_SECONDS * 30 );
  271. }
  272. ?>
  273. <li class="low-in-stock">
  274. <a href="<?php echo esc_url( admin_url( $lowstock_link ) ); ?>">
  275. <?php
  276. printf(
  277. /* translators: %s: order count */
  278. _n( '<strong>%s product</strong> low in stock', '<strong>%s products</strong> low in stock', $lowinstock_count, 'woocommerce' ),
  279. $lowinstock_count
  280. ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
  281. ?>
  282. </a>
  283. </li>
  284. <li class="out-of-stock">
  285. <a href="<?php echo esc_url( admin_url( $outofstock_link ) ); ?>">
  286. <?php
  287. printf(
  288. /* translators: %s: order count */
  289. _n( '<strong>%s product</strong> out of stock', '<strong>%s products</strong> out of stock', $outofstock_count, 'woocommerce' ),
  290. $outofstock_count
  291. ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped
  292. ?>
  293. </a>
  294. </li>
  295. <?php
  296. }
  297. /**
  298. * Recent reviews widget.
  299. */
  300. public function recent_reviews() {
  301. global $wpdb;
  302. $query_from = apply_filters(
  303. 'woocommerce_report_recent_reviews_query_from',
  304. "FROM {$wpdb->comments} comments
  305. LEFT JOIN {$wpdb->posts} posts ON (comments.comment_post_ID = posts.ID)
  306. WHERE comments.comment_approved = '1'
  307. AND comments.comment_type = 'review'
  308. AND posts.post_password = ''
  309. AND posts.post_type = 'product'
  310. AND comments.comment_parent = 0
  311. ORDER BY comments.comment_date_gmt DESC
  312. LIMIT 5"
  313. );
  314. $comments = $wpdb->get_results(
  315. "SELECT posts.ID, posts.post_title, comments.comment_author, comments.comment_author_email, comments.comment_ID, comments.comment_content {$query_from};" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
  316. );
  317. if ( $comments ) {
  318. echo '<ul>';
  319. foreach ( $comments as $comment ) {
  320. echo '<li>';
  321. echo get_avatar( $comment->comment_author_email, '32' );
  322. $rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) );
  323. /* translators: %s: rating */
  324. echo '<div class="star-rating"><span style="width:' . esc_attr( $rating * 20 ) . '%">' . sprintf( esc_html__( '%s out of 5', 'woocommerce' ), esc_html( $rating ) ) . '</span></div>';
  325. /* translators: %s: review author */
  326. echo '<h4 class="meta"><a href="' . esc_url( get_permalink( $comment->ID ) ) . '#comment-' . esc_attr( absint( $comment->comment_ID ) ) . '">' . esc_html( apply_filters( 'woocommerce_admin_dashboard_recent_reviews', $comment->post_title, $comment ) ) . '</a> ' . sprintf( esc_html__( 'reviewed by %s', 'woocommerce' ), esc_html( $comment->comment_author ) ) . '</h4>';
  327. echo '<blockquote>' . wp_kses_data( $comment->comment_content ) . '</blockquote></li>';
  328. }
  329. echo '</ul>';
  330. } else {
  331. echo '<p>' . esc_html__( 'There are no product reviews yet.', 'woocommerce' ) . '</p>';
  332. }
  333. }
  334. /**
  335. * Network orders widget.
  336. */
  337. public function network_orders() {
  338. $suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
  339. $version = Constants::get_constant( 'WC_VERSION' );
  340. wp_enqueue_style( 'wc-network-orders', WC()->plugin_url() . '/assets/css/network-order-widget.css', array(), $version );
  341. wp_enqueue_script( 'wc-network-orders', WC()->plugin_url() . '/assets/js/admin/network-orders' . $suffix . '.js', array( 'jquery', 'underscore' ), $version, true );
  342. $user = wp_get_current_user();
  343. $blogs = get_blogs_of_user( $user->ID );
  344. $blog_ids = wp_list_pluck( $blogs, 'userblog_id' );
  345. wp_localize_script(
  346. 'wc-network-orders',
  347. 'woocommerce_network_orders',
  348. array(
  349. 'nonce' => wp_create_nonce( 'wp_rest' ),
  350. 'sites' => array_values( $blog_ids ),
  351. 'order_endpoint' => get_rest_url( null, 'wc/v3/orders/network' ),
  352. )
  353. );
  354. ?>
  355. <div class="post-type-shop_order">
  356. <div id="woocommerce-network-order-table-loading" class="woocommerce-network-order-table-loading is-active">
  357. <p>
  358. <span class="spinner is-active"></span> <?php esc_html_e( 'Loading network orders', 'woocommerce' ); ?>
  359. </p>
  360. </div>
  361. <table id="woocommerce-network-order-table" class="woocommerce-network-order-table">
  362. <thead>
  363. <tr>
  364. <td><?php esc_html_e( 'Order', 'woocommerce' ); ?></td>
  365. <td><?php esc_html_e( 'Status', 'woocommerce' ); ?></td>
  366. <td><?php esc_html_e( 'Total', 'woocommerce' ); ?></td>
  367. </tr>
  368. </thead>
  369. <tbody id="network-orders-tbody">
  370. </tbody>
  371. </table>
  372. <div id="woocommerce-network-orders-no-orders" class="woocommerce-network-orders-no-orders">
  373. <p>
  374. <?php esc_html_e( 'No orders found', 'woocommerce' ); ?>
  375. </p>
  376. </div>
  377. <?php // @codingStandardsIgnoreStart ?>
  378. <script type="text/template" id="network-orders-row-template">
  379. <tr>
  380. <td>
  381. <a href="<%- edit_url %>" class="order-view"><strong>#<%- number %> <%- customer %></strong></a>
  382. <br>
  383. <em>
  384. <%- blog.blogname %>
  385. </em>
  386. </td>
  387. <td>
  388. <mark class="order-status status-<%- status %>"><span><%- status_name %></span></mark>
  389. </td>
  390. <td>
  391. <%= formatted_total %>
  392. </td>
  393. </tr>
  394. </script>
  395. <?php // @codingStandardsIgnoreEnd ?>
  396. </div>
  397. <?php
  398. }
  399. /**
  400. * Gets the sales performance data from the new WooAdmin store.
  401. *
  402. * @return stdClass|WP_Error|WP_REST_Response
  403. */
  404. private function get_wc_admin_performance_data() {
  405. $request = new \WP_REST_Request( 'GET', '/wc-analytics/reports/performance-indicators' );
  406. $start_date = gmdate( 'Y-m-01 00:00:00', current_time( 'timestamp' ) );
  407. $end_date = gmdate( 'Y-m-d 23:59:59', current_time( 'timestamp' ) );
  408. $request->set_query_params(
  409. array(
  410. 'before' => $end_date,
  411. 'after' => $start_date,
  412. 'stats' => 'revenue/total_sales,revenue/net_revenue,orders/orders_count,products/items_sold,variations/items_sold',
  413. )
  414. );
  415. $response = rest_do_request( $request );
  416. if ( is_wp_error( $response ) ) {
  417. return $response;
  418. }
  419. if ( 200 !== $response->get_status() ) {
  420. return new \WP_Error( 'woocommerce_analytics_performance_indicators_result_failed', __( 'Sorry, fetching performance indicators failed.', 'woocommerce' ) );
  421. }
  422. $report_keys = array(
  423. 'net_revenue' => 'net_sales',
  424. );
  425. $performance_data = new stdClass();
  426. foreach ( $response->get_data() as $indicator ) {
  427. if ( isset( $indicator['chart'] ) && isset( $indicator['value'] ) ) {
  428. $key = isset( $report_keys[ $indicator['chart'] ] ) ? $report_keys[ $indicator['chart'] ] : $indicator['chart'];
  429. $performance_data->$key = $indicator['value'];
  430. }
  431. }
  432. return $performance_data;
  433. }
  434. /**
  435. * Overwrites the original sparkline to use the new reports data if WooAdmin is enabled.
  436. * Prepares a sparkline to show sales in the last X days.
  437. *
  438. * @param WC_Admin_Report $reports old class for getting reports.
  439. * @param bool $is_wc_admin_disabled If WC Admin is disabled or not.
  440. * @param int $id ID of the product to show. Blank to get all orders.
  441. * @param string $type Type of sparkline to get. Ignored if ID is not set.
  442. * @return string
  443. */
  444. private function sales_sparkline( $reports, $is_wc_admin_disabled = false, $id = '', $type = 'sales' ) {
  445. $days = max( 7, gmdate( 'd', current_time( 'timestamp' ) ) );
  446. if ( $is_wc_admin_disabled ) {
  447. return $reports->sales_sparkline( $id, $days, $type );
  448. }
  449. $sales_endpoint = '/wc-analytics/reports/revenue/stats';
  450. $start_date = gmdate( 'Y-m-d 00:00:00', current_time( 'timestamp' ) - ( ( $days - 1 ) * DAY_IN_SECONDS ) );
  451. $end_date = gmdate( 'Y-m-d 23:59:59', current_time( 'timestamp' ) );
  452. $meta_key = 'net_revenue';
  453. $params = array(
  454. 'order' => 'asc',
  455. 'interval' => 'day',
  456. 'per_page' => 100,
  457. 'before' => $end_date,
  458. 'after' => $start_date,
  459. );
  460. if ( $id ) {
  461. $sales_endpoint = '/wc-analytics/reports/products/stats';
  462. $meta_key = ( 'sales' === $type ) ? 'net_revenue' : 'items_sold';
  463. $params['products'] = $id;
  464. }
  465. $request = new \WP_REST_Request( 'GET', $sales_endpoint );
  466. $params['fields'] = array( $meta_key );
  467. $request->set_query_params( $params );
  468. $response = rest_do_request( $request );
  469. if ( is_wp_error( $response ) ) {
  470. return $response;
  471. }
  472. $resp_data = $response->get_data();
  473. $data = $resp_data['intervals'];
  474. $sparkline_data = array();
  475. $total = 0;
  476. foreach ( $data as $d ) {
  477. $total += $d['subtotals']->$meta_key;
  478. array_push( $sparkline_data, array( strval( strtotime( $d['interval'] ) * 1000 ), $d['subtotals']->$meta_key ) );
  479. }
  480. if ( 'sales' === $type ) {
  481. /* translators: 1: total income 2: days */
  482. $tooltip = sprintf( __( 'Sold %1$s worth in the last %2$d days', 'woocommerce' ), strip_tags( wc_price( $total ) ), $days );
  483. } else {
  484. /* translators: 1: total items sold 2: days */
  485. $tooltip = sprintf( _n( 'Sold %1$d item in the last %2$d days', 'Sold %1$d items in the last %2$d days', $total, 'woocommerce' ), $total, $days );
  486. }
  487. return '<span class="wc_sparkline ' . ( ( 'sales' === $type ) ? 'lines' : 'bars' ) . ' tips" data-color="#777" data-tip="' . esc_attr( $tooltip ) . '" data-barwidth="' . 60 * 60 * 16 * 1000 . '" data-sparkline="' . wc_esc_json( wp_json_encode( $sparkline_data ) ) . '"></span>';
  488. }
  489. }
  490. endif;
  491. return new WC_Admin_Dashboard();