Passed
Push — master ( 3bda5e...50908e )
by
unknown
03:36 queued 12s
created

wpinv-item-functions.php ➔ wpinv_get_all_items()   B

Complexity

Conditions 9
Paths 80

Size

Total Lines 71

Duplication

Lines 19
Ratio 26.76 %

Importance

Changes 0
Metric Value
cc 9
nc 80
nop 1
dl 19
loc 71
rs 7.0771
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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();
0 ignored issues
show
Unused Code introduced by
$posts is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
15
16
    switch( strtolower( $field ) ) {
17
        case 'id':
18
            $item = new WPInv_Item( $value );
0 ignored issues
show
Documentation introduced by
$value is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
19
20
            if ( !empty( $item ) && $item->post_type == 'wpi_item' ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

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' ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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 )
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 View Code Duplication
    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 View Code Duplication
    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 View Code Duplication
    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 );
0 ignored issues
show
Documentation introduced by
$item_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
181
    
182
    return $item->is_free();
183
}
184
185
function wpinv_item_is_editable( $item = 0 ) {
186
    if ( !empty( $item ) && is_a( $item, 'WP_Post' ) ) {
187
        $item = $item->ID;
188
    }
189
        
190
    if ( empty( $item ) ) {
191
        return true;
192
    }
193
194
    $item = new WPInv_Item( $item );
195
    
196
    return (bool) $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 );
0 ignored issues
show
Documentation introduced by
$item_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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 );
0 ignored issues
show
Documentation introduced by
$item_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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 );
0 ignored issues
show
Bug introduced by
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
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 );
0 ignored issues
show
Bug introduced by
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
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 );
442
}
443
444
function wpinv_item_in_cart( $item_id = 0, $options = array() ) {
445
    $cart_items = wpinv_get_cart_contents();
446
447
    $ret = false;
448
449
    if ( is_array( $cart_items ) ) {
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 ) {
474
    $item_id    = isset( $item['id'] ) ? $item['id'] : 0;
475
    $price      = isset( $item['item_price'] ) ? wpinv_round_amount( $item['item_price'] ) : 0;
476
    $tax        = wpinv_price( wpinv_format_amount( $item['tax'] ) );
477
    
478
    if ( !wpinv_is_free_item( $item_id ) && !wpinv_item_is_tax_exclusive( $item_id ) ) {
479
        if ( wpinv_prices_show_tax_on_checkout() && !wpinv_prices_include_tax() ) {
480
            $price += $tax;
481
        }
482
        
483
        if( !wpinv_prices_show_tax_on_checkout() && wpinv_prices_include_tax() ) {
484
            $price -= $tax;
485
        }        
486
    }
487
488
    $price = wpinv_price( wpinv_format_amount( $price ) );
489
490
    return apply_filters( 'wpinv_cart_item_price_label', $price, $item );
491
}
492
493
function wpinv_cart_item_subtotal( $item ) {
494
    $subtotal   = isset( $item['subtotal'] ) ? $item['subtotal'] : 0;
495
    $subtotal   = wpinv_price( wpinv_format_amount( $subtotal ) );
496
497
    return apply_filters( 'wpinv_cart_item_subtotal_label', $subtotal, $item );
498
}
499
500
function wpinv_cart_item_tax( $item ) {
501
    $tax        = '';
502
    $tax_rate   = '';
503
    
504 View Code Duplication
    if ( isset( $item['tax'] ) && $item['tax'] > 0 && $item['subtotal'] > 0 ) {
505
        $tax      = wpinv_price( wpinv_format_amount( $item['tax'] ) );
506
        $tax_rate = !empty( $item['vat_rate'] ) ? $item['vat_rate'] : ( $item['tax'] / $item['subtotal'] ) * 100;
507
        $tax_rate = $tax_rate > 0 ? (float)wpinv_round_amount( $tax_rate, 4 ) : '';
508
        $tax_rate = $tax_rate != '' ? ' <small class="tax-rate normal small">(' . $tax_rate . '%)</small>' : '';
509
    }
510
    
511
    $tax        = $tax . $tax_rate;
512
    
513
    if ( $tax === '' ) {
514
        $tax = 0; // Zero tax
515
    }
516
517
    return apply_filters( 'wpinv_cart_item_tax_label', $tax, $item );
518
}
519
520
function wpinv_get_cart_item_price( $item_id = 0, $cart_item = array(), $options = array(), $remove_tax_from_inclusive = false ) {
521
    $price = 0;
522
    
523
    // Set custom price
524
    if ( isset( $cart_item['custom_price'] ) && $cart_item['custom_price'] !== '' ) {
525
        $price = $cart_item['custom_price'];
526
    } else {
527
        $variable_prices = wpinv_has_variable_prices( $item_id );
528
529
        if ( $variable_prices ) {
530
            $prices = wpinv_get_variable_prices( $item_id );
531
532
            if ( $prices ) {
533
                if( ! empty( $options ) ) {
534
                    $price = isset( $prices[ $options['price_id'] ] ) ? $prices[ $options['price_id'] ]['amount'] : false;
535
                } else {
536
                    $price = false;
537
                }
538
            }
539
        }
540
541
        if( ! $variable_prices || false === $price ) {
542
            if($cart_item['item_price'] > 0){
543
                $price = $cart_item['item_price'];
544
            } else {
545
                // Get the standard Item price if not using variable prices
546
                $price = wpinv_get_item_price( $item_id );
547
            }
548
        }
549
    }
550
551
    if ( $remove_tax_from_inclusive && wpinv_prices_include_tax() ) {
552
        $price -= wpinv_get_cart_item_tax( $item_id, $price, $options );
553
    }
554
555
    return apply_filters( 'wpinv_cart_item_price', $price, $item_id, $cart_item, $options, $remove_tax_from_inclusive );
556
}
557
558
function wpinv_get_cart_item_price_id( $item = array() ) {
559
    if( isset( $item['item_number'] ) ) {
560
        $price_id = isset( $item['item_number']['options']['price_id'] ) ? $item['item_number']['options']['price_id'] : null;
561
    } else {
562
        $price_id = isset( $item['options']['price_id'] ) ? $item['options']['price_id'] : null;
563
    }
564
    return $price_id;
565
}
566
567
function wpinv_get_cart_item_price_name( $item = array() ) {
568
    $price_id = (int)wpinv_get_cart_item_price_id( $item );
569
    $prices   = wpinv_get_variable_prices( $item['id'] );
570
    $name     = ! empty( $prices[ $price_id ] ) ? $prices[ $price_id ]['name'] : '';
571
    return apply_filters( 'wpinv_get_cart_item_price_name', $name, $item['id'], $price_id, $item );
572
}
573
574
function wpinv_get_cart_item_name( $item = array() ) {
575
    $item_title = !empty( $item['name'] ) ? $item['name'] : get_the_title( $item['id'] );
576
577
    if ( empty( $item_title ) ) {
578
        $item_title = $item['id'];
579
    }
580
581
    /*
582
    if ( wpinv_has_variable_prices( $item['id'] ) && false !== wpinv_get_cart_item_price_id( $item ) ) {
583
        $item_title .= ' - ' . wpinv_get_cart_item_price_name( $item );
584
    }
585
    */
586
587
    return apply_filters( 'wpinv_get_cart_item_name', $item_title, $item['id'], $item );
588
}
589
590
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.

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

Loading history...
591
    return false;
592
}
593
594
function wpinv_get_item_position_in_cart( $item_id = 0, $options = array() ) {
595
    $cart_items = wpinv_get_cart_contents();
596
597
    if ( !is_array( $cart_items ) ) {
598
        return false; // Empty cart
599
    } else {
600
        foreach ( $cart_items as $position => $item ) {
601
            if ( $item['id'] == $item_id ) {
602
                if ( isset( $options['price_id'] ) && isset( $item['options']['price_id'] ) ) {
603
                    if ( (int) $options['price_id'] == (int) $item['options']['price_id'] ) {
604
                        return $position;
605
                    }
606
                } else {
607
                    return $position;
608
                }
609
            }
610
        }
611
    }
612
613
    return false; // Not found
614
}
615
616
function wpinv_get_cart_item_quantity( $item ) {
617
    if ( wpinv_item_quantities_enabled() ) {
618
        $quantity = !empty( $item['quantity'] ) && (int)$item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
619
    } else {
620
        $quantity = 1;
621
    }
622
    
623
    if ( $quantity < 1 ) {
624
        $quantity = 1;
625
    }
626
    
627
    return apply_filters( 'wpinv_get_cart_item_quantity', $quantity, $item );
628
}
629
630
function wpinv_get_item_suffix( $item, $html = true ) {
631
    if ( empty( $item ) ) {
632
        return NULL;
633
    }
634
    
635
    if ( is_int( $item ) ) {
636
        $item = new WPInv_Item( $item );
0 ignored issues
show
Documentation introduced by
$item is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
637
    }
638
    
639
    if ( !( is_object( $item ) && is_a( $item, 'WPInv_Item' ) ) ) {
640
        return NULL;
641
    }
642
    
643
    $suffix = $item->is_recurring() ? ' <span class="wpi-suffix">' . __( '(r)', 'invoicing' ) . '</span>' : '';
644
    
645
    if ( !$html && $suffix ) {
646
        $suffix = strip_tags( $suffix );
647
    }
648
    
649
    return apply_filters( 'wpinv_get_item_suffix', $suffix, $item, $html );
650
}
651
652
function wpinv_remove_item( $item = 0, $force_delete = false ) {
653
    if ( empty( $item ) ) {
654
        return NULL;
655
    }
656
    
657
    if ( is_int( $item ) ) {
658
        $item = new WPInv_Item( $item );
0 ignored issues
show
Documentation introduced by
$item is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
659
    }
660
    
661
    if ( !( is_object( $item ) && is_a( $item, 'WPInv_Item' ) ) ) {
662
        return NULL;
663
    }
664
    
665
    do_action( 'wpinv_pre_delete_item', $item );
666
667
    wp_delete_post( $item->ID, $force_delete );
668
669
    do_action( 'wpinv_post_delete_item', $item );
670
}
671
672
function wpinv_can_delete_item( $post_id ) {
673
    $return = wpinv_current_user_can_manage_invoicing() ? true : false;
674
    
675
    if ( $return && wpinv_item_in_use( $post_id ) ) {
676
        $return = false; // Don't delete item already use in invoices.
677
    }
678
    
679
    return apply_filters( 'wpinv_can_delete_item', $return, $post_id );
680
}
681
682
function wpinv_admin_action_delete() {
683
    $screen = get_current_screen();
684
    
685
    if ( !empty( $screen->post_type ) && $screen->post_type == 'wpi_item' && !empty( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] ) ) {
686
        $post_ids = array();
687
        
688
        foreach ( $_REQUEST['post'] as $post_id ) {
689
            if ( !wpinv_can_delete_item( $post_id ) ) {
690
                continue;
691
            }
692
            
693
            $post_ids[] = $post_id;
694
        }
695
        
696
        $_REQUEST['post'] = $post_ids;
697
    }
698
}
699
add_action( 'admin_action_trash', 'wpinv_admin_action_delete', -10 );
700
add_action( 'admin_action_delete', 'wpinv_admin_action_delete', -10 );
701
702
function wpinv_check_delete_item( $check, $post, $force_delete ) {
703
    if ( $post->post_type == 'wpi_item' ) {
704
        if ( $force_delete && !wpinv_can_delete_item( $post->ID ) ) {
705
            return true;
706
        }
707
    }
708
    
709
    return $check;
710
}
711
add_filter( 'pre_delete_post', 'wpinv_check_delete_item', 10, 3 );
712
713
function wpinv_item_in_use( $item_id ) {
714
    global $wpdb, $wpi_items_in_use;
715
    
716
    if ( !$item_id > 0 ) {
717
        return false;
718
    }
719
    
720
    if ( !empty( $wpi_items_in_use ) ) {
721
        if ( isset( $wpi_items_in_use[$item_id] ) ) {
722
            return $wpi_items_in_use[$item_id];
723
        }
724
    } else {
725
        $wpi_items_in_use = array();
726
    }
727
    
728
    $statuses   = array_keys( wpinv_get_invoice_statuses( true, true ) );
729
    
730
    $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 )";
731
    $in_use = $wpdb->get_var( $query ) > 0 ? true : false;
732
    
733
    $wpi_items_in_use[$item_id] = $in_use;
734
    
735
    return $in_use;
736
}
737
738
function wpinv_create_item( $args = array(), $wp_error = false, $force_update = false ) {
739
    // Set some defaults
740
    $defaults = array(
741
        'type'                 => 'custom',                                                // Optional. Item type. Default 'custom'.
742
        'title'                => '',                                                      // Required. Item title.
743
        'custom_id'            => 0,                                                       // Optional. Any integer or non numeric id. Must be unique within item type.
744
        'price'                => '0.00',                                                  // Optional. Item price. Default '0.00'.
745
        'status'               => 'pending',                                               // Optional. pending, publish
746
        'custom_name'          => '',                                                      // Optional. Plural sub title for item.
747
        'custom_singular_name' => '',                                                      // Optional. Singular sub title for item.
748
        'vat_rule'             => 'digital',                                               // Optional. digital => Digital item, physical => Physical item
749
        'editable'             => true,                                                    // Optional. Item editable from Items list page? Default true.
750
        'excerpt'              => '',                                                      // Optional. Item short description
751
        /* Recurring item fields */
752
        'is_recurring'         => 0,                                                       // Optional. 1 => Allow recurring or 0 => Don't allow recurring
753
        'recurring_period'     => 'M',                                                     // Optional. D => Daily, W => Weekly, M => Monthly, Y => Yearly
754
        'recurring_interval'   => 0,                                                       // Optional. Integer value between 1 - 90.
755
        'recurring_limit'      => 0,                                                       // Optional. Any integer number. 0 for recurring forever until cancelled.
756
        'free_trial'           => 0,                                                       // Optional. 1 => Allow free trial or 0 => Don't free trial
757
        'trial_period'         => 'M',                                                     // Optional. D => Daily, W => Weekly, M => Monthly, Y => Yearly
758
        'trial_interval'       => 0,                                                       // Optional. Any integer number.
759
        'minimum_price'        => '0.00',                                                  // Optional. Minimum allowed prices for items with dynamic pricing.
760
        'dynamic_pricing'      => 0,                                                       // Optional. Whether or not the item supports dynamic prices.
761
    );
762
763
    $data = wp_parse_args( $args, $defaults );
764
765
    if ( empty( $data['type'] ) ) {
766
        $data['type'] = 'custom';
767
    }
768
769
    if ( !empty( $data['custom_id'] ) ) {
770
        $item = wpinv_get_item_by( 'custom_id', $data['custom_id'], $data['type'] );
771
    } else {
772
        $item = NULL;
773
    }
774
775
    if ( !empty( $item ) ) {
776
        if ( $force_update ) {
777
            if ( empty( $args['ID'] ) ) {
778
                $args['ID'] = $item->ID;
779
            }
780
            return wpinv_update_item( $args, $wp_error );
781
        }
782
783
        return $item;
784
    }
785
786
    $meta                           = array();
787
    $meta['type']                   = $data['type'];
788
    $meta['custom_id']              = $data['custom_id'];
789
    $meta['custom_singular_name']   = $data['custom_singular_name'];
790
    $meta['custom_name']            = $data['custom_name'];
791
    $meta['price']                  = wpinv_round_amount( $data['price'] );
792
    $meta['editable']               = (int)$data['editable'];
793
    $meta['vat_rule']               = $data['vat_rule'];
794
    $meta['vat_class']              = '_standard';
795
    $meta['dynamic_pricing']        = (int) $data['dynamic_pricing'];
796
    $meta['minimum_price']          = wpinv_round_amount( $data['minimum_price'] );
797
    
798
    if ( !empty( $data['is_recurring'] ) ) {
799
        $meta['is_recurring']       = $data['is_recurring'];
800
        $meta['recurring_period']   = $data['recurring_period'];
801
        $meta['recurring_interval'] = absint( $data['recurring_interval'] );
802
        $meta['recurring_limit']    = absint( $data['recurring_limit'] );
803
        $meta['free_trial']         = $data['free_trial'];
804
        $meta['trial_period']       = $data['trial_period'];
805
        $meta['trial_interval']     = absint( $data['trial_interval'] );
806
    } else {
807
        $meta['is_recurring']       = 0;
808
        $meta['recurring_period']   = '';
809
        $meta['recurring_interval'] = '';
810
        $meta['recurring_limit']    = '';
811
        $meta['free_trial']         = 0;
812
        $meta['trial_period']       = '';
813
        $meta['trial_interval']     = '';
814
    }
815
    
816
    $post_data  = array( 
817
        'post_title'    => $data['title'],
818
        'post_excerpt'  => $data['excerpt'],
819
        'post_status'   => $data['status'],
820
        'meta'          => $meta
821
    );
822
823
    $item = new WPInv_Item();
824
    $return = $item->create( $post_data, $wp_error );
825
826
    if ( $return && !empty( $item ) && !is_wp_error( $return ) ) {
827
        return $item;
828
    }
829
830
    if ( $wp_error && is_wp_error( $return ) ) {
831
        return $return;
832
    }
833
    return 0;
834
}
835
836
function wpinv_update_item( $args = array(), $wp_error = false ) {
837
    $item = !empty( $args['ID'] ) ? new WPInv_Item( $args['ID'] ) : NULL;
838
839 View Code Duplication
    if ( empty( $item ) || !( !empty( $item->post_type ) && $item->post_type == 'wpi_item' ) ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
840
        if ( $wp_error ) {
841
            return new WP_Error( 'wpinv_invalid_item', __( 'Invalid item.', 'invoicing' ) );
842
        }
843
        return 0;
844
    }
845
    
846
    if ( !empty( $args['custom_id'] ) ) {
847
        $item_exists = wpinv_get_item_by( 'custom_id', $args['custom_id'], ( !empty( $args['type'] ) ? $args['type'] : $item->type ) );
0 ignored issues
show
Documentation introduced by
The property $type is declared private in WPInv_Item. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
848
        
849 View Code Duplication
        if ( !empty( $item_exists ) && $item_exists->ID != $args['ID'] ) {
850
            if ( $wp_error ) {
851
                return new WP_Error( 'wpinv_invalid_custom_id', __( 'Item with custom id already exists.', 'invoicing' ) );
852
            }
853
            return 0;
854
        }
855
    }
856
857
    $meta_fields = array( 
858
        'type', 
859
        'custom_id', 
860
        'custom_singular_name', 
861
        'custom_name', 
862
        'price', 
863
        'editable', 
864
        'vat_rule', 
865
        'vat_class', 
866
        'is_recurring', 
867
        'recurring_period', 
868
        'recurring_interval', 
869
        'recurring_limit', 
870
        'free_trial', 
871
        'trial_period', 
872
        'trial_interval', 
873
        'minimum_price', 
874
        'dynamic_pricing'
875
    );
876
877
    $post_data = array();
878
    if ( isset( $args['title'] ) ) { 
879
        $post_data['post_title'] = $args['title'];
880
    }
881
    if ( isset( $args['excerpt'] ) ) { 
882
        $post_data['post_excerpt'] = $args['excerpt'];
883
    }
884
    if ( isset( $args['status'] ) ) { 
885
        $post_data['post_status'] = $args['status'];
886
    }
887
    
888
    foreach ( $meta_fields as $meta_field ) {
889
        if ( isset( $args[ $meta_field ] ) ) { 
890
            $value = $args[ $meta_field ];
891
892
            switch ( $meta_field ) {
893
                case 'price':
894
                case 'minimum_price':
895
                    $value = wpinv_round_amount( $value );
896
                break;
897
                case 'recurring_interval':
898
                case 'recurring_limit':
899
                case 'trial_interval':
900
                    $value = absint( $value );
901
                break;
902
				case 'editable':
903
                    $value = (int) $value;
904
                break;
905
            }
906
907
            $post_data['meta'][ $meta_field ] = $value;
908
        };
909
    }
910
911
    if ( empty( $post_data ) ) {
912
        if ( $wp_error ) {
913
            return new WP_Error( 'wpinv_invalid_item_data', __( 'Invalid item data.', 'invoicing' ) );
914
        }
915
        return 0;
916
    }
917
    $post_data['ID'] = $args['ID'];
918
919
    $return = $item->update( $post_data, $wp_error );
920
921
    if ( $return && !empty( $item ) && !is_wp_error( $return ) ) {
922
        return $item;
923
    }
924
925
    if ( $wp_error && is_wp_error( $return ) ) {
926
        return $return;
927
    }
928
    return 0;
929
}