Passed
Push — master ( 6929e7...c24352 )
by Brian
10:18 queued 05:36
created

getpaid_ip_location_url()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 1
nc 1
nop 1
1
<?php
2
// MUST have WordPress.
3
if ( !defined( 'WPINC' ) ) {
4
    exit( 'Do NOT access this file directly: ' . basename( __FILE__ ) );
5
}
6
7
/**
8
 * Retrieves the tax class.
9
 * 
10
 * @return WPInv_EUVat
11
 */
12
function getpaid_tax() {
13
    return new WPInv_EUVat();
14
}
15
16
/**
17
 * Checks if a given country is an EU state.
18
 * 
19
 * @return bool
20
 */
21
function getpaid_is_eu_state( $country ) {
22
    return WPInv_EUVat::is_eu_state( $country );
23
}
24
25
/**
26
 * Checks if a given country is GST country.
27
 * 
28
 * @return bool
29
 */
30
function getpaid_is_gst_country( $country ) {
31
    return WPInv_EUVat::is_gst_country( $country );
32
}
33
34
/**
35
 * Returns the vat name.
36
 * 
37
 * @return string
38
 */
39
function getpaid_vat_name() {
40
    return getpaid_tax()->get_vat_name();
41
}
42
43
44
function wpinv_use_taxes() {
45
    $ret = wpinv_get_option( 'enable_taxes', false );
46
    
47
    return (bool) apply_filters( 'wpinv_use_taxes', $ret );
48
}
49
50
function wpinv_get_tax_rates() {
51
    $rates = get_option( 'wpinv_tax_rates', array() );
52
    
53
    return apply_filters( 'wpinv_get_tax_rates', $rates );
54
}
55
56
function wpinv_get_tax_rate( $country = false, $state = false, $item_id = 0 ) {
57
    global $wpinv_euvat, $wpi_tax_rates, $wpi_userID;
58
    $wpi_tax_rates = !empty( $wpi_tax_rates ) ? $wpi_tax_rates : array();
59
    
60
    if ( !empty( $wpi_tax_rates ) && !empty( $item_id ) && isset( $wpi_tax_rates[$item_id] ) ) {
61
        return $wpi_tax_rates[$item_id];
62
    }
63
    
64
    if ( !$wpinv_euvat->item_is_taxable( $item_id, $country, $state ) ) {
65
        $wpi_tax_rates[$item_id] = 0;
66
        return 0;
67
    }
68
69
    $is_global = false;
70
    if ( $item_id == 'global' ) {
71
        $is_global = true;
72
        $item_id = 0;
73
    }
74
    
75
    $rate           = (float)wpinv_get_option( 'tax_rate', 0 );
76
    $user_address   = wpinv_get_user_address( $wpi_userID );
77
    
78
    if( empty( $country ) ) {
79
        if( !empty( $_POST['wpinv_country'] ) ) {
80
            $country = $_POST['wpinv_country'];
81
        } elseif( !empty( $_POST['wpinv_country'] ) ) {
82
            $country = $_POST['wpinv_country'];
83
        } elseif( !empty( $_POST['country'] ) ) {
84
            $country = $_POST['country'];
85
        } elseif( is_user_logged_in() && !empty( $user_address ) ) {
86
            $country = $user_address['country'];
87
        }
88
        $country = !empty( $country ) ? $country : wpinv_get_default_country();
89
    }
90
91
    if( empty( $state ) ) {
92
        if( !empty( $_POST['wpinv_state'] ) ) {
93
            $state = $_POST['wpinv_state'];
94
        } elseif( !empty( $_POST['wpinv_state'] ) ) {
95
            $state = $_POST['wpinv_state'];
96
        } elseif( !empty( $_POST['state'] ) ) {
97
            $state = $_POST['state'];
98
        } elseif( is_user_logged_in() && !empty( $user_address ) ) {
99
            $state = $user_address['state'];
100
        }
101
        $state = !empty( $state ) ? $state : wpinv_get_default_state();
102
    }
103
    
104
    if( !empty( $country ) ) {
105
        $tax_rates   = wpinv_get_tax_rates();
106
107
        if( !empty( $tax_rates ) ) {
108
            // Locate the tax rate for this country / state, if it exists
109
            foreach( $tax_rates as $key => $tax_rate ) {
110
                if( $country != $tax_rate['country'] )
111
                    continue;
112
113
                if( !empty( $tax_rate['global'] ) ) {
114
                    if( !empty( $tax_rate['rate'] ) ) {
115
                        $rate = number_format( $tax_rate['rate'], 4 );
116
                    }
117
                } else {
118
119
                    if( empty( $tax_rate['state'] ) || strtolower( $state ) != strtolower( $tax_rate['state'] ) )
120
                        continue;
121
122
                    $state_rate = $tax_rate['rate'];
123
                    if( 0 !== $state_rate || !empty( $state_rate ) ) {
124
                        $rate = number_format( $state_rate, 4 );
125
                    }
126
                }
127
            }
128
        }
129
    }
130
    
131
    $rate = apply_filters( 'wpinv_tax_rate', $rate, $country, $state, $item_id );
132
    
133
    if ( !empty( $item_id ) ) {
134
        $wpi_tax_rates[$item_id] = $rate;
135
    } else if ( $is_global ) {
136
        $wpi_tax_rates['global'] = $rate;
137
    }
138
    
139
    return $rate;
140
}
141
142
function wpinv_get_formatted_tax_rate( $country = false, $state = false, $item_id ) {
143
    $rate = wpinv_get_tax_rate( $country, $state, $item_id );
144
    $rate = round( $rate, 4 );
145
    $formatted = $rate .= '%';
146
    return apply_filters( 'wpinv_formatted_tax_rate', $formatted, $rate, $country, $state, $item_id );
147
}
148
149
function wpinv_calculate_tax( $amount = 0, $country = false, $state = false, $item_id = 0 ) {
150
    $rate = wpinv_get_tax_rate( $country, $state, $item_id );
151
    $tax  = 0.00;
152
153
    if ( wpinv_use_taxes() ) {        
154
        if ( wpinv_prices_include_tax() ) {
155
            $pre_tax = ( $amount / ( ( 1 + $rate ) * 0.01 ) );
156
            $tax     = $amount - $pre_tax;
157
        } else {
158
            $tax = $amount * $rate * 0.01;
159
        }
160
161
    }
162
163
    return apply_filters( 'wpinv_taxed_amount', $tax, $rate, $country, $state, $item_id );
164
}
165
166
function wpinv_prices_include_tax() {
167
    return false; // TODO
168
    $ret = ( wpinv_get_option( 'prices_include_tax', false ) == 'yes' && wpinv_use_taxes() );
0 ignored issues
show
Unused Code introduced by
$ret = wpinv_get_option(...s' && wpinv_use_taxes() is not 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...
169
170
    return apply_filters( 'wpinv_prices_include_tax', $ret );
171
}
172
173
function wpinv_sales_tax_for_year( $year = null ) {
174
    return wpinv_price( wpinv_format_amount( wpinv_get_sales_tax_for_year( $year ) ) );
175
}
176
177
function wpinv_get_sales_tax_for_year( $year = null ) {
178
    global $wpdb;
179
180
    // Start at zero
181
    $tax = 0;
182
183
    if ( ! empty( $year ) ) {
184
        $args = array(
185
            'post_type'      => 'wpi_invoice',
186
            'post_status'    => array( 'publish' ),
187
            'posts_per_page' => -1,
188
            'year'           => $year,
189
            'fields'         => 'ids'
190
        );
191
192
        $payments    = get_posts( $args );
193
        $payment_ids = implode( ',', $payments );
194
195
        if ( count( $payments ) > 0 ) {
196
            $sql = "SELECT SUM( meta_value ) FROM $wpdb->postmeta WHERE meta_key = '_wpinv_tax' AND post_id IN( $payment_ids )";
197
            $tax = $wpdb->get_var( $sql );
198
        }
199
200
    }
201
202
    return apply_filters( 'wpinv_get_sales_tax_for_year', $tax, $year );
203
}
204
205
function wpinv_is_cart_taxed() {
206
    return wpinv_use_taxes();
207
}
208
209
function wpinv_prices_show_tax_on_checkout() {
210
    return false; // TODO
211
    $ret = ( wpinv_get_option( 'checkout_include_tax', false ) == 'yes' && wpinv_use_taxes() );
0 ignored issues
show
Unused Code introduced by
$ret = wpinv_get_option(...s' && wpinv_use_taxes() is not 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...
212
213
    return apply_filters( 'wpinv_taxes_on_prices_on_checkout', $ret );
214
}
215
216
function wpinv_display_tax_rate() {
217
    $ret = wpinv_use_taxes() && wpinv_get_option( 'display_tax_rate', false );
218
219
    return apply_filters( 'wpinv_display_tax_rate', $ret );
220
}
221
222
function wpinv_cart_needs_tax_address_fields() {
223
    if( !wpinv_is_cart_taxed() )
224
        return false;
225
226
    return ! did_action( 'wpinv_after_cc_fields', 'wpinv_default_cc_address_fields' );
0 ignored issues
show
Unused Code introduced by
The call to did_action() has too many arguments starting with 'wpinv_default_cc_address_fields'. ( Ignorable by Annotation )

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

226
    return ! /** @scrutinizer ignore-call */ did_action( 'wpinv_after_cc_fields', 'wpinv_default_cc_address_fields' );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
227
}
228
229
function wpinv_item_is_tax_exclusive( $item_id = 0 ) {
230
    $ret = (bool)get_post_meta( $item_id, '_wpinv_tax_exclusive', false );
231
    return apply_filters( 'wpinv_is_tax_exclusive', $ret, $item_id );
232
}
233
234
function wpinv_currency_decimal_filter( $decimals = 2 ) {
235
    $currency = wpinv_get_currency();
236
237
    switch ( $currency ) {
238
        case 'RIAL' :
239
        case 'JPY' :
240
        case 'TWD' :
241
        case 'HUF' :
242
            $decimals = 0;
243
            break;
244
    }
245
246
    return apply_filters( 'wpinv_currency_decimal_count', $decimals, $currency );
247
}
248
249
function wpinv_tax_amount() {
250
    $output = 0.00;
251
    
252
    return apply_filters( 'wpinv_tax_amount', $output );
253
}
254
255
function wpinv_recalculated_tax() {
256
    define( 'WPINV_RECALCTAX', true );
257
}
258
add_action( 'wp_ajax_wpinv_recalculate_tax', 'wpinv_recalculated_tax', 1 );
259
260
function wpinv_recalculate_tax( $return = false ) {
261
    $invoice_id = (int)wpinv_get_invoice_cart_id();
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_invoice_cart_id() 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

261
    $invoice_id = (int)/** @scrutinizer ignore-deprecated */ wpinv_get_invoice_cart_id();
Loading history...
262
    if ( empty( $invoice_id ) ) {
263
        return false;
264
    }
265
    
266
    $invoice = wpinv_get_invoice_cart( $invoice_id );
0 ignored issues
show
Unused Code introduced by
The call to wpinv_get_invoice_cart() has too many arguments starting with $invoice_id. ( Ignorable by Annotation )

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

266
    $invoice = /** @scrutinizer ignore-call */ wpinv_get_invoice_cart( $invoice_id );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Deprecated Code introduced by
The function wpinv_get_invoice_cart() 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

266
    $invoice = /** @scrutinizer ignore-deprecated */ wpinv_get_invoice_cart( $invoice_id );
Loading history...
267
268
    if ( empty( $invoice ) ) {
269
        return false;
270
    }
271
272
    if ( empty( $_POST['country'] ) ) {
273
        $_POST['country'] = !empty($invoice->country) ? $invoice->country : wpinv_get_default_country();
0 ignored issues
show
Bug Best Practice introduced by
The property country does not exist on WPInv_Invoice. Since you implemented __get, consider adding a @property annotation.
Loading history...
274
    }
275
        
276
    $invoice->country = sanitize_text_field($_POST['country']);
0 ignored issues
show
Bug Best Practice introduced by
The property country does not exist on WPInv_Invoice. Since you implemented __set, consider adding a @property annotation.
Loading history...
277
    $invoice->set( 'country', sanitize_text_field( $_POST['country'] ) );
278
    if (isset($_POST['state'])) {
279
        $invoice->state = sanitize_text_field($_POST['state']);
0 ignored issues
show
Bug Best Practice introduced by
The property state does not exist on WPInv_Invoice. Since you implemented __set, consider adding a @property annotation.
Loading history...
280
        $invoice->set( 'state', sanitize_text_field( $_POST['state'] ) );
281
    }
282
283
    $invoice->cart_details  = wpinv_get_cart_content_details();
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_cart_content_details() 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

283
    $invoice->cart_details  = /** @scrutinizer ignore-deprecated */ wpinv_get_cart_content_details();
Loading history...
Bug Best Practice introduced by
The property cart_details does not exist on WPInv_Invoice. Since you implemented __set, consider adding a @property annotation.
Loading history...
284
    
285
    $subtotal               = wpinv_get_cart_subtotal( $invoice->cart_details );
0 ignored issues
show
Unused Code introduced by
The call to wpinv_get_cart_subtotal() has too many arguments starting with $invoice->cart_details. ( Ignorable by Annotation )

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

285
    $subtotal               = /** @scrutinizer ignore-call */ wpinv_get_cart_subtotal( $invoice->cart_details );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug Best Practice introduced by
The property cart_details does not exist on WPInv_Invoice. Since you implemented __get, consider adding a @property annotation.
Loading history...
Deprecated Code introduced by
The function wpinv_get_cart_subtotal() 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

285
    $subtotal               = /** @scrutinizer ignore-deprecated */ wpinv_get_cart_subtotal( $invoice->cart_details );
Loading history...
286
    $tax                    = wpinv_get_cart_tax( $invoice->cart_details );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_cart_tax() 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

286
    $tax                    = /** @scrutinizer ignore-deprecated */ wpinv_get_cart_tax( $invoice->cart_details );
Loading history...
Unused Code introduced by
The call to wpinv_get_cart_tax() has too many arguments starting with $invoice->cart_details. ( Ignorable by Annotation )

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

286
    $tax                    = /** @scrutinizer ignore-call */ wpinv_get_cart_tax( $invoice->cart_details );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
287
    $total                  = wpinv_get_cart_total( $invoice->cart_details );
0 ignored issues
show
Unused Code introduced by
The call to wpinv_get_cart_total() has too many arguments starting with $invoice->cart_details. ( Ignorable by Annotation )

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

287
    $total                  = /** @scrutinizer ignore-call */ wpinv_get_cart_total( $invoice->cart_details );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Deprecated Code introduced by
The function wpinv_get_cart_total() 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

287
    $total                  = /** @scrutinizer ignore-deprecated */ wpinv_get_cart_total( $invoice->cart_details );
Loading history...
288
289
    $invoice->tax           = $tax;
0 ignored issues
show
Bug Best Practice introduced by
The property tax does not exist on WPInv_Invoice. Since you implemented __set, consider adding a @property annotation.
Loading history...
290
    $invoice->subtotal      = $subtotal;
0 ignored issues
show
Bug Best Practice introduced by
The property subtotal does not exist on WPInv_Invoice. Since you implemented __set, consider adding a @property annotation.
Loading history...
291
    $invoice->total         = $total;
0 ignored issues
show
Bug Best Practice introduced by
The property total does not exist on WPInv_Invoice. Since you implemented __set, consider adding a @property annotation.
Loading history...
292
293
    $invoice->save();
294
    
295
    if ( $invoice->is_free_trial() ) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $invoice->is_free_trial() targeting WPInv_Invoice::is_free_trial() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Deprecated Code introduced by
The function WPInv_Invoice::is_free_trial() 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

295
    if ( /** @scrutinizer ignore-deprecated */ $invoice->is_free_trial() ) {
Loading history...
296
        $total = 0;
297
    }
298
    
299
    $response = array(
300
        'total'        => html_entity_decode( wpinv_price( wpinv_format_amount( $total ) ), ENT_COMPAT, 'UTF-8' ),
301
        'total_raw'    => $total,
302
        'free'         => !( (float)$total > 0 ) && $invoice->is_free() ? true : false,
303
        'html'         => wpinv_checkout_cart( $invoice->cart_details, false ),
304
    );
305
    
306
    if ( $return ) {
307
        return $response;
308
    }
309
310
    wp_send_json( $response );
311
}
312
add_action( 'wp_ajax_wpinv_recalculate_tax', 'wpinv_recalculate_tax' );
313
add_action( 'wp_ajax_nopriv_wpinv_recalculate_tax', 'wpinv_recalculate_tax' );
314
315
// VAT Settings
316
function wpinv_vat_rate_add_callback( $args ) {
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed. ( Ignorable by Annotation )

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

316
function wpinv_vat_rate_add_callback( /** @scrutinizer ignore-unused */ $args ) {

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

Loading history...
317
    ?>
318
    <p class="wpi-vat-rate-actions"><input id="wpi_vat_rate_add" type="button" value="<?php esc_attr_e( 'Add', 'invoicing' );?>" class="button button-primary" />&nbsp;&nbsp;<i style="display:none;" class="fa fa-refresh fa-spin"></i></p>
319
    <?php
320
}
321
322
function wpinv_vat_rate_delete_callback( $args ) {
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed. ( Ignorable by Annotation )

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

322
function wpinv_vat_rate_delete_callback( /** @scrutinizer ignore-unused */ $args ) {

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

Loading history...
323
    global $wpinv_euvat;
324
    
325
    $vat_classes = $wpinv_euvat->get_rate_classes();
326
    $vat_class = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '';
327
    if ( isset( $vat_classes[$vat_class] ) ) {
328
    ?>
329
    <p class="wpi-vat-rate-actions"><input id="wpi_vat_rate_delete" type="button" value="<?php echo wp_sprintf( esc_attr__( 'Delete class "%s"', 'invoicing' ), $vat_classes[$vat_class] );?>" class="button button-primary" />&nbsp;&nbsp;<i style="display:none;" class="fa fa-refresh fa-spin"></i></p>
330
    <?php
331
    }
332
}
333
334
function wpinv_vat_rates_callback( $args ) {
335
    global $wpinv_euvat;
336
    
337
    $vat_classes    = $wpinv_euvat->get_rate_classes();
338
    $vat_class      = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '_standard';
339
    
340
    $eu_states      = $wpinv_euvat->get_eu_states();
341
    $countries      = wpinv_get_country_list();
342
    $vat_groups     = $wpinv_euvat->get_vat_groups();
343
    $rates          = $wpinv_euvat->get_vat_rates( $vat_class );
344
    ob_start();
345
?>
346
</td><tr>
347
    <td colspan="2" class="wpinv_vat_tdbox">
348
    <input type="hidden" name="wpi_vat_class" value="<?php echo $vat_class;?>" />
349
    <p><?php echo ( isset( $args['desc'] ) ? $args['desc'] : '' ); ?></p>
350
    <table id="wpinv_vat_rates" class="wp-list-table widefat fixed posts">
351
        <colgroup>
352
            <col width="50px" />
353
            <col width="auto" />
354
            <col width="auto" />
355
            <col width="auto" />
356
            <col width="auto" />
357
            <col width="auto" />
358
        </colgroup>
359
        <thead>
360
            <tr>
361
                <th scope="col" colspan="2" class="wpinv_vat_country_name"><?php _e( 'Country', 'invoicing' ); ?></th>
362
                <th scope="col" class="wpinv_vat_global" title="<?php esc_attr_e( 'Apply rate to whole country', 'invoicing' ); ?>"><?php _e( 'Country Wide', 'invoicing' ); ?></th>
363
                <th scope="col" class="wpinv_vat_rate"><?php _e( 'Rate %', 'invoicing' ); ?></th> 
364
                <th scope="col" class="wpinv_vat_name"><?php _e( 'VAT Name', 'invoicing' ); ?></th>
365
                <th scope="col" class="wpinv_vat_group"><?php _e( 'Tax Group', 'invoicing' ); ?></th>
366
            </tr>
367
        </thead>
368
        <tbody>
369
        <?php if( !empty( $eu_states ) ) { ?>
370
        <?php 
371
        foreach ( $eu_states as $state ) { 
372
            $country_name = isset( $countries[$state] ) ? $countries[$state] : '';
373
            
374
            // Filter the rate for each country
375
            $country_rate = array_filter( $rates, function( $rate ) use( $state ) { return $rate['country'] === $state; } );
376
            
377
            // If one does not exist create a default
378
            $country_rate = is_array( $country_rate ) && count( $country_rate ) > 0 ? reset( $country_rate ) : array();
379
            
380
            $vat_global = isset( $country_rate['global'] ) ? !empty( $country_rate['global'] ) : true;
381
            $vat_rate = isset( $country_rate['rate'] ) ? $country_rate['rate'] : '';
382
            $vat_name = !empty( $country_rate['name'] ) ? esc_attr( stripslashes( $country_rate['name'] ) ) : '';
383
            $vat_group = !empty( $country_rate['group'] ) ? $country_rate['group'] : ( $vat_class === '_standard' ? 'standard' : 'reduced' );
384
        ?>
385
        <tr>
386
            <td class="wpinv_vat_country"><?php echo $state; ?><input type="hidden" name="vat_rates[<?php echo $state; ?>][country]" value="<?php echo $state; ?>" /><input type="hidden" name="vat_rates[<?php echo $state; ?>][state]" value="" /></td>
387
            <td class="wpinv_vat_country_name"><?php echo $country_name; ?></td>
388
            <td class="wpinv_vat_global">
389
                <input type="checkbox" name="vat_rates[<?php echo $state;?>][global]" id="vat_rates[<?php echo $state;?>][global]" value="1" <?php checked( true, $vat_global );?> disabled="disabled" />
390
                <label for="tax_rates[<?php echo $state;?>][global]"><?php _e( 'Apply to whole country', 'invoicing' ); ?></label>
391
                <input type="hidden" name="vat_rates[<?php echo $state;?>][global]" value="1" checked="checked" />
392
            </td>
393
            <td class="wpinv_vat_rate"><input type="number" class="small-text" step="any" min="0" max="99" name="vat_rates[<?php echo $state;?>][rate]" value="<?php echo $vat_rate; ?>" /></td>
394
            <td class="wpinv_vat_name"><input type="text" class="regular-text" name="vat_rates[<?php echo $state;?>][name]" value="<?php echo $vat_name; ?>" /></td>
395
            <td class="wpinv_vat_group">
396
            <?php
397
            echo wpinv_html_select( array(
398
                                        'name'             => 'vat_rates[' . $state . '][group]',
399
                                        'selected'         => $vat_group,
400
                                        'id'               => 'vat_rates[' . $state . '][group]',
401
                                        'class'            => 'wpi_select2',
402
                                        'options'          => $vat_groups,
403
                                        'multiple'         => false,
404
                                        'show_option_all'  => false,
405
                                        'show_option_none' => false
406
                                    ) );
407
            ?>
408
            </td>
409
        </tr>
410
        <?php } ?>
411
        <tr>
412
            <td colspan="6" style="background-color:#fafafa;">
413
                <span><input id="wpi_vat_get_rates_group" type="button" class="button-secondary" value="<?php esc_attr_e( 'Update EU VAT Rates', 'invoicing' ); ?>" />&nbsp;&nbsp;<i style="display:none" class="fa fa-refresh fa-spin"></i></span><span id="wpinv-rates-error-wrap" class="wpinv_errors" style="display:none;"></span>
414
            </td>
415
        </tr>
416
        <?php } ?>
417
        </tbody>
418
    </table>
419
    <?php
420
    $content = ob_get_clean();
421
    
422
    echo $content;
423
}
424
425
function wpinv_vat_number_callback( $args ) {
426
    global $wpinv_euvat;
427
    
428
    $vat_number     = $wpinv_euvat->get_vat_number();
429
    $vat_valid      = $wpinv_euvat->is_vat_validated();
430
431
    $size           = ( isset( $args['size'] ) && !is_null( $args['size'] ) ) ? $args['size'] : 'regular';
432
    $validated_text = $vat_valid ? __( 'VAT number validated', 'invoicing' ) : __( 'VAT number not validated', 'invoicing' );
433
    $disabled       = $vat_valid ? 'disabled="disabled"' : " ";
434
    
435
    $html = '<input type="text" class="' . $size . '-text" id="wpinv_settings[' . $args['id'] . ']" name="wpinv_settings[' . $args['id'] . ']" placeholder="GB123456789" value="' . esc_attr( stripslashes( $vat_number ) ) . '"/>';
436
    $html .= '<span>&nbsp;<input type="button" id="wpinv_vat_validate" class="wpinv_validate_vat_button button-secondary" ' . $disabled . ' value="' . esc_attr__( 'Validate VAT Number', 'invoicing' ) . '" /></span>';
437
    $html .= '<span class="wpinv-vat-stat wpinv-vat-stat-' . (int)$vat_valid . '"><i class="fa"></i> <font>' . $validated_text . '</font></span>';
438
    $html .= '<label for="wpinv_settings[' . $args['id'] . ']">' . '<p>' . __( 'Enter your VAT number including country identifier, eg: GB123456789 (Settings must be saved after validation)', 'invoicing' ).'</p>' . '</label>';
439
    $html .= '<input type="hidden" name="_wpi_nonce" value="' . wp_create_nonce( 'vat_validation' ) . '">';
440
441
    echo $html;
442
}
443
444
function wpinv_eu_fallback_rate_callback( $args ) {
445
    global $wpinv_options;
446
447
    $value = isset( $wpinv_options[$args['id']] ) ? $wpinv_options[ $args['id'] ] : ( isset( $args['std'] ) ? $args['std'] : '' );
448
    $size = ( isset( $args['size'] ) && !is_null( $args['size'] ) ) ? $args['size'] : 'small';
449
    
450
    $html = '<input type="number" min="0" max="99" step="any" class="' . $size . '-text" id="wpinv_settings_' . $args['section'] . '_' . $args['id'] . '" name="wpinv_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $value ) ) . '" />';
451
    $html .= '<span>&nbsp;<input id="wpi_add_eu_states" type="button" class="button-secondary" value="' . esc_attr__( 'Add EU Member States', 'invoicing' ) . '" /></span>';
452
    $html .= '<span>&nbsp;<input id="wpi_remove_eu_states" type="button" class="button-secondary" value="' . esc_attr__( 'Remove EU Member States', 'invoicing' ) . '" /></span>';
453
    $html .= '<span>&nbsp;<input id="wpi_vat_get_rates" type="button" class="button-secondary" value="' . esc_attr__( 'Update EU VAT Rates', 'invoicing' ) . '" />&nbsp;&nbsp;<i style="display:none" class="fa fa-refresh fa-spin"></i></span>';
454
    $html .= '<p><label for="wpinv_settings_' . $args['section'] . '_' . $args['id'] . '">' . $args['desc'] . '</label></p>';
455
    echo $html;
456
    ?>
457
    <span id="wpinv-rates-error-wrap" class="wpinv_errors" style="display:none;"></span>
458
    <?php
459
}
460
461
function wpinv_vat_ip_lookup_callback( $args ) {
462
    global $wpinv_options, $wpinv_euvat;
463
464
    $value =  isset( $wpinv_options[ $args['id'] ] ) ? $wpinv_options[ $args['id'] ]  : ( isset( $args['std'] ) ? $args['std'] : 'default' );
465
    
466
    $options = array();
467
    if ( function_exists( 'geoip_country_code_by_name' ) ) {
468
        $options['geoip'] = __( 'PHP GeoIP extension', 'invoicing' );
469
    }
470
    
471
    $geoip2_database = $wpinv_euvat->geoip2_country_dbfile();
472
    
473
    if ( !function_exists( 'bcadd' ) ) {
474
        $geoip2_message = __( 'GeoIP2 service requires the BC Math PHP extension, it is not loaded in your version of PHP!', 'invoicing' );
475
    } else {
476
        $geoip2_message = ini_get('safe_mode') ? __( 'GeoIP2 is not supported with PHP safe mode enabled!', 'invoicing' ) : '';
477
    }
478
    
479
    if ( $geoip2_database !== false && empty( $geoip2_message ) ) {
480
        $options['geoip2'] = __( 'GeoIP2 Database', 'invoicing' );
481
    }
482
    
483
    if ( function_exists( 'simplexml_load_file' ) ) {
484
        $options['geoplugin'] = __( 'geoPlugin Web Service', 'invoicing' );
485
    }
486
    
487
    $options['site']    = __( 'Use default country', 'invoicing' );
488
    $options['default'] = __( 'Auto', 'invoicing' );
489
490
    $html = wpinv_html_select( array(
491
        'name'             => "wpinv_settings[{$args['id']}]",
492
        'selected'         => $value,
493
        'id'               => "wpinv_settings[{$args['id']}]",
494
        'class'            => isset($args['class']) ? $args['class'] : "",
495
        'options'          => $options,
496
        'multiple'         => false,
497
        'show_option_all'  => false,
498
        'show_option_none' => false
499
    ));
500
    
501
    $desc = '<label for="wpinv_settings[' . $args['id'] . ']">';
502
    $desc .= __( 'Select the option Invoicing should use to determine the country from the IP address of the user.', 'invoicing' );
503
    $desc .= '<p>';
504
    if ( empty( $geoip2_message ) ) {
505
        if ( $geoip2_database ) {
506
            $last_updated = '';
507
            if ( $time_updated = wpinv_get_option( 'wpinv_geoip2_date_updated' ) ) {
508
                $date_updated = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $time_updated );
0 ignored issues
show
Bug introduced by
Are you sure get_option('date_format') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

508
                $date_updated = date_i18n( /** @scrutinizer ignore-type */ get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $time_updated );
Loading history...
Bug introduced by
Are you sure get_option('time_format') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

508
                $date_updated = date_i18n( get_option( 'date_format' ) . ' ' . /** @scrutinizer ignore-type */ get_option( 'time_format' ), $time_updated );
Loading history...
509
                $last_updated = '<br>' . sprintf( __( 'The GeoIP2 database was last updated on: <b>%s</b>', 'invoicing' ), $date_updated );
510
            }
511
            $desc .= __(  'GeoIP2 database exists:', 'invoicing' ) . $last_updated . '&nbsp;<input type="button" id="wpi_geoip2" action="update" class="wpinv-refresh-geoip2-btn button-secondary" value="' . __( 'Update GeoIP2 database now (~30MB)', 'invoicing' ) . '"></input>';
512
        } else {
513
            $desc .= __( 'GeoIP2 database does not exist:', 'invoicing' ) . '&nbsp;<input type="button" id="wpi_geoip2" action="download" class="wpinv-download-geoip2-btn button-secondary" value="' . __( 'Download GeoIP2 database now', 'invoicing' ) . ' (~30MB)"></input><br>' . __(  'After downloading the GeoIP2 database the GeoIP2 lookup option will show.', 'invoicing' );
514
        }
515
    } else {
516
        $desc .= $geoip2_message;
517
    }
518
    $desc .= '</p><p>'. __( 'geoPlugin is a great free service please consider supporting them: ', 'invoicing' ) . ' <a href="http://www.geoplugin.com/" target="_blank">GeoPlugin.com</a></p>';
519
    $desc .= '</label>';
520
    
521
    $html .= $desc;
522
523
    echo $html;
524
    ?>
525
    <span id="wpinv-geoip2-errors" class="wpinv_errors" style="display:none;padding:4px;"></span>
526
    <?php
527
}
528
529
/**
530
 * Filters the VAT rules to ensure that each item has a VAT rule.
531
 * 
532
 * @param string|bool|null $vat_rule
533
 */
534
function getpaid_filter_vat_rule( $vat_rule ) {
535
    global $wpinv_euvat;
536
537
    if ( empty( $vat_rule ) ) {        
538
        return $wpinv_euvat->allow_vat_rules() ? 'digital' : 'physical';
539
    }
540
541
    return $vat_rule;
542
}
543
add_filter( 'wpinv_get_item_vat_rule', 'getpaid_filter_vat_rule' );
544
545
/**
546
 * Filters the VAT class to ensure that each item has a VAT class.
547
 * 
548
 * @param string|bool|null $vat_rule
549
 */
550
function getpaid_filter_vat_class( $vat_class ) {
551
    return empty( $vat_class ) ? '_standard' : $vat_class;
552
}
553
add_filter( 'wpinv_get_item_vat_class', 'getpaid_filter_vat_class' );
554
555
/**
556
 * Returns the ip address location url.
557
 * 
558
 */
559
function getpaid_ip_location_url( $ip_address ) {
560
561
    return add_query_arg(
562
        array(
563
            'action'   => 'wpinv_ip_geolocation',
564
            'ip'       => $ip_address,
565
            '_wpnonce' => wp_create_nonce( 'getpaid-ip-location' )
566
        ),
567
        admin_url( 'admin-ajax.php' )
568
    );
569
570
}
571
572
/**
573
 * GeoLocates an ip address.
574
 * 
575
 * @return array|bool
576
 */
577
function getpaid_geolocate_ip_address( $ip_address ) {
578
579
    // Do we have an ip address?
580
    if ( empty( $ip_address ) ) {
581
        return false;
582
    }
583
584
    /**
585
     * Retrieve ip address using max mind.
586
     */
587
    if ( wpinv_get_option( 'vat_ip_lookup' ) == 'geoip2' && $geoip2_city = getpaid_tax()->geoip2_city_record( $ip_address ) ) {
588
589
        try {
590
            $iso        = $geoip2_city->country->isoCode;
591
            $country    = $geoip2_city->country->name;
592
            $region     = ! empty( $geoip2_city->subdivisions ) && ! empty( $geoip2_city->subdivisions[0]->name ) ? $geoip2_city->subdivisions[0]->name : '';
593
            $city       = $geoip2_city->city->name;
594
            $longitude  = $geoip2_city->location->longitude;
595
            $latitude   = $geoip2_city->location->latitude;
596
            $credit     = __( 'Geolocated using the information by MaxMind, available from <a href="http://www.maxmind.com" target="_blank">www.maxmind.com</a>', 'invoicing' );
597
        } catch( Exception $e ) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
598
599
    }
600
601
    // If that fails, GeoLocate using GeoPlugin.
602
    if ( ( empty( $iso ) || empty( $longitude ) || empty( $latitude ) ) && function_exists( 'simplexml_load_file' ) ) {
603
604
        try {
605
            $load_xml = simplexml_load_file( 'http://www.geoplugin.net/xml.gp?ip=' . $ip_address );
0 ignored issues
show
Security File Exposure introduced by
'http://www.geoplugin.ne...l.gp?ip=' . $ip_address can contain request data and is used in file inclusion context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_GET, and getpaid_geolocate_ip_address() is called
    in includes/class-wpinv-ajax.php on line 891
  2. Enters via parameter $ip_address
    in includes/wpinv-tax-functions.php on line 577

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
606
            
607
            if ( ! empty( $load_xml ) && isset( $load_xml->geoplugin_countryCode ) && ! empty( $load_xml->geoplugin_latitude ) && ! empty( $load_xml->geoplugin_longitude ) ) {
608
                $iso        = $load_xml->geoplugin_countryCode;
609
                $country    = $load_xml->geoplugin_countryName;
610
                $region     = ! empty( $load_xml->geoplugin_regionName ) ? $load_xml->geoplugin_regionName : '';
611
                $city       = ! empty( $load_xml->geoplugin_city ) ? $load_xml->geoplugin_city : '';
612
                $longitude  = $load_xml->geoplugin_longitude;
613
                $latitude   = $load_xml->geoplugin_latitude;
614
                $credit     = $load_xml->geoplugin_credit;
0 ignored issues
show
Unused Code introduced by
The assignment to $credit is dead and can be removed.
Loading history...
615
                $credit     = __( 'Geolocated using the information by geoPlugin, available from <a href="http://www.geoplugin.com" target="_blank">www.geoplugin.com</a>', 'invoicing' ) . '<br>' . $load_xml->geoplugin_credit;
616
            }
617
        } catch( Exception $e ) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
618
619
    }
620
621
   if ( empty( $iso )  ) {
622
       return false;
623
   }
624
625
   return compact( 'iso', 'country', 'region', 'city', 'longitude', 'latitude', 'credit' );
626
627
}
628