Test Failed
Pull Request — master (#2482)
by Devin
05:37
created

Give_Import_Donations::save()   C

Complexity

Conditions 11
Paths 3

Size

Total Lines 34
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 24
nc 3
nop 0
dl 0
loc 34
rs 5.2653
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Donations Import Class
4
 *
5
 * This class handles donations import.
6
 *
7
 * @package     Give
8
 * @subpackage  Classes/Give_Import_Donations
9
 * @copyright   Copyright (c) 2017, WordImpress
10
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
11
 * @since       1.8.14
12
 */
13
14
if ( ! defined( 'ABSPATH' ) ) {
15
	exit; // Exit if accessed directly
16
}
17
18
if ( ! class_exists( 'Give_Import_Donations' ) ) {
19
20
	/**
21
	 * Give_Import_Donations.
22
	 *
23
	 * @since 1.8.14
24
	 */
25
	final class Give_Import_Donations {
26
27
		/**
28
		 * Importer type
29
		 *
30
		 * @since 1.8.13
31
		 * @var string
32
		 */
33
		private $importer_type = 'import_donations';
34
35
		/**
36
		 * Instance.
37
		 *
38
		 * @since
39
		 * @access private
40
		 * @var
41
		 */
42
		static private $instance;
43
44
		/**
45
		 * Importing donation per page.
46
		 *
47
		 * @since 1.8.14
48
		 *
49
		 * @var   int
50
		 */
51
		public static $per_page = 25;
52
53
		/**
54
		 * Singleton pattern.
55
		 *
56
		 * @since
57
		 * @access private
58
		 */
59
		private function __construct() {
60
			self::$per_page  = ! empty( $_GET['per_page'] ) ? absint( $_GET['per_page'] ) : self::$per_page;
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
61
		}
62
63
		/**
64
		 * Get instance.
65
		 *
66
		 * @since
67
		 * @access public
68
		 *
69
		 * @return static
70
		 */
71
		public static function get_instance() {
72
			if ( null === static::$instance ) {
73
				self::$instance = new static();
74
			}
75
76
			return self::$instance;
77
		}
78
79
		/**
80
		 * Setup
81
		 *
82
		 * @since 1.8.14
83
		 *
84
		 * @return void
85
		 */
86
		public function setup() {
87
			$this->setup_hooks();
88
		}
89
90
91
		/**
92
		 * Setup Hooks.
93
		 *
94
		 * @since 1.8.14
95
		 *
96
		 * @return void
97
		 */
98 View Code Duplication
		private function setup_hooks() {
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...
99
			if ( ! $this->is_donations_import_page() ) {
100
				return;
101
			}
102
103
			// Do not render main import tools page.
104
			remove_action( 'give_admin_field_tools_import', array( 'Give_Settings_Import', 'render_import_field', ) );
105
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
106
107
			// Render donation import page
108
			add_action( 'give_admin_field_tools_import', array( $this, 'render_page' ) );
109
110
			// Print the HTML.
111
			add_action( 'give_tools_import_donations_form_start', array( $this, 'html' ), 10 );
112
113
			// Run when form submit.
114
			add_action( 'give-tools_save_import', array( $this, 'save' ) );
115
116
			add_action( 'give-tools_update_notices', array( $this, 'update_notices' ), 11, 1 );
117
118
			// Used to add submit button.
119
			add_action( 'give_tools_import_donations_form_end', array( $this, 'submit' ), 10 );
120
		}
121
122
		/**
123
		 * Update notice
124
		 *
125
		 * @since 1.8.14
126
		 *
127
		 * @param $messages
128
		 *
129
		 * @return mixed
130
		 */
131 View Code Duplication
		public function update_notices( $messages ) {
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...
132
			if ( ! empty( $_GET['tab'] ) && 'import' === give_clean( $_GET['tab'] ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
133
				unset( $messages['give-setting-updated'] );
134
			}
135
136
			return $messages;
137
		}
138
139
		/**
140
		 * Print submit and nonce button.
141
		 *
142
		 * @since 1.8.14
143
		 */
144
		public function submit() {
145
			wp_nonce_field( 'give-save-settings', '_give-save-settings' );
146
			?>
147
			<input type="hidden" class="import-step" id="import-step" name="step" value="<?php echo $this->get_step(); ?>"/>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$this'
Loading history...
148
			<input type="hidden" class="importer-type" value="<?php echo $this->importer_type; ?>"/>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$this'
Loading history...
149
			<?php
150
		}
151
152
		/**
153
		 * Print the HTML for importer.
154
		 *
155
		 * @since 1.8.14
156
		 */
157
		public function html() {
158
			$step = $this->get_step();
159
160
			// Show progress.
161
			$this->render_progress();
162
			?>
163
			<section>
164
				<table class="widefat export-options-table give-table <?php echo "step-{$step}"; ?>" id="<?php echo "step-{$step}"; ?>">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '"step-{$step}"'
Loading history...
165
					<tbody>
166
						<?php
167
						switch ( $this->get_step() ) {
168
							case 1:
169
								$this->render_media_csv();
170
								break;
171
172
							case 2:
173
								$this->render_dropdown();
174
								break;
175
176
							case 3:
177
								$this->start_import();
178
								break;
179
180
							case 4:
181
								$this->import_success();
182
						}
183
184
						if ( false === $this->check_for_dropdown_or_import() ) {
185
							?>
186
							<tr valign="top">
187
								<th></th>
188
								<th>
189
									<input type="submit"
190
										   class="button button-primary button-large button-secondary <?php echo "step-{$step}"; ?>"
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '"step-{$step}"'
Loading history...
191
										   id="recount-stats-submit"
192
										   value="<?php esc_attr_e( 'Submit', 'give' ); ?>"/>
193
								</th>
194
							</tr>
195
							<?php
196
						}
197
						?>
198
					</tbody>
199
				</table>
200
			</section>
201
			<?php
202
		}
203
204
		/**
205
		 * Show success notice
206
		 *
207
		 * @since 1.8.14
208
		 */
209
		public function import_success() {
210
211
			$delete_csv = ( ! empty( $_GET['delete_csv'] ) ? absint( $_GET['delete_csv'] ) : false );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
212
			$csv        = ( ! empty( $_GET['csv'] ) ? absint( $_GET['csv'] ) : false );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
213
			if ( ! empty( $delete_csv ) && ! empty( $csv ) ) {
214
				wp_delete_attachment( $csv, true );
215
			}
216
217
			$report      = give_import_donation_report();
218
			$report_html = array(
219
				'duplicate_donor'    => array(
220
					__( '%s duplicate %s detected', 'give' ),
221
					__( 'donor', 'give' ),
222
					__( 'donors', 'give' ),
223
				),
224
				'create_donor'       => array(
225
					__( '%s %s created', 'give' ),
226
					__( 'donor', 'give' ),
227
					__( 'donors', 'give' ),
228
				),
229
				'create_form'        => array(
230
					__( '%s donation %s created', 'give' ),
231
					__( 'form', 'give' ),
232
					__( 'forms', 'give' ),
233
				),
234
				'duplicate_donation' => array(
235
					__( '%s duplicate %s detected', 'give' ),
236
					__( 'donation', 'give' ),
237
					__( 'donations', 'give' ),
238
				),
239
				'create_donation'    => array(
240
					__( '%s %s imported', 'give' ),
241
					__( 'donation', 'give' ),
242
					__( 'donations', 'give' ),
243
				),
244
			);
245
			$total       = (int) $_GET['total'];
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-validated input variable: $_GET
Loading history...
246
			-- $total;
247
			$success = (bool) $_GET['success'];
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-validated input variable: $_GET
Loading history...
248
			?>
249
			<tr valign="top" class="give-import-dropdown">
250
				<th colspan="2">
251
					<h2>
252
						<?php
253
						if ( $success ) {
254
							echo sprintf(
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'sprintf'
Loading history...
255
								__( 'Import complete! %s donations processed', 'give' ),
256
								"<strong>{$total}</strong>"
257
							);
258
						} else {
259
							echo sprintf(
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'sprintf'
Loading history...
260
								__( 'Failed to import %s donations', 'give' ),
261
								"<strong>{$total}</strong>"
262
							);
263
						}
264
						?>
265
					</h2>
266
267
					<?php
268
					$text      = __( 'Import Donation', 'give' );
269
					$query_arg = array(
270
						'post_type' => 'give_forms',
271
						'page'      => 'give-tools',
272
						'tab'       => 'import',
273
					);
274
					if ( $success ) {
275
						$query_arg = array(
276
							'post_type' => 'give_forms',
277
							'page'      => 'give-payment-history',
278
						);
279
						$text      = __( 'View Donations', 'give' );
280
					}
281
282
					foreach ( $report as $key => $value ) {
283
						if ( array_key_exists( $key, $report_html ) && ! empty( $value ) ) {
284
							?>
285
							<p>
286
								<?php echo esc_html( wp_sprintf( $report_html[ $key ][0], $value, _n( $report_html[ $key ][1], $report_html[ $key ][2], $value, 'give' ) ) ); ?>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$report_html'
Loading history...
287
							</p>
288
							<?php
289
						}
290
					}
291
					?>
292
293
					<p>
294
						<a class="button button-large button-secondary" href="<?php echo add_query_arg( $query_arg, admin_url( 'edit.php' ) ); ?>"><?php echo $text; ?></a>
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'add_query_arg'
Loading history...
introduced by
Expected next thing to be a escaping function, not '$text'
Loading history...
295
					</p>
296
				</th>
297
			</tr>
298
			<?php
299
		}
300
301
		/**
302
		 * Will start Import
303
		 *
304
		 * @since 1.8.14
305
		 */
306
		public function start_import() {
307
			// Reset the donation form report.
308
			give_import_donation_report_reset();
309
310
			$csv         = (int) $_REQUEST['csv'];
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-validated input variable: $_REQUEST
Loading history...
311
			$delimiter   = ( ! empty( $_REQUEST['delimiter'] ) ? give_clean( $_REQUEST['delimiter'] ) : 'csv' );
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
312
			$index_start = 1;
313
			$index_end   = 1;
0 ignored issues
show
Unused Code introduced by
$index_end is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
314
			$next        = true;
315
			$total       = self::get_csv_total( $csv );
316
			if ( self::$per_page < $total ) {
317
				$total_ajax = ceil( $total / self::$per_page );
318
				$index_end  = self::$per_page;
319
			} else {
320
				$total_ajax = 1;
321
				$index_end  = $total;
322
				$next       = false;
323
			}
324
			$current_percentage = 100 / ( $total_ajax + 1 );
325
326
			?>
327
			<tr valign="top" class="give-import-dropdown">
328
				<th colspan="2">
329
					<h2 id="give-import-title"><?php esc_html_e( 'Importing', 'give' ) ?></h2>
330
					<p class="give-field-description"><?php esc_html_e( 'Your donations are now being imported...', 'give' ) ?></p>
331
				</th>
332
			</tr>
333
334
			<tr valign="top" class="give-import-dropdown">
335
				<th colspan="2">
336
					<span class="spinner is-active"></span>
337
					<div class="give-progress"
338
						 data-current="1"
339
						 data-total_ajax="<?php echo $total_ajax; ?>"
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$total_ajax'
Loading history...
340
						 data-start="<?php echo $index_start; ?>"
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$index_start'
Loading history...
341
						 data-end="<?php echo $index_end; ?>"
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$index_end'
Loading history...
342
						 data-next="<?php echo $next; ?>"
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$next'
Loading history...
343
						 data-total="<?php echo $total; ?>"
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$total'
Loading history...
344
						 data-per_page="<?php echo self::$per_page; ?>">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not 'self'
Loading history...
345
346
						<div style="width: <?php echo $current_percentage; ?>%"></div>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$current_percentage'
Loading history...
347
					</div>
348
					<input type="hidden" value="3" name="step">
349
					<input type="hidden" value='<?php echo maybe_serialize( $_REQUEST['mapto'] ); ?>' name="mapto"
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'maybe_serialize'
Loading history...
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-validated input variable: $_REQUEST
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
350
						   class="mapto">
351
					<input type="hidden" value="<?php echo $_REQUEST['csv']; ?>" name="csv" class="csv">
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Expected next thing to be a escaping function, not '$_REQUEST'
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
352
					<input type="hidden" value="<?php echo $_REQUEST['mode']; ?>" name="mode" class="mode">
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Expected next thing to be a escaping function, not '$_REQUEST'
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
353
					<input type="hidden" value="<?php echo $_REQUEST['create_user']; ?>" name="create_user"
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Expected next thing to be a escaping function, not '$_REQUEST'
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
354
						   class="create_user">
355
					<input type="hidden" value="<?php echo $_REQUEST['delete_csv']; ?>" name="delete_csv"
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Expected next thing to be a escaping function, not '$_REQUEST'
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
356
						   class="delete_csv">
357
					<input type="hidden" value="<?php echo $delimiter; ?>" name="delimiter">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$delimiter'
Loading history...
358
					<input type="hidden" value='<?php echo maybe_serialize( self::get_importer( $csv, 0, $delimiter ) ); ?>'
0 ignored issues
show
Bug introduced by
It seems like $delimiter defined by !empty($_REQUEST['delimi...T['delimiter']) : 'csv' on line 311 can also be of type array; however, Give_Import_Donations::get_importer() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'maybe_serialize'
Loading history...
359
						   name="main_key"
360
						   class="main_key">
361
				</th>
362
			</tr>
363
364
			<script type="text/javascript">
365
				jQuery(document).ready(function () {
366
					give_on_donation_import_start();
367
				});
368
			</script>
369
			<?php
370
		}
371
372
		/**
373
		 * Will return true if importing can be started or not else false.
374
		 *
375
		 * @since 1.8.14
376
		 */
377
		public function check_for_dropdown_or_import() {
378
			$return = true;
379
			if ( isset( $_REQUEST['mapto'] ) ) {
380
				$mapto = (array) $_REQUEST['mapto'];
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
381
				if ( false === in_array( 'form_title', $mapto ) && false === in_array( 'form_id', $mapto ) ) {
382
					Give_Admin_Settings::add_error( 'give-import-csv-form', __( 'In order to import donations, a column must be mapped to either the "Donation Form Title" or "Donation Form ID" field. Please map a column to one of those fields.', 'give' ) );
383
					$return = false;
384
				}
385
386
				if ( false === in_array( 'amount', $mapto ) ) {
387
					Give_Admin_Settings::add_error( 'give-import-csv-amount', __( 'In order to import donations, a column must be mapped to the "Amount" field. Please map a column to that field.', 'give' ) );
388
					$return = false;
389
				}
390
391
				if ( false === in_array( 'email', $mapto ) && false === in_array( 'donor_id', $mapto ) ) {
392
					Give_Admin_Settings::add_error( 'give-import-csv-donor', __( 'In order to import donations, a column must be mapped to either the "Donor Email" or "Donor ID" field. Please map a column to that field.', 'give' ) );
393
					$return = false;
394
				}
395
			} else {
396
				$return = false;
397
			}
398
399
			return $return;
400
		}
401
402
		/**
403
		 * Print the Dropdown option for CSV.
404
		 *
405
		 * @since 1.8.14
406
		 */
407
		public function render_dropdown() {
408
			$csv       = (int) $_GET['csv'];
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-validated input variable: $_GET
Loading history...
409
			$delimiter = ( ! empty( $_GET['delimiter'] ) ? give_clean( $_GET['delimiter'] ) : 'csv' );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
410
411
			// TO check if the CSV files that is being add is valid or not if not then redirect to first step again
412
			if ( ! $this->is_valid_csv( $csv ) ) {
413
				$url = give_import_page_url();
414
				?>
415
				<script type="text/javascript">
416
					window.location = "<?php echo $url; ?>"
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$url'
Loading history...
417
				</script>
418
				<?php
419
			} else {
420
				?>
421
				<tr valign="top" class="give-import-dropdown">
422
					<th colspan="2">
423
						<h2 id="give-import-title"><?php esc_html_e( 'Map CSV fields to donations', 'give' ) ?></h2>
424
						<p class="give-field-description"><?php esc_html_e( 'Select fields from your CSV file to map against donations fields or to ignore during import.', 'give' ) ?></p>
425
					</th>
426
				</tr>
427
428
				<tr valign="top" class="give-import-dropdown">
429
					<th><b><?php esc_html_e( 'Column name', 'give' ); ?></b></th>
430
					<th><b><?php esc_html_e( 'Map to field', 'give' ); ?></b></th>
431
				</tr>
432
433
				<?php
434
				$raw_key   = $this->get_importer( $csv, 0, $delimiter );
0 ignored issues
show
Bug introduced by
It seems like $delimiter defined by !empty($_GET['delimiter'...T['delimiter']) : 'csv' on line 409 can also be of type array; however, Give_Import_Donations::get_importer() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
435
				$mapto     = (array) ( isset( $_REQUEST['mapto'] ) ? $_REQUEST['mapto'] : array() );
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
436
437
				foreach ( $raw_key as $index => $value ) {
0 ignored issues
show
Bug introduced by
The expression $raw_key of type array<integer,string|nul...":"string|null"}>|false 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...
438
					?>
439
					<tr valign="top" class="give-import-option">
440
						<th><?php echo $value; ?></th>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$value'
Loading history...
441
						<th>
442
							<?php
443
							$this->get_columns( $index, $value, $mapto );
444
							?>
445
						</th>
446
					</tr>
447
					<?php
448
				}
449
			}
450
		}
451
452
		/**
453
		 * @param $option_value
454
		 * @param $value
455
		 *
456
		 * @return string
457
		 */
458
		public function selected( $option_value, $value ) {
459
			$option_value = strtolower( $option_value );
460
			$value = strtolower( $value );
461
462
			$selected = '';
463
			if ( stristr( $value, $option_value ) ) {
464
				$selected = 'selected';
465
			} elseif ( strrpos( $value, '_' ) && stristr( $option_value, __( 'Import as Meta', 'give' ) ) ) {
466
				$selected = 'selected';
467
			}
468
469
			return $selected;
470
		}
471
472
		/**
473
		 * Print the columns from the CSV.
474
		 *
475
		 * @since 1.8.14
476
		 * @access private
477
		 *
478
		 * @param string  $index
479
		 * @param bool  $value
480
		 * @param array $mapto
481
		 *
482
		 * @return void
483
		 */
484
		private function get_columns( $index, $value = false, $mapto = array() ) {
485
			$default       = give_import_default_options();
486
			$current_mapto = (string) ( ! empty( $mapto[ $index ] ) ? $mapto[ $index ] : '' );
487
			?>
488
			<select name="mapto[<?php echo $index; ?>]">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$index'
Loading history...
489
				<?php $this->get_dropdown_option_html( $default, $current_mapto, $value ); ?>
490
491
				<optgroup label="<?php _e( 'Donations', 'give' ); ?>">
492
					<?php
493
					$this->get_dropdown_option_html( give_import_donations_options(), $current_mapto, $value );
494
					?>
495
				</optgroup>
496
497
				<optgroup label="<?php _e( 'Donors', 'give' ); ?>">
498
					<?php
499
					$this->get_dropdown_option_html( give_import_donor_options(), $current_mapto, $value );
500
					?>
501
				</optgroup>
502
503
				<optgroup label="<?php _e( 'Forms', 'give' ); ?>">
504
					<?php
505
					$this->get_dropdown_option_html( give_import_donation_form_options(), $current_mapto, $value );
506
					?>
507
				</optgroup>
508
509
				<?php
510
				/**
511
				 * Fire the action
512
				 * You can use this filter to add new options.
513
				 *
514
				 * @since 1.8.15
515
				 */
516
				do_action( 'give_import_dropdown_option', $index, $value, $mapto, $current_mapto );
517
				?>
518
			</select>
519
			<?php
520
		}
521
522
		/**
523
		 * Print the option html for select in importer
524
		 *
525
		 * @since  1.8.15
526
		 * @access public
527
		 *
528
		 * @param  array  $options
529
		 * @param  string $current_mapto
530
		 * @param bool    $value
531
		 *
532
		 * @return void
533
		 */
534
		public function get_dropdown_option_html( $options, $current_mapto, $value = false ) {
535
			foreach ( $options as $option => $option_value ) {
536
				$option_value_texts = (array) $option_value;
537
				$option_text = $option_value_texts[0];
538
539
				$checked = ( ( $current_mapto === $option ) ? 'selected' : false );
540
				if ( empty( $checked ) ) {
541
					foreach ( $option_value_texts as $option_value_text ) {
542
						$checked = $this->selected( $option_value_text, $value );
543
						if ( $checked ) {
544
							break;
545
						}
546
					}
547
				}
548
549
				echo sprintf(
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'sprintf'
Loading history...
550
					'<option value="%1$s" %2$s >%3$s</option>',
551
					$option,
552
					$checked,
553
					$option_text
554
				);
555
			}
556
		}
557
558
		/**
559
		 * Get column count of csv file.
560
		 *
561
		 * @since 1.8.14
562
		 *
563
		 * @param $file_id
564
		 *
565
		 * @return bool|int
566
		 */
567
		public function get_csv_total( $file_id ) {
568
			$total = false;
569
			if ( $file_id ) {
570
				$file_dir = get_attached_file( $file_id );
571
				if ( $file_dir ) {
572
					$file = new SplFileObject( $file_dir, 'r' );
573
					$file->seek( PHP_INT_MAX );
574
					$total = $file->key() + 1;
575
				}
576
			}
577
578
			return $total;
579
		}
580
581
		/**
582
		 * Get the CSV fields title from the CSV.
583
		 *
584
		 * @since 1.8.14
585
		 *
586
		 * @param (int) $file_id
587
		 * @param int    $index
588
		 * @param string $delimiter
589
		 *
590
		 * @return array|bool $raw_data title of the CSV file fields
591
		 */
592
		public function get_importer( $file_id, $index = 0, $delimiter = 'csv' ) {
593
			/**
594
			 * Filter to modify delimiter of Import.
595
			 *
596
			 * @since 1.8.14
597
			 *
598
			 * Return string $delimiter.
599
			 */
600
			$delimiter = (string) apply_filters( 'give_import_delimiter_set', $delimiter );
601
602
			$raw_data = false;
603
			$file_dir = get_attached_file( $file_id );
604
			if ( $file_dir ) {
605
				if ( false !== ( $handle = fopen( $file_dir, 'r' ) ) ) {
606
					$raw_data = fgetcsv( $handle, $index, $delimiter );
607
					// Remove BOM signature from the first item.
608
					if ( isset( $raw_data[0] ) ) {
609
						$raw_data[0] = $this->remove_utf8_bom( $raw_data[0] );
610
					}
611
				}
612
			}
613
614
			return $raw_data;
615
		}
616
617
		/**
618
		 * Remove UTF-8 BOM signature.
619
		 *
620
		 * @since 1.8.14
621
		 *
622
		 * @param  string $string String to handle.
623
		 *
624
		 * @return string
625
		 */
626
		public function remove_utf8_bom( $string ) {
627
			if ( 'efbbbf' === substr( bin2hex( $string ), 0, 6 ) ) {
628
				$string = substr( $string, 3 );
629
			}
630
631
			return $string;
632
		}
633
634
635
		/**
636
		 * Is used to show the process when user upload the donor form.
637
		 *
638
		 * @since 1.8.14
639
		 */
640
		public function render_progress() {
641
			$step = $this->get_step();
642
			?>
643
			<ol class="give-progress-steps">
644
				<li class="<?php echo( 1 === $step ? 'active' : '' ); ?>">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '('
Loading history...
645
					<?php esc_html_e( 'Upload CSV file', 'give' ); ?>
646
				</li>
647
				<li class="<?php echo( 2 === $step ? 'active' : '' ); ?>">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '('
Loading history...
648
					<?php esc_html_e( 'Column mapping', 'give' ); ?>
649
				</li>
650
				<li class="<?php echo( 3 === $step ? 'active' : '' ); ?>">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '('
Loading history...
651
					<?php esc_html_e( 'Import', 'give' ); ?>
652
				</li>
653
				<li class="<?php echo( 4 === $step ? 'active' : '' ); ?>">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '('
Loading history...
654
					<?php esc_html_e( 'Done!', 'give' ); ?>
655
				</li>
656
			</ol>
657
			<?php
658
		}
659
660
		/**
661
		 * Will return the import step.
662
		 *
663
		 * @since 1.8.14
664
		 *
665
		 * @return int $step on which step doest the import is on.
666
		 */
667 View Code Duplication
		public function get_step() {
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...
668
			$step    = (int) ( isset( $_REQUEST['step'] ) ? give_clean( $_REQUEST['step'] ) : 0 );
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
669
			$on_step = 1;
670
671
			if ( empty( $step ) || 1 === $step ) {
672
				$on_step = 1;
673
			} elseif ( $this->check_for_dropdown_or_import() ) {
674
				$on_step = 3;
675
			} elseif ( 2 === $step ) {
676
				$on_step = 2;
677
			} elseif ( 4 === $step ) {
678
				$on_step = 4;
679
			}
680
681
			return $on_step;
682
		}
683
684
		/**
685
		 * Render donations import page
686
		 *
687
		 * @since 1.8.14
688
		 */
689
		public function render_page() {
690
			include_once GIVE_PLUGIN_DIR . 'includes/admin/tools/views/html-admin-page-import-donations.php';
691
		}
692
693
		/**
694
		 * Add CSV upload HTMl
695
		 *
696
		 * Print the html of the file upload from which CSV will be uploaded.
697
		 *
698
		 * @since 1.8.14
699
		 * @return void
700
		 */
701
		public function render_media_csv() {
702
			?>
703
			<tr valign="top">
704
				<th colspan="2">
705
					<h2 id="give-import-title"><?php esc_html_e( 'Import donations from a CSV file', 'give' ) ?></h2>
706
					<p class="give-field-description"><?php esc_html_e( 'This tool allows you to import or add donation data to your give form(s) via a CSV file.', 'give' ) ?></p>
707
				</th>
708
			</tr>
709
			<?php
710
			$csv         = ( isset( $_POST['csv'] ) ? give_clean( $_POST['csv'] ) : '' );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
711
			$csv_id      = ( isset( $_POST['csv_id'] ) ? give_clean( $_POST['csv_id'] ) : '' );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
712
			$delimiter   = ( isset( $_POST['delimiter'] ) ? give_clean( $_POST['delimiter'] ) : 'csv' );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
713
			$mode        = empty( $_POST['mode'] ) ?
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
714
				'disabled' :
715
				( give_is_setting_enabled( give_clean( $_POST['mode'] ) ) ? 'enabled' : 'disabled' );
0 ignored issues
show
Bug introduced by
It seems like give_clean($_POST['mode']) targeting give_clean() can also be of type array; however, give_is_setting_enabled() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
716
			$create_user = empty( $_POST['create_user'] ) ?
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
717
				'enabled' :
718
				( give_is_setting_enabled( give_clean( $_POST['create_user'] ) ) ? 'enabled' : 'disabled' );
0 ignored issues
show
Bug introduced by
It seems like give_clean($_POST['create_user']) targeting give_clean() can also be of type array; however, give_is_setting_enabled() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
719
			$delete_csv  = empty( $_POST['delete_csv'] ) ?
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
720
				'enabled' :
721
				( give_is_setting_enabled( give_clean( $_POST['delete_csv'] ) ) ? 'enabled' : 'disabled' );
0 ignored issues
show
Bug introduced by
It seems like give_clean($_POST['delete_csv']) targeting give_clean() can also be of type array; however, give_is_setting_enabled() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
722
723
			// Reset csv and csv_id if csv
724
			if ( empty( $csv_id ) || ! $this->is_valid_csv( $csv_id, $csv ) ) {
0 ignored issues
show
Documentation introduced by
$csv_id is of type string|array, but the function expects a integer|boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
It seems like $csv defined by isset($_POST['csv']) ? g...ean($_POST['csv']) : '' on line 710 can also be of type array; however, Give_Import_Donations::is_valid_csv() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
725
				$csv_id = $csv = '';
726
			}
727
			$per_page = isset( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : self::$per_page;
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
728
729
			$settings = array(
730
				array(
731
					'id'          => 'csv',
732
					'name'        => __( 'Choose a CSV file:', 'give' ),
733
					'type'        => 'file',
734
					'attributes'  => array( 'editing' => 'false', 'library' => 'text' ),
735
					'description' => __( 'The file must be a Comma Seperated Version (CSV) file type only.', 'give' ),
736
					'fvalue'      => 'url',
737
					'default'     => $csv,
738
				),
739
				array(
740
					'id'    => 'csv_id',
741
					'type'  => 'hidden',
742
					'value' => $csv_id,
743
				),
744
				array(
745
					'id'          => 'delimiter',
746
					'name'        => __( 'CSV Delimiter:', 'give' ),
747
					'description' => __( 'In case your CSV file supports a different type of separator (or delimiter) -- like a tab or space -- you can set that here.', 'give' ),
748
					'default'     => $delimiter,
749
					'type'        => 'select',
750
					'options'     => array(
751
						'csv'                  => esc_html__( 'Comma', 'give' ),
752
						'tab-separated-values' => esc_html__( 'Tab', 'give' ),
753
					),
754
				),
755
				array(
756
					'id'          => 'mode',
757
					'name'        => __( 'Test Mode:', 'give' ),
758
					'description' => __( 'Test mode allows you to preview what this import would look like without making any actual changes to your site or your database.', 'give' ),
759
					'default'     => $mode,
760
					'type'        => 'radio_inline',
761
					'options'     => array(
762
						'enabled'  => __( 'Enabled', 'give' ),
763
						'disabled' => __( 'Disabled', 'give' ),
764
					),
765
				),
766
				array(
767
					'id'          => 'create_user',
768
					'name'        => __( 'Create WP users for new donors:', 'give' ),
769
					'description' => __( 'The importer can create WordPress user accounts based on the names and email addresses of the donations in your CSV file. Enable this option if you\'d like the importer to do that.', 'give' ),
770
					'default'     => $create_user,
771
					'type'        => 'radio_inline',
772
					'options'     => array(
773
						'enabled'  => __( 'Enabled', 'give' ),
774
						'disabled' => __( 'Disabled', 'give' ),
775
					),
776
				),
777
				array(
778
					'id'          => 'delete_csv',
779
					'name'        => __( 'Delete CSV after import:', 'give' ),
780
					'description' => __( 'Your CSV file will be uploaded via the WordPress Media Library. It\'s a good idea to delete it after the import is finished so that your sensitive data is not accessible on the web. Disable this only if you plan to delete the file manually later.', 'give' ),
781
					'default'     => $delete_csv,
782
					'type'        => 'radio_inline',
783
					'options'     => array(
784
						'enabled'  => __( 'Enabled', 'give' ),
785
						'disabled' => __( 'Disabled', 'give' ),
786
					),
787
				),
788
				array(
789
					'id'          => 'per_page',
790
					'name'        => __( 'Process Rows Per Batch:', 'give' ),
791
					'type'        => 'number',
792
					'description' => __( 'Determine how many rows you would like to import per cycle.', 'give' ),
793
					'default'     => $per_page,
794
					'class'       => 'give-text-small',
795
				),
796
			);
797
798
			$settings = apply_filters( 'give_import_file_upload_html', $settings );
799
800
			Give_Admin_Settings::output_fields( $settings, 'give_settings' );
801
		}
802
803
		/**
804
		 * Run when user click on the submit button.
805
		 *
806
		 * @since 1.8.14
807
		 */
808
		public function save() {
809
			// Get the current step.
810
			$step = $this->get_step();
811
812
			// Validation for first step.
813
			if ( 1 === $step ) {
814
				$csv_id = absint( $_POST['csv_id'] );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-validated input variable: $_POST
Loading history...
815
816
				if ( $this->is_valid_csv( $csv_id, esc_url( $_POST['csv'] ) ) ) {
817
818
					$url = give_import_page_url( (array) apply_filters( 'give_import_step_two_url', array(
819
						'step'          => '2',
820
						'importer-type' => $this->importer_type,
821
						'csv'           => $csv_id,
822
						'delimiter'     => isset( $_REQUEST['delimiter'] ) ? give_clean( $_REQUEST['delimiter'] ) : 'csv',
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
823
						'mode'          => empty( $_POST['mode'] ) ?
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
824
							'0' :
825
							( give_is_setting_enabled( give_clean( $_POST['mode'] ) ) ? '1' : '0' ),
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
826
						'create_user'   => empty( $_POST['create_user'] ) ?
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
827
							'0' :
828
							( give_is_setting_enabled( give_clean( $_POST['create_user'] ) ) ? '1' : '0' ),
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
829
						'delete_csv'    => empty( $_POST['delete_csv'] ) ?
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
830
							'1' :
831
							( give_is_setting_enabled( give_clean( $_POST['delete_csv'] ) ) ? '1' : '0' ),
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
832
						'per_page'      => isset( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : self::$per_page,
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
833
					) ) );
834
					?>
835
					<script type="text/javascript">
836
						window.location = "<?php echo $url; ?>"
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$url'
Loading history...
837
					</script>
838
					<?php
839
				}
840
			}
841
		}
842
843
		/**
844
		 * Check if user uploaded csv is valid or not.
845
		 *
846
		 * @since  1.8.14
847
		 * @access public
848
		 *
849
		 * @param int|bool $csv       ID of the CSV files.
850
		 * @param string   $match_url ID of the CSV files.
851
		 *
852
		 * @return bool $has_error CSV is valid or not.
853
		 */
854
		private function is_valid_csv( $csv = false, $match_url = '' ) {
855
			$is_valid_csv = true;
856
857
			if ( $csv ) {
858
				$csv_url = wp_get_attachment_url( $csv );
859
860
				$delimiter = ( ! empty( $_REQUEST['delimiter'] ) ? give_clean( $_REQUEST['delimiter'] ) : 'csv' );
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
861
862
				if (
863
					! $csv_url ||
864
					( ! empty( $match_url ) && ( $csv_url !== $match_url ) ) ||
865
					( ( $mime_type = get_post_mime_type( $csv ) ) && ! strpos( $mime_type, $delimiter ) )
866
				) {
867
					$is_valid_csv = false;
868
					Give_Admin_Settings::add_error( 'give-import-csv', __( 'Please upload or provide a valid CSV file.', 'give' ) );
869
				}
870
			} else {
871
				$is_valid_csv = false;
872
				Give_Admin_Settings::add_error( 'give-import-csv', __( 'Please upload or provide a valid CSV file.', 'give' ) );
873
			}
874
875
			return $is_valid_csv;
876
		}
877
878
879
		/**
880
		 * Render report import field
881
		 *
882
		 * @since  1.8.14
883
		 * @access public
884
		 *
885
		 * @param $field
886
		 * @param $option_value
887
		 */
888
		public function render_import_field( $field, $option_value ) {
889
			include_once GIVE_PLUGIN_DIR . 'includes/admin/tools/views/html-admin-page-imports.php';
890
		}
891
892
		/**
893
		 * Get if current page import donations page or not
894
		 *
895
		 * @since 1.8.14
896
		 * @return bool
897
		 */
898
		private function is_donations_import_page() {
899
			return 'import' === give_get_current_setting_tab() &&
900
			       isset( $_GET['importer-type'] ) &&
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
901
			       $this->importer_type === give_clean( $_GET['importer-type'] );
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
902
		}
903
	}
904
905
	Give_Import_Donations::get_instance()->setup();
906
}
907