Edit File by line
/home/zeestwma/richards.../wp-conte.../plugins/woocomme.../src/Admin
File: ReportCSVExporter.php
<?php
[0] Fix | Delete
/**
[1] Fix | Delete
* Handles reports CSV export batches.
[2] Fix | Delete
*/
[3] Fix | Delete
[4] Fix | Delete
namespace Automattic\WooCommerce\Admin;
[5] Fix | Delete
[6] Fix | Delete
if ( ! defined( 'ABSPATH' ) ) {
[7] Fix | Delete
exit;
[8] Fix | Delete
}
[9] Fix | Delete
[10] Fix | Delete
use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface;
[11] Fix | Delete
[12] Fix | Delete
/**
[13] Fix | Delete
* Include dependencies.
[14] Fix | Delete
*/
[15] Fix | Delete
if ( ! class_exists( 'WC_CSV_Batch_Exporter', false ) ) {
[16] Fix | Delete
include_once WC_ABSPATH . 'includes/export/abstract-wc-csv-batch-exporter.php';
[17] Fix | Delete
}
[18] Fix | Delete
[19] Fix | Delete
/**
[20] Fix | Delete
* ReportCSVExporter Class.
[21] Fix | Delete
*/
[22] Fix | Delete
class ReportCSVExporter extends \WC_CSV_Batch_Exporter {
[23] Fix | Delete
/**
[24] Fix | Delete
* Type of report being exported.
[25] Fix | Delete
*
[26] Fix | Delete
* @var string
[27] Fix | Delete
*/
[28] Fix | Delete
protected $report_type;
[29] Fix | Delete
[30] Fix | Delete
/**
[31] Fix | Delete
* Parameters for the report query.
[32] Fix | Delete
*
[33] Fix | Delete
* @var array
[34] Fix | Delete
*/
[35] Fix | Delete
protected $report_args;
[36] Fix | Delete
[37] Fix | Delete
/**
[38] Fix | Delete
* REST controller for the report.
[39] Fix | Delete
*
[40] Fix | Delete
* @var WC_REST_Reports_Controller
[41] Fix | Delete
*/
[42] Fix | Delete
protected $controller;
[43] Fix | Delete
[44] Fix | Delete
/**
[45] Fix | Delete
* Constructor.
[46] Fix | Delete
*
[47] Fix | Delete
* @param string $type Report type. E.g. 'customers'.
[48] Fix | Delete
* @param array $args Report parameters.
[49] Fix | Delete
*/
[50] Fix | Delete
public function __construct( $type = false, $args = array() ) {
[51] Fix | Delete
parent::__construct();
[52] Fix | Delete
[53] Fix | Delete
self::maybe_create_directory();
[54] Fix | Delete
[55] Fix | Delete
if ( ! empty( $type ) ) {
[56] Fix | Delete
$this->set_report_type( $type );
[57] Fix | Delete
$this->set_column_names( $this->get_report_columns() );
[58] Fix | Delete
}
[59] Fix | Delete
[60] Fix | Delete
if ( ! empty( $args ) ) {
[61] Fix | Delete
$this->set_report_args( $args );
[62] Fix | Delete
}
[63] Fix | Delete
}
[64] Fix | Delete
[65] Fix | Delete
/**
[66] Fix | Delete
* Create the directory for reports if it does not yet exist.
[67] Fix | Delete
*/
[68] Fix | Delete
public static function maybe_create_directory() {
[69] Fix | Delete
$reports_dir = self::get_reports_directory();
[70] Fix | Delete
[71] Fix | Delete
$files = array(
[72] Fix | Delete
array(
[73] Fix | Delete
'base' => $reports_dir,
[74] Fix | Delete
'file' => '.htaccess',
[75] Fix | Delete
'content' => 'DirectoryIndex index.php index.html' . PHP_EOL . 'deny from all',
[76] Fix | Delete
),
[77] Fix | Delete
array(
[78] Fix | Delete
'base' => $reports_dir,
[79] Fix | Delete
'file' => 'index.html',
[80] Fix | Delete
'content' => '',
[81] Fix | Delete
),
[82] Fix | Delete
);
[83] Fix | Delete
[84] Fix | Delete
foreach ( $files as $file ) {
[85] Fix | Delete
if ( ! file_exists( trailingslashit( $file['base'] ) ) ) {
[86] Fix | Delete
wp_mkdir_p( $file['base'] );
[87] Fix | Delete
}
[88] Fix | Delete
if ( ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
[89] Fix | Delete
$file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'wb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen
[90] Fix | Delete
if ( $file_handle ) {
[91] Fix | Delete
fwrite( $file_handle, $file['content'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
[92] Fix | Delete
fclose( $file_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
[93] Fix | Delete
}
[94] Fix | Delete
}
[95] Fix | Delete
}
[96] Fix | Delete
}
[97] Fix | Delete
[98] Fix | Delete
/**
[99] Fix | Delete
* Get report uploads directory.
[100] Fix | Delete
*
[101] Fix | Delete
* @return string
[102] Fix | Delete
*/
[103] Fix | Delete
public static function get_reports_directory() {
[104] Fix | Delete
$upload_dir = wp_upload_dir();
[105] Fix | Delete
return trailingslashit( $upload_dir['basedir'] ) . 'woocommerce_uploads/reports/';
[106] Fix | Delete
}
[107] Fix | Delete
[108] Fix | Delete
/**
[109] Fix | Delete
* Get file path to export to.
[110] Fix | Delete
*
[111] Fix | Delete
* @return string
[112] Fix | Delete
*/
[113] Fix | Delete
protected function get_file_path() {
[114] Fix | Delete
return self::get_reports_directory() . $this->get_filename();
[115] Fix | Delete
}
[116] Fix | Delete
[117] Fix | Delete
[118] Fix | Delete
/**
[119] Fix | Delete
* Setter for report type.
[120] Fix | Delete
*
[121] Fix | Delete
* @param string $type The report type. E.g. customers.
[122] Fix | Delete
*/
[123] Fix | Delete
public function set_report_type( $type ) {
[124] Fix | Delete
$this->report_type = $type;
[125] Fix | Delete
$this->export_type = "admin_{$type}_report";
[126] Fix | Delete
$this->filename = "wc-{$type}-report-export";
[127] Fix | Delete
$this->controller = $this->map_report_controller();
[128] Fix | Delete
}
[129] Fix | Delete
[130] Fix | Delete
/**
[131] Fix | Delete
* Setter for report args.
[132] Fix | Delete
*
[133] Fix | Delete
* @param array $args The report args.
[134] Fix | Delete
*/
[135] Fix | Delete
public function set_report_args( $args ) {
[136] Fix | Delete
// Use our own internal limit and include all extended info.
[137] Fix | Delete
$report_args = array_merge(
[138] Fix | Delete
$args,
[139] Fix | Delete
array(
[140] Fix | Delete
'per_page' => $this->get_limit(),
[141] Fix | Delete
'extended_info' => true,
[142] Fix | Delete
)
[143] Fix | Delete
);
[144] Fix | Delete
[145] Fix | Delete
// Should this happen externally?
[146] Fix | Delete
if ( isset( $report_args['page'] ) ) {
[147] Fix | Delete
$this->set_page( $report_args['page'] );
[148] Fix | Delete
}
[149] Fix | Delete
[150] Fix | Delete
$this->report_args = $report_args;
[151] Fix | Delete
}
[152] Fix | Delete
[153] Fix | Delete
/**
[154] Fix | Delete
* Get a REST controller instance for the report type.
[155] Fix | Delete
*
[156] Fix | Delete
* @return bool|WC_REST_Reports_Controller Report controller instance or boolean false on error.
[157] Fix | Delete
*/
[158] Fix | Delete
protected function map_report_controller() {
[159] Fix | Delete
/**
[160] Fix | Delete
* Used to add custom report controllers.
[161] Fix | Delete
*
[162] Fix | Delete
* @since x.x.x
[163] Fix | Delete
*
[164] Fix | Delete
* @params array $controller_map A report type to report controller class map.
[165] Fix | Delete
*
[166] Fix | Delete
* @returns array Report type to report controller class map.
[167] Fix | Delete
*/
[168] Fix | Delete
$controller_map = apply_filters(
[169] Fix | Delete
'woocommerce_export_report_controller_map',
[170] Fix | Delete
array(
[171] Fix | Delete
'products' => 'Automattic\WooCommerce\Admin\API\Reports\Products\Controller',
[172] Fix | Delete
'variations' => 'Automattic\WooCommerce\Admin\API\Reports\Variations\Controller',
[173] Fix | Delete
'orders' => 'Automattic\WooCommerce\Admin\API\Reports\Orders\Controller',
[174] Fix | Delete
'categories' => 'Automattic\WooCommerce\Admin\API\Reports\Categories\Controller',
[175] Fix | Delete
'taxes' => 'Automattic\WooCommerce\Admin\API\Reports\Taxes\Controller',
[176] Fix | Delete
'coupons' => 'Automattic\WooCommerce\Admin\API\Reports\Coupons\Controller',
[177] Fix | Delete
'stock' => 'Automattic\WooCommerce\Admin\API\Reports\Stock\Controller',
[178] Fix | Delete
'downloads' => 'Automattic\WooCommerce\Admin\API\Reports\Downloads\Controller',
[179] Fix | Delete
'customers' => 'Automattic\WooCommerce\Admin\API\Reports\Customers\Controller',
[180] Fix | Delete
'revenue' => 'Automattic\WooCommerce\Admin\API\Reports\Revenue\Stats\Controller',
[181] Fix | Delete
)
[182] Fix | Delete
);
[183] Fix | Delete
[184] Fix | Delete
if ( isset( $controller_map[ $this->report_type ] ) ) {
[185] Fix | Delete
// Load the controllers if accessing outside the REST API.
[186] Fix | Delete
return new $controller_map[ $this->report_type ]();
[187] Fix | Delete
}
[188] Fix | Delete
[189] Fix | Delete
// Should this do something else?
[190] Fix | Delete
return false;
[191] Fix | Delete
}
[192] Fix | Delete
[193] Fix | Delete
/**
[194] Fix | Delete
* Get the report columns from the controller.
[195] Fix | Delete
*
[196] Fix | Delete
* @return array Array of report column names.
[197] Fix | Delete
*/
[198] Fix | Delete
protected function get_report_columns() {
[199] Fix | Delete
// Default to the report's defined export columns.
[200] Fix | Delete
if ( $this->controller instanceof ExportableInterface ) {
[201] Fix | Delete
return $this->controller->get_export_columns();
[202] Fix | Delete
}
[203] Fix | Delete
[204] Fix | Delete
// Fallback to generating columns from the report schema.
[205] Fix | Delete
$report_columns = array();
[206] Fix | Delete
$report_schema = $this->controller->get_item_schema();
[207] Fix | Delete
[208] Fix | Delete
if ( isset( $report_schema['properties'] ) ) {
[209] Fix | Delete
foreach ( $report_schema['properties'] as $column_name => $column_info ) {
[210] Fix | Delete
// Expand extended info columns into export.
[211] Fix | Delete
if ( 'extended_info' === $column_name ) {
[212] Fix | Delete
// Remove columns with questionable CSV values, like markup.
[213] Fix | Delete
$extended_info = array_diff( array_keys( $column_info ), array( 'image' ) );
[214] Fix | Delete
$report_columns = array_merge( $report_columns, $extended_info );
[215] Fix | Delete
} else {
[216] Fix | Delete
$report_columns[] = $column_name;
[217] Fix | Delete
}
[218] Fix | Delete
}
[219] Fix | Delete
}
[220] Fix | Delete
[221] Fix | Delete
return $report_columns;
[222] Fix | Delete
}
[223] Fix | Delete
[224] Fix | Delete
/**
[225] Fix | Delete
* Get total % complete.
[226] Fix | Delete
*
[227] Fix | Delete
* Forces an int from parent::get_percent_complete(), which can return a float.
[228] Fix | Delete
*
[229] Fix | Delete
* @return int Percent complete.
[230] Fix | Delete
*/
[231] Fix | Delete
public function get_percent_complete() {
[232] Fix | Delete
return intval( parent::get_percent_complete() );
[233] Fix | Delete
}
[234] Fix | Delete
[235] Fix | Delete
/**
[236] Fix | Delete
* Get total number of rows in export.
[237] Fix | Delete
*
[238] Fix | Delete
* @return int Number of rows to export.
[239] Fix | Delete
*/
[240] Fix | Delete
public function get_total_rows() {
[241] Fix | Delete
return $this->total_rows;
[242] Fix | Delete
}
[243] Fix | Delete
[244] Fix | Delete
/**
[245] Fix | Delete
* Prepare data for export.
[246] Fix | Delete
*/
[247] Fix | Delete
public function prepare_data_to_export() {
[248] Fix | Delete
/**
[249] Fix | Delete
* Used to add/overwrite report data endpoint.
[250] Fix | Delete
*
[251] Fix | Delete
* @since x.x.x
[252] Fix | Delete
*
[253] Fix | Delete
* @param string $endpoint The report's data endpoint.
[254] Fix | Delete
* @param string $type The report's type.
[255] Fix | Delete
*
[256] Fix | Delete
* @returns string The report's endpoint.
[257] Fix | Delete
*/
[258] Fix | Delete
$report_endpoint = apply_filters(
[259] Fix | Delete
'woocommerce_export_report_data_endpoint',
[260] Fix | Delete
"/wc-analytics/reports/{$this->report_type}",
[261] Fix | Delete
$this->report_type
[262] Fix | Delete
);
[263] Fix | Delete
[264] Fix | Delete
$request = new \WP_REST_Request( 'GET', $report_endpoint );
[265] Fix | Delete
$params = $this->controller->get_collection_params();
[266] Fix | Delete
$defaults = array();
[267] Fix | Delete
[268] Fix | Delete
foreach ( $params as $arg => $options ) {
[269] Fix | Delete
if ( isset( $options['default'] ) ) {
[270] Fix | Delete
$defaults[ $arg ] = $options['default'];
[271] Fix | Delete
}
[272] Fix | Delete
}
[273] Fix | Delete
[274] Fix | Delete
$request->set_attributes( array( 'args' => $params ) );
[275] Fix | Delete
$request->set_default_params( $defaults );
[276] Fix | Delete
$request->set_query_params( $this->report_args );
[277] Fix | Delete
$request->sanitize_params();
[278] Fix | Delete
[279] Fix | Delete
// Does the controller have an export-specific item retrieval method?
[280] Fix | Delete
// @todo - Potentially revisit. This is only for /revenue/stats/.
[281] Fix | Delete
if ( is_callable( array( $this->controller, 'get_export_items' ) ) ) {
[282] Fix | Delete
$response = $this->controller->get_export_items( $request );
[283] Fix | Delete
} else {
[284] Fix | Delete
$response = $this->controller->get_items( $request );
[285] Fix | Delete
}
[286] Fix | Delete
[287] Fix | Delete
// Use WP_REST_Server::response_to_data() to embed links in data.
[288] Fix | Delete
add_filter( 'woocommerce_rest_check_permissions', '__return_true' );
[289] Fix | Delete
$rest_server = rest_get_server();
[290] Fix | Delete
$report_data = $rest_server->response_to_data( $response, true );
[291] Fix | Delete
remove_filter( 'woocommerce_rest_check_permissions', '__return_true' );
[292] Fix | Delete
[293] Fix | Delete
$report_meta = $response->get_headers();
[294] Fix | Delete
$this->total_rows = $report_meta['X-WP-Total'];
[295] Fix | Delete
$this->row_data = array_map( array( $this, 'generate_row_data' ), $report_data );
[296] Fix | Delete
}
[297] Fix | Delete
[298] Fix | Delete
/**
[299] Fix | Delete
* Generate row data from a raw report item.
[300] Fix | Delete
*
[301] Fix | Delete
* @param object $item Report item data.
[302] Fix | Delete
* @return array CSV row data.
[303] Fix | Delete
*/
[304] Fix | Delete
protected function get_raw_row_data( $item ) {
[305] Fix | Delete
$columns = $this->get_column_names();
[306] Fix | Delete
$row = array();
[307] Fix | Delete
[308] Fix | Delete
// Expand extended info.
[309] Fix | Delete
if ( isset( $item['extended_info'] ) ) {
[310] Fix | Delete
// Pull extended info property from report item object.
[311] Fix | Delete
$extended_info = (array) $item['extended_info'];
[312] Fix | Delete
unset( $item['extended_info'] );
[313] Fix | Delete
[314] Fix | Delete
// Merge extended info columns into report item object.
[315] Fix | Delete
$item = array_merge( $item, $extended_info );
[316] Fix | Delete
}
[317] Fix | Delete
[318] Fix | Delete
foreach ( $columns as $column_id => $column_name ) {
[319] Fix | Delete
$value = isset( $item[ $column_name ] ) ? $item[ $column_name ] : null;
[320] Fix | Delete
[321] Fix | Delete
if ( has_filter( "woocommerce_export_{$this->export_type}_column_{$column_name}" ) ) {
[322] Fix | Delete
// Filter for 3rd parties.
[323] Fix | Delete
$value = apply_filters( "woocommerce_export_{$this->export_type}_column_{$column_name}", '', $item );
[324] Fix | Delete
[325] Fix | Delete
} elseif ( is_callable( array( $this, "get_column_value_{$column_name}" ) ) ) {
[326] Fix | Delete
// Handle special columns which don't map 1:1 to item data.
[327] Fix | Delete
$value = $this->{"get_column_value_{$column_name}"}( $item, $this->export_type );
[328] Fix | Delete
[329] Fix | Delete
} elseif ( ! is_scalar( $value ) ) {
[330] Fix | Delete
// Ensure that the value is somewhat readable in CSV.
[331] Fix | Delete
$value = wp_json_encode( $value );
[332] Fix | Delete
}
[333] Fix | Delete
[334] Fix | Delete
$row[ $column_id ] = $value;
[335] Fix | Delete
}
[336] Fix | Delete
[337] Fix | Delete
return $row;
[338] Fix | Delete
}
[339] Fix | Delete
[340] Fix | Delete
/**
[341] Fix | Delete
* Get the export row for a given report item.
[342] Fix | Delete
*
[343] Fix | Delete
* @param object $item Report item data.
[344] Fix | Delete
* @return array CSV row data.
[345] Fix | Delete
*/
[346] Fix | Delete
protected function generate_row_data( $item ) {
[347] Fix | Delete
// Default to the report's export method.
[348] Fix | Delete
if ( $this->controller instanceof ExportableInterface ) {
[349] Fix | Delete
$row = $this->controller->prepare_item_for_export( $item );
[350] Fix | Delete
} else {
[351] Fix | Delete
// Fallback to raw report data.
[352] Fix | Delete
$row = $this->get_raw_row_data( $item );
[353] Fix | Delete
}
[354] Fix | Delete
[355] Fix | Delete
return apply_filters( "woocommerce_export_{$this->export_type}_row_data", $row, $item );
[356] Fix | Delete
}
[357] Fix | Delete
}
[358] Fix | Delete
[359] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function