Passed
Push — master ( de43a2...6374db )
by Brian
05:18
created

WPInv_Ajax   F

Complexity

Total Complexity 128

Size/Duplication

Total Lines 851
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 128
eloc 378
c 1
b 0
f 0
dl 0
loc 851
rs 2

23 Methods

Rating   Name   Duplication   Size   Complexity  
A wpinv_ajax_headers() 0 8 2
A init() 0 4 1
A define_ajax() 0 9 5
A do_wpinv_ajax() 0 14 3
A add_note() 0 22 6
A delete_note() 0 14 3
A get_states_field() 0 4 1
A add_ajax_events() 0 32 3
A run_tool() 0 12 3
A payment_form() 0 17 2
B get_payment_form() 0 30 7
A get_billing_details() 0 31 6
A check_new_user_email() 0 30 5
B ip_geolocation() 0 45 6
A get_aui_states_field() 0 51 4
B recalculate_invoice_totals() 0 53 9
B get_invoicing_items() 0 47 6
C add_invoice_items() 0 55 13
A get_invoice_items() 0 30 5
B edit_invoice_item() 0 64 11
A payment_form_refresh_prices() 0 27 3
C get_payment_form_states_field() 0 80 15
B remove_invoice_item() 0 45 9

How to fix   Complexity   

Complex Class

Complex classes like WPInv_Ajax often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WPInv_Ajax, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Contains the ajax handlers.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
 
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * WPInv_Ajax class.
13
 */
14
class WPInv_Ajax {
15
16
    /**
17
	 * Hook in ajax handlers.
18
	 */
19
	public static function init() {
20
		add_action( 'init', array( __CLASS__, 'define_ajax' ), 0 );
21
		add_action( 'template_redirect', array( __CLASS__, 'do_wpinv_ajax' ), 0 );
22
		self::add_ajax_events();
23
    }
24
25
    /**
26
	 * Set GetPaid AJAX constant and headers.
27
	 */
28
	public static function define_ajax() {
29
30
		if ( ! empty( $_GET['wpinv-ajax'] ) ) {
31
			getpaid_maybe_define_constant( 'DOING_AJAX', true );
32
			getpaid_maybe_define_constant( 'WPInv_DOING_AJAX', true );
33
			if ( ! WP_DEBUG || ( WP_DEBUG && ! WP_DEBUG_DISPLAY ) ) {
34
				/** @scrutinizer ignore-unhandled */ @ini_set( 'display_errors', 0 );
35
			}
36
			$GLOBALS['wpdb']->hide_errors();
37
		}
38
39
    }
40
    
41
    /**
42
	 * Send headers for GetPaid Ajax Requests.
43
	 *
44
	 * @since 1.0.18
45
	 */
46
	private static function wpinv_ajax_headers() {
47
		if ( ! headers_sent() ) {
48
			send_origin_headers();
49
			send_nosniff_header();
50
			nocache_headers();
51
			header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
52
			header( 'X-Robots-Tag: noindex' );
53
			status_header( 200 );
54
		}
55
    }
56
    
57
    /**
58
	 * Check for GetPaid Ajax request and fire action.
59
	 */
60
	public static function do_wpinv_ajax() {
61
		global $wp_query;
62
63
		if ( ! empty( $_GET['wpinv-ajax'] ) ) {
64
			$wp_query->set( 'wpinv-ajax', sanitize_text_field( wp_unslash( $_GET['wpinv-ajax'] ) ) );
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($_GET['wpinv-ajax']) can also be of type string[]; however, parameter $str of sanitize_text_field() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
			$wp_query->set( 'wpinv-ajax', sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $_GET['wpinv-ajax'] ) ) );
Loading history...
65
		}
66
67
		$action = $wp_query->get( 'wpinv-ajax' );
68
69
		if ( $action ) {
70
			self::wpinv_ajax_headers();
71
			$action = sanitize_text_field( $action );
72
			do_action( 'wpinv_ajax_' . $action );
73
			wp_die();
74
		}
75
76
    }
77
78
    /**
79
	 * Hook in ajax methods.
80
	 */
81
    public static function add_ajax_events() {
82
83
        // array( 'event' => is_frontend )
84
        $ajax_events = array(
85
            'add_note'                    => false,
86
            'delete_note'                 => false,
87
            'get_states_field'            => true,
88
            'get_aui_states_field'        => true,
89
            'payment_form'                => true,
90
            'get_payment_form'            => true,
91
            'get_payment_form_states_field' => true,
92
            'get_invoicing_items'         => false,
93
            'get_invoice_items'           => false,
94
            'add_invoice_items'           => false,
95
            'edit_invoice_item'           => false,
96
            'remove_invoice_item'         => false,
97
            'get_billing_details'         => false,
98
            'recalculate_invoice_totals'  => false,
99
            'check_new_user_email'        => false,
100
            'run_tool'                    => false,
101
            'payment_form_refresh_prices' => true,
102
            'ip_geolocation'              => true,
103
        );
104
105
        foreach ( $ajax_events as $ajax_event => $nopriv ) {
106
            add_action( 'wp_ajax_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
107
            add_action( 'wp_ajax_getpaid_' . $ajax_event, array( __CLASS__, $ajax_event ) );
108
109
            if ( $nopriv ) {
110
                add_action( 'wp_ajax_nopriv_wpinv_' . $ajax_event, array( __CLASS__, $ajax_event ) );
111
                add_action( 'wp_ajax_nopriv_getpaid_' . $ajax_event, array( __CLASS__, $ajax_event ) );
112
                add_action( 'wpinv_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
113
            }
114
        }
115
    }
116
    
117
    public static function add_note() {
118
        check_ajax_referer( 'add-invoice-note', '_nonce' );
119
120
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
121
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
122
        }
123
124
        $post_id   = absint( $_POST['post_id'] );
125
        $note      = wp_kses_post( trim( stripslashes( $_POST['note'] ) ) );
126
        $note_type = sanitize_text_field( $_POST['note_type'] );
127
128
        $is_customer_note = $note_type == 'customer' ? 1 : 0;
129
130
        if ( $post_id > 0 ) {
131
            $note_id = wpinv_insert_payment_note( $post_id, $note, $is_customer_note );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

131
            $note_id = /** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $post_id, $note, $is_customer_note );
Loading history...
132
133
            if ( $note_id > 0 && !is_wp_error( $note_id ) ) {
134
                wpinv_get_invoice_note_line_item( $note_id );
135
            }
136
        }
137
138
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
139
    }
140
141
    public static function delete_note() {
142
        check_ajax_referer( 'delete-invoice-note', '_nonce' );
143
144
        if ( !wpinv_current_user_can_manage_invoicing() ) {
145
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
146
        }
147
148
        $note_id = (int)$_POST['note_id'];
149
150
        if ( $note_id > 0 ) {
151
            wp_delete_comment( $note_id, true );
152
        }
153
154
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
155
    }
156
    
157
    public static function get_states_field() {
158
        echo wpinv_get_states_field();
159
        
160
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
161
    }
162
163
    /**
164
     * Retrieves a given user's billing address.
165
     */
166
    public static function get_billing_details() {
167
168
        // Verify nonce.
169
        check_ajax_referer( 'wpinv-nonce' );
170
171
        // Can the user manage the plugin?
172
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
173
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
174
        }
175
176
        // Do we have a user id?
177
        $user_id = $_GET['user_id'];
178
179
        if ( empty( $user_id ) || ! is_numeric( $user_id ) ) {
180
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
181
        }
182
183
        // Fetch the billing details.
184
        $billing_details    = wpinv_get_user_address( $user_id );
185
        $billing_details    = apply_filters( 'wpinv_ajax_billing_details', $billing_details, $user_id );
186
187
        // unset the user id and email.
188
        $to_ignore = array( 'user_id', 'email' );
189
190
        foreach ( $to_ignore as $key ) {
191
            if ( isset( $billing_details[ $key ] ) ) {
192
                unset( $billing_details[ $key ] );
193
            }
194
        }
195
196
        wp_send_json_success( $billing_details );
197
198
    }
199
200
    /**
201
     * Checks if a new users email is valid.
202
     */
203
    public static function check_new_user_email() {
204
205
        // Verify nonce.
206
        check_ajax_referer( 'wpinv-nonce' );
207
208
        // Can the user manage the plugin?
209
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
210
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
211
        }
212
213
        // We need an email address.
214
        if ( empty( $_GET['email'] ) ) {
215
            _e( "Provide the new user's email address", 'invoicing' );
216
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
217
        }
218
219
        // Ensure the email is valid.
220
        $email = sanitize_text_field( $_GET['email'] );
221
        if ( ! is_email( $email ) ) {
222
            _e( 'Invalid email address', 'invoicing' );
223
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
224
        }
225
226
        // And it does not exist.
227
        if ( email_exists( $email ) ) {
228
            _e( 'A user with this email address already exists', 'invoicing' );
229
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
230
        }
231
232
        wp_send_json_success( true );
233
    }
234
    
235
    public static function run_tool() {
236
        check_ajax_referer( 'wpinv-nonce', '_nonce' );
237
        if ( !wpinv_current_user_can_manage_invoicing() ) {
238
            die(-1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
239
        }
240
        
241
        $tool = sanitize_text_field( $_POST['tool'] );
242
        
243
        do_action( 'wpinv_run_tool' );
244
        
245
        if ( !empty( $tool ) ) {
246
            do_action( 'wpinv_tool_' . $tool );
247
        }
248
    }
249
250
    /**
251
     * Retrieves the markup for a payment form.
252
     */
253
    public static function get_payment_form() {
254
255
        // Check nonce.
256
        if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], 'getpaid_ajax_form' ) ) {
257
            _e( 'Error: Reload the page and try again.', 'invoicing' );
258
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
259
        }
260
261
        // Is the request set up correctly?
262
		if ( empty( $_GET['form'] ) && empty( $_GET['item'] ) ) {
263
			echo aui()->alert(
264
				array(
265
					'type'    => 'warning',
266
					'content' => __( 'No payment form or item provided', 'invoicing' ),
267
				)
268
            );
269
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
270
        }
271
272
        // Payment form or button?
273
		if ( ! empty( $_GET['form'] ) ) {
274
            getpaid_display_payment_form( $_GET['form'] );
275
		} else if( ! empty( $_GET['invoice'] ) ) {
276
		    getpaid_display_invoice_payment_form( $_GET['invoice'] );
277
        } else {
278
			$items = getpaid_convert_items_to_array( $_GET['item'] );
279
		    getpaid_display_item_payment_form( $items );
280
        }
281
282
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
283
284
    }
285
286
    /**
287
     * Payment forms.
288
     *
289
     * @since 1.0.18
290
     */
291
    public static function payment_form() {
292
293
        // Check nonce.
294
        check_ajax_referer( 'getpaid_form_nonce' );
295
296
        // ... form fields...
297
        if ( empty( $_POST['getpaid_payment_form_submission'] ) ) {
298
            _e( 'Error: Reload the page and try again.', 'invoicing' );
299
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
300
        }
301
302
        // Process the payment form.
303
        $checkout_class = apply_filters( 'getpaid_checkout_class', 'GetPaid_Checkout' );
304
        $checkout       = new $checkout_class( new GetPaid_Payment_Form_Submission() );
305
        $checkout->process_checkout();
306
307
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
308
    }
309
310
    /**
311
     * Payment forms.
312
     *
313
     * @since 1.0.18
314
     */
315
    public static function get_payment_form_states_field() {
316
317
        if ( empty( $_GET['country'] ) || empty( $_GET['form'] ) ) {
318
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
319
        }
320
321
        $elements = getpaid_get_payment_form_elements( $_GET['form'] );
322
323
        if ( empty( $elements ) ) {
324
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
325
        }
326
327
        $address_fields = array();
328
        foreach ( $elements as $element ) {
329
            if ( 'address' === $element['type'] ) {
330
                $address_fields = $element;
331
                break;
332
            }
333
        }
334
335
        if ( empty( $address_fields ) ) {
336
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
337
        }
338
339
        foreach ( $address_fields['fields'] as $address_field ) {
340
341
            if ( 'wpinv_state' == $address_field['name'] ) {
342
343
                $placeholder = empty( $address_field['placeholder'] ) ? '' : esc_attr( $address_field['placeholder'] );
344
                $description = empty( $address_field['description'] ) ? '' : wp_kses_post( $address_field['description'] );
345
                $value       = is_user_logged_in() ? get_user_meta( get_current_user_id(), '_wpinv_state', true ) : '';
346
                $label       = empty( $address_field['label'] ) ? '' : wp_kses_post( $address_field['label'] );
347
348
                if ( ! empty( $address_field['required'] ) ) {
349
                    $label .= "<span class='text-danger'> *</span>";
350
                }
351
352
                $states = wpinv_get_country_states( $_GET['country'] );
353
354
                if ( ! empty( $states ) ) {
355
356
                    $html = aui()->select( array(
357
                        'options'          => $states,
358
                        'name'             => 'wpinv_state',
359
                        'id'               => 'wpinv_state' . uniqid(),
360
                        'value'            => sanitize_text_field( $value ),
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type false; however, parameter $str of sanitize_text_field() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

360
                        'value'            => sanitize_text_field( /** @scrutinizer ignore-type */ $value ),
Loading history...
361
                        'placeholder'      => $placeholder,
362
                        'required'         => ! empty( $address_field['required'] ),
363
                        'label'            => wp_kses_post( $label ),
364
                        'label_type'       => 'vertical',
365
                        'help_text'        => $description,
366
                        'class'            => 'wpinv_state',
367
                    ));
368
369
                } else {
370
371
                    $html = aui()->input(
372
                        array(
373
                            'name'       => 'wpinv_state',
374
                            'id'         => 'wpinv_state' . uniqid(),
375
                            'placeholder'=> $placeholder,
376
                            'required'   => ! empty( $address_field['required'] ),
377
                            'label'      => wp_kses_post( $label ),
378
                            'label_type' => 'vertical',
379
                            'help_text'  => $description,
380
                            'value'      => $value,
381
                            'class'      => 'wpinv_state',
382
                        )
383
                    );
384
385
                }
386
387
                wp_send_json_success( $html );
388
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
389
390
            }
391
392
        }
393
    
394
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
395
    }
396
397
    /**
398
     * Recalculates invoice totals.
399
     */
400
    public static function recalculate_invoice_totals() {
401
402
        // Verify nonce.
403
        check_ajax_referer( 'wpinv-nonce' );
404
405
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
406
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
407
        }
408
409
        // We need an invoice.
410
        if ( empty( $_POST['post_id'] ) ) {
411
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
412
        }
413
414
        // Fetch the invoice.
415
        $invoice = new WPInv_Invoice( trim( $_POST['post_id'] ) );
416
417
        // Ensure it exists.
418
        if ( ! $invoice->get_id() ) {
419
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
420
        }
421
422
        // Maybe set the country, state, currency.
423
        foreach ( array( 'country', 'state', 'currency' ) as $key ) {
424
            if ( isset( $_POST[ $key ] ) ) {
425
                $method = "set_$key";
426
                $invoice->$method( $_POST[ $key ] );
427
            }
428
        }
429
430
        // Maybe disable taxes.
431
        $invoice->set_disable_taxes( ! empty( $_POST['taxes'] ) );
432
433
        // Recalculate totals.
434
        $invoice->recalculate_total();
435
436
        $total = wpinv_price( wpinv_format_amount( $invoice->get_total() ), $invoice->get_currency() );
437
438
        if ( $invoice->is_recurring() && $invoice->is_parent() && $invoice->get_total() != $invoice->get_recurring_total() ) {
439
            $recurring_total = wpinv_price( wpinv_format_amount( $invoice->get_recurring_total() ), $invoice->get_currency() );
440
            $total          .= '<small class="form-text text-muted">' . sprintf( __( 'Recurring Price: %s', 'invoicing' ), $recurring_total ) . '</small>';
441
        }
442
443
        $totals = array(
444
            'subtotal' => wpinv_price( wpinv_format_amount( $invoice->get_subtotal() ), $invoice->get_currency() ),
445
            'discount' => wpinv_price( wpinv_format_amount( $invoice->get_total_discount() ), $invoice->get_currency() ),
446
            'tax'      => wpinv_price( wpinv_format_amount( $invoice->get_total_tax() ), $invoice->get_currency() ),
447
            'total'    => $total,
448
        );
449
450
        $totals = apply_filters( 'getpaid_invoice_totals', $totals, $invoice );
451
452
        wp_send_json_success( compact( 'totals' ) );
453
    }
454
455
    /**
456
     * Get items belonging to a given invoice.
457
     */
458
    public static function get_invoice_items() {
459
460
        // Verify nonce.
461
        check_ajax_referer( 'wpinv-nonce' );
462
463
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
464
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
465
        }
466
467
        // We need an invoice and items.
468
        if ( empty( $_POST['post_id'] ) ) {
469
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
470
        }
471
472
        // Fetch the invoice.
473
        $invoice = new WPInv_Invoice( trim( $_POST['post_id'] ) );
474
475
        // Ensure it exists.
476
        if ( ! $invoice->get_id() ) {
477
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
478
        }
479
480
        // Return an array of invoice items.
481
        $items = array();
482
483
        foreach ( $invoice->get_items() as $item_id => $item ) {
484
            $items[ $item_id ] = $item->prepare_data_for_invoice_edit_ajax(  $invoice->get_currency()  );
485
        }
486
487
        wp_send_json_success( compact( 'items' ) );
488
    }
489
490
    /**
491
     * Edits an invoice item.
492
     */
493
    public static function edit_invoice_item() {
494
495
        // Verify nonce.
496
        check_ajax_referer( 'wpinv-nonce' );
497
498
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
499
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
500
        }
501
502
        // We need an invoice and item details.
503
        if ( empty( $_POST['post_id'] ) || empty( $_POST['data'] ) ) {
504
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
505
        }
506
507
        // Fetch the invoice.
508
        $invoice = new WPInv_Invoice( trim( $_POST['post_id'] ) );
509
510
        // Ensure it exists and its not been paid for.
511
        if ( ! $invoice->get_id() || $invoice->is_paid() || $invoice->is_refunded() ) {
512
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
513
        }
514
515
        // Format the data.
516
        $data = wp_list_pluck( $_POST['data'], 'value', 'field' );
517
518
        // Ensure that we have an item id.
519
        if ( empty( $data['id'] ) ) {
520
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
521
        }
522
523
        // Abort if the invoice does not have the specified item.
524
        $item = $invoice->get_item( (int) $data['id'] );
525
526
        if ( empty( $item ) ) {
527
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
528
        }
529
530
        // Update the item.
531
        $item->set_price( $data['price'] );
532
        $item->set_name( $data['name'] );
533
        $item->set_description( $data['description'] );
534
        $item->set_quantity( $data['quantity'] );
535
536
        // Add it to the invoice.
537
        $error = $invoice->add_item( $item );
538
        $alert = false;
539
        if ( is_wp_error( $error ) ) {
540
            $alert = $error->get_error_message();
541
        }
542
543
        // Update totals.
544
        $invoice->recalculate_total();
545
546
        // Save the invoice.
547
        $invoice->save();
548
549
        // Return an array of invoice items.
550
        $items = array();
551
552
        foreach ( $invoice->get_items() as $item_id => $item ) {
553
            $items[ $item_id ] = $item->prepare_data_for_invoice_edit_ajax(  $invoice->get_currency()  );
554
        }
555
556
        wp_send_json_success( compact( 'items', 'alert' ) );
557
    }
558
559
    /**
560
     * Deletes an invoice item.
561
     */
562
    public static function remove_invoice_item() {
563
564
        // Verify nonce.
565
        check_ajax_referer( 'wpinv-nonce' );
566
567
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
568
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
569
        }
570
571
        // We need an invoice and an item.
572
        if ( empty( $_POST['post_id'] ) || empty( $_POST['item_id'] ) ) {
573
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
574
        }
575
576
        // Fetch the invoice.
577
        $invoice = new WPInv_Invoice( trim( $_POST['post_id'] ) );
578
579
        // Ensure it exists and its not been paid for.
580
        if ( ! $invoice->get_id() || $invoice->is_paid() || $invoice->is_refunded() ) {
581
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
582
        }
583
584
        // Abort if the invoice does not have the specified item.
585
        $item = $invoice->get_item( (int) $_POST['item_id'] );
586
587
        if ( empty( $item ) ) {
588
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
589
        }
590
591
        $invoice->remove_item( (int) $_POST['item_id'] );
592
593
        // Update totals.
594
        $invoice->recalculate_total();
595
596
        // Save the invoice.
597
        $invoice->save();
598
599
        // Return an array of invoice items.
600
        $items = array();
601
602
        foreach ( $invoice->get_items() as $item_id => $item ) {
603
            $items[ $item_id ] = $item->prepare_data_for_invoice_edit_ajax(  $invoice->get_currency()  );
604
        }
605
606
        wp_send_json_success( compact( 'items' ) );
607
    }
608
609
    /**
610
     * Adds a items to an invoice.
611
     */
612
    public static function add_invoice_items() {
613
614
        // Verify nonce.
615
        check_ajax_referer( 'wpinv-nonce' );
616
617
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
618
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
619
        }
620
621
        // We need an invoice and items.
622
        if ( empty( $_POST['post_id'] ) || empty( $_POST['items'] ) ) {
623
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
624
        }
625
626
        // Fetch the invoice.
627
        $invoice = new WPInv_Invoice( trim( $_POST['post_id'] ) );
628
        $alert   = false;
629
630
        // Ensure it exists and its not been paid for.
631
        if ( ! $invoice->get_id() || $invoice->is_paid() || $invoice->is_refunded() ) {
632
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
633
        }
634
635
        // Add the items.
636
        foreach ( $_POST['items'] as $data ) {
637
638
            $item = new GetPaid_Form_Item( $data[ 'id' ] );
639
640
            if ( is_numeric( $data[ 'qty' ] ) && (int) $data[ 'qty' ] > 0 ) {
641
                $item->set_quantity( $data[ 'qty' ] );
642
            }
643
644
            if ( $item->get_id() > 0 ) {
645
                $error = $invoice->add_item( $item );
646
647
                if ( is_wp_error( $error ) ) {
648
                    $alert = $error->get_error_message();
649
                }
650
651
            }
652
653
        }
654
655
        // Save the invoice.
656
        $invoice->recalculate_total();
657
        $invoice->save();
658
659
        // Return an array of invoice items.
660
        $items = array();
661
662
        foreach ( $invoice->get_items() as $item_id => $item ) {
663
            $items[ $item_id ] = $item->prepare_data_for_invoice_edit_ajax( $invoice->get_currency() );
664
        }
665
666
        wp_send_json_success( compact( 'items', 'alert' ) );
667
    }
668
669
    /**
670
     * Retrieves items that should be added to an invoice.
671
     */
672
    public static function get_invoicing_items() {
673
674
        // Verify nonce.
675
        check_ajax_referer( 'wpinv-nonce' );
676
677
        if ( ! wpinv_current_user_can_manage_invoicing() ) {
678
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
679
        }
680
681
        // We need a search term.
682
        if ( empty( $_GET['search'] ) ) {
683
            wp_send_json_success( array() );
684
        }
685
686
        // Retrieve items.
687
        $item_args = array(
688
            'post_type'      => 'wpi_item',
689
            'orderby'        => 'title',
690
            'order'          => 'ASC',
691
            'posts_per_page' => -1,
692
            'post_status'    => array( 'publish' ),
693
            's'              => trim( $_GET['search'] ),
694
            'meta_query'     => array(
695
                array(
696
                    'key'       => '_wpinv_type',
697
                    'compare'   => '!=',
698
                    'value'     => 'package'
699
                )
700
            )
701
        );
702
703
        $items = get_posts( apply_filters( 'getpaid_ajax_invoice_items_query_args', $item_args ) );
704
        $data  = array();
705
706
707
        $is_payment_form = ( ! empty( $_GET['post_id'] ) && 'wpi_payment_form' == get_post_type( $_GET['post_id'] ) );
708
709
        foreach ( $items as $item ) {
710
            $item      = new GetPaid_Form_Item( $item );
711
            $data[] = array(
712
                'id'        => (int) $item->get_id(),
713
                'text'      => strip_tags( $item->get_name() ),
714
                'form_data' => $is_payment_form ? $item->prepare_data_for_use( false ) : '',
715
            );
716
        }
717
718
        wp_send_json_success( $data );
719
720
    }
721
722
    /**
723
     * Retrieves the states field for AUI forms.
724
     */
725
    public static function get_aui_states_field() {
726
727
        // Verify nonce.
728
        check_ajax_referer( 'wpinv-nonce' );
729
730
        // We need a country.
731
        if ( empty( $_GET['country'] ) ) {
732
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
733
        }
734
735
        $states = wpinv_get_country_states( trim( $_GET['country'] ) );
736
        $state  = isset( $_GET['state'] ) ? trim( $_GET['state'] ) : wpinv_get_default_state();
737
738
        if ( empty( $states ) ) {
739
740
            $html = aui()->input(
741
                array(
742
                    'type'        => 'text',
743
                    'id'          => 'wpinv_state',
744
                    'name'        => 'wpinv_state',
745
                    'label'       => __( 'State', 'invoicing' ),
746
                    'label_type'  => 'vertical',
747
                    'placeholder' => 'Liège',
748
                    'class'       => 'form-control-sm',
749
                    'value'       => $state,
750
                )
751
            );
752
753
        } else {
754
755
            $html = aui()->select(
756
                array(
757
                    'id'          => 'wpinv_state',
758
                    'name'        => 'wpinv_state',
759
                    'label'       => __( 'State', 'invoicing' ),
760
                    'label_type'  => 'vertical',
761
                    'placeholder' => __( 'Select a state', 'invoicing' ),
762
                    'class'       => 'form-control-sm',
763
                    'value'       => $state,
764
                    'options'     => $states,
765
                    'data-allow-clear' => 'false',
766
                    'select2'          => true,
767
                )
768
            );
769
770
        }
771
772
        wp_send_json_success(
773
            array(
774
                'html'   => $html,
775
                'select' => ! empty ( $states )
776
            )
777
        );
778
779
    }
780
781
    /**
782
     * IP geolocation.
783
     *
784
     * @since 1.0.19
785
     */
786
    public static function ip_geolocation() {
787
788
        // Check nonce.
789
        check_ajax_referer( 'getpaid-ip-location' );
790
791
        // IP address.
792
        if ( empty( $_GET['ip'] ) || ! rest_is_ip_address( $_GET['ip'] ) ) {
793
            _e( 'Invalid IP Address.', 'invoicing' );
794
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
795
        }
796
797
        // Retrieve location info.
798
        $location = getpaid_geolocate_ip_address( $_GET['ip'] );
799
800
        if ( empty( $location ) ) {
801
            _e( 'Unable to find geolocation for the IP Address.', 'invoicing' );
802
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
803
        }
804
805
        // Sorry.
806
        extract( $location );
807
808
        // Prepare the address.
809
        $content = '';
810
811
        if ( ! empty( $location['city'] ) ) {
812
            $content .=  $location['city']  . ', ';
813
        }
814
        
815
        if ( ! empty( $location['region'] ) ) {
816
            $content .=  $location['region']  . ', ';
817
        }
818
        
819
        $content .=  $location['country'] . ' (' . $location['iso'] . ')';
820
821
        $location['address'] = $content;
822
823
        $content  = '<p>'. sprintf( __( '<b>Address:</b> %s', 'invoicing' ), $content ) . '</p>';
824
        $content .= '<p>'. $location['credit'] . '</p>';
825
826
        $location['content'] = $content;
827
828
        wpinv_get_template( 'geolocation.php', $location );
829
830
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
831
    }
832
833
    /**
834
     * Refresh prices.
835
     *
836
     * @since 1.0.19
837
     */
838
    public static function payment_form_refresh_prices() {
839
840
        // Check nonce.
841
        check_ajax_referer( 'getpaid_form_nonce' );
842
843
        // ... form fields...
844
        if ( empty( $_POST['getpaid_payment_form_submission'] ) ) {
845
            _e( 'Error: Reload the page and try again.', 'invoicing' );
846
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
847
        }
848
849
        // Load the submission.
850
        $submission = new GetPaid_Payment_Form_Submission();
851
852
        // Do we have an error?
853
        if ( ! empty( $submission->last_error ) ) {
854
            echo $submission->last_error;
855
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
856
        }
857
858
        // Prepare the response.
859
        $response = new GetPaid_Payment_Form_Submission_Refresh_Prices( $submission );
860
        
861
        // Filter the response.
862
        $response = apply_filters( 'getpaid_payment_form_ajax_refresh_prices', $response->response, $submission );
863
864
        wp_send_json_success( $response );
865
    }
866
867
}
868
869
WPInv_Ajax::init();