|
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
|
|
|
|
|
15
|
|
|
if ( ! class_exists( 'WC_Admin_Dashboard' ) ) : |
|
16
|
|
|
|
|
17
|
|
|
/** |
|
18
|
|
|
* WC_Admin_Dashboard Class. |
|
19
|
|
|
*/ |
|
20
|
|
|
class WC_Admin_Dashboard { |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* Hook in tabs. |
|
24
|
|
|
*/ |
|
25
|
|
|
public function __construct() { |
|
26
|
|
|
// Only hook in admin parts if the user has admin access |
|
27
|
|
|
if ( current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'manage_woocommerce' ) || current_user_can( 'publish_shop_orders' ) ) { |
|
28
|
|
|
add_action( 'wp_dashboard_setup', array( $this, 'init' ) ); |
|
29
|
|
|
} |
|
30
|
|
|
} |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* Init dashboard widgets. |
|
34
|
|
|
*/ |
|
35
|
|
|
public function init() { |
|
36
|
|
|
if ( current_user_can( 'publish_shop_orders' ) ) { |
|
37
|
|
|
wp_add_dashboard_widget( 'woocommerce_dashboard_recent_reviews', __( 'WooCommerce Recent Reviews', 'woocommerce' ), array( $this, 'recent_reviews' ) ); |
|
38
|
|
|
} |
|
39
|
|
|
|
|
40
|
|
|
wp_add_dashboard_widget( 'woocommerce_dashboard_status', __( 'WooCommerce Status', 'woocommerce' ), array( $this, 'status_widget' ) ); |
|
41
|
|
|
} |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* Show status widget. |
|
45
|
|
|
*/ |
|
46
|
|
|
public function status_widget() { |
|
47
|
|
|
global $wpdb; |
|
48
|
|
|
|
|
49
|
|
|
include_once( 'reports/class-wc-admin-report.php' ); |
|
50
|
|
|
include_once( 'reports/class-wc-report-sales-by-date.php' ); |
|
51
|
|
|
|
|
52
|
|
|
$reports = new WC_Admin_Report(); |
|
53
|
|
|
$sales_by_date = new WC_Report_Sales_By_Date(); |
|
54
|
|
|
$sales_by_date->start_date = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) ); |
|
|
|
|
|
|
55
|
|
|
$sales_by_date->end_date = current_time( 'timestamp' ); |
|
56
|
|
|
$sales_by_date->chart_groupby = 'day'; |
|
57
|
|
|
$sales_by_date->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)'; |
|
58
|
|
|
$report_data = $sales_by_date->get_report_data(); |
|
59
|
|
|
|
|
60
|
|
|
// Get top seller |
|
61
|
|
|
$query = array(); |
|
62
|
|
|
$query['fields'] = "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id |
|
63
|
|
|
FROM {$wpdb->posts} as posts"; |
|
64
|
|
|
$query['join'] = "INNER JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id "; |
|
65
|
|
|
$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 "; |
|
66
|
|
|
$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 "; |
|
67
|
|
|
$query['where'] = "WHERE posts.post_type IN ( '" . implode( "','", wc_get_order_types( 'order-count' ) ) . "' ) "; |
|
68
|
|
|
$query['where'] .= "AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) "; |
|
69
|
|
|
$query['where'] .= "AND order_item_meta.meta_key = '_qty' "; |
|
70
|
|
|
$query['where'] .= "AND order_item_meta_2.meta_key = '_product_id' "; |
|
71
|
|
|
$query['where'] .= "AND posts.post_date >= '" . date( 'Y-m-01', current_time( 'timestamp' ) ) . "' "; |
|
72
|
|
|
$query['where'] .= "AND posts.post_date <= '" . date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) . "' "; |
|
73
|
|
|
$query['groupby'] = "GROUP BY product_id"; |
|
74
|
|
|
$query['orderby'] = "ORDER BY qty DESC"; |
|
75
|
|
|
$query['limits'] = "LIMIT 1"; |
|
76
|
|
|
|
|
77
|
|
|
$top_seller = $wpdb->get_row( implode( ' ', apply_filters( 'woocommerce_dashboard_status_widget_top_seller_query', $query ) ) ); |
|
78
|
|
|
|
|
79
|
|
|
// Counts |
|
80
|
|
|
$on_hold_count = 0; |
|
81
|
|
|
$processing_count = 0; |
|
82
|
|
|
|
|
83
|
|
|
foreach ( wc_get_order_types( 'order-count' ) as $type ) { |
|
84
|
|
|
$counts = (array) wp_count_posts( $type ); |
|
85
|
|
|
$on_hold_count += isset( $counts['wc-on-hold'] ) ? $counts['wc-on-hold'] : 0; |
|
86
|
|
|
$processing_count += isset( $counts['wc-processing'] ) ? $counts['wc-processing'] : 0; |
|
87
|
|
|
} |
|
88
|
|
|
|
|
89
|
|
|
// Get products using a query - this is too advanced for get_posts :( |
|
90
|
|
|
$stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) ); |
|
91
|
|
|
$nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) ); |
|
92
|
|
|
$transient_name = 'wc_low_stock_count'; |
|
93
|
|
|
|
|
94
|
|
View Code Duplication |
if ( false === ( $lowinstock_count = get_transient( $transient_name ) ) ) { |
|
|
|
|
|
|
95
|
|
|
$query_from = apply_filters( 'woocommerce_report_low_in_stock_query_from', "FROM {$wpdb->posts} as posts |
|
96
|
|
|
INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id |
|
97
|
|
|
INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id |
|
98
|
|
|
WHERE 1=1 |
|
99
|
|
|
AND posts.post_type IN ( 'product', 'product_variation' ) |
|
100
|
|
|
AND posts.post_status = 'publish' |
|
101
|
|
|
AND postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' |
|
102
|
|
|
AND postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' |
|
103
|
|
|
AND postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' |
|
104
|
|
|
" ); |
|
105
|
|
|
$lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); |
|
106
|
|
|
set_transient( $transient_name, $lowinstock_count, DAY_IN_SECONDS * 30 ); |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
|
|
$transient_name = 'wc_outofstock_count'; |
|
110
|
|
|
|
|
111
|
|
View Code Duplication |
if ( false === ( $outofstock_count = get_transient( $transient_name ) ) ) { |
|
|
|
|
|
|
112
|
|
|
$query_from = apply_filters( 'woocommerce_report_out_of_stock_query_from', "FROM {$wpdb->posts} as posts |
|
113
|
|
|
INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id |
|
114
|
|
|
INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id |
|
115
|
|
|
WHERE 1=1 |
|
116
|
|
|
AND posts.post_type IN ( 'product', 'product_variation' ) |
|
117
|
|
|
AND posts.post_status = 'publish' |
|
118
|
|
|
AND postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' |
|
119
|
|
|
AND postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$nostock}' |
|
120
|
|
|
" ); |
|
121
|
|
|
$outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) ); |
|
122
|
|
|
set_transient( $transient_name, $outofstock_count, DAY_IN_SECONDS * 30 ); |
|
123
|
|
|
} |
|
124
|
|
|
?> |
|
125
|
|
|
<ul class="wc_status_list"> |
|
126
|
|
|
<li class="sales-this-month"> |
|
127
|
|
|
<a href="<?php echo admin_url( 'admin.php?page=wc-reports&tab=orders&range=month' ); ?>"> |
|
128
|
|
|
<?php echo $reports->sales_sparkline( '', max( 7, date( 'd', current_time( 'timestamp' ) ) ) ); ?> |
|
129
|
|
|
<?php printf( __( "<strong>%s</strong> net sales this month", 'woocommerce' ), wc_price( $report_data->net_sales ) ); ?> |
|
130
|
|
|
</a> |
|
131
|
|
|
</li> |
|
132
|
|
|
<?php if ( $top_seller && $top_seller->qty ) : ?> |
|
133
|
|
|
<li class="best-seller-this-month"> |
|
134
|
|
|
<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 ); ?>"> |
|
135
|
|
|
<?php echo $reports->sales_sparkline( $top_seller->product_id, max( 7, date( 'd', current_time( 'timestamp' ) ) ), 'count' ); ?> |
|
136
|
|
|
<?php printf( __( "%s top seller this month (sold %d)", 'woocommerce' ), "<strong>" . get_the_title( $top_seller->product_id ) . "</strong>", $top_seller->qty ); ?> |
|
137
|
|
|
</a> |
|
138
|
|
|
</li> |
|
139
|
|
|
<?php endif; ?> |
|
140
|
|
|
<li class="processing-orders"> |
|
141
|
|
|
<a href="<?php echo admin_url( 'edit.php?post_status=wc-processing&post_type=shop_order' ); ?>"> |
|
142
|
|
|
<?php printf( _n( "<strong>%s order</strong> awaiting processing", "<strong>%s orders</strong> awaiting processing", $processing_count, 'woocommerce' ), $processing_count ); ?> |
|
143
|
|
|
</a> |
|
144
|
|
|
</li> |
|
145
|
|
|
<li class="on-hold-orders"> |
|
146
|
|
|
<a href="<?php echo admin_url( 'edit.php?post_status=wc-on-hold&post_type=shop_order' ); ?>"> |
|
147
|
|
|
<?php printf( _n( "<strong>%s order</strong> on-hold", "<strong>%s orders</strong> on-hold", $on_hold_count, 'woocommerce' ), $on_hold_count ); ?> |
|
148
|
|
|
</a> |
|
149
|
|
|
</li> |
|
150
|
|
|
<li class="low-in-stock"> |
|
151
|
|
|
<a href="<?php echo admin_url( 'admin.php?page=wc-reports&tab=stock&report=low_in_stock' ); ?>"> |
|
152
|
|
|
<?php printf( _n( "<strong>%s product</strong> low in stock", "<strong>%s products</strong> low in stock", $lowinstock_count, 'woocommerce' ), $lowinstock_count ); ?> |
|
153
|
|
|
</a> |
|
154
|
|
|
</li> |
|
155
|
|
|
<li class="out-of-stock"> |
|
156
|
|
|
<a href="<?php echo admin_url( 'admin.php?page=wc-reports&tab=stock&report=out_of_stock' ); ?>"> |
|
157
|
|
|
<?php printf( _n( "<strong>%s product</strong> out of stock", "<strong>%s products</strong> out of stock", $outofstock_count, 'woocommerce' ), $outofstock_count ); ?> |
|
158
|
|
|
</a> |
|
159
|
|
|
</li> |
|
160
|
|
|
|
|
161
|
|
|
<?php do_action( 'woocommerce_after_dashboard_status_widget', $reports ); ?> |
|
162
|
|
|
</ul> |
|
163
|
|
|
<?php |
|
164
|
|
|
} |
|
165
|
|
|
|
|
166
|
|
|
/** |
|
167
|
|
|
* Recent reviews widget. |
|
168
|
|
|
*/ |
|
169
|
|
|
public function recent_reviews() { |
|
170
|
|
|
global $wpdb; |
|
171
|
|
|
$comments = $wpdb->get_results( "SELECT *, SUBSTRING(comment_content,1,100) AS comment_excerpt |
|
172
|
|
|
FROM $wpdb->comments |
|
173
|
|
|
LEFT JOIN $wpdb->posts ON ($wpdb->comments.comment_post_ID = $wpdb->posts.ID) |
|
174
|
|
|
WHERE comment_approved = '1' |
|
175
|
|
|
AND comment_type = '' |
|
176
|
|
|
AND post_password = '' |
|
177
|
|
|
AND post_type = 'product' |
|
178
|
|
|
ORDER BY comment_date_gmt DESC |
|
179
|
|
|
LIMIT 8" ); |
|
180
|
|
|
|
|
181
|
|
|
if ( $comments ) { |
|
182
|
|
|
echo '<ul>'; |
|
183
|
|
|
foreach ( $comments as $comment ) { |
|
184
|
|
|
|
|
185
|
|
|
echo '<li>'; |
|
186
|
|
|
|
|
187
|
|
|
echo get_avatar( $comment->comment_author, '32' ); |
|
188
|
|
|
|
|
189
|
|
|
$rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) ); |
|
190
|
|
|
|
|
191
|
|
|
echo '<div class="star-rating" title="' . esc_attr( $rating ) . '"> |
|
192
|
|
|
<span style="width:' . ( $rating * 20 ) . '%">' . $rating . ' ' . __( 'out of 5', 'woocommerce' ) . '</span></div>'; |
|
193
|
|
|
|
|
194
|
|
|
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> ' . __( 'reviewed by', 'woocommerce' ) . ' ' . esc_html( $comment->comment_author ) .'</h4>'; |
|
195
|
|
|
echo '<blockquote>' . wp_kses_data( $comment->comment_excerpt ) . ' [...]</blockquote></li>'; |
|
196
|
|
|
|
|
197
|
|
|
} |
|
198
|
|
|
echo '</ul>'; |
|
199
|
|
|
} else { |
|
200
|
|
|
echo '<p>' . __( 'There are no product reviews yet.', 'woocommerce' ) . '</p>'; |
|
201
|
|
|
} |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
} |
|
205
|
|
|
|
|
206
|
|
|
endif; |
|
207
|
|
|
|
|
208
|
|
|
return new WC_Admin_Dashboard(); |
|
209
|
|
|
|
This check looks for assignments to scalar types that may be of the wrong type.
To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.