Test Failed
Push — release/2.0 ( 6ea777...f7bd0b )
by Ravinder
05:21
created

Give_Tools_Reset_Stats::store_data()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 12

Duplication

Lines 19
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 2
dl 19
loc 19
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Recount income and stats
4
 *
5
 * This class handles batch processing of resetting donations and income stats.
6
 *
7
 * @subpackage  Admin/Tools/Give_Tools_Reset_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_Reset_Stats Class
20
 *
21
 * @since 1.5
22
 */
23
class Give_Tools_Reset_Stats extends Give_Batch_Export {
24
25
	/**
26
	 * Our export type. Used for export-type specific filters/actions
27
	 *
28
	 * @var string
29
	 * @since 1.5
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 boolean
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 integer
46
	 */
47
	public $per_step = 30;
48
49
	/**
50
	 * Get the Export Data
51
	 *
52
	 * @access public
53
	 * @since  1.5
54
	 * @global object $wpdb Used to query the database using the WordPress
55
	 *                      Database API
56
	 * @return bool   $data The data for the CSV file
57
	 */
58
	public function get_data() {
59
		global $wpdb;
60
61
		$items = $this->get_stored_data( 'give_temp_reset_ids' );
62
63
		if ( ! is_array( $items ) ) {
64
			return false;
65
		}
66
67
		$offset     = ( $this->step - 1 ) * $this->per_step;
68
		$step_items = array_slice( $items, $offset, $this->per_step );
69
70
		if ( $step_items ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $step_items 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...
71
72
			$step_ids = array(
73
				'customers' => array(),
74
				'forms'     => array(),
75
				'other'     => array(),
76
			);
77
78
			foreach ( $step_items as $item ) {
79
80
				switch ( $item['type'] ) {
81
					case 'customer':
82
						$step_ids['customers'][] = $item['id'];
83
						break;
84
					case 'forms':
85
						$step_ids['give_forms'][] = $item['id'];
86
						break;
87
					default:
88
						$item_type                = apply_filters( 'give_reset_item_type', 'other', $item );
89
						$step_ids[ $item_type ][] = $item['id'];
90
						break;
91
				}
92
			}
93
94
			$sql = array();
95
			$meta_table = __give_v20_bc_table_details('form' );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
96
97
			foreach ( $step_ids as $type => $ids ) {
98
99
				if ( empty( $ids ) ) {
100
					continue;
101
				}
102
103
				$ids = implode( ',', $ids );
104
105
				switch ( $type ) {
106
					case 'customers':
107
						$sql[]      = "DELETE FROM $wpdb->donors WHERE id IN ($ids)";
108
						$table_name = $wpdb->prefix . 'give_customers';
109
						$meta_table_name = $wpdb->prefix . 'give_customermeta';
110
						$sql[]      = "DELETE FROM $table_name WHERE id IN ($ids)";
111
						$sql[]      = "DELETE FROM $meta_table_name WHERE customer_id IN ($ids)";
112
						break;
113
					case 'forms':
114
						$sql[] = "UPDATE {$meta_table['name']} SET meta_value = 0 WHERE meta_key = '_give_form_sales' AND {$meta_table['column']['id']} IN ($ids)";
115
						$sql[] = "UPDATE {$meta_table['name']} SET meta_value = 0.00 WHERE meta_key = '_give_form_earnings' AND {$meta_table['column']['id']} IN ($ids)";
116
						break;
117
					case 'other':
118
						$sql[] = "DELETE FROM $wpdb->posts WHERE id IN ($ids)";
119
						$sql[] = "DELETE FROM $wpdb->postmeta WHERE post_id IN ($ids)";
120
						$sql[] = "DELETE FROM $wpdb->comments WHERE comment_post_ID IN ($ids)";
121
						$sql[] = "DELETE FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)";
122
						break;
123
				}
124
125
				if ( ! in_array( $type, array( 'customers', 'forms', 'other' ) ) ) {
126
					// Allows other types of custom post types to filter on their own post_type
127
					// and add items to the query list, for the IDs found in their post type.
128
					$sql = apply_filters( "give_reset_add_queries_{$type}", $sql, $ids );
129
				}
130
			}
131
132
			if ( ! empty( $sql ) ) {
133
				foreach ( $sql as $query ) {
134
					$wpdb->query( $query );
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...
135
				}
136
			}
137
138
			return true;
139
140
		}// End if().
141
142
		return false;
143
144
	}
145
146
	/**
147
	 * Return the calculated completion percentage.
148
	 *
149
	 * @since 1.5
150
	 * @return int
151
	 */
152 View Code Duplication
	public function get_percentage_complete() {
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...
153
154
		$items = $this->get_stored_data( 'give_temp_reset_ids' );
155
		$total = count( $items );
156
157
		$percentage = 100;
158
159
		if ( $total > 0 ) {
160
			$percentage = ( ( $this->per_step * $this->step ) / $total ) * 100;
161
		}
162
163
		if ( $percentage > 100 ) {
164
			$percentage = 100;
165
		}
166
167
		return $percentage;
168
	}
169
170
	/**
171
	 * Set the properties specific to the payments export.
172
	 *
173
	 * @since 1.5
174
	 *
175
	 * @param array $request The Form Data passed into the batch processing.
176
	 */
177
	public function set_properties( $request ) {
178
	}
179
180
	/**
181
	 * Process a step
182
	 *
183
	 * @since 1.5
184
	 * @return bool
185
	 */
186
	public function process_step() {
187
188
		if ( ! $this->can_export() ) {
189
			wp_die( esc_html__( 'You do not have permission to reset data.', 'give' ), esc_html__( 'Error', 'give' ), array(
190
				'response' => 403,
191
			) );
192
		}
193
194
		$had_data = $this->get_data();
195
196
		if ( $had_data ) {
197
			$this->done = false;
198
199
			return true;
200
		} else {
201
			update_option( 'give_earnings_total', 0 );
202
			Give_Cache::delete( Give_Cache::get_key( 'give_estimated_monthly_stats' ) );
203
204
			$this->delete_data( 'give_temp_reset_ids' );
205
206
			// Reset the sequential order numbers
207
			if ( give_get_option( 'enable_sequential' ) ) {
208
				delete_option( 'give_last_payment_number' );
209
			}
210
211
			$this->done    = true;
212
			$this->message = esc_html__( 'Donation forms, income, donations counts, and logs successfully reset.', 'give' );
213
214
			return false;
215
		}
216
	}
217
218
	/**
219
	 * Headers
220
	 */
221
	public function headers() {
222
		give_ignore_user_abort();
223
	}
224
225
	/**
226
	 * Perform the export
227
	 *
228
	 * @access public
229
	 * @since  1.5
230
	 * @return void
231
	 */
232
	public function export() {
233
234
		// Set headers
235
		$this->headers();
236
237
		give_die();
238
	}
239
240
	/**
241
	 * Pre Fetch
242
	 */
243
	public function pre_fetch() {
244
245
		if ( $this->step == 1 ) {
0 ignored issues
show
introduced by
Found "== 1". Use Yoda Condition checks, you must
Loading history...
246
			$this->delete_data( 'give_temp_reset_ids' );
247
		}
248
249
		$items = get_option( 'give_temp_reset_ids', false );
250
251
		if ( false === $items ) {
252
			$items = array();
253
254
			$give_types_for_reset = array( 'give_forms', 'give_payment' );
255
			$give_types_for_reset = apply_filters( 'give_reset_store_post_types', $give_types_for_reset );
256
257
			$args = apply_filters( 'give_tools_reset_stats_total_args', array(
258
				'post_type'      => $give_types_for_reset,
259
				'post_status'    => 'any',
260
				'posts_per_page' => - 1,
261
			) );
262
263
			$posts = get_posts( $args );
264
			foreach ( $posts as $post ) {
265
				$items[] = array(
266
					'id'   => (int) $post->ID,
267
					'type' => $post->post_type,
268
				);
269
			}
270
271
			$donor_args = array(
272
				'number' => - 1,
273
			);
274
			$donors     = Give()->donors->get_donors( $donor_args );
275
			foreach ( $donors as $donor ) {
0 ignored issues
show
Bug introduced by
The expression $donors of type array|object|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
276
				$items[] = array(
277
					'id'   => (int) $donor->id,
278
					'type' => 'customer',
279
				);
280
			}
281
282
			// Allow filtering of items to remove with an unassociative array for each item
283
			// The array contains the unique ID of the item, and a 'type' for you to use in the execution of the get_data method
284
			$items = apply_filters( 'give_reset_items', $items );
285
286
			$this->store_data( 'give_temp_reset_ids', $items );
287
		}// End if().
288
289
	}
290
291
	/**
292
	 * Given a key, get the information from the Database Directly.
293
	 *
294
	 * @since  1.5
295
	 *
296
	 * @param  string $key The option_name
297
	 *
298
	 * @return mixed       Returns the data from the database.
299
	 */
300 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...
301
		global $wpdb;
302
		$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...
303
304
		if ( empty( $value ) ) {
305
			return false;
306
		}
307
308
		$maybe_json = json_decode( $value );
309
		if ( ! is_null( $maybe_json ) ) {
310
			$value = json_decode( $value, true );
311
		}
312
313
		return $value;
314
	}
315
316
	/**
317
	 * Give a key, store the value.
318
	 *
319
	 * @since  1.5
320
	 *
321
	 * @param  string $key   The option_name.
322
	 * @param  mixed  $value The value to store.
323
	 *
324
	 * @return void
325
	 */
326 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...
327
		global $wpdb;
328
329
		$value = is_array( $value ) ? wp_json_encode( $value ) : esc_attr( $value );
330
331
		$data = array(
332
			'option_name'  => $key,
333
			'option_value' => $value,
334
			'autoload'     => 'no',
335
		);
336
337
		$formats = array(
338
			'%s',
339
			'%s',
340
			'%s',
341
		);
342
343
		$wpdb->replace( $wpdb->options, $data, $formats );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
344
	}
345
346
	/**
347
	 * Delete an option
348
	 *
349
	 * @since  1.5
350
	 *
351
	 * @param  string $key The option_name to delete
352
	 *
353
	 * @return void
354
	 */
355
	private function delete_data( $key ) {
356
		global $wpdb;
357
		$wpdb->delete( $wpdb->options, array(
358
			'option_name' => $key,
359
		) );
360
	}
361
362
}