관리-도구
편집 파일: class-kkart-admin-dashboard.php
<?php /** * Admin Dashboard * * @package Kkart\Admin * @version 2.1.0 */ use Automattic\Jetpack\Constants; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } if ( ! class_exists( 'KKART_Admin_Dashboard', false ) ) : /** * KKART_Admin_Dashboard Class. */ class KKART_Admin_Dashboard { /** * Hook in tabs. */ public function __construct() { // Only hook in admin parts if the user has admin access. if ( current_user_can( 'view_kkart_reports' ) || current_user_can( 'manage_kkart' ) || current_user_can( 'publish_shop_orders' ) ) { // If on network admin, only load the widget that works in that context and skip the rest. if ( is_multisite() && is_network_admin() ) { add_action( 'wp_network_dashboard_setup', array( $this, 'register_network_order_widget' ) ); } else { add_action( 'wp_dashboard_setup', array( $this, 'init' ) ); } } } /** * Init dashboard widgets. */ public function init() { // Reviews Widget. if ( current_user_can( 'publish_shop_orders' ) && post_type_supports( 'product', 'comments' ) ) { wp_add_dashboard_widget( 'kkart_dashboard_recent_reviews', __( 'Kkart Recent Reviews', 'kkart' ), array( $this, 'recent_reviews' ) ); } wp_add_dashboard_widget( 'kkart_dashboard_status', __( 'Kkart Status', 'kkart' ), array( $this, 'status_widget' ) ); // Network Order Widget. if ( is_multisite() && is_main_site() ) { $this->register_network_order_widget(); } } /** * Register the network order dashboard widget. */ public function register_network_order_widget() { wp_add_dashboard_widget( 'kkart_network_orders', __( 'Kkart Network Orders', 'kkart' ), array( $this, 'network_orders' ) ); } /** * Get top seller from DB. * * @return object */ private function get_top_seller() { global $wpdb; $query = array(); $query['fields'] = "SELECT SUM( order_item_meta.meta_value ) as qty, order_item_meta_2.meta_value as product_id FROM {$wpdb->posts} as posts"; $query['join'] = "INNER JOIN {$wpdb->prefix}kkart_order_items AS order_items ON posts.ID = order_id "; $query['join'] .= "INNER JOIN {$wpdb->prefix}kkart_order_itemmeta AS order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id "; $query['join'] .= "INNER JOIN {$wpdb->prefix}kkart_order_itemmeta AS order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id "; $query['where'] = "WHERE posts.post_type IN ( '" . implode( "','", kkart_get_order_types( 'order-count' ) ) . "' ) "; $query['where'] .= "AND posts.post_status IN ( 'kkart-" . implode( "','kkart-", apply_filters( 'kkart_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) "; $query['where'] .= "AND order_item_meta.meta_key = '_qty' "; $query['where'] .= "AND order_item_meta_2.meta_key = '_product_id' "; $query['where'] .= "AND posts.post_date >= '" . gmdate( 'Y-m-01', current_time( 'timestamp' ) ) . "' "; $query['where'] .= "AND posts.post_date <= '" . gmdate( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) . "' "; $query['groupby'] = 'GROUP BY product_id'; $query['orderby'] = 'ORDER BY qty DESC'; $query['limits'] = 'LIMIT 1'; return $wpdb->get_row( implode( ' ', apply_filters( 'kkart_dashboard_status_widget_top_seller_query', $query ) ) ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared } /** * Get sales report data. * * @return object */ private function get_sales_report_data() { include_once dirname( __FILE__ ) . '/reports/class-kkart-report-sales-by-date.php'; $sales_by_date = new KKART_Report_Sales_By_Date(); $sales_by_date->start_date = strtotime( gmdate( 'Y-m-01', current_time( 'timestamp' ) ) ); $sales_by_date->end_date = strtotime( gmdate( 'Y-m-d', current_time( 'timestamp' ) ) ); $sales_by_date->chart_groupby = 'day'; $sales_by_date->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)'; return $sales_by_date->get_report_data(); } /** * Show status widget. */ public function status_widget() { include_once dirname( __FILE__ ) . '/reports/class-kkart-admin-report.php'; $reports = new KKART_Admin_Report(); echo '<ul class="kkart_status_list">'; if ( current_user_can( 'view_kkart_reports' ) ) { $report_data = $this->get_sales_report_data(); if ( $report_data ) { ?> <li class="sales-this-month"> <a href="<?php echo esc_url( admin_url( 'admin.php?page=kkart-reports&tab=orders&range=month' ) ); ?>"> <?php echo $reports->sales_sparkline( '', max( 7, gmdate( 'd', current_time( 'timestamp' ) ) ) ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?> <?php printf( /* translators: %s: net sales */ esc_html__( '%s net sales this month', 'kkart' ), '<strong>' . kkart_price( $report_data->net_sales ) . '</strong>' ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?> </a> </li> <?php } $top_seller = $this->get_top_seller(); if ( $top_seller && $top_seller->qty ) { ?> <li class="best-seller-this-month"> <a href="<?php echo esc_url( admin_url( 'admin.php?page=kkart-reports&tab=orders&report=sales_by_product&range=month&product_ids=' . $top_seller->product_id ) ); ?>"> <?php echo $reports->sales_sparkline( $top_seller->product_id, max( 7, gmdate( 'd', current_time( 'timestamp' ) ) ), 'count' ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?> <?php printf( /* translators: 1: top seller product title 2: top seller quantity */ esc_html__( '%1$s top seller this month (sold %2$d)', 'kkart' ), '<strong>' . get_the_title( $top_seller->product_id ) . '</strong>', $top_seller->qty ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?> </a> </li> <?php } } $this->status_widget_order_rows(); $this->status_widget_stock_rows(); do_action( 'kkart_after_dashboard_status_widget', $reports ); echo '</ul>'; } /** * Show order data is status widget. */ private function status_widget_order_rows() { if ( ! current_user_can( 'edit_shop_orders' ) ) { return; } $on_hold_count = 0; $processing_count = 0; foreach ( kkart_get_order_types( 'order-count' ) as $type ) { $counts = (array) wp_count_posts( $type ); $on_hold_count += isset( $counts['kkart-on-hold'] ) ? $counts['kkart-on-hold'] : 0; $processing_count += isset( $counts['kkart-processing'] ) ? $counts['kkart-processing'] : 0; } ?> <li class="processing-orders"> <a href="<?php echo esc_url( admin_url( 'edit.php?post_status=kkart-processing&post_type=shop_order' ) ); ?>"> <?php printf( /* translators: %s: order count */ _n( '<strong>%s order</strong> awaiting processing', '<strong>%s orders</strong> awaiting processing', $processing_count, 'kkart' ), $processing_count ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?> </a> </li> <li class="on-hold-orders"> <a href="<?php echo esc_url( admin_url( 'edit.php?post_status=kkart-on-hold&post_type=shop_order' ) ); ?>"> <?php printf( /* translators: %s: order count */ _n( '<strong>%s order</strong> on-hold', '<strong>%s orders</strong> on-hold', $on_hold_count, 'kkart' ), $on_hold_count ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?> </a> </li> <?php } /** * Show stock data is status widget. */ private function status_widget_stock_rows() { global $wpdb; // Requires lookup table added in 3.6. if ( version_compare( get_option( 'kkart_db_version', null ), '3.6', '<' ) ) { return; } $stock = absint( max( get_option( 'kkart_notify_low_stock_amount' ), 1 ) ); $nostock = absint( max( get_option( 'kkart_notify_no_stock_amount' ), 0 ) ); $transient_name = 'kkart_low_stock_count'; $lowinstock_count = get_transient( $transient_name ); if ( false === $lowinstock_count ) { /** * Status widget low in stock count pre query. * * @since 4.3.0 * @param null|string $low_in_stock_count Low in stock count, by default null. * @param int $stock Low stock amount. * @param int $nostock No stock amount */ $lowinstock_count = apply_filters( 'kkart_status_widget_low_in_stock_count_pre_query', null, $stock, $nostock ); if ( is_null( $lowinstock_count ) ) { $lowinstock_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( product_id ) FROM {$wpdb->kkart_product_meta_lookup} AS lookup INNER JOIN {$wpdb->posts} as posts ON lookup.product_id = posts.ID WHERE stock_quantity <= %d AND stock_quantity > %d AND posts.post_status = 'publish'", $stock, $nostock ) ); } set_transient( $transient_name, (int) $lowinstock_count, DAY_IN_SECONDS * 30 ); } $transient_name = 'kkart_outofstock_count'; $outofstock_count = get_transient( $transient_name ); if ( false === $outofstock_count ) { /** * Status widget out of stock count pre query. * * @since 4.3.0 * @param null|string $outofstock_count Out of stock count, by default null. * @param int $nostock No stock amount */ $outofstock_count = apply_filters( 'kkart_status_widget_out_of_stock_count_pre_query', null, $nostock ); if ( is_null( $outofstock_count ) ) { $outofstock_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( product_id ) FROM {$wpdb->kkart_product_meta_lookup} AS lookup INNER JOIN {$wpdb->posts} as posts ON lookup.product_id = posts.ID WHERE stock_quantity <= %d AND posts.post_status = 'publish'", $nostock ) ); } set_transient( $transient_name, (int) $outofstock_count, DAY_IN_SECONDS * 30 ); } ?> <li class="low-in-stock"> <a href="<?php echo esc_url( admin_url( 'admin.php?page=kkart-reports&tab=stock&report=low_in_stock' ) ); ?>"> <?php printf( /* translators: %s: order count */ _n( '<strong>%s product</strong> low in stock', '<strong>%s products</strong> low in stock', $lowinstock_count, 'kkart' ), $lowinstock_count ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?> </a> </li> <li class="out-of-stock"> <a href="<?php echo esc_url( admin_url( 'admin.php?page=kkart-reports&tab=stock&report=out_of_stock' ) ); ?>"> <?php printf( /* translators: %s: order count */ _n( '<strong>%s product</strong> out of stock', '<strong>%s products</strong> out of stock', $outofstock_count, 'kkart' ), $outofstock_count ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?> </a> </li> <?php } /** * Recent reviews widget. */ public function recent_reviews() { global $wpdb; $query_from = apply_filters( 'kkart_report_recent_reviews_query_from', "FROM {$wpdb->comments} comments LEFT JOIN {$wpdb->posts} posts ON (comments.comment_post_ID = posts.ID) WHERE comments.comment_approved = '1' AND comments.comment_type = 'review' AND posts.post_password = '' AND posts.post_type = 'product' AND comments.comment_parent = 0 ORDER BY comments.comment_date_gmt DESC LIMIT 5" ); $comments = $wpdb->get_results( "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 ); if ( $comments ) { echo '<ul>'; foreach ( $comments as $comment ) { echo '<li>'; echo get_avatar( $comment->comment_author_email, '32' ); $rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) ); /* translators: %s: rating */ echo '<div class="star-rating"><span style="width:' . esc_attr( $rating * 20 ) . '%">' . sprintf( esc_html__( '%s out of 5', 'kkart' ), esc_html( $rating ) ) . '</span></div>'; /* translators: %s: review author */ echo '<h4 class="meta"><a href="' . esc_url( get_permalink( $comment->ID ) ) . '#comment-' . esc_attr( absint( $comment->comment_ID ) ) . '">' . esc_html( apply_filters( 'kkart_admin_dashboard_recent_reviews', $comment->post_title, $comment ) ) . '</a> ' . sprintf( esc_html__( 'reviewed by %s', 'kkart' ), esc_html( $comment->comment_author ) ) . '</h4>'; echo '<blockquote>' . wp_kses_data( $comment->comment_content ) . '</blockquote></li>'; } echo '</ul>'; } else { echo '<p>' . esc_html__( 'There are no product reviews yet.', 'kkart' ) . '</p>'; } } /** * Network orders widget. */ public function network_orders() { $suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min'; $version = Constants::get_constant( 'KKART_VERSION' ); wp_enqueue_style( 'kkart-network-orders', KKART()->plugin_url() . '/assets/css/network-order-widget.css', array(), $version ); wp_enqueue_script( 'kkart-network-orders', KKART()->plugin_url() . '/assets/js/admin/network-orders' . $suffix . '.js', array( 'jquery', 'underscore' ), $version, true ); $user = wp_get_current_user(); $blogs = get_blogs_of_user( $user->ID ); $blog_ids = wp_list_pluck( $blogs, 'userblog_id' ); wp_localize_script( 'kkart-network-orders', 'kkart_network_orders', array( 'nonce' => wp_create_nonce( 'wp_rest' ), 'sites' => array_values( $blog_ids ), 'order_endpoint' => get_rest_url( null, 'kkart/v3/orders/network' ), ) ); ?> <div class="post-type-shop_order"> <div id="kkart-network-order-table-loading" class="kkart-network-order-table-loading is-active"> <p> <span class="spinner is-active"></span> <?php esc_html_e( 'Loading network orders', 'kkart' ); ?> </p> </div> <table id="kkart-network-order-table" class="kkart-network-order-table"> <thead> <tr> <td><?php esc_html_e( 'Order', 'kkart' ); ?></td> <td><?php esc_html_e( 'Status', 'kkart' ); ?></td> <td><?php esc_html_e( 'Total', 'kkart' ); ?></td> </tr> </thead> <tbody id="network-orders-tbody"> </tbody> </table> <div id="kkart-network-orders-no-orders" class="kkart-network-orders-no-orders"> <p> <?php esc_html_e( 'No orders found', 'kkart' ); ?> </p> </div> <?php // @codingStandardsIgnoreStart ?> <script type="text/template" id="network-orders-row-template"> <tr> <td> <a href="<%- edit_url %>" class="order-view"><strong>#<%- number %> <%- customer %></strong></a> <br> <em> <%- blog.blogname %> </em> </td> <td> <mark class="order-status status-<%- status %>"><span><%- status_name %></span></mark> </td> <td> <%= formatted_total %> </td> </tr> </script> <?php // @codingStandardsIgnoreEnd ?> </div> <?php } } endif; return new KKART_Admin_Dashboard();