Test Failed
Push — release/1.8.12 ( b58a2f...d255b1 )
by Ravinder
375:09 queued 372:17
created

class-give-tools-recount-single-donor-stats.php (9 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Recount single donor stats.
4
 *
5
 * This class handles batch processing of recounting a single donor's stats.
6
 *
7
 * @subpackage  Admin/Tools/Give_Tools_Recount_Single_Customer_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_Single_Customer_Stats Class
20
 *
21
 * @since 1.5
22
 */
23
class Give_Tools_Recount_Single_Customer_Stats extends Give_Batch_Export {
24
25
	/**
26
	 * Our export type. Used for export-type specific filters/actions
27
	 * @var string
28
	 * @since 1.5
29
	 */
30
	public $export_type = '';
31
32
	/**
33
	 * Allows for a non-form batch processing to be run.
34
	 * @since  1.5
35
	 * @var boolean
36
	 */
37
	public $is_void = true;
38
39
	/**
40
	 * Sets the number of items to pull on each step
41
	 * @since  1.5
42
	 * @var integer
43
	 */
44
	public $per_step = 10;
45
46
	/**
47
	 * Get the Export Data
48
	 *
49
	 * @access public
50
	 * @since  1.5
51
	 * @global object $wpdb Used to query the database using the WordPress Database API
52
	 * @return bool
53
	 */
54
	public function get_data() {
55
56
		$donor    = new Give_Donor( $this->customer_id );
57
		$payments = $this->get_stored_data( 'give_recount_donor_payments_' . $donor->id );
58
59
		$offset     = ( $this->step - 1 ) * $this->per_step;
60
		$step_items = array_slice( $payments, $offset, $this->per_step );
61
62
		if ( count( $step_items ) > 0 ) {
63
			$pending_total = (float) $this->get_stored_data( 'give_stats_donor_pending_total' . $donor->id );
64
			$step_total    = 0;
65
66
			$found_payment_ids = $this->get_stored_data( 'give_stats_found_payments_' . $donor->id );
67
68
			foreach ( $step_items as $payment ) {
69
				$payment = get_post( $payment->ID );
70
71
				if ( is_null( $payment ) || is_wp_error( $payment ) || 'give_payment' !== $payment->post_type ) {
72
73
					$missing_payments   = $this->get_stored_data( 'give_stats_missing_payments' . $donor->id );
74
					$missing_payments[] = $payment->ID;
75
					$this->store_data( 'give_stats_missing_payments' . $donor->id, $missing_payments );
76
77
					continue;
78
				}
79
80
				$should_process_payment = 'publish' == $payment->post_status ? true : false;
81
				$should_process_payment = apply_filters( 'give_donor_recount_should_process_donation', $should_process_payment, $payment );
82
83
				if ( true === $should_process_payment ) {
84
85
					$found_payment_ids[] = $payment->ID;
86
87
					if ( apply_filters( 'give_donor_recount_should_increase_value', true, $payment ) ) {
88
						$payment_amount = give_get_payment_amount( $payment->ID );
89
						$step_total     += $payment_amount;
90
					}
0 ignored issues
show
Blank line found after control structure
Loading history...
91
92
				}
0 ignored issues
show
Blank line found after control structure
Loading history...
93
94
			}
95
96
			$updated_total = $pending_total + $step_total;
97
			$this->store_data( 'give_stats_donor_pending_total' . $donor->id, $updated_total );
98
			$this->store_data( 'give_stats_found_payments_' . $donor->id, $found_payment_ids );
99
100
			return true;
101
		}
102
103
		return false;
104
105
	}
106
107
	/**
108
	 * Return the calculated completion percentage
109
	 *
110
	 * @since 1.5
111
	 * @return int
112
	 */
113
	public function get_percentage_complete() {
114
115
		$payments = $this->get_stored_data( 'give_recount_donor_payments_' . $this->customer_id );
116
		$total    = count( $payments );
117
118
		$percentage = 100;
119
120
		if ( $total > 0 ) {
121
			$percentage = ( ( $this->per_step * $this->step ) / $total ) * 100;
122
		}
123
124
		if ( $percentage > 100 ) {
125
			$percentage = 100;
126
		}
127
128
		return $percentage;
129
	}
130
131
	/**
132
	 * Set the properties specific to the payments export
133
	 *
134
	 * @since 1.5
135
	 *
136
	 * @param array $request The Form Data passed into the batch processing
137
	 */
138
	public function set_properties( $request ) {
139
		$this->customer_id = isset( $request['customer_id'] ) ? sanitize_text_field( $request['customer_id'] ) : false;
140
	}
141
142
	/**
143
	 * Process a step
144
	 *
145
	 * @since 1.5
146
	 * @return bool
147
	 */
148
	public function process_step() {
149
150
		if ( ! $this->can_export() ) {
151
			wp_die( esc_html__( 'You do not have permission to recount stats.', 'give' ), esc_html__( 'Error', 'give' ), array( 'response' => 403 ) );
152
		}
153
154
		$had_data = $this->get_data();
155
156
		if ( $had_data ) {
157
			$this->done = false;
158
159
			return true;
160
		} else {
161
			$donor       = new Give_Donor( $this->customer_id );
162
			$payment_ids = get_option( 'give_stats_found_payments_' . $donor->id, array() );
163
			$this->delete_data( 'give_stats_found_payments_' . $donor->id );
164
165
			$removed_payments = array_unique( get_option( 'give_stats_missing_payments' . $donor->id, array() ) );
166
167
			// Find non-existing payments (deleted) and total up the donation count
168
			$purchase_count = 0;
169
			foreach ( $payment_ids as $key => $payment_id ) {
170
				if ( in_array( $payment_id, $removed_payments ) ) {
171
					unset( $payment_ids[ $key ] );
172
					continue;
173
				}
174
175
				$payment = get_post( $payment_id );
176
				if ( apply_filters( 'give_donor_recount_should_increase_count', true, $payment ) ) {
177
					$purchase_count ++;
178
				}
179
			}
180
181
			$this->delete_data( 'give_stats_missing_payments' . $donor->id );
182
183
			$pending_total = $this->get_stored_data( 'give_stats_donor_pending_total' . $donor->id );
184
			$this->delete_data( 'give_stats_donor_pending_total' . $donor->id );
185
			$this->delete_data( 'give_recount_donor_stats_' . $donor->id );
186
			$this->delete_data( 'give_recount_donor_payments_' . $this->customer_id );
187
188
			$payment_ids = implode( ',', $payment_ids );
189
			$donor->update( array(
190
				'payment_ids'    => $payment_ids,
191
				'purchase_count' => $purchase_count,
192
				'purchase_value' => $pending_total,
193
			) );
194
195
			$this->done    = true;
196
			$this->message = esc_html__( 'Donor stats have been successfully recounted.', 'give' );
197
198
			return false;
199
		}
200
	}
201
202 View Code Duplication
	public function headers() {
0 ignored issues
show
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...
203
		ignore_user_abort( true );
204
205
		if ( ! give_is_func_disabled( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) {
206
			set_time_limit( 0 );
207
		}
208
	}
209
210
	/**
211
	 * Perform the export
212
	 *
213
	 * @access public
214
	 * @since  1.5
215
	 * @return void
216
	 */
217
	public function export() {
218
219
		// Set headers
220
		$this->headers();
221
222
		give_die();
223
	}
224
225
	/**
226
	 * Zero out the data on step one
227
	 *
228
	 * @access public
229
	 * @since  1.5
230
	 * @return void
231
	 */
232
	public function pre_fetch() {
233
		if ( $this->step === 1 ) {
0 ignored issues
show
Found "=== 1". Use Yoda Condition checks, you must
Loading history...
234
			$allowed_payment_status = apply_filters( 'give_recount_donors_donation_statuses', give_get_payment_status_keys() );
235
236
			// Before we start, let's zero out the customer's data
237
			$donor = new Give_Donor( $this->customer_id );
238
			$donor->update( array( 'purchase_value' => give_format_amount( 0, array( 'sanitize' => false ) ), 'purchase_count' => 0 ) );
239
240
			$attached_payment_ids = explode( ',', $donor->payment_ids );
241
242
			$attached_args = array(
243
				'post__in' => $attached_payment_ids,
244
				'number'   => - 1,
245
				'status'   => $allowed_payment_status,
246
			);
247
248
			$attached_payments = give_get_payments( $attached_args );
249
250
			$unattached_args = array(
251
				'post__not_in' => $attached_payment_ids,
252
				'number'       => - 1,
253
				'status'       => $allowed_payment_status,
254
				'meta_query'   => array(
0 ignored issues
show
Detected usage of meta_query, possible slow query.
Loading history...
255
					array(
256
						'key'   => '_give_payment_user_email',
257
						'value' => $donor->email,
258
					),
259
				),
260
			);
261
262
			$unattached_payments = give_get_payments( $unattached_args );
263
264
			$payments = array_merge( $attached_payments, $unattached_payments );
265
266
			$this->store_data( 'give_recount_donor_payments_' . $donor->id, $payments );
267
		}
268
	}
269
270
	/**
271
	 * Given a key, get the information from the Database Directly
272
	 *
273
	 * @since  1.5
274
	 *
275
	 * @param  string $key The option_name
276
	 *
277
	 * @return mixed       Returns the data from the database
278
	 */
279
	private function get_stored_data( $key ) {
280
		global $wpdb;
281
		$value = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = '%s'", $key ) );
0 ignored issues
show
Usage of a direct database call is discouraged.
Loading history...
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
282
283
		return empty( $value ) ? false : maybe_unserialize( $value );
284
	}
285
286
	/**
287
	 * Give a key, store the value
288
	 *
289
	 * @since  1.5
290
	 *
291
	 * @param  string $key   The option_name
292
	 * @param  mixed  $value The value to store
293
	 *
294
	 * @return void
295
	 */
296 View Code Duplication
	private function store_data( $key, $value ) {
0 ignored issues
show
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...
297
		global $wpdb;
298
299
		$value = maybe_serialize( $value );
300
301
		$data = array(
302
			'option_name'  => $key,
303
			'option_value' => $value,
304
			'autoload'     => 'no',
305
		);
306
307
		$formats = array(
308
			'%s',
309
			'%s',
310
			'%s',
311
		);
312
313
		$wpdb->replace( $wpdb->options, $data, $formats );
0 ignored issues
show
Usage of a direct database call is discouraged.
Loading history...
314
	}
315
316
	/**
317
	 * Delete an option
318
	 *
319
	 * @since  1.5
320
	 *
321
	 * @param  string $key The option_name to delete
322
	 *
323
	 * @return void
324
	 */
325
	private function delete_data( $key ) {
326
		global $wpdb;
327
		$wpdb->delete( $wpdb->options, array( 'option_name' => $key ) );
328
	}
329
330
}
331