Dashboard. Closes #3493

This commit is contained in:
Mike Jolley 2013-07-25 15:00:23 +01:00
parent 29945ed3ad
commit 8f04446a61
10 changed files with 531 additions and 436 deletions

File diff suppressed because one or more lines are too long

View File

@ -1535,63 +1535,6 @@ ul.recent-orders, ul.stock_list {
}
}
}
#woocommerce_dashboard_recent_reviews {
li {
line-height: 1.5em;
margin-bottom: 12px;
}
h4.meta {
line-height: 1.4;
margin: -.2em 0 0 0;
font-weight: normal;
color: #999;
}
blockquote {
padding: 0;
margin: 0;
}
.avatar {
float: left;
margin: 0 10px 5px 0;
}
.star-rating {
float: right;
overflow: hidden;
position: relative;
height: 1.5em;
line-height: 1.5;
margin-left:.5em;
width: 5.4em;
font-family: 'WooCommerce' !important;
&:before {
content: "\e021\e021\e021\e021\e021";
color: darken( #ccc, 10 );
float: left;
top: 0;
left: 0;
position: absolute;
letter-spacing: 0.1em;
letter-spacing:0\9; // IE8 & below hack ;-(
}
span {
overflow: hidden;
float: left;
top: 0;
left: 0;
position: absolute;
padding-top: 1.5em;
}
span:before {
content: "\e020\e020\e020\e020\e020";
top: 0;
position: absolute;
left: 0;
letter-spacing: 0.1em;
letter-spacing:0\9; // IE8 & below hack ;-(
color: #9c5d90;
}
}
}
/* Settings */
mark.notice {

21
assets/css/dashboard.css Normal file
View File

@ -0,0 +1,21 @@
.clear{clear:both}
.nobr{white-space:nowrap}
@font-face{font-family:'WooCommerce';src:url('../fonts/WooCommerce.eot');src:url('../fonts/WooCommerce.eot?#iefix') format('embedded-opentype'),url('../fonts/WooCommerce.woff') format('woff'),url('../fonts/WooCommerce.ttf') format('truetype'),url('../fonts/WooCommerce.svg#WooCommerce') format('svg');font-weight:normal;font-style:normal}#woocommerce_dashboard_status .inside{padding:0;margin:0}
#woocommerce_dashboard_status .wc_status_list{overflow:hidden;margin:0}#woocommerce_dashboard_status .wc_status_list li{width:50%;float:left;padding:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border-top:1px solid #ececec;color:#aaa}#woocommerce_dashboard_status .wc_status_list li a{display:block;color:#aaa;padding:9px 12px;-webkit-transition:all ease .5s;position:relative}#woocommerce_dashboard_status .wc_status_list li a .wc_sparkline{width:4em;height:2em;display:block;float:right;position:absolute;right:0;top:50%;margin-right:12px;margin-top:-1.25em}
#woocommerce_dashboard_status .wc_status_list li a strong{font-size:18px;line-height:1.2em;font-weight:normal;display:block;color:#21759b;font-family:Georgia,"Times New Roman","Bitstream Charter",Times,serif}
#woocommerce_dashboard_status .wc_status_list li a:hover{box-shadow:inset 0 -1px 0 0 #dfdfdf,inset 700px 0 0 rgba(156,93,144,0.1);padding-left:2em;color:#9c5d90}#woocommerce_dashboard_status .wc_status_list li a:hover:before,#woocommerce_dashboard_status .wc_status_list li a:hover strong{color:#9c5d90 !important}
#woocommerce_dashboard_status .wc_status_list li a:before{font-family:'WooCommerce';speak:none;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;margin:0;text-indent:0;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;font-size:2em;position:relative;width:auto;line-height:1.2em;color:#464646;float:left;margin-right:12px;margin-bottom:12px}
#woocommerce_dashboard_status .wc_status_list li:first-child{border-top:0}
#woocommerce_dashboard_status .wc_status_list li.sales-this-month{width:100%}#woocommerce_dashboard_status .wc_status_list li.sales-this-month a:before{content:"\e01f"}
#woocommerce_dashboard_status .wc_status_list li.best-seller-this-month{width:100%}#woocommerce_dashboard_status .wc_status_list li.best-seller-this-month a:before{content:"\e006"}
#woocommerce_dashboard_status .wc_status_list li.processing-orders{border-right:1px solid #ececec}#woocommerce_dashboard_status .wc_status_list li.processing-orders a:before{content:"\e011";color:#73a724}
#woocommerce_dashboard_status .wc_status_list li.on-hold-orders a:before{content:"\e033";color:#999}
#woocommerce_dashboard_status .wc_status_list li.low-in-stock{border-right:1px solid #ececec}#woocommerce_dashboard_status .wc_status_list li.low-in-stock a:before{content:"\e02c";color:#c1692f}
#woocommerce_dashboard_status .wc_status_list li.out-of-stock a:before{content:"\e02c";color:#a44}
#woocommerce_dashboard_recent_reviews li{line-height:1.5em;margin-bottom:12px}
#woocommerce_dashboard_recent_reviews h4.meta{line-height:1.4;margin:-0.2em 0 0 0;font-weight:normal;color:#999}
#woocommerce_dashboard_recent_reviews blockquote{padding:0;margin:0}
#woocommerce_dashboard_recent_reviews .avatar{float:left;margin:0 10px 5px 0}
#woocommerce_dashboard_recent_reviews .star-rating{float:right;overflow:hidden;position:relative;height:1.5em;line-height:1.5;margin-left:.5em;width:5.4em;font-family:'WooCommerce' !important}#woocommerce_dashboard_recent_reviews .star-rating:before{content:"\e021\e021\e021\e021\e021";color:#b3b3b3;float:left;top:0;left:0;position:absolute;letter-spacing:.1em;letter-spacing:0\9}
#woocommerce_dashboard_recent_reviews .star-rating span{overflow:hidden;float:left;top:0;left:0;position:absolute;padding-top:1.5em}
#woocommerce_dashboard_recent_reviews .star-rating span:before{content:"\e020\e020\e020\e020\e020";top:0;position:absolute;left:0;letter-spacing:.1em;letter-spacing:0\9;color:#9c5d90}

188
assets/css/dashboard.less Normal file
View File

@ -0,0 +1,188 @@
@import 'mixins.less';
@font-face {
font-family: 'WooCommerce';
src:url('../fonts/WooCommerce.eot');
src:url('../fonts/WooCommerce.eot?#iefix') format('embedded-opentype'),
url('../fonts/WooCommerce.woff') format('woff'),
url('../fonts/WooCommerce.ttf') format('truetype'),
url('../fonts/WooCommerce.svg#WooCommerce') format('svg');
font-weight: normal;
font-style: normal;
}
#woocommerce_dashboard_status {
.inside {
padding: 0;
margin: 0;
}
.wc_status_list {
overflow: hidden;
margin:0;
li {
width: 50%;
float: left;
padding:0;
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box; /* Firefox, other Gecko */
box-sizing: border-box; /* Opera/IE 8+ */
margin: 0;
border-top: 1px solid #ececec;
color: #aaa;
a {
display: block;
color: #aaa;
padding: 9px 12px;
-webkit-transition:all ease .5s;
position: relative;
.wc_sparkline {
width: 4em;
height: 2em;
display: block;
float: right;
position: absolute;
right: 0;
top: 50%;
margin-right: 12px;
margin-top: -1.25em
}
strong {
font-size: 18px;
line-height: 1.2em;
font-weight: normal;
display: block;
color: #21759b;
font-family: Georgia,"Times New Roman","Bitstream Charter",Times,serif;
}
&:hover {
box-shadow:
inset 0 -1px 0 0 #dfdfdf,
inset 700px 0 0 fade(#9c5d90,10%);
padding-left:2em;
color: #9c5d90;
&:before, strong {
color: #9c5d90 !important;
}
}
&:before {
.icon();
font-size: 2em;
position: relative;
width: auto;
line-height: 1.2em;
color: #464646;
float: left;
margin-right: 12px;
margin-bottom: 12px;
}
}
}
li:first-child {
border-top: 0;
}
li.sales-this-month {
width: 100%;
a:before {
content: "\e01f";
}
}
li.best-seller-this-month {
width: 100%;
a:before {
content: "\e006";
}
}
li.processing-orders {
border-right: 1px solid #ececec;
a:before {
content: "\e011";
color: #73a724;
}
}
li.on-hold-orders {
a:before {
content: "\e033";
color: #999;
}
}
li.low-in-stock {
border-right: 1px solid #ececec;
a:before {
content: "\e02c";
color: #c1692f;
}
}
li.out-of-stock {
a:before {
content: "\e02c";
color: #aa4444;
}
}
}
}
#woocommerce_dashboard_recent_reviews {
li {
line-height: 1.5em;
margin-bottom: 12px;
}
h4.meta {
line-height: 1.4;
margin: -.2em 0 0 0;
font-weight: normal;
color: #999;
}
blockquote {
padding: 0;
margin: 0;
}
.avatar {
float: left;
margin: 0 10px 5px 0;
}
.star-rating {
float: right;
overflow: hidden;
position: relative;
height: 1.5em;
line-height: 1.5;
margin-left:.5em;
width: 5.4em;
font-family: 'WooCommerce' !important;
&:before {
content: "\e021\e021\e021\e021\e021";
color: darken( #ccc, 10 );
float: left;
top: 0;
left: 0;
position: absolute;
letter-spacing: 0.1em;
letter-spacing:0\9; // IE8 & below hack ;-(
}
span {
overflow: hidden;
float: left;
top: 0;
left: 0;
position: absolute;
padding-top: 1.5em;
}
span:before {
content: "\e020\e020\e020\e020\e020";
top: 0;
position: absolute;
left: 0;
letter-spacing: 0.1em;
letter-spacing:0\9; // IE8 & below hack ;-(
color: #9c5d90;
}
}
}

View File

@ -47,6 +47,10 @@ class WC_Admin_Assets {
wp_enqueue_style( 'wp-color-picker' );
}
if ( in_array( $screen->id, array( 'dashboard' ) ) ) {
wp_enqueue_style( 'woocommerce_admin_dashboard_styles', $woocommerce->plugin_url() . '/assets/css/dashboard.css' );
}
do_action( 'woocommerce_admin_css' );
}
@ -176,7 +180,7 @@ class WC_Admin_Assets {
}
// Reports Pages
if ( in_array( $screen->id, apply_filters( 'woocommerce_reports_screen_ids', array( $wc_screen_id . '_page_wc_reports' ) ) ) ) {
if ( in_array( $screen->id, apply_filters( 'woocommerce_reports_screen_ids', array( $wc_screen_id . '_page_wc_reports', 'dashboard' ) ) ) ) {
wp_enqueue_script( 'wc-reports', WC()->plugin_url() . '/assets/js/admin/reports' . $suffix . '.js', array( 'jquery', 'jquery-ui-datepicker' ), '1.0' );
wp_enqueue_script( 'flot', WC()->plugin_url() . '/assets/js/admin/jquery.flot' . $suffix . '.js', array( 'jquery' ), '1.0' );
wp_enqueue_script( 'flot-resize', WC()->plugin_url() . '/assets/js/admin/jquery.flot.resize' . $suffix . '.js', array('jquery', 'flot'), '1.0' );

View File

@ -0,0 +1,208 @@
<?php
/**
* Admin Dashboard
*
* @author WooThemes
* @category Admin
* @package WooCommerce/Admin
* @version 2.1.0
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
if ( ! class_exists( 'WC_Admin_Dashboard' ) ) :
/**
* WC_Admin_Dashboard Class
*/
class WC_Admin_Dashboard {
/**
* Hook in tabs.
*/
public function __construct() {
// Only hook in admin parts if the user has admin access
if ( current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'manage_woocommerce' ) || current_user_can( 'publish_shop_orders' ) )
add_action( 'wp_dashboard_setup', array( $this, 'init' ) );
}
/**
* Init dashboard widgets
*/
public function init() {
if ( current_user_can( 'publish_shop_orders' ) ) {
wp_add_dashboard_widget( 'woocommerce_dashboard_recent_reviews', __( 'WooCommerce Recent Reviews', 'woocommerce' ), array( $this, 'recent_reviews' ) );
}
wp_add_dashboard_widget( 'woocommerce_dashboard_status', __( 'WooCommerce Status', 'woocommerce' ), array( $this, 'status_widget' ) );
}
/**
* Show status widget
*/
public function status_widget() {
global $wpdb;
include_once( 'reports/class-wc-admin-report.php' );
$reports = new WC_Admin_Report();
// Get sales
$sales = $wpdb->get_var( "SELECT SUM( postmeta.meta_value ) FROM {$wpdb->posts} as posts
LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
LEFT JOIN {$wpdb->terms} AS term USING( term_id )
LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
WHERE posts.post_type = 'shop_order'
AND posts.post_status = 'publish'
AND tax.taxonomy = 'shop_order_status'
AND term.slug IN ( 'completed', 'processing', 'on-hold' )
AND postmeta.meta_key = '_order_total'
AND posts.post_date > '" . date( 'Y-m-d', strtotime( 'first day of this month', current_time('timestamp') ) ) . "'
AND posts.post_date < '" . date( 'Y-m-d', current_time('timestamp') ) . "'
" );
// Get top seller
$top_seller = $wpdb->get_row( "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id
FROM {$wpdb->posts} as posts
LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
LEFT JOIN {$wpdb->terms} AS term USING( term_id )
LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
WHERE posts.post_type = 'shop_order'
AND posts.post_status = 'publish'
AND tax.taxonomy = 'shop_order_status'
AND term.slug IN ( 'completed', 'processing', 'on-hold' )
AND order_item_meta.meta_key = '_qty'
AND order_item_meta_2.meta_key = '_product_id'
AND posts.post_date > '" . date( 'Y-m-d', strtotime( 'first day of this month', current_time('timestamp') ) ) . "'
AND posts.post_date < '" . date( 'Y-m-d', current_time('timestamp') ) . "'
GROUP BY product_id
ORDER BY qty DESC
LIMIT 1
" );
// Counts
$on_hold_count = get_term_by( 'slug', 'on-hold', 'shop_order_status' )->count;
$processing_count = get_term_by( 'slug', 'processing', 'shop_order_status' )->count;
// Get products using a query - this is too advanced for get_posts :(
$stock = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
$nostock = absint( max( get_option( 'woocommerce_notify_no_stock_amount' ), 0 ) );
$query_from = "FROM {$wpdb->posts} as posts
INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id
WHERE 1=1
AND posts.post_type IN ('product', 'product_variation')
AND posts.post_status = 'publish'
AND (
postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND CAST(postmeta.meta_value AS SIGNED) > '{$nostock}' AND postmeta.meta_value != ''
)
AND (
( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' )
)
";
$lowinstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) );
$query_from = "FROM {$wpdb->posts} as posts
INNER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
INNER JOIN {$wpdb->postmeta} AS postmeta2 ON posts.ID = postmeta2.post_id
WHERE 1=1
AND posts.post_type IN ('product', 'product_variation')
AND posts.post_status = 'publish'
AND (
postmeta.meta_key = '_stock' AND CAST(postmeta.meta_value AS SIGNED) <= '{$stock}' AND postmeta.meta_value != ''
)
AND (
( postmeta2.meta_key = '_manage_stock' AND postmeta2.meta_value = 'yes' ) OR ( posts.post_type = 'product_variation' )
)
";
$outofstock_count = absint( $wpdb->get_var( "SELECT COUNT( DISTINCT posts.ID ) {$query_from};" ) );
?>
<ul class="wc_status_list">
<li class="sales-this-month">
<a href="<?php echo admin_url( 'admin.php?page=wc_reports&tab=orders&range=month' ); ?>">
<?php echo $reports->sales_sparkline( '', date( 'd', current_time('timestamp') ) ); ?>
<?php printf( __( "<strong>%s</strong> sales this month", 'woocommerce' ), woocommerce_price( $sales ) ); ?>
</a>
</li>
<?php if ( $top_seller->qty ) : ?>
<li class="best-seller-this-month">
<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 ); ?>">
<?php echo $reports->sales_sparkline( $top_seller->product_id, date( 'd', current_time('timestamp') ), 'count' ); ?>
<?php printf( __( "%s top seller this month (sold %d)", 'woocommerce' ), "<strong>" . get_the_title( $top_seller->product_id ) . "</strong>", $top_seller->qty ); ?>
</a>
</li>
<?php endif; ?>
<li class="processing-orders">
<a href="<?php echo admin_url( 'edit.php?s&post_status=all&post_type=shop_order&shop_order_status=processing' ); ?>">
<?php printf( _n( "<strong>%s order</strong> awaiting processing", "<strong>%s orders</strong> are awaiting processing", $processing_count, 'woocommerce' ), $processing_count ); ?>
</a>
</li>
<li class="on-hold-orders">
<a href="<?php echo admin_url( 'edit.php?s&post_status=all&post_type=shop_order&shop_order_status=on-hold' ); ?>">
<?php printf( _n( "<strong>%s order</strong> are on-hold", "<strong>%s orders</strong> are currently on-hold", $on_hold_count, 'woocommerce' ), $on_hold_count ); ?>
</a>
</li>
<li class="low-in-stock">
<a href="<?php echo admin_url( 'admin.php?page=wc_reports&tab=stock&report=low_in_stock' ); ?>">
<?php printf( _n( "<strong>%s product</strong> low in stock", "<strong>%s products</strong> are low in stock", $lowinstock_count, 'woocommerce' ), $lowinstock_count ); ?>
</a>
</li>
<li class="out-of-stock">
<a href="<?php echo admin_url( 'admin.php?page=wc_reports&tab=stock&report=out_of_stock' ); ?>">
<?php printf( _n( "<strong>%s product</strong> out of stock", "<strong>%s products</strong> are out of stock", $outofstock_count, 'woocommerce' ), $outofstock_count ); ?>
</a>
</li>
</ul>
<?php
}
/**
* Recent reviews widget
*/
public function recent_reviews() {
global $wpdb;
$comments = $wpdb->get_results( "SELECT *, SUBSTRING(comment_content,1,100) AS comment_excerpt
FROM $wpdb->comments
LEFT JOIN $wpdb->posts ON ($wpdb->comments.comment_post_ID = $wpdb->posts.ID)
WHERE comment_approved = '1'
AND comment_type = ''
AND post_password = ''
AND post_type = 'product'
ORDER BY comment_date_gmt DESC
LIMIT 8" );
if ( $comments ) {
echo '<ul>';
foreach ( $comments as $comment ) {
echo '<li>';
echo get_avatar( $comment->comment_author, '32' );
$rating = get_comment_meta( $comment->comment_ID, 'rating', true );
echo '<div class="star-rating" title="' . $rating . '">
<span style="width:'. ( $rating * 20 ) . '%">' . $rating . ' ' . __( 'out of 5', 'woocommerce' ) . '</span></div>';
echo '<h4 class="meta"><a href="' . get_permalink( $comment->ID ) . '#comment-' . absint( $comment->comment_ID ) .'">' . esc_html__( $comment->post_title ) . '</a> reviewed by ' . esc_html( $comment->comment_author ) .'</h4>';
echo '<blockquote>' . wp_kses_data( $comment->comment_excerpt ) . ' [...]</blockquote></li>';
}
echo '</ul>';
} else {
echo '<p>' . __( 'There are no product reviews yet.', 'woocommerce' ) . '</p>';
}
}
}
endif;
return new WC_Admin_Dashboard();

View File

@ -82,7 +82,7 @@ class WC_Admin {
switch ( $screen->id ) {
case 'dashboard' :
include_once( 'woocommerce-admin-dashboard.php' );
include( 'class-wc-admin-dashboard.php' );
break;
case 'users' :
case 'user' :

View File

@ -113,14 +113,16 @@ class WC_Admin_Report {
if ( ! is_array( $value ) )
continue;
$key = is_array( $value['meta_key'] ) ? $value['meta_key'][0] : $value['meta_key'];
if ( isset( $value['type'] ) && $value['type'] == 'order_item_meta' ) {
$joins["order_items"] = "LEFT JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_id";
$joins["order_item_meta_{$value['meta_key']}"] = "LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$value['meta_key']} ON order_items.order_item_id = order_item_meta_{$value['meta_key']}.order_item_id";
$joins["order_item_meta_{$key}"] = "LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$key} ON order_items.order_item_id = order_item_meta_{$key}.order_item_id";
} else {
// If we have a where clause for meta, join the postmeta table
$joins["meta_{$value['meta_key']}"] = "LEFT JOIN {$wpdb->postmeta} AS meta_{$value['meta_key']} ON posts.ID = meta_{$value['meta_key']}.post_id";
$joins["meta_{$key}"] = "LEFT JOIN {$wpdb->postmeta} AS meta_{$key} ON posts.ID = meta_{$key}.post_id";
}
}
}
@ -163,6 +165,8 @@ class WC_Admin_Report {
if ( ! is_array( $value ) )
continue;
$key = is_array( $value['meta_key'] ) ? $value['meta_key'][0] : $value['meta_key'];
if ( strtolower( $value['operator'] ) == 'in' ) {
if ( is_array( $value['meta_value'] ) )
$value['meta_value'] = implode( "','", $value['meta_value'] );
@ -177,11 +181,19 @@ class WC_Admin_Report {
$query['where'] .= ' ' . $relation;
if ( isset( $value['type'] ) && $value['type'] == 'order_item_meta' ) {
$query['where'] .= " ( order_item_meta_{$value['meta_key']}.meta_key = '{$value['meta_key']}'";
$query['where'] .= " AND order_item_meta_{$value['meta_key']}.meta_value {$where_value} )";
if ( is_array( $value['meta_key'] ) )
$query['where'] .= " ( order_item_meta_{$key}.meta_key IN ('" . implode( "','", $value['meta_key'] ) . "')";
else
$query['where'] .= " ( order_item_meta_{$key}.meta_key = '{$value['meta_key']}'";
$query['where'] .= " AND order_item_meta_{$key}.meta_value {$where_value} )";
} else {
$query['where'] .= " ( meta_{$value['meta_key']}.meta_key = '{$value['meta_key']}'";
$query['where'] .= " AND meta_{$value['meta_key']}.meta_value {$where_value} )";
if ( is_array( $value['meta_key'] ) )
$query['where'] .= " ( meta_{$key}.meta_key IN ('" . implode( "','", $value['meta_key'] ) . "')";
else
$query['where'] .= " ( meta_{$key}.meta_key = '{$value['meta_key']}'";
$query['where'] .= " AND meta_{$key}.meta_value {$where_value} )";
}
}
}
@ -296,60 +308,90 @@ class WC_Admin_Report {
/**
* Prepares a sparkline to show sales in the last X days
*
* @param int $id
* @param int $days
* @param int $id ID of the product to show. Blank to get all orders.
* @param int $days Days of stats to get.
* @param string $type Type of sparkline to get. Ignored if ID is not set.
* @return string
*/
public function sales_sparkline( $id, $days, $type ) {
$meta_key = $type == 'sales' ? '_line_total' : '_qty';
public function sales_sparkline( $id = '', $days = 7, $type = 'sales' ) {
$data = $this->get_order_report_data( array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id'
if ( $id ) {
$meta_key = $type == 'sales' ? '_line_total' : '_qty';
$data = $this->get_order_report_data( array(
'data' => array(
'_product_id' => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id'
),
$meta_key => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'sparkline_value'
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date'
),
),
$meta_key => array(
'type' => 'order_item_meta',
'order_item_type' => 'line_item',
'function' => 'SUM',
'name' => 'order_item_value'
'where' => array(
array(
'key' => 'post_date',
'value' => date( 'Y-m-d', strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ) ),
'operator' => '>'
),
array(
'key' => 'order_item_meta__product_id.meta_value',
'value' => $id,
'operator' => '='
)
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date'
'group_by' => 'YEAR(post_date), MONTH(post_date), DAY(post_date)',
'query_type' => 'get_results',
'filter_range' => false
) );
} else {
$data = $this->get_order_report_data( array(
'data' => array(
'_order_total' => array(
'type' => 'meta',
'function' => 'SUM',
'name' => 'sparkline_value'
),
'post_date' => array(
'type' => 'post_data',
'function' => '',
'name' => 'post_date'
),
),
),
'where' => array(
array(
'key' => 'post_date',
'value' => date( 'Y-m-d', strtotime( 'midnight -7 days', current_time( 'timestamp' ) ) ),
'operator' => '>'
'where' => array(
array(
'key' => 'post_date',
'value' => date( 'Y-m-d', strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ) ),
'operator' => '>'
)
),
array(
'key' => 'order_item_meta__product_id.meta_value',
'value' => $id,
'operator' => '='
)
),
'group_by' => 'YEAR(post_date), MONTH(post_date), DAY(post_date)',
'query_type' => 'get_results',
'filter_range' => false
) );
'group_by' => 'YEAR(post_date), MONTH(post_date), DAY(post_date)',
'query_type' => 'get_results',
'filter_range' => false
) );
}
$total = 0;
foreach ( $data as $d )
$total += $d->order_item_value;
$total += $d->sparkline_value;
if ( $type == 'sales' ) {
$tooltip = sprintf( __( 'Sold %s worth in the last %d days', 'woocommerce' ), strip_tags( woocommerce_price( $total ) ), $days );
} else {
$tooltip = sprintf( _n( 'Sold 1 time in the last %d days', 'Sold %d times in the last %d days', $total, 'woocommerce' ), $total, $days );
$tooltip = sprintf( _n( 'Sold 1 item in the last %d days', 'Sold %d items in the last %d days', $total, 'woocommerce' ), $total, $days );
}
$sparkline_data = array_values( $this->prepare_chart_data( $data, 'post_date', 'order_item_value', $days - 1, strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ), 'day' ) );
$sparkline_data = array_values( $this->prepare_chart_data( $data, 'post_date', 'sparkline_value', $days - 1, strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ), 'day' ) );
return '<span class="wc_sparkline ' . ( $type == 'sales' ? 'lines' : 'bars' ) . ' tips" data-color="#777" data-tip="' . $tooltip . '" data-barwidth="' . 60*60*16*1000 . '" data-sparkline="' . esc_attr( json_encode( $sparkline_data ) ) . '"></span>';
}

View File

@ -39,23 +39,18 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id'
),
)
),
'where_meta' => array(
'relation' => 'OR',
array(
'type' => 'order_item_meta',
'meta_key' => '_product_id',
'meta_value' => $this->product_ids,
'operator' => 'IN'
),
array(
'type' => 'order_item_meta',
'meta_key' => '_variation_id',
'meta_key' => array( '_product_id', '_variation_id' ),
'meta_value' => $this->product_ids,
'operator' => 'IN'
)
),
'group_by' => 'product_id',
'query_type' => 'get_var',
'filter_range' => true
) );
@ -78,13 +73,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'relation' => 'OR',
array(
'type' => 'order_item_meta',
'meta_key' => '_product_id',
'meta_value' => $this->product_ids,
'operator' => 'IN'
),
array(
'type' => 'order_item_meta',
'meta_key' => '_variation_id',
'meta_key' => array( '_product_id', '_variation_id' ),
'meta_value' => $this->product_ids,
'operator' => 'IN'
)
@ -293,10 +282,10 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'name' => 'order_item_qty'
)
),
'order_by' => 'order_item_qty DESC',
'group_by' => 'product_id',
'limit' => 12,
'query_type' => 'get_results',
'order_by' => 'order_item_qty DESC',
'group_by' => 'product_id',
'limit' => 12,
'query_type' => 'get_results',
'filter_range' => true
) );
@ -305,7 +294,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
<td class="count">' . $product->order_item_qty . '</td>
<td class="name"><a href="' . add_query_arg( 'product_ids', $product->product_id ) . '">' . get_the_title( $product->product_id ) . '</a></td>
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 14, 'count' ) . '</td>
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'count' ) . '</td>
</tr>';
}
} else {
@ -333,10 +322,10 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'name' => 'order_item_total'
)
),
'order_by' => 'order_item_total DESC',
'group_by' => 'product_id',
'limit' => 12,
'query_type' => 'get_results',
'order_by' => 'order_item_total DESC',
'group_by' => 'product_id',
'limit' => 12,
'query_type' => 'get_results',
'filter_range' => true
) );
@ -345,7 +334,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
echo '<tr class="' . ( in_array( $product->product_id, $this->product_ids ) ? 'active' : '' ) . '">
<td class="count">' . woocommerce_price( $product->order_item_total ) . '</td>
<td class="name"><a href="' . add_query_arg( 'product_ids', $product->product_id ) . '">' . get_the_title( $product->product_id ) . '</a></td>
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 14, 'sales' ) . '</td>
<td class="sparkline">' . $this->sales_sparkline( $product->product_id, 7, 'sales' ) . '</td>
</tr>';
}
} else {
@ -434,13 +423,7 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'relation' => 'OR',
array(
'type' => 'order_item_meta',
'meta_key' => '_product_id',
'meta_value' => $this->product_ids,
'operator' => 'IN'
),
array(
'type' => 'order_item_meta',
'meta_key' => '_variation_id',
'meta_key' => array( '_product_id', '_variation_id' ),
'meta_value' => $this->product_ids,
'operator' => 'IN'
)
@ -469,27 +452,22 @@ class WC_Report_Sales_By_Product extends WC_Admin_Report {
'order_item_type' => 'line_item',
'function' => '',
'name' => 'product_id'
)
),
),
'where_meta' => array(
'relation' => 'OR',
array(
'type' => 'order_item_meta',
'meta_key' => '_product_id',
'meta_value' => $this->product_ids,
'operator' => 'IN'
),
array(
'type' => 'order_item_meta',
'meta_key' => '_variation_id',
'meta_key' => array( '_product_id', '_variation_id' ),
'meta_value' => $this->product_ids,
'operator' => 'IN'
)
),
'group_by' => 'product_id,' . $this->group_by_query,
'group_by' => 'product_id, ' . $this->group_by_query,
'order_by' => 'post_date ASC',
'query_type' => 'get_results',
'filter_range' => true
'filter_range' => true,
'nocache' => true
) );
// Prepare data for report

View File

@ -1,289 +0,0 @@
<?php
/**
* Functions used for displaying the WooCommerce dashboard widgets
*
* @author WooThemes
* @category Admin
* @package WooCommerce/Admin/Dashboard
* @version 1.6.4
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
// Only hook in admin parts if the user has admin access
if ( current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'manage_woocommerce' ) || current_user_can( 'publish_shop_orders' ) )
add_action( 'wp_dashboard_setup', 'woocommerce_init_dashboard_widgets' );
/**
* Init the dashboard widgets.
*
* @access public
* @return void
*/
function woocommerce_init_dashboard_widgets() {
global $current_month_offset, $the_month_num, $the_year;
$current_month_offset = 0;
if (isset($_GET['wc_sales_month'])) $current_month_offset = (int) $_GET['wc_sales_month'];
$the_month_num = date('n', strtotime('NOW '.($current_month_offset).' MONTH'));
$the_year = date('Y', strtotime('NOW '.($current_month_offset).' MONTH'));
$sales_heading = '';
if ($the_month_num!=date('m')) :
$sales_heading .= '<a href="index.php?wc_sales_month='.($current_month_offset+1).'" class="next">'.date_i18n('F', strtotime('01-'.($the_month_num+1).'-2011')).' &rarr;</a>';
endif;
$sales_heading .= '<a href="index.php?wc_sales_month='.($current_month_offset-1).'" class="previous">&larr; '.date_i18n('F', strtotime('01-'.($the_month_num-1).'-2011')).'</a><span>'.__( 'Monthly Sales', 'woocommerce' ).'</span>';
if ( current_user_can( 'publish_shop_orders' ) ) {
wp_add_dashboard_widget( 'woocommerce_dashboard_right_now', __( 'WooCommerce Right Now', 'woocommerce' ), 'woocommerce_dashboard_widget_right_now' );
wp_add_dashboard_widget( 'woocommerce_dashboard_recent_orders', __( 'WooCommerce Recent Orders', 'woocommerce' ), 'woocommerce_dashboard_recent_orders');
wp_add_dashboard_widget( 'woocommerce_dashboard_recent_reviews', __( 'WooCommerce Recent Reviews', 'woocommerce' ), 'woocommerce_dashboard_recent_reviews' );
}
if ( current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'publish_shop_orders' ) ) {
//wp_add_dashboard_widget( 'woocommerce_dashboard_sales', $sales_heading, 'woocommerce_dashboard_sales' );
}
}
/**
* WooCommerce Right Now widget.
*
* Adds a dashboard widget with shop statistics.
*
* @access public
* @return void
*/
function woocommerce_dashboard_widget_right_now() {
global $woocommerce;
$product_count = wp_count_posts( 'product' );
$product_cat_count = wp_count_terms( 'product_cat' );
$product_tag_count = wp_count_terms( 'product_tag' );
$product_attr_count = count( $woocommerce->get_helper( 'attribute' )->get_attribute_taxonomies() );
$pending_count = get_term_by( 'slug', 'pending', 'shop_order_status' )->count;
$completed_count = get_term_by( 'slug', 'completed', 'shop_order_status' )->count;
$on_hold_count = get_term_by( 'slug', 'on-hold', 'shop_order_status' )->count;
$processing_count = get_term_by( 'slug', 'processing', 'shop_order_status' )->count;
?>
<div class="table table_shop_content">
<p class="sub woocommerce_sub"><?php _e( 'Shop Content', 'woocommerce' ); ?></p>
<table>
<tr class="first">
<?php
$num = number_format_i18n( $product_count->publish );
$text = _n( 'Product', 'Products', intval( $product_count->publish ), 'woocommerce' );
$link = add_query_arg( array( 'post_type' => 'product' ), get_admin_url( null, 'edit.php' ) );
$num = '<a href="' . esc_url($link ) . '">' . esc_html( $num ) . '</a>';
$text = '<a href="' . esc_url( $link ) . '">' . esc_html( $text ) . '</a>';
?>
<td class="first b b-products"><?php echo $num; ?></td>
<td class="t products"><?php echo $text; ?></td>
</tr>
<tr>
<?php
$num = number_format_i18n( $product_cat_count );
$text = _n( 'Product Category', 'Product Categories', $product_cat_count, 'woocommerce' );
$link = add_query_arg( array( 'taxonomy' => 'product_cat', 'post_type' => 'product' ), get_admin_url( null, 'edit-tags.php' ) );
$num = '<a href="' . esc_url($link ) . '">' . esc_html( $num ) . '</a>';
$text = '<a href="' . esc_url( $link ) . '">' . esc_html( $text ) . '</a>';
?>
<td class="first b b-product_cats"><?php echo $num; ?></td>
<td class="t product_cats"><?php echo $text; ?></td>
</tr>
<tr>
<?php
$num = number_format_i18n( $product_tag_count );
$text = _n( 'Product Tag', 'Product Tags', $product_tag_count, 'woocommerce' );
$link = add_query_arg( array( 'taxonomy' => 'product_tag', 'post_type' => 'product' ), get_admin_url( null, 'edit-tags.php' ) );
$num = '<a href="' . esc_url($link ) . '">' . esc_html( $num ) . '</a>';
$text = '<a href="' . esc_url( $link ) . '">' . esc_html( $text ) . '</a>';
?>
<td class="first b b-product_tag"><?php echo $num; ?></td>
<td class="t product_tag"><?php echo $text; ?></td>
</tr>
<tr>
<?php
$num = number_format_i18n( $product_attr_count );
$text = _n( 'Attribute', 'Attributes', $product_attr_count, 'woocommerce' );
$link = add_query_arg( array( 'page' => 'woocommerce_attributes' ), get_admin_url( null, 'admin.php' ) );
$num = '<a href="' . esc_url($link ) . '">' . esc_html( $num ) . '</a>';
$text = '<a href="' . esc_url( $link ) . '">' . esc_html( $text ) . '</a>';
?>
<td class="first b b-attributes"><?php echo $num; ?></td>
<td class="t attributes"><?php echo $text; ?></td>
</tr>
<?php do_action( 'woocommerce_right_now_shop_content_table_end' ); ?>
</table>
</div>
<div class="table table_orders">
<p class="sub woocommerce_sub"><?php _e( 'Orders', 'woocommerce' ); ?></p>
<table>
<tr class="first">
<?php
$num = number_format_i18n( $pending_count );
$text = __( 'Pending', 'woocommerce' );
$link = add_query_arg( array( 'post_type' => 'shop_order', 'shop_order_status' => 'pending' ), get_admin_url( null, 'edit.php' ) );
$num = '<a href="' . esc_url($link ) . '">' . esc_html( $num ) . '</a>';
$text = '<a href="' . esc_url( $link ) . '">' . esc_html( $text ) . '</a>';
?>
<td class="b b-pending"><?php echo $num; ?></td>
<td class="last t pending"><?php echo $text; ?></td>
</tr>
<tr>
<?php
$num = number_format_i18n( $on_hold_count );
$text = __( 'On-Hold', 'woocommerce' );
$link = add_query_arg( array( 'post_type' => 'shop_order', 'shop_order_status' => 'on-hold' ), get_admin_url( null, 'edit.php' ) );
$num = '<a href="' . esc_url($link ) . '">' . esc_html( $num ) . '</a>';
$text = '<a href="' . esc_url( $link ) . '">' . esc_html( $text ) . '</a>';
?>
<td class="b b-on-hold"><?php echo $num; ?></td>
<td class="last t on-hold"><?php echo $text; ?></td>
</tr>
<tr>
<?php
$num = number_format_i18n( $processing_count );
$text = __( 'Processing', 'woocommerce' );
$link = add_query_arg( array( 'post_type' => 'shop_order', 'shop_order_status' => 'processing' ), get_admin_url( null, 'edit.php' ) );
$num = '<a href="' . esc_url($link ) . '">' . esc_html( $num ) . '</a>';
$text = '<a href="' . esc_url( $link ) . '">' . esc_html( $text ) . '</a>';
?>
<td class="b b-processing"><?php echo $num; ?></td>
<td class="last t processing"><?php echo $text; ?></td>
</tr>
<tr>
<?php
$num = number_format_i18n( $completed_count );
$text = __( 'Completed', 'woocommerce' );
$link = add_query_arg( array( 'post_type' => 'shop_order', 'shop_order_status' => 'completed' ), get_admin_url( null, 'edit.php' ) );
$num = '<a href="' . esc_url($link ) . '">' . esc_html( $num ) . '</a>';
$text = '<a href="' . esc_url( $link ) . '">' . esc_html( $text ) . '</a>';
?>
<td class="b b-completed"><?php echo $num; ?></td>
<td class="last t completed"><?php echo $text; ?></td>
</tr>
<?php do_action( 'woocommerce_right_now_orders_table_end' ); ?>
</table>
</div>
<div class="versions">
<p id="wp-version-message">
<?php printf( __( 'You are using <strong>WooCommerce %s</strong>.', 'woocommerce' ), $woocommerce->version ); ?>
</p>
</div>
<?php
}
/**
* Recent orders widget
*
* @access public
* @return void
*/
function woocommerce_dashboard_recent_orders() {
$args = array(
'numberposts' => 8,
'orderby' => 'post_date',
'order' => 'DESC',
'post_type' => 'shop_order',
'post_status' => 'publish'
);
$orders = get_posts( $args );
if ($orders) :
echo '<ul class="recent-orders">';
foreach ($orders as $order) :
$this_order = new WC_Order( $order->ID );
echo '
<li>
<span class="order-status ' . sanitize_title( $this_order->status ) . '">' . ucwords( __( $this_order->status, 'woocommerce' ) ) . '</span> <a href="' . admin_url( 'post.php?post=' . $order->ID ) . '&action=edit">' . get_the_time( __( 'l jS \of F Y h:i:s A', 'woocommerce' ), $order->ID ) . '</a><br />
<small>' . $this_order->get_item_count() . ' ' . _n( 'item', 'items', $this_order->get_item_count(), 'woocommerce' ) . ' <span class="order-cost">'.__('Total:', 'woocommerce' ) . ' ' . woocommerce_price( $this_order->order_total ).'</span></small>
</li>';
endforeach;
echo '</ul>';
else:
echo '<p>' . __( 'There are no product orders yet.', 'woocommerce' ) . '</p>';
endif;
}
/**
* Recent reviews widget
*
* @access public
* @return void
*/
function woocommerce_dashboard_recent_reviews() {
global $wpdb;
$comments = $wpdb->get_results( "SELECT *, SUBSTRING(comment_content,1,100) AS comment_excerpt
FROM $wpdb->comments
LEFT JOIN $wpdb->posts ON ($wpdb->comments.comment_post_ID = $wpdb->posts.ID)
WHERE comment_approved = '1'
AND comment_type = ''
AND post_password = ''
AND post_type = 'product'
ORDER BY comment_date_gmt DESC
LIMIT 5" );
if ( $comments ) {
echo '<ul>';
foreach ( $comments as $comment ) {
echo '<li>';
echo get_avatar( $comment->comment_author, '32' );
$rating = get_comment_meta( $comment->comment_ID, 'rating', true );
echo '<div class="star-rating" title="' . $rating . '">
<span style="width:'. ( $rating * 20 ) . '%">' . $rating . ' ' . __( 'out of 5', 'woocommerce' ) . '</span></div>';
echo '<h4 class="meta"><a href="' . get_permalink( $comment->ID ) . '#comment-' . absint( $comment->comment_ID ) .'">' . esc_html__( $comment->post_title ) . '</a> reviewed by ' . esc_html( $comment->comment_author ) .'</h4>';
echo '<blockquote>' . wp_kses_data( $comment->comment_excerpt ) . ' [...]</blockquote></li>';
}
echo '</ul>';
} else {
echo '<p>' . __( 'There are no product reviews yet.', 'woocommerce' ) . '</p>';
}
}