installed plugin Easy Digital Downloads
version 3.1.0.3
This commit is contained in:
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Bar Dataset class
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports\Data\Charts
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Charts\v2;
|
||||
|
||||
/**
|
||||
* Represents a manifestation of a ChartJS v2 bar chart dataset in PHP form.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see Dataset
|
||||
* @see Manifest
|
||||
*/
|
||||
class Bar_Dataset extends Dataset {
|
||||
|
||||
/**
|
||||
* Represents the list of fields for a given dataset.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = array(
|
||||
'borderSkipped', 'hoverBackgroundColor',
|
||||
'hoverBorderColor', 'hoverBorderWidth'
|
||||
);
|
||||
|
||||
}
|
@ -0,0 +1,340 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Dataset class
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports\Data\Charts
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Charts\v2;
|
||||
|
||||
use EDD\Reports\Data\Chart_Endpoint;
|
||||
use EDD\Utils\Error_Logger_Interface as Error_Logger;
|
||||
|
||||
/**
|
||||
* Represents the manifestation of a ChartJS v2 dataset in PHP form.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see Error_Logger_Interface
|
||||
*/
|
||||
abstract class Dataset implements Error_Logger {
|
||||
|
||||
/**
|
||||
* The ID associated with the dataset.
|
||||
*
|
||||
* Primarily used for locating associated data via the endpoint's data callback.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
private $dataset_id;
|
||||
|
||||
/**
|
||||
* Represents the list of fields for a given dataset.
|
||||
*
|
||||
* Should be defined by all sub-classes.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = array();
|
||||
|
||||
/**
|
||||
* Holds errors related to instantiating the object.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var \WP_Error
|
||||
*/
|
||||
protected $errors;
|
||||
|
||||
/**
|
||||
* Raw dataset options and data.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
private $options = array();
|
||||
|
||||
/**
|
||||
* Represents the chart endpoint the dataset is associated with.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var Chart_Endpoint
|
||||
*/
|
||||
private $endpoint;
|
||||
|
||||
/**
|
||||
* Represents the list of global fields for all datasets.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
protected $global_fields = array(
|
||||
'label', 'xAxisID', 'yAxisID', 'data',
|
||||
'backgroundColor', 'borderColor', 'borderWidth',
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets up the dataset for population.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $dataset_id Dataset ID.
|
||||
* @param Chart_Endpoint $endpoint Chart endpoint object.
|
||||
* @param array $options Dataset options.
|
||||
*/
|
||||
public function __construct( $dataset_id, $endpoint, $options ) {
|
||||
$this->setup_error_logger();
|
||||
|
||||
$this->set_id( $dataset_id );
|
||||
$this->set_endpoint( $endpoint );
|
||||
$this->validate( $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the dataset ID.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Dataset ID.
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->dataset_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dataset ID.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $dataset_id Dataset ID
|
||||
*/
|
||||
private function set_id( $dataset_id ) {
|
||||
$this->dataset_id = sanitize_key( $dataset_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the chart endpoint object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param EDD\Reports\Data\Chart_Endpoint $endpoint Chart_Endpoint object.
|
||||
*/
|
||||
private function set_endpoint( $endpoint ) {
|
||||
$this->endpoint = $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the raw dataset options.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Dataset options (raw).
|
||||
*/
|
||||
public function get_options() {
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the chart endpoint object for this dataset.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return Chart_Endpoint Chart endpoint.
|
||||
*/
|
||||
public function get_endpoint() {
|
||||
return $this->endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of local fields.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array List of local fields.
|
||||
*/
|
||||
public function get_fields() {
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of global fields.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array List of global fields.
|
||||
*/
|
||||
public function get_global_fields() {
|
||||
return $this->global_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of fields for the current dataset.
|
||||
*
|
||||
* Includes the global fields.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array List of fields available to the dataset.
|
||||
*/
|
||||
public function get_all_fields() {
|
||||
$fields = array_merge( $this->get_global_fields(), $this->get_fields() );
|
||||
|
||||
/**
|
||||
* Filters the fields available to a ChartJS graph.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $fields ChartJS fields (global and local).
|
||||
* @param Dataset $this Dataset instance.
|
||||
*/
|
||||
return apply_filters( 'edd_reports_chart_fields', $fields, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to retrieve data associated with the current dataset.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return mixed Data associated with the current dataset.
|
||||
*/
|
||||
public function get_data() {
|
||||
return $this->get_endpoint()->get_data_by_set( $this->get_id() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs validation on incoming dataset options.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $options Dataset options.
|
||||
*/
|
||||
public function validate( $options ) {
|
||||
$fields = $this->get_all_fields();
|
||||
|
||||
// Strip invalid options.
|
||||
foreach ( $options as $key => $value ) {
|
||||
if ( ! in_array( $key, $fields, true ) ) {
|
||||
unset( $options[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->get_data();
|
||||
$processed = array();
|
||||
|
||||
if ( ! empty( $data ) ) {
|
||||
|
||||
$options['data'] = $this->parse_data_for_output( $data );
|
||||
|
||||
$this->options = $options;
|
||||
|
||||
} else {
|
||||
|
||||
$message = sprintf( 'The data for the \'%1$s\' dataset for the \'%2$s\' endpoint in the \'%3$s\' report is missing or invalid.',
|
||||
$this->get_id(),
|
||||
$this->get_endpoint()->get_id(),
|
||||
$this->get_endpoint()->get_report_id()
|
||||
);
|
||||
|
||||
$this->errors->add( 'missing_chart_data', $message, $data );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the dataset data for output via JS.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $data Dataset data.
|
||||
* @return array Processed data.
|
||||
*/
|
||||
public function parse_data_for_output( $data ) {
|
||||
|
||||
if ( $this instanceof Pie_Dataset ) {
|
||||
|
||||
$processed = $data;
|
||||
|
||||
} else {
|
||||
|
||||
foreach ( $data as $key => $values ) {
|
||||
if ( is_array( $values ) && isset( $values[1] ) ) {
|
||||
$processed[ $key ] = array(
|
||||
'x' => $this->adjust_time_string( $values[0] ),
|
||||
'y' => $values[1],
|
||||
);
|
||||
} else {
|
||||
$processed[ $key ] = array(
|
||||
'x' => $this->adjust_time_string( $values ),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a date as a string or numeric timestamp, adjust it for a specific timezone.
|
||||
*
|
||||
* This allows the points on the graph to line up with the ticks, which are already adjusted.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param string|int $time_string The time string to possibly adjust.
|
||||
*
|
||||
* @return string If a timestamp, it's adjusted for the timezone of the store.
|
||||
*/
|
||||
private function adjust_time_string( $time_string ) {
|
||||
if ( is_numeric( $time_string ) ) {
|
||||
$timezone = new \DateTimeZone( edd_get_timezone_id() );
|
||||
$date_on_chart = new \DateTime( '@' . $time_string );
|
||||
|
||||
$time_string = $date_on_chart->setTimeZone( $timezone )->format( 'Y-m-d H:i:s' );
|
||||
}
|
||||
|
||||
return $time_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the dataset has generated errors during instantiation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return bool True if errors have been logged, otherwise false.
|
||||
*/
|
||||
public function has_errors() {
|
||||
if ( method_exists( $this->errors, 'has_errors' ) ) {
|
||||
return $this->errors->has_errors();
|
||||
} else {
|
||||
$errors = $this->errors->get_error_codes();
|
||||
|
||||
return ! empty( $errors );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves any logged errors for the dataset.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return \WP_Error WP_Error object for the current dataset.
|
||||
*/
|
||||
public function get_errors() {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the WP_Error instance.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function setup_error_logger() {
|
||||
if ( ! isset( $this->errors ) ) {
|
||||
$this->errors = new \WP_Error();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - LINE Dataset Class
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports\Data\Charts
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Charts\v2;
|
||||
|
||||
/**
|
||||
* Represents a manifestation of a ChartJS v2 line chart dataset in PHP form.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see Dataset
|
||||
* @see Manifest
|
||||
*/
|
||||
class Line_Dataset extends Dataset {
|
||||
|
||||
/**
|
||||
* Represents the list of fields for a given dataset.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = array(
|
||||
'borderDash', 'borderDashOffset', 'borderCapStyle', 'borderJoinStyle',
|
||||
'cubicInterpolationMode', 'fill', 'lineTension', 'pointBackgroundColor',
|
||||
'pointBorderColor', 'pointBorderWidth', 'pointRadius', 'pointStyle',
|
||||
'pointHitRadius', 'pointHoverBackgroundColor', 'pointHoverBorderColor',
|
||||
'pointHoverBorderWidth', 'pointHoverRadius', 'showLine', 'spanGaps',
|
||||
'steppedLine',
|
||||
);
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,557 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Chart Manifest class
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports\Data\Charts
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/namespace EDD\Reports\Data\Charts\v2;
|
||||
|
||||
use EDD\Reports;
|
||||
use EDD\Reports\Data\Chart_Endpoint;
|
||||
use EDD\Utils\Error_Logger_Interface as Error_Logger;
|
||||
|
||||
/**
|
||||
* Represents a manifestation of a ChartJS v2 object's attributes in PHP form.
|
||||
*
|
||||
* Primarily used to simplify translating server-side arguments into client-side ones.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Manifest implements Error_Logger {
|
||||
|
||||
/**
|
||||
* Represents the chart type to be manifested.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* Represents the unfiltered chart options for the manifest.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
private $options = array();
|
||||
|
||||
/**
|
||||
* Datasets associated with the current chart.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var Dataset[]
|
||||
*/
|
||||
private $datasets = array();
|
||||
|
||||
/**
|
||||
* Labels associated with the current pie or doughnut chart.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
private $labels = array();
|
||||
|
||||
/**
|
||||
* Represents the current Chart_Endpoint instance.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var Chart_Endpoint
|
||||
*/
|
||||
private $endpoint;
|
||||
|
||||
/**
|
||||
* Holds errors related to instantiating the manifest.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var \WP_Error
|
||||
*/
|
||||
protected $errors;
|
||||
|
||||
/**
|
||||
* Sets up the manifest.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param Chart_Endpoint $endpoint Chart endpoint.
|
||||
*/
|
||||
public function __construct( $endpoint ) {
|
||||
$this->setup_error_logger();
|
||||
$this->set_type( $endpoint->get_type() );
|
||||
$this->set_endpoint( $endpoint );
|
||||
|
||||
$options = $endpoint->get_options();
|
||||
|
||||
if ( $this->is_pie_manifest() && ! empty( $options['labels'] ) ) {
|
||||
$this->set_labels( $options['labels'] );
|
||||
|
||||
unset( $options['labels'] );
|
||||
}
|
||||
|
||||
$this->set_options( $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the chart type.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Chart type.
|
||||
*/
|
||||
public function get_type() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the chart type for the manifest.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $type Chart type to be manifested.
|
||||
*/
|
||||
private function set_type( $type ) {
|
||||
$this->type = sanitize_key( $type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the chart endpoint object for this manifest.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return Chart_Endpoint Chart endpoint.
|
||||
*/
|
||||
public function get_endpoint() {
|
||||
return $this->endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the chart endpoint object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param EDD\Reports\Data\Chart_Endpoint $endpoint Chart_Endpoint object.
|
||||
*/
|
||||
private function set_endpoint( $endpoint ) {
|
||||
$this->endpoint = $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the unfiltered chart options for later access.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $options Chart options and datasets.
|
||||
*/
|
||||
private function set_options( $options ) {
|
||||
if ( ! empty( $options['datasets'] ) && is_array( $options['datasets'] ) ) {
|
||||
|
||||
foreach ( $options['datasets'] as $id => $data ) {
|
||||
$this->add_dataset( $id, $data );
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$message = sprintf( 'The %s endpoint has no datasets.', $this->get_endpoint()->get_id() );
|
||||
|
||||
$this->errors->add( 'missing_chart_datasets', $message, $this->get_endpoint() );
|
||||
|
||||
}
|
||||
|
||||
unset( $options['datasets'] );
|
||||
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves parsed options for the chart manifest.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Chart options.
|
||||
*/
|
||||
public function get_options() {
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the manifest datasets.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return Dataset[] Datasets for this chart if any are defined, otherwise an empty array.
|
||||
*/
|
||||
public function get_datasets() {
|
||||
return $this->datasets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current chart manifest contains any datasets.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return bool True if there are datasets, otherwise false.
|
||||
*/
|
||||
public function has_datasets() {
|
||||
$datasets = $this->get_datasets();
|
||||
|
||||
return ! empty( $datasets );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the labels property (for pie and doughnut charts).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $labels Array of pie or doughnut chart labels.
|
||||
*/
|
||||
private function set_labels( $labels ) {
|
||||
$this->labels = $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the manifest labels (for pie and doughnut charts).
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function get_labels() {
|
||||
return $this->labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current chart manifest contains any labels (for pie and doughnut charts).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return bool True if there are labels, otherwise false.
|
||||
*/
|
||||
public function has_labels() {
|
||||
$labels = $this->get_labels();
|
||||
|
||||
return ! empty( $labels );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dataset.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $dataset_id ID to associate the dataset with.
|
||||
* @param array $options Dataset options.
|
||||
* @return bool True if the dataset was added, otherwise false.
|
||||
*/
|
||||
public function add_dataset( $dataset_id, $options ) {
|
||||
$handler = $this->get_dataset_handler();
|
||||
|
||||
if ( ! empty( $handler ) && class_exists( $handler ) ) {
|
||||
/** @var Dataset $dataset */
|
||||
$dataset = new $handler( $dataset_id, $this->get_endpoint(), $options );
|
||||
|
||||
if ( ! $dataset->has_errors() ) {
|
||||
|
||||
$this->datasets[ $dataset_id ] = $dataset;
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors->add( 'dataset_errors_passthrough', 'Errors have been passed through from dataset parsing.', $dataset->get_errors() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the handler class for the current dataset type.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Dataset handler class.
|
||||
*/
|
||||
public function get_dataset_handler() {
|
||||
$handler = '';
|
||||
|
||||
switch( $this->get_type() ) {
|
||||
|
||||
case 'doughnut':
|
||||
case 'pie':
|
||||
$handler = 'EDD\Reports\Data\Charts\v2\Pie_Dataset';
|
||||
break;
|
||||
|
||||
case 'bar':
|
||||
$handler = 'EDD\Reports\Data\Charts\v2\Bar_Dataset';
|
||||
break;
|
||||
|
||||
case 'line':
|
||||
$handler = 'EDD\Reports\Data\Charts\v2\Line_Dataset';
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the name of an element used to reference a rendered chart.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_target_el() {
|
||||
$endpoint = $this->get_endpoint();
|
||||
$default = "edd_reports_graph_{$endpoint->get_id()}";
|
||||
|
||||
return $endpoint->get_display_arg( 'target', $default );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the manifest in JS form.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function render() {
|
||||
// Render a <canvas> element to inject the chart in to.
|
||||
printf( '<canvas id="%s"></canvas>', esc_attr( $this->get_target_el() ) );
|
||||
|
||||
// Enqueue script and configuration to render the chart.
|
||||
wp_enqueue_script( 'edd-admin-reports' );
|
||||
|
||||
wp_add_inline_script(
|
||||
'edd-admin-reports',
|
||||
sprintf( 'window.edd.renderChart(%s)', wp_json_encode( $this->build_config() ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the chart config.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return object Config object.
|
||||
*/
|
||||
public function build_config() {
|
||||
$config = new \stdClass();
|
||||
|
||||
// Dates.
|
||||
$dates = Reports\get_dates_filter( 'objects' );
|
||||
$day_by_day = Reports\get_dates_filter_day_by_day();
|
||||
$hour_by_hour = Reports\get_dates_filter_hour_by_hour();
|
||||
|
||||
// Adjust end date forward by 1 second to push into the next day (for ChartJS display purposes).
|
||||
$dates['end']->addSeconds( 1 );
|
||||
|
||||
// Get the timezone ID for parsing.
|
||||
$timezone = edd_get_timezone_id();
|
||||
|
||||
// Apply UTC offset.
|
||||
$dates['start']->setTimezone( $timezone );
|
||||
$dates['end']->setTimezone( $timezone );
|
||||
|
||||
$time_format = 'MMM YYYY';
|
||||
|
||||
if ( $hour_by_hour ) {
|
||||
$time_format = 'hA';
|
||||
} else if ( $day_by_day ) {
|
||||
$time_format = 'MMM D';
|
||||
}
|
||||
|
||||
$config->type = $this->get_type();
|
||||
$config->data = $this->get_chart_data();
|
||||
$config->options = $this->get_chart_options();
|
||||
$config->target = $this->get_target_el();
|
||||
$config->dates = array_merge(
|
||||
$dates,
|
||||
array(
|
||||
'hour_by_hour' => $hour_by_hour,
|
||||
'day_by_day' => $day_by_day,
|
||||
'utc_offset' => esc_js( EDD()->utils->get_gmt_offset() / HOUR_IN_SECONDS ),
|
||||
'timezone' => $timezone,
|
||||
'time_format' => $time_format,
|
||||
)
|
||||
);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the parsed chart datasets as an object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Parsed chart data.
|
||||
*/
|
||||
public function get_chart_data() {
|
||||
$data = array();
|
||||
|
||||
if ( $this->has_datasets() ) {
|
||||
$datasets = $this->get_datasets();
|
||||
|
||||
$data['datasets'] = array();
|
||||
|
||||
foreach ( $datasets as $id => $set ) {
|
||||
if ( $set->has_errors() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data['datasets'][] = $set->get_options();
|
||||
}
|
||||
}
|
||||
|
||||
if ( $this->is_pie_manifest() ) {
|
||||
$data['labels'] = $this->get_labels();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the parsed chart options as an object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Parsed chart options.
|
||||
*/
|
||||
public function get_chart_options() {
|
||||
$endpoint_options = $this->get_endpoint()->get_options();
|
||||
|
||||
if ( $this->is_pie_manifest() ) {
|
||||
$defaults = array(
|
||||
'animation' => array(
|
||||
'duration' => 0,
|
||||
),
|
||||
'responsive' => true,
|
||||
'legend' => array(
|
||||
'position' => 'left',
|
||||
),
|
||||
);
|
||||
} else {
|
||||
$day_by_day = Reports\get_dates_filter_day_by_day();
|
||||
$hour_by_hour = Reports\get_dates_filter_hour_by_hour();
|
||||
|
||||
$time_unit = 'month';
|
||||
$time_format = 'MMM YYYY';
|
||||
|
||||
if ( $hour_by_hour ) {
|
||||
$time_unit = 'hour';
|
||||
$time_format = 'hA';
|
||||
} else if ( $day_by_day ) {
|
||||
$time_unit = 'day';
|
||||
$time_format = 'MMM D';
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'animation' => array(
|
||||
'duration' => 0,
|
||||
),
|
||||
'responsive' => true,
|
||||
'hoverMode' => 'index',
|
||||
'stacked' => false,
|
||||
'title' => array(
|
||||
'display' => $this->get_endpoint()->get_label() && $this->get_endpoint()->get( 'show_chart_title' ),
|
||||
'text' => $this->get_endpoint()->get_label(),
|
||||
),
|
||||
'scales' => array(
|
||||
'xAxes' => array(),
|
||||
'yAxes' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
$default_xAxes = array(
|
||||
array(
|
||||
'type' => 'time',
|
||||
'display' => true,
|
||||
'ticks' => array(
|
||||
'source' => 'auto',
|
||||
'maxRotation' => 0,
|
||||
),
|
||||
'position' => 'bottom',
|
||||
'time' => array(
|
||||
'unit' => $time_unit,
|
||||
'tooltipFormat' => $time_format,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$default_yAxes = array(
|
||||
array(
|
||||
'type' => 'linear',
|
||||
'display' => true,
|
||||
'position' => 'left',
|
||||
'ticks' => array(
|
||||
'formattingType' => 'format',
|
||||
'beginAtZero' => true,
|
||||
'suggestedMin' => 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Check if specific axes are missing from the endpoint options and load them from defaults.
|
||||
foreach ( array( 'xAxes', 'yAxes' ) as $axes_name) {
|
||||
if ( empty( $endpoint_options['scales'][ $axes_name ] ) ) {
|
||||
$endpoint_options['scales'][ $axes_name ] = ${ "default_{$axes_name}" };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array_merge( $defaults, $endpoint_options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the chart manifest is for a pie or doughnut chart.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return bool True if the manifest is for a pie or doughnut chart, otherwise false.
|
||||
*/
|
||||
public function is_pie_manifest() {
|
||||
return in_array( $this->get_type(), array( 'pie', 'doughnut' ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the dataset has generated errors during instantiation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return bool True if errors have been logged, otherwise false.
|
||||
*/
|
||||
public function has_errors() {
|
||||
if ( method_exists( $this->errors, 'has_errors' ) ) {
|
||||
return $this->errors->has_errors();
|
||||
} else {
|
||||
$errors = $this->errors->get_error_codes();
|
||||
|
||||
return ! empty( $errors );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves any logged errors for the dataset.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return \WP_Error WP_Error object for the current dataset.
|
||||
*/
|
||||
public function get_errors() {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the WP_Error instance.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function setup_error_logger() {
|
||||
if ( ! isset( $this->errors ) ) {
|
||||
$this->errors = new \WP_Error();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Pie Dataset
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports\Data\Charts
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Charts\v2;
|
||||
|
||||
/**
|
||||
* Represents a manifestation of a ChartJS v2 pie chart dataset in PHP form.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see Dataset
|
||||
* @see Manifest
|
||||
*/
|
||||
class Pie_Dataset extends Dataset {
|
||||
|
||||
/**
|
||||
* Represents the list of fields for a given dataset.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = array(
|
||||
'hoverBackgroundColor', 'hoverBorderColor',
|
||||
'hoverBorderWidth'
|
||||
);
|
||||
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Base object
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
use EDD\Utils\Error_Logger_Interface as Error_Logger;
|
||||
|
||||
/**
|
||||
* Represents an abstract base reports object.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
abstract class Base_Object implements Error_Logger {
|
||||
|
||||
/**
|
||||
* Object ID.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
public $object_id;
|
||||
|
||||
/**
|
||||
* Object label.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* Holds errors related to instantiating the object.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var \WP_Error
|
||||
*/
|
||||
protected $errors;
|
||||
|
||||
/**
|
||||
* Constructs the object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $args Arguments for instantiating the object.
|
||||
*/
|
||||
public function __construct( $args ) {
|
||||
$this->setup_error_logger();
|
||||
$this->set_props( $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets props for the object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $attributes Object attributes.
|
||||
*/
|
||||
public function set_props( $attributes ) {
|
||||
if ( ! empty( $attributes['id'] ) ) {
|
||||
|
||||
$this->set_id( $attributes['id'] );
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors->add( 'missing_object_id', 'The object ID is missing.', $attributes );
|
||||
|
||||
}
|
||||
|
||||
if ( ! empty( $attributes['label'] ) ) {
|
||||
|
||||
$this->set_label( $attributes['label'] );
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors->add( 'missing_object_label', 'The object label is missing.', $attributes );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object ID.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Object ID.
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->object_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object ID.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $object_id Object ID
|
||||
* @return void
|
||||
*/
|
||||
private function set_id( $object_id ) {
|
||||
$this->object_id = sanitize_key( $object_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the global label for the current object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Object label string.
|
||||
*/
|
||||
public function get_label() {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object label.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $label Object label.
|
||||
* @return void
|
||||
*/
|
||||
private function set_label( $label ) {
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the object via its display callback.
|
||||
*
|
||||
* Each sub-class must define its own display() method.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
abstract public function display();
|
||||
|
||||
/**
|
||||
* Determines whether the object has generated errors during instantiation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return bool True if errors have been logged, otherwise false.
|
||||
*/
|
||||
public function has_errors() {
|
||||
if ( method_exists( $this->errors, 'has_errors' ) ) {
|
||||
return $this->errors->has_errors();
|
||||
} else {
|
||||
$errors = $this->errors->get_error_codes();
|
||||
|
||||
return ! empty( $errors );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves any logged errors for the object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return \WP_Error WP_Error object for the current object.
|
||||
*/
|
||||
public function get_errors() {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the WP_Error instance for logging errors.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function setup_error_logger() {
|
||||
if ( ! isset( $this->errors ) ) {
|
||||
$this->errors = new \WP_Error();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,275 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Chart Endpoint Handler
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
use EDD\Reports\Data\Charts\v2 as Chart;
|
||||
|
||||
/**
|
||||
* Handler for building a chart endpoint in the Reports API.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Chart_Endpoint extends Endpoint {
|
||||
|
||||
/**
|
||||
* Endpoint view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
protected $view = 'chart';
|
||||
|
||||
/**
|
||||
* Represents the chart type.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* Represents the PHP manifestation of the chart data and options.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var Chart\Manifest
|
||||
*/
|
||||
private $manifest;
|
||||
|
||||
/**
|
||||
* Represents the ChartJS options passed to the chart endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
private $options = array();
|
||||
|
||||
/**
|
||||
* Call to override JS output for the chart.
|
||||
*
|
||||
* Completely overrides the manifest process for the current chart..
|
||||
*
|
||||
* @since 3.0
|
||||
* @var callable
|
||||
*/
|
||||
private $js_callback;
|
||||
|
||||
/**
|
||||
* Sets up the chart endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $args Chart endpoint attributes.
|
||||
*/
|
||||
public function __construct( $args ) {
|
||||
$this->errors = new \WP_Error();
|
||||
|
||||
// ID and Label.
|
||||
$this->set_props( $args );
|
||||
|
||||
$args = $this->parse_display_props( $args );
|
||||
|
||||
// Common values set last to account for overrides.
|
||||
parent::__construct( $args );
|
||||
|
||||
// Chart props.
|
||||
$this->setup_chart( $args );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the chart props needed for rendering.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $atts Endpoint attributes.
|
||||
*/
|
||||
private function setup_chart( $atts ) {
|
||||
$view_type = $this->get_view();
|
||||
|
||||
if ( ! empty( $atts['views'][ $view_type ] ) ) {
|
||||
|
||||
$view_atts = $atts['views'][ $view_type ];
|
||||
|
||||
if ( ! empty( $view_atts['type'] ) ) {
|
||||
$this->set_type( $view_atts['type'] );
|
||||
} else {
|
||||
$this->errors->add(
|
||||
'missing_chart_type',
|
||||
sprintf( 'The chart type for \'%1$s\' endpoint is missing.', $this->get_id() )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $view_atts['options'] ) ) {
|
||||
$this->set_options( $view_atts['options'] );
|
||||
} else {
|
||||
$this->errors->add(
|
||||
'missing_chart_options',
|
||||
sprintf( 'The chart options for the \'%1$s\' endpoint is missing.', $this->get_id() )
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $view_atts['render_js'] ) && is_callable( $view_atts['render_js'] ) ) {
|
||||
$this->js_callback = $atts['render_js'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( null === $this->js_callback ) {
|
||||
// Due to the parent constructor firing last, make sure the report gets set for the benefit of the manifest.
|
||||
if ( ! empty( $atts['report'] ) ) {
|
||||
parent::set_report_id( $atts['report'] );
|
||||
}
|
||||
|
||||
$this->build_manifest();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets display-related properties for the Endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $atts Endpoint attributes.
|
||||
*/
|
||||
private function parse_display_props( $atts ) {
|
||||
|
||||
$view_type = $this->get_view();
|
||||
|
||||
if ( ! empty( $atts['views'][ $view_type ] ) ) {
|
||||
|
||||
$atts['views'][ $view_type ] = $this->maybe_convert_callbacks_to_methods( $atts['views'][ $view_type ] );
|
||||
|
||||
}
|
||||
|
||||
return $atts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the graphing library options set for the current endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Options set for the current graph endpoint.
|
||||
*/
|
||||
public function get_options() {
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets options for displaying the graph.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $options Options for displaying the graph via the graphing library.
|
||||
*/
|
||||
protected function set_options( $options ) {
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a graph option if set.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $key Option key to retrieve a value for.
|
||||
* @return mixed Value of the option key if set, otherwise an empty string.
|
||||
*/
|
||||
public function get( $key ) {
|
||||
if ( isset( $this->options[ $key ] ) ) {
|
||||
$value = $this->options[ $key ];
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the chart type.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Chart type.
|
||||
*/
|
||||
public function get_type() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the chart type.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $type Chart type to set.
|
||||
*/
|
||||
private function set_type( $type ) {
|
||||
$this->type = sanitize_key( $type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the manifest instance.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return Chart\Manifest Chart manifest.
|
||||
*/
|
||||
public function get_manifest() {
|
||||
return $this->manifest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates the manifest based on chart type and options.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
private function build_manifest() {
|
||||
$this->manifest = new Chart\Manifest( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific axis' data if set.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $set Dataset to retrieve corresponding data for.
|
||||
* @return array Data corresponding to `$set` if it's set, otherwise an empty array.
|
||||
*/
|
||||
public function get_data_by_set( $set ) {
|
||||
$data = $this->get_data();
|
||||
|
||||
if ( isset( $data[ $set ] ) ) {
|
||||
return $data[ $set ];
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and outputs the graph JS to the page.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function display() {
|
||||
// JS callback override.
|
||||
if ( is_callable( $this->js_callback ) ) {
|
||||
call_user_func( $this->js_callback, $this->get_display_args() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Start parsing the manifest for output as JS.
|
||||
$manifest = $this->get_manifest();
|
||||
|
||||
$manifest->render();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,314 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Data Endpoints Registry
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
use EDD\Utils;
|
||||
use EDD\Reports;
|
||||
use EDD\Reports\Exceptions as Reports_Exceptions;
|
||||
|
||||
/**
|
||||
* Implements a singleton registry for registering reports data endpoints.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see \EDD\Reports\Registry
|
||||
* @see \EDD\Utils\Static_Registry
|
||||
*
|
||||
* @method array get_endpoint( string $endpoint_id )
|
||||
* @method bool endpoint_exists( string $endpoing_id )
|
||||
* @method void unregister_endpoint( string $endpoint_id )
|
||||
* @method array get_endpoints( string $sort )
|
||||
*/
|
||||
class Endpoint_Registry extends Reports\Registry implements Utils\Static_Registry {
|
||||
|
||||
/**
|
||||
* Registry item error label.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
public static $item_error_label = 'reports endpoint';
|
||||
|
||||
/**
|
||||
* The one true Endpoint_Registry instance.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var Endpoint_Registry
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Retrieves the one true Endpoint_Registry instance.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return Endpoint_Registry Registry instance.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$instance ) ) {
|
||||
self::$instance = new Endpoint_Registry();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles magic method calls for endpoint manipulation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception If the endpoint doesn't exist for get_endpoint().
|
||||
*
|
||||
* @param string $name Method name.
|
||||
* @param array $arguments Method arguments (if any)
|
||||
* @return mixed Results of the method call (if any).
|
||||
*/
|
||||
public function __call( $name, $arguments ) {
|
||||
|
||||
$endpoint_id_or_sort = isset( $arguments[0] )
|
||||
? $arguments[0]
|
||||
: '';
|
||||
|
||||
switch( $name ) {
|
||||
case 'get_endpoint':
|
||||
return parent::get_item( $endpoint_id_or_sort );
|
||||
|
||||
case 'endpoint_exists':
|
||||
return parent::offsetExists( $endpoint_id_or_sort );
|
||||
|
||||
case 'unregister_endpoint':
|
||||
parent::remove_item( $endpoint_id_or_sort );
|
||||
break;
|
||||
|
||||
case 'get_endpoints':
|
||||
return $this->get_items_sorted( $endpoint_id_or_sort );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new data endpoint to the master registry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception if the endpoint could not be validated.
|
||||
*
|
||||
* @param string $endpoint_id Reports data endpoint ID.
|
||||
* @param array $attributes {
|
||||
* Endpoint attributes. All arguments are required unless otherwise noted.
|
||||
*
|
||||
* @type string $label Endpoint label.
|
||||
* @type int $priority Optional. Priority by which to retrieve the endpoint. Default 10.
|
||||
* @type array $views {
|
||||
* Array of view handlers by type.
|
||||
*
|
||||
* @type array $view_type {
|
||||
* View type slug, with array beneath it.
|
||||
*
|
||||
* @type callable $data_callback Callback used to retrieve data for the view.
|
||||
* @type callable $display_callback Callback used to render the view.
|
||||
* @type array $display_args Optional. Array of arguments to pass to the
|
||||
* display_callback (if any). Default empty array.
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* @return bool True if the endpoint was successfully registered, otherwise false.
|
||||
*/
|
||||
public function register_endpoint( $endpoint_id, $attributes ) {
|
||||
|
||||
$defaults = array(
|
||||
'label' => '',
|
||||
'priority' => 10,
|
||||
'views' => array(),
|
||||
);
|
||||
|
||||
$attributes = array_merge( $defaults, $attributes );
|
||||
|
||||
$attributes['id'] = $endpoint_id;
|
||||
$attributes['views'] = Reports\parse_endpoint_views( $attributes['views'] );
|
||||
|
||||
// Bail if this endpoint ID is already registered.
|
||||
if ( $this->offsetExists( $endpoint_id ) ) {
|
||||
$message = sprintf( 'The \'%1$s\' endpoint already exists and cannot be registered.', $endpoint_id );
|
||||
|
||||
throw new Utils\Exception( $message );
|
||||
}
|
||||
|
||||
try {
|
||||
$valid = $this->validate_endpoint( $endpoint_id, $attributes );
|
||||
} catch ( \EDD_Exception $exception ) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ( false === $valid ) {
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
$return_value = parent::add_item( $endpoint_id, $attributes );
|
||||
} catch ( \EDD_Exception $exception ) {
|
||||
throw $exception;
|
||||
}
|
||||
return $return_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the endpoint attributes.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception if the `$label` or `$views` attributes are empty.
|
||||
* @throws \EDD_Exception if any of the `$views` sub-attributes are empty, except `$filters`.
|
||||
*
|
||||
* @param string $endpoint_id Reports data endpoint ID.
|
||||
* @param array $attributes Endpoint attributes. See register_endpoint() for full accepted attributes.
|
||||
* @return bool True if the endpoint is considered 'valid', otherwise false.
|
||||
*/
|
||||
public function validate_endpoint( $endpoint_id, $attributes ) {
|
||||
$is_valid = true;
|
||||
|
||||
try {
|
||||
|
||||
$this->validate_attributes( $attributes, $endpoint_id );
|
||||
|
||||
try {
|
||||
$this->validate_views( $attributes['views'], $endpoint_id );
|
||||
|
||||
} catch( \EDD_Exception $exception ) {
|
||||
edd_debug_log_exception( $exception );
|
||||
|
||||
$is_valid = false;
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
} catch( \EDD_Exception $exception ) {
|
||||
edd_debug_log_exception( $exception );
|
||||
|
||||
$is_valid = false;
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return $is_valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an endpoint object from a registry entry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string|Endpoint $endpoint Endpoint ID or object.
|
||||
* @param string $view_type View type to use when building the object.
|
||||
* @param string $report Optional. Report ID. Default null.
|
||||
* @return Endpoint|\WP_Error Endpoint object on success, otherwise a WP_Error object.
|
||||
*/
|
||||
public function build_endpoint( $endpoint, $view_type, $report = null ) {
|
||||
|
||||
// If an endpoint object was passed, just return it.
|
||||
if ( $endpoint instanceof Endpoint ) {
|
||||
return $endpoint;
|
||||
}
|
||||
|
||||
try {
|
||||
$_endpoint = $this->get_endpoint( $endpoint );
|
||||
|
||||
} catch( \EDD_Exception $exception ) {
|
||||
edd_debug_log_exception( $exception );
|
||||
|
||||
return new \WP_Error( 'invalid_endpoint', $exception->getMessage(), $endpoint );
|
||||
}
|
||||
|
||||
if ( ! empty( $_endpoint ) ) {
|
||||
|
||||
if ( Reports\validate_endpoint_view( $view_type ) ) {
|
||||
$_endpoint['report'] = $report;
|
||||
|
||||
$handler = Reports\get_endpoint_handler( $view_type );
|
||||
|
||||
if ( ! empty( $handler ) && class_exists( $handler ) ) {
|
||||
$_endpoint = new $handler( $_endpoint );
|
||||
|
||||
} else {
|
||||
$_endpoint = new \WP_Error(
|
||||
'invalid_handler',
|
||||
sprintf( 'The handler for the \'%1$s\' view is invalid.', $view_type ),
|
||||
$handler
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
$_endpoint = new \WP_Error(
|
||||
'invalid_view',
|
||||
sprintf( 'The \'%1$s\' view is invalid.', $view_type )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates view properties for an incoming endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception if the view attributes is empty or it's not a valid view.
|
||||
*
|
||||
* @param array $views List of attributes to check.
|
||||
* @param string $endpoint_id Endpoint ID.
|
||||
* @return void
|
||||
*/
|
||||
public function validate_views( $views, $endpoint_id ) {
|
||||
$valid_views = Reports\get_endpoint_views();
|
||||
|
||||
$this->validate_attributes( $views, $endpoint_id );
|
||||
|
||||
foreach ( $views as $view => $attributes ) {
|
||||
if ( array_key_exists( $view, $valid_views ) ) {
|
||||
if ( ! empty( $valid_views[ $view ]['allow_empty'] ) ) {
|
||||
$skip = $valid_views[ $view ]['allow_empty'];
|
||||
} else {
|
||||
$skip = array();
|
||||
}
|
||||
|
||||
// View atts have already been parsed at this point, just validate them.
|
||||
$this->validate_view_attributes( $attributes, $view, $skip );
|
||||
} else {
|
||||
throw Reports_Exceptions\Invalid_View::from( $view, __METHOD__, $endpoint_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a list of endpoint view attributes.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception if a required view attribute is empty.
|
||||
*
|
||||
* @param array $attributes List of view attributes to check for emptiness.
|
||||
* @param string $view View slug.
|
||||
* @param array $skip Optional. List of view attributes to skip validating.
|
||||
* Default empty array.
|
||||
* @return void
|
||||
*/
|
||||
public function validate_view_attributes( $attributes, $view, $skip = array() ) {
|
||||
foreach ( $attributes as $attribute => $value ) {
|
||||
if ( in_array( $attribute, $skip, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( empty( $value ) ) {
|
||||
throw Reports_Exceptions\Invalid_View_Parameter::from( $attribute, __METHOD__, $view );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Endpoint View Registry
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports
|
||||
* @copyright Copyright (c) 2019, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
use EDD\Utils;
|
||||
use EDD\Reports;
|
||||
|
||||
/**
|
||||
* Implements a singleton registry for registering endpoint views.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see \EDD\Reports\Registry
|
||||
* @see \EDD\Utils\Static_Registry
|
||||
*
|
||||
* @method array get_endpoint_view( string $view_id )
|
||||
*/
|
||||
final class Endpoint_View_Registry extends Reports\Registry implements Utils\Static_Registry {
|
||||
|
||||
/**
|
||||
* Item error label.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
public static $item_error_label = 'endpoint view';
|
||||
|
||||
/**
|
||||
* The one true registry instance.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var Endpoint_View_Registry
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Retrieves the one true Endpoint View registry instance.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return Endpoint_View_Registry Registry instance.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$instance ) ) {
|
||||
self::$instance = new Endpoint_View_Registry();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles magic method calls for endpoint view manipulation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception in get_item() if the item does not exist.
|
||||
*
|
||||
* @param string $name Method name.
|
||||
* @param array $arguments Method arguments (if any)
|
||||
* @return mixed Results of the method call (if any).
|
||||
*/
|
||||
public function __call( $name, $arguments ) {
|
||||
$view_id_or_sort = isset( $arguments[0] )
|
||||
? $arguments[0]
|
||||
: '';
|
||||
|
||||
switch ( $name ) {
|
||||
case 'get_endpoint_view':
|
||||
return parent::get_item( $view_id_or_sort );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new endpoint view to the master registry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception if the `$view_id` doesn't match a core view.
|
||||
* @throws \EDD_Exception if attributes other than 'group_callback', 'handler', or 'display_callback'
|
||||
* are defined.
|
||||
* @throws \EDD_Exception if one or more attributes are not of a valid specification.
|
||||
*
|
||||
* @param string $view_id Endpoint view ID.
|
||||
* @param array $attributes {
|
||||
* Endpoint view attributes. All arguments are required.
|
||||
*
|
||||
* @type string $label Report label.
|
||||
* @type int $priority Optional. Priority by which to register the report. Default 10.
|
||||
* @type array $filters Filters available to the report.
|
||||
* @type array $endpoints Optional. Endpoints to associate with the report.
|
||||
* }
|
||||
* @return bool True if the report was successfully registered, otherwise false.
|
||||
*/
|
||||
public function register_endpoint_view( $view_id, $attributes ) {
|
||||
$error = false;
|
||||
|
||||
$view_atts = $this->get_core_view( $view_id );
|
||||
|
||||
if ( empty( $view_atts ) ) {
|
||||
throw new Utils\Exception( sprintf( 'The \'%1$s\' endpoint view is invalid.', $view_id ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $attributes['group_callback'] ) ) {
|
||||
$view_atts['group_callback'] = $attributes['group_callback'];
|
||||
}
|
||||
|
||||
if ( ! empty( $attributes['handler'] ) ) {
|
||||
$view_atts['handler'] = $attributes['handler'];
|
||||
}
|
||||
|
||||
if ( ! empty( $attributes['fields']['display_callback'] ) ) {
|
||||
$view_atts['fields']['display_callback'] = $attributes['fields']['display_callback'];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validate_attributes( $view_atts, $view_id );
|
||||
} catch ( \EDD_Exception $exception ) {
|
||||
$error = true;
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ( true === $error ) {
|
||||
return false;
|
||||
|
||||
} else {
|
||||
return parent::add_item( $view_id, $view_atts );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves registered endpoint views.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Endpoint view records.
|
||||
*/
|
||||
public function get_endpoint_views() {
|
||||
return $this->get_items_sorted( 'ID' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents removing items from the registry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $item_id Item ID.
|
||||
*/
|
||||
public function remove_item( $item_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents removing items from the registry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param mixed $index Item index to check.
|
||||
*/
|
||||
public function offsetUnset( $index ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the core-defined views and their (mostly) immutable defaults.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $view_id View ID.
|
||||
* @return array List of attributes for the given view ID if it exists, otherwise an empty array.
|
||||
*/
|
||||
public function get_core_view( $view_id ) {
|
||||
$views = $this->get_core_views();
|
||||
|
||||
$attributes = array();
|
||||
|
||||
if ( array_key_exists( $view_id, $views ) ) {
|
||||
$attributes = $views[ $view_id ];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the core-defined views and their (mostly) immutable defaults.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array List of supported endpoint types and their attributes.
|
||||
*/
|
||||
public function get_core_views() {
|
||||
return array(
|
||||
'tile' => array(
|
||||
'group' => 'tiles',
|
||||
'group_callback' => 'EDD\Reports\default_display_tiles_group',
|
||||
'handler' => 'EDD\Reports\Data\Tile_Endpoint',
|
||||
'fields' => array(
|
||||
'data_callback' => '::get_data',
|
||||
'display_callback' => 'EDD\Reports\default_display_tile',
|
||||
'display_args' => array(
|
||||
'type' => '',
|
||||
'context' => 'primary',
|
||||
'comparison_label' => __( 'All time', 'easy-digital-downloads' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
'chart' => array(
|
||||
'group' => 'charts',
|
||||
'group_callback' => 'EDD\Reports\default_display_charts_group',
|
||||
'handler' => 'EDD\Reports\Data\Chart_Endpoint',
|
||||
'fields' => array(
|
||||
'type' => 'line',
|
||||
'options' => array(),
|
||||
'data_callback' => '::get_data',
|
||||
'display_callback' => '::display',
|
||||
'display_args' => array(
|
||||
'colors' => 'core',
|
||||
),
|
||||
),
|
||||
),
|
||||
'table' => array(
|
||||
'group' => 'tables',
|
||||
'group_callback' => 'EDD\Reports\default_display_tables_group',
|
||||
'handler' => 'EDD\Reports\Data\Table_Endpoint',
|
||||
'fields' => array(
|
||||
'data_callback' => '::prepare_items',
|
||||
'display_callback' => '::display',
|
||||
'display_args' => array(
|
||||
'class_name' => '',
|
||||
'class_file' => '',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,460 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Endpoint View object
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
use EDD\Reports;
|
||||
|
||||
/**
|
||||
* Represents a data endpoint for the Reports API.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
abstract class Endpoint extends Base_Object {
|
||||
|
||||
/**
|
||||
* Endpoint view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* ID of the report the endpoint is being built against (if provided).
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
protected $report_id;
|
||||
|
||||
/**
|
||||
* Represents the callback used to retrieve data based on the set view type.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var callable
|
||||
*/
|
||||
private $data_callback;
|
||||
|
||||
/**
|
||||
* Represents the display callback based on the set view type.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var callable
|
||||
*/
|
||||
private $display_callback;
|
||||
|
||||
/**
|
||||
* Represents the display arguments (passed to the display callback) for the view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
private $display_args = array();
|
||||
|
||||
/**
|
||||
* Constructs the tile endpoint object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $args Arguments for instantiating the endpoint as retrieved from the endpoint registry.
|
||||
*/
|
||||
public function __construct( $args ) {
|
||||
parent::__construct( $args );
|
||||
|
||||
$this->check_view();
|
||||
|
||||
if ( ! empty( $args['report'] ) ) {
|
||||
$this->set_report_id( $args['report'] );
|
||||
}
|
||||
|
||||
$this->set_display_props( $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the endpoint based on the view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function display() {
|
||||
$callback = $this->get_display_callback();
|
||||
|
||||
if ( is_callable( $callback ) ) {
|
||||
call_user_func_array( $callback, array(
|
||||
$this, // Endpoint
|
||||
$this->get_data(), // Data
|
||||
$this->get_display_args(), // Args
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the data for the endpoint view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return mixed Endpoint data.
|
||||
*/
|
||||
public function get_data() {
|
||||
$data_callback = $this->get_data_callback();
|
||||
|
||||
if ( is_callable( $data_callback ) ) {
|
||||
$data = call_user_func( $data_callback );
|
||||
} else {
|
||||
$data = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters data for the current endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param mixed|string $data Endpoint data.
|
||||
* @param Endpoint $this Endpoint object.
|
||||
*/
|
||||
return apply_filters( 'edd_reports_endpoint_data', $data, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the endpoint view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Endpoint view.
|
||||
*/
|
||||
public function get_view() {
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the endpoint view (type) against the list of available views..
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $view_type Endpoint type.
|
||||
*/
|
||||
protected function check_view() {
|
||||
$views = Reports\get_endpoint_views();
|
||||
|
||||
if ( ! array_key_exists( $this->get_view(), $views ) ) {
|
||||
$this->errors->add(
|
||||
'invalid_view',
|
||||
sprintf( 'The \'%1$s\' view is invalid.', $this->get_view() ),
|
||||
$this
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID of the report currently associated with the endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string|null Report ID if set, otherwise null.
|
||||
*/
|
||||
public function get_report_id() {
|
||||
return $this->report_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID for the report currently associated with the endpoint at the point of render.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $report Report ID.
|
||||
*/
|
||||
protected function set_report_id( $report ) {
|
||||
$this->report_id = $report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets display-related properties for the Endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $endpoint Endpoint record from the registry.
|
||||
*/
|
||||
protected function set_display_props( $endpoint ) {
|
||||
|
||||
$view_type = $this->get_view();
|
||||
|
||||
if ( ! empty( $endpoint['views'][ $view_type ] ) ) {
|
||||
|
||||
$view_atts = $endpoint['views'][ $view_type ];
|
||||
|
||||
// display_args is optional.
|
||||
if ( ! empty( $view_atts['display_args'] ) ) {
|
||||
$this->set_display_args( $view_atts['display_args'] );
|
||||
}
|
||||
|
||||
// display_callback
|
||||
if ( ! empty( $view_atts['display_callback'] ) ) {
|
||||
$this->set_display_callback( $view_atts['display_callback'] );
|
||||
} else {
|
||||
$this->flag_missing_view_arg( 'display_callback' );
|
||||
}
|
||||
|
||||
// data_callback
|
||||
if ( ! empty( $view_atts['data_callback'] ) ) {
|
||||
$this->set_data_callback( $view_atts['data_callback'] );
|
||||
} else {
|
||||
$this->flag_missing_view_arg( 'data_callback' );
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$message = sprintf( 'The \'%1$s\' view type is not defined for the \'%2$s\' endpoint.',
|
||||
$view_type,
|
||||
$this->get_id()
|
||||
);
|
||||
|
||||
$this->errors->add( 'view_not_defined', $message, array(
|
||||
'view_type' => $view_type,
|
||||
'endpoint_id' => $this->get_id(),
|
||||
) );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a given display argument if set.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $key Display argument key.
|
||||
* @param string $default Optional. Default value to return in the event the argument isn't set.
|
||||
* Default empty string.
|
||||
* @return mixed|string Value of the display argument if set, otherwise an empty string.
|
||||
*/
|
||||
public function get_display_arg( $key, $default = '' ) {
|
||||
$display_args = $this->get_display_args();
|
||||
|
||||
if ( isset( $display_args[ $key ] ) ) {
|
||||
$value = $display_args[ $key ];
|
||||
} else {
|
||||
$value = $default;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the display arguments for the view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Display arguments.
|
||||
*/
|
||||
public function get_display_args() {
|
||||
/**
|
||||
* Filters the display arguments for the current endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $display_args Display arguments.
|
||||
* @param Endpoint $this Endpoint object.
|
||||
*/
|
||||
return apply_filters( 'edd_reports_endpoint_display_args', $this->display_args, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and sets the display_args prop.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array|mixed $display_args Display arguments.
|
||||
* @return void
|
||||
*/
|
||||
protected function set_display_args( $display_args ) {
|
||||
if ( is_array( $display_args ) ) {
|
||||
|
||||
$this->display_args = $display_args;
|
||||
|
||||
} else {
|
||||
|
||||
$this->flag_invalid_view_arg_type( 'display_args', 'array' );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the display callback for the endpoint view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return callable Display callback.
|
||||
*/
|
||||
public function get_display_callback() {
|
||||
/**
|
||||
* Filters the display callback for the current endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param callable $display_callback Display callback.
|
||||
* @param Endpoint $this Endpoint object.
|
||||
*/
|
||||
return apply_filters( 'edd_reports_endpoint_display_callback', $this->display_callback, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and sets the display_args prop.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param callable|mixed $display_callback Display callback.
|
||||
* @return void
|
||||
*/
|
||||
private function set_display_callback( $display_callback ) {
|
||||
if ( is_callable( $display_callback ) ) {
|
||||
|
||||
$this->display_callback = $display_callback;
|
||||
|
||||
} elseif ( is_string( $display_callback ) && '::' === substr( $display_callback, 0, 2 ) ) {
|
||||
|
||||
$method = str_replace( '::', '', $display_callback );
|
||||
|
||||
$display_callback = array( $this, $display_callback );
|
||||
|
||||
$this->set_display_callback( $display_callback );
|
||||
|
||||
} else {
|
||||
|
||||
$this->flag_invalid_view_arg_type( 'display_callback', 'callable' );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the data callback for the endpoint view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return callable Data callback.
|
||||
*/
|
||||
public function get_data_callback() {
|
||||
/**
|
||||
* Filters the data callback for the current endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param callable $data_callback Data callback.
|
||||
* @param Endpoint $this Endpoint object.
|
||||
*/
|
||||
return apply_filters( 'edd_reports_endpoint_data_callback', $this->data_callback, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and sets the display_args prop.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param callable|mixed $data_callback Data callback.
|
||||
* @return void
|
||||
*/
|
||||
private function set_data_callback( $data_callback ) {
|
||||
if ( is_callable( $data_callback ) ) {
|
||||
|
||||
$this->data_callback = $data_callback;
|
||||
|
||||
} elseif ( is_string( $data_callback ) && '::' === substr( $data_callback, 0, 2 ) ) {
|
||||
|
||||
$method = str_replace( '::', '', $data_callback );
|
||||
|
||||
$data_callback = array( $this, $data_callback );
|
||||
|
||||
$this->set_data_callback( $data_callback );
|
||||
|
||||
} else {
|
||||
|
||||
$this->flag_invalid_view_arg_type( 'data_callback', 'callable' );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags an error for an invalid view argument type.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $argument Argument name.
|
||||
* @return void
|
||||
*/
|
||||
protected function flag_invalid_view_arg_type( $argument, $expected_type ) {
|
||||
$message = sprintf( 'The \'%1$s\' argument must be of type %2$s for the \'%3$s\' endpoint \'%4$s\' view.',
|
||||
$argument,
|
||||
$expected_type,
|
||||
$this->get_view(),
|
||||
$this->get_id()
|
||||
);
|
||||
|
||||
$this->errors->add( 'invalid_view_arg_type', $message, array(
|
||||
'view_type' => $this->get_view(),
|
||||
'endpoint_id' => $this->get_id(),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags an error for a missing required view argument.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $argument Argument name.
|
||||
* @return void
|
||||
*/
|
||||
protected function flag_missing_view_arg( $argument ) {
|
||||
$message = sprintf( 'The \'%1$s\' argument must be set for the \'%2$s\' endpoint \'%3$s\' view.',
|
||||
$argument,
|
||||
$this->get_id(),
|
||||
$this->get_view()
|
||||
);
|
||||
|
||||
$this->errors->add( "missing_{$argument}", $message, array(
|
||||
'view_type' => $this->get_view(),
|
||||
'endpoint_id' => $this->get_id(),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts callback attributes signified as methods (prefixed with '::')
|
||||
* to methods under the given object.
|
||||
*
|
||||
* This conversion can only really happen once the Endpoint is generated
|
||||
* because the object context doesn't yet exist during registration.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $atts View attributes for an endpoint.
|
||||
* @param object $object Optional. Object under which the method should be assigned.
|
||||
* Default is the current Endpoint object.
|
||||
* @return array (Maybe) adjusted list of view attributes.
|
||||
*/
|
||||
protected function maybe_convert_callbacks_to_methods( $atts, $object = null ) {
|
||||
$callbacks = array( 'display_callback', 'data_callback' );
|
||||
|
||||
if ( null === $object ) {
|
||||
$object = $this;
|
||||
}
|
||||
|
||||
foreach ( $callbacks as $callback ) {
|
||||
if ( ! empty( $atts[ $callback ] )
|
||||
&& ( is_string( $atts[ $callback ] ) && '::' === substr( $atts[ $callback ], 0, 2 ) )
|
||||
) {
|
||||
$method = str_replace( '::', '', $atts[ $callback ] );
|
||||
|
||||
$atts[ $callback ] = array( $object, $method );
|
||||
}
|
||||
}
|
||||
|
||||
return $atts;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,292 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Report Registry
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
use EDD\Utils;
|
||||
use EDD\Reports;
|
||||
|
||||
/**
|
||||
* Implements a singleton registry for registering reports.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see \EDD\Reports\Registry
|
||||
* @see \EDD\Utils\Static_Registry
|
||||
*
|
||||
* @method array get_report( string $report_id )
|
||||
* @method void remove_report( string $report_id )
|
||||
*/
|
||||
class Report_Registry extends Reports\Registry implements Utils\Static_Registry {
|
||||
|
||||
/**
|
||||
* Item error label.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
public static $item_error_label = 'report';
|
||||
|
||||
/**
|
||||
* The one true Report registry instance.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var Report_Registry
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Retrieves the one true Report registry instance.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return Report_Registry Report registry instance.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$instance ) ) {
|
||||
self::$instance = new Report_Registry();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles magic method calls for report manipulation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception in get_report() if the item does not exist.
|
||||
*
|
||||
* @param string $name Method name.
|
||||
* @param array $arguments Method arguments (if any)
|
||||
* @return mixed Results of the method call (if any).
|
||||
*/
|
||||
public function __call( $name, $arguments ) {
|
||||
$report_id_or_sort = isset( $arguments[0] )
|
||||
? $arguments[0]
|
||||
: '';
|
||||
|
||||
switch ( $name ) {
|
||||
case 'get_report':
|
||||
return parent::get_item( $report_id_or_sort );
|
||||
|
||||
case 'remove_report':
|
||||
return parent::remove_item( $report_id_or_sort );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new report to the master registry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception if the 'label' or 'endpoints' attributes are empty.
|
||||
* @throws \EDD_Exception if one or more endpoints are not of a valid specification.
|
||||
*
|
||||
* @param string $report_id Report ID.
|
||||
* @param array $attributes {
|
||||
* Reports attributes. All arguments are required unless otherwise noted.
|
||||
*
|
||||
* @type string $label Report label.
|
||||
* @type int $priority Optional. Priority by which to register the report. Default 10.
|
||||
* @type array $filters Filters available to the report.
|
||||
* @type array $endpoints Optional. Endpoints to associate with the report.
|
||||
* }
|
||||
* @return bool True if the report was successfully registered, otherwise false.
|
||||
*/
|
||||
public function add_report( $report_id, $attributes ) {
|
||||
$error = false;
|
||||
|
||||
$defaults = array(
|
||||
'label' => '',
|
||||
'priority' => 10,
|
||||
'group' => 'core',
|
||||
'capability' => 'view_shop_reports',
|
||||
'filters' => array(
|
||||
'dates',
|
||||
'taxes',
|
||||
)
|
||||
);
|
||||
|
||||
$attributes['id'] = $report_id;
|
||||
$attributes = array_merge( $defaults, $attributes );
|
||||
|
||||
try {
|
||||
// Filters can be empty.
|
||||
$this->validate_attributes( $attributes, $report_id, array( 'filters' ) );
|
||||
} catch ( \EDD_Exception $exception ) {
|
||||
$error = true;
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ( isset( $attributes['endpoints'] ) && is_array( $attributes['endpoints'] ) ) {
|
||||
foreach ( $attributes['endpoints'] as $view_group => $endpoints ) {
|
||||
foreach ( $endpoints as $index => $endpoint ) {
|
||||
if ( ! is_string( $endpoint ) && ! ( $endpoint instanceof \EDD\Reports\Data\Endpoint ) ) {
|
||||
unset( $attributes['endpoints'][ $view_group ][ $index ] );
|
||||
|
||||
throw new Utils\Exception( sprintf( 'The \'%1$s\' report contains one or more invalidly defined endpoints.', $report_id ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $attributes['filters'] ) && is_array( $attributes['filters'] ) ) {
|
||||
foreach ( $attributes['filters'] as $index => $filter ) {
|
||||
if ( ! Reports\validate_filter( $filter ) ) {
|
||||
$message = sprintf( 'The \'%1$s\' report contains one or more invalid filters.', $report_id );
|
||||
|
||||
unset( $attributes['filters'][ $index ] );
|
||||
|
||||
throw new Utils\Exception( $message );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( true === $error ) {
|
||||
return false;
|
||||
|
||||
} else {
|
||||
return parent::add_item( $report_id, $attributes );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves registered reports.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $sort Optional. How to sort the list of registered reports before retrieval.
|
||||
* Accepts 'priority' or 'ID' (alphabetized by item ID), or empty (none).
|
||||
* Default empty.
|
||||
* @param string $group Optional. The reports group to retrieve reports for. Default 'core'.
|
||||
* @return
|
||||
*/
|
||||
public function get_reports( $sort = '', $group = 'core' ) {
|
||||
$reports = $this->get_items_sorted( $sort );
|
||||
|
||||
foreach ( $reports as $report_id => $atts ) {
|
||||
if ( $group !== $atts['group'] ) {
|
||||
unset( $reports[ $report_id ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $reports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new data endpoint to the master endpoints registry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception if the `$label` or `$views` attributes are empty.
|
||||
* @throws \EDD_Exception if any of the required `$views` sub-attributes are empty.
|
||||
*
|
||||
* @see \EDD\Reports\Data\Endpoint_Registry::register_endpoint()
|
||||
*
|
||||
* @param string $endpoint_id Reports data endpoint ID.
|
||||
* @param array $attributes Attributes of the endpoint. See Endpoint_Registry::register_endpoint()
|
||||
* for more information on expected arguments.
|
||||
* @return bool True if the endpoint was successfully registered, otherwise false.
|
||||
*/
|
||||
public function register_endpoint( $endpoint_id, $attributes ) {
|
||||
/** @var \EDD\Reports\Data\Endpoint_Registry|\WP_Error $registry */
|
||||
$registry = EDD()->utils->get_registry( 'reports:endpoints' );
|
||||
|
||||
if ( is_wp_error( $registry ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $registry->register_endpoint( $endpoint_id, $attributes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a data endpoint from the master endpoints registry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see \EDD\Reports\Data\Endpoint_Registry::unregister_endpoint()
|
||||
*
|
||||
* @param string $endpoint_id Endpoint ID.
|
||||
*/
|
||||
public function unregister_endpoint( $endpoint_id ) {
|
||||
/** @var \EDD\Reports\Data\Endpoint_Registry|\WP_Error $registry */
|
||||
$registry = EDD()->utils->get_registry( 'reports:endpoints' );
|
||||
|
||||
if ( ! is_wp_error( $registry ) ) {
|
||||
$registry->unregister_endpoint( $endpoint_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an endpoint view to the master endpoint views registry.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception if all expected attributes are not set.
|
||||
*
|
||||
* @see \EDD\Reports\Data\Endpoint_View_Registry::register_endpoint_view()
|
||||
*
|
||||
* @param string $view_id View ID. Currently only core endpoint views can be added.
|
||||
* @param array $attributes Attributes of the endpoint view. See Endpoint_View_Registry::register_endpoint_view()
|
||||
* for more information on expected/allowed arguments.
|
||||
* @return bool True if the endpoint view was successfully registered, otherwise false.
|
||||
*/
|
||||
public function register_endpoint_view( $view_id, $attributes ) {
|
||||
/** @var \EDD\Reports\Data\Endpoint_View_Registry|\WP_Error $registry */
|
||||
$registry = EDD()->utils->get_registry( 'reports:endpoints:views' );
|
||||
|
||||
if ( is_wp_error( $registry ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $registry->register_endpoint_view( $view_id, $attributes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and retrieves a Report object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string|Report $report Report ID or object.
|
||||
* @param bool $build_endpoints Optional. Whether to build the endpoints (includes
|
||||
* registering any endpoint dependencies, such as
|
||||
* registering meta boxes). Default true.
|
||||
* @return Report|\WP_Error Report object on success, otherwise a WP_Error object.
|
||||
*/
|
||||
public function build_report( $report, $build_endpoints = true ) {
|
||||
|
||||
// If a report object was passed, just return it.
|
||||
if ( $report instanceof Report ) {
|
||||
return $report;
|
||||
}
|
||||
|
||||
try {
|
||||
$_report = $this->get_report( $report );
|
||||
|
||||
} catch( \EDD_Exception $exception ) {
|
||||
|
||||
edd_debug_log_exception( $exception );
|
||||
|
||||
return new \WP_Error( 'invalid_report', $exception->getMessage(), $report );
|
||||
}
|
||||
|
||||
if ( ! empty( $_report ) ) {
|
||||
$_report = new Report( $_report );
|
||||
|
||||
if ( true === $build_endpoints ) {
|
||||
$_report->build_endpoints();
|
||||
}
|
||||
}
|
||||
|
||||
return $_report;
|
||||
}
|
||||
}
|
@ -0,0 +1,447 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Report object
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
use EDD\Reports;
|
||||
use EDD\Utils;
|
||||
|
||||
/**
|
||||
* Represents an encapsulated report for the Reports API.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
final class Report extends Base_Object {
|
||||
|
||||
/**
|
||||
* Represents valid endpoints available for display.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var Endpoint[]
|
||||
*/
|
||||
private $endpoints = array();
|
||||
|
||||
/**
|
||||
* Represents the raw endpoints passed to the Report constructor.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
private $raw_endpoints = array();
|
||||
|
||||
/**
|
||||
* Represents the capability needed to view the rendered report.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
private $capability;
|
||||
|
||||
/**
|
||||
* Represents the display callback used to output the report.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var callable
|
||||
*/
|
||||
private $display_callback = '\EDD\Reports\default_display_report';
|
||||
|
||||
/**
|
||||
* Represents filters the report has opted into.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var array
|
||||
*/
|
||||
private $filters = array();
|
||||
|
||||
/**
|
||||
* Represents the group to display the report under.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
private $group;
|
||||
|
||||
/**
|
||||
* Constructs the report object.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $args Arguments for building the report (usually taking
|
||||
* the form of a report record from the registry).
|
||||
*/
|
||||
public function __construct( $args ) {
|
||||
parent::__construct( $args );
|
||||
|
||||
if ( ! empty( $args['endpoints'] ) ) {
|
||||
$this->raw_endpoints = $args['endpoints'];
|
||||
} else {
|
||||
$this->errors->add( 'missing_endpoints', 'No endpoints are defined for the report.', $args );
|
||||
}
|
||||
|
||||
if ( ! empty( $args['capability'] ) ) {
|
||||
$this->set_capability( $args['capability'] );
|
||||
} else {
|
||||
$this->errors->add( 'missing_capability', 'No capability is defined for the report.', $args );
|
||||
}
|
||||
|
||||
if ( ! empty( $args['display_callback'] ) ) {
|
||||
$this->set_display_callback( $args['display_callback'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $args['filters'] ) ) {
|
||||
$this->set_filters( $args['filters'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $args['group'] ) ) {
|
||||
$this->set_group( $args['group'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers building the report's endpoints if defined and the current user
|
||||
* has the ability to view them.
|
||||
*
|
||||
* This is abstracted away from instantiation to allow for building Report objects
|
||||
* without always registering meta boxes and other endpoint dependencies for display.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function build_endpoints() {
|
||||
if ( ! empty( $this->raw_endpoints ) && current_user_can( $this->get_capability() ) ) {
|
||||
try {
|
||||
$this->parse_endpoints( $this->raw_endpoints );
|
||||
|
||||
} catch ( \EDD_Exception $exception ) {
|
||||
edd_debug_log_exception( $exception );
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->errors->add( 'missing_endpoints', 'No endpoints are defined for the report.' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses Endpoint objects for each endpoint in the report.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws \EDD_Exception
|
||||
*
|
||||
* @param array $endpoints Endpoints, keyed by view type.
|
||||
*/
|
||||
public function parse_endpoints( $report_endpoints ) {
|
||||
/** @var \EDD\Reports\Data\Endpoint_Registry|\WP_Error $registry */
|
||||
$registry = EDD()->utils->get_registry( 'reports:endpoints' );
|
||||
|
||||
if ( is_wp_error( $registry ) ) {
|
||||
throw new Utils\Exception( $registry->get_error_message() );
|
||||
}
|
||||
|
||||
$view_groups = $this->parse_view_groups();
|
||||
|
||||
// Loop through all passed endpoints using view groups.
|
||||
foreach ( $report_endpoints as $group => $endpoints ) {
|
||||
|
||||
// Skip any invalid views based on view group.
|
||||
if ( ! array_key_exists( $group, $view_groups ) ) {
|
||||
throw new Utils\Exception( sprintf(
|
||||
'The \'%1$s\' view group does not correspond to a known endpoint view type.',
|
||||
$group
|
||||
) );
|
||||
}
|
||||
|
||||
// Loop through all endpoints for each view group and build endpoint objects.
|
||||
foreach ( $endpoints as $endpoint ) {
|
||||
|
||||
$endpoint = $registry->build_endpoint( $endpoint, $view_groups[ $group ], $this->get_id() );
|
||||
|
||||
$this->validate_endpoint( $group, $endpoint );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the views whitelist to retrieve corresponding view groups.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array List of view group and view slug pairs.
|
||||
*/
|
||||
public function parse_view_groups() {
|
||||
$views = Reports\get_endpoint_views();
|
||||
|
||||
$view_groups = array();
|
||||
|
||||
foreach ( $views as $view_type => $atts ) {
|
||||
if ( ! empty( $atts['group'] ) ) {
|
||||
$view_group = $atts['group'];
|
||||
|
||||
$view_groups[ $view_group ] = $view_type;
|
||||
}
|
||||
}
|
||||
|
||||
return $view_groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an endpoint for rendering.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see \EDD\Reports\Data\Report::$valid_endpoints
|
||||
*
|
||||
* @param string $view_group View group corresponding to the endpoint view.
|
||||
* @param Data\Endpoint|\WP_Error $endpoint Endpoint object.
|
||||
*/
|
||||
public function validate_endpoint( $view_group, $endpoint ) {
|
||||
if ( is_wp_error( $endpoint ) ) {
|
||||
$this->errors->add(
|
||||
$endpoint->get_error_code(),
|
||||
$endpoint->get_error_message(),
|
||||
$endpoint->get_error_data()
|
||||
);
|
||||
|
||||
} elseif ( ! is_wp_error( $endpoint ) && $endpoint->has_errors() ) {
|
||||
$message = sprintf( 'The \'%1$s\' endpoint is invalid.', $endpoint->get_id() );
|
||||
|
||||
$this->errors->add( 'invalid_endpoint', $message, $endpoint->get_errors() );
|
||||
|
||||
// Valid.
|
||||
} else {
|
||||
$this->endpoints[ $view_group ][ $endpoint->get_id() ] = $endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of validated endpoints for the current report.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $view_group Optional. View group for the type of endpoints to retrieve.
|
||||
* Default empty (all valid endpoints).
|
||||
* @return Endpoint[] List of validated endpoints by view view group.
|
||||
*/
|
||||
public function get_endpoints( $view_group = '' ) {
|
||||
if ( ! empty( $view_group ) && ! empty( $this->endpoints[ $view_group ] ) ) {
|
||||
return $this->endpoints[ $view_group ];
|
||||
} else {
|
||||
return $this->endpoints;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the report has any valid endpoints.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $view_group Optional. View group for the type of endpoints
|
||||
* to check the existence of. Default empty.
|
||||
* @return bool True if there is at least one valid endpoint, otherwise false.
|
||||
*/
|
||||
public function has_endpoints( $view_group = '' ) {
|
||||
if ( ! empty( $view_group ) ) {
|
||||
$has_endpoints = ! empty( $this->endpoints[ $view_group ] );
|
||||
} else {
|
||||
$has_endpoints = ! empty( $this->endpoints );
|
||||
}
|
||||
|
||||
return $has_endpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a given endpoint by view group.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $endpoint_id Endpoint ID.
|
||||
* @param string $view_group Endpoint view group.
|
||||
* @return Endpoint|\WP_Error Endpoint object if it exists, otherwise a WP_Error object.
|
||||
*/
|
||||
public function get_endpoint( $endpoint_id, $view_group ) {
|
||||
$endpoints = $this->get_endpoints( $view_group );
|
||||
|
||||
if ( isset( $endpoints[ $endpoint_id ] ) ) {
|
||||
$endpoint = $endpoints[ $endpoint_id ];
|
||||
|
||||
} else {
|
||||
$message = sprintf( 'The \'%1$s\' endpoint does not exist for the \'%2$s\' view group in the \'%3$s\' report.',
|
||||
$endpoint_id,
|
||||
$view_group,
|
||||
$this->get_id()
|
||||
);
|
||||
|
||||
$endpoint = new \WP_Error( 'invalid_report_endpoint', $message );
|
||||
}
|
||||
|
||||
return $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the capability needed to view the rendered report.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Report capability.
|
||||
*/
|
||||
public function get_capability() {
|
||||
return $this->capability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the capability needed for the current user to view the report.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $capability Capability.
|
||||
*/
|
||||
private function set_capability( $capability ) {
|
||||
$this->capability = sanitize_key( $capability );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the endpoint based on the view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function display() {
|
||||
$callback = $this->get_display_callback();
|
||||
|
||||
if ( is_callable( $callback ) ) {
|
||||
call_user_func( $callback, $this );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current report's display callback.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return callable Display callback.
|
||||
*/
|
||||
public function get_display_callback() {
|
||||
return $this->display_callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display callback used to render the report.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param callable $callback Display callback.
|
||||
*/
|
||||
private function set_display_callback( $callback ) {
|
||||
if ( is_callable( $callback ) ) {
|
||||
$this->display_callback = $callback;
|
||||
|
||||
} else {
|
||||
$this->flag_invalid_report_arg_type( 'display_callback', 'callable' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of filters registered for use with this report.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array List of support filters.
|
||||
*/
|
||||
public function get_filters() {
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the endpoint filters supported by the current report's endpoints.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $filters Filters to set for this report.
|
||||
*/
|
||||
private function set_filters( $filters ) {
|
||||
|
||||
foreach ( $filters as $filter ) {
|
||||
if ( Reports\validate_filter( $filter ) ) {
|
||||
$this->filters[] = $filter;
|
||||
|
||||
} else {
|
||||
$message = sprintf( 'The \'%1$s\' filter for the \'%2$s\' report is invalid.',
|
||||
$filter,
|
||||
$this->get_id()
|
||||
);
|
||||
|
||||
$this->errors->add( 'invalid_report_filter', $message, $this );
|
||||
}
|
||||
}
|
||||
|
||||
$this->filters = array_unique( $this->filters );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the display group for the current report.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string Display group. Default 'reports'.
|
||||
*/
|
||||
public function get_group() {
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display group for the current report.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $group Report display group.
|
||||
*/
|
||||
private function set_group( $group ) {
|
||||
$this->group = sanitize_key( $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an entire group of an endpoints view.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $view_group Endpoints view group.
|
||||
* @return void
|
||||
*/
|
||||
public function display_endpoint_group( $view_group ) {
|
||||
$groups = $this->parse_view_groups();
|
||||
|
||||
if ( array_key_exists( $view_group, $groups ) ) {
|
||||
$callback = Reports\get_endpoint_group_callback( $groups[ $view_group ] );
|
||||
|
||||
if ( is_callable( $callback ) ) {
|
||||
call_user_func( $callback, $this );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags an error for an invalid report argument type.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $argument Argument name.
|
||||
*/
|
||||
protected function flag_invalid_report_arg_type( $argument, $expected_type ) {
|
||||
$message = sprintf( 'The \'%1$s\' argument must be of type %2$s for the \'%3$s\' report.',
|
||||
$argument,
|
||||
$expected_type,
|
||||
$this->get_id()
|
||||
);
|
||||
|
||||
$this->errors->add( 'invalid_report_arg_type', $message, array(
|
||||
'report_id' => $this->get_id(),
|
||||
) );
|
||||
}
|
||||
}
|
@ -0,0 +1,223 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Table Endpoint Handler
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Handler for building a table endpoint in the Reports API.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
final class Table_Endpoint extends Endpoint {
|
||||
|
||||
/**
|
||||
* Endpoint view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
protected $view = 'table';
|
||||
|
||||
/**
|
||||
* List table instance.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var WP_List_Table
|
||||
*/
|
||||
private $list_table;
|
||||
|
||||
/**
|
||||
* Represents the full path to the list table class file.
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
private $class_file;
|
||||
|
||||
/**
|
||||
* Sets up the table endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $args Table endpoint attributes.
|
||||
*/
|
||||
public function __construct( array $args ) {
|
||||
$this->errors = new \WP_Error();
|
||||
|
||||
// ID and Label.
|
||||
$this->set_props( $args );
|
||||
|
||||
// List table set up and dumping display args.
|
||||
$this->setup_list_table( $args );
|
||||
|
||||
// Parse display attributes from defaults.
|
||||
$args = $this->parse_display_props( $args );
|
||||
|
||||
parent::__construct( $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets display-related properties for the Endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $endpoint Endpoint record from the registry.
|
||||
*/
|
||||
private function parse_display_props( $endpoint ) {
|
||||
|
||||
$view_type = $this->get_view();
|
||||
|
||||
if ( ! empty( $endpoint['views'][ $view_type ] ) ) {
|
||||
|
||||
$view_atts = $endpoint['views'][ $view_type ];
|
||||
|
||||
$list_table = $this->get_list_table();
|
||||
|
||||
if ( null === $list_table ) {
|
||||
return $endpoint;
|
||||
}
|
||||
|
||||
$endpoint['views'][ $view_type ] = $this->maybe_convert_callbacks_to_methods( $view_atts, $list_table );
|
||||
}
|
||||
|
||||
return $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets attributes related to the list table.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $endpoint Table endpoint arguments.
|
||||
*/
|
||||
private function setup_list_table( $endpoint ) {
|
||||
|
||||
if ( ! empty( $endpoint['views'][ $this->view ]['display_args'] ) ) {
|
||||
|
||||
$display_args = $endpoint['views'][ $this->view ]['display_args'];
|
||||
|
||||
if ( ! empty( $display_args['class_name'] ) ) {
|
||||
|
||||
if ( ! empty( $display_args['class_file'] ) ) {
|
||||
|
||||
$this->set_class_file( $display_args['class_file'] );
|
||||
|
||||
$this->set_list_table( $display_args['class_name'] );
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors->add(
|
||||
'missing_table_class_file',
|
||||
sprintf( 'The list table class file for the \'%1$s\' endpoint is missing.', $this->get_id() )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$this->errors->add(
|
||||
'missing_table_class_name',
|
||||
sprintf( 'The list table class name for the \'%1$s\' endpoint is missing.',
|
||||
$this->get_id()
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// Dump the display args as they're no longer needed.
|
||||
$endpoint['views'][ $this->view ]['display_args'] = array();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list table class file.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string|null Class file if set, otherwise null.
|
||||
*/
|
||||
public function get_class_file() {
|
||||
return $this->class_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list table class file.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $file Class file.
|
||||
*/
|
||||
private function set_class_file( $file ) {
|
||||
if ( false === strpos( $file, '..' ) && false === strpos( $file, './' ) ) {
|
||||
$this->class_file = $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list table instance.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return WP_List_Table|null List table instance if set, otherwise null.
|
||||
*/
|
||||
public function get_list_table() {
|
||||
return $this->list_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list table instance.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @see get_class_file()
|
||||
*
|
||||
* @param string $class List table class name.
|
||||
*/
|
||||
private function set_list_table( $class ) {
|
||||
if ( ! class_exists( $class ) ) {
|
||||
$path_to_file = $this->get_class_file();
|
||||
|
||||
if ( file_exists( $path_to_file ) ) {
|
||||
require_once $path_to_file;
|
||||
}
|
||||
}
|
||||
$this->list_table = new $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display logic for the current table endpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function display() {
|
||||
$callback = $this->get_display_callback();
|
||||
|
||||
if ( is_callable( $callback ) ) {
|
||||
$table = $this->get_list_table();
|
||||
|
||||
if ( null !== $table ) {
|
||||
// Prep the table data for display (prepare_items).
|
||||
$this->get_data();
|
||||
|
||||
call_user_func_array( $callback, array(
|
||||
$this, // Endpoint
|
||||
$table, // Table
|
||||
$this->get_display_args(), // Args
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Reports API - Tile Endpoint Handler
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data;
|
||||
|
||||
/**
|
||||
* Handler for building a tile endpoint in the Reports API.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
final class Tile_Endpoint extends Endpoint {
|
||||
|
||||
/**
|
||||
* Endpoint view (type).
|
||||
*
|
||||
* @since 3.0
|
||||
* @var string
|
||||
*/
|
||||
protected $view = 'tile';
|
||||
|
||||
/**
|
||||
* Display logic for the current tile endpoint.
|
||||
*
|
||||
* Tiles are rendered via meta boxes, so this method is deliberately empty.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function display() {
|
||||
$classnames = array(
|
||||
'edd-reports-tile',
|
||||
);
|
||||
|
||||
echo '<div id="' . esc_attr( $this->get_id() ) . '" class="' . esc_attr( implode( ' ', $classnames ) ) . '">';
|
||||
parent::display();
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* Most Valuable Customers list table.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data/Customers
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Customers;
|
||||
|
||||
use EDD\Reports as Reports;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
// Load \EDD_Customer_Reports_Table if not loaded
|
||||
if ( ! class_exists( '\EDD_Customer_Reports_Table' ) ) {
|
||||
require_once EDD_PLUGIN_DIR . 'includes/admin/customers/class-customer-table.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Most_Valuable_Customers_List_Table class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Most_Valuable_Customers_List_Table extends \EDD_Customer_Reports_Table {
|
||||
|
||||
/**
|
||||
* Query the database and fetch the top five customers of all time.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $data Customers.
|
||||
*/
|
||||
public function get_data() {
|
||||
global $wpdb;
|
||||
|
||||
$data = array();
|
||||
|
||||
$dates = Reports\get_filter_value( 'dates' );
|
||||
$date_range = Reports\parse_dates_for_range( $dates['range'] );
|
||||
$column = Reports\get_taxes_excluded_filter() ? 'total - tax' : 'total';
|
||||
$currency = Reports\get_filter_value( 'currencies' );
|
||||
|
||||
$currency_clause = '';
|
||||
if ( empty( $currency ) || 'convert' === $currency ) {
|
||||
$column = sprintf( '%s / rate', $column );
|
||||
} else {
|
||||
$currency_clause = $wpdb->prepare( " AND currency = %s ", $currency );
|
||||
}
|
||||
|
||||
$start_date = sanitize_text_field( date( 'Y-m-d 00:00:00', strtotime( $date_range['start'] ) ) );
|
||||
$end_date = sanitize_text_field( date( 'Y-m-d 23:59:59', strtotime( $date_range['end'] ) ) );
|
||||
|
||||
$sql = "SELECT customer_id, COUNT(id) AS order_count, SUM({$column}) AS total_spent
|
||||
FROM {$wpdb->edd_orders}
|
||||
WHERE status IN (%s, %s) AND date_created >= %s AND date_created <= %s AND type = 'sale'
|
||||
{$currency_clause}
|
||||
GROUP BY customer_id
|
||||
ORDER BY total_spent DESC
|
||||
LIMIT 5";
|
||||
|
||||
$results = $wpdb->get_results( $wpdb->prepare( $sql, sanitize_text_field( 'complete' ), sanitize_text_field( 'revoked' ), $start_date, $end_date ) );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$customer = edd_get_customer( (int) $result->customer_id );
|
||||
|
||||
// Skip if customer record not found.
|
||||
if ( ! $customer ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$user_id = ! empty( $customer->user_id )
|
||||
? intval( $customer->user_id )
|
||||
: 0;
|
||||
|
||||
$data[] = array(
|
||||
'id' => $customer->id,
|
||||
'user_id' => $user_id,
|
||||
'name' => $customer->name,
|
||||
'email' => $customer->email,
|
||||
'order_count' => absint( $result->order_count ),
|
||||
'spent' => $result->total_spent,
|
||||
'date_created' => $customer->date_created,
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the table columns.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $columns Array of all the list table columns.
|
||||
*/
|
||||
public function get_columns() {
|
||||
$columns = parent::get_columns();
|
||||
|
||||
// Remove the checkbox if it exists.
|
||||
if ( isset( $columns['cb'] ) ) {
|
||||
unset( $columns['cb'] );
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to disable sorting.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to remove bulk actions.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_bulk_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide pagination.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function pagination( $which ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide table navigation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
<?php
|
||||
/**
|
||||
* Top Five Customers list table.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data/Customers
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Customers;
|
||||
|
||||
use EDD\Reports as Reports;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
// Load \EDD_Customer_Reports_Table if not loaded
|
||||
if ( ! class_exists( '\EDD_Customer_Reports_Table' ) ) {
|
||||
require_once EDD_PLUGIN_DIR . 'includes/admin/customers/class-customer-table.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Top_Five_Customers_List_Table class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Top_Five_Customers_List_Table extends \EDD_Customer_Reports_Table {
|
||||
|
||||
/**
|
||||
* Query the database and fetch the top five customers of all time.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $data Customers.
|
||||
*/
|
||||
public function get_data() {
|
||||
$data = array();
|
||||
$taxes = Reports\get_taxes_excluded_filter();
|
||||
|
||||
if ( false === $taxes ) {
|
||||
$args = array(
|
||||
'number' => 5,
|
||||
'order' => 'DESC',
|
||||
'orderby' => 'purchase_value',
|
||||
);
|
||||
|
||||
$customers = edd_get_customers( $args );
|
||||
|
||||
foreach ( $customers as $customer ) {
|
||||
/** @var \EDD_Customer $customer */
|
||||
|
||||
$user_id = ! empty( $customer->user_id )
|
||||
? intval( $customer->user_id )
|
||||
: 0;
|
||||
|
||||
$data[] = array(
|
||||
'id' => $customer->id,
|
||||
'user_id' => $user_id,
|
||||
'name' => $customer->name,
|
||||
'email' => $customer->email,
|
||||
'order_count' => $customer->purchase_count,
|
||||
'spent' => $customer->purchase_value,
|
||||
'date_created' => $customer->date_created,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
global $wpdb;
|
||||
|
||||
// @todo DRY with Most_Valuable_Customers_List_Table
|
||||
|
||||
$column = Reports\get_taxes_excluded_filter() ? 'total - tax' : 'total';
|
||||
$currency = Reports\get_filter_value( 'currencies' );
|
||||
$currency_clause = '';
|
||||
|
||||
if ( empty( $currency ) || 'convert' === $currency ) {
|
||||
$column = sprintf( '%s / rate', $column );
|
||||
} else {
|
||||
$currency_clause = $wpdb->prepare( 'AND status = %s', strtoupper( $currency ) );
|
||||
}
|
||||
|
||||
$sql = "SELECT customer_id, COUNT(id) AS order_count, SUM({$column}) AS total_spent
|
||||
FROM {$wpdb->edd_orders}
|
||||
WHERE status IN (%s, %s) AND type = 'sale'
|
||||
{$currency_clause}
|
||||
GROUP BY customer_id
|
||||
ORDER BY total_spent DESC
|
||||
LIMIT 5";
|
||||
|
||||
$results = $wpdb->get_results( $wpdb->prepare( $sql, sanitize_text_field( 'complete' ), sanitize_text_field( 'revoked' ) ) );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$customer = edd_get_customer( (int) $result->customer_id );
|
||||
|
||||
// Skip if customer record not found.
|
||||
if ( ! $customer ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$user_id = ! empty( $customer->user_id )
|
||||
? intval( $customer->user_id )
|
||||
: 0;
|
||||
|
||||
$data[] = array(
|
||||
'id' => $customer->id,
|
||||
'user_id' => $user_id,
|
||||
'name' => $customer->name,
|
||||
'email' => $customer->email,
|
||||
'order_count' => absint( $result->order_count ),
|
||||
'spent' => $result->total_spent,
|
||||
'date_created' => $customer->date_created,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the table columns.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $columns Array of all the list table columns.
|
||||
*/
|
||||
public function get_columns() {
|
||||
$columns = parent::get_columns();
|
||||
|
||||
// Remove the checkbox if it exists.
|
||||
if ( isset( $columns['cb'] ) ) {
|
||||
unset( $columns['cb'] );
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the `spent` column value to possibly display in the filtered currency.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param array $item
|
||||
* @param string $column_name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function column_default( $item, $column_name ) {
|
||||
if ( 'spent' !== $column_name ) {
|
||||
return parent::column_default( $item, $column_name );
|
||||
}
|
||||
|
||||
$currency = '';
|
||||
$selected_currency = Reports\get_filter_value( 'currencies' );
|
||||
if ( ! empty( $selected_currency ) && 'convert' !== $selected_currency ) {
|
||||
$currency = $selected_currency;
|
||||
}
|
||||
|
||||
$value = edd_currency_filter( edd_format_amount( $item[ $column_name ] ), $currency );
|
||||
|
||||
return apply_filters( 'edd_customers_column_' . $column_name, $value, $item['id'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to disable sorting.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to remove bulk actions.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_bulk_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide pagination.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function pagination( $which ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide table navigation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* Top Five Discounts list table.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data/Customers
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Discounts;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use EDD\Reports as Reports;
|
||||
use EDD\Stats as Stats;
|
||||
use EDD\Admin\List_Table;
|
||||
|
||||
/**
|
||||
* Top_Five_Discounts_List_Table class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Top_Five_Discounts_List_Table extends List_Table {
|
||||
|
||||
/**
|
||||
* Query the database and fetch the top five discounts.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $data Discounts.
|
||||
*/
|
||||
public function get_data() {
|
||||
$filter = Reports\get_filter_value( 'dates' );
|
||||
|
||||
$stats = new Stats();
|
||||
|
||||
$d = $stats->get_most_popular_discounts( array(
|
||||
'number' => 5,
|
||||
'range' => $filter['range'],
|
||||
) );
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach ( $d as $result ) {
|
||||
if ( empty( $result->object ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$c = new \stdClass();
|
||||
$c->id = $result->object->id;
|
||||
$c->name = $result->object->name;
|
||||
$c->status = $result->object->status;
|
||||
$c->use_count = $result->count;
|
||||
$c->code = $result->object->code;
|
||||
$c->type = $result->object->type;
|
||||
$c->amount = $result->object->amount;
|
||||
|
||||
$data[] = $c;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the table columns.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $columns Array of all the list table columns
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'name' => __( 'Name', 'easy-digital-downloads' ),
|
||||
'code' => __( 'Code', 'easy-digital-downloads' ),
|
||||
'use_count' => __( 'Uses', 'easy-digital-downloads' ),
|
||||
'amount' => __( 'Amount', 'easy-digital-downloads' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function renders most of the columns in the list table.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $discount Discount object.
|
||||
* @param string $column_name The name of the column
|
||||
*
|
||||
* @return string Column Name
|
||||
*/
|
||||
public function column_default( $discount, $column_name ) {
|
||||
return property_exists( $discount, $column_name ) ? $discount->$column_name : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* This function renders the amount column.
|
||||
*
|
||||
* @access public
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $discount Data for the discount code.
|
||||
* @return string Formatted amount.
|
||||
*/
|
||||
public function column_amount( $discount ) {
|
||||
return edd_format_discount_rate( $discount->type, $discount->amount );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Name Column
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $discount Discount object.
|
||||
* @return string Data shown in the Name column
|
||||
*/
|
||||
public function column_name( $discount ) {
|
||||
$base = $this->get_base_url();
|
||||
$state = '';
|
||||
|
||||
// Bail if current user cannot manage discounts
|
||||
if ( ! current_user_can( 'manage_shop_discounts' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// State
|
||||
if ( ( ! empty( $status ) && ( $status !== $discount->status ) ) || ( $discount->status !== 'active' ) ) {
|
||||
$state = ' — ' . edd_get_discount_status_label( $discount->id );
|
||||
}
|
||||
|
||||
// Wrap discount title in strong anchor
|
||||
$discount_title = '<strong><a class="row-title" href="' . esc_url( add_query_arg( array(
|
||||
'edd-action' => 'edit_discount',
|
||||
'discount' => absint( $discount->id ),
|
||||
), $base ) ) . '">' . stripslashes( $discount->name ) . '</a>' . esc_html( $state ) . '</strong>';
|
||||
|
||||
// Return discount title & row actions
|
||||
return $discount_title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the final data for the table.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$columns = $this->get_columns();
|
||||
$hidden = array();
|
||||
$sortable = $this->get_sortable_columns();
|
||||
|
||||
$this->_column_headers = array( $columns, $hidden, $sortable );
|
||||
$this->items = $this->get_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base URL for the discount list table
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_base_url() {
|
||||
|
||||
// Remove some query arguments
|
||||
$base = remove_query_arg( edd_admin_removable_query_args(), edd_get_admin_base_url() );
|
||||
|
||||
// Add base query args
|
||||
return add_query_arg( array(
|
||||
'page' => 'edd-discounts',
|
||||
), $base );
|
||||
}
|
||||
|
||||
/**
|
||||
* Message to be displayed when there are no items
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No discounts found.', 'easy-digital-downloads' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the primary column.
|
||||
*
|
||||
* @since 3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return string Name of the primary column.
|
||||
*/
|
||||
protected function get_primary_column_name() {
|
||||
return 'name';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to disable sorting.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to remove bulk actions.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_bulk_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide pagination.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function pagination( $which ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide table navigation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,341 @@
|
||||
<?php
|
||||
/**
|
||||
* Earnings by Taxonomy list table.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data/File_Downloads
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Downloads;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use EDD\Reports as Reports;
|
||||
use EDD\Admin\List_Table;
|
||||
|
||||
/**
|
||||
* Earnings_By_Taxonomy_List_Table class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Earnings_By_Taxonomy_List_Table extends List_Table {
|
||||
|
||||
/**
|
||||
* Query the database and fetch the top five most downloaded products.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Taxonomies.
|
||||
*/
|
||||
public function get_data() {
|
||||
global $wpdb;
|
||||
|
||||
$dates = Reports\get_filter_value( 'dates' );
|
||||
$date_range = Reports\parse_dates_for_range( $dates['range'] );
|
||||
$currency = Reports\get_filter_value( 'currencies' );
|
||||
|
||||
// Generate date query SQL if dates have been set.
|
||||
$date_query_sql = '';
|
||||
|
||||
if ( ! empty( $date_range['start'] ) || ! empty( $date_range['end'] ) ) {
|
||||
if ( ! empty( $date_range['start'] ) ) {
|
||||
$date_query_sql .= $wpdb->prepare( 'AND oi.date_created >= %s', $date_range['start']->format( 'mysql' ) );
|
||||
}
|
||||
|
||||
// Join dates with `AND` if start and end date set.
|
||||
if ( ! empty( $date_range['start'] ) && ! empty( $date_range['end'] ) ) {
|
||||
$date_query_sql .= ' AND ';
|
||||
}
|
||||
|
||||
if ( ! empty( $date_range['end'] ) ) {
|
||||
$date_query_sql .= $wpdb->prepare( 'oi.date_created <= %s', $date_range['end']->format( 'mysql' ) );
|
||||
}
|
||||
}
|
||||
|
||||
$taxonomies = edd_get_download_taxonomies();
|
||||
$taxonomies = array_map( 'sanitize_text_field', $taxonomies );
|
||||
|
||||
$placeholders = implode( ', ', array_fill( 0, count( $taxonomies ), '%s' ) );
|
||||
|
||||
$taxonomy__in = $wpdb->prepare( "tt.taxonomy IN ({$placeholders})", $taxonomies );
|
||||
|
||||
$sql = "SELECT t.*, tt.*, tr.object_id
|
||||
FROM {$wpdb->terms} AS t
|
||||
INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id
|
||||
INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id
|
||||
WHERE {$taxonomy__in}";
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
// Build intermediate array to allow for better data processing.
|
||||
$taxonomies = array();
|
||||
foreach ( $results as $r ) {
|
||||
$taxonomies[ absint( $r->term_id ) ]['name'] = esc_html( $r->name );
|
||||
$taxonomies[ absint( $r->term_id ) ]['object_ids'][] = absint( $r->object_id );
|
||||
$taxonomies[ absint( $r->term_id ) ]['parent'] = absint( $r->parent );
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$parent_ids = array();
|
||||
|
||||
$column = Reports\get_taxes_excluded_filter() ? 'oi.total - oi.tax' : 'oi.total';
|
||||
$join = " INNER JOIN {$wpdb->edd_orders} o ON o.id = oi.order_id ";
|
||||
$currency_clause = '';
|
||||
|
||||
if ( empty( $currency ) || 'convert' === $currency ) {
|
||||
$column = sprintf( '(%s) / oi.rate', $column );
|
||||
} elseif ( array_key_exists( strtoupper( $currency ), edd_get_currencies() ) ) {
|
||||
$currency_clause = $wpdb->prepare(
|
||||
" AND o.currency = %s ",
|
||||
strtoupper( $currency )
|
||||
);
|
||||
}
|
||||
|
||||
$statuses = edd_get_net_order_statuses();
|
||||
$status_string = implode( ', ', array_fill( 0, count( $statuses ), '%s' ) );
|
||||
$status_sql = $wpdb->prepare(
|
||||
" AND oi.status IN('complete','partially_refunded')
|
||||
AND o.status IN({$status_string})",
|
||||
...$statuses
|
||||
);
|
||||
|
||||
foreach ( $taxonomies as $k => $t ) {
|
||||
$c = new \stdClass();
|
||||
$c->id = $k;
|
||||
$c->name = $taxonomies[ $k ]['name'];
|
||||
|
||||
$placeholders = implode( ', ', array_fill( 0, count( $taxonomies[ $k ]['object_ids'] ), '%d' ) );
|
||||
$product_id__in = $wpdb->prepare( "oi.product_id IN({$placeholders})", $taxonomies[ $k ]['object_ids'] );
|
||||
|
||||
$sql = "SELECT SUM({$column}) as total
|
||||
FROM {$wpdb->edd_order_items} oi
|
||||
{$join}
|
||||
WHERE {$product_id__in} {$currency_clause} {$date_query_sql} {$status_sql}";
|
||||
|
||||
$result = $wpdb->get_row( $sql ); // WPCS: unprepared SQL ok.
|
||||
|
||||
$earnings = null === $result && null === $result->total
|
||||
? 0.00
|
||||
: floatval( $result->total );
|
||||
|
||||
$complete_orders = "SELECT SUM(oi.quantity) as sales
|
||||
FROM {$wpdb->edd_order_items} oi
|
||||
{$join}
|
||||
WHERE {$product_id__in} {$currency_clause} {$date_query_sql} {$status_sql}";
|
||||
$partial_orders = "SELECT SUM(oi.quantity) as sales
|
||||
FROM {$wpdb->edd_order_items} oi
|
||||
LEFT JOIN {$wpdb->edd_order_items} ri
|
||||
ON ri.parent = oi.id
|
||||
{$join}
|
||||
WHERE {$product_id__in} {$currency_clause} {$date_query_sql}
|
||||
AND oi.status ='partially_refunded'
|
||||
AND oi.quantity = - ri.quantity";
|
||||
$sql_sales = $wpdb->get_row( "SELECT SUM(sales) AS sales FROM ({$complete_orders} UNION {$partial_orders})a" );
|
||||
|
||||
$sales = ! empty( $sql_sales->sales ) ? $sql_sales->sales : 0;
|
||||
|
||||
$c->sales = $sales;
|
||||
$c->earnings = $earnings;
|
||||
$c->parent = 0 === $t['parent']
|
||||
? null
|
||||
: $t['parent'];
|
||||
|
||||
$average_sales = 0;
|
||||
$average_earnings = 0.00;
|
||||
|
||||
foreach ( $taxonomies[ $k ]['object_ids'] as $download ) {
|
||||
$average_sales += edd_get_average_monthly_download_sales( $download );
|
||||
$average_earnings += edd_get_average_monthly_download_earnings( $download );
|
||||
}
|
||||
|
||||
$c->average_sales = $average_sales;
|
||||
$c->average_earnings = $average_earnings;
|
||||
|
||||
$data[] = $c;
|
||||
}
|
||||
|
||||
$sorted_data = array();
|
||||
|
||||
foreach ( $data as $d ) {
|
||||
|
||||
// Get parent level elements
|
||||
if ( null === $d->parent ) {
|
||||
$sorted_data[] = $d;
|
||||
|
||||
$objects = array_values( wp_filter_object_list( $data, array( 'parent' => $d->id ) ) );
|
||||
|
||||
foreach ( $objects as $o ) {
|
||||
$sorted_data[] = $o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by total earnings
|
||||
usort( $sorted_data, function( $a, $b ) {
|
||||
return ( $a->earnings < $b->earnings ) ? -1 : 1;
|
||||
} );
|
||||
|
||||
return $sorted_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the table columns.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $columns Array of all the list table columns
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'name' => __( 'Name', 'easy-digital-downloads' ),
|
||||
'sales' => __( 'Total Sales', 'easy-digital-downloads' ),
|
||||
'earnings' => __( 'Total Earnings', 'easy-digital-downloads' ),
|
||||
'average_sales' => __( 'Monthly Sales Average', 'easy-digital-downloads' ),
|
||||
'average_earnings' => __( 'Monthly Earnings Average', 'easy-digital-downloads' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Name Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $taxonomy Taxonomy object.
|
||||
* @return string Data shown in the Name column.
|
||||
*/
|
||||
public function column_name( $taxonomy ) {
|
||||
return 0 < $taxonomy->parent
|
||||
? '— ' . $taxonomy->name
|
||||
: $taxonomy->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Sales Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $taxonomy Taxonomy object.
|
||||
* @return string Data shown in the Sales column.
|
||||
*/
|
||||
public function column_sales( $taxonomy ) {
|
||||
return $taxonomy->sales;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Earnings Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $taxonomy Taxonomy object.
|
||||
* @return string Data shown in the Earnings column.
|
||||
*/
|
||||
public function column_earnings( $taxonomy ) {
|
||||
return edd_currency_filter( edd_format_amount( $taxonomy->earnings ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Average Sales Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $taxonomy Taxonomy object.
|
||||
* @return int Data shown in the Average Sales column.
|
||||
*/
|
||||
public function column_average_sales( $taxonomy ) {
|
||||
return (int) round( $taxonomy->average_sales );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Average Earnings Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $taxonomy Taxonomy object.
|
||||
* @return string Data shown in the Average Earnings column.
|
||||
*/
|
||||
public function column_average_earnings( $taxonomy ) {
|
||||
return edd_currency_filter( edd_format_amount( $taxonomy->average_earnings ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the final data for the table.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$columns = $this->get_columns();
|
||||
$hidden = array();
|
||||
$sortable = $this->get_sortable_columns();
|
||||
|
||||
$this->_column_headers = array( $columns, $hidden, $sortable );
|
||||
$this->items = $this->get_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Message to be displayed when there are no items
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No taxonomies found.', 'easy-digital-downloads' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the primary column.
|
||||
*
|
||||
* @since 3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return string Name of the primary column.
|
||||
*/
|
||||
protected function get_primary_column_name() {
|
||||
return 'name';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to disable sorting.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to remove bulk actions.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_bulk_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide pagination.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function pagination( $which ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide table navigation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
<?php
|
||||
/**
|
||||
* Top Selling Downloads list table.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data/File_Downloads
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Downloads;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use EDD\Stats as Stats;
|
||||
use EDD\Reports as Reports;
|
||||
use EDD\Admin\List_Table;
|
||||
|
||||
/**
|
||||
* Top_Selling_Downloads_List_Table class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Top_Selling_Downloads_List_Table extends List_Table {
|
||||
|
||||
/**
|
||||
* Query the database and fetch the top five most downloaded products.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Downloads.
|
||||
*/
|
||||
public function get_data() {
|
||||
$filter = Reports\get_filter_value( 'dates' );
|
||||
|
||||
$stats = new Stats();
|
||||
|
||||
return $stats->get_most_valuable_order_items( array(
|
||||
'number' => 10,
|
||||
'range' => $filter['range'],
|
||||
'currency' => '',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the table columns.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $columns Array of all the list table columns
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'name' => __( 'Name', 'easy-digital-downloads' ),
|
||||
'price' => __( 'Price', 'easy-digital-downloads' ),
|
||||
'sales' => __( 'Sales', 'easy-digital-downloads' ),
|
||||
'earnings' => __( 'Net Earnings', 'easy-digital-downloads' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Name Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $download Download object.
|
||||
* @return string Data shown in the Name column.
|
||||
*/
|
||||
public function column_name( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
// Check for variable pricing
|
||||
$retval = ! is_null( $download->price_id ) && is_numeric( $download->price_id )
|
||||
? edd_get_download_name( $download->object->ID, $download->price_id )
|
||||
: edd_get_download_name( $download->object->ID );
|
||||
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Price Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $download Download object.
|
||||
* @return string Data shown in the Price column.
|
||||
*/
|
||||
public function column_price( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
// Check for variable pricing
|
||||
$retval = ! is_null( $download->price_id ) && is_numeric( $download->price_id )
|
||||
? edd_price( $download->object->ID, false, $download->price_id )
|
||||
: edd_price( $download->object->ID, false );
|
||||
|
||||
return $retval;
|
||||
}
|
||||
|
||||
public function column_sales( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return current_user_can( 'view_product_stats', $download->object->ID )
|
||||
? $download->sales
|
||||
: '—';
|
||||
}
|
||||
|
||||
public function column_earnings( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return current_user_can( 'view_product_stats', $download->object->ID )
|
||||
? edd_currency_filter( edd_format_amount( $download->total ) )
|
||||
: '—';
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the final data for the table.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$columns = $this->get_columns();
|
||||
$hidden = array();
|
||||
$sortable = $this->get_sortable_columns();
|
||||
|
||||
$this->_column_headers = array( $columns, $hidden, $sortable );
|
||||
$this->items = $this->get_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base URL for the discount list table
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_base_url() {
|
||||
return remove_query_arg( edd_admin_removable_query_args(), edd_get_admin_base_url() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Message to be displayed when there are no items
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No downloads found.', 'easy-digital-downloads' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the primary column.
|
||||
*
|
||||
* @since 3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return string Name of the primary column.
|
||||
*/
|
||||
protected function get_primary_column_name() {
|
||||
return 'name';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to disable sorting.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to remove bulk actions.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_bulk_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide pagination.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function pagination( $which ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide table navigation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,227 @@
|
||||
<?php
|
||||
/**
|
||||
* Top Five Most Downloaded Products list table.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data/File_Downloads
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\File_Downloads;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use EDD\Reports as Reports;
|
||||
use EDD\Stats as Stats;
|
||||
use EDD\Admin\List_Table;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Top_Five_Most_Downloaded_List_Table class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Top_Five_Most_Downloaded_List_Table extends List_Table {
|
||||
|
||||
/**
|
||||
* Query the database and fetch the top five most downloaded products.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array Downloads.
|
||||
*/
|
||||
public function get_data() {
|
||||
$filter = Reports\get_filter_value( 'dates' );
|
||||
|
||||
$stats = new Stats();
|
||||
|
||||
return $stats->get_most_downloaded_products( array(
|
||||
'number' => 5,
|
||||
'range' => $filter['range']
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the table columns.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $columns Array of all the list table columns
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'name' => __( 'Name', 'easy-digital-downloads' ),
|
||||
'download_count' => __( 'File Downloads', 'easy-digital-downloads' ),
|
||||
'price' => __( 'Price', 'easy-digital-downloads' ),
|
||||
'sales' => __( 'Sales', 'easy-digital-downloads' ),
|
||||
'earnings' => __( 'Earnings', 'easy-digital-downloads' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Name Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $download Download object.
|
||||
* @return string Data shown in the Name column.
|
||||
*/
|
||||
public function column_name( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
// Download title
|
||||
return $download->object->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Download Count Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $download Download object.
|
||||
* @return string Data shown in the Download Count column.
|
||||
*/
|
||||
public function column_download_count( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return $download->total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Price Column.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param \stdClass $download Download object.
|
||||
* @return string Data shown in the Price column.
|
||||
*/
|
||||
public function column_price( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
if ( $download->object->has_variable_prices() ) {
|
||||
return edd_price_range( $download->object->ID );
|
||||
} else {
|
||||
return edd_price( $download->object->ID, false );
|
||||
}
|
||||
}
|
||||
|
||||
public function column_sales( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return current_user_can( 'view_product_stats', $download->object->ID )
|
||||
? edd_get_download_sales_stats( $download->object->ID )
|
||||
: '—';
|
||||
}
|
||||
|
||||
public function column_earnings( $download ) {
|
||||
if ( ! $download->object instanceof \EDD_Download ) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return current_user_can( 'view_product_stats', $download->object->ID )
|
||||
? edd_currency_filter( edd_format_amount( edd_get_download_earnings_stats( $download->object->ID ) ) )
|
||||
: '—';
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the final data for the table.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$columns = $this->get_columns();
|
||||
$hidden = array();
|
||||
$sortable = $this->get_sortable_columns();
|
||||
|
||||
$this->_column_headers = array( $columns, $hidden, $sortable );
|
||||
$this->items = $this->get_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base URL for the discount list table
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_base_url() {
|
||||
return remove_query_arg( edd_admin_removable_query_args(), edd_get_admin_base_url() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Message to be displayed when there are no items
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public function no_items() {
|
||||
esc_html_e( 'No downloads found.', 'easy-digital-downloads' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the primary column.
|
||||
*
|
||||
* @since 3.0
|
||||
* @access protected
|
||||
*
|
||||
* @return string Name of the primary column.
|
||||
*/
|
||||
protected function get_primary_column_name() {
|
||||
return 'name';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to disable sorting.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to remove bulk actions.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_bulk_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide pagination.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function pagination( $which ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide table navigation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/**
|
||||
* Gateway Stats list table.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data/Customers
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Payment_Gateways;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use EDD\Stats as Stats;
|
||||
use EDD\Reports as Reports;
|
||||
use EDD\Admin\List_Table;
|
||||
|
||||
/**
|
||||
* Top_Five_Customers_List_Table class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Gateway_Stats extends List_Table {
|
||||
|
||||
/**
|
||||
* Get things started
|
||||
*
|
||||
* @since 1.5
|
||||
* @see WP_List_Table::__construct()
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct( array(
|
||||
'singular' => 'report-gateway',
|
||||
'plural' => 'report-gateways',
|
||||
'ajax' => false,
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the primary column.
|
||||
*
|
||||
* @since 2.5
|
||||
* @access protected
|
||||
*
|
||||
* @return string Name of the primary column.
|
||||
*/
|
||||
protected function get_primary_column_name() {
|
||||
return 'label';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render each column.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param array $item Contains all the data of the downloads
|
||||
* @param string $column_name The name of the column
|
||||
*
|
||||
* @return string Column Name
|
||||
*/
|
||||
public function column_default( $item, $column_name ) {
|
||||
return $item[ $column_name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Column names.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $columns Array of all the list table columns
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'label' => __( 'Gateway', 'easy-digital-downloads' ),
|
||||
'complete_sales' => __( 'Complete Sales', 'easy-digital-downloads' ),
|
||||
'pending_sales' => __( 'Pending / Failed Sales', 'easy-digital-downloads' ),
|
||||
'refunded_sales' => __( 'Refunds Issued', 'easy-digital-downloads' ),
|
||||
'total_sales' => __( 'Total Sales', 'easy-digital-downloads' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build all the reports data
|
||||
*
|
||||
* @since 1.5
|
||||
* @return array All the data for customer reports
|
||||
*/
|
||||
public function get_data() {
|
||||
$filter = Reports\get_filter_value( 'dates' );
|
||||
$currency = Reports\get_filter_value( 'currencies' );
|
||||
|
||||
$reports_data = array();
|
||||
$gateways = edd_get_payment_gateways();
|
||||
|
||||
foreach ( $gateways as $gateway_id => $gateway ) {
|
||||
$stats = new Stats();
|
||||
|
||||
$complete_count = $stats->get_gateway_sales( array(
|
||||
'range' => $filter['range'],
|
||||
'gateway' => $gateway_id,
|
||||
'status' => edd_get_gross_order_statuses(),
|
||||
'type' => array( 'sale' ),
|
||||
'currency' => $currency,
|
||||
) );
|
||||
|
||||
$pending_count = $stats->get_gateway_sales( array(
|
||||
'range' => $filter['range'],
|
||||
'gateway' => $gateway_id,
|
||||
'status' => edd_get_incomplete_order_statuses(),
|
||||
'type' => array( 'sale' ),
|
||||
'currency' => $currency,
|
||||
) );
|
||||
|
||||
$refunded_count = $stats->get_gateway_sales( array(
|
||||
'range' => $filter['range'],
|
||||
'gateway' => $gateway_id,
|
||||
'status' => array( 'complete' ),
|
||||
'type' => array( 'refund' ),
|
||||
'currency' => $currency,
|
||||
) );
|
||||
|
||||
$total_count = $stats->get_gateway_sales( array(
|
||||
'range' => $filter['range'],
|
||||
'gateway' => $gateway_id,
|
||||
'status' => 'any',
|
||||
'type' => array( 'sale' ),
|
||||
'currency' => $currency,
|
||||
) );
|
||||
|
||||
$reports_data[] = array(
|
||||
'ID' => $gateway_id,
|
||||
'label' => '<a href="' . esc_url( edd_get_admin_url( array( 'page' => 'edd-payment-history', 'gateway' => sanitize_key( $gateway_id ) ) ) ) . '">' . esc_html( $gateway['admin_label'] ) . '</a>',
|
||||
'complete_sales' => edd_format_amount( $complete_count, false ),
|
||||
'pending_sales' => edd_format_amount( $pending_count, false ),
|
||||
'refunded_sales' => edd_format_amount( $refunded_count, false ),
|
||||
'total_sales' => edd_format_amount( $total_count, false ),
|
||||
);
|
||||
}
|
||||
|
||||
return $reports_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the final data for the table
|
||||
*
|
||||
* @since 1.5
|
||||
* @uses EDD_Gateway_Reports_Table::get_columns()
|
||||
* @uses EDD_Gateway_Reports_Table::get_sortable_columns()
|
||||
* @uses EDD_Gateway_Reports_Table::reports_data()
|
||||
* @return void
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$columns = $this->get_columns();
|
||||
$hidden = array(); // No hidden columns
|
||||
$sortable = $this->get_sortable_columns();
|
||||
$this->_column_headers = array( $columns, $hidden, $sortable );
|
||||
$this->items = $this->get_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to disable sorting.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to remove bulk actions.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_bulk_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide pagination.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function pagination( $which ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide table navigation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
<?php
|
||||
/**
|
||||
* Tax Collected by Location list table.
|
||||
*
|
||||
* @package EDD
|
||||
* @subpackage Reports/Data/Customers
|
||||
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||||
* @since 3.0
|
||||
*/
|
||||
namespace EDD\Reports\Data\Taxes;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use EDD\Admin\List_Table;
|
||||
use EDD\Reports;
|
||||
|
||||
/**
|
||||
* Tax_Collected_by_Location class.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class Tax_Collected_By_Location extends List_Table {
|
||||
|
||||
/**
|
||||
* Get things started
|
||||
*
|
||||
* @since 1.5
|
||||
* @see WP_List_Table::__construct()
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct( array(
|
||||
'singular' => 'report-tax-collected-by-location',
|
||||
'plural' => 'report-tax-collected-by-locations',
|
||||
'ajax' => false,
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the primary column.
|
||||
*
|
||||
* @since 2.5
|
||||
* @access protected
|
||||
*
|
||||
* @return string Name of the primary column.
|
||||
*/
|
||||
protected function get_primary_column_name() {
|
||||
return 'label';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render each column.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param array $item Contains all the data of the downloads
|
||||
* @param string $column_name The name of the column
|
||||
*
|
||||
* @return string Column Name
|
||||
*/
|
||||
public function column_default( $item, $column_name ) {
|
||||
return $item[ $column_name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Column names.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $columns Array of all the list table columns
|
||||
*/
|
||||
public function get_columns() {
|
||||
return array(
|
||||
'country' => __( 'Country/Region', 'easy-digital-downloads' ),
|
||||
'from' => __( 'From', 'easy-digital-downloads' ),
|
||||
'to' => __( 'To', 'easy-digital-downloads' ),
|
||||
'gross' => __( 'Gross', 'easy-digital-downloads' ),
|
||||
'tax' => __( 'Tax', 'easy-digital-downloads' ),
|
||||
'net' => __( 'Net', 'easy-digital-downloads' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query data for the list table.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array $data All the data for the list table.
|
||||
*/
|
||||
public function get_data() {
|
||||
global $wpdb;
|
||||
|
||||
$data = array();
|
||||
$tax_rates = edd_get_tax_rates( array(), OBJECT );
|
||||
$date_filter = Reports\get_filter_value( 'dates' );
|
||||
$date_range = Reports\parse_dates_for_range( $date_filter['range'] );
|
||||
$currency = Reports\get_filter_value( 'currencies' );
|
||||
$convert_currency = empty( $currency ) || 'convert' === $currency;
|
||||
$format_currency = $convert_currency ? edd_get_currency() : strtoupper( $currency );
|
||||
|
||||
// Date query.
|
||||
$date_query = '';
|
||||
|
||||
if ( ! empty( $date_range['start'] ) && '0000-00-00 00:00:00' !== $date_range['start'] ) {
|
||||
$date_query .= $wpdb->prepare( " AND {$wpdb->edd_orders}.date_created >= %s", esc_sql( EDD()->utils->date( $date_range['start'], null, false )->startOfDay()->format( 'mysql' ) ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $date_range['end'] ) && '0000-00-00 00:00:00' !== $date_range['end'] ) {
|
||||
$date_query .= $wpdb->prepare( " AND {$wpdb->edd_orders}.date_created <= %s", esc_sql( EDD()->utils->date( $date_range['end'], null, false )->endOfDay()->format( 'mysql' ) ) );
|
||||
}
|
||||
|
||||
$from = empty( $date_range['start'] ) || '0000-00-00 00:00:00' === $date_range['start']
|
||||
? '—'
|
||||
: edd_date_i18n( EDD()->utils->date( $date_range['start'], null, false )->startOfDay()->timestamp );
|
||||
|
||||
$to = empty( $date_range['end'] ) || '0000-00-00 00:00:00' === $date_range['end']
|
||||
? '—'
|
||||
: edd_date_i18n( EDD()->utils->date( $date_range['end'], null, false )->endOfDay()->timestamp );
|
||||
|
||||
$tax_column = $convert_currency ? 'tax / rate' : 'tax';
|
||||
$total_column = $convert_currency ? 'total / rate' : 'total';
|
||||
|
||||
$currency_sql = '';
|
||||
if ( ! $convert_currency && array_key_exists( strtoupper( $currency ), edd_get_currencies() ) ) {
|
||||
$currency_sql = $wpdb->prepare( " AND currency = %s ", strtoupper( $currency ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to first calculate the total tax collected for all orders so we can determine the amount of tax collected for the global rate
|
||||
*
|
||||
* The total determined here will be reduced by the amount collected for each specified tax rate/region.
|
||||
*/
|
||||
$all_orders = $wpdb->get_results( "
|
||||
SELECT SUM({$tax_column}) as tax, SUM({$total_column}) as total
|
||||
FROM {$wpdb->edd_orders}
|
||||
WHERE 1=1 {$currency_sql} {$date_query}
|
||||
", ARRAY_A );
|
||||
|
||||
foreach ( $tax_rates as $tax_rate ) {
|
||||
|
||||
$country_region = $tax_rate->name . '-' . $tax_rate->description;
|
||||
|
||||
if ( array_key_exists( $country_region, $data ) ) {
|
||||
continue; // We've already pulled numbers for this country / region
|
||||
}
|
||||
|
||||
$location = edd_get_country_name( $tax_rate->name );
|
||||
|
||||
if ( ! empty( $tax_rate->description ) ) {
|
||||
$location .= ' — ' . edd_get_state_name( $tax_rate->name, $tax_rate->description );
|
||||
}
|
||||
|
||||
$region = ! empty( $tax_rate->description )
|
||||
? $wpdb->prepare( ' AND region = %s', esc_sql( $tax_rate->description ) )
|
||||
: '';
|
||||
|
||||
$results = $wpdb->get_results( $wpdb->prepare( "
|
||||
SELECT SUM($tax_column) as tax, SUM($total_column) as total, country, region
|
||||
FROM {$wpdb->edd_orders}
|
||||
INNER JOIN {$wpdb->edd_order_addresses} ON {$wpdb->edd_order_addresses}.order_id = {$wpdb->edd_orders}.id
|
||||
WHERE {$wpdb->edd_order_addresses}.country = %s {$region} {$date_query} {$currency_sql}
|
||||
", esc_sql( $tax_rate->name ) ), ARRAY_A );
|
||||
|
||||
$all_orders[0]['tax'] -= $results[0]['tax'];
|
||||
$all_orders[0]['total'] -= $results[0]['total'];
|
||||
|
||||
$data[ $country_region ] = array(
|
||||
'country' => $location,
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
'gross' => edd_currency_filter( edd_format_amount( floatval( $results[0]['total'] ) ), $format_currency ),
|
||||
'tax' => edd_currency_filter( edd_format_amount( floatval( $results[0]['tax'] ) ), $format_currency ),
|
||||
'net' => edd_currency_filter( edd_format_amount( floatval( $results[0]['total'] - $results[0]['tax'] ) ), $format_currency ),
|
||||
);
|
||||
}
|
||||
|
||||
if( $all_orders[0]['total'] > 0 && $all_orders[0]['tax'] > 0 ) {
|
||||
|
||||
$data[ 'global' ] = array(
|
||||
'country' => __( 'Global Rate', 'easy-digital-downloads' ),
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
'gross' => edd_currency_filter( edd_format_amount( floatval( max( 0, $all_orders[0]['total'] ) ) ), $format_currency ),
|
||||
'tax' => edd_currency_filter( edd_format_amount( floatval( max( 0, $all_orders[0]['tax'] ) ) ), $format_currency ),
|
||||
'net' => edd_currency_filter( edd_format_amount( floatval( max( 0, $all_orders[0]['total'] - $all_orders[0]['tax'] ) ) ), $format_currency ),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the final data for the table
|
||||
*
|
||||
* @since 1.5
|
||||
* @uses EDD_Gateway_Reports_Table::get_columns()
|
||||
* @uses EDD_Gateway_Reports_Table::get_sortable_columns()
|
||||
* @uses EDD_Gateway_Reports_Table::reports_data()
|
||||
* @return void
|
||||
*/
|
||||
public function prepare_items() {
|
||||
$columns = $this->get_columns();
|
||||
$hidden = array(); // No hidden columns
|
||||
$sortable = $this->get_sortable_columns();
|
||||
$this->_column_headers = array( $columns, $hidden, $sortable );
|
||||
$this->items = $this->get_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to disable sorting.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sortable_columns() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return empty array to remove bulk actions.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_bulk_actions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide pagination.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function pagination( $which ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide table navigation.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $which
|
||||
*/
|
||||
protected function display_tablenav( $which ) {
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user