Passed
Push — master ( c24352...fabb71 )
by Brian
14:05 queued 08:38
created

getpaid_item_recurring_price_help_text()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 49
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 31
c 1
b 0
f 0
dl 0
loc 49
rs 9.1128
cc 5
nc 5
nop 2
1
<?php
2
// Exit if accessed directly
3
if ( ! defined( 'ABSPATH' ) ) exit;
4
5
function wpinv_get_item_by_id( $id ) {
6
    return wpinv_get_item_by( 'id', $id );
7
}
8
9
function wpinv_get_item_by( $field = '', $value = '', $type = '' ) {
10
    if( empty( $field ) || empty( $value ) ) {
11
        return false;
12
    }
13
    
14
    $posts = array();
15
16
    switch( strtolower( $field ) ) {
17
        case 'id':
18
            $item = new WPInv_Item( $value );
19
20
            if ( !empty( $item ) && $item->post_type == 'wpi_item' ) {
0 ignored issues
show
Bug Best Practice introduced by
The property post_type does not exist on WPInv_Item. Since you implemented __get, consider adding a @property annotation.
Loading history...
21
                return $item;
22
            }
23
            return false;
24
25
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
26
27
        case 'slug':
28
        case 'name':
29
            $posts = get_posts( array(
30
                'post_type'      => 'wpi_item',
31
                'name'           => $value,
32
                'posts_per_page' => 1,
33
                'post_status'    => 'any'
34
            ) );
35
36
            break;
37
        case 'custom_id':
38
            if ( empty( $value ) || empty( $type ) ) {
39
                return false;
40
            }
41
            
42
            $meta_query = array();
43
            $meta_query[] = array(
44
                'key'   => '_wpinv_type',
45
                'value' => $type,
46
            );
47
            $meta_query[] = array(
48
                'key'   => '_wpinv_custom_id',
49
                'value' => $value,
50
            );
51
            
52
            $args = array(
53
                'post_type'      => 'wpi_item',
54
                'posts_per_page' => 1,
55
                'post_status'    => 'any',
56
                'orderby'        => 'ID',
57
                'order'          => 'ASC',
58
                'meta_query'     => array( $meta_query )
59
            );
60
            
61
            $posts = get_posts( $args );
62
63
            break;
64
65
        default:
66
            return false;
67
    }
68
    
69
    if ( !empty( $posts[0] ) ) {
70
        $item = new WPInv_Item( $posts[0]->ID );
71
72
        if ( !empty( $item ) && $item->post_type == 'wpi_item' ) {
73
            return $item;
74
        }
75
    }
76
77
    return false;
78
}
79
80
function wpinv_get_item( $item = 0 ) {
81
    if ( is_numeric( $item ) ) {
82
        $item = get_post( $item );
83
        if ( ! $item || 'wpi_item' !== $item->post_type )
0 ignored issues
show
introduced by
$item is of type WP_Post, thus it always evaluated to true.
Loading history...
84
            return null;
85
        return $item;
86
    }
87
88
    $args = array(
89
        'post_type'   => 'wpi_item',
90
        'name'        => $item,
91
        'numberposts' => 1
92
    );
93
94
    $item = get_posts($args);
95
96
    if ( $item ) {
97
        return $item[0];
98
    }
99
100
    return null;
101
}
102
103
function wpinv_get_all_items( $args = array() ) {
104
105
    $args = wp_parse_args( $args, array(
106
        'status'         => array( 'publish' ),
107
        'limit'          => get_option( 'posts_per_page' ),
108
        'page'           => 1,
109
        'exclude'        => array(),
110
        'orderby'        => 'date',
111
        'order'          => 'DESC',
112
        'type'           => wpinv_item_types(),
113
        'meta_query'     => array(),
114
        'return'         => 'objects',
115
        'paginate'       => false,
116
    ) );
117
118
    $wp_query_args = array(
119
        'post_type'      => 'wpi_item',
120
        'post_status'    => $args['status'],
121
        'posts_per_page' => $args['limit'],
122
        'meta_query'     => $args['meta_query'],
123
        'fields'         => 'ids',
124
        'orderby'        => $args['orderby'],
125
        'order'          => $args['order'],
126
        'paged'          => absint( $args['page'] ),
127
    );
128
129
    if ( ! empty( $args['exclude'] ) ) {
130
        $wp_query_args['post__not_in'] = array_map( 'absint', $args['exclude'] );
131
    }
132
133
    if ( ! $args['paginate' ] ) {
134
        $wp_query_args['no_found_rows'] = true;
135
    }
136
137
    if ( ! empty( $args['search'] ) ) {
138
        $wp_query_args['s'] = $args['search'];
139
    }
140
141
    if ( ! empty( $args['type'] ) && $args['type'] !== wpinv_item_types() ) {
142
        $types = wpinv_parse_list( $args['type'] );
143
        $wp_query_args['meta_query'][] = array(
144
            'key'     => '_wpinv_type',
145
            'value'   => implode( ',', $types ),
146
            'compare' => 'IN',
147
        );
148
    }
149
150
    $wp_query_args = apply_filters('wpinv_get_items_args', $wp_query_args, $args);
151
152
    // Get results.
153
    $items = new WP_Query( $wp_query_args );
154
155
    if ( 'objects' === $args['return'] ) {
156
        $return = array_map( 'wpinv_get_item_by_id', $items->posts );
157
    } elseif ( 'self' === $args['return'] ) {
158
        return $items;
159
    } else {
160
        $return = $items->posts;
161
    }
162
163
    if ( $args['paginate' ] ) {
164
        return (object) array(
165
            'items'      => $return,
166
            'total'         => $items->found_posts,
167
            'max_num_pages' => $items->max_num_pages,
168
        );
169
    } else {
170
        return $return;
171
    }
172
173
}
174
175
function wpinv_is_free_item( $item_id = 0 ) {
176
    if( empty( $item_id ) ) {
177
        return false;
178
    }
179
180
    $item = new WPInv_Item( $item_id );
181
    
182
    return $item->is_free();
183
}
184
185
/**
186
 * Checks whether an item is editable.
187
 * 
188
 * @param WP_Post|WPInv_Item|Int $item The item to check for.
189
 */
190
function wpinv_item_is_editable( $item = 0 ) {
191
192
    // Fetch the item.
193
    $item = new WPInv_Item( $item );
194
195
    // Check if it is editable.
196
    return $item->is_editable();
197
}
198
199
function wpinv_get_item_price( $item_id = 0 ) {
200
    if( empty( $item_id ) ) {
201
        return false;
202
    }
203
204
    $item = new WPInv_Item( $item_id );
205
    
206
    return $item->get_price();
207
}
208
209
function wpinv_is_recurring_item( $item_id = 0 ) {
210
    if( empty( $item_id ) ) {
211
        return false;
212
    }
213
214
    $item = new WPInv_Item( $item_id );
215
    
216
    return $item->is_recurring();
217
}
218
219
function wpinv_item_price( $item_id = 0 ) {
220
    if( empty( $item_id ) ) {
221
        return false;
222
    }
223
224
    $price = wpinv_get_item_price( $item_id );
225
    $price = wpinv_price( wpinv_format_amount( $price ) );
226
    
227
    return apply_filters( 'wpinv_item_price', $price, $item_id );
228
}
229
230
function wpinv_item_show_price( $item_id = 0, $echo = true ) {
231
    if ( empty( $item_id ) ) {
232
        $item_id = get_the_ID();
233
    }
234
235
    $price = wpinv_item_price( $item_id );
236
237
    $price           = apply_filters( 'wpinv_item_price', wpinv_sanitize_amount( $price ), $item_id );
238
    $formatted_price = '<span class="wpinv_price" id="wpinv_item_' . $item_id . '">' . $price . '</span>';
239
    $formatted_price = apply_filters( 'wpinv_item_price_after_html', $formatted_price, $item_id, $price );
240
241
    if ( $echo ) {
242
        echo $formatted_price;
243
    } else {
244
        return $formatted_price;
245
    }
246
}
247
248
function wpinv_get_item_final_price( $item_id = 0, $amount_override = null ) {
249
    if ( is_null( $amount_override ) ) {
250
        $original_price = get_post_meta( $item_id, '_wpinv_price', true );
251
    } else {
252
        $original_price = $amount_override;
253
    }
254
    
255
    $price = $original_price;
256
257
    return apply_filters( 'wpinv_get_item_final_price', $price, $item_id );
258
}
259
260
function wpinv_item_custom_singular_name( $item_id ) {
261
    if( empty( $item_id ) ) {
262
        return false;
263
    }
264
265
    $item = new WPInv_Item( $item_id );
266
    
267
    return $item->get_custom_singular_name();
268
}
269
270
function wpinv_get_item_types() {
271
    $item_types = array(
272
            'custom'    => __( 'Standard', 'invoicing' ),
273
            'fee'       => __( 'Fee', 'invoicing' ),
274
        );
275
    return apply_filters( 'wpinv_get_item_types', $item_types );
276
}
277
278
function wpinv_item_types() {
279
    $item_types = wpinv_get_item_types();
280
    
281
    return ( !empty( $item_types ) ? array_keys( $item_types ) : array() );
282
}
283
284
function wpinv_get_item_type( $item_id ) {
285
    if( empty( $item_id ) ) {
286
        return false;
287
    }
288
289
    $item = new WPInv_Item( $item_id );
290
    
291
    return $item->get_type();
292
}
293
294
function wpinv_item_type( $item_id ) {
295
    $item_types = wpinv_get_item_types();
296
    
297
    $item_type = wpinv_get_item_type( $item_id );
298
    
299
    if ( empty( $item_type ) ) {
300
        $item_type = '-';
301
    }
302
    
303
    $item_type = isset( $item_types[$item_type] ) ? $item_types[$item_type] : __( $item_type, 'invoicing' );
304
305
    return apply_filters( 'wpinv_item_type', $item_type, $item_id );
306
}
307
308
function wpinv_record_item_in_log( $item_id = 0, $file_id, $user_info, $ip, $invoice_id ) {
309
    global $wpinv_logs;
310
    
311
    if ( empty( $wpinv_logs ) ) {
312
        return false;
313
    }
314
315
    $log_data = array(
316
        'post_parent'	=> $item_id,
317
        'log_type'		=> 'wpi_item'
318
    );
319
320
    $user_id = isset( $user_info['user_id'] ) ? $user_info['user_id'] : (int) -1;
321
322
    $log_meta = array(
323
        'user_info'	=> $user_info,
324
        'user_id'	=> $user_id,
325
        'file_id'	=> (int)$file_id,
326
        'ip'		=> $ip,
327
        'invoice_id'=> $invoice_id,
328
    );
329
330
    $wpinv_logs->insert_log( $log_data, $log_meta );
331
}
332
333
function wpinv_remove_item_logs_on_delete( $item_id = 0 ) {
334
    if ( 'wpi_item' !== get_post_type( $item_id ) )
335
        return;
336
337
    global $wpinv_logs;
338
    
339
    if ( empty( $wpinv_logs ) ) {
340
        return false;
341
    }
342
343
    // Remove all log entries related to this item
344
    $wpinv_logs->delete_logs( $item_id );
345
}
346
add_action( 'delete_post', 'wpinv_remove_item_logs_on_delete' );
347
348
function wpinv_get_random_item( $post_ids = true ) {
349
    wpinv_get_random_items( 1, $post_ids );
350
}
351
352
function wpinv_get_random_items( $num = 3, $post_ids = true ) {
353
    if ( $post_ids ) {
354
        $args = array( 'post_type' => 'wpi_item', 'orderby' => 'rand', 'post_count' => $num, 'fields' => 'ids' );
355
    } else {
356
        $args = array( 'post_type' => 'wpi_item', 'orderby' => 'rand', 'post_count' => $num );
357
    }
358
    
359
    $args  = apply_filters( 'wpinv_get_random_items', $args );
360
    
361
    return get_posts( $args );
362
}
363
364
function wpinv_get_item_token( $url = '' ) {
365
    $args    = array();
366
    $hash    = apply_filters( 'wpinv_get_url_token_algorithm', 'sha256' );
367
    $secret  = apply_filters( 'wpinv_get_url_token_secret', hash( $hash, wp_salt() ) );
368
369
    $parts   = parse_url( $url );
370
    $options = array();
371
372
    if ( isset( $parts['query'] ) ) {
373
        wp_parse_str( $parts['query'], $query_args );
374
375
        if ( ! empty( $query_args['o'] ) ) {
376
            $options = explode( ':', rawurldecode( $query_args['o'] ) );
377
378
            if ( in_array( 'ip', $options ) ) {
379
                $args['ip'] = wpinv_get_ip();
380
            }
381
382
            if ( in_array( 'ua', $options ) ) {
383
                $ua = wpinv_get_user_agent();
384
                $args['user_agent'] = rawurlencode( $ua );
385
            }
386
        }
387
    }
388
389
    $args = apply_filters( 'wpinv_get_url_token_args', $args, $url, $options );
390
391
    $args['secret'] = $secret;
392
    $args['token']  = false;
393
394
    $url   = add_query_arg( $args, $url );
395
    $parts = parse_url( $url );
396
397
    if ( ! isset( $parts['path'] ) ) {
398
        $parts['path'] = '';
399
    }
400
401
    $token = md5( $parts['path'] . '?' . $parts['query'] );
402
403
    return $token;
404
}
405
406
function wpinv_validate_url_token( $url = '' ) {
407
    $ret   = false;
408
    $parts = parse_url( $url );
409
410
    if ( isset( $parts['query'] ) ) {
411
        wp_parse_str( $parts['query'], $query_args );
412
413
        $allowed = apply_filters( 'wpinv_url_token_allowed_params', array(
414
            'item',
415
            'ttl',
416
            'token'
417
        ) );
418
419
        $remove = array();
420
421
        foreach( $query_args as $key => $value ) {
422
            if( false === in_array( $key, $allowed ) ) {
423
                $remove[] = $key;
424
            }
425
        }
426
427
        if( ! empty( $remove ) ) {
428
            $url = remove_query_arg( $remove, $url );
429
        }
430
431
        if ( isset( $query_args['ttl'] ) && current_time( 'timestamp' ) > $query_args['ttl'] ) {
432
            wp_die( apply_filters( 'wpinv_item_link_expired_text', __( 'Sorry but your item link has expired.', 'invoicing' ) ), __( 'Error', 'invoicing' ), array( 'response' => 403 ) );
433
        }
434
435
        if ( isset( $query_args['token'] ) && $query_args['token'] == wpinv_get_item_token( $url ) ) {
436
            $ret = true;
437
        }
438
439
    }
440
441
    return apply_filters( 'wpinv_validate_url_token', $ret, $url, $query_args );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $query_args does not seem to be defined for all execution paths leading up to this point.
Loading history...
442
}
443
444
function wpinv_item_in_cart( $item_id = 0, $options = array() ) {
445
    $cart_items = wpinv_get_cart_contents();
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_cart_contents() has been deprecated. ( Ignorable by Annotation )

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

445
    $cart_items = /** @scrutinizer ignore-deprecated */ wpinv_get_cart_contents();
Loading history...
446
447
    $ret = false;
448
449
    if ( is_array( $cart_items ) ) {
0 ignored issues
show
introduced by
The condition is_array($cart_items) is always true.
Loading history...
450
        foreach ( $cart_items as $item ) {
451
            if ( $item['id'] == $item_id ) {
452
                $ret = true;
453
                break;
454
            }
455
        }
456
    }
457
458
    return (bool) apply_filters( 'wpinv_item_in_cart', $ret, $item_id, $options );
459
}
460
461
function wpinv_get_cart_item_tax( $item_id = 0, $subtotal = '', $options = array() ) {
462
    $tax = 0;
463
    if ( ! wpinv_item_is_tax_exclusive( $item_id ) ) {
464
        $country = !empty( $_POST['country'] ) ? $_POST['country'] : false;
465
        $state   = isset( $_POST['state'] ) ? $_POST['state'] : '';
466
467
        $tax = wpinv_calculate_tax( $subtotal, $country, $state, $item_id );
468
    }
469
470
    return apply_filters( 'wpinv_get_cart_item_tax', $tax, $item_id, $subtotal, $options );
471
}
472
473
function wpinv_cart_item_price( $item, $currency = '' ) {
474
475
    if( empty( $currency ) ) {
476
        $currency = wpinv_get_currency();
477
    }
478
479
    $item_id    = isset( $item['id'] ) ? $item['id'] : 0;
480
    $price      = isset( $item['item_price'] ) ? wpinv_round_amount( $item['item_price'] ) : 0;
481
    $tax        = wpinv_price( wpinv_format_amount( $item['tax'] ) );
482
    
483
    if ( !wpinv_is_free_item( $item_id ) && !wpinv_item_is_tax_exclusive( $item_id ) ) {
484
        if ( wpinv_prices_show_tax_on_checkout() && !wpinv_prices_include_tax() ) {
485
            $price += $tax;
486
        }
487
        
488
        if( !wpinv_prices_show_tax_on_checkout() && wpinv_prices_include_tax() ) {
489
            $price -= $tax;
490
        }        
491
    }
492
493
    $price = wpinv_price( wpinv_format_amount( $price ), $currency );
494
495
    return apply_filters( 'wpinv_cart_item_price_label', $price, $item );
496
}
497
498
function wpinv_cart_item_subtotal( $item, $currency = '' ) {
499
500
    if( empty( $currency ) ) {
501
        $currency = wpinv_get_currency();
502
    }
503
504
    $subtotal   = isset( $item['subtotal'] ) ? $item['subtotal'] : 0;
505
    $subtotal   = wpinv_price( wpinv_format_amount( $subtotal ), $currency );
506
507
    return apply_filters( 'wpinv_cart_item_subtotal_label', $subtotal, $item );
508
}
509
510
function wpinv_cart_item_tax( $item, $currency = '' ) {
511
    $tax        = '';
512
    $tax_rate   = '';
513
514
    if( empty( $currency ) ) {
515
        $currency = wpinv_get_currency();
516
    }
517
    
518
    if ( isset( $item['tax'] ) && $item['tax'] > 0 && $item['subtotal'] > 0 ) {
519
        $tax      = wpinv_price( wpinv_format_amount( $item['tax'] ), $currency );
520
        $tax_rate = !empty( $item['vat_rate'] ) ? $item['vat_rate'] : ( $item['tax'] / $item['subtotal'] ) * 100;
521
        $tax_rate = $tax_rate > 0 ? (float)wpinv_round_amount( $tax_rate, 4 ) : '';
522
        $tax_rate = $tax_rate != '' ? ' <small class="tax-rate normal small">(' . $tax_rate . '%)</small>' : '';
523
    }
524
    
525
    $tax        = $tax . $tax_rate;
526
    
527
    if ( $tax === '' ) {
528
        $tax = 0; // Zero tax
529
    }
530
531
    return apply_filters( 'wpinv_cart_item_tax_label', $tax, $item );
532
}
533
534
function wpinv_get_cart_item_price( $item_id = 0, $cart_item = array(), $options = array(), $remove_tax_from_inclusive = false ) {
535
    $price = 0;
536
    
537
    // Set custom price
538
    if ( isset( $cart_item['custom_price'] ) && $cart_item['custom_price'] !== '' ) {
539
        $price = $cart_item['custom_price'];
540
    } else {
541
        $variable_prices = wpinv_has_variable_prices( $item_id );
542
543
        if ( $variable_prices ) {
0 ignored issues
show
introduced by
The condition $variable_prices is always false.
Loading history...
544
            $prices = wpinv_get_variable_prices( $item_id );
0 ignored issues
show
Bug introduced by
The function wpinv_get_variable_prices was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

544
            $prices = /** @scrutinizer ignore-call */ wpinv_get_variable_prices( $item_id );
Loading history...
545
546
            if ( $prices ) {
547
                if( ! empty( $options ) ) {
548
                    $price = isset( $prices[ $options['price_id'] ] ) ? $prices[ $options['price_id'] ]['amount'] : false;
549
                } else {
550
                    $price = false;
551
                }
552
            }
553
        }
554
555
        if( ! $variable_prices || false === $price ) {
0 ignored issues
show
introduced by
The condition $variable_prices is always false.
Loading history...
556
            if($cart_item['item_price'] > 0){
557
                $price = $cart_item['item_price'];
558
            } else {
559
                // Get the standard Item price if not using variable prices
560
                $price = wpinv_get_item_price( $item_id );
561
            }
562
        }
563
    }
564
565
    if ( $remove_tax_from_inclusive && wpinv_prices_include_tax() ) {
566
        $price -= wpinv_get_cart_item_tax( $item_id, $price, $options );
567
    }
568
569
    return apply_filters( 'wpinv_cart_item_price', $price, $item_id, $cart_item, $options, $remove_tax_from_inclusive );
570
}
571
572
function wpinv_get_cart_item_price_id( $item = array() ) {
573
    if( isset( $item['item_number'] ) ) {
574
        $price_id = isset( $item['item_number']['options']['price_id'] ) ? $item['item_number']['options']['price_id'] : null;
575
    } else {
576
        $price_id = isset( $item['options']['price_id'] ) ? $item['options']['price_id'] : null;
577
    }
578
    return $price_id;
579
}
580
581
function wpinv_get_cart_item_price_name( $item = array() ) {
582
    $price_id = (int)wpinv_get_cart_item_price_id( $item );
583
    $prices   = wpinv_get_variable_prices( $item['id'] );
0 ignored issues
show
Bug introduced by
The function wpinv_get_variable_prices was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

583
    $prices   = /** @scrutinizer ignore-call */ wpinv_get_variable_prices( $item['id'] );
Loading history...
584
    $name     = ! empty( $prices[ $price_id ] ) ? $prices[ $price_id ]['name'] : '';
585
    return apply_filters( 'wpinv_get_cart_item_price_name', $name, $item['id'], $price_id, $item );
586
}
587
588
function wpinv_get_cart_item_name( $item = array() ) {
589
    $item_title = !empty( $item['name'] ) ? $item['name'] : get_the_title( $item['id'] );
590
591
    if ( empty( $item_title ) ) {
592
        $item_title = $item['id'];
593
    }
594
595
    /*
596
    if ( wpinv_has_variable_prices( $item['id'] ) && false !== wpinv_get_cart_item_price_id( $item ) ) {
597
        $item_title .= ' - ' . wpinv_get_cart_item_price_name( $item );
598
    }
599
    */
600
601
    return apply_filters( 'wpinv_get_cart_item_name', $item_title, $item['id'], $item );
602
}
603
604
function wpinv_has_variable_prices( $item_id = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $item_id 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

604
function wpinv_has_variable_prices( /** @scrutinizer ignore-unused */ $item_id = 0 ) {

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...
605
    return false;
606
}
607
608
function wpinv_get_item_position_in_cart( $item_id = 0, $options = array() ) {
609
    $cart_items = wpinv_get_cart_contents();
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_cart_contents() has been deprecated. ( Ignorable by Annotation )

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

609
    $cart_items = /** @scrutinizer ignore-deprecated */ wpinv_get_cart_contents();
Loading history...
610
611
    if ( !is_array( $cart_items ) ) {
0 ignored issues
show
introduced by
The condition is_array($cart_items) is always true.
Loading history...
612
        return false; // Empty cart
613
    } else {
614
        foreach ( $cart_items as $position => $item ) {
615
            if ( $item['id'] == $item_id ) {
616
                if ( isset( $options['price_id'] ) && isset( $item['options']['price_id'] ) ) {
617
                    if ( (int) $options['price_id'] == (int) $item['options']['price_id'] ) {
618
                        return $position;
619
                    }
620
                } else {
621
                    return $position;
622
                }
623
            }
624
        }
625
    }
626
627
    return false; // Not found
628
}
629
630
function wpinv_get_cart_item_quantity( $item ) {
631
    if ( wpinv_item_quantities_enabled() ) {
632
        $quantity = !empty( $item['quantity'] ) && (int)$item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
633
    } else {
634
        $quantity = 1;
635
    }
636
    
637
    if ( $quantity < 1 ) {
638
        $quantity = 1;
639
    }
640
    
641
    return apply_filters( 'wpinv_get_cart_item_quantity', $quantity, $item );
642
}
643
644
function wpinv_get_item_suffix( $item, $html = true ) {
645
    if ( empty( $item ) ) {
646
        return NULL;
647
    }
648
    
649
    if ( is_int( $item ) ) {
650
        $item = new WPInv_Item( $item );
651
    }
652
    
653
    if ( !( is_object( $item ) && is_a( $item, 'WPInv_Item' ) ) ) {
654
        return NULL;
655
    }
656
    
657
    $suffix = $item->is_recurring() ? ' <span class="wpi-suffix">' . __( '(r)', 'invoicing' ) . '</span>' : '';
658
    
659
    if ( !$html && $suffix ) {
660
        $suffix = strip_tags( $suffix );
661
    }
662
    
663
    return apply_filters( 'wpinv_get_item_suffix', $suffix, $item, $html );
664
}
665
666
function wpinv_remove_item( $item = 0, $force_delete = false ) {
667
    if ( empty( $item ) ) {
668
        return NULL;
669
    }
670
    
671
    if ( is_int( $item ) ) {
672
        $item = new WPInv_Item( $item );
673
    }
674
    
675
    if ( !( is_object( $item ) && is_a( $item, 'WPInv_Item' ) ) ) {
676
        return NULL;
677
    }
678
    
679
    do_action( 'wpinv_pre_delete_item', $item );
680
681
    wp_delete_post( $item->ID, $force_delete );
682
683
    do_action( 'wpinv_post_delete_item', $item );
684
}
685
686
function wpinv_can_delete_item( $post_id ) {
687
    $return = wpinv_current_user_can_manage_invoicing() ? true : false;
688
    
689
    if ( $return && wpinv_item_in_use( $post_id ) ) {
690
        $return = false; // Don't delete item already use in invoices.
691
    }
692
    
693
    return apply_filters( 'wpinv_can_delete_item', $return, $post_id );
694
}
695
696
function wpinv_admin_action_delete() {
697
    $screen = get_current_screen();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $screen is correct as get_current_screen() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
698
    
699
    if ( !empty( $screen->post_type ) && $screen->post_type == 'wpi_item' && !empty( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] ) ) {
700
        $post_ids = array();
701
        
702
        foreach ( $_REQUEST['post'] as $post_id ) {
703
            if ( !wpinv_can_delete_item( $post_id ) ) {
704
                continue;
705
            }
706
            
707
            $post_ids[] = $post_id;
708
        }
709
        
710
        $_REQUEST['post'] = $post_ids;
711
    }
712
}
713
add_action( 'admin_action_trash', 'wpinv_admin_action_delete', -10 );
714
add_action( 'admin_action_delete', 'wpinv_admin_action_delete', -10 );
715
716
function wpinv_check_delete_item( $check, $post, $force_delete ) {
717
    if ( $post->post_type == 'wpi_item' ) {
718
        if ( $force_delete && !wpinv_can_delete_item( $post->ID ) ) {
719
            return true;
720
        }
721
    }
722
    
723
    return $check;
724
}
725
add_filter( 'pre_delete_post', 'wpinv_check_delete_item', 10, 3 );
726
727
function wpinv_item_in_use( $item_id ) {
728
    global $wpdb, $wpi_items_in_use;
729
    
730
    if ( !$item_id > 0 ) {
731
        return false;
732
    }
733
    
734
    if ( !empty( $wpi_items_in_use ) ) {
735
        if ( isset( $wpi_items_in_use[$item_id] ) ) {
736
            return $wpi_items_in_use[$item_id];
737
        }
738
    } else {
739
        $wpi_items_in_use = array();
740
    }
741
    
742
    $statuses   = array_keys( wpinv_get_invoice_statuses( true, true ) );
743
    
744
    $query  = "SELECT p.ID FROM " . $wpdb->posts . " AS p INNER JOIN " . $wpdb->postmeta . " AS pm ON p.ID = pm.post_id WHERE p.post_type = 'wpi_invoice' AND p.post_status IN( '" . implode( "','", $statuses ) . "' ) AND pm.meta_key = '_wpinv_item_ids' AND FIND_IN_SET( '" . (int)$item_id . "', pm.meta_value )";
745
    $in_use = $wpdb->get_var( $query ) > 0 ? true : false;
746
    
747
    $wpi_items_in_use[$item_id] = $in_use;
748
    
749
    return $in_use;
750
}
751
752
function wpinv_create_item( $args = array(), $wp_error = false, $force_update = false ) {
753
    // Set some defaults
754
    $defaults = array(
755
        'type'                 => 'custom',                                                // Optional. Item type. Default 'custom'.
756
        'title'                => '',                                                      // Required. Item title.
757
        'custom_id'            => 0,                                                       // Optional. Any integer or non numeric id. Must be unique within item type.
758
        'price'                => '0.00',                                                  // Optional. Item price. Default '0.00'.
759
        'status'               => 'pending',                                               // Optional. pending, publish
760
        'custom_name'          => '',                                                      // Optional. Plural sub title for item.
761
        'custom_singular_name' => '',                                                      // Optional. Singular sub title for item.
762
        'vat_rule'             => 'digital',                                               // Optional. digital => Digital item, physical => Physical item
763
        'editable'             => true,                                                    // Optional. Item editable from Items list page? Default true.
764
        'excerpt'              => '',                                                      // Optional. Item short description
765
        /* Recurring item fields */
766
        'is_recurring'         => 0,                                                       // Optional. 1 => Allow recurring or 0 => Don't allow recurring
767
        'recurring_period'     => 'M',                                                     // Optional. D => Daily, W => Weekly, M => Monthly, Y => Yearly
768
        'recurring_interval'   => 0,                                                       // Optional. Integer value between 1 - 90.
769
        'recurring_limit'      => 0,                                                       // Optional. Any integer number. 0 for recurring forever until cancelled.
770
        'free_trial'           => 0,                                                       // Optional. 1 => Allow free trial or 0 => Don't free trial
771
        'trial_period'         => 'M',                                                     // Optional. D => Daily, W => Weekly, M => Monthly, Y => Yearly
772
        'trial_interval'       => 0,                                                       // Optional. Any integer number.
773
        'minimum_price'        => '0.00',                                                  // Optional. Minimum allowed prices for items with dynamic pricing.
774
        'dynamic_pricing'      => 0,                                                       // Optional. Whether or not the item supports dynamic prices.
775
    );
776
777
    $data = wp_parse_args( $args, $defaults );
778
779
    if ( empty( $data['type'] ) ) {
780
        $data['type'] = 'custom';
781
    }
782
783
    if ( !empty( $data['custom_id'] ) ) {
784
        $item = wpinv_get_item_by( 'custom_id', $data['custom_id'], $data['type'] );
785
    } else {
786
        $item = NULL;
787
    }
788
789
    if ( !empty( $item ) ) {
790
        if ( $force_update ) {
791
            if ( empty( $args['ID'] ) ) {
792
                $args['ID'] = $item->ID;
793
            }
794
            return wpinv_update_item( $args, $wp_error );
795
        }
796
797
        return $item;
798
    }
799
800
    $meta                           = array();
801
    $meta['type']                   = $data['type'];
802
    $meta['custom_id']              = $data['custom_id'];
803
    $meta['custom_singular_name']   = $data['custom_singular_name'];
804
    $meta['custom_name']            = $data['custom_name'];
805
    $meta['price']                  = wpinv_round_amount( $data['price'] );
806
    $meta['editable']               = (int)$data['editable'];
807
    $meta['vat_rule']               = $data['vat_rule'];
808
    $meta['vat_class']              = '_standard';
809
    $meta['dynamic_pricing']        = (int) $data['dynamic_pricing'];
810
    $meta['minimum_price']          = wpinv_round_amount( $data['minimum_price'] );
811
    
812
    if ( !empty( $data['is_recurring'] ) ) {
813
        $meta['is_recurring']       = $data['is_recurring'];
814
        $meta['recurring_period']   = $data['recurring_period'];
815
        $meta['recurring_interval'] = absint( $data['recurring_interval'] );
816
        $meta['recurring_limit']    = absint( $data['recurring_limit'] );
817
        $meta['free_trial']         = $data['free_trial'];
818
        $meta['trial_period']       = $data['trial_period'];
819
        $meta['trial_interval']     = absint( $data['trial_interval'] );
820
    } else {
821
        $meta['is_recurring']       = 0;
822
        $meta['recurring_period']   = '';
823
        $meta['recurring_interval'] = '';
824
        $meta['recurring_limit']    = '';
825
        $meta['free_trial']         = 0;
826
        $meta['trial_period']       = '';
827
        $meta['trial_interval']     = '';
828
    }
829
    
830
    $post_data  = array( 
831
        'post_title'    => $data['title'],
832
        'post_excerpt'  => $data['excerpt'],
833
        'post_status'   => $data['status'],
834
        'meta'          => $meta
835
    );
836
837
    $item = new WPInv_Item();
838
    $return = $item->create( $post_data, $wp_error );
0 ignored issues
show
Deprecated Code introduced by
The function WPInv_Item::create() has been deprecated. ( Ignorable by Annotation )

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

838
    $return = /** @scrutinizer ignore-deprecated */ $item->create( $post_data, $wp_error );
Loading history...
Unused Code introduced by
The call to WPInv_Item::create() has too many arguments starting with $wp_error. ( Ignorable by Annotation )

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

838
    /** @scrutinizer ignore-call */ 
839
    $return = $item->create( $post_data, $wp_error );

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...
839
840
    if ( $return && !empty( $item ) && !is_wp_error( $return ) ) {
841
        return $item;
842
    }
843
844
    if ( $wp_error && is_wp_error( $return ) ) {
845
        return $return;
846
    }
847
    return 0;
848
}
849
850
function wpinv_update_item( $args = array(), $wp_error = false ) {
851
    $item = !empty( $args['ID'] ) ? new WPInv_Item( $args['ID'] ) : NULL;
852
853
    if ( empty( $item ) || !( !empty( $item->post_type ) && $item->post_type == 'wpi_item' ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property post_type does not exist on WPInv_Item. Since you implemented __get, consider adding a @property annotation.
Loading history...
854
        if ( $wp_error ) {
855
            return new WP_Error( 'wpinv_invalid_item', __( 'Invalid item.', 'invoicing' ) );
856
        }
857
        return 0;
858
    }
859
    
860
    if ( !empty( $args['custom_id'] ) ) {
861
        $item_exists = wpinv_get_item_by( 'custom_id', $args['custom_id'], ( !empty( $args['type'] ) ? $args['type'] : $item->type ) );
0 ignored issues
show
Bug Best Practice introduced by
The property type does not exist on WPInv_Item. Since you implemented __get, consider adding a @property annotation.
Loading history...
862
        
863
        if ( !empty( $item_exists ) && $item_exists->ID != $args['ID'] ) {
864
            if ( $wp_error ) {
865
                return new WP_Error( 'wpinv_invalid_custom_id', __( 'Item with custom id already exists.', 'invoicing' ) );
866
            }
867
            return 0;
868
        }
869
    }
870
871
    $meta_fields = array( 
872
        'type', 
873
        'custom_id', 
874
        'custom_singular_name', 
875
        'custom_name', 
876
        'price', 
877
        'editable', 
878
        'vat_rule', 
879
        'vat_class', 
880
        'is_recurring', 
881
        'recurring_period', 
882
        'recurring_interval', 
883
        'recurring_limit', 
884
        'free_trial', 
885
        'trial_period', 
886
        'trial_interval', 
887
        'minimum_price', 
888
        'dynamic_pricing'
889
    );
890
891
    $post_data = array();
892
    if ( isset( $args['title'] ) ) { 
893
        $post_data['post_title'] = $args['title'];
894
    }
895
    if ( isset( $args['excerpt'] ) ) { 
896
        $post_data['post_excerpt'] = $args['excerpt'];
897
    }
898
    if ( isset( $args['status'] ) ) { 
899
        $post_data['post_status'] = $args['status'];
900
    }
901
    
902
    foreach ( $meta_fields as $meta_field ) {
903
        if ( isset( $args[ $meta_field ] ) ) { 
904
            $value = $args[ $meta_field ];
905
906
            switch ( $meta_field ) {
907
                case 'price':
908
                case 'minimum_price':
909
                    $value = wpinv_round_amount( $value );
910
                break;
911
                case 'recurring_interval':
912
                case 'recurring_limit':
913
                case 'trial_interval':
914
                    $value = absint( $value );
915
                break;
916
				case 'editable':
917
                    $value = (int) $value;
918
                break;
919
            }
920
921
            $post_data['meta'][ $meta_field ] = $value;
922
        };
923
    }
924
925
    if ( empty( $post_data ) ) {
926
        if ( $wp_error ) {
927
            return new WP_Error( 'wpinv_invalid_item_data', __( 'Invalid item data.', 'invoicing' ) );
928
        }
929
        return 0;
930
    }
931
    $post_data['ID'] = $args['ID'];
932
933
    $return = $item->update( $post_data, $wp_error );
0 ignored issues
show
Deprecated Code introduced by
The function WPInv_Item::update() has been deprecated. ( Ignorable by Annotation )

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

933
    $return = /** @scrutinizer ignore-deprecated */ $item->update( $post_data, $wp_error );
Loading history...
Unused Code introduced by
The call to WPInv_Item::update() has too many arguments starting with $wp_error. ( Ignorable by Annotation )

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

933
    /** @scrutinizer ignore-call */ 
934
    $return = $item->update( $post_data, $wp_error );

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...
934
935
    if ( $return && !empty( $item ) && !is_wp_error( $return ) ) {
936
        return $item;
937
    }
938
939
    if ( $wp_error && is_wp_error( $return ) ) {
940
        return $return;
941
    }
942
    return 0;
943
}
944
945
/**
946
 * Sanitizes a recurring period
947
 */
948
function getpaid_sanitize_recurring_period( $period, $full = false ) {
949
950
    $periods = array(
951
        'D' => 'day',
952
        'W' => 'week',
953
        'M' => 'month',
954
        'Y' => 'year',
955
    );
956
957
    if ( ! isset( $periods[ $period ] ) ) {
958
        $period = 'D';
959
    }
960
961
    return $full ? $periods[ $period ] : $period;
962
963
}
964
965
/**
966
 * Retrieves recurring price description.
967
 * 
968
 * @param WPInv_Item $item
969
 */
970
function getpaid_item_recurring_price_help_text( $item, $currency = '' ) {
971
972
    // Abort if it is not recurring.
973
    if ( ! $item->is_recurring() ) {
974
        return '';
975
    }
976
977
    $initial_price   = wpinv_price( wpinv_sanitize_amount( $item->get_initial_price() ), $currency );
978
    $recurring_price = wpinv_price( wpinv_sanitize_amount( $item->get_recurring_price() ), $currency );
979
    $period          = WPInv_Subscriptions::wpinv_get_pretty_subscription_frequency( $item->get_recurring_period(), $item->get_recurring_interval() );
980
    $period          = str_replace( '1 ', '', strtolower( $period ) );
981
982
    // For free trial items.
983
    if ( $item->has_free_trial() ) {
984
        $trial_period = WPInv_Subscriptions::wpinv_get_pretty_subscription_frequency( $item->get_trial_period(), $item->get_trial_interval() );
985
986
        if ( 0 == $item->get_initial_price() ) {
987
            return sprintf(
988
                __( 'Free for %s then %s every %s', 'invoicing' ),
989
                strtolower( $trial_period ),
990
                $recurring_price,
991
                $period
992
            );
993
        }
994
995
        return sprintf(
996
            __( '%s for %s then %s every %s', 'invoicing' ),
997
            $initial_price,
998
            strtolower( $trial_period ),
999
            $recurring_price,
1000
            $period
1001
        );
1002
1003
    }
1004
1005
    if ( $initial_price == $recurring_price ) {
1006
        return sprintf(
1007
            __( '%s every %s', 'invoicing' ),
1008
            $recurring_price,
1009
            $period
1010
        );
1011
    }
1012
1013
    return sprintf(
1014
        __( '%s for %s then %s every %s', 'invoicing' ),
1015
        $initial_price,
1016
        $period,
1017
        $recurring_price,
1018
        $period
1019
    );
1020
1021
}
1022