Test Failed
Push — master ( cd44a0...d8bbfb )
by Devin
01:14
created

Give_Tools_Recount_All_Stats::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Recount all donation counts and income stats
4
 *
5
 * This class handles batch processing of recounting donations and income stat totals
6
 *
7
 * @subpackage Admin/Tools/Give_Tools_Recount_All_Stats
8
 * @copyright  Copyright (c) 2016, WordImpress
9
 * @license    https://opensource.org/licenses/gpl-license GNU Public License
10
 * @since      1.5
11
 */
12
13
// Exit if accessed directly.
14
if ( ! defined( 'ABSPATH' ) ) {
15
	exit;
16
}
17
18
/**
19
 * Give_Tools_Recount_All_Stats Class
20
 *
21
 * @since 1.5
22
 */
23
class Give_Tools_Recount_All_Stats extends Give_Batch_Export {
24
25
	/**
26
	 * Our export type. Used for export-type specific filters/actions
27
	 *
28
	 * @since 1.5
29
	 * @var   string
30
	 */
31
	public $export_type = '';
32
33
	/**
34
	 * Allows for a non-form batch processing to be run.
35
	 *
36
	 * @since 1.5
37
	 * @var   bool
38
	 */
39
	public $is_void = true;
40
41
	/**
42
	 * Sets the number of items to pull on each step
43
	 *
44
	 * @since 1.5
45
	 * @var   int
46
	 */
47
	public $per_step = 30;
48
49
	/**
50
	 * Display message on completing recount process
51
	 *
52
	 * @since 1.8.9
53
	 * @var   string
54
	 */
55
	public $message = '';
56
57
	/**
58
	 * Sets donation form id for recalculation
59
	 *
60
	 * @since 1.8.9
61
	 * @var   int
62
	 */
63
	protected $form_id = 0;
64
65
	/**
66
	 * Is Recount process completed
67
	 *
68
	 * @since 1.8.9
69
	 * @var   bool
70
	 */
71
	public $done = false;
72
73
	/**
74
	 * Constructor.
75
	 */
76
	public function __construct( $_step = 1 ) {
77
		parent::__construct( $_step );
78
79
		$this->is_writable = true;
80
	}
81
82
	/**
83
	 * Get the recount all stats data
84
	 *
85
	 * @access public
86
	 * @since  1.5
87
	 *
88
	 * @return bool
89
	 */
90
	public function get_data() {
91
92
		$totals             = $this->get_stored_data( 'give_temp_recount_all_stats' );
93
		$payment_items      = $this->get_stored_data( 'give_temp_payment_items' );
94
		$processed_payments = $this->get_stored_data( 'give_temp_processed_payments' );
95
		$accepted_statuses  = apply_filters( 'give_recount_accepted_statuses', array( 'publish' ) );
96
97
		if ( false === $totals ) {
98
			$totals = array();
99
		}
100
101
		if ( false === $payment_items ) {
102
			$payment_items = array();
103
		}
104
105
		if ( false === $processed_payments ) {
106
			$processed_payments = array();
107
		}
108
109
		$all_forms = $this->get_stored_data( 'give_temp_form_ids' );
110
111
		$payments = $this->get_stored_data( 'give_temp_all_payments_data' );
112
113
		if ( empty( $payments ) ) {
114
			$args = apply_filters( 'give_recount_form_stats_args', array(
115
				'give_forms' => $all_forms,
116
				'number'     => $this->per_step,
117
				'status'     => $accepted_statuses,
118
				'paged'      => $this->step,
119
				'output'     => 'give_payments',
120
			) );
121
122
			$payments_query = new Give_Payments_Query( $args );
123
			$payments       = $payments_query->get_payments();
124
		}
125
126
		if ( ! empty( $payments ) ) {
127
128
			//Loop through payments
129
			foreach ( $payments as $payment ) {
130
131
				$payment_id = ( ! empty( $payment['ID'] ) ? absint( $payment['ID'] ) : ( ! empty( $payment->ID ) ? absint( $payment->ID ) : false ) );
132
				$payment = new Give_Payment( $payment_id );
133
134
				// Prevent payments that have all ready been retrieved from a previous sales log from counting again.
135
				if ( in_array( $payment->ID, $processed_payments ) ) {
136
					continue;
137
				}
138
139
				// Verify accepted status.
140
				if ( ! in_array( $payment->post_status, $accepted_statuses ) ) {
141
					$processed_payments[] = $payment->ID;
142
					continue;
143
				}
144
145
				$payment_item = $payment_items[ $payment->ID ];
146
147
				$form_id = isset( $payment_item['id'] ) ? $payment_item['id'] : '';
148
149
				// Must have a form ID.
150
				if ( empty( $form_id ) ) {
151
					continue;
152
				}
153
154
				// Form ID must be within $all_forms array to be validated.
155
				if ( ! in_array( $form_id, $all_forms ) ) {
156
					continue;
157
				}
158
159
				// Set Sales count
160
				$totals[ $form_id ]['sales'] = isset( $totals[ $form_id ]['sales'] ) ?
161
					++ $totals[ $form_id ]['sales'] :
162
					1;
163
164
				// Set Total Earnings
165
				$totals[ $form_id ]['earnings'] = isset( $totals[ $form_id ]['earnings'] ) ?
166
					( $totals[ $form_id ]['earnings'] + $payment_item['price'] ) :
167
					$payment_item['price'];
168
169
				$processed_payments[] = $payment->ID;
170
			}
171
172
			// Get the list of form ids which does not contain any payment record.
173
			$remaining_form_ids = array_diff( $all_forms, array_keys( $totals ) );
174
			foreach ( $remaining_form_ids as $form_id ) {
175
				//If array key doesn't exist, create it
176
				if ( ! array_key_exists( $form_id, $totals ) ) {
177
					$totals[ $form_id ] = array(
178
						'sales'    => (int) 0,
179
						'earnings' => (float) 0,
180
					);
181
				}
182
			}
183
184
			$this->store_data( 'give_temp_processed_payments', $processed_payments );
185
			$this->store_data( 'give_temp_recount_all_stats', $totals );
186
187
			return true;
188
		}
189
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
190
191
		foreach ( $totals as $key => $stats ) {
192
			give_update_meta( $key, '_give_form_sales', $stats['sales'] );
193
			give_update_meta( $key, '_give_form_earnings', give_sanitize_amount_for_db( $stats['earnings'] ) );
194
		}
195
196
		return false;
197
198
	}
199
200
	/**
201
	 * Return the calculated completion percentage
202
	 *
203
	 * @since 1.5
204
	 * @return int
205
	 */
206
	public function get_percentage_complete() {
207
208
		$total = $this->get_stored_data( 'give_recount_all_total' );
209
210
		if ( false === $total ) {
211
			$this->pre_fetch();
212
			$total = $this->get_stored_data( 'give_recount_all_total' );
213
		}
214
215
		$percentage = 100;
216
217
		if ( $total > 0 ) {
218
			$percentage = ( ( $this->per_step * $this->step ) / $total ) * 100;
219
		}
220
221
		if ( $percentage > 100 ) {
222
			$percentage = 100;
223
		}
224
225
		return $percentage;
226
	}
227
228
	/**
229
	 * Set the properties specific to the payments export
230
	 *
231
	 * @since 1.5
232
	 *
233
	 * @param array $request The Form Data passed into the batch processing
234
	 */
235
	public function set_properties( $request ) {
236
		$this->form_id = isset( $request['form_id'] ) ? sanitize_text_field( $request['form_id'] ) : false;
237
	}
238
239
	/**
240
	 * Process a step
241
	 *
242
	 * @since 1.5
243
	 * @return bool
244
	 */
245
	public function process_step() {
246
247
		if ( ! $this->can_export() ) {
248
			wp_die( esc_html__( 'You do not have permission to recount stats.', 'give' ), esc_html__( 'Error', 'give' ), array( 'response' => 403 ) );
249
		}
250
251
		$had_data = $this->get_data();
252
253
		if ( $had_data ) {
254
			$this->done = false;
255
256
			return true;
257
		} else {
258
			$this->delete_data( 'give_recount_total_' . $this->form_id );
259
			$this->delete_data( 'give_recount_all_total' );
260
			$this->delete_data( 'give_temp_recount_all_stats' );
261
			$this->delete_data( 'give_temp_payment_items' );
262
			$this->delete_data( 'give_temp_form_ids' );
263
			$this->delete_data( 'give_temp_processed_payments' );
264
			$this->done    = true;
265
			$this->message = esc_html__( 'Donation form income amounts and donation counts stats successfully recounted.', 'give' );
266
267
			return false;
268
		}
269
	}
270
271
	/**
272
	 * Set headers.
273
	 */
274
	public function headers() {
275
		give_ignore_user_abort();
276
	}
277
278
	/**
279
	 * Perform the export
280
	 *
281
	 * @access public
282
	 * @since  1.5
283
	 * @return void
284
	 */
285
	public function export() {
286
287
		// Set headers
288
		$this->headers();
289
290
		give_die();
291
	}
292
293
	/**
294
	 * Pre Fetch Data
295
	 *
296
	 * @access public
297
	 * @since  1.5
298
	 */
299
	public function pre_fetch() {
300
301
		if ( 1 == $this->step ) {
302
			$this->delete_data( 'give_temp_recount_all_total' );
303
			$this->delete_data( 'give_temp_recount_all_stats' );
304
			$this->delete_data( 'give_temp_payment_items' );
305
			$this->delete_data( 'give_temp_processed_payments' );
306
			$this->delete_data( 'give_temp_all_payments_data' );
307
		}
308
309
		$accepted_statuses = apply_filters( 'give_recount_accepted_statuses', array( 'publish' ) );
310
		$total             = $this->get_stored_data( 'give_temp_recount_all_total' );
311
312
		if ( false === $total ) {
313
314
			$payment_items = $this->get_stored_data( 'give_temp_payment_items' );
315
316
			if ( false === $payment_items ) {
317
				$payment_items = array();
318
				$this->store_data( 'give_temp_payment_items', $payment_items );
319
			}
320
321
			$args = array(
322
				'post_status'    => 'publish',
323
				'post_type'      => 'give_forms',
324
				'posts_per_page' => - 1,
325
				'fields'         => 'ids',
326
			);
327
328
			$all_forms = get_posts( $args );
329
330
			$this->store_data( 'give_temp_form_ids', $all_forms );
331
332
			$args = apply_filters( 'give_recount_form_stats_total_args', array(
333
				'give_forms' => $all_forms,
334
				'number'     => $this->per_step,
335
				'status'     => $accepted_statuses,
336
				'page'       => $this->step,
337
				'output'     => 'payments',
338
			) );
339
340
			$payments_query = new Give_Payments_Query( $args );
341
			$payments       = $payments_query->get_payments();
342
343
			$total = wp_count_posts( 'give_payment' )->publish;
344
345
			$this->store_data( 'give_temp_all_payments_data', $payments );
346
347
			if ( $payments ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $payments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
348
349
				foreach ( $payments as $payment ) {
350
351
					$form_id = $payment->form_id;
352
353
					//If for some reason somehow the form_ID isn't set check payment meta
354
					if ( empty( $payment->form_id ) ) {
355
						$payment_meta = $payment->get_meta();
356
						$form_id      = isset( $payment_meta['form_id'] ) ? $payment_meta['form_id'] : 0;
357
					}
358
359
					if ( ! in_array( $payment->post_status, $accepted_statuses ) ) {
360
						continue;
361
					}
362
363
					if ( ! array_key_exists( $payment->ID, $payment_items ) ) {
364
						$payment_items[ $payment->ID ] = array(
365
							'id'         => $form_id,
366
							'payment_id' => $payment->ID,
367
							'price'      => $payment->total,
368
						);
369
					}
370
				}
371
			}
372
			$this->store_data( 'give_temp_payment_items', $payment_items );
373
			$this->store_data( 'give_recount_all_total', $total );
374
		}
375
376
	}
377
378
	/**
379
	 * Given a key, get the information from the Database Directly
380
	 *
381
	 * @since  1.5
382
	 *
383
	 * @param  string $key The option_name
384
	 *
385
	 * @return mixed       Returns the data from the database
386
	 */
387 View Code Duplication
	private function get_stored_data( $key ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
388
		global $wpdb;
389
		$value = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = '%s'", $key ) );
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...
390
		if ( empty( $value ) ) {
391
			return false;
392
		}
393
394
		$maybe_json = json_decode( $value );
395
396
		if ( ! is_null( $maybe_json ) ) {
397
			$value = json_decode( $value, true );
398
		}
399
400
		return $value;
401
	}
402
403
	/**
404
	 * Give a key, store the value
405
	 *
406
	 * @since  1.5
407
	 *
408
	 * @param  string $key   The option_name
409
	 * @param  mixed  $value The value to store
410
	 *
411
	 * @return void
412
	 */
413 View Code Duplication
	private function store_data( $key, $value ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
414
		global $wpdb;
415
416
		$value = is_array( $value ) ? wp_json_encode( $value ) : esc_attr( $value );
417
418
		$data = array(
419
			'option_name'  => $key,
420
			'option_value' => $value,
421
			'autoload'     => 'no',
422
		);
423
424
		$formats = array(
425
			'%s',
426
			'%s',
427
			'%s',
428
		);
429
430
		$wpdb->replace( $wpdb->options, $data, $formats );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
431
	}
432
433
	/**
434
	 * Delete an option
435
	 *
436
	 * @since  1.5
437
	 *
438
	 * @param  string $key The option_name to delete
439
	 *
440
	 * @return void
441
	 */
442
	private function delete_data( $key ) {
443
		global $wpdb;
444
		$wpdb->delete( $wpdb->options, array( 'option_name' => $key ) );
445
	}
446
447
}
448