Issues (1282)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/class-give-cli-commands.php (19 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
 * GIVE WP_CLI commands
4
 *
5
 * @package give
6
 * @since   1.7
7
 */
8
9
// Exit if accessed directly.
10
if ( ! defined( 'ABSPATH' ) ) {
11
	exit;
12
}
13
14
// Add give command.
15
WP_CLI::add_command( 'give', 'GIVE_CLI_COMMAND' );
16
17
18
/**
19
 * Work with Give through WP-CLI
20
 *
21
 * Adds CLI support to Give through WP-CLI
22
 *
23
 * @since 1.7
24
 */
25
class GIVE_CLI_COMMAND {
26
27
	/**
28
	 * This param uses to count process/step inside loop.
29
	 *
30
	 * @var int $counter Counter.
31
	 */
32
	private static $counter;
33
34
	/**
35
	 * This helps to get information give plugin data.
36
	 *
37
	 * @var Give_API Object.
38
	 */
39
	private $api;
40
41
	/**
42
	 * This helps to get unique name.
43
	 *
44
	 * @since 1.8.17
45
	 * @var array
46
	 */
47
	private $new_donor_names = array();
48
49
50
	/**
51
	 * GIVE_CLI_Command constructor.
52
	 */
53
	public function __construct() {
54
		$this->api = new Give_API();
55
	}
56
57
58
	/**
59
	 * Get Give logo
60
	 *
61
	 * ## OPTIONS
62
	 *
63
	 * None. for a fun surprise.
64
	 *
65
	 * ## EXAMPLES
66
	 *
67
	 * wp give logo
68
	 *
69
	 * @since         1.7
70
	 * @access        public
71
	 *
72
	 * @param        string $args       Command Data.
73
	 * @param        array  $assoc_args List of command data.
74
	 *
75
	 * @return        void
76
	 *
77
	 * @subcommand    ascii
78
	 */
79
	public function ascii( $args, $assoc_args ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $assoc_args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
80
		WP_CLI::log( file_get_contents( GIVE_PLUGIN_DIR . 'assets/dist/images/give-ascii-logo.txt' ) );
81
	}
82
83
84
	/**
85
	 * Get Give details
86
	 *
87
	 * ## OPTIONS
88
	 *
89
	 * None. Returns basic info regarding your Give instance.
90
	 *
91
	 * ## EXAMPLES
92
	 *
93
	 * wp give details
94
	 *
95
	 * @since         1.7
96
	 * @access        public
97
	 *
98
	 * @param        string $args       Command Data.
99
	 * @param        array  $assoc_args List of command data.
100
	 *
101
	 * @return        void
102
	 *
103
	 * @subcommand    details
104
	 */
105
	public function details( $args, $assoc_args ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $assoc_args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
106
107
		/**
108
		 * Plugin Information
109
		 */
110
		WP_CLI::log( $this->color_message( __( 'Give Version: ', 'give' ) ) . GIVE_VERSION );
111
112
		/**
113
		 * General Information.
114
		 */
115
		WP_CLI::log( "\n####   " . $this->color_message( __( 'General information', 'give' ) ) . '   ####' );
116
117
		$success_page = give_get_option( 'success_page' );
118
		$failure_page = give_get_option( 'failure_page' );
119
		$history_page = give_get_option( 'history_page' );
120
121
		WP_CLI::log( $this->color_message( sprintf( __( 'Success Page: ', 'give' ) ) ) . ( $success_page ? "[{$success_page}] " . get_permalink( $success_page ) : __( 'Not Set', 'give' ) ) );
122
		WP_CLI::log( $this->color_message( __( 'Failed Donation Page: ', 'give' ) ) . ( $failure_page ? "[{$failure_page}] " . get_permalink( $failure_page ) : __( 'Not Set', 'give' ) ) );
123
		WP_CLI::log( $this->color_message( __( 'Donation History Page: ', 'give' ) ) . ( $history_page ? "[{$history_page}] " . get_permalink( $history_page ) : __( 'Not Set', 'give' ) ) );
124
		WP_CLI::log( $this->color_message( __( 'Country: ', 'give' ) ) . give_get_country() );
125
126
		/**
127
		 * Currency Information.
128
		 */
129
		$default_gateway = give_get_option( 'default_gateway' );
130
131
		WP_CLI::log( "\n####   " . $this->color_message( __( 'Currency Information', 'give' ) ) . '   ####' );
132
133
		WP_CLI::log( $this->color_message( __( 'Currency: ', 'give' ), give_get_currency() ) );
134
		WP_CLI::log( $this->color_message( __( 'Currency Position: ', 'give' ), give_get_currency_position() ) );
135
		WP_CLI::log( $this->color_message( __( 'Thousand Separator: ', 'give' ), give_get_price_thousand_separator() ) );
136
		WP_CLI::log( $this->color_message( __( 'Decimal Separator: ', 'give' ), give_get_price_decimal_separator() ) );
137
		WP_CLI::log( $this->color_message( __( 'Number of Decimals: ', 'give' ), give_get_price_decimals() ) );
138
		WP_CLI::log( $this->color_message( __( 'Test Mode: ', 'give' ), ( give_get_option( 'test_mode' ) ? __( 'Yes', 'give' ) : __( 'No', 'give' ) ) ) );
139
		WP_CLI::log( $this->color_message( __( 'Default Gateway: ', 'give' ), ( $default_gateway ? $default_gateway : __( 'Not Set', 'give' ) ) ) );
140
141
		// Payment gateways Information.
142
		$gateways = give_get_ordered_payment_gateways( give_get_payment_gateways() );
143
		WP_CLI::log( $this->color_message( __( 'Enabled Gateways: ', 'give' ) ) );
144
145
		if ( ! empty( $gateways ) ) {
146
			self::$counter = 1;
147
			foreach ( $gateways as $gateway ) {
148
				WP_CLI::log( '  ' . $this->color_message( self::$counter, $gateway['admin_label'] ) );
149
				self::$counter ++;
150
			}
151
		} else {
152
			WP_CLI::log( __( 'Not any payment gateways found', 'give' ) );
153
		}
154
	}
155
156
157
	/**
158
	 * Get the forms currently posted on your Give site
159
	 *
160
	 * ## OPTIONS
161
	 *
162
	 * [--id=<form_id>]
163
	 * : A specific form ID to retrieve
164
	 *
165
	 * [--number=<form_count>]
166
	 * : Number of form to retrieve
167
	 *
168
	 * ## EXAMPLES
169
	 *
170
	 * wp give forms
171
	 * wp give forms --id=103
172
	 * wp give forms --number=103
173
	 *
174
	 * @since         1.7
175
	 * @access        public
176
	 *
177
	 * @param        string $args       Command Data.
178
	 * @param        array  $assoc_args List of command data.
179
	 *
180
	 * @return        void
181
	 *
182
	 * @subcommand    forms
183
	 */
184
	public function forms( $args, $assoc_args ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
185
		global $wp_query;
186
		$form_id = isset( $assoc_args ) && array_key_exists( 'id', $assoc_args ) ? absint( $assoc_args['id'] ) : false;
187
		$number  = isset( $assoc_args ) && array_key_exists( 'number', $assoc_args ) ? absint( $assoc_args['number'] ) : 10;
188
		$start   = time();
0 ignored issues
show
$start 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...
189
190
		// Cache previous number query var.
191
		$is_set_number = $cache_per_page = false;
192 View Code Duplication
		if ( isset( $wp_query->query_vars['number'] ) ) {
0 ignored issues
show
This code seems to be duplicated across 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...
193
			$cache_per_page = $wp_query->query_vars['number'];
194
			$is_set_number  = true;
195
		}
196
197
		// Change number query var.
198
		$wp_query->query_vars['number'] = $number;
199
200
		// Get forms.
201
		$forms = $form_id ? $this->api->get_forms( $form_id ) : $this->api->get_forms();
202
203
		// Reset number query var.
204
		if ( $is_set_number ) {
205
			$wp_query->query_vars['number'] = $cache_per_page;
206
		}
207
208
		// Bailout.
209
		if ( array_key_exists( 'error', $forms ) ) {
210
211
			WP_CLI::warning( $forms['error'] );
212
213
			return;
214
		} elseif ( empty( $forms['forms'] ) ) {
215
216
			WP_CLI::error( __( 'No forms found.', 'give' ) );
217
218
			return;
219
		}
220
221
		// Param to check if form typeis already showed or not.
222
		$is_show_form_type = false;
223
224
		if ( 1 === count( $forms ) && $form_id ) {
225
			// Show single form.
226
			foreach ( $forms['forms'][0] as $key => $info ) {
227
				switch ( $key ) {
228
					case 'stats':
229
						$this->color_main_heading( ucfirst( $key ) );
230
231
						foreach ( $info as $heading => $data ) {
232
							$this->color_sub_heading( ucfirst( $heading ) );
233
							switch ( $heading ) {
234
								default:
0 ignored issues
show
default: foreach ($d...data)); } } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
235
									foreach ( $data as $subheading => $subdata ) {
236
237
										switch ( $subheading ) {
238
											case 'earnings':
239
												WP_CLI::log( $this->color_message( $subheading . ': ', give_currency_filter( $subdata, array( 'decode_currency' => true ) ) ) );
240
												break;
241
											default:
242
												WP_CLI::log( $this->color_message( $subheading . ': ', $subdata ) );
243
										}
244
									}
245
							}
246
						}
247
						break;
248
249
					case 'pricing':
250
					case 'info':
251
					default:
252
						$this->color_main_heading( ucfirst( $key ) );
253
254
						// Show form type.
255
						if ( ! $is_show_form_type ) {
256
							$form              = new Give_Donate_Form( $form_id );
257
							$is_show_form_type = true;
258
259
							WP_CLI::log( $this->color_message( __( 'form type', 'give' ), $form->get_type() ) );
260
						}
261
262
						foreach ( $info as $heading => $data ) {
263
264
							switch ( $heading ) {
265
								case 'id':
266
									WP_CLI::log( $this->color_message( $heading, $data ) );
267
									break;
268
269
								default:
270
									$data = empty( $data ) ? __( 'Not set', 'give' ) : $data;
271
									WP_CLI::log( $this->color_message( $heading, $data ) );
272
							}
273
						}
274
				}// End switch().
275
			}// End foreach().
276
		} else {
277
			// Show multiple form.
278
			$table_data             = array();
279
			$is_table_first_row_set = false;
280
			$table_column_count     = 0;
0 ignored issues
show
$table_column_count 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...
281
282
			WP_CLI::line( $this->color_message( sprintf( __( '%d donation forms found', 'give' ), count( $forms['forms'] ) ), '', false ) );
283
284
			foreach ( $forms['forms'] as $index => $form_data ) {
285
286
				// Default table data.
287
				$table_first_row = array();
288
				$table_row       = array();
289
290
				foreach ( $form_data['info'] as $key => $form ) {
291
292
					// Do not show thumbnail, content and link in table.
293
					if ( in_array( $key, array( 'content', 'thumbnail', 'link' ), true ) ) {
294
						continue;
295
					}
296
297
					if ( ! $is_table_first_row_set ) {
298
						$table_first_row[] = $key;
299
					}
300
301
					$table_row[] = $form;
302
303
					if ( 'status' === $key ) {
304
						// First array item will be an form id in our case.
305
						$form = new Give_Donate_Form( absint( $table_row[0] ) );
306
307
						$table_row[] = $form->get_type();
308
					}
309
				}
310
311
				// Set table first row.
312
				if ( ! $is_table_first_row_set ) {
313
314
					// Add extra column to table.
315
					$table_first_row[] = 'type';
316
317
					$table_data[]           = $table_first_row;
318
					$is_table_first_row_set = true;
319
				}
320
321
				// set table data.
322
				$table_data[] = $table_row;
323
			}// End foreach().
324
325
			$this->display_table( $table_data );
326
		}// End if().
327
	}
328
329
330
	/**
331
	 * Get the donors currently on your Give site. Can also be used to create donors records
332
	 *
333
	 * ## OPTIONS
334
	 *
335
	 * [--id=<donor_id>]
336
	 * : A specific donor ID to retrieve
337
	 *
338
	 * [--email=<donor_email>]
339
	 * : The email address of the donor to retrieve
340
	 *
341
	 * [--number=<donor_count>]
342
	 * : The number of donor to retrieve
343
	 *
344
	 * [--create=<number>]
345
	 * : The number of arbitrary donors to create. Leave as 1 or blank to create a
346
	 * donor with a specific email
347
	 *
348
	 * [--form-id=<donation_form_id>]
349
	 * : Get list of donors of specific donation form
350
	 *
351
	 * [--name=<name_of_donor>]
352
	 * : Name with which you want to create new donor
353
	 *
354
	 * [--format=<output_format>]
355
	 * : In which format you want to see results. Valid formats: table, json, csv
356
	 *
357
	 * ## EXAMPLES
358
	 *
359
	 * wp give donors
360
	 * wp give donors --id=103
361
	 * wp give donors [email protected]
362
	 * wp give donors --create=1 [email protected]
363
	 * wp give donors --create=1 [email protected] --name="John Doe"
364
	 * wp give donors --create=1000
365
	 * wp give donors --number=1000
366
	 * wp give donors --form-id=1024
367
	 *
368
	 * @since         1.7
369
	 * @access        public
370
	 *
371
	 * @param        string $args       Command Data.
372
	 * @param        array  $assoc_args List of command data.
373
	 *
374
	 * @return        void
375
	 *
376
	 * @subcommand    donors
377
	 */
378
	public function donors( $args, $assoc_args ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
379
		global $wp_query;
380
		$donor_id = isset( $assoc_args ) && array_key_exists( 'id', $assoc_args ) ? absint( $assoc_args['id'] ) : false;
381
		$email    = isset( $assoc_args ) && array_key_exists( 'email', $assoc_args ) ? $assoc_args['email'] : false;
382
		$name     = isset( $assoc_args ) && array_key_exists( 'name', $assoc_args ) ? $assoc_args['name'] : '';
383
		$create   = isset( $assoc_args ) && array_key_exists( 'create', $assoc_args ) ? $assoc_args['create'] : false;
384
		$number   = isset( $assoc_args ) && array_key_exists( 'number', $assoc_args ) ? $assoc_args['number'] : 10;
385
		$form_id  = isset( $assoc_args ) && array_key_exists( 'form-id', $assoc_args ) ? $assoc_args['form-id'] : 0;
386
		$format   = isset( $assoc_args ) && array_key_exists( 'format', $assoc_args ) ? $assoc_args['format'] : 'table';
387
		$start    = time();
388
389
		if ( $create ) {
390
			if ( 80 < $create ) {
391
				WP_CLI::warning( 'Currently we can only generate maximum 80 donors.', 'give' );
392
				$create = 80;
393
			}
394
395
			$number = 1;
396
397
			if ( isset( $assoc_args['email'] ) && ! is_email( $email ) ) {
398
				WP_CLI::warning( 'Wrong email address provided.', 'give' );
399
400
				return;
401
			}
402
403
			// Create one or more donors.
404
			if ( ! $email ) {
405
				// If no email is specified, look to see if we are generating arbitrary donor accounts.
406
				$number = is_numeric( $create ) ? absint( $create ) : 1;
407
			}
408
409
			for ( $i = 0; $i < $number; $i ++ ) {
410
				$name  = $name ? $name : $this->get_random_name();
411
				$email = $email ? $email : $this->get_random_email( $name );
412
413
				$args = array(
414
					'email' => $email,
415
					'name'  => $name,
416
				);
417
418
				$donor_id = Give()->donors->add( $args );
419
420
				if ( $donor_id ) {
421
					WP_CLI::line( $this->color_message( sprintf( __( 'Donor #%d created successfully', 'give' ), $donor_id ) ) );
422
				} else {
423
					WP_CLI::error( __( 'Failed to create donor', 'give' ) );
424
				}
425
426
				// Reset email and name to false so it is generated on the next loop (if creating donors).
427
				$email = $name = false;
428
			}
429
430
			WP_CLI::line( $this->color_message( sprintf( __( '%1$d donors created in %2$d seconds', 'give' ), $number, time() - $start ) ) );
431
432
		} else {
433
			// Counter.
434
			self::$counter = 1;
435
436
			// Search for customers.
437
			$search = $donor_id ? $donor_id : $email;
438
439
			/**
440
			 * Get donors.
441
			 */
442
			// Cache previous number query var.
443
			$is_set_number = $cache_per_page = false;
444 View Code Duplication
			if ( isset( $wp_query->query_vars['number'] ) ) {
0 ignored issues
show
This code seems to be duplicated across 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...
445
				$cache_per_page = $wp_query->query_vars['number'];
446
				$is_set_number  = true;
447
			}
448
449
			// Change number query var.
450
			$wp_query->query_vars['number'] = $number;
451
452
			// Get donors.
453
			if ( $form_id ) {
454
				// @TODO: Allow user to get a list of donors by donation status.
455
				$donors = $this->get_donors_by_form_id( $form_id );
456
			} else {
457
				$donors = $this->api->get_donors( $search );
458
			}
459
460
			// Reset number query var.
461
			if ( $is_set_number ) {
462
				$wp_query->query_vars['number'] = $cache_per_page;
463
			}
464
465
			if ( isset( $donors['error'] ) ) {
466
				WP_CLI::error( $donors['error'] );
467
			}
468
469
			if ( empty( $donors ) ) {
470
				WP_CLI::error( __( 'No donors found.', 'give' ) );
471
472
				return;
473
			}
474
475
			$table_data             = array();
476
			$is_table_first_row_set = false;
477
478
			foreach ( $donors['donors'] as $donor_data ) {
479
				// Set default table row data.
480
				$table_first_row = array( __( 's_no', 'give' ) );
481
				$table_row       = array( self::$counter );
482
483
				foreach ( $donor_data as $key => $donor ) {
484
					switch ( $key ) {
485
						case 'stats':
486
							foreach ( $donor as $heading => $data ) {
487
488
								// Get first row.
489
								if ( ! $is_table_first_row_set ) {
490
									$table_first_row[] = $heading;
491
								}
492
493
								switch ( $heading ) {
494
									case 'total_spent':
495
										$table_row[] = give_currency_filter( $data, array( 'decode_currency' => true ) );
496
										break;
497
498
									default:
499
										$table_row[] = $data;
500
								}
501
							}
502
							break;
503
504
						case 'address':
505
							break;
506
507
						case 'info':
508
						default:
509
							foreach ( $donor as $heading => $data ) {
510
511
								// Get first row.
512
								if ( ! $is_table_first_row_set ) {
513
									$table_first_row[] = $heading;
514
								}
515
516
								$table_row[] = $data;
517
							}
518
					}
519
				}
520
521
				// Add first row data to table data.
522
				if ( ! $is_table_first_row_set ) {
523
					$table_data[]           = $table_first_row;
524
					$is_table_first_row_set = true;
525
				}
526
527
				// Add table row data.
528
				$table_data[] = $table_row;
529
530
				// Increase counter.
531
				self::$counter ++;
532
			}// End foreach().
533
534
			switch ( $format ) {
535
				case 'json':
536
					$table_column_name = $table_data[0];
537
					unset( $table_data[0] );
538
539
					$new_table_data = array();
540
					foreach ( $table_data as $index => $data ) {
541
						foreach ( $data as $key => $value ) {
542
							$new_table_data[ $index ][ $table_column_name[ $key ] ] = $value;
543
						}
544
					}
545
546
					WP_CLI::log( json_encode( $new_table_data ) );
547
					break;
548
549
				case 'csv':
550
					$file_path = trailingslashit( WP_CONTENT_DIR ) . 'uploads/give_donors_' . date( 'Y_m_d_s', current_time( 'timestamp' ) ) . '.csv';
551
					$fp        = fopen( $file_path, 'w' );
552
553
					if ( is_writable( $file_path ) ) {
554
						foreach ( $table_data as $fields ) {
555
							fputcsv( $fp, $fields );
556
						}
557
558
						fclose( $fp );
559
560
						WP_CLI::success( "Donors list csv created successfully: {$file_path}" );
561
					} else {
562
						WP_CLI::warning( "Unable to create donors list csv file: {$file_path} (May folder do not have write permission)" );
563
					}
564
565
					break;
566
567
				default:
568
					$this->display_table( $table_data );
569
			}// End switch().
570
		}// End if().
571
	}
572
573
574
	/**
575
	 * Get the recent donations for your Give site
576
	 *
577
	 * ## OPTIONS
578
	 *
579
	 * [--number=<donation_count>]
580
	 * : The number of donations to retrieve
581
	 *
582
	 *
583
	 * ## EXAMPLES
584
	 *
585
	 * wp give donations
586
	 * wp give donations --number=100
587
	 *
588
	 * @since         1.7
589
	 * @access        public
590
	 *
591
	 * @param        string $args       Command Data.
592
	 * @param        array  $assoc_args List of command data.
593
	 *
594
	 * @return        void
595
	 *
596
	 * @subcommand    donations
597
	 */
598
	public function donations( $args, $assoc_args ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
599
		global $wp_query;
600
		$number = isset( $assoc_args ) && array_key_exists( 'number', $assoc_args ) ? $assoc_args['number'] : 10;
601
602
		// Cache previous number query var.
603
		$is_set_number = $cache_per_page = false;
604 View Code Duplication
		if ( isset( $wp_query->query_vars['number'] ) ) {
0 ignored issues
show
This code seems to be duplicated across 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...
605
			$cache_per_page = $wp_query->query_vars['number'];
606
			$is_set_number  = true;
607
		}
608
609
		// Change number query var.
610
		$wp_query->query_vars['number'] = $number;
611
612
		// Get donations.
613
		$donations = $this->api->get_recent_donations();
614
615
		// Reset number query var.
616
		if ( $is_set_number ) {
617
			$wp_query->query_vars['number'] = $cache_per_page;
618
		}
619
620
		if ( empty( $donations ) ) {
621
			WP_CLI::error( __( 'No donations found.', 'give' ) );
622
623
			return;
624
		}
625
626
		self::$counter = 1;
627
628
		foreach ( $donations['donations'] as $key => $donation ) {
629
			$this->color_main_heading( sprintf( __( '%1$s. Donation #%2$s', 'give' ), self::$counter, $donation['ID'] ), 'Y' );
630
			self::$counter ++;
631
632
			foreach ( $donation as $column => $data ) {
633
634
				if ( is_array( $data ) ) {
635
					$this->color_sub_heading( $column );
636
					foreach ( $data as $subcolumn => $subdata ) {
637
638
						// Decode html codes.
639
						switch ( $subcolumn ) {
640
							case 'name':
641
								$subdata = html_entity_decode( $subdata );
642
								break;
643
						}
644
645
						// @TODO Check if multi dimension array information is importent to show or not. For example inside donation array we have array for fees data inside payment meta.
646
						if ( is_array( $subdata ) ) {
647
							continue;
648
						}
649
650
						WP_CLI::log( $this->color_message( $subcolumn, $subdata ) );
651
					}
652
					continue;
653
				}
654
655
				WP_CLI::log( $this->color_message( $column, $data ) );
656
			}
657
		}
658
	}
659
660
	/**
661
	 * Get give plugin report.
662
	 *
663
	 * ## OPTIONS
664
	 *
665
	 * [--id=<donation_form_id>]
666
	 * : The ID of a specific donation_form to retrieve stats for, or all
667
	 *
668
	 * [--date=<range|this_month|last_month|today|yesterday|this_quarter|last_quarter|this_year|last_year>]
669
	 * : A specific date range to retrieve stats for
670
	 *
671
	 * [--start-date=<date>]
672
	 * : The start date of a date range to retrieve stats for. Date format is MM/DD/YYYY
673
	 *
674
	 * [--end-date=<date>]
675
	 * : The end date of a date range to retrieve stats for. Date format is MM/DD/YYYY
676
	 *
677
	 * ## EXAMPLES
678
	 *
679
	 * wp give report
680
	 * wp give report --date=this_month
681
	 * wp give report --start-date=01/02/2014 --end-date=02/23/2014
682
	 * wp give report --date=last_year
683
	 * wp give report --date=last_year --id=15
684
	 *
685
	 * @since         1.7
686
	 * @access        public
687
	 *
688
	 * @param        string $args       Command Data.
689
	 * @param        array  $assoc_args List of command data.
690
	 *
691
	 * @subcommand    report
692
	 *
693
	 * @return        void
694
	 */
695
	public function report( $args, $assoc_args ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
696
		$stats      = new Give_Payment_Stats();
697
		$date       = isset( $assoc_args ) && array_key_exists( 'date', $assoc_args ) ? $assoc_args['date'] : false;
698
		$start_date = isset( $assoc_args ) && array_key_exists( 'start-date', $assoc_args ) ? $assoc_args['start-date'] : false;
699
		$end_date   = isset( $assoc_args ) && array_key_exists( 'end-date', $assoc_args ) ? $assoc_args['end-date'] : false;
700
		$form_id    = isset( $assoc_args ) && array_key_exists( 'id', $assoc_args ) ? $assoc_args['id'] : 0;
701
702
		if ( ! empty( $date ) ) {
703
			$start_date = $date;
704
			$end_date   = false;
705
		} elseif ( empty( $date ) && empty( $start_date ) ) {
706
			$start_date = 'this_month';
707
			$end_date   = false;
708
		}
709
710
		// Get stats.
711
		$earnings = $stats->get_earnings( $form_id, $start_date, $end_date );
712
		$sales    = $stats->get_sales( $form_id, $start_date, $end_date );
713
714
		WP_CLI::line( $this->color_message( __( 'Earnings', 'give' ), give_currency_filter( $earnings, array( 'decode_currency' => true ) ) ) );
715
		WP_CLI::line( $this->color_message( __( 'Sales', 'give' ), $sales ) );
716
	}
717
718
719
	/**
720
	 * Delete cache (transient).
721
	 *
722
	 * ## OPTIONS
723
	 *
724
	 * --action=<cache_action>
725
	 * : Value of this parameter can be delete (in case you want to delete all stat cache).
726
	 *
727
	 * ## EXAMPLES
728
	 *
729
	 *    # See form report
730
	 *    wp give cache --action=delete
731
	 *
732
	 * @since         1.7
733
	 * @access        public
734
	 *
735
	 * @param        string $args       Command Data.
736
	 * @param        array  $assoc_args List of command data.
737
	 *
738
	 * @return        void
739
	 *
740
	 * @subcommand    cache
741
	 */
742
	public function cache( $args, $assoc_args ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
743
		$action = isset( $assoc_args ) && array_key_exists( 'action', $assoc_args ) ? $assoc_args['action'] : false;
744
745
		// Bailout.
746
		if ( ! $action || ! in_array( $action, array( 'delete' ), true ) ) {
747
			WP_CLI::warning( __( 'Type wp give cache --action=delete to delete all stat transients', 'give' ) );
748
749
			return;
750
		}
751
752
		switch ( $action ) {
753
			case 'delete':
754
				// Reset counter.
755
				self::$counter = 1;
756
757
				if ( $this->delete_stats_transients() ) {
758
					// Report .eading.
759
					WP_CLI::success( 'Give cache deleted.' );
760
				} else {
761
					// Report .eading.
762
					WP_CLI::warning( 'We did not find any Give plugin cache to delete :)' );
763
				}
764
				break;
765
		}
766
767
	}
768
769
	/**
770
	 * Delete all form stat transient
771
	 *
772
	 * @since     1.7
773
	 * @access    private
774
	 *
775
	 * @return    bool
776
	 */
777
	private function delete_stats_transients() {
778
		global $wpdb;
779
780
		$stat_option_names = $wpdb->get_results(
781
			$wpdb->prepare(
782
				"SELECT option_name FROM {$wpdb->options} where (option_name LIKE '%%%s%%' OR option_name LIKE '%%%s%%')",
783
				array(
784
					'_transient_give_stats_',
785
					'give_cache',
786
				)
787
			),
788
			ARRAY_A
789
		);
790
791
		if ( ! empty( $stat_option_names ) ) {
792
793
			foreach ( $stat_option_names as $option_name ) {
794
				$error       = false;
0 ignored issues
show
$error 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...
795
				$option_name = $option_name['option_name'];
796
797 View Code Duplication
				switch ( true ) {
0 ignored issues
show
This code seems to be duplicated across 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...
798
					case ( false !== strpos( $option_name, 'transient' ) ):
799
						$option_name = str_replace( '_transient_', '', $option_name );
800
						$error       = delete_transient( $option_name );
801
						break;
802
803
					default:
804
						$error = delete_option( $option_name );
805
				}
806
807
				if ( $error ) {
808
					WP_CLI::log( $this->color_message( self::$counter, $option_name ) );
809
					self::$counter ++;
810
				} else {
811
					WP_CLI::log( $this->color_message( __( 'Error while deleting this transient', 'give' ), $option_name ) );
812
				}
813
			}
814
815
			return true;
816
		}
817
818
		return false;
819
	}
820
821
822
	/**
823
	 * Return colored message
824
	 *
825
	 * @param    string $heading Message heading.
826
	 * @param    string $message Message content.
827
	 * @param    bool   $colon   Check if add colon between heading and message.
828
	 * @param    string $color   Heading color.
829
	 *
830
	 * @return   string
831
	 */
832
	private function color_message( $heading, $message = '', $colon = true, $color = 'g' ) {
833
		// Add colon.
834
		if ( $colon ) {
835
			$heading = $heading . ': ';
836
		}
837
838
		return WP_CLI::colorize( "%{$color}" . $heading . '%n' ) . $message;
839
	}
840
841
842
	/**
843
	 * Output section heading.
844
	 *
845
	 * @since     1.7
846
	 * @access    private
847
	 *
848
	 * @param    string $heading Heading.
849
	 * @param    string $color   Color.
850
	 *
851
	 * @return    void
852
	 */
853
	private function color_main_heading( $heading, $color = 'g' ) {
854
		WP_CLI::log( "\n######   " . $this->color_message( $heading, '', false, $color ) . '   ######' );
855
	}
856
857
	/**
858
	 * Output section sub heading.
859
	 *
860
	 * @since     1.7
861
	 * @access    private
862
	 *
863
	 * @param    string $subheading Sub heading.
864
	 *
865
	 * @return    void
866
	 */
867
	private function color_sub_heading( $subheading ) {
868
		WP_CLI::log( "\n--->" . $subheading . '', '', false );
869
	}
870
871
872
	/**
873
	 * Display data in table format.
874
	 *
875
	 * @since     1.7
876
	 * @access    private
877
	 *
878
	 * @param    array $data Array of table data.
879
	 *
880
	 * @return    void
881
	 */
882
	private function display_table( $data ) {
883
		$table = new \cli\Table();
884
885
		// Set table header.
886
		$table->setHeaders( $data[0] );
887
888
		// Remove table header.
889
		unset( $data[0] );
890
891
		// Set table data.
892
		$table->setRows( $data );
893
894
		// Display table.
895
		$table->display();
896
	}
897
898
899
	/**
900
	 * Get donors by form id.
901
	 *
902
	 * @since 1.8
903
	 *
904
	 * @param int $form_id From id.
905
	 *
906
	 * @return array
907
	 */
908
909
	private function get_donors_by_form_id( $form_id ) {
910
		$donors = array();
911
912
		$donations = new Give_Payments_Query(
913
			array(
914
				'give_forms' => array( $form_id ),
915
				'number'     => - 1,
916
				'status'     => array( 'publish' ),
917
			)
918
		);
919
920
		$donations   = $donations->get_payments();
921
		$skip_donors = array();
922
923
		/* @var Give_Payment|object $donation Payment object. */
924
		foreach ( $donations as $donation ) {
925
926
			if ( in_array( $donation->customer_id, $skip_donors, true ) ) {
927
				continue;
928
			}
929
930
			if ( ! empty( $donors ) ) {
931
				$donors['donors'][] = current( current( $this->api->get_donors( (int) $donation->customer_id ) ) );
932
			} else {
933
				$donors = array_merge( $donors, $this->api->get_donors( (int) $donation->customer_id ) );
934
			}
935
936
			$skip_donors[] = $donation->customer_id;
937
		}
938
939
		return $donors;
940
	}
941
942
	/**
943
	 * Get random user name
944
	 *
945
	 * @since 1.8.17
946
	 * @return string
947
	 */
948
	private function get_random_name() {
949
		// First names.
950
		$names = array(
951
			'Devin',
952
			'Christopher',
953
			'Ryan',
954
			'Ethan',
955
			'John',
956
			'Zoey',
957
			'Sarah',
958
			'Michelle',
959
			'Samantha',
960
		);
961
962
		// Surnames.
963
		$surnames = array(
964
			'Walker',
965
			'Josh',
966
			'Thompson',
967
			'Anderson',
968
			'Johnson',
969
			'Tremblay',
970
			'Peltier',
971
			'Cunningham',
972
			'Simpson',
973
			'Mercado',
974
			'Sellers',
975
		);
976
977
		// Generate a random forename.
978
		$random_name = $names[ mt_rand( 0, sizeof( $names ) - 1 ) ];
979
980
		// Generate a random surname.
981
		$random_surname = $surnames[ mt_rand( 0, sizeof( $surnames ) - 1 ) ];
982
983
		// Generate name.
984
		$name = "{$random_name} {$random_surname}";
985
986
		if ( in_array( $name, $this->new_donor_names ) ) {
987
			$name = $this->get_random_name();
988
		}
989
990
		// Collect new donor names.
991
		$this->new_donor_names[] = $name;
992
993
		return $name;
994
	}
995
996
	/**
997
	 * Get random email
998
	 *
999
	 * @since 1.8.17
1000
	 *
1001
	 * @param string $name
1002
	 *
1003
	 * @return string
1004
	 */
1005
	private function get_random_email( $name ) {
1006
		return implode( '.', explode( ' ', strtolower( $name ) ) ) . '@test.com';
1007
	}
1008
1009
	/**
1010
	 * Toggle settings for Give's test mode.
1011
	 *
1012
	 * [--enable]
1013
	 * : Enable Give's test mode
1014
	 *
1015
	 * [--disable]
1016
	 * : Enable Give's test mode
1017
	 *
1018
	 * @when after_wp_load
1019
	 * @subcommand test-mode
1020
	 */
1021
	public function test_mode( $args, $assoc ) {
0 ignored issues
show
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1022
1023
		// Return if associative arguments are not specified.
1024
		if ( empty( $assoc ) ) {
1025
			WP_CLI::error( "--enable or --disable flag is missing." );
1026
			return;
1027
		}
1028
1029
		$enabled_gateways = give_get_option( 'gateways' );
1030
		$default_gateway  = give_get_option( 'default_gateway' );
1031
1032
1033
		// Enable Test Mode.
1034
		if ( true === WP_CLI\Utils\get_flag_value( $assoc, 'enable' ) ) {
1035
1036
			// Set `Test Mode` to `enabled`.
1037
			give_update_option( 'test_mode', 'enabled' );
1038
1039
1040
			// Enable `Test Donation` gateway.
1041
			$enabled_gateways['manual'] = "1";
1042
			give_update_option( 'gateways', $enabled_gateways );
1043
1044
1045
			// Set `Test Donation` as default gateway.
1046
			add_option( 'give_test_mode_default_gateway', $default_gateway );
1047
			give_update_option( 'default_gateway', 'manual' );
1048
1049
1050
			// Show success message on completion.
1051
			WP_CLI::success( 'Give Test mode enabled' );
1052
		}
1053
1054
		// Disable Test Mode.
1055
		if ( true === WP_CLI\Utils\get_flag_value( $assoc, 'disable' ) ) {
1056
1057
			// Set `Test Mode` to `disabled`.
1058
			give_update_option( 'test_mode', 'disabled' );
1059
1060
1061
			// Disable `Test Donation` gateway.
1062
			unset( $enabled_gateways['manual'] );
1063
			give_update_option( 'gateways', $enabled_gateways );
1064
1065
1066
			// Backup `Default Gateway` setting for restore on test mode disable.
1067
			$default_gateway_backup = get_option( 'give_test_mode_default_gateway' );
1068
			give_update_option( 'default_gateway', $default_gateway_backup );
1069
			delete_option( 'give_test_mode_default_gateway' );
1070
1071
1072
			// Show success message on completion.
1073
			WP_CLI::success( 'Give Test mode disabled' );
1074
		}
1075
	}
1076
1077
1078
	/**
1079
	 * Checks if the given path has a give repository installed
1080
	 * or not.
1081
	 *
1082
	 * @param string $repo_path Path to a Give Addon.
1083
	 *
1084
	 * @since 2.1.3
1085
	 *
1086
	 * @return boolean
1087
	 */
1088
	private function is_git_repo( $repo_path ) {
1089
		if ( is_dir( "{$repo_path}.git" ) ) {
1090
			return true;
1091
		}
1092
1093
		return false;
1094
	}
1095
1096
1097
	/**
1098
	 * Gets the current branch name of a Give Addon.
1099
	 *
1100
	 * @param string $repo_path Path to a Give Addon.
1101
	 *
1102
	 * @since 2.1.3
1103
	 *
1104
	 * @return string
1105
	 */
1106
	private function get_git_current_branch( $repo_path ) {
1107
1108
		exec( "cd $repo_path && git branch | grep '\*'", $branch_names );
1109
1110
		$branch_name = trim( strtolower( str_replace( '* ', '', $branch_names[0] ) ) );
1111
1112
		return $branch_name;
1113
	}
1114
1115
1116
	/**
1117
	 * Updates the current branch of Give Addons.
1118
	 * Uses the remote origin to pull the latest code.
1119
	 *
1120
	 * ## OPTIONS
1121
	 *
1122
	 * [--name=<name>]
1123
	 * : Update a single addon.
1124
	 *
1125
	 * [--exclude=<names>]
1126
	 * : Names of addons that should be excluded from updating.
1127
	 *
1128
	 * ## EXAMPLES
1129
	 * 	wp give addon-update
1130
	 * 	wp give addon-update --name="Give-Stripe"
1131
	 * 	wp give addon-update --exclude="Give-Stripe, Give-Recurring-Donations"
1132
	 *
1133
	 * @param array $pos   Array of positional arguments.
1134
	 * @param array $assoc Array of associative arguments.
1135
	 *
1136
	 * @since 2.1.3
1137
	 *
1138
	 * @subcommand addon-update
1139
	 */
1140
	public function addon_update( $pos, $assoc ) {
0 ignored issues
show
The parameter $pos is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1141
1142
		/**
1143
		 * Only 1 associative argument should be passed.
1144
		 * It can be either `--name` or `--exclude`
1145
		 */
1146
		if ( count( $assoc ) > 1 ) {
1147
			WP_CLI::error( __( 'Too many associative arguments.', 'give' ) );
1148
		}
1149
1150
		/**
1151
		 * Update a single Give addon.
1152
		 */
1153
		if ( false !== ( $addon_name = WP_CLI\Utils\get_flag_value( $assoc, 'name', false ) ) ) {
1154
			$give_addon_path = glob( WP_CONTENT_DIR . "/plugins/$addon_name/" , GLOB_ONLYDIR );
1155
1156
			/**
1157
			 * Display error if the plugin (addon) name entered does
1158
			 * not exist.
1159
			 */
1160
			if ( empty( $give_addon_path ) ) {
1161
				WP_CLI::error( sprintf( __( "The Give addon '%s' does not exist.", 'give' ), $addon_name ) );
1162
			}
1163
1164
			/**
1165
			 * If the directory does not contain a Git
1166
			 * repository, then display error and halt.
1167
			 */
1168
			if ( ! $this->is_git_repo( $give_addon_path[0] ) ) {
1169
				WP_CLI::error( __( 'This is not a Git repo', 'give' ) );
1170
			}
1171
1172
			/**
1173
			 * Get the current branch name. This branch will be updated next.
1174
			 */
1175
			$branch_name = $this->get_git_current_branch( $give_addon_path[0] );
1176
1177
			/**
1178
			 * Take the latest pull of the current branch, i.e.;
1179
			 * sync it with origin.
1180
			 */
1181
			passthru( "cd $give_addon_path[0] && git pull origin $branch_name", $return_var );
1182
1183
			/**
1184
			 * Show success/error messages depending on whether the
1185
			 * current branch of the addon was updated or not.
1186
			 */
1187
			if ( 0 === $return_var ) {
1188
				WP_CLI::success( sprintf( __( "The Give addon '%s' is up-to-date with origin." ), $addon_name ) );
1189
1190
				return;
1191
			} elseif ( 1 === $return_var ) {
1192
				WP_CLI::error( sprintf( __( "The Give addon '%s' was not updated." ), $addon_name ) );
1193
			}
1194
		}
1195
1196
		/**
1197
		 * Convert the comma-separated string of Give-addons in the
1198
		 * excluded list into array.
1199
		 */
1200
		$addon_names = WP_CLI\Utils\get_flag_value( $assoc, 'exclude', array() );
1201
		if ( ! empty( $addon_names ) ) {
1202
			$addon_names = array_map( 'trim', explode( ',', $addon_names ) );
1203
		}
1204
1205
		/**
1206
		 * Get directory paths of all the addons including
1207
		 * Give Core.
1208
		 */
1209
		$give_addon_directories = glob( WP_CONTENT_DIR . '/plugins/[gG]ive*/' , GLOB_ONLYDIR );
1210
1211
		foreach ( $give_addon_directories as $repo ) {
1212
1213
			/**
1214
			 * Extract the plugin/addon folder name
1215
			 * from the absolute path.
1216
			 */
1217
			$plugin_name = basename( $repo );
1218
1219
			/**
1220
			 * If the Give addon directory does not contain
1221
			 * a Git repo, then continue.
1222
			 */
1223
			if ( ! $this->is_git_repo( $repo ) ) {
1224
				WP_CLI::line(
1225
					sprintf(
1226
						__( "%s: '%s' does not contain git repo.", 'give' ),
1227
						WP_CLI::colorize( '%RError%n' ),
1228
						$plugin_name
1229
					)
1230
				);
1231
1232
				continue;
1233
			}
1234
1235
			/**
1236
			 * Continue if the Give addon name is in the exlusion list.
1237
			 */
1238
			if ( in_array( $plugin_name, $addon_names, true ) ) {
1239
				continue;
1240
			}
1241
1242
			/* Get the current branch name */
1243
			$branch_name = $this->get_git_current_branch( $repo );
1244
1245
			/**
1246
			 * Show a colorized (CYAN) title for each addon/plugin
1247
			 * before a pull.
1248
			 */
1249
			WP_CLI::line( WP_CLI::colorize( "> %CUpdating $plugin_name | $branch_name%n" ) );
1250
1251
			/**
1252
			 * Git pull from the current branch using
1253
			 * remote `origin`.
1254
			 */
1255
			if ( ! empty( $branch_name ) ) {
1256
				passthru( "cd $repo && git pull origin $branch_name", $return_var );
1257
			}
1258
1259
			$items[] = array(
1260
				'Give Addon' => $plugin_name,
1261
				'Branch'     => $branch_name,
1262
				'Remote'     => 'origin',
1263
				'Status'     => ( 0 === $return_var )
1264
					? __( 'Success', 'give' )
1265
					: __( 'Failed', 'give' ),
1266
			);
1267
1268
			/**
1269
			 * Leave a blank line for aesthetics.
1270
			 */
1271
			WP_CLI::line();
1272
		}
1273
1274
		/**
1275
		 * Display final results in a tabular format.
1276
		 */
1277
		WP_CLI\Utils\format_items(
1278
			'table',
1279
			$items,
1280
			array(
1281
				'Give Addon',
1282
				'Branch',
1283
				'Remote',
1284
				'Status',
1285
			)
1286
		);
1287
	}
1288
}
1289