Passed
Push — master ( 99ae46...ecf53c )
by Brian
05:15
created

wpinv_the_price()   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 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
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
 * Prints an amount with the correct format.
303
 *
304
 * @param  float  $amount Raw price.
305
 * @param  string $currency Currency.
306
 * @return string
307
 */
308
function wpinv_the_price( $amount = 0, $currency = '' ) {
309
    echo wp_kses_post( wpinv_price( $amount, $currency ) );
310
}
311
312
/**
313
 * Format the amount with a currency symbol.
314
 *
315
 * @param  float  $amount Raw price.
316
 * @param  string $currency Currency.
317
 * @return string
318
 */
319
function wpinv_price( $amount = 0, $currency = '' ) {
320
321
    // Backwards compatibility.
322
    $amount             = wpinv_sanitize_amount( $amount );
323
324
    // Prepare variables.
325
    $currency           = wpinv_get_currency( $currency );
326
    $amount             = (float) $amount;
327
    $unformatted_amount = $amount;
328
    $negative           = $amount < 0;
329
    $amount             = apply_filters( 'getpaid_raw_amount', floatval( $negative ? $amount * -1 : $amount ) );
330
    $amount             = wpinv_format_amount( $amount );
331
332
    // Format the amount.
333
    $format             = getpaid_get_price_format();
334
    $formatted_amount   = ( $negative ? '-' : '' ) . sprintf( $format, '<span class="getpaid-currency__symbol">' . wpinv_currency_symbol( $currency ) . '</span>', $amount );
335
336
    // Filter the formatting.
337
    return apply_filters( 'wpinv_price', $formatted_amount, $amount, $currency, $unformatted_amount );
338
}
339
340
/**
341
 * Format an amount with separators.
342
 *
343
 * @param  float    $amount Raw amount.
344
 * @param  null|int $decimals Number of decimals to use.
345
 * @param  bool     $calculate Whether or not to apply separators.
346
 * @return string
347
 */
348
function wpinv_format_amount( $amount, $decimals = null, $calculate = false ) {
349
    $thousands_sep = wpinv_thousands_separator();
350
    $decimal_sep   = wpinv_decimal_separator();
351
    $decimals      = wpinv_decimals( $decimals );
352
    $amount        = wpinv_sanitize_amount( $amount );
353
354
    if ( $calculate ) {
355
        return $amount;
356
    }
357
358
    // Fomart the amount.
359
    return number_format( $amount, $decimals, $decimal_sep, $thousands_sep );
360
}
361
362
function wpinv_sanitize_key( $key ) {
363
    $raw_key = $key;
364
    $key = preg_replace( '/[^a-zA-Z0-9_\-\.\:\/]/', '', $key );
365
366
    return apply_filters( 'wpinv_sanitize_key', $key, $raw_key );
367
}
368
369
/**
370
 * Returns a file extesion.
371
 *
372
 * @param $str the file whose extension should be retrieved.
373
 */
374
function wpinv_get_file_extension( $str ) {
375
    $filetype = wp_check_filetype( $str );
376
    return $filetype['ext'];
377
}
378
379
/**
380
 * Checks if a given string is an image URL.
381
 *
382
 * @param string $string
383
 */
384
function wpinv_string_is_image_url( $string ) {
385
    $extension = strtolower( wpinv_get_file_extension( $string ) );
386
    return in_array( $extension, array( 'jpeg', 'jpg', 'png', 'gif', 'ico' ), true );
387
}
388
389
/**
390
 * Returns the current URL.
391
 */
392
function wpinv_get_current_page_url() {
393
    return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
394
}
395
396
/**
397
 * Define a constant if it is not already defined.
398
 *
399
 * @since 1.0.19
400
 * @param string $name  Constant name.
401
 * @param mixed  $value Value.
402
 */
403
function getpaid_maybe_define_constant( $name, $value ) {
404
	if ( ! defined( $name ) ) {
405
		define( $name, $value );
406
	}
407
}
408
409
function wpinv_get_php_arg_separator_output() {
410
	return ini_get( 'arg_separator.output' );
411
}
412
413
function wpinv_rgb_from_hex( $color ) {
414
    $color = str_replace( '#', '', $color );
415
416
    // Convert shorthand colors to full format, e.g. "FFF" -> "FFFFFF"
417
    $color = preg_replace( '~^(.)(.)(.)$~', '$1$1$2$2$3$3', $color );
418
    if ( empty( $color ) ) {
419
        return null;
420
    }
421
422
    $color = str_split( $color );
423
424
    $rgb      = array();
425
    $rgb['R'] = hexdec( $color[0] . $color[1] );
426
    $rgb['G'] = hexdec( $color[2] . $color[3] );
427
    $rgb['B'] = hexdec( $color[4] . $color[5] );
428
429
    return $rgb;
430
}
431
432
function wpinv_hex_darker( $color, $factor = 30 ) {
433
    $base  = wpinv_rgb_from_hex( $color );
434
    $color = '#';
435
436
    foreach ( $base as $k => $v ) {
437
        $amount      = $v / 100;
438
        $amount      = round( $amount * $factor );
439
        $new_decimal = $v - $amount;
440
441
        $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

441
        $new_hex_component = dechex( /** @scrutinizer ignore-type */ $new_decimal );
Loading history...
442
        if ( strlen( $new_hex_component ) < 2 ) {
443
            $new_hex_component = '0' . $new_hex_component;
444
        }
445
        $color .= $new_hex_component;
446
    }
447
448
    return $color;
449
}
450
451
function wpinv_hex_lighter( $color, $factor = 30 ) {
452
    $base  = wpinv_rgb_from_hex( $color );
453
    $color = '#';
454
455
    foreach ( $base as $k => $v ) {
456
        $amount      = 255 - $v;
457
        $amount      = $amount / 100;
458
        $amount      = round( $amount * $factor );
459
        $new_decimal = $v + $amount;
460
461
        $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

461
        $new_hex_component = dechex( /** @scrutinizer ignore-type */ $new_decimal );
Loading history...
462
        if ( strlen( $new_hex_component ) < 2 ) {
463
            $new_hex_component = '0' . $new_hex_component;
464
        }
465
        $color .= $new_hex_component;
466
    }
467
468
    return $color;
469
}
470
471
function wpinv_light_or_dark( $color, $dark = '#000000', $light = '#FFFFFF' ) {
472
    $hex = str_replace( '#', '', $color );
473
474
    $c_r = hexdec( substr( $hex, 0, 2 ) );
475
    $c_g = hexdec( substr( $hex, 2, 2 ) );
476
    $c_b = hexdec( substr( $hex, 4, 2 ) );
477
478
    $brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
479
480
    return $brightness > 155 ? $dark : $light;
481
}
482
483
function wpinv_format_hex( $hex ) {
484
    $hex = trim( str_replace( '#', '', $hex ) );
485
486
    if ( strlen( $hex ) == 3 ) {
487
        $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
488
    }
489
490
    return $hex ? '#' . $hex : null;
491
}
492
493
/**
494
 * Get truncated string with specified width.
495
 *
496
 * @since 1.0.0
497
 *
498
 * @param string $str The string being decoded.
499
 * @param int $start The start position offset. Number of characters from the beginning of string.
500
 *                      For negative value, number of characters from the end of the string.
501
 * @param int $width The width of the desired trim. Negative widths count from the end of the string.
502
 * @param string $trimmaker A string that is added to the end of string when string is truncated. Ex: "...".
503
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
504
 * @return string
505
 */
506
function wpinv_utf8_strimwidth( $str, $start, $width, $trimmaker = '', $encoding = 'UTF-8' ) {
507
    if ( function_exists( 'mb_strimwidth' ) ) {
508
        return mb_strimwidth( $str, $start, $width, $trimmaker, $encoding );
509
    }
510
511
    return wpinv_utf8_substr( $str, $start, $width, $encoding ) . $trimmaker;
512
}
513
514
/**
515
 * Get the string length.
516
 *
517
 * @since 1.0.0
518
 *
519
 * @param string $str The string being checked for length.
520
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
521
 * @return int Returns the number of characters in string.
522
 */
523
function wpinv_utf8_strlen( $str, $encoding = 'UTF-8' ) {
524
    if ( function_exists( 'mb_strlen' ) ) {
525
        return mb_strlen( $str, $encoding );
526
    }
527
528
    return strlen( $str );
529
}
530
531
function wpinv_utf8_strtolower( $str, $encoding = 'UTF-8' ) {
532
    if ( function_exists( 'mb_strtolower' ) ) {
533
        return mb_strtolower( $str, $encoding );
534
    }
535
536
    return strtolower( $str );
537
}
538
539
function wpinv_utf8_strtoupper( $str, $encoding = 'UTF-8' ) {
540
    if ( function_exists( 'mb_strtoupper' ) ) {
541
        return mb_strtoupper( $str, $encoding );
542
    }
543
544
    return strtoupper( $str );
545
}
546
547
/**
548
 * Find position of first occurrence of string in a string
549
 *
550
 * @since 1.0.0
551
 *
552
 * @param string $str The string being checked.
553
 * @param string $find The string to find in input string.
554
 * @param int $offset The search offset. Default "0". A negative offset counts from the end of the string.
555
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
556
 * @return int Returns the position of the first occurrence of search in the string.
557
 */
558
function wpinv_utf8_strpos( $str, $find, $offset = 0, $encoding = 'UTF-8' ) {
559
    if ( function_exists( 'mb_strpos' ) ) {
560
        return mb_strpos( $str, $find, $offset, $encoding );
561
    }
562
563
    return strpos( $str, $find, $offset );
564
}
565
566
/**
567
 * Find position of last occurrence of a string in a string.
568
 *
569
 * @since 1.0.0
570
 *
571
 * @param string $str The string being checked, for the last occurrence of search.
572
 * @param string $find The string to find in input string.
573
 * @param int $offset Specifies begin searching an arbitrary number of characters into the string.
574
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
575
 * @return int Returns the position of the last occurrence of search.
576
 */
577
function wpinv_utf8_strrpos( $str, $find, $offset = 0, $encoding = 'UTF-8' ) {
578
    if ( function_exists( 'mb_strrpos' ) ) {
579
        return mb_strrpos( $str, $find, $offset, $encoding );
580
    }
581
582
    return strrpos( $str, $find, $offset );
583
}
584
585
/**
586
 * Get the part of string.
587
 *
588
 * @since 1.0.0
589
 *
590
 * @param string $str The string to extract the substring from.
591
 * @param int $start If start is non-negative, the returned string will start at the entered position in string, counting from zero.
592
 *                      If start is negative, the returned string will start at the entered position from the end of string.
593
 * @param int|null $length Maximum number of characters to use from string.
594
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
595
 * @return string
596
 */
597
function wpinv_utf8_substr( $str, $start, $length = null, $encoding = 'UTF-8' ) {
598
    if ( function_exists( 'mb_substr' ) ) {
599
        if ( $length === null ) {
600
            return mb_substr( $str, $start, wpinv_utf8_strlen( $str, $encoding ), $encoding );
601
        } else {
602
            return mb_substr( $str, $start, $length, $encoding );
603
        }
604
    }
605
606
    return substr( $str, $start, $length );
607
}
608
609
/**
610
 * Get the width of string.
611
 *
612
 * @since 1.0.0
613
 *
614
 * @param string $str The string being decoded.
615
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
616
 * @return string The width of string.
617
 */
618
function wpinv_utf8_strwidth( $str, $encoding = 'UTF-8' ) {
619
    if ( function_exists( 'mb_strwidth' ) ) {
620
        return mb_strwidth( $str, $encoding );
621
    }
622
623
    return wpinv_utf8_strlen( $str, $encoding );
624
}
625
626
function wpinv_utf8_ucfirst( $str, $lower_str_end = false, $encoding = 'UTF-8' ) {
627
    if ( function_exists( 'mb_strlen' ) ) {
628
        $first_letter = wpinv_utf8_strtoupper( wpinv_utf8_substr( $str, 0, 1, $encoding ), $encoding );
629
        $str_end = '';
630
631
        if ( $lower_str_end ) {
632
            $str_end = wpinv_utf8_strtolower( wpinv_utf8_substr( $str, 1, wpinv_utf8_strlen( $str, $encoding ), $encoding ), $encoding );
633
        } else {
634
            $str_end = wpinv_utf8_substr( $str, 1, wpinv_utf8_strlen( $str, $encoding ), $encoding );
635
        }
636
637
        return $first_letter . $str_end;
638
    }
639
640
    return ucfirst( $str );
641
}
642
643
function wpinv_utf8_ucwords( $str, $encoding = 'UTF-8' ) {
644
    if ( function_exists( 'mb_convert_case' ) ) {
645
        return mb_convert_case( $str, MB_CASE_TITLE, $encoding );
646
    }
647
648
    return ucwords( $str );
649
}
650
651
function wpinv_period_in_days( $period, $unit ) {
652
    $period = absint( $period );
653
654
    if ( $period > 0 ) {
655
        if ( in_array( strtolower( $unit ), array( 'w', 'week', 'weeks' ) ) ) {
656
            $period = $period * 7;
657
        } elseif ( in_array( strtolower( $unit ), array( 'm', 'month', 'months' ) ) ) {
658
            $period = $period * 30;
659
        } elseif ( in_array( strtolower( $unit ), array( 'y', 'year', 'years' ) ) ) {
660
            $period = $period * 365;
661
        }
662
    }
663
664
    return $period;
665
}
666
667
function wpinv_cal_days_in_month( $calendar, $month, $year ) {
668
    if ( function_exists( 'cal_days_in_month' ) ) {
669
        return cal_days_in_month( $calendar, $month, $year );
670
    }
671
672
    // Fallback in case the calendar extension is not loaded in PHP
673
    // Only supports Gregorian calendar
674
    return date( 't', mktime( 0, 0, 0, $month, 1, $year ) );
675
}
676
677
/**
678
 * Display a help tip for settings.
679
 *
680
 * @param  string $tip Help tip text
681
 * @param  bool $allow_html Allow sanitized HTML if true or escape
682
 *
683
 * @return string
684
 */
685
function wpi_help_tip( $tip, $allow_html = false, $is_vue = false, $echo = false ) {
686
687
    if ( $allow_html ) {
688
        $tip = wpi_sanitize_tooltip( $tip );
689
    } else {
690
        $tip = strip_tags( $tip );
691
    }
692
693
    if ( $is_vue ) {
694
695
        if ( $echo ) {
696
            echo '<span class="dashicons dashicons-editor-help" title="' . esc_attr( $tip ) . '"></span>';
697
        } else {
698
            return '<span class="dashicons dashicons-editor-help" title="' . esc_attr( $tip ) . '"></span>';
699
        }
700
}
701
702
    if ( $echo ) {
703
        echo '<span class="wpi-help-tip dashicons dashicons-editor-help" title="' . esc_attr( $tip ) . '"></span>';
704
    } else {
705
        return '<span class="wpi-help-tip dashicons dashicons-editor-help" title="' . esc_attr( $tip ) . '"></span>';
706
    }
707
}
708
709
/**
710
 * Sanitize a string destined to be a tooltip.
711
 *
712
 * Tooltips are encoded with htmlspecialchars to prevent XSS. Should not be used in conjunction with esc_attr()
713
 *
714
 * @param string $var
715
 * @return string
716
 */
717
function wpi_sanitize_tooltip( $var ) {
718
    return wp_kses(
719
        html_entity_decode( $var ),
720
        array(
721
			'br'     => array(),
722
			'em'     => array(),
723
			'strong' => array(),
724
			'b'      => array(),
725
			'small'  => array(),
726
			'span'   => array(),
727
			'ul'     => array(),
728
			'li'     => array(),
729
			'ol'     => array(),
730
			'p'      => array(),
731
        )
732
    );
733
}
734
735
/**
736
 * Get all WPI screen ids.
737
 *
738
 * @return array
739
 */
740
function wpinv_get_screen_ids() {
741
742
    $screen_id = sanitize_title( __( 'Invoicing', 'invoicing' ) );
743
744
    $screen_ids = array(
745
        'toplevel_page_' . $screen_id,
746
        'wpi_invoice',
747
        'wpi_item',
748
        'wpi_quote',
749
        'wpi_discount',
750
        'wpi_payment_form',
751
        'edit-wpi_invoice',
752
        'edit-wpi_item',
753
        'edit-wpi_discount',
754
        'edit-wpi_quote',
755
        'edit-wpi_payment_form',
756
        'getpaid_page_wpinv-settings',
757
        'getpaid_page_wpinv-subscriptions',
758
        'getpaid_page_wpinv-reports',
759
        'getpaid_page_wpi-addons',
760
        'getpaid_page_wpinv-customers',
761
        'gp-setup', // setup wizard
762
    );
763
764
    return apply_filters( 'wpinv_screen_ids', $screen_ids );
765
}
766
767
/**
768
 * Cleans up an array, comma- or space-separated list of scalar values.
769
 *
770
 * @since 1.0.13
771
 *
772
 * @param array|string $list List of values.
773
 * @return array Sanitized array of values.
774
 */
775
function wpinv_parse_list( $list ) {
776
777
    if ( empty( $list ) ) {
778
        $list = array();
779
    }
780
781
	if ( ! is_array( $list ) ) {
782
		return preg_split( '/[\s,]+/', $list, -1, PREG_SPLIT_NO_EMPTY );
783
	}
784
785
	return $list;
786
}
787
788
/**
789
 * Fetches data stored on disk.
790
 *
791
 * @since 1.0.14
792
 *
793
 * @param string $key Type of data to fetch.
794
 * @return mixed Fetched data.
795
 */
796
function wpinv_get_data( $key ) {
797
798
    // Try fetching it from the cache.
799
    $data = wp_cache_get( "wpinv-data-$key", 'wpinv' );
800
    if ( $data ) {
801
        return $data;
802
    }
803
804
    $data = apply_filters( "wpinv_get_$key", include WPINV_PLUGIN_DIR . "includes/data/$key.php" );
805
	wp_cache_set( "wpinv-data-$key", $data, 'wpinv' );
806
807
	return $data;
808
}
809
810
/**
811
 * (Maybe) Adds an empty option to an array of options.
812
 *
813
 * @since 1.0.14
814
 *
815
 * @param array $options
816
 * @param bool $first_empty Whether or not the first item in the list should be empty
817
 * @return mixed Fetched data.
818
 */
819
function wpinv_maybe_add_empty_option( $options, $first_empty ) {
820
821
    if ( ! empty( $options ) && $first_empty ) {
822
        return array_merge( array( '' => '' ), $options );
823
    }
824
    return $options;
825
826
}
827
828
/**
829
 * Clean variables using sanitize_text_field.
830
 *
831
 * @param mixed $var Data to sanitize.
832
 * @return string|array
833
 */
834
function wpinv_clean( $var ) {
835
836
	if ( is_array( $var ) ) {
837
		return array_map( 'wpinv_clean', $var );
838
    }
839
840
    if ( is_object( $var ) ) {
841
		$object_vars = get_object_vars( $var );
842
		foreach ( $object_vars as $property_name => $property_value ) {
843
			$var->$property_name = wpinv_clean( $property_value );
844
        }
845
        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...
846
	}
847
848
    return is_string( $var ) ? sanitize_text_field( stripslashes( $var ) ) : $var;
849
}
850
851
/**
852
 * Converts a price string into an options array.
853
 *
854
 * @param string $str Data to convert.
855
 * @return string|array
856
 */
857
function getpaid_convert_price_string_to_options( $str ) {
858
859
	$raw_options = array_map( 'trim', explode( ',', $str ) );
860
    $options     = array();
861
862
    foreach ( $raw_options as $option ) {
863
864
        if ( '' == $option ) {
865
            continue;
866
        }
867
868
        $option = array_map( 'trim', explode( '|', $option ) );
869
870
        $price = null;
871
        $label = null;
872
873
        if ( isset( $option[0] ) && '' != $option[0] ) {
874
            $label  = $option[0];
875
        }
876
877
        if ( isset( $option[1] ) && '' != $option[1] ) {
878
            $price = $option[1];
879
        }
880
881
        if ( ! isset( $price ) ) {
882
            $price = $label;
883
        }
884
885
        if ( ! isset( $price ) || ! is_numeric( $price ) ) {
886
            continue;
887
        }
888
889
        if ( ! isset( $label ) ) {
890
            $label = $price;
891
        }
892
893
        $options[ "$label|$price" ] = $label;
894
    }
895
896
    return $options;
897
}
898
899
/**
900
 * Returns the help tip.
901
 */
902
function getpaid_get_help_tip( $tip, $additional_classes = '' ) {
903
    $additional_classes = sanitize_html_class( $additional_classes );
904
    $tip                = esc_attr__( $tip );
905
    return "<span class='wpi-help-tip dashicons dashicons-editor-help $additional_classes' title='$tip'></span>";
906
}
907
908
/**
909
 * Formats a date
910
 */
911
function getpaid_format_date( $date, $with_time = false ) {
912
913
    if ( empty( $date ) || $date == '0000-00-00 00:00:00' ) {
914
        return '';
915
    }
916
917
    $format = getpaid_date_format();
918
919
    if ( $with_time ) {
920
        $format .= ' ' . getpaid_time_format();
921
    }
922
    return date_i18n( $format, strtotime( $date ) );
923
924
}
925
926
/**
927
 * Formats a date into the website's date setting.
928
 *
929
 * @return string
930
 */
931
function getpaid_format_date_value( $date, $default = '&mdash;', $with_time = false ) {
932
    $date = getpaid_format_date( $date, $with_time );
933
    return empty( $date ) ? $default : $date;
934
}
935
936
/**
937
 * Get the date format used all over the plugin.
938
 *
939
 * @return string
940
 */
941
function getpaid_date_format() {
942
	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...
943
}
944
945
/**
946
 * Get the time format used all over the plugin.
947
 *
948
 * @return string
949
 */
950
function getpaid_time_format() {
951
	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...
952
}
953
954
/**
955
 * Limit length of a string.
956
 *
957
 * @param  string  $string string to limit.
958
 * @param  integer $limit Limit size in characters.
959
 * @return string
960
 */
961
function getpaid_limit_length( $string, $limit ) {
962
    $str_limit = $limit - 3;
963
964
	if ( function_exists( 'mb_strimwidth' ) ) {
965
		if ( mb_strlen( $string ) > $limit ) {
966
			$string = mb_strimwidth( $string, 0, $str_limit ) . '...';
967
		}
968
	} else {
969
		if ( strlen( $string ) > $limit ) {
970
			$string = substr( $string, 0, $str_limit ) . '...';
971
		}
972
	}
973
    return $string;
974
975
}
976
977
/**
978
 * Returns the REST API handler.
979
 *
980
 * @return WPInv_API
981
 * @since 1.0.19
982
 */
983
function getpaid_api() {
984
    return getpaid()->get( 'api' );
985
}
986
987
/**
988
 * Returns the post types object.
989
 *
990
 * @return GetPaid_Post_Types
991
 * @since 1.0.19
992
 */
993
function getpaid_post_types() {
994
    return getpaid()->get( 'post_types' );
995
}
996
997
/**
998
 * Returns the session handler.
999
 *
1000
 * @return WPInv_Session_Handler
1001
 * @since 1.0.19
1002
 */
1003
function getpaid_session() {
1004
    return getpaid()->get( 'session' );
1005
}
1006
1007
/**
1008
 * Returns the notes handler.
1009
 *
1010
 * @return WPInv_Notes
1011
 * @since 1.0.19
1012
 */
1013
function getpaid_notes() {
1014
    return getpaid()->get( 'notes' );
1015
}
1016
1017
/**
1018
 * Returns the main admin class.
1019
 *
1020
 * @return GetPaid_Admin
1021
 */
1022
function getpaid_admin() {
1023
    return getpaid()->get( 'admin' );
1024
}
1025
1026
/**
1027
 * Retrieves a URL to an authenticated action
1028
 *
1029
 * @param string $action
1030
 * @param string $base the base url
1031
 * @return string
1032
 */
1033
function getpaid_get_authenticated_action_url( $action, $base = false ) {
1034
    return wp_nonce_url( add_query_arg( 'getpaid-action', $action, $base ), 'getpaid-nonce', 'getpaid-nonce' );
1035
}
1036
1037
/**
1038
 * Returns a post type label.
1039
 *
1040
 * @return string
1041
 */
1042
function getpaid_get_post_type_label( $post_type, $plural = true ) {
1043
1044
    $post_type = get_post_type_object( $post_type );
1045
1046
    if ( ! is_object( $post_type ) ) {
1047
        return null;
1048
    }
1049
1050
    return $plural ? $post_type->labels->name : $post_type->labels->singular_name;
1051
1052
}
1053
1054
/**
1055
 * Retrieves an array
1056
 *
1057
 * @return mixed|null
1058
 */
1059
function getpaid_get_array_field( $array, $key, $secondary_key = null ) {
1060
1061
    if ( ! is_array( $array ) ) {
1062
        return null;
1063
    }
1064
1065
    if ( ! empty( $secondary_key ) ) {
1066
        $array = isset( $array[ $secondary_key ] ) ? $array[ $secondary_key ] : array();
1067
        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...
1068
    }
1069
1070
    return isset( $array[ $key ] ) ? $array[ $key ] : null;
1071
1072
}
1073
1074
/**
1075
 * Merges an empty array
1076
 *
1077
 * @return array
1078
 */
1079
function getpaid_array_merge_if_empty( $args, $defaults ) {
1080
1081
    foreach ( $defaults as $key => $value ) {
1082
1083
        if ( array_key_exists( $key, $args ) && empty( $args[ $key ] ) ) {
1084
            $args[ $key ] = $value;
1085
        }
1086
}
1087
1088
    return $args;
1089
1090
}
1091
1092
/**
1093
 * Returns allowed file types.
1094
 *
1095
 * @return array
1096
 */
1097
function getpaid_get_allowed_mime_types() {
1098
1099
    $types = get_allowed_mime_types();
1100
1101
    if ( isset( $types['htm|html'] ) ) {
1102
		unset( $types['htm|html'] );
1103
	}
1104
1105
    if ( isset( $types['js'] ) ) {
1106
		unset( $types['js'] );
1107
	}
1108
1109
    return $types;
1110
1111
}
1112
1113
1114
function getpaid_user_delete_invoice( $data ) {
1115
1116
    // Ensure there is an invoice to delete.
1117
    if ( empty( $data['invoice_id'] ) ) {
1118
        return;
1119
    }
1120
1121
    $invoice = new WPInv_Invoice( (int) $data['invoice_id'] );
1122
1123
    // Ensure that it exists and that it belongs to the current user.
1124
    if ( ! $invoice->exists() || $invoice->get_customer_id() != get_current_user_id() ) {
1125
        wpinv_set_error( 'invalid_invoice', __( 'You do not have permission to delete this invoice', 'invoicing' ) );
1126
1127
    // Can it be deleted?
1128
    } elseif ( ! $invoice->needs_payment() ) {
1129
        wpinv_set_error( 'cannot_delete', __( 'This invoice cannot be deleted as it has already been paid.', 'invoicing' ) );
1130
1131
    // Delete it.
1132
    } else {
1133
1134
        $invoice->delete();
1135
        wpinv_set_error( 'delete', __( 'The invoice has been deleted.', 'invoicing' ), 'info' );
1136
    }
1137
1138
    $redirect = remove_query_arg( array( 'getpaid-action', 'getpaid-nonce', 'invoice_id' ) );
1139
1140
    wp_safe_redirect( $redirect );
1141
    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...
1142
1143
}
1144
add_action( 'getpaid_authenticated_action_delete_invoice', 'getpaid_user_delete_invoice' );
1145