Passed
Pull Request — master (#398)
by Brian
05:25
created

wpinv_recalculated_tax()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
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 getpaid()->tax;
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
// VAT Settings
256
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

256
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...
257
    ?>
258
    <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>
259
    <?php
260
}
261
262
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

262
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...
263
    global $wpinv_euvat;
264
    
265
    $vat_classes = $wpinv_euvat->get_rate_classes();
266
    $vat_class = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '';
267
    if ( isset( $vat_classes[$vat_class] ) ) {
268
    ?>
269
    <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>
270
    <?php
271
    }
272
}
273
274
function wpinv_vat_rates_callback( $args ) {
275
    global $wpinv_euvat;
276
    
277
    $vat_classes    = $wpinv_euvat->get_rate_classes();
278
    $vat_class      = isset( $_REQUEST['wpi_sub'] ) && $_REQUEST['wpi_sub'] !== '' && isset( $vat_classes[$_REQUEST['wpi_sub']] )? sanitize_text_field( $_REQUEST['wpi_sub'] ) : '_standard';
279
    
280
    $eu_states      = $wpinv_euvat->get_eu_states();
281
    $countries      = wpinv_get_country_list();
282
    $vat_groups     = $wpinv_euvat->get_vat_groups();
283
    $rates          = $wpinv_euvat->get_vat_rates( $vat_class );
284
    ob_start();
285
?>
286
</td><tr>
287
    <td colspan="2" class="wpinv_vat_tdbox">
288
    <input type="hidden" name="wpi_vat_class" value="<?php echo $vat_class;?>" />
289
    <p><?php echo ( isset( $args['desc'] ) ? $args['desc'] : '' ); ?></p>
290
    <table id="wpinv_vat_rates" class="wp-list-table widefat fixed posts">
291
        <colgroup>
292
            <col width="50px" />
293
            <col width="auto" />
294
            <col width="auto" />
295
            <col width="auto" />
296
            <col width="auto" />
297
            <col width="auto" />
298
        </colgroup>
299
        <thead>
300
            <tr>
301
                <th scope="col" colspan="2" class="wpinv_vat_country_name"><?php _e( 'Country', 'invoicing' ); ?></th>
302
                <th scope="col" class="wpinv_vat_global" title="<?php esc_attr_e( 'Apply rate to whole country', 'invoicing' ); ?>"><?php _e( 'Country Wide', 'invoicing' ); ?></th>
303
                <th scope="col" class="wpinv_vat_rate"><?php _e( 'Rate %', 'invoicing' ); ?></th> 
304
                <th scope="col" class="wpinv_vat_name"><?php _e( 'VAT Name', 'invoicing' ); ?></th>
305
                <th scope="col" class="wpinv_vat_group"><?php _e( 'Tax Group', 'invoicing' ); ?></th>
306
            </tr>
307
        </thead>
308
        <tbody>
309
        <?php if( !empty( $eu_states ) ) { ?>
310
        <?php 
311
        foreach ( $eu_states as $state ) { 
312
            $country_name = isset( $countries[$state] ) ? $countries[$state] : '';
313
            
314
            // Filter the rate for each country
315
            $country_rate = array_filter( $rates, function( $rate ) use( $state ) { return $rate['country'] === $state; } );
316
            
317
            // If one does not exist create a default
318
            $country_rate = is_array( $country_rate ) && count( $country_rate ) > 0 ? reset( $country_rate ) : array();
319
            
320
            $vat_global = isset( $country_rate['global'] ) ? !empty( $country_rate['global'] ) : true;
321
            $vat_rate = isset( $country_rate['rate'] ) ? $country_rate['rate'] : '';
322
            $vat_name = !empty( $country_rate['name'] ) ? esc_attr( stripslashes( $country_rate['name'] ) ) : '';
323
            $vat_group = !empty( $country_rate['group'] ) ? $country_rate['group'] : ( $vat_class === '_standard' ? 'standard' : 'reduced' );
324
        ?>
325
        <tr>
326
            <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>
327
            <td class="wpinv_vat_country_name"><?php echo $country_name; ?></td>
328
            <td class="wpinv_vat_global">
329
                <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" />
330
                <label for="tax_rates[<?php echo $state;?>][global]"><?php _e( 'Apply to whole country', 'invoicing' ); ?></label>
331
                <input type="hidden" name="vat_rates[<?php echo $state;?>][global]" value="1" checked="checked" />
332
            </td>
333
            <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>
334
            <td class="wpinv_vat_name"><input type="text" class="regular-text" name="vat_rates[<?php echo $state;?>][name]" value="<?php echo $vat_name; ?>" /></td>
335
            <td class="wpinv_vat_group">
336
            <?php
337
            echo wpinv_html_select( array(
338
                                        'name'             => 'vat_rates[' . $state . '][group]',
339
                                        'selected'         => $vat_group,
340
                                        'id'               => 'vat_rates[' . $state . '][group]',
341
                                        'class'            => 'wpi_select2',
342
                                        'options'          => $vat_groups,
343
                                        'multiple'         => false,
344
                                        'show_option_all'  => false,
345
                                        'show_option_none' => false
346
                                    ) );
347
            ?>
348
            </td>
349
        </tr>
350
        <?php } ?>
351
        <tr>
352
            <td colspan="6" style="background-color:#fafafa;">
353
                <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>
354
            </td>
355
        </tr>
356
        <?php } ?>
357
        </tbody>
358
    </table>
359
    <?php
360
    $content = ob_get_clean();
361
    
362
    echo $content;
363
}
364
365
function wpinv_vat_number_callback( $args ) {
366
    global $wpinv_euvat;
367
    
368
    $vat_number     = $wpinv_euvat->get_vat_number();
369
    $vat_valid      = $wpinv_euvat->is_vat_validated();
370
371
    $size           = ( isset( $args['size'] ) && !is_null( $args['size'] ) ) ? $args['size'] : 'regular';
372
    $validated_text = $vat_valid ? __( 'VAT number validated', 'invoicing' ) : __( 'VAT number not validated', 'invoicing' );
373
    $disabled       = $vat_valid ? 'disabled="disabled"' : " ";
374
    
375
    $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 ) ) . '"/>';
376
    $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>';
377
    $html .= '<span class="wpinv-vat-stat wpinv-vat-stat-' . (int)$vat_valid . '"><i class="fa"></i> <font>' . $validated_text . '</font></span>';
378
    $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>';
379
    $html .= '<input type="hidden" name="_wpi_nonce" value="' . wp_create_nonce( 'vat_validation' ) . '">';
380
381
    echo $html;
382
}
383
384
function wpinv_eu_fallback_rate_callback( $args ) {
385
    global $wpinv_options;
386
387
    $value = isset( $wpinv_options[$args['id']] ) ? $wpinv_options[ $args['id'] ] : ( isset( $args['std'] ) ? $args['std'] : '' );
388
    $size = ( isset( $args['size'] ) && !is_null( $args['size'] ) ) ? $args['size'] : 'small';
389
    
390
    $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 ) ) . '" />';
391
    $html .= '<span>&nbsp;<input id="wpi_add_eu_states" type="button" class="button-secondary" value="' . esc_attr__( 'Add EU Member States', 'invoicing' ) . '" /></span>';
392
    $html .= '<span>&nbsp;<input id="wpi_remove_eu_states" type="button" class="button-secondary" value="' . esc_attr__( 'Remove EU Member States', 'invoicing' ) . '" /></span>';
393
    $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>';
394
    $html .= '<p><label for="wpinv_settings_' . $args['section'] . '_' . $args['id'] . '">' . $args['desc'] . '</label></p>';
395
    echo $html;
396
    ?>
397
    <span id="wpinv-rates-error-wrap" class="wpinv_errors" style="display:none;"></span>
398
    <?php
399
}
400
401
function wpinv_vat_ip_lookup_callback( $args ) {
402
    global $wpinv_options, $wpinv_euvat;
403
404
    $value =  isset( $wpinv_options[ $args['id'] ] ) ? $wpinv_options[ $args['id'] ]  : ( isset( $args['std'] ) ? $args['std'] : 'default' );
405
    
406
    $options = array();
407
    if ( function_exists( 'geoip_country_code_by_name' ) ) {
408
        $options['geoip'] = __( 'PHP GeoIP extension', 'invoicing' );
409
    }
410
    
411
    $geoip2_database = $wpinv_euvat->geoip2_country_dbfile();
412
    
413
    if ( !function_exists( 'bcadd' ) ) {
414
        $geoip2_message = __( 'GeoIP2 service requires the BC Math PHP extension, it is not loaded in your version of PHP!', 'invoicing' );
415
    } else {
416
        $geoip2_message = ini_get('safe_mode') ? __( 'GeoIP2 is not supported with PHP safe mode enabled!', 'invoicing' ) : '';
417
    }
418
    
419
    if ( $geoip2_database !== false && empty( $geoip2_message ) ) {
420
        $options['geoip2'] = __( 'GeoIP2 Database', 'invoicing' );
421
    }
422
    
423
    if ( function_exists( 'simplexml_load_file' ) ) {
424
        $options['geoplugin'] = __( 'geoPlugin Web Service', 'invoicing' );
425
    }
426
    
427
    $options['site']    = __( 'Use default country', 'invoicing' );
428
    $options['default'] = __( 'Auto', 'invoicing' );
429
430
    $html = wpinv_html_select( array(
431
        'name'             => "wpinv_settings[{$args['id']}]",
432
        'selected'         => $value,
433
        'id'               => "wpinv_settings[{$args['id']}]",
434
        'class'            => isset($args['class']) ? $args['class'] : "",
435
        'options'          => $options,
436
        'multiple'         => false,
437
        'show_option_all'  => false,
438
        'show_option_none' => false
439
    ));
440
    
441
    $desc = '<label for="wpinv_settings[' . $args['id'] . ']">';
442
    $desc .= __( 'Select the option Invoicing should use to determine the country from the IP address of the user.', 'invoicing' );
443
    $desc .= '<p>';
444
    if ( empty( $geoip2_message ) ) {
445
        if ( $geoip2_database ) {
446
            $last_updated = '';
447
            if ( $time_updated = wpinv_get_option( 'wpinv_geoip2_date_updated' ) ) {
448
                $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('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

448
                $date_updated = date_i18n( get_option( 'date_format' ) . ' ' . /** @scrutinizer ignore-type */ get_option( 'time_format' ), $time_updated );
Loading history...
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

448
                $date_updated = date_i18n( /** @scrutinizer ignore-type */ get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $time_updated );
Loading history...
449
                $last_updated = '<br>' . sprintf( __( 'The GeoIP2 database was last updated on: <b>%s</b>', 'invoicing' ), $date_updated );
450
            }
451
            $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>';
452
        } else {
453
            $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' );
454
        }
455
    } else {
456
        $desc .= $geoip2_message;
457
    }
458
    $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>';
459
    $desc .= '</label>';
460
    
461
    $html .= $desc;
462
463
    echo $html;
464
    ?>
465
    <span id="wpinv-geoip2-errors" class="wpinv_errors" style="display:none;padding:4px;"></span>
466
    <?php
467
}
468
469
/**
470
 * Filters the VAT rules to ensure that each item has a VAT rule.
471
 * 
472
 * @param string|bool|null $vat_rule
473
 */
474
function getpaid_filter_vat_rule( $vat_rule ) {
475
476
    if ( empty( $vat_rule ) ) {        
477
        return getpaid_tax()->allow_vat_rules() ? 'digital' : 'physical';
478
    }
479
480
    return $vat_rule;
481
}
482
add_filter( 'wpinv_get_item_vat_rule', 'getpaid_filter_vat_rule' );
483
484
/**
485
 * Filters the VAT class to ensure that each item has a VAT class.
486
 * 
487
 * @param string|bool|null $vat_rule
488
 */
489
function getpaid_filter_vat_class( $vat_class ) {
490
    return empty( $vat_class ) ? '_standard' : $vat_class;
491
}
492
add_filter( 'wpinv_get_item_vat_class', 'getpaid_filter_vat_class' );
493
494
/**
495
 * Returns the ip address location url.
496
 * 
497
 */
498
function getpaid_ip_location_url( $ip_address ) {
499
500
    return add_query_arg(
501
        array(
502
            'action'   => 'wpinv_ip_geolocation',
503
            'ip'       => $ip_address,
504
            '_wpnonce' => wp_create_nonce( 'getpaid-ip-location' )
505
        ),
506
        admin_url( 'admin-ajax.php' )
507
    );
508
509
}
510
511
/**
512
 * GeoLocates an ip address.
513
 * 
514
 * @return array|bool
515
 */
516
function getpaid_geolocate_ip_address( $ip_address ) {
517
518
    // Do we have an ip address?
519
    if ( empty( $ip_address ) ) {
520
        return false;
521
    }
522
523
    /**
524
     * Retrieve ip address using max mind.
525
     */
526
    if ( wpinv_get_option( 'vat_ip_lookup' ) == 'geoip2' && $geoip2_city = getpaid_tax()->geoip2_city_record( $ip_address ) ) {
527
528
        try {
529
            $iso        = $geoip2_city->country->isoCode;
530
            $country    = $geoip2_city->country->name;
531
            $region     = ! empty( $geoip2_city->subdivisions ) && ! empty( $geoip2_city->subdivisions[0]->name ) ? $geoip2_city->subdivisions[0]->name : '';
532
            $city       = $geoip2_city->city->name;
533
            $longitude  = $geoip2_city->location->longitude;
534
            $latitude   = $geoip2_city->location->latitude;
535
            $credit     = __( 'Geolocated using the information by MaxMind, available from <a href="http://www.maxmind.com" target="_blank">www.maxmind.com</a>', 'invoicing' );
536
        } catch( Exception $e ) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
537
538
    }
539
540
    // If that fails, GeoLocate using GeoPlugin.
541
    if ( ( empty( $iso ) || empty( $longitude ) || empty( $latitude ) ) && function_exists( 'simplexml_load_file' ) ) {
542
543
        try {
544
            $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 952
  2. Enters via parameter $ip_address
    in includes/wpinv-tax-functions.php on line 516

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...
545
            
546
            if ( ! empty( $load_xml ) && isset( $load_xml->geoplugin_countryCode ) && ! empty( $load_xml->geoplugin_latitude ) && ! empty( $load_xml->geoplugin_longitude ) ) {
547
                $iso        = $load_xml->geoplugin_countryCode;
548
                $country    = $load_xml->geoplugin_countryName;
549
                $region     = ! empty( $load_xml->geoplugin_regionName ) ? $load_xml->geoplugin_regionName : '';
550
                $city       = ! empty( $load_xml->geoplugin_city ) ? $load_xml->geoplugin_city : '';
551
                $longitude  = $load_xml->geoplugin_longitude;
552
                $latitude   = $load_xml->geoplugin_latitude;
553
                $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...
554
                $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;
555
            }
556
        } catch( Exception $e ) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
557
558
    }
559
560
   if ( empty( $iso )  ) {
561
       return false;
562
   }
563
564
   return compact( 'iso', 'country', 'region', 'city', 'longitude', 'latitude', 'credit' );
565
566
}
567