Passed
Push — master ( 00f338...fab582 )
by Brian
05:14
created

getpaid_user_delete_invoice()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 5
eloc 13
c 1
b 1
f 0
nc 4
nop 1
dl 0
loc 28
rs 9.5222
1
<?php
2
/**
3
 * Contains helper functions.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
 
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Are we supporting item quantities?
13
 */
14
function wpinv_item_quantities_enabled() {
15
    return true;
16
}
17
18
/**
19
 * Returns the user's ip address.
20
 */
21
function wpinv_get_ip() {
22
23
    if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) {
24
        return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) );
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($_SERVER['HTTP_X_REAL_IP']) can also be of type array; 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

24
        return sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) );
Loading history...
25
    }
26
27
    if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
28
        // Proxy servers can send through this header like this: X-Forwarded-For: client1, proxy1, proxy2
29
        // Make sure we always only send through the first IP in the list which should always be the client IP.
30
        return (string) rest_is_ip_address( trim( current( preg_split( '/,/', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) );
31
    }
32
33
    if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
34
        return sanitize_text_field( wp_unslash( $_SERVER['HTTP_CLIENT_IP'] ) );
35
    }
36
37
    if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
38
        return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
39
    }
40
41
    return '';
42
}
43
44
function wpinv_get_user_agent() {
45
    if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
46
        $user_agent = sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] );
47
    } else {
48
        $user_agent = '';
49
    }
50
51
    return apply_filters( 'wpinv_get_user_agent', $user_agent );
52
}
53
54
/**
55
 * Standardizes an amount for insterting into the database.
56
 *
57
 * @param string $amount The amount to sanitize.
58
 * @return float
59
 */
60
function getpaid_standardize_amount( $amount ) {
61
62
    $amount = str_replace( wpinv_thousands_separator(), '', $amount );
63
    $amount = str_replace( wpinv_decimal_separator(), '.', $amount );
64
    if ( is_numeric( $amount ) ) {
65
        return floatval( $amount );
66
    }
67
68
    // Cast the remaining to a float.
69
    return wpinv_round_amount( preg_replace( '/[^0-9\.\-]/', '', $amount ) );
0 ignored issues
show
Bug introduced by
preg_replace('/[^0-9\.\-]/', '', $amount) of type string is incompatible with the type double expected by parameter $amount of wpinv_round_amount(). ( Ignorable by Annotation )

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

69
    return wpinv_round_amount( /** @scrutinizer ignore-type */ preg_replace( '/[^0-9\.\-]/', '', $amount ) );
Loading history...
70
71
}
72
73
/**
74
 * Standardizes an amount that has been retrieved from the database.
75
 *
76
 * @param string $amount The amount to sanitize.
77
 */
78
function getpaid_unstandardize_amount( $amount ) {
79
    return str_replace( '.', wpinv_decimal_separator(), $amount );
80
}
81
82
/**
83
 * Sanitizes an amount.
84
 * 
85
 * @param string $amount The amount to sanitize.
86
 */
87
function wpinv_sanitize_amount( $amount ) {
88
89
    if ( is_numeric( $amount ) ) {
90
        return floatval( $amount );
91
    }
92
93
    // Separate the decimals and thousands.
94
    $amount    = explode( wpinv_decimal_separator(), $amount );
95
96
    // Remove thousands.
97
    $amount[0] = str_replace( wpinv_thousands_separator(), '', $amount[0] );
98
99
    // Convert back to string.
100
    $amount = count( $amount ) > 1 ? "{$amount[0]}.{$amount[1]}" : $amount[0];
101
102
    // Cast the remaining to a float.
103
    return (float) preg_replace( '/[^0-9\.\-]/', '', $amount );
104
105
}
106
107
/**
108
 * Rounds an amount.
109
 * 
110
 * @param float $amount
111
 * @param float|string|int|null $decimals
112
 */
113
function wpinv_round_amount( $amount, $decimals = null, $use_sprintf = false ) {
114
115
    if ( $decimals === null ) {
116
        $decimals = wpinv_decimals();
117
    }
118
119
    if ( $use_sprintf ) {
120
        $amount = sprintf( "%.{$decimals}f", (float) $amount );
121
    } else {
122
        $amount = round( (float) $amount, absint( $decimals ) );
123
    }
124
125
    return apply_filters( 'wpinv_round_amount', $amount, $decimals );
126
}
127
128
/**
129
 * Get all invoice statuses.
130
 *
131
 * @since 1.0.19
132
 * @param bool $draft Whether or not to include the draft status.
133
 * @param bool $trashed Whether or not to include the trash status.
134
 * @param string|WPInv_Invoice $invoice The invoice object|post type|type
135
 * @return array
136
 */
137
function wpinv_get_invoice_statuses( $draft = false, $trashed = false, $invoice = false ) {
138
139
	$invoice_statuses = array(
140
		'wpi-pending'    => _x( 'Pending payment', 'Invoice status', 'invoicing' ),
141
        'publish'        => _x( 'Paid', 'Invoice status', 'invoicing' ),
142
        'wpi-processing' => _x( 'Processing', 'Invoice status', 'invoicing' ),
143
		'wpi-onhold'     => _x( 'On hold', 'Invoice status', 'invoicing' ),
144
		'wpi-cancelled'  => _x( 'Cancelled', 'Invoice status', 'invoicing' ),
145
		'wpi-refunded'   => _x( 'Refunded', 'Invoice status', 'invoicing' ),
146
        'wpi-failed'     => _x( 'Failed', 'Invoice status', 'invoicing' ),
147
        'wpi-renewal'    => _x( 'Renewal Payment', 'Invoice status', 'invoicing' ),
148
    );
149
150
    if ( $draft ) {
151
        $invoice_statuses['draft'] = __( 'Draft', 'invoicing' );
152
    }
153
154
    if ( $trashed ) {
155
        $invoice_statuses['trash'] = __( 'Trash', 'invoicing' );
156
    }
157
158
    if ( $invoice instanceof WPInv_Invoice ) {
159
        $invoice = $invoice->get_post_type();
160
    }
161
162
	return apply_filters( 'wpinv_statuses', $invoice_statuses, $invoice );
163
}
164
165
/**
166
 * Returns the formated invoice status.
167
 * 
168
 * @param string $status The raw status
169
 * @param string|WPInv_Invoice $invoice The invoice object|post type|type
170
 */
171
function wpinv_status_nicename( $status, $invoice = false ) {
172
    $statuses = wpinv_get_invoice_statuses( true, true, $invoice );
0 ignored issues
show
Bug introduced by
It seems like $invoice can also be of type false; however, parameter $invoice of wpinv_get_invoice_statuses() does only seem to accept WPInv_Invoice|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

172
    $statuses = wpinv_get_invoice_statuses( true, true, /** @scrutinizer ignore-type */ $invoice );
Loading history...
173
    $status   = isset( $statuses[$status] ) ? $statuses[$status] : $status;
174
175
    return sanitize_text_field( $status );
176
}
177
178
/**
179
 * Retrieves the default currency code.
180
 * 
181
 * @param string $current
182
 */
183
function wpinv_get_currency( $current = '' ) {
184
185
    if ( empty( $current ) ) {
186
        $current = apply_filters( 'wpinv_currency', wpinv_get_option( 'currency', 'USD' ) );
187
    }
188
189
    return trim( strtoupper( $current ) );
190
}
191
192
/**
193
 * Given a currency, it returns a currency symbol.
194
 * 
195
 * @param string|null $currency The currency code. Defaults to the default currency.
196
 */
197
function wpinv_currency_symbol( $currency = null ) {
198
199
    // Prepare the currency.
200
    $currency = empty( $currency ) ? wpinv_get_currency() : wpinv_clean( $currency );
201
202
    // Fetch all symbols.
203
    $symbols = wpinv_get_currency_symbols();
204
205
    // Fetch this currencies symbol.
206
    $currency_symbol = isset( $symbols[$currency] ) ? $symbols[$currency] : $currency;
207
208
    // Filter the symbol.
209
    return apply_filters( 'wpinv_currency_symbol', $currency_symbol, $currency );
210
}
211
212
function wpinv_currency_position() {
213
    $position = wpinv_get_option( 'currency_position', 'left' );
214
    
215
    return apply_filters( 'wpinv_currency_position', $position );
216
}
217
218
/**
219
 * Returns the thousands separator for a currency.
220
 * 
221
 * @param $string|null $current
0 ignored issues
show
Documentation Bug introduced by
The doc comment $string|null at position 0 could not be parsed: Unknown type name '$string' at position 0 in $string|null.
Loading history...
222
 */
223
function wpinv_thousands_separator( $current = null ) {
224
225
    if ( null == $current ) {
226
        $current = wpinv_get_option( 'thousands_separator', ',' );
227
    }
228
229
    return trim( $current );
230
}
231
232
/**
233
 * Returns the decimal separator for a currency.
234
 * 
235
 * @param $string|null $current
0 ignored issues
show
Documentation Bug introduced by
The doc comment $string|null at position 0 could not be parsed: Unknown type name '$string' at position 0 in $string|null.
Loading history...
236
 */
237
function wpinv_decimal_separator( $current = null ) {
238
239
    if ( null == $current ) {
240
        $current = wpinv_get_option( 'decimal_separator', '.' );
241
    }
242
    
243
    return trim( $current );
244
}
245
246
/**
247
 * Returns the number of decimals to use.
248
 * 
249
 * @param $string|null $current
0 ignored issues
show
Documentation Bug introduced by
The doc comment $string|null at position 0 could not be parsed: Unknown type name '$string' at position 0 in $string|null.
Loading history...
250
 */
251
function wpinv_decimals( $current = null ) {
252
253
    if ( null == $current ) {
254
        $current = wpinv_get_option( 'decimals', 2 );
255
    }
256
    
257
    return absint( $current );
258
}
259
260
/**
261
 * Retrieves a list of all supported currencies.
262
 */
263
function wpinv_get_currencies() {
264
    return apply_filters( 'wpinv_currencies', wpinv_get_data( 'currencies' ) );
265
}
266
267
/**
268
 * Retrieves a list of all currency symbols.
269
 */
270
function wpinv_get_currency_symbols() {
271
    return apply_filters( 'wpinv_currency_symbols', wpinv_get_data( 'currency-symbols' ) );
272
}
273
274
/**
275
 * Get the price format depending on the currency position.
276
 *
277
 * @return string
278
 */
279
function getpaid_get_price_format() {
280
	$currency_pos = wpinv_currency_position();
281
	$format       = '%1$s%2$s';
282
283
	switch ( $currency_pos ) {
284
		case 'left':
285
			$format = '%1$s%2$s';
286
			break;
287
		case 'right':
288
			$format = '%2$s%1$s';
289
			break;
290
		case 'left_space':
291
			$format = '%1$s&nbsp;%2$s';
292
			break;
293
		case 'right_space':
294
			$format = '%2$s&nbsp;%1$s';
295
			break;
296
	}
297
298
	return apply_filters( 'getpaid_price_format', $format, $currency_pos );
299
}
300
301
/**
302
 * Format the amount with a currency symbol.
303
 *
304
 * @param  float  $amount Raw price.
305
 * @param  string $currency Currency.
306
 * @return string
307
 */
308
function wpinv_price( $amount = 0, $currency = '' ) {
309
310
    // Backwards compatibility.
311
    $amount             = wpinv_sanitize_amount( $amount );
312
313
    // Prepare variables.
314
    $currency           = wpinv_get_currency( $currency );
315
    $amount             = (float) $amount;
316
    $unformatted_amount = $amount;
317
    $negative           = $amount < 0;
318
    $amount             = apply_filters( 'getpaid_raw_amount', floatval( $negative ? $amount * -1 : $amount ) );
319
    $amount             = wpinv_format_amount( $amount );
320
321
    // Format the amount.
322
    $format             = getpaid_get_price_format();
323
    $formatted_amount   = ( $negative ? '-' : '' ) . sprintf( $format, '<span class="getpaid-currency__symbol">' . wpinv_currency_symbol( $currency ) . '</span>', $amount );
324
325
    // Filter the formatting.
326
    return apply_filters( 'wpinv_price', $formatted_amount, $amount, $currency, $unformatted_amount );
327
}
328
329
/**
330
 * Format an amount with separators.
331
 *
332
 * @param  float    $amount Raw amount.
333
 * @param  null|int $decimals Number of decimals to use.
334
 * @param  bool     $calculate Whether or not to apply separators.
335
 * @return string
336
 */
337
function wpinv_format_amount( $amount, $decimals = null, $calculate = false ) {
338
    $thousands_sep = wpinv_thousands_separator();
339
    $decimal_sep   = wpinv_decimal_separator();
340
    $decimals      = wpinv_decimals( $decimals );
341
    $amount        = wpinv_sanitize_amount( $amount );
342
343
    if ( $calculate ) {
344
        return $amount;
345
    }
346
347
    // Fomart the amount.
348
    return number_format( $amount, $decimals, $decimal_sep, $thousands_sep );
349
}
350
351
function wpinv_sanitize_key( $key ) {
352
    $raw_key = $key;
353
    $key = preg_replace( '/[^a-zA-Z0-9_\-\.\:\/]/', '', $key );
354
355
    return apply_filters( 'wpinv_sanitize_key', $key, $raw_key );
356
}
357
358
/**
359
 * Returns a file extesion.
360
 * 
361
 * @param $str the file whose extension should be retrieved.
362
 */
363
function wpinv_get_file_extension( $str ) {
364
    $filetype = wp_check_filetype( $str );
365
    return $filetype['ext'];
366
}
367
368
/**
369
 * Checks if a given string is an image URL.
370
 * 
371
 * @param string $string
372
 */
373
function wpinv_string_is_image_url( $string ) {
374
    $extension = strtolower( wpinv_get_file_extension( $string ) );
375
    return in_array( $extension, array( 'jpeg', 'jpg', 'png', 'gif', 'ico' ), true );
376
}
377
378
/**
379
 * Returns the current URL.
380
 */
381
function wpinv_get_current_page_url() {
382
    return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
383
}
384
385
/**
386
 * Define a constant if it is not already defined.
387
 *
388
 * @since 1.0.19
389
 * @param string $name  Constant name.
390
 * @param mixed  $value Value.
391
 */
392
function getpaid_maybe_define_constant( $name, $value ) {
393
	if ( ! defined( $name ) ) {
394
		define( $name, $value );
395
	}
396
}
397
398
function wpinv_get_php_arg_separator_output() {
399
	return ini_get( 'arg_separator.output' );
400
}
401
402
function wpinv_rgb_from_hex( $color ) {
403
    $color = str_replace( '#', '', $color );
404
405
    // Convert shorthand colors to full format, e.g. "FFF" -> "FFFFFF"
406
    $color = preg_replace( '~^(.)(.)(.)$~', '$1$1$2$2$3$3', $color );
407
    if ( empty( $color ) ) {
408
        return NULL;
409
    }
410
411
    $color = str_split( $color );
412
413
    $rgb      = array();
414
    $rgb['R'] = hexdec( $color[0] . $color[1] );
415
    $rgb['G'] = hexdec( $color[2] . $color[3] );
416
    $rgb['B'] = hexdec( $color[4] . $color[5] );
417
418
    return $rgb;
419
}
420
421
function wpinv_hex_darker( $color, $factor = 30 ) {
422
    $base  = wpinv_rgb_from_hex( $color );
423
    $color = '#';
424
425
    foreach ( $base as $k => $v ) {
426
        $amount      = $v / 100;
427
        $amount      = round( $amount * $factor );
428
        $new_decimal = $v - $amount;
429
430
        $new_hex_component = dechex( $new_decimal );
0 ignored issues
show
Bug introduced by
$new_decimal of type double is incompatible with the type integer expected by parameter $num of dechex(). ( Ignorable by Annotation )

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

430
        $new_hex_component = dechex( /** @scrutinizer ignore-type */ $new_decimal );
Loading history...
431
        if ( strlen( $new_hex_component ) < 2 ) {
432
            $new_hex_component = "0" . $new_hex_component;
433
        }
434
        $color .= $new_hex_component;
435
    }
436
437
    return $color;
438
}
439
440
function wpinv_hex_lighter( $color, $factor = 30 ) {
441
    $base  = wpinv_rgb_from_hex( $color );
442
    $color = '#';
443
444
    foreach ( $base as $k => $v ) {
445
        $amount      = 255 - $v;
446
        $amount      = $amount / 100;
447
        $amount      = round( $amount * $factor );
448
        $new_decimal = $v + $amount;
449
450
        $new_hex_component = dechex( $new_decimal );
0 ignored issues
show
Bug introduced by
$new_decimal of type double is incompatible with the type integer expected by parameter $num of dechex(). ( Ignorable by Annotation )

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

450
        $new_hex_component = dechex( /** @scrutinizer ignore-type */ $new_decimal );
Loading history...
451
        if ( strlen( $new_hex_component ) < 2 ) {
452
            $new_hex_component = "0" . $new_hex_component;
453
        }
454
        $color .= $new_hex_component;
455
    }
456
457
    return $color;
458
}
459
460
function wpinv_light_or_dark( $color, $dark = '#000000', $light = '#FFFFFF' ) {
461
    $hex = str_replace( '#', '', $color );
462
463
    $c_r = hexdec( substr( $hex, 0, 2 ) );
464
    $c_g = hexdec( substr( $hex, 2, 2 ) );
465
    $c_b = hexdec( substr( $hex, 4, 2 ) );
466
467
    $brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
468
469
    return $brightness > 155 ? $dark : $light;
470
}
471
472
function wpinv_format_hex( $hex ) {
473
    $hex = trim( str_replace( '#', '', $hex ) );
474
475
    if ( strlen( $hex ) == 3 ) {
476
        $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
477
    }
478
479
    return $hex ? '#' . $hex : null;
480
}
481
482
/**
483
 * Get truncated string with specified width.
484
 *
485
 * @since 1.0.0
486
 *
487
 * @param string $str The string being decoded.
488
 * @param int $start The start position offset. Number of characters from the beginning of string.
489
 *                      For negative value, number of characters from the end of the string.
490
 * @param int $width The width of the desired trim. Negative widths count from the end of the string.
491
 * @param string $trimmaker A string that is added to the end of string when string is truncated. Ex: "...".
492
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
493
 * @return string
494
 */
495
function wpinv_utf8_strimwidth( $str, $start, $width, $trimmaker = '', $encoding = 'UTF-8' ) {
496
    if ( function_exists( 'mb_strimwidth' ) ) {
497
        return mb_strimwidth( $str, $start, $width, $trimmaker, $encoding );
498
    }
499
    
500
    return wpinv_utf8_substr( $str, $start, $width, $encoding ) . $trimmaker;
501
}
502
503
/**
504
 * Get the string length.
505
 *
506
 * @since 1.0.0
507
 *
508
 * @param string $str The string being checked for length. 
509
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
510
 * @return int Returns the number of characters in string.
511
 */
512
function wpinv_utf8_strlen( $str, $encoding = 'UTF-8' ) {
513
    if ( function_exists( 'mb_strlen' ) ) {
514
        return mb_strlen( $str, $encoding );
515
    }
516
        
517
    return strlen( $str );
518
}
519
520
function wpinv_utf8_strtolower( $str, $encoding = 'UTF-8' ) {
521
    if ( function_exists( 'mb_strtolower' ) ) {
522
        return mb_strtolower( $str, $encoding );
523
    }
524
    
525
    return strtolower( $str );
526
}
527
528
function wpinv_utf8_strtoupper( $str, $encoding = 'UTF-8' ) {
529
    if ( function_exists( 'mb_strtoupper' ) ) {
530
        return mb_strtoupper( $str, $encoding );
531
    }
532
    
533
    return strtoupper( $str );
534
}
535
536
/**
537
 * Find position of first occurrence of string in a string
538
 *
539
 * @since 1.0.0
540
 *
541
 * @param string $str The string being checked.
542
 * @param string $find The string to find in input string.
543
 * @param int $offset The search offset. Default "0". A negative offset counts from the end of the string.
544
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
545
 * @return int Returns the position of the first occurrence of search in the string.
546
 */
547
function wpinv_utf8_strpos( $str, $find, $offset = 0, $encoding = 'UTF-8' ) {
548
    if ( function_exists( 'mb_strpos' ) ) {
549
        return mb_strpos( $str, $find, $offset, $encoding );
550
    }
551
        
552
    return strpos( $str, $find, $offset );
553
}
554
555
/**
556
 * Find position of last occurrence of a string in a string.
557
 *
558
 * @since 1.0.0
559
 *
560
 * @param string $str The string being checked, for the last occurrence of search.
561
 * @param string $find The string to find in input string.
562
 * @param int $offset Specifies begin searching an arbitrary number of characters into the string.
563
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
564
 * @return int Returns the position of the last occurrence of search.
565
 */
566
function wpinv_utf8_strrpos( $str, $find, $offset = 0, $encoding = 'UTF-8' ) {
567
    if ( function_exists( 'mb_strrpos' ) ) {
568
        return mb_strrpos( $str, $find, $offset, $encoding );
569
    }
570
        
571
    return strrpos( $str, $find, $offset );
572
}
573
574
/**
575
 * Get the part of string.
576
 *
577
 * @since 1.0.0
578
 *
579
 * @param string $str The string to extract the substring from.
580
 * @param int $start If start is non-negative, the returned string will start at the entered position in string, counting from zero.
581
 *                      If start is negative, the returned string will start at the entered position from the end of string. 
582
 * @param int|null $length Maximum number of characters to use from string.
583
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
584
 * @return string
585
 */
586
function wpinv_utf8_substr( $str, $start, $length = null, $encoding = 'UTF-8' ) {
587
    if ( function_exists( 'mb_substr' ) ) {
588
        if ( $length === null ) {
589
            return mb_substr( $str, $start, wpinv_utf8_strlen( $str, $encoding ), $encoding );
590
        } else {
591
            return mb_substr( $str, $start, $length, $encoding );
592
        }
593
    }
594
        
595
    return substr( $str, $start, $length );
596
}
597
598
/**
599
 * Get the width of string.
600
 *
601
 * @since 1.0.0
602
 *
603
 * @param string $str The string being decoded.
604
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
605
 * @return string The width of string.
606
 */
607
function wpinv_utf8_strwidth( $str, $encoding = 'UTF-8' ) {
608
    if ( function_exists( 'mb_strwidth' ) ) {
609
        return mb_strwidth( $str, $encoding );
610
    }
611
    
612
    return wpinv_utf8_strlen( $str, $encoding );
613
}
614
615
function wpinv_utf8_ucfirst( $str, $lower_str_end = false, $encoding = 'UTF-8' ) {
616
    if ( function_exists( 'mb_strlen' ) ) {
617
        $first_letter = wpinv_utf8_strtoupper( wpinv_utf8_substr( $str, 0, 1, $encoding ), $encoding );
618
        $str_end = "";
619
        
620
        if ( $lower_str_end ) {
621
            $str_end = wpinv_utf8_strtolower( wpinv_utf8_substr( $str, 1, wpinv_utf8_strlen( $str, $encoding ), $encoding ), $encoding );
622
        } else {
623
            $str_end = wpinv_utf8_substr( $str, 1, wpinv_utf8_strlen( $str, $encoding ), $encoding );
624
        }
625
626
        return $first_letter . $str_end;
627
    }
628
    
629
    return ucfirst( $str );
630
}
631
632
function wpinv_utf8_ucwords( $str, $encoding = 'UTF-8' ) {
633
    if ( function_exists( 'mb_convert_case' ) ) {
634
        return mb_convert_case( $str, MB_CASE_TITLE, $encoding );
635
    }
636
    
637
    return ucwords( $str );
638
}
639
640
function wpinv_period_in_days( $period, $unit ) {
641
    $period = absint( $period );
642
    
643
    if ( $period > 0 ) {
644
        if ( in_array( strtolower( $unit ), array( 'w', 'week', 'weeks' ) ) ) {
645
            $period = $period * 7;
646
        } else if ( in_array( strtolower( $unit ), array( 'm', 'month', 'months' ) ) ) {
647
            $period = $period * 30;
648
        } else if ( in_array( strtolower( $unit ), array( 'y', 'year', 'years' ) ) ) {
649
            $period = $period * 365;
650
        }
651
    }
652
    
653
    return $period;
654
}
655
656
function wpinv_cal_days_in_month( $calendar, $month, $year ) {
657
    if ( function_exists( 'cal_days_in_month' ) ) {
658
        return cal_days_in_month( $calendar, $month, $year );
659
    }
660
661
    // Fallback in case the calendar extension is not loaded in PHP
662
    // Only supports Gregorian calendar
663
    return date( 't', mktime( 0, 0, 0, $month, 1, $year ) );
664
}
665
666
/**
667
 * Display a help tip for settings.
668
 *
669
 * @param  string $tip Help tip text
670
 * @param  bool $allow_html Allow sanitized HTML if true or escape
671
 *
672
 * @return string
673
 */
674
function wpi_help_tip( $tip, $allow_html = false, $is_vue = false ) {
675
676
    if ( $allow_html ) {
677
        $tip = wpi_sanitize_tooltip( $tip );
678
    } else {
679
        $tip = esc_attr( $tip );
680
    }
681
682
    if ( $is_vue ) {
683
        return '<span class="dashicons dashicons-editor-help" title="' . $tip . '"></span>';
684
    }
685
686
    return '<span class="wpi-help-tip dashicons dashicons-editor-help" title="' . $tip . '"></span>';
687
}
688
689
/**
690
 * Sanitize a string destined to be a tooltip.
691
 *
692
 * Tooltips are encoded with htmlspecialchars to prevent XSS. Should not be used in conjunction with esc_attr()
693
 *
694
 * @param string $var
695
 * @return string
696
 */
697
function wpi_sanitize_tooltip( $var ) {
698
    return wp_kses( html_entity_decode( $var ), array(
699
        'br'     => array(),
700
        'em'     => array(),
701
        'strong' => array(),
702
        'b'      => array(),
703
        'small'  => array(),
704
        'span'   => array(),
705
        'ul'     => array(),
706
        'li'     => array(),
707
        'ol'     => array(),
708
        'p'      => array(),
709
    ) );
710
}
711
712
/**
713
 * Get all WPI screen ids.
714
 *
715
 * @return array
716
 */
717
function wpinv_get_screen_ids() {
718
719
    $screen_id = sanitize_title( __( 'Invoicing', 'invoicing' ) );
720
721
    $screen_ids = array(
722
        'toplevel_page_' . $screen_id,
723
        'wpi_invoice',
724
        'wpi_item',
725
        'wpi_quote',
726
        'wpi_discount',
727
        'wpi_payment_form',
728
        'edit-wpi_invoice',
729
        'edit-wpi_item',
730
        'edit-wpi_discount',
731
        'edit-wpi_quote',
732
        'edit-wpi_payment_form',
733
        'getpaid_page_wpinv-settings',
734
        'getpaid_page_wpinv-subscriptions',
735
        'getpaid_page_wpinv-reports',
736
        'getpaid_page_wpi-addons',
737
        'getpaid_page_wpinv-customers',
738
        'gp-setup',// setup wizard
739
    );
740
741
    return apply_filters( 'wpinv_screen_ids', $screen_ids );
742
}
743
744
/**
745
 * Cleans up an array, comma- or space-separated list of scalar values.
746
 *
747
 * @since 1.0.13
748
 *
749
 * @param array|string $list List of values.
750
 * @return array Sanitized array of values.
751
 */
752
function wpinv_parse_list( $list ) {
753
754
    if ( empty( $list ) ) {
755
        $list = array();
756
    }
757
758
	if ( ! is_array( $list ) ) {
759
		return preg_split( '/[\s,]+/', $list, -1, PREG_SPLIT_NO_EMPTY );
760
	}
761
762
	return $list;
763
}
764
765
/**
766
 * Fetches data stored on disk.
767
 *
768
 * @since 1.0.14
769
 *
770
 * @param string $key Type of data to fetch.
771
 * @return mixed Fetched data.
772
 */
773
function wpinv_get_data( $key ) {
774
775
    // Try fetching it from the cache.
776
    $data = wp_cache_get( "wpinv-data-$key", 'wpinv' );
777
    if( $data ) {
778
        return $data;
779
    }
780
781
    $data = apply_filters( "wpinv_get_$key", include WPINV_PLUGIN_DIR . "includes/data/$key.php" );
782
	wp_cache_set( "wpinv-data-$key", $data, 'wpinv' );
783
784
	return $data;
785
}
786
787
/**
788
 * (Maybe) Adds an empty option to an array of options.
789
 *
790
 * @since 1.0.14
791
 *
792
 * @param array $options
793
 * @param bool $first_empty Whether or not the first item in the list should be empty
794
 * @return mixed Fetched data.
795
 */
796
function wpinv_maybe_add_empty_option( $options, $first_empty ) {
797
798
    if ( ! empty( $options ) && $first_empty ) {
799
        return array_merge( array( '' => '' ), $options );
800
    }
801
    return $options;
802
803
}
804
805
/**
806
 * Clean variables using sanitize_text_field.
807
 *
808
 * @param mixed $var Data to sanitize.
809
 * @return string|array
810
 */
811
function wpinv_clean( $var ) {
812
813
	if ( is_array( $var ) ) {
814
		return array_map( 'wpinv_clean', $var );
815
    }
816
817
    if ( is_object( $var ) ) {
818
		$object_vars = get_object_vars( $var );
819
		foreach ( $object_vars as $property_name => $property_value ) {
820
			$var->$property_name = wpinv_clean( $property_value );
821
        }
822
        return $var;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $var returns the type object which is incompatible with the documented return type array|string.
Loading history...
823
	}
824
825
    return is_string( $var ) ? sanitize_text_field( stripslashes( $var ) ) : $var;
826
}
827
828
/**
829
 * Converts a price string into an options array.
830
 *
831
 * @param string $str Data to convert.
832
 * @return string|array
833
 */
834
function getpaid_convert_price_string_to_options( $str ) {
835
836
	$raw_options = array_map( 'trim', explode( ',', $str ) );
837
    $options     = array();
838
839
    foreach ( $raw_options as $option ) {
840
841
        if ( '' == $option ) {
842
            continue;
843
        }
844
845
        $option = array_map( 'trim', explode( '|', $option ) );
846
847
        $price = null;
848
        $label = null;
849
850
        if ( isset( $option[0] ) && '' !=  $option[0] ) {
851
            $label  = $option[0];
852
        }
853
854
        if ( isset( $option[1] ) && '' !=  $option[1] ) {
855
            $price = $option[1];
856
        }
857
858
        if ( ! isset( $price ) ) {
859
            $price = $label;
860
        }
861
862
        if ( ! isset( $price ) || ! is_numeric( $price ) ) {
863
            continue;
864
        }
865
866
        if ( ! isset( $label ) ) {
867
            $label = $price;
868
        }
869
870
        $options[ "$label|$price" ] = $label;
871
    }
872
873
    return $options;
874
}
875
876
/**
877
 * Returns the help tip.
878
 */
879
function getpaid_get_help_tip( $tip, $additional_classes = '' ) {
880
    $additional_classes = sanitize_html_class( $additional_classes );
881
    $tip                = esc_attr__( $tip );
882
    return "<span class='wpi-help-tip dashicons dashicons-editor-help $additional_classes' title='$tip'></span>";
883
}
884
885
/**
886
 * Formats a date
887
 */
888
function getpaid_format_date( $date, $with_time = false ) {
889
890
    if ( empty( $date ) || $date == '0000-00-00 00:00:00' ) {
891
        return '';
892
    }
893
894
    $format = getpaid_date_format();
895
896
    if ( $with_time ) {
897
        $format .= ' ' . getpaid_time_format();
898
    }
899
    return date_i18n( $format, strtotime( $date ) );
900
901
}
902
903
/**
904
 * Formats a date into the website's date setting.
905
 *
906
 * @return string
907
 */
908
function getpaid_format_date_value( $date, $default = "&mdash;", $with_time = false ) {
909
    $date = getpaid_format_date( $date, $with_time );
910
    return empty( $date ) ? $default : $date;
911
}
912
913
/**
914
 * Get the date format used all over the plugin.
915
 *
916
 * @return string
917
 */
918
function getpaid_date_format() {
919
	return apply_filters( 'getpaid_date_format', get_option( 'date_format' ) );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('ge..._option('date_format')) could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
920
}
921
922
/**
923
 * Get the time format used all over the plugin.
924
 *
925
 * @return string
926
 */
927
function getpaid_time_format() {
928
	return apply_filters( 'getpaid_time_format', get_option( 'time_format' ) );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('ge..._option('time_format')) could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
929
}
930
931
/**
932
 * Limit length of a string.
933
 *
934
 * @param  string  $string string to limit.
935
 * @param  integer $limit Limit size in characters.
936
 * @return string
937
 */
938
function getpaid_limit_length( $string, $limit ) {
939
    $str_limit = $limit - 3;
940
941
	if ( function_exists( 'mb_strimwidth' ) ) {
942
		if ( mb_strlen( $string ) > $limit ) {
943
			$string = mb_strimwidth( $string, 0, $str_limit ) . '...';
944
		}
945
	} else {
946
		if ( strlen( $string ) > $limit ) {
947
			$string = substr( $string, 0, $str_limit ) . '...';
948
		}
949
	}
950
    return $string;
951
952
}
953
954
/**
955
 * Returns the REST API handler.
956
 * 
957
 * @return WPInv_API
958
 * @since 1.0.19
959
 */
960
function getpaid_api() {
961
    return getpaid()->get( 'api' );
962
}
963
964
/**
965
 * Returns the post types object.
966
 * 
967
 * @return GetPaid_Post_Types
968
 * @since 1.0.19
969
 */
970
function getpaid_post_types() {
971
    return getpaid()->get( 'post_types' );
972
}
973
974
/**
975
 * Returns the session handler.
976
 * 
977
 * @return WPInv_Session_Handler
978
 * @since 1.0.19
979
 */
980
function getpaid_session() {
981
    return getpaid()->get( 'session' );
982
}
983
984
/**
985
 * Returns the notes handler.
986
 * 
987
 * @return WPInv_Notes
988
 * @since 1.0.19
989
 */
990
function getpaid_notes() {
991
    return getpaid()->get( 'notes' );
992
}
993
994
/**
995
 * Returns the main admin class.
996
 * 
997
 * @return GetPaid_Admin
998
 */
999
function getpaid_admin() {
1000
    return getpaid()->get( 'admin' );
1001
}
1002
1003
/**
1004
 * Retrieves a URL to an authenticated action
1005
 *
1006
 * @param string $action
1007
 * @param string $base the base url
1008
 * @return string
1009
 */
1010
function getpaid_get_authenticated_action_url( $action, $base = false ) {
1011
    return wp_nonce_url( add_query_arg( 'getpaid-action', $action, $base ), 'getpaid-nonce', 'getpaid-nonce' );
1012
}
1013
1014
/**
1015
 * Returns a post type label.
1016
 *
1017
 * @return string
1018
 */
1019
function getpaid_get_post_type_label( $post_type, $plural = true ) {
1020
1021
    $post_type = get_post_type_object( $post_type );
1022
1023
    if ( ! is_object( $post_type ) ) {
1024
        return null;
1025
    }
1026
1027
    return $plural ? $post_type->labels->name : $post_type->labels->singular_name;
1028
1029
}
1030
1031
/**
1032
 * Retrieves an array
1033
 *
1034
 * @return mixed|null
1035
 */
1036
function getpaid_get_array_field( $array, $key, $secondary_key = null ) {
1037
1038
    if ( ! is_array( $array ) ) {
1039
        return null;
1040
    }
1041
1042
    if ( ! empty( $secondary_key ) ) {
1043
        $array = isset( $array[ $secondary_key ] ) ? $array[ $secondary_key ] : array();
1044
        return getpaid_get_array_field( $array, $key );
0 ignored issues
show
Bug introduced by
Are you sure the usage of getpaid_get_array_field($array, $key) is correct as it 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...
1045
    }
1046
1047
    return isset( $array[ $key ] ) ? $array[ $key ] : null;
1048
1049
}
1050
1051
/**
1052
 * Merges an empty array
1053
 *
1054
 * @return array
1055
 */
1056
function getpaid_array_merge_if_empty( $args, $defaults ) {
1057
1058
    foreach ( $defaults as $key => $value ) {
1059
1060
        if ( array_key_exists( $key, $args ) && empty( $args[ $key ] ) ) {
1061
            $args[ $key ] = $value;
1062
        }
1063
1064
    }
1065
1066
    return $args;
1067
1068
}
1069
1070
/**
1071
 * Returns allowed file types.
1072
 *
1073
 * @return array
1074
 */
1075
function getpaid_get_allowed_mime_types() {
1076
1077
    $types = get_allowed_mime_types();
1078
1079
    if ( isset( $types['htm|html'] ) ) {
1080
		unset( $types['htm|html'] );
1081
	}
1082
1083
    if ( isset( $types['js'] ) ) {
1084
		unset( $types['js'] );
1085
	}
1086
1087
    return $types;
1088
1089
}
1090
1091
1092
function getpaid_user_delete_invoice( $data ) {
1093
1094
    // Ensure there is an invoice to delete.
1095
    if ( empty( $data['invoice_id'] ) ) {
1096
        return;
1097
    }
1098
1099
    $invoice = new WPInv_Invoice( (int) $data['invoice_id'] );
1100
1101
    // Ensure that it exists and that it belongs to the current user.
1102
    if ( ! $invoice->exists() || $invoice->get_customer_id() != get_current_user_id() ) {
1103
        wpinv_set_error( 'invalid_invoice', __( 'You do not have permission to delete this invoice', 'invoicing' ) );
1104
1105
    // Can it be deleted?
1106
    } else if ( ! $invoice->needs_payment() ) {
1107
        wpinv_set_error( 'cannot_delete', __( 'This invoice cannot be deleted as it has already been paid.', 'invoicing' ) );
1108
1109
    // Delete it.
1110
    } else {
1111
1112
        $invoice->delete();
1113
        wpinv_set_error( 'delete', __( 'The invoice has been deleted.', 'invoicing' ), 'info' );
1114
    }
1115
1116
    $redirect = remove_query_arg( array( 'getpaid-action', 'getpaid-nonce', 'invoice_id' ) );
1117
1118
    wp_safe_redirect( $redirect );
1119
    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...
1120
1121
}
1122
add_action( 'getpaid_authenticated_action_delete_invoice', 'getpaid_user_delete_invoice' );