Completed
Push — master ( 08c7be...bf151a )
by
unknown
25:52 queued 10s
created

WC_Admin_Report::enable_big_selects()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 10
ccs 5
cts 5
cp 1
crap 2
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * Admin Report.
9
 *
10
 * Extended by reports to show charts and stats in admin.
11
 *
12
 * @author      WooThemes
13
 * @category    Admin
14
 * @package     WooCommerce/Admin/Reports
15
 * @version     2.1.0
16
 */
17
class WC_Admin_Report {
18
19
	/**
20
	 * @var array List of transients name that have been updated and need persisting.
21
	 */
22
	protected static $transients_to_update = array();
23
	
24
	/**
25
	 * @var array The list of transients.
26
	 */
27
	protected static $cached_results = array();
28
29
	/**
30
	 * The chart interval.
31
	 *
32
	 * @var int
33
	 */
34
	public $chart_interval;
35
36
	/**
37
	 * Group by SQL query.
38
	 *
39
	 * @var string
40
	 */
41
	public $group_by_query;
42
43
	/**
44
	 * The bar width.
45
	 *
46
	 * @var int
47
	 */
48
	public $barwidth;
49
50
	/**
51
	 * Group chart item by day or month.
52
	 *
53
	 * @var string
54
	 */
55
	public $chart_groupby;
56
57
	/**
58
	 * The start date of the report.
59
	 *
60
	 * @var int timestamp
61
	 */
62
	public $start_date;
63
64
	/**
65
	 * The end date of the report.
66
	 *
67
	 * @var int timestamp
68
	 */
69
	public $end_date;
70
71
	/**
72
	 * Get report totals such as order totals and discount amounts.
73
	 *
74
	 * Data example:
75
	 *
76
	 * '_order_total' => array(
77
	 *     'type'     => 'meta',
78
	 *     'function' => 'SUM',
79
	 *     'name'     => 'total_sales'
80
	 * )
81
	 *
82
	 * @param  array $args
83
	 * @return mixed depending on query_type
84
	 */
85 7
	public function get_order_report_data( $args = array() ) {
86
		global $wpdb;
87
88
		$default_args = array(
89 7
			'data'                => array(),
90
			'where'               => array(),
91
			'where_meta'          => array(),
92 7
			'query_type'          => 'get_row',
93 7
			'group_by'            => '',
94 7
			'order_by'            => '',
95 7
			'limit'               => '',
96
			'filter_range'        => false,
97
			'nocache'             => false,
98
			'debug'               => false,
99 7
			'order_types'         => wc_get_order_types( 'reports' ),
100
			'order_status'        => array( 'completed', 'processing', 'on-hold' ),
101
			'parent_order_status' => false,
102
		);
103 7
		$args         = apply_filters( 'woocommerce_reports_get_order_report_data_args', $args );
104 7
		$args         = wp_parse_args( $args, $default_args );
105
106 7
		extract( $args );
0 ignored issues
show
introduced by
extract() usage is highly discouraged, due to the complexity and unintended issues it might cause.
Loading history...
107
108 7
		if ( empty( $data ) ) {
109 1
			return '';
110
		}
111
112 6
		$order_status = apply_filters( 'woocommerce_reports_order_statuses', $order_status );
113
114 6
		$query  = array();
115 6
		$select = array();
116
117 6
		foreach ( $data as $raw_key => $value ) {
118 6
			$key      = sanitize_key( $raw_key );
119 6
			$distinct = '';
120
121 6
			if ( isset( $value['distinct'] ) ) {
122 1
				$distinct = 'DISTINCT';
123
			}
124
125 6
			switch ( $value['type'] ) {
126
				case 'meta':
127 2
					$get_key = "meta_{$key}.meta_value";
128 2
					break;
129
				case 'parent_meta':
130 2
					$get_key = "parent_meta_{$key}.meta_value";
131 2
					break;
132
				case 'post_data':
133 3
					$get_key = "posts.{$key}";
134 3
					break;
135
				case 'order_item_meta':
136 1
					$get_key = "order_item_meta_{$key}.meta_value";
137 1
					break;
138
				case 'order_item':
139 2
					$get_key = "order_items.{$key}";
140 2
					break;
141
			}
142
143 6
			if ( empty( $get_key ) ) {
144
				// Skip to the next foreach iteration else the query will be invalid.
145
				continue;
146
			}
147
148 6
			if ( $value['function'] ) {
149 2
				$get = "{$value['function']}({$distinct} {$get_key})";
150
			} else {
151 5
				$get = "{$distinct} {$get_key}";
152
			}
153
154 6
			$select[] = "{$get} as {$value['name']}";
155
		}
156
157 6
		$query['select'] = 'SELECT ' . implode( ',', $select );
158 6
		$query['from']   = "FROM {$wpdb->posts} AS posts";
159
160
		// Joins
161 6
		$joins = array();
162
163 6
		foreach ( ( $data + $where ) as $raw_key => $value ) {
164 6
			$join_type = isset( $value['join_type'] ) ? $value['join_type'] : 'INNER';
165 6
			$type      = isset( $value['type'] ) ? $value['type'] : false;
166 6
			$key       = sanitize_key( $raw_key );
167
168 6
			switch ( $type ) {
169 1 View Code Duplication
				case 'meta':
170 2
					$joins[ "meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS meta_{$key} ON ( posts.ID = meta_{$key}.post_id AND meta_{$key}.meta_key = '{$raw_key}' )";
171 2
					break;
172 1 View Code Duplication
				case 'parent_meta':
173 2
					$joins[ "parent_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS parent_meta_{$key} ON (posts.post_parent = parent_meta_{$key}.post_id) AND (parent_meta_{$key}.meta_key = '{$raw_key}')";
174 2
					break;
175 1
				case 'order_item_meta':
176 1
					$joins['order_items'] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON (posts.ID = order_items.order_id)";
177
178 1
					if ( ! empty( $value['order_item_type'] ) ) {
179 1
						$joins['order_items'] .= " AND (order_items.order_item_type = '{$value['order_item_type']}')";
180
					}
181
182 1
					$joins[ "order_item_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$key} ON " .
183 1
														"(order_items.order_item_id = order_item_meta_{$key}.order_item_id) " .
184 1
														" AND (order_item_meta_{$key}.meta_key = '{$raw_key}')";
185 1
					break;
186 1
				case 'order_item':
187 2
					$joins['order_items'] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_items.order_id";
188 2
					break;
189
			}
190
		}
191
192 6
		if ( ! empty( $where_meta ) ) {
193
			foreach ( $where_meta as $value ) {
194
				if ( ! is_array( $value ) ) {
195
					continue;
196
				}
197
				$join_type = isset( $value['join_type'] ) ? $value['join_type'] : 'INNER';
198
				$type      = isset( $value['type'] ) ? $value['type'] : false;
199
				$key       = sanitize_key( is_array( $value['meta_key'] ) ? $value['meta_key'][0] . '_array' : $value['meta_key'] );
200
201
				if ( 'order_item_meta' === $type ) {
202
203
					$joins['order_items']              = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_items AS order_items ON posts.ID = order_items.order_id";
204
					$joins[ "order_item_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_item_meta_{$key} ON order_items.order_item_id = order_item_meta_{$key}.order_item_id";
205
206
				} else {
207
					// If we have a where clause for meta, join the postmeta table
208
					$joins[ "meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS meta_{$key} ON posts.ID = meta_{$key}.post_id";
209
				}
210
			}
211
		}
212
213 6
		if ( ! empty( $parent_order_status ) ) {
214 1
			$joins['parent'] = "LEFT JOIN {$wpdb->posts} AS parent ON posts.post_parent = parent.ID";
215
		}
216
217 6
		$query['join'] = implode( ' ', $joins );
218
219 6
		$query['where'] = "
220 6
			WHERE 	posts.post_type 	IN ( '" . implode( "','", $order_types ) . "' )
221
			";
222
223 6
		if ( ! empty( $order_status ) ) {
224 6
			$query['where'] .= "
225 6
				AND 	posts.post_status 	IN ( 'wc-" . implode( "','wc-", $order_status ) . "')
226
			";
227
		}
228
229 6
		if ( ! empty( $parent_order_status ) ) {
230 1
			if ( ! empty( $order_status ) ) {
231
				$query['where'] .= " AND ( parent.post_status IN ( 'wc-" . implode( "','wc-", $parent_order_status ) . "') OR parent.ID IS NULL ) ";
232
			} else {
233 1
				$query['where'] .= " AND parent.post_status IN ( 'wc-" . implode( "','wc-", $parent_order_status ) . "') ";
234
			}
235
		}
236
237 6
		if ( $filter_range ) {
238 1
			$query['where'] .= "
239 1
				AND 	posts.post_date >= '" . date( 'Y-m-d H:i:s', $this->start_date ) . "'
240 1
				AND 	posts.post_date < '" . date( 'Y-m-d H:i:s', strtotime( '+1 DAY', $this->end_date ) ) . "'
241
			";
242
		}
243
244 6
		if ( ! empty( $where_meta ) ) {
245
246
			$relation = isset( $where_meta['relation'] ) ? $where_meta['relation'] : 'AND';
247
248
			$query['where'] .= ' AND (';
249
250
			foreach ( $where_meta as $index => $value ) {
251
252
				if ( ! is_array( $value ) ) {
253
					continue;
254
				}
255
256
				$key = sanitize_key( is_array( $value['meta_key'] ) ? $value['meta_key'][0] . '_array' : $value['meta_key'] );
257
258 View Code Duplication
				if ( strtolower( $value['operator'] ) == 'in' || strtolower( $value['operator'] ) == 'not in' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
259
260
					if ( is_array( $value['meta_value'] ) ) {
261
						$value['meta_value'] = implode( "','", $value['meta_value'] );
0 ignored issues
show
introduced by
Detected usage of meta_value, possible slow query.
Loading history...
262
					}
263
264
					if ( ! empty( $value['meta_value'] ) ) {
265
						$where_value = "{$value['operator']} ('{$value['meta_value']}')";
266
					}
267
				} else {
268
					$where_value = "{$value['operator']} '{$value['meta_value']}'";
269
				}
270
271
				if ( ! empty( $where_value ) ) {
272
					if ( $index > 0 ) {
273
						$query['where'] .= ' ' . $relation;
274
					}
275
276
					if ( isset( $value['type'] ) && 'order_item_meta' === $value['type'] ) {
277
278 View Code Duplication
						if ( is_array( $value['meta_key'] ) ) {
279
							$query['where'] .= " ( order_item_meta_{$key}.meta_key   IN ('" . implode( "','", $value['meta_key'] ) . "')";
280
						} else {
281
							$query['where'] .= " ( order_item_meta_{$key}.meta_key   = '{$value['meta_key']}'";
282
						}
283
284
						$query['where'] .= " AND order_item_meta_{$key}.meta_value {$where_value} )";
285
					} else {
286
287 View Code Duplication
						if ( is_array( $value['meta_key'] ) ) {
288
							$query['where'] .= " ( meta_{$key}.meta_key   IN ('" . implode( "','", $value['meta_key'] ) . "')";
289
						} else {
290
							$query['where'] .= " ( meta_{$key}.meta_key   = '{$value['meta_key']}'";
291
						}
292
293
						$query['where'] .= " AND meta_{$key}.meta_value {$where_value} )";
294
					}
295
				}
296
			}
297
298
			$query['where'] .= ')';
299
		}
300
301 6
		if ( ! empty( $where ) ) {
302
303 1
			foreach ( $where as $value ) {
304
305 1 View Code Duplication
				if ( strtolower( $value['operator'] ) == 'in' || strtolower( $value['operator'] ) == 'not in' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
306
307
					if ( is_array( $value['value'] ) ) {
308
						$value['value'] = implode( "','", $value['value'] );
309
					}
310
311
					if ( ! empty( $value['value'] ) ) {
312
						$where_value = "{$value['operator']} ('{$value['value']}')";
313
					}
314
				} else {
315 1
					$where_value = "{$value['operator']} '{$value['value']}'";
316
				}
317
318 1
				if ( ! empty( $where_value ) ) {
319 1
					$query['where'] .= " AND {$value['key']} {$where_value}";
320
				}
321
			}
322
		}
323
324 6
		if ( $group_by ) {
325 1
			$query['group_by'] = "GROUP BY {$group_by}";
326
		}
327
328 6
		if ( $order_by ) {
329 1
			$query['order_by'] = "ORDER BY {$order_by}";
330
		}
331
332 6
		if ( $limit ) {
333
			$query['limit'] = "LIMIT {$limit}";
334
		}
335
336 6
		$query          = apply_filters( 'woocommerce_reports_get_order_report_query', $query );
337 6
		$query          = implode( ' ', $query );
338
339 6
		if ( $debug ) {
340
			echo '<pre>';
341
			wc_print_r( $query );
342
			echo '</pre>';
343
		}
344
345 6
		if ( $debug || $nocache ) {
346
			self::enable_big_selects();
347
348
			$result = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data );
349
		} else {
350 6
			$query_hash = md5( $query_type . $query );
351 6
			$result = $this->get_cached_query( $query_hash );
352 6
			if ( $result === null ) {
353 6
				self::enable_big_selects();
354
355 6
				$result = apply_filters( 'woocommerce_reports_get_order_report_data', $wpdb->$query_type( $query ), $data );
356
			}
357 6
			$this->set_cached_query( $query_hash, $result );
358
		}
359
360 6
		return $result;
361
	}
362
363
	/**
364
	 * Init the static hooks of the class.
365
	 */
366 6
	protected static function add_update_transients_hook() {
367 6
		if ( ! has_action( 'shutdown', array( 'WC_Admin_Report', 'maybe_update_transients' ) ) ) {
368 6
			add_action( 'shutdown', array( 'WC_Admin_Report', 'maybe_update_transients' ) );
369
		}
370
	}
371
	
372
	/**
373
	 * Enables big mysql selects for reports, just once for this session.
374
	 */
375 6
	protected static function enable_big_selects() {
376 6
		static $big_selects = false;
377
378
		global $wpdb;
379
380 6
		if ( ! $big_selects ) {
381 1
			$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
382 1
			$big_selects = true;
383
		}
384
	}
385
386
	/**
387
	 * Get the cached query result or null if it's not in the cache.
388
	 *
389
	 * @param string $query_hash The query hash.
390
	 *
391
	 * @return mixed
392
	 */
393 6
	protected function get_cached_query( $query_hash ) {
394 6
		$class = strtolower( get_class( $this ) );
395
		
396 6 View Code Duplication
		if ( ! isset( self::$cached_results[ $class ] ) ) {
397 2
			self::$cached_results[ $class ] = get_transient( strtolower( get_class( $this ) ) );
398
		}
399
		
400 6
		if ( isset( self::$cached_results[ $class ][ $query_hash ] ) ) {
401
			return self::$cached_results[ $class ][ $query_hash ];
402
		}
403
404 6
		return null;
405
	}
406
407
	/**
408
	 * Set the cached query result.
409
	 *
410
	 * @param string $query_hash The query hash.
411
	 * @param mixed  $data The data to cache.
412
	 */
413 6
	protected function set_cached_query( $query_hash, $data ) {
414 6
		$class = strtolower( get_class( $this ) );
415
		
416 6 View Code Duplication
		if ( ! isset( self::$cached_results[ $class ] ) ) {
417
			self::$cached_results[ $class ] = get_transient( strtolower( get_class( $this ) ) );
418
		}
419
		
420 6
		self::add_update_transients_hook();
421
		
422 6
		self::$transients_to_update[ $class ]          = $class;
423 6
		self::$cached_results[ $class ][ $query_hash ] = $data;
424
	}
425
426
	/**
427
	 * Function to update the modified transients at the end of the request.
428
	 */
429 1
	public static function maybe_update_transients() {
430 1
		foreach ( self::$transients_to_update as $key => $transient_name ) {
431 1
			set_transient( $transient_name, self::$cached_results[ $transient_name ], DAY_IN_SECONDS );
432
		}
433
		// Transients have been updated reset the list.
434 1
		self::$transients_to_update = array();
435
	}
436
437
	/**
438
	 * Put data with post_date's into an array of times.
439
	 *
440
	 * @param  array  $data array of your data
441
	 * @param  string $date_key key for the 'date' field. e.g. 'post_date'
442
	 * @param  string $data_key key for the data you are charting
443
	 * @param  int    $interval
444
	 * @param  string $start_date
445
	 * @param  string $group_by
446
	 * @return array
447
	 */
448
	public function prepare_chart_data( $data, $date_key, $data_key, $interval, $start_date, $group_by ) {
449
		$prepared_data = array();
450
451
		// Ensure all days (or months) have values in this range.
452
		if ( 'day' === $group_by ) {
453
			for ( $i = 0; $i <= $interval; $i ++ ) {
454
				$time = strtotime( date( 'Ymd', strtotime( "+{$i} DAY", $start_date ) ) ) . '000';
455
456 View Code Duplication
				if ( ! isset( $prepared_data[ $time ] ) ) {
457
					$prepared_data[ $time ] = array( esc_js( $time ), 0 );
458
				}
459
			}
460
		} else {
461
			$current_yearnum  = date( 'Y', $start_date );
462
			$current_monthnum = date( 'm', $start_date );
463
464
			for ( $i = 0; $i <= $interval; $i ++ ) {
465
				$time = strtotime( $current_yearnum . str_pad( $current_monthnum, 2, '0', STR_PAD_LEFT ) . '01' ) . '000';
466
467 View Code Duplication
				if ( ! isset( $prepared_data[ $time ] ) ) {
468
					$prepared_data[ $time ] = array( esc_js( $time ), 0 );
469
				}
470
471
				$current_monthnum ++;
472
473
				if ( $current_monthnum > 12 ) {
474
					$current_monthnum = 1;
475
					$current_yearnum  ++;
476
				}
477
			}
478
		}
479
480
		foreach ( $data as $d ) {
481
			switch ( $group_by ) {
482
				case 'day':
483
					$time = strtotime( date( 'Ymd', strtotime( $d->$date_key ) ) ) . '000';
484
					break;
485
				case 'month':
486
				default:
487
					$time = strtotime( date( 'Ym', strtotime( $d->$date_key ) ) . '01' ) . '000';
488
					break;
489
			}
490
491
			if ( ! isset( $prepared_data[ $time ] ) ) {
492
				continue;
493
			}
494
495
			if ( $data_key ) {
496
				$prepared_data[ $time ][1] += $d->$data_key;
497
			} else {
498
				$prepared_data[ $time ][1] ++;
499
			}
500
		}
501
502
		return $prepared_data;
503
	}
504
505
	/**
506
	 * Prepares a sparkline to show sales in the last X days.
507
	 *
508
	 * @param  int    $id ID of the product to show. Blank to get all orders.
509
	 * @param  int    $days Days of stats to get.
510
	 * @param  string $type Type of sparkline to get. Ignored if ID is not set.
511
	 * @return string
512
	 */
513
	public function sales_sparkline( $id = '', $days = 7, $type = 'sales' ) {
514
515
		if ( $id ) {
516
			$meta_key = ( 'sales' === $type ) ? '_line_total' : '_qty';
517
518
			$data = $this->get_order_report_data(
519
				array(
520
					'data'         => array(
521
						'_product_id' => array(
522
							'type'            => 'order_item_meta',
523
							'order_item_type' => 'line_item',
524
							'function'        => '',
525
							'name'            => 'product_id',
526
						),
527
						$meta_key     => array(
528
							'type'            => 'order_item_meta',
529
							'order_item_type' => 'line_item',
530
							'function'        => 'SUM',
531
							'name'            => 'sparkline_value',
532
						),
533
						'post_date'   => array(
534
							'type'     => 'post_data',
535
							'function' => '',
536
							'name'     => 'post_date',
537
						),
538
					),
539
					'where'        => array(
540
						array(
541
							'key'      => 'post_date',
542
							'value'    => date( 'Y-m-d', strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ) ),
543
							'operator' => '>',
544
						),
545
						array(
546
							'key'      => 'order_item_meta__product_id.meta_value',
547
							'value'    => $id,
548
							'operator' => '=',
549
						),
550
					),
551
					'group_by'     => 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)',
552
					'query_type'   => 'get_results',
553
					'filter_range' => false,
554
				)
555
			);
556
		} else {
557
558
			$data = $this->get_order_report_data(
559
				array(
560
					'data'         => array(
561
						'_order_total' => array(
562
							'type'     => 'meta',
563
							'function' => 'SUM',
564
							'name'     => 'sparkline_value',
565
						),
566
						'post_date'    => array(
567
							'type'     => 'post_data',
568
							'function' => '',
569
							'name'     => 'post_date',
570
						),
571
					),
572
					'where'        => array(
573
						array(
574
							'key'      => 'post_date',
575
							'value'    => date( 'Y-m-d', strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ) ),
576
							'operator' => '>',
577
						),
578
					),
579
					'group_by'     => 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)',
580
					'query_type'   => 'get_results',
581
					'filter_range' => false,
582
				)
583
			);
584
		}
585
586
		$total = 0;
587
		foreach ( $data as $d ) {
588
			$total += $d->sparkline_value;
589
		}
590
591
		if ( 'sales' === $type ) {
592
			/* translators: 1: total income 2: days */
593
			$tooltip = sprintf( __( 'Sold %1$s worth in the last %2$d days', 'woocommerce' ), strip_tags( wc_price( $total ) ), $days );
594
		} else {
595
			/* translators: 1: total items sold 2: days */
596
			$tooltip = sprintf( _n( 'Sold 1 item in the last %2$d days', 'Sold %1$d items in the last %2$d days', $total, 'woocommerce' ), $total, $days );
597
		}
598
599
		$sparkline_data = array_values( $this->prepare_chart_data( $data, 'post_date', 'sparkline_value', $days - 1, strtotime( 'midnight -' . ( $days - 1 ) . ' days', current_time( 'timestamp' ) ), 'day' ) );
600
601
		return '<span class="wc_sparkline ' . ( ( 'sales' === $type ) ? 'lines' : 'bars' ) . ' tips" data-color="#777" data-tip="' . esc_attr( $tooltip ) . '" data-barwidth="' . 60 * 60 * 16 * 1000 . '" data-sparkline="' . wc_esc_json( wp_json_encode( $sparkline_data ) ) . '"></span>';
602
	}
603
604
	/**
605
	 * Get the current range and calculate the start and end dates.
606
	 *
607
	 * @param  string $current_range
608
	 */
609
	public function calculate_current_range( $current_range ) {
610
611
		switch ( $current_range ) {
612
613
			case 'custom':
614
				$this->start_date = max( strtotime( '-20 years' ), strtotime( sanitize_text_field( $_GET['start_date'] ) ) );
0 ignored issues
show
introduced by
Detected usage of a non-validated input variable: $_GET
Loading history...
615
616
				if ( empty( $_GET['end_date'] ) ) {
617
					$this->end_date = strtotime( 'midnight', current_time( 'timestamp' ) );
618
				} else {
619
					$this->end_date = strtotime( 'midnight', strtotime( sanitize_text_field( $_GET['end_date'] ) ) );
620
				}
621
622
				$interval = 0;
623
				$min_date = $this->start_date;
624
625
				while ( ( $min_date = strtotime( '+1 MONTH', $min_date ) ) <= $this->end_date ) {
626
					$interval ++;
627
				}
628
629
				// 3 months max for day view
630
				if ( $interval > 3 ) {
631
					$this->chart_groupby = 'month';
632
				} else {
633
					$this->chart_groupby = 'day';
634
				}
635
				break;
636
637 View Code Duplication
			case 'year':
638
				$this->start_date    = strtotime( date( 'Y-01-01', current_time( 'timestamp' ) ) );
639
				$this->end_date      = strtotime( 'midnight', current_time( 'timestamp' ) );
640
				$this->chart_groupby = 'month';
641
				break;
642
643
			case 'last_month':
644
				$first_day_current_month = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) );
645
				$this->start_date        = strtotime( date( 'Y-m-01', strtotime( '-1 DAY', $first_day_current_month ) ) );
646
				$this->end_date          = strtotime( date( 'Y-m-t', strtotime( '-1 DAY', $first_day_current_month ) ) );
647
				$this->chart_groupby     = 'day';
648
				break;
649
650 View Code Duplication
			case 'month':
651
				$this->start_date    = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) );
652
				$this->end_date      = strtotime( 'midnight', current_time( 'timestamp' ) );
653
				$this->chart_groupby = 'day';
654
				break;
655
656 View Code Duplication
			case '7day':
657
				$this->start_date    = strtotime( '-6 days', strtotime( 'midnight', current_time( 'timestamp' ) ) );
658
				$this->end_date      = strtotime( 'midnight', current_time( 'timestamp' ) );
659
				$this->chart_groupby = 'day';
660
				break;
661
		}
662
663
		// Group by
664
		switch ( $this->chart_groupby ) {
665
666
			case 'day':
667
				$this->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)';
668
				$this->chart_interval = absint( ceil( max( 0, ( $this->end_date - $this->start_date ) / ( 60 * 60 * 24 ) ) ) );
669
				$this->barwidth       = 60 * 60 * 24 * 1000;
670
				break;
671
672
			case 'month':
673
				$this->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date)';
674
				$this->chart_interval = 0;
675
				$min_date             = strtotime( date( 'Y-m-01', $this->start_date ) );
676
677
				while ( ( $min_date = strtotime( '+1 MONTH', $min_date ) ) <= $this->end_date ) {
678
					$this->chart_interval ++;
679
				}
680
681
				$this->barwidth = 60 * 60 * 24 * 7 * 4 * 1000;
682
				break;
683
		}
684
	}
685
686
	/**
687
	 * Return currency tooltip JS based on WooCommerce currency position settings.
688
	 *
689
	 * @return string
690
	 */
691
	public function get_currency_tooltip() {
692
		switch ( get_option( 'woocommerce_currency_pos' ) ) {
693
			case 'right':
694
				$currency_tooltip = 'append_tooltip: "' . get_woocommerce_currency_symbol() . '"';
695
				break;
696
			case 'right_space':
697
				$currency_tooltip = 'append_tooltip: "&nbsp;' . get_woocommerce_currency_symbol() . '"';
698
				break;
699
			case 'left':
700
				$currency_tooltip = 'prepend_tooltip: "' . get_woocommerce_currency_symbol() . '"';
701
				break;
702
			case 'left_space':
703
			default:
704
				$currency_tooltip = 'prepend_tooltip: "' . get_woocommerce_currency_symbol() . '&nbsp;"';
705
				break;
706
		}
707
708
		return $currency_tooltip;
709
	}
710
711
	/**
712
	 * Get the main chart.
713
	 */
714
	public function get_main_chart() {}
715
716
	/**
717
	 * Get the legend for the main chart sidebar.
718
	 *
719
	 * @return array
720
	 */
721
	public function get_chart_legend() {
722
		return array();
723
	}
724
725
	/**
726
	 * Get chart widgets.
727
	 *
728
	 * @return array
729
	 */
730
	public function get_chart_widgets() {
731
		return array();
732
	}
733
734
	/**
735
	 * Get an export link if needed.
736
	 */
737
	public function get_export_button() {}
738
739
	/**
740
	 * Output the report.
741
	 */
742
	public function output_report() {}
743
744
	/**
745
	 * Check nonce for current range.
746
	 *
747
	 * @since  3.0.4
748
	 * @param  string $current_range Current range.
749
	 */
750
	public function check_current_range_nonce( $current_range ) {
751
		if ( 'custom' !== $current_range ) {
752
			return;
753
		}
754
755
		if ( ! isset( $_GET['wc_reports_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['wc_reports_nonce'] ), 'custom_range' ) ) { // WPCS: input var ok, CSRF ok.
756
			wp_die(
757
				/* translators: %1$s: open link, %2$s: close link */
758
				sprintf( esc_html__( 'This report link has expired. %1$sClick here to view the filtered report%2$s.', 'woocommerce' ), '<a href="' . esc_url( wp_nonce_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 'custom_range', 'wc_reports_nonce' ) ) . '">', '</a>' ), // @codingStandardsIgnoreLine.
759
				esc_attr__( 'Confirm navigation', 'woocommerce' )
760
			);
761
			exit;
762
		}
763
	}
764
}
765