Completed
Pull Request — master (#2165)
by Justin
05:08
created

ajax.php ➔ _wpsc_ajax_add_log_item()   D

Complexity

Conditions 10
Paths 17

Size

Total Lines 42
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 25
nc 17
nop 0
dl 0
loc 42
rs 4.8196
c 0
b 0
f 0

How to fix   Complexity   

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
/**
3
 * Verify nonce of an AJAX request
4
 *
5
 * @since  3.8.9
6
 * @access private
7
 *
8
 * @uses WP_Error           WordPress Error Class
9
 * @uses wp_verify_nonce()    Verify that correct nonce was used with time limit.
10
 *
11
 * @param string $ajax_action Name of AJAX action
12
 * @return WP_Error|boolean True if nonce is valid. WP_Error if otherwise.
13
 */
14
function _wpsc_ajax_verify_nonce( $ajax_action ) {
15
	// nonce can be passed with name wpsc_nonce or _wpnonce
16
	$nonce = '';
17
18
	if ( isset( $_REQUEST['nonce'] ) )
19
		$nonce = $_REQUEST['nonce'];
20
	elseif ( isset( $_REQUEST['_wpnonce'] ) )
21
		$nonce = $_REQUEST['_wpnonce'];
22
	else
23
		return _wpsc_error_invalid_nonce();
24
25
	// validate nonce
26
	if ( ! wp_verify_nonce( $nonce, 'wpsc_ajax_' . $ajax_action ) )
27
		return _wpsc_error_invalid_nonce();
28
29
	return true;
30
}
31
32
function _wpsc_error_invalid_nonce() {
33
	return new WP_Error( 'wpsc_ajax_invalid_nonce', __( 'Your session has expired. Please refresh the page and try again.', 'wp-e-commerce' ) );
34
}
35
36
/**
37
 * Verify AJAX callback and call it if it exists.
38
 *
39
 * @since  3.8.9
40
 * @access private
41
 *
42
 * @uses WP_Error   WordPress Error object
43
 *
44
 * @param  string $ajax_action Name of AJAX action
45
 * @return WP_Error|array Array of response args if callback is valid. WP_Error if otherwise.
46
 */
47
function _wpsc_ajax_fire_callback( $ajax_action ) {
48
	// if callback exists, call it and output JSON response
49
	$callback = "_wpsc_ajax_{$ajax_action}";
50
51
	if ( is_callable( $callback ) )
52
		$result = call_user_func( $callback );
53
	else
54
		$result = new WP_Error( 'wpsc_invalid_ajax_callback', __( 'Invalid AJAX callback.', 'wp-e-commerce' ) );
55
56
	return $result;
57
}
58
59
/**
60
 * AJAX handler for all WPEC ajax requests.
61
 *
62
 * This function automates nonce checking and outputs JSON response.
63
 *
64
 * @since 3.8.9
65
 * @access private
66
 *
67
 * @uses _wpsc_ajax_fire_callback()     Verify ajax callback if it exists
68
 * @uses _wpsc_ajax_verify_nonce()      Verify nonce of an ajax request
69
 * @uses is_wp_error()                  Check whether variable is a WordPress Error.
70
 *
71
 * @return array $output    json encoded response
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
72
 */
73
function _wpsc_ajax_handler() {
74
	$ajax_action = str_replace( '-', '_', $_REQUEST['wpsc_action'] );
75
76
	if ( is_callable( '_wpsc_ajax_verify_' . $ajax_action ) ) {
77
		$result = call_user_func( '_wpsc_ajax_verify_' . $ajax_action );
78
	} else {
79
		$result = _wpsc_ajax_verify_nonce( $ajax_action );
80
	}
81
82
	if ( ! is_wp_error( $result ) ) {
83
		$result = _wpsc_ajax_fire_callback( $ajax_action );
84
	}
85
86
	$output = array(
87
		'is_successful' => false,
88
	);
89
90
	if ( is_wp_error( $result ) ) {
91
		$output['error'] = array(
92
			'code'     => $result->get_error_code(),
93
			'messages' => $result->get_error_messages(),
94
			'data'     => $result->get_error_data(),
95
		);
96
	} else {
97
		$output['is_successful'] = true;
98
		$output['obj'] = $result;
99
	}
100
101
	echo json_encode( $output );
102
	exit;
103
}
104
add_action( 'wp_ajax_wpsc_ajax', '_wpsc_ajax_handler' );
105
106
/**
107
 * Checks if WPSC is doing ajax
108
 *
109
 * @param   string  $action     req     The action we're checking
110
 * @return  bool    True if doing ajax
111
 */
112
function wpsc_is_doing_ajax( $action = '' ) {
113
	$ajax = defined( 'DOING_AJAX' ) && DOING_AJAX && ! empty( $_REQUEST['action'] ) && $_REQUEST['action'] == 'wpsc_ajax';
114
115
	if ( $action ) {
116
		$ajax = $ajax && ! empty( $_REQUEST['wpsc_action'] ) && $action == str_replace( '-', '_', $_REQUEST['wpsc_action'] );
117
	}
118
119
	return $ajax;
120
}
121
122
/**
123
 * Helper function that generates nonce for an AJAX action. Basically just a wrapper of
124
 * wp_create_nonce() but automatically add prefix.
125
 *
126
 * @since  3.8.9
127
 * @access private
128
 *
129
 * @uses wp_create_nonce()  Creates a random one time use token
130
 *
131
 * @param  string $action AJAX action without prefix
0 ignored issues
show
Documentation introduced by
There is no parameter named $action. Did you maybe mean $ajax_action?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
132
 * @return string         The generated nonce.
133
 */
134
function _wpsc_create_ajax_nonce( $ajax_action ) {
135
	return wp_create_nonce( "wpsc_ajax_{$ajax_action}" );
136
}
137
138
/**
139
 * Add new variation set via AJAX.
140
 *
141
 * If the variation set name is the same as an existing variation set,
142
 * the children variant terms will be added inside that existing set.
143
 *
144
 * @since 3.8.8
145
 * @access private
146
 *
147
 * @uses term_exists()                      Returns true if term exists
148
 * @uses get_term()                         Gets all term data by term_id
149
 * @uses wp_insert_term()                   Inserts a term to the WordPress database
150
 * @uses is_wp_error()                      Checks whether variable is a WordPress error
151
 * @uses WP_Error                           WordPress Error class
152
 * @uses clean_term_cache()                 Will remove all of the term ids from the cache.
153
 * @uses delete_option()                    Deletes option from the database
154
 * @uses wp_cache_set()                     Saves the data to the cache.
155
 * @uses _get_term_hierarchy()              Retrieves children of taxonomy as Term IDs.
156
 * @uses wp_terms_checklist()               Output an unordered list of checkbox <input> elements labelled
157
 * @uses WPSC_Walker_Variation_Checklist    Walker variation checklist
158
 *
159
 * @return array Response args
160
 */
161
function _wpsc_ajax_add_variation_set() {
162
	$new_variation_set = $_POST['variation_set'];
163
	$variants = preg_split( '/\s*,\s*/', $_POST['variants'] );
164
165
	$return = array();
166
167
	$parent_term_exists = term_exists( $new_variation_set, 'wpsc-variation' );
168
169
	// only use an existing parent ID if the term is not a child term
170
	if ( $parent_term_exists ) {
171
		$parent_term = get_term( $parent_term_exists['term_id'], 'wpsc-variation' );
172
		if ( $parent_term->parent == '0' )
173
			$variation_set_id = $parent_term_exists['term_id'];
174
	}
175
176
	if ( empty( $variation_set_id ) ) {
177
		$results = wp_insert_term( apply_filters( 'wpsc_new_variation_set', $new_variation_set ), 'wpsc-variation' );
178
		if ( is_wp_error( $results ) )
179
			return $results;
180
		$variation_set_id = $results['term_id'];
181
	}
182
183
	if ( empty( $variation_set_id ) )
184
		return new WP_Error( 'wpsc_invalid_variation_id', __( 'Cannot retrieve the variation set in order to proceed.', 'wp-e-commerce' ) );
185
186
	foreach ( $variants as $variant ) {
187
		$results = wp_insert_term( apply_filters( 'wpsc_new_variant', $variant, $variation_set_id ), 'wpsc-variation', array( 'parent' => $variation_set_id ) );
188
189
		if ( is_wp_error( $results ) )
190
			return $results;
191
192
		$inserted_variants[] = $results['term_id'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$inserted_variants was never initialized. Although not strictly required by PHP, it is generally a good practice to add $inserted_variants = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
193
	}
194
195
	require_once( 'includes/walker-variation-checklist.php' );
196
197
	if ( ! version_compare( $GLOBALS['wp_version'], '3.8.3', '>' ) ) {
198
199
		/* --- DIRTY HACK START --- */
200
		/*
201
		There's a bug with term cache in WordPress core. See http://core.trac.wordpress.org/ticket/14485. Fixed in 3.9.
202
		The next 3 lines will delete children term cache for wpsc-variation.
203
		Without this hack, the new child variations won't be displayed on "Variations" page and
204
		also won't be displayed in wp_terms_checklist() call below.
205
		*/
206
		clean_term_cache( $variation_set_id, 'wpsc-variation' );
207
		delete_option('wpsc-variation_children');
208
		wp_cache_set( 'last_changed', 1, 'terms' );
209
		_get_term_hierarchy('wpsc-variation');
210
		/* --- DIRTY HACK END --- */
211
212
	}
213
214
	ob_start();
215
216
	wp_terms_checklist( (int) $_POST['post_id'], array(
217
		'taxonomy'      => 'wpsc-variation',
218
		'descendants_and_self' => $variation_set_id,
219
		'walker'        => new WPSC_Walker_Variation_Checklist( $inserted_variants ),
0 ignored issues
show
Bug introduced by
The variable $inserted_variants does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
220
		'checked_ontop' => false,
221
	) );
222
223
	$content = ob_get_clean();
224
225
	$return = array(
226
		'variation_set_id'  => $variation_set_id,
227
		'inserted_variants' => $inserted_variants,
228
		'content'           => $content,
229
	);
230
231
	return $return;
232
}
233
234
/**
235
 * Display gateway settings form via AJAX
236
 *
237
 * @since  3.8.9
238
 * @access private
239
 *
240
 * @uses WPSC_Settings_Tab_Gateway
241
 * @uses WPSC_Settings_Tab_Gateway::display_payment_gateway_settings_form()     Displays payment gateway form
242
 *
243
 * @return array Response args
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
244
 */
245
function _wpsc_ajax_payment_gateway_settings_form() {
246
247
	require_once( 'settings-page.php' );
248
	require_once( 'includes/settings-tabs/gateway.php' );
249
250
	$return = array();
251
	ob_start();
252
	$tab = new WPSC_Settings_Tab_Gateway();
253
	$tab->display_payment_gateway_settings_form();
254
	$return['content'] = ob_get_clean();
255
256
	return $return;
257
}
258
259
/**
260
 * Display shipping module settings form via AJAX
261
 *
262
 * @since  3.8.9
263
 * @access private
264
 *
265
 * @uses WPSC_Settings_Table_Shipping
266
 * @uses WPSC_Settings_Table_Shipping::display_shipping_module_settings_form()  Displays shipping module form
267
 *
268
 * @return array $return    Response args
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
269
 */
270
function _wpsc_ajax_shipping_module_settings_form() {
271
	require_once( 'settings-page.php' );
272
	require_once( 'includes/settings-tabs/shipping.php' );
273
274
	$return = array();
275
	ob_start();
276
	$tab = new WPSC_Settings_Tab_Shipping();
277
	$tab->display_shipping_module_settings_form();
278
	$return['content'] = ob_get_clean();
279
280
	return $return;
281
}
282
283
/**
284
 * Display settings tab via AJAX
285
 *
286
 * @since 3.8.9
287
 * @access private
288
 *
289
 * @uses WPSC_Settings_Page
290
 * @uses WPSC_Settings_Page::display_current_tab()  Shows current tab of settings page
291
 *
292
 * @return array $return    Response args
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
293
 */
294
function _wpsc_ajax_navigate_settings_tab() {
295
	require_once( 'settings-page.php' );
296
297
	$return = array();
298
	ob_start();
299
	$settings_page = new WPSC_Settings_Page( $_POST['tab'] );
300
	$settings_page->display_current_tab();
301
	$return['content'] = ob_get_clean();
302
303
	return $return;
304
}
305
306
/**
307
 * Display base region list in Store Settings -> General
308
 *
309
 * @since 3.8.9
310
 * @access private
311
 *
312
 * @uses WPSC_Settings_Tab_General
313
 * @uses WPSC_Settings_Tab_General::display_region_drop_down()  Shows region dropdown
314
 *
315
 * @return array    $return     Response args
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
316
 */
317
function _wpsc_ajax_display_region_list() {
318
	require_once( 'settings-page.php' );
319
	require_once( 'includes/settings-tabs/general.php' );
320
321
	$return = array();
322
	ob_start();
323
	$tab = new WPSC_Settings_Tab_General();
324
	$tab->display_region_drop_down();
325
	$return['content'] = ob_get_clean();
326
327
	return $return;
328
}
329
330
/**
331
 * Save tracking ID of a sales log.
332
 *
333
 * @since 3.8.9
334
 * @access private
335
 *
336
 * @uses WP_Error   WordPress Error class
337
 *
338
 * @return array|WP_Error   $return     Response args if successful, WP_Error if otherwise.
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
339
 */
340
function _wpsc_ajax_purchase_log_save_tracking_id() {
341
	global $wpdb;
342
343
	$result = $wpdb->update(
344
		WPSC_TABLE_PURCHASE_LOGS,
345
		array(
346
			'track_id' => $_POST['value']
347
		),
348
		array(
349
			'id' => $_POST['log_id']
350
		),
351
		'%s',
352
		'%d'
353
	);
354
355
	if ( ! $result )
356
		return new WP_Error( 'wpsc_cannot_save_tracking_id', __( "Couldn't save tracking ID of the transaction. Please try again.", 'wp-e-commerce' ) );
357
358
	$return = array(
359
		'rows_affected' => $result,
360
		'id'            => $_POST['log_id'],
361
		'track_id'      => $_POST['value'],
362
	);
363
364
	return $return;
365
}
366
367
/**
368
 * Send sales log tracking email via AJAX
369
 *
370
 * @since 3.8.9
371
 * @access private
372
 *
373
 * @uses $wpdb              WordPress database object for queries
374
 * @uses get_option()       Gets option from DB given key
375
 * @uses add_filter()       Calls 'wp_mail_from' which can replace the from email address
376
 * @uses add_filter()       Calls 'wp_mail_from_name' allows replacement of the from name on WordPress emails
377
 * @uses wp_mail()          All the emailses in WordPress are sent through this function
378
 * @uses WP_Error           WordPress Error class
379
 *
380
 * @return array|WP_Error   $return     Response args if successful, WP_Error if otherwise
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
381
 */
382
function _wpsc_ajax_purchase_log_send_tracking_email() {
383
	global $wpdb;
384
385
	$id = absint( $_POST['log_id'] );
386
	$sql = $wpdb->prepare( "SELECT `track_id` FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE `id`=%d LIMIT 1", $id );
387
	$trackingid = $wpdb->get_var( $sql );
388
389
	$message = get_option( 'wpsc_trackingid_message' );
390
	$message = str_replace( '%trackid%', $trackingid, $message );
391
	$message = str_replace( '%shop_name%', get_option( 'blogname' ), $message );
392
393
	$email = wpsc_get_buyers_email( $id );
394
395
	$subject = get_option( 'wpsc_trackingid_subject' );
396
	$subject = str_replace( '%shop_name%', get_option( 'blogname' ), $subject );
397
398
	add_filter( 'wp_mail_from', 'wpsc_replace_reply_address', 0 );
399
	add_filter( 'wp_mail_from_name', 'wpsc_replace_reply_name', 0 );
400
401
	$result = wp_mail( $email, $subject, $message);
402
403
	if ( ! $result ) {
404
		return new WP_Error( 'wpsc_cannot_send_tracking_email', __( "Couldn't send tracking email. Please try again.", 'wp-e-commerce' ) );
405
	}
406
407
	$return = array(
408
		'id'          => $id,
409
		'tracking_id' => $trackingid,
410
		'subject'     => $subject,
411
		'message'     => $message,
412
		'email'       => $email
413
	);
414
415
	return $return;
416
}
417
418
/**
419
 * Do purchase log action link via AJAX
420
 *
421
 * @since   3.9.0
422
 * @access  private
423
 *
424
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
425
 */
426
function _wpsc_ajax_purchase_log_action_link() {
427
428
	if ( isset( $_POST['log_id'] ) && isset( $_POST['purchase_log_action_link'] ) && isset( $_POST['purchase_log_action_nonce'] ) ) {
429
430
		$log_id = absint( $_POST['log_id'] );
431
		$purchase_log_action_link = sanitize_key( $_POST['purchase_log_action_link'] );
432
433
		// Verify action nonce
434
		if ( wp_verify_nonce( $_POST['purchase_log_action_nonce'], 'wpsc_purchase_log_action_ajax_' . $purchase_log_action_link ) ) {
435
436
			// Expected to receive success = true by default, or false on error.
437
			$return = apply_filters( 'wpsc_purchase_log_action_ajax-' . $purchase_log_action_link, array( 'success' => null ), $log_id );
438
439
		} else {
440
			$return = _wpsc_error_invalid_nonce();
441
		}
442
443
		if ( ! is_wp_error( $return ) ) {
444
			$return['log_id'] = $log_id;
445
			$return['purchase_log_action_link'] = $purchase_log_action_link;
446
			$return['success'] = isset( $return['success'] ) ? (bool) $return['success'] : null;
447
		}
448
449
		return $return;
450
451
	}
452
453
	return new WP_Error( 'wpsc_ajax_invalid_purchase_log_action', __( 'Purchase log action failed.', 'wp-e-commerce' ) );
454
455
}
456
457
/**
458
 * Remove purchase log item.
459
 *
460
 * @since   4.0
461
 * @access  private
462
 *
463
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
464
 */
465
function _wpsc_ajax_remove_log_item() {
466
467
	if ( isset( $_POST['item_id'], $_POST['log_id'] ) ) {
468
469
		$item_id = absint( $_POST['item_id'] );
470
		$log_id  = absint( $_POST['log_id'] );
471
		$log     = new WPSC_Purchase_Log( $log_id );
472
473
		if ( $log->remove_item( $item_id ) ) {
474
			return _wpsc_init_log_items( $log );
475
		}
476
	}
477
478
	return new WP_Error( 'wpsc_ajax_invalid_remove_log_item', __( 'Removing log item failed.', 'wp-e-commerce' ) );
479
}
480
481
/**
482
 * Update purchase log item quantity.
483
 *
484
 * @since   4.0
485
 * @access  private
486
 *
487
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
488
 */
489
function _wpsc_ajax_update_log_item_qty() {
490
491
	if ( isset( $_POST['item_id'], $_POST['log_id'], $_POST['qty'] ) ) {
492
493
		if ( empty( $_POST['qty'] ) ) {
494
			return _wpsc_ajax_remove_log_item();
495
		}
496
497
		$item_id = absint( $_POST['item_id'] );
498
		$log_id  = absint( $_POST['log_id'] );
499
		$log     = new WPSC_Purchase_Log( $log_id );
500
		$result  = $log->update_item( $item_id, array( 'quantity' => absint( $_POST['qty'] ) ) );
501
502
		if ( 0 === $result ) {
503
			return true;
504
		} elseif ( false !== $result ) {
505
			return _wpsc_init_log_items( $log );
506
		}
507
	}
508
509
	return new WP_Error( 'wpsc_ajax_invalid_update_log_item_qty', __( 'Updating log item quantity failed.', 'wp-e-commerce' ) );
510
}
511
512
/**
513
 * Add purchase log item.
514
 *
515
 * @since   4.0
516
 * @access  private
517
 *
518
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
519
 */
520
function _wpsc_ajax_add_log_item() {
521
	global $wpsc_cart;
522
523
	if (
524
		isset( $_POST['product_ids'], $_POST['log_id'] )
525
		&& is_array( $_POST['product_ids'] )
526
		&& ! empty( $_POST['product_ids'] )
527
	) {
528
529
		$existing = isset( $_POST['existing'] ) && is_array( $_POST['existing'] )
530
			? array_map( 'absint', $_POST['existing'] )
531
			: false;
532
533
		$item_ids = array();
534
		$log      = null;
535
536
		foreach ( $_POST['product_ids'] as $product_id ) {
537
			$product_id = absint( $product_id );
538
			$log_id     = absint( $_POST['log_id'] );
539
			$log        = new WPSC_Purchase_Log( $log_id );
540
541
			// Is product is already in item list?
542
			if ( $existing && in_array( $product_id, $existing, true ) ) {
543
				$item = $log->get_item_from_product_id( $product_id );
544
				if ( $item ) {
545
					// Update item quantity...
546
					$log->update_item( $item->id, array( 'quantity' => ++$item->quantity ) );
547
					// And move on.
548
					continue;
549
				}
550
			}
551
552
			$item       = new wpsc_cart_item( $product_id, array(), $wpsc_cart );
553
			$item_id    = $item->save_to_db( $log_id );
554
			$item_ids[] = absint( $item_id );
555
		}
556
557
		return _wpsc_init_log_items( $log, $item_ids );
0 ignored issues
show
Bug introduced by
It seems like $log defined by null on line 534 can be null; however, _wpsc_init_log_items() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
558
	}
559
560
	return new WP_Error( 'wpsc_ajax_invalid_add_log_item', __( 'Adding log item failed.', 'wp-e-commerce' ) );
561
}
562
563
function _wpsc_init_log_items( WPSC_Purchase_Log $log, $item_ids = array() ) {
564
	$log->init_items();
565
566
	require_once( WPSC_FILE_PATH . '/wpsc-admin/display-sales-logs.php' );
567
568
	$html = '';
569
	$htmls = array();
570
	$htmls[] = array();
571
572
	while ( wpsc_have_purchaselog_details() ) {
573
		wpsc_the_purchaselog_item();
574
575
		ob_start();
576
		WPSC_Purchase_Log_Page::purchase_log_cart_item( $log->can_edit() );
577
		$cart_item = ob_get_clean();
578
579
		$htmls[ wpsc_purchaselog_details_id() ] = $cart_item;
580
		if ( ! empty( $item_ids ) && in_array( absint( wpsc_purchaselog_details_id() ), $item_ids, true ) ) {
581
			$html .= $cart_item;
582
		}
583
	}
584
585
	return array(
586
		'quantities'     => wp_list_pluck( $log->get_items(), 'quantity', 'id' ),
587
		'html'           => $html,
588
		'htmls'          => $htmls,
589
		'discount_data'  => wpsc_purchlog_has_discount_data() ? esc_html__( 'Coupon Code', 'wp-e-commerce' ) . ': ' . wpsc_display_purchlog_discount_data() : '',
590
		'discount'       => wpsc_display_purchlog_discount(),
591
		'total_taxes'    => wpsc_display_purchlog_taxes(),
592
		'total_shipping' => wpsc_display_purchlog_shipping(),
593
		'final_total'    => wpsc_display_purchlog_totalprice(),
594
	);
595
}
596
597
/**
598
 * Search for products.
599
 *
600
 * @since   4.0
601
 * @access  private
602
 *
603
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
604
 */
605
function _wpsc_ajax_search_products() {
606
	$pt_object = get_post_type_object( 'wpsc-product' );
607
608
	$s = wp_unslash( $_POST['search'] );
609
	$args = array(
610
		'post_type' => 'wpsc-product',
611
		'post_status' => array( 'publish', 'inherit' ),
612
		'posts_per_page' => 50,
613
	);
614
	if ( '' !== $s ) {
615
		$args['s'] = $s;
616
	}
617
618
	$posts = get_posts( $args );
619
620
	if ( ! $posts ) {
621
		return new WP_Error( 'wpsc_ajax_invalid_search_products', __( 'No items found.', 'wp-e-commerce' ) );
622
	}
623
624
	$alt = '';
625
	foreach ( $posts as $post ) {
626
		$post->title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
627
		$alt = ( 'alternate' === $alt ) ? '' : 'alternate';
628
629
		$post->status = $post->post_status;
630
631
		switch ( $post->post_status ) {
632
			case 'publish' :
633
			case 'private' :
634
				$post->status = __( 'Published' );
635
				break;
636
			case 'future' :
637
				$post->status = __( 'Scheduled' );
638
				break;
639
			case 'pending' :
640
				$post->status = __( 'Pending Review' );
641
				break;
642
			case 'draft' :
643
				$post->status = __( 'Draft' );
644
				break;
645
			default :
646
				$post->status = $post->post_status;
647
				break;
648
		}
649
650
		if ( '0000-00-00 00:00:00' === $post->post_date ) {
651
			$post->time = '';
652
		} else {
653
			/* translators: date format in table columns, see https://secure.php.net/date */
654
			$post->time = mysql2date( __( 'Y/m/d' ), $post->post_date );
655
		}
656
657
		$post->class = $alt;
658
	}
659
660
	return $posts;
661
}
662
663
/**
664
 * Handle AJAX clear downloads lock purchase log action
665
 *
666
 * The _wpsc_ajax_purchase_log_action_link() function which triggers this function is nonce
667
 * and capability checked in _wpsc_ajax_handler().
668
 *
669
 * @since   3.9.0
670
 * @access  private
671
 *
672
 * @param  array  $response  AJAX response.
673
 * @param  int    $log_id    Purchase log ID.
674
 */
675
function wpsc_purchase_log_action_ajax_downloads_lock( $response, $log_id ) {
676
677
	$response['success'] = wpsc_purchlog_clear_download_items( $log_id );
678
679
	return $response;
680
681
}
682
add_action( 'wpsc_purchase_log_action_ajax-downloads_lock', 'wpsc_purchase_log_action_ajax_downloads_lock', 10, 2 );
683
684
685
/**
686
 * Handle AJAX email receipt purchase log action
687
 *
688
 * The _wpsc_ajax_purchase_log_action_link() function which triggers this function is nonce
689
 * and capability checked in _wpsc_ajax_handler().
690
 *
691
 * @since   3.9.0
692
 * @access  private
693
 *
694
 * @param  array  $response  AJAX response.
695
 * @param  int    $log_id    Purchase log ID.
696
 */
697
function wpsc_purchase_log_action_ajax_email_receipt( $response, $log_id ) {
698
699
	$response['success'] = wpsc_purchlog_resend_email( $log_id );
700
701
	return $response;
702
703
}
704
add_action( 'wpsc_purchase_log_action_ajax-email_receipt', 'wpsc_purchase_log_action_ajax_email_receipt', 10, 2 );
705
706
/**
707
 * Delete an attached downloadable file via AJAX.
708
 *
709
 * @since 3.8.9
710
 * @access private
711
 *
712
 * @uses _wpsc_delete_file()    Deletes files associated with a product
713
 * @uses WP_Error               WordPress error class
714
 *
715
 * @return array|WP_Error   $return     Response args if successful, WP_Error if otherwise
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
716
 */
717
function _wpsc_ajax_delete_file() {
718
	$product_id = absint( $_REQUEST['product_id'] );
719
	$file_name = basename( $_REQUEST['file_name'] );
720
721
	$result = _wpsc_delete_file( $product_id, $file_name );
722
723
	if ( ! $result )
724
		return new WP_Error( 'wpsc_cannot_delete_file', __( "Couldn't delete the file. Please try again.", 'wp-e-commerce' ) );
725
726
	$return = array(
727
		'product_id' => $product_id,
728
		'file_name'  => $file_name,
729
	);
730
731
	return $return;
732
}
733
734
/**
735
 * Delete a product meta via AJAX
736
 *
737
 * @since 3.8.9
738
 * @access private
739
 *
740
 * @uses delete_meta()      Deletes metadata by meta id
741
 * @uses WP_Error           WordPress error class
742
 *
743
 * @return  array|WP_Error  $return     Response args if successful, WP_Error if otherwise
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array<string,integer>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
744
 */
745
function _wpsc_ajax_remove_product_meta() {
746
	$meta_id = (int) $_POST['meta_id'];
747
	if ( ! delete_meta( $meta_id ) )
748
		return new WP_Error( 'wpsc_cannot_delete_product_meta', __( "Couldn't delete product meta. Please try again.", 'wp-e-commerce' ) );
749
750
	return array( 'meta_id' => $meta_id );
751
}
752
753
/**
754
 * Modify a purchase log's status.
755
 *
756
 * @since 3.8.9
757
 * @access private
758
 *
759
 * @uses wpsc_purchlog_edit_status()                    Edits purchase log status
760
 * @uses WP_Error                                       WordPress Error class
761
 * @uses WPSC_Purchase_Log_List_Table
762
 * @uses WPSC_Purchase_Log_List_Table::prepare_items()
763
 * @uses WPSC_Purchase_Log_List_Table::views()
764
 * @uses WPSC_Purchase_Log_List_Table::display_tablenav()   @todo docs
765
 *
766
 * @return array|WP_Error   $return     Response args if successful, WP_Error if otherwise.
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
767
 */
768
function _wpsc_ajax_change_purchase_log_status() {
769
	$result = wpsc_purchlog_edit_status( $_POST['id'], $_POST['new_status'] );
770
	if ( ! $result )
771
		return new WP_Error( 'wpsc_cannot_edit_purchase_log_status', __( "Couldn't modify purchase log's status. Please try again.", 'wp-e-commerce' ) );
772
773
	$args = array();
774
775
	$args['screen'] = 'dashboard_page_wpsc-sales-logs';
776
777
	require_once( WPSC_FILE_PATH . '/wpsc-admin/includes/purchase-log-list-table-class.php' );
778
	$purchaselog_table = new WPSC_Purchase_Log_List_Table( $args );
779
	$purchaselog_table->prepare_items();
780
781
	ob_start();
782
	$purchaselog_table->views();
783
	$views = ob_get_clean();
784
785
	ob_start();
786
	$purchaselog_table->display_tablenav( 'top' );
787
	$tablenav_top = ob_get_clean();
788
789
	ob_start();
790
	$purchaselog_table->display_tablenav( 'bottom' );
791
	$tablenav_bottom = ob_get_clean();
792
793
	$return = array(
794
		'id'              => $_POST['id'],
795
		'new_status'      => $_POST['new_status'],
796
		'views'           => $views,
797
		'tablenav_top'    => $tablenav_top,
798
		'tablenav_bottom' => $tablenav_bottom,
799
	);
800
801
	return $return;
802
}
803
804
/**
805
 * Save product ordering after drag-and-drop sorting
806
 *
807
 * @since 3.8.9
808
 * @access private
809
 *
810
 * @uses $wpdb              WordPress database object for use in queries
811
 * @uses wp_update_post()   Updates post based on passed $args. Needs a post_id
812
 * @uses WP_Error           WordPress Error class
813
 *
814
 * @return array|WP_Error Response args if successful, WP_Error if otherwise
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
815
 */
816
function _wpsc_ajax_save_product_order() {
817
818
	$products = array( );
0 ignored issues
show
introduced by
Empty array declaration must have no space between the parentheses
Loading history...
819
	foreach ( $_POST['post'] as $product ) {
820
		$products[] = (int) str_replace( 'post-', '', $product );
821
	}
822
823
	$failed = array();
824
	foreach ( $products as $order => $product_id ) {
825
		$result = wp_update_post( array(
826
			'ID' => $product_id,
827
			'menu_order' => $order,
828
		) );
829
830
		if ( ! $result )
831
			$failed[] = $product_id;
832
	}
833
834
	// Validate data before exposing to action
835
	$category = isset( $_POST['category_id'] ) ? get_term_by( 'slug', $_POST['category_id'], 'wpsc_product_category' ) : false;
836
	do_action( 'wpsc_save_product_order', $products, $category );
837
838
	if ( ! empty( $failed ) ) {
839
		$error_data = array(
840
			'failed_ids' => $failed,
841
		);
842
843
		return new WP_Error( 'wpsc_cannot_save_product_sort_order', __( "Couldn't save the products' sort order. Please try again.", 'wp-e-commerce' ), $error_data );
844
	}
845
846
	return array(
847
		'ids' => $products,
848
	);
849
}
850
851
/**
852
 * Save Category Product Order
853
 *
854
 * Note that this uses the 'term_order' field in the 'term_relationships' table to store
855
 * the order. Although this column presently seems to be unused by WordPress, the intention
856
 * is it should be used to store the order of terms associates to a post, not the order
857
 * of posts as we are doing. This shouldn't be an issue for WPEC unless WordPress adds a UI
858
 * for this. More info at http://core.trac.wordpress.org/ticket/9547
859
 *
860
 * @since 3.9
861
 * @access private
862
 *
863
 * @uses $wpdb   WordPress database object used for queries
864
 */
865
function _wpsc_save_category_product_order( $products, $category ) {
866
	global $wpdb;
867
868
	// Only save category product order if in category
869
	if ( ! $category )
870
		return;
871
872
	// Save product order in term_relationships table
873
	foreach ( $products as $order => $product_id ) {
874
		$wpdb->update( $wpdb->term_relationships,
875
			array( 'term_order' => $order ),
876
			array( 'object_id' => $product_id, 'term_taxonomy_id' => $category->term_taxonomy_id ),
877
			array( '%d' ),
878
			array( '%d', '%d' )
879
		);
880
	}
881
}
882
add_action( 'wpsc_save_product_order', '_wpsc_save_category_product_order', 10, 2 );
883
884
/**
885
 * Update Checkout fields order
886
 *
887
 * @since 3.8.9
888
 * @access private
889
 *
890
 * @uses $wpdb      WordPress database object used for queries
891
 * @uses WP_Error   WordPress error class
892
 *
893
 * @return array|WP_Error Response args or WP_Error
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
894
 */
895
function _wpsc_ajax_update_checkout_fields_order() {
896
	global $wpdb;
897
898
	$checkout_fields = $_REQUEST['sort_order'];
899
	$order = 1;
900
	$failed = array();
901
	$modified = array();
902
	foreach ( $checkout_fields as &$checkout_field ) {
903
		// ignore new fields
904
		if ( strpos( $checkout_field, 'new-field' ) === 0 )
905
			continue;
906
		$checkout_field = absint( preg_replace('/[^0-9]+/', '', $checkout_field ) );
907
		$result = $wpdb->update(
908
			WPSC_TABLE_CHECKOUT_FORMS,
909
			array(
910
				'checkout_order' => $order
911
			),
912
			array(
913
				'id' => $checkout_field
914
			),
915
			'%d',
916
			'%d'
917
		);
918
		$order ++;
919
		if ( $result === false )
920
			$failed[] = $checkout_field;
921
		elseif ( $result > 0 )
922
			$modified[] = $checkout_field;
923
	}
924
925
	if ( ! empty( $failed ) )
926
		return new WP_Error( 'wpsc_cannot_save_checkout_field_sort_order', __( "Couldn't save checkout field sort order. Please try again.", 'wp-e-commerce' ), array( 'failed_ids' => $failed ) );
927
928
	return array(
929
		'modified' => $modified,
930
	);
931
}
932
933
/**
934
 * Save a downloadable file to a product
935
 *
936
 * @since 3.8.9
937
 * @access private
938
 *
939
 * @uses $wpdb                          WordPress database object for use in queries
940
 * @uses _wpsc_create_ajax_nonce()      Creates nonce for an ajax action
941
 * @uses wpsc_get_mimetype()            Returns mimetype of file
942
 * @uses wp_insert_post()               Inserts post to WordPress database
943
 * @uses wp_nonce_url()                 Retrieve URL with nonce added to URL query.
944
 * @uses wpsc_convert_bytes()           Formats bytes
945
 * @uses wpsc_get_extension()           Gets extension of file
946
 * @uses esc_attr()                     Escapes HTML attributes
947
 * @uses _x()                           Retrieve translated string with gettext context
948
 *
949
 * @return array|WP_Error Response args if successful, WP_Error if otherwise.
950
 */
951
function _wpsc_ajax_upload_product_file() {
952
	global $wpdb;
953
	$product_id = absint( $_POST["product_id"] );
954
	$output = '';
955
	$delete_nonce = _wpsc_create_ajax_nonce( 'delete_file' );
956
957
	foreach ( $_POST["select_product_file"] as $selected_file ) {
958
		// if we already use this file, there is no point doing anything more.
959
		$sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_type = 'wpsc-product-file' AND post_title = %s", $selected_file ); // TODO it's safer to select by post ID, in that case we will use get_posts()
960
		$file_post_data = $wpdb->get_row( $sql, ARRAY_A );
961
		$selected_file_path = WPSC_FILE_DIR . basename( $selected_file );
962
		$file_url = WPSC_FILE_URL . basename( $selected_file );
963
		$file_size = filesize( $selected_file_path );
964
		if ( empty( $file_post_data ) ) {
965
			$type = wpsc_get_mimetype( $selected_file_path );
966
			$attachment = array(
967
				'post_mime_type' => $type,
968
				'post_parent' => $product_id,
969
				'post_title' => $selected_file,
970
				'post_content' => '',
971
				'post_type' => "wpsc-product-file",
972
				'post_status' => 'inherit'
973
			);
974
			$id = wp_insert_post( $attachment );
975
		} else {
976
			// already attached
977
			if ( $file_post_data['post_parent'] == $product_id )
978
				continue;
979
			$type = $file_post_data["post_mime_type"];
980
			$url = $file_post_data["guid"];
981
			$title = $file_post_data["post_title"];
982
			$content = $file_post_data["post_content"];
983
			// Construct the attachment
984
			$attachment = array(
985
				'post_mime_type' => $type,
986
				'guid' => $url,
987
				'post_parent' => absint( $product_id ),
988
				'post_title' => $title,
989
				'post_content' => $content,
990
				'post_type' => "wpsc-product-file",
991
				'post_status' => 'inherit'
992
			);
993
			// Save the data
994
			$id = wp_insert_post( $attachment );
995
		}
996
997
		$deletion_url = wp_nonce_url( "admin.php?wpsc_admin_action=delete_file&amp;file_name={$attachment['post_title']}&amp;product_id={$product_id}", 'delete_file_' . $attachment['post_title'] );
998
999
		$output .= '<tr class="wpsc_product_download_row">';
1000
		$output .= '<td style="padding-right: 30px;">' . $attachment['post_title'] . '</td>';
1001
		$output .= '<td>' . wpsc_convert_byte( $file_size ) . '</td>';
1002
		$output .= '<td>.' . wpsc_get_extension( $attachment['post_title'] ) . '</td>';
1003
		$output .= "<td><a data-file-name='" . esc_attr( $attachment['post_title'] ) . "' data-product-id='" . esc_attr( $product_id ) . "' data-nonce='" . esc_attr( $delete_nonce ) . "' class='file_delete_button' href='{$deletion_url}' >" . _x( 'Delete', 'Digital Download UI row', 'wp-e-commerce' ) . "</a></td>";
1004
		$output .= '<td><a href=' .$file_url .'>' . _x( 'Download', 'Digital Download UI row', 'wp-e-commerce' ) . '</a></td>';
1005
		$output .= '</tr>';
1006
	}
1007
1008
	return array(
1009
		'content' => $output,
1010
	);
1011
}
1012
1013
/**
1014
 * Generate variations
1015
 *
1016
 * @since 3.8.9
1017
 * @access private
1018
 *
1019
 * @uses wpsc_update_variations()       Updates product variations given
1020
 * @uses wpsc_admin_product_listing()   DEPRECATED
1021
 *
1022
 * @return array|WP_Error Response args if successful, WP_Error if otherwise
1023
 */
1024
function _wpsc_ajax_update_variations() {
1025
	$product_id = absint( $_REQUEST["product_id"] );
1026
	wpsc_update_variations();
1027
1028
	ob_start();
1029
	wpsc_admin_product_listing( $product_id );
1030
	$content = ob_get_clean();
1031
1032
	return array( 'content' => $content );
1033
}
1034
1035
/**
1036
 * Display the shortcode generator.
1037
 *
1038
 * @since  3.8.9
1039
 * @access private
1040
 */
1041
function _wpsc_action_tinymce_window() {
1042
	require_once( WPSC_CORE_JS_PATH . '/tinymce3/window.php' );
1043
	exit;
1044
}
1045
add_action( 'wp_ajax_wpsc_tinymce_window', '_wpsc_action_tinymce_window' );
1046
1047
/**
1048
 * Add tax rate
1049
 * @since  3.8.9
1050
 * @access private
1051
 *
1052
 * @uses wpec_taxes_controller                                                  Contains all the logic to communicate with the taxes system
1053
 * @uses wpec_taxes_controller::wpec_taxes::wpec_taxes_get_regions()            Gets tax regions based on input country code
1054
 * @uses wpec_taxes_controller::wpec_taxes_build_select_options()               Returns HTML formatted options from input array
1055
 * @uses wpec_taxes_controller::wpec_taxes_build_form()                         Builds the tax rate form
1056
 * @uses wpec_taxes_controller::wpec_taxes::wpec_taxes_get_band_from_index()    Retrieves tax band for given name
1057
 *
1058
 * @return array|WP_Error Response args if successful, WP_Error if otherwise
1059
 */
1060
function _wpsc_ajax_add_tax_rate() {
1061
	//include taxes controller
1062
	$wpec_taxes_controller = new wpec_taxes_controller;
1063
1064
	switch ( $_REQUEST['wpec_taxes_action'] ) {
1065
		case 'wpec_taxes_get_regions':
1066
			$regions = $wpec_taxes_controller->wpec_taxes->wpec_taxes_get_regions( $_REQUEST['country_code'] );
0 ignored issues
show
Bug introduced by
The property wpec_taxes does not seem to exist in wpec_taxes_controller.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1067
			$key = $_REQUEST['current_key'];
1068
			$type = $_REQUEST['taxes_type'];
1069
			$default_option = array( 'region_code' => 'all-markets', 'name' => 'All Markets' );
1070
			$select_settings = array(
1071
				'id' => "{$type}-region-{$key}",
1072
				'name' => "wpsc_options[wpec_taxes_{$type}][{$key}][region_code]",
1073
				'class' => 'wpsc-taxes-region-drop-down'
1074
			);
1075
			$returnable = $wpec_taxes_controller->wpec_taxes_build_select_options( $regions, 'region_code', 'name', $default_option, $select_settings );
1076
			break;
1077
	}// switch
1078
1079
	return array(
1080
		'content' => $returnable,
0 ignored issues
show
Bug introduced by
The variable $returnable does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1081
	);
1082
}
1083
1084
/**
1085
 * Displays the WPSC product variations table
1086
 *
1087
 * @uses check_admin_referrer()                     Makes sure user was referred from another admin page
1088
 * @uses WPSC_Product_Variations_Page               The WPSC Product variations class
1089
 * @uses WPSC_Product_Variations_Page::display()    Displays the product variations page
1090
 */
1091
function wpsc_product_variations_table() {
1092
	check_admin_referer( 'wpsc_product_variations_table' );
1093
	set_current_screen( 'wpsc-product' );
1094
	require_once( WPSC_FILE_PATH . '/wpsc-admin/includes/product-variations-page.class.php' );
1095
	$page = new WPSC_Product_Variations_Page();
1096
	$page->display();
1097
1098
	exit;
1099
}
1100
add_action( 'wp_ajax_wpsc_product_variations_table', 'wpsc_product_variations_table' );
1101
1102
/**
1103
 * @access private
1104
 *
1105
 * @uses current_user_can()             Checks user capabilities given string
1106
 * @uses delete_post_thumbnail()        Deletes post thumbnail given thumbnail id
1107
 * @uses set_post_thumbnail()           Sets post thumbnail given post_id and thumbnail_id
1108
 * @uses wpsc_the_product_thumbnail()   Returns URL to the product thumbnail
1109
 *
1110
 * @return array    $response           Includes the thumbnail URL and success bool value
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1111
 */
1112
function _wpsc_ajax_set_variation_product_thumbnail() {
1113
	$response = array(
1114
		'success' => false
1115
	);
1116
1117
	$post_ID = intval( $_POST['post_id'] );
1118
	if ( current_user_can( 'edit_post', $post_ID ) ) {
1119
		$thumbnail_id = intval( $_POST['thumbnail_id'] );
1120
1121
		if ( $thumbnail_id == '-1' )
1122
			delete_post_thumbnail( $post_ID );
1123
1124
		set_post_thumbnail( $post_ID, $thumbnail_id );
1125
1126
		$thumbnail = wpsc_the_product_thumbnail( 50, 50, $post_ID, '' );
1127
		if ( ! $thumbnail )
1128
			$thumbnail = WPSC_CORE_IMAGES_URL . '/no-image-uploaded.gif';
1129
		$response['src'] = $thumbnail;
1130
		$response['success'] = true;
1131
	}
1132
1133
	echo json_encode( $response );
1134
	exit;
1135
}
1136
add_action( 'wp_ajax_wpsc_set_variation_product_thumbnail', '_wpsc_ajax_set_variation_product_thumbnail' );
1137
1138
/**
1139
 * Delete WPSC product image from gallery
1140
 *
1141
 * @uses check_ajax_referer()		Verifies the AJAX request to prevent processing external requests
1142
 * @uses get_post_meta()		Returns meta from the specified post
1143
 * @uses update_post_meta()		Updates meta from the specified post
1144
 */
1145
function product_gallery_image_delete_action() {
1146
1147
	$product_gallery = array();
1148
	$gallery_image_id = $gallery_post_id = '';
1149
1150
	$gallery_image_id = absint($_POST['product_gallery_image_id']);
1151
	$gallery_post_id = absint($_POST['product_gallery_post_id']);
1152
1153
	check_ajax_referer( 'wpsc_gallery_nonce', 'wpsc_gallery_nonce_check' );
1154
1155
	$product_gallery = get_post_meta( $gallery_post_id, '_wpsc_product_gallery', true );
1156
1157
	foreach ( $product_gallery as $index => $image_id ) {
1158
		if ( $image_id == $gallery_image_id ) {
1159
			unset( $product_gallery[$index] );
1160
		}
1161
	}
1162
1163
	update_post_meta( $gallery_post_id, '_wpsc_product_gallery', $product_gallery );
1164
}
1165
add_action( 'wp_ajax_product_gallery_image_delete', 'product_gallery_image_delete_action' );
1166