/includes/admin/class-wc-admin-dashboard.php

  1. <?php 
  2. /** 
  3. * Admin Dashboard 
  4. * 
  5. * @author WooThemes 
  6. * @category Admin 
  7. * @package WooCommerce/Admin 
  8. * @version 2.1.0 
  9. */ 
  10.  
  11. if ( ! defined( 'ABSPATH' ) ) { 
  12. exit; // Exit if accessed directly 
  13.  
  14. if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) : 
  15.  
  16. /** 
  17. * WC_Admin_Dashboard Class. 
  18. */ 
  19. class WC_Admin_Dashboard { 
  20.  
  21. /** 
  22. * Hook in tabs. 
  23. */ 
  24. public function __construct() { 
  25. // Only hook in admin parts if the user has admin access 
  26. if ( current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'manage_woocommerce' ) || current_user_can( 'publish_shop_orders' ) ) { 
  27. add_action( 'wp_dashboard_setup', array( $this, 'init' ) ); 
  28.  
  29. /** 
  30. * Init dashboard widgets. 
  31. */ 
  32. public function init() { 
  33. if ( current_user_can( 'publish_shop_orders' ) ) { 
  34. wp_add_dashboard_widget( 'woocommerce_dashboard_recent_reviews', __( 'WooCommerce recent reviews', 'woocommerce' ), array( $this, 'recent_reviews' ) ); 
  35. wp_add_dashboard_widget( 'woocommerce_dashboard_status', __( 'WooCommerce status', 'woocommerce' ), array( $this, 'status_widget' ) ); 
  36.  
  37. /** 
  38. * Get top seller from DB. 
  39. * @return object 
  40. */ 
  41. private function get_top_seller() { 
  42. global $wpdb; 
  43.  
  44. $query = array(); 
  45. $query['fields'] = "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id 
  46. FROM {$wpdb->posts} as posts"; 
  47. $query['join'] = "INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id "; 
  48. $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 "; 
  49. $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 "; 
  50. $query['where'] = "WHERE posts.post_type IN ( '" . implode( "', '", wc_get_order_types( 'order-count' ) ) . "' ) "; 
  51. $query['where'] .= "AND posts.post_status IN ( 'wc-" . implode( "', 'wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) "; 
  52. $query['where'] .= "AND order_item_meta.meta_key = '_qty' "; 
  53. $query['where'] .= "AND order_item_meta_2.meta_key = '_product_id' "; 
  54. $query['where'] .= "AND posts.post_date >= '" . date( 'Y-m-01', current_time( 'timestamp' ) ) . "' "; 
  55. $query['where'] .= "AND posts.post_date <= '" . date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) . "' "; 
  56. $query['groupby'] = "GROUP BY product_id"; 
  57. $query['orderby'] = "ORDER BY qty DESC"; 
  58. $query['limits'] = "LIMIT 1"; 
  59.  
  60. return $wpdb->get_row( implode( ' ', apply_filters( 'woocommerce_dashboard_status_widget_top_seller_query', $query ) ) ); 
  61.  
  62. /** 
  63. * Get sales report data. 
  64. * @return object 
  65. */ 
  66. private function get_sales_report_data() { 
  67. include_once( dirname( __FILE__ ) . '/reports/class-wc-report-sales-by-date.php' ); 
  68.  
  69. $sales_by_date = new WC_Report_Sales_By_Date(); 
  70. $sales_by_date->start_date = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) ); 
  71. $sales_by_date->end_date = current_time( 'timestamp' ); 
  72. $sales_by_date->chart_groupby = 'day'; 
  73. $sales_by_date->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)'; 
  74.  
  75. return $sales_by_date->get_report_data(); 
  76.  
  77. /** 
  78. * Show status widget. 
  79. */ 
  80. public function status_widget() { 
  81. include_once( dirname( __FILE__ ) . '/reports/class-wc-admin-report.php' ); 
  82.  
  83. $reports = new WC_Admin_Report(); 
  84.  
  85. echo '<ul class="wc_status_list">'; 
  86.  
  87. if ( current_user_can( 'view_woocommerce_reports' ) && ( $report_data = $this->get_sales_report_data() ) ) { 
  88. ?> 
  89. <li class="sales-this-month"> 
  90. <a href="<?php echo admin_url( 'admin.php?page=wc-reports&tab=orders&range=month' ); ?>"> 
  91. <?php echo $reports->sales_sparkline( '', max( 7, date( 'd', current_time( 'timestamp' ) ) ) ); ?> 
  92. <?php 
  93. /** translators: %s: net sales */ 
  94. printf( 
  95. __( '%s net sales this month', 'woocommerce' ),  
  96. '<strong>' . wc_price( $report_data->net_sales ) . '</strong>' 
  97. ); 
  98. ?> 
  99. </a> 
  100. </li> 
  101. <?php 
  102.  
  103. if ( current_user_can( 'view_woocommerce_reports' ) && ( $top_seller = $this->get_top_seller() ) && $top_seller->qty ) { 
  104. ?> 
  105. <li class="best-seller-this-month"> 
  106. <a href="<?php echo admin_url( 'admin.php?page=wc-reports&tab=orders&report=sales_by_product&range=month&product_ids=' . $top_seller->product_id ); ?>"> 
  107. <?php echo $reports->sales_sparkline( $top_seller->product_id, max( 7, date( 'd', current_time( 'timestamp' ) ) ), 'count' ); ?> 
  108. <?php 
  109. /** translators: 1: top seller product title 2: top seller quantity */ 
  110. printf( 
  111. __( '%1$s top seller this month (sold %2$d)', 'woocommerce' ),  
  112. '<strong>' . get_the_title( $top_seller->product_id ) . '</strong>',  
  113. $top_seller->qty 
  114. ); 
  115. ?> 
  116. </a> 
  117. </li> 
  118. <?php 
  119.  
  120. $this->status_widget_order_rows(); 
  121. $this->status_widget_stock_rows(); 
  122.  
  123. do_action( 'woocommerce_after_dashboard_status_widget', $reports ); 
  124. echo '</ul>'; 
  125.  
  126. /** 
  127. * Show order data is status widget. 
  128. */ 
  129. private function status_widget_order_rows() { 
  130. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  131. return; 
  132. $on_hold_count = 0; 
  133. $processing_count = 0; 
  134.  
  135. foreach ( wc_get_order_types( 'order-count' ) as $type ) { 
  136. $counts = (array) wp_count_posts( $type ); 
  137. $on_hold_count += isset( $counts['wc-on-hold'] ) ? $counts['wc-on-hold'] : 0; 
  138. $processing_count += isset( $counts['wc-processing'] ) ? $counts['wc-processing'] : 0; 
  139. ?> 
  140. <li class="processing-orders"> 
  141. <a href="<?php echo admin_url( 'edit.php?post_status=wc-processing&post_type=shop_order' ); ?>"> 
  142. <?php 
  143. /** translators: %s: order count */ 
  144. printf( 
  145. _n( '<strong>%s order</strong> awaiting processing', '<strong>%s orders</strong> awaiting processing', $processing_count, 'woocommerce' ),  
  146. $processing_count 
  147. ); 
  148. ?> 
  149. </a> 
  150. </li> 
  151. <li class="on-hold-orders"> 
  152. <a href="<?php echo admin_url( 'edit.php?post_status=wc-on-hold&post_type=shop_order' ); ?>"> 
  153. <?php 
  154. /** translators: %s: order count */ 
  155. printf( 
  156. _n( '<strong>%s order</strong> on-hold', '<strong>%s orders</strong> on-hold', $on_hold_count, 'woocommerce' ),  
  157. $on_hold_count 
  158. ); 
  159. ?> 
  160. </a> 
  161. </li> 
  162. <?php 
  163.  
  164. /** 
  165. * Show stock data is status widget. 
  166. */ 
  167. private function status_widget_stock_rows() { 
  168. global $wpdb; 
  169.  
  170. // Get products using a query - this is too advanced for get_posts :( 
  171. $stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) ); 
  172. $nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) ); 
  173. $transient_name = 'wc_low_stock_count'; 
  174.  
  175. if ( false === ( $lowinstock_count = get_transient( $transient_name ) ) ) { 
  176. $query_from = apply_filters( 'woocommerce_report_low_in_stock_query_from', "FROM {$wpdb->posts} as posts 
  177. INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id 
  178. INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id 
  179. WHERE 1=1 
  180. AND posts.post_type IN ( 'product', 'product_variation' ) 
  181. AND posts.post_status = 'publish' 
  182. AND postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' 
  183. AND postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' 
  184. AND postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' 
  185. " ); 
  186. $lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); 
  187. set_transient( $transient_name, $lowinstock_count, DAY_IN_SECONDS * 30 ); 
  188.  
  189. $transient_name = 'wc_outofstock_count'; 
  190.  
  191. if ( false === ( $outofstock_count = get_transient( $transient_name ) ) ) { 
  192. $query_from = apply_filters( 'woocommerce_report_out_of_stock_query_from', "FROM {$wpdb->posts} as posts 
  193. INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id 
  194. INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id 
  195. WHERE 1=1 
  196. AND posts.post_type IN ( 'product', 'product_variation' ) 
  197. AND posts.post_status = 'publish' 
  198. AND postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' 
  199. AND postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$nostock}' 
  200. " ); 
  201. $outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); 
  202. set_transient( $transient_name, $outofstock_count, DAY_IN_SECONDS * 30 ); 
  203. ?> 
  204. <li class="low-in-stock"> 
  205. <a href="<?php echo admin_url( 'admin.php?page=wc-reports&tab=stock&report=low_in_stock' ); ?>"> 
  206. <?php 
  207. /** translators: %s: order count */ 
  208. printf( 
  209. _n( '<strong>%s product</strong> low in stock', '<strong>%s products</strong> low in stock', $lowinstock_count, 'woocommerce' ),  
  210. $lowinstock_count 
  211. ); 
  212. ?> 
  213. </a> 
  214. </li> 
  215. <li class="out-of-stock"> 
  216. <a href="<?php echo admin_url( 'admin.php?page=wc-reports&tab=stock&report=out_of_stock' ); ?>"> 
  217. <?php 
  218. /** translators: %s: order count */ 
  219. printf( 
  220. _n( '<strong>%s product</strong> out of stock', '<strong>%s products</strong> out of stock', $outofstock_count, 'woocommerce' ),  
  221. $outofstock_count 
  222. ); 
  223. ?> 
  224. </a> 
  225. </li> 
  226. <?php 
  227.  
  228. /** 
  229. * Recent reviews widget. 
  230. */ 
  231. public function recent_reviews() { 
  232. global $wpdb; 
  233. $comments = $wpdb->get_results( " 
  234. SELECT posts.ID, posts.post_title, comments.comment_author, comments.comment_ID, SUBSTRING(comments.comment_content, 1, 100) AS comment_excerpt 
  235. FROM $wpdb->comments comments 
  236. LEFT JOIN $wpdb->posts posts ON (comments.comment_post_ID = posts.ID) 
  237. WHERE comments.comment_approved = '1' 
  238. AND comments.comment_type = '' 
  239. AND posts.post_password = '' 
  240. AND posts.post_type = 'product' 
  241. ORDER BY comments.comment_date_gmt DESC 
  242. LIMIT 5 
  243. " ); 
  244.  
  245. if ( $comments ) { 
  246. echo '<ul>'; 
  247. foreach ( $comments as $comment ) { 
  248.  
  249. echo '<li>'; 
  250.  
  251. echo get_avatar( $comment->comment_author, '32' ); 
  252.  
  253. $rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) ); 
  254.  
  255. /** translators: %s: rating */ 
  256. echo '<div class="star-rating"><span style="width:' . ( $rating * 20 ) . '%">' . sprintf( __( '%s out of 5', 'woocommerce' ), $rating ) . '</span></div>'; 
  257.  
  258. /** translators: %s: review author */ 
  259. echo '<h4 class="meta"><a href="' . get_permalink( $comment->ID ) . '#comment-' . absint( $comment->comment_ID ) . '">' . esc_html( apply_filters( 'woocommerce_admin_dashboard_recent_reviews', $comment->post_title, $comment ) ) . '</a> ' . sprintf( __( 'reviewed by %s', 'woocommerce' ), esc_html( $comment->comment_author ) ) . '</h4>'; 
  260. echo '<blockquote>' . wp_kses_data( $comment->comment_excerpt ) . ' [...]</blockquote></li>'; 
  261.  
  262. echo '</ul>'; 
  263. } else { 
  264. echo '<p>' . __( 'There are no product reviews yet.', 'woocommerce' ) . '</p>'; 
  265.  
  266. endif; 
  267.  
  268. return new WC_Admin_Dashboard(); 
.