Completed
Pull Request — master (#2185)
by Justin
06:08
created

ajax.php ➔ _wpsc_ajax_purchase_log_refund_items()   C

Complexity

Conditions 11
Paths 194

Size

Total Lines 53
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 30
nc 194
nop 0
dl 0
loc 53
rs 5.8259
c 0
b 0
f 0

How to fix   Long Method    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
function _wpsc_ajax_purchase_log_refund_items() {
3
	if ( isset( $_POST['order_id'] ) ) {
4
		
5
		_wpsc_ajax_verify_nonce( 'purchase_log_refund_items' );
6
7
		if ( ! current_user_can( 'manage_options' ) ) {
8
			die(-1);
9
		}
10
	
11
		$order_id               = absint( $_POST['order_id'] );
12
		$refund_reason          = isset( $_POST['refund_reason'] ) ? sanitize_text_field( $_POST['refund_reason'] ) : '';
13
		$refund_amount          = isset( $_POST['refund_amount'] ) ? sanitize_text_field( $_POST['refund_amount'] ) : false;
14
		$api_refund             = $_POST['api_refund'] === 'true' ? true : false;
15
		$refund                 = false;
16
		$response_data          = array();
17
18
		try {
19
			// Validate that the refund can occur
20
			$log            = new WPSC_Purchase_Log( $order_id );
21
			$order_items    = $log->get_items();
22
			$refund_amount  = $log->get( 'totalprice' );
23
24
			// Refund via API
25
			if ( $api_refund ) {
26
					if ( wpsc_payment_gateway_supports( $log->get( 'gateway' ), 'refunds' ) ) {
27
						// Send api request to process refund. Returns Refund transaction ID
28
						$result = wpsc_get_payment_gateway( $log->get( 'gateway' ) )->process_refund( $order_id, $refund_amount, $refund_reason );
29
30
						do_action( 'wpec_refund_processed', $log, $result );
31
32
						if ( is_wp_error( $result ) ) {
33
							throw new Exception( $result->get_error_message() );
34
						} elseif ( ! $result ) {
35
							throw new Exception( __( 'Refund failed', 'wp-e-commerce' ) );
36
						}
37
				}
38
			}
39
40
			// Trigger notifications and status changes
41
			do_action( 'wpec_order_fully_refunded', $order_id );
42
43
			$response_data['status'] = 'fully_refunded';
44
45
			do_action( 'wpec_order_refunded', $order_id );
46
47
			wp_send_json_success( $response_data );
48
49
		} catch ( Exception $e ) {
50
			wp_send_json_error( array( 'error' => $e->getMessage() ) );
51
		}
52
	}
53
	return new WP_Error( 'wpsc_ajax_invalid_purchase_log_refund_items', __( 'Refund failed.', 'wp-e-commerce' ) );	
54
}
55
56
/**
57
 * Verify nonce of an AJAX request
58
 *
59
 * @since  3.8.9
60
 * @access private
61
 *
62
 * @uses WP_Error           WordPress Error Class
63
 * @uses wp_verify_nonce()    Verify that correct nonce was used with time limit.
64
 *
65
 * @param string $ajax_action Name of AJAX action
66
 * @return WP_Error|boolean True if nonce is valid. WP_Error if otherwise.
67
 */
68
function _wpsc_ajax_verify_nonce( $ajax_action ) {
69
	// nonce can be passed with name wpsc_nonce or _wpnonce
70
	$nonce = '';
71
72
	if ( isset( $_REQUEST['nonce'] ) )
73
		$nonce = $_REQUEST['nonce'];
74
	elseif ( isset( $_REQUEST['_wpnonce'] ) )
75
		$nonce = $_REQUEST['_wpnonce'];
76
	else
77
		return _wpsc_error_invalid_nonce();
78
79
	// validate nonce
80
	if ( ! wp_verify_nonce( $nonce, 'wpsc_ajax_' . $ajax_action ) )
81
		return _wpsc_error_invalid_nonce();
82
83
	return true;
84
}
85
86
function _wpsc_error_invalid_nonce() {
87
	return new WP_Error( 'wpsc_ajax_invalid_nonce', __( 'Your session has expired. Please refresh the page and try again.', 'wp-e-commerce' ) );
88
}
89
90
/**
91
 * Verify AJAX callback and call it if it exists.
92
 *
93
 * @since  3.8.9
94
 * @access private
95
 *
96
 * @uses WP_Error   WordPress Error object
97
 *
98
 * @param  string $ajax_action Name of AJAX action
99
 * @return WP_Error|array Array of response args if callback is valid. WP_Error if otherwise.
100
 */
101
function _wpsc_ajax_fire_callback( $ajax_action ) {
102
	// if callback exists, call it and output JSON response
103
	$callback = "_wpsc_ajax_{$ajax_action}";
104
105
	if ( is_callable( $callback ) )
106
		$result = call_user_func( $callback );
107
	else
108
		$result = new WP_Error( 'wpsc_invalid_ajax_callback', __( 'Invalid AJAX callback.', 'wp-e-commerce' ) );
109
110
	return $result;
111
}
112
113
/**
114
 * AJAX handler for all WPEC ajax requests.
115
 *
116
 * This function automates nonce checking and outputs JSON response.
117
 *
118
 * @since 3.8.9
119
 * @access private
120
 *
121
 * @uses _wpsc_ajax_fire_callback()     Verify ajax callback if it exists
122
 * @uses _wpsc_ajax_verify_nonce()      Verify nonce of an ajax request
123
 * @uses is_wp_error()                  Check whether variable is a WordPress Error.
124
 *
125
 * @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...
126
 */
127
function _wpsc_ajax_handler() {
128
	$ajax_action = str_replace( '-', '_', $_REQUEST['wpsc_action'] );
129
130
	if ( is_callable( '_wpsc_ajax_verify_' . $ajax_action ) ) {
131
		$result = call_user_func( '_wpsc_ajax_verify_' . $ajax_action );
132
	} else {
133
		$result = _wpsc_ajax_verify_nonce( $ajax_action );
134
	}
135
136
	if ( ! is_wp_error( $result ) ) {
137
		$result = _wpsc_ajax_fire_callback( $ajax_action );
138
	}
139
140
	$output = array(
141
		'is_successful' => false,
142
	);
143
144
	if ( is_wp_error( $result ) ) {
145
		$output['error'] = array(
146
			'code'     => $result->get_error_code(),
147
			'messages' => $result->get_error_messages(),
148
			'data'     => $result->get_error_data(),
149
		);
150
	} else {
151
		$output['is_successful'] = true;
152
		$output['obj'] = $result;
153
	}
154
155
	echo json_encode( $output );
156
	exit;
157
}
158
add_action( 'wp_ajax_wpsc_ajax', '_wpsc_ajax_handler' );
159
160
/**
161
 * Checks if WPSC is doing ajax
162
 *
163
 * @param   string  $action     req     The action we're checking
164
 * @return  bool    True if doing ajax
165
 */
166
function wpsc_is_doing_ajax( $action = '' ) {
167
	$ajax = defined( 'DOING_AJAX' ) && DOING_AJAX && ! empty( $_REQUEST['action'] ) && $_REQUEST['action'] == 'wpsc_ajax';
168
169
	if ( $action ) {
170
		$ajax = $ajax && ! empty( $_REQUEST['wpsc_action'] ) && $action == str_replace( '-', '_', $_REQUEST['wpsc_action'] );
171
	}
172
173
	return $ajax;
174
}
175
176
/**
177
 * Helper function that generates nonce for an AJAX action. Basically just a wrapper of
178
 * wp_create_nonce() but automatically add prefix.
179
 *
180
 * @since  3.8.9
181
 * @access private
182
 *
183
 * @uses wp_create_nonce()  Creates a random one time use token
184
 *
185
 * @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...
186
 * @return string         The generated nonce.
187
 */
188
function _wpsc_create_ajax_nonce( $ajax_action ) {
189
	return wp_create_nonce( "wpsc_ajax_{$ajax_action}" );
190
}
191
192
/**
193
 * Add new variation set via AJAX.
194
 *
195
 * If the variation set name is the same as an existing variation set,
196
 * the children variant terms will be added inside that existing set.
197
 *
198
 * @since 3.8.8
199
 * @access private
200
 *
201
 * @uses term_exists()                      Returns true if term exists
202
 * @uses get_term()                         Gets all term data by term_id
203
 * @uses wp_insert_term()                   Inserts a term to the WordPress database
204
 * @uses is_wp_error()                      Checks whether variable is a WordPress error
205
 * @uses WP_Error                           WordPress Error class
206
 * @uses clean_term_cache()                 Will remove all of the term ids from the cache.
207
 * @uses delete_option()                    Deletes option from the database
208
 * @uses wp_cache_set()                     Saves the data to the cache.
209
 * @uses _get_term_hierarchy()              Retrieves children of taxonomy as Term IDs.
210
 * @uses wp_terms_checklist()               Output an unordered list of checkbox <input> elements labelled
211
 * @uses WPSC_Walker_Variation_Checklist    Walker variation checklist
212
 *
213
 * @return array Response args
214
 */
215
function _wpsc_ajax_add_variation_set() {
216
	$new_variation_set = $_POST['variation_set'];
217
	$variants = preg_split( '/\s*,\s*/', $_POST['variants'] );
218
219
	$return = array();
220
221
	$parent_term_exists = term_exists( $new_variation_set, 'wpsc-variation' );
222
223
	// only use an existing parent ID if the term is not a child term
224
	if ( $parent_term_exists ) {
225
		$parent_term = get_term( $parent_term_exists['term_id'], 'wpsc-variation' );
226
		if ( $parent_term->parent == '0' )
227
			$variation_set_id = $parent_term_exists['term_id'];
228
	}
229
230
	if ( empty( $variation_set_id ) ) {
231
		$results = wp_insert_term( apply_filters( 'wpsc_new_variation_set', $new_variation_set ), 'wpsc-variation' );
232
		if ( is_wp_error( $results ) )
233
			return $results;
234
		$variation_set_id = $results['term_id'];
235
	}
236
237
	if ( empty( $variation_set_id ) )
238
		return new WP_Error( 'wpsc_invalid_variation_id', __( 'Cannot retrieve the variation set in order to proceed.', 'wp-e-commerce' ) );
239
240
	foreach ( $variants as $variant ) {
241
		$results = wp_insert_term( apply_filters( 'wpsc_new_variant', $variant, $variation_set_id ), 'wpsc-variation', array( 'parent' => $variation_set_id ) );
242
243
		if ( is_wp_error( $results ) )
244
			return $results;
245
246
		$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...
247
	}
248
249
	require_once( 'includes/walker-variation-checklist.php' );
250
251
	if ( ! version_compare( $GLOBALS['wp_version'], '3.8.3', '>' ) ) {
252
253
		/* --- DIRTY HACK START --- */
254
		/*
255
		There's a bug with term cache in WordPress core. See http://core.trac.wordpress.org/ticket/14485. Fixed in 3.9.
256
		The next 3 lines will delete children term cache for wpsc-variation.
257
		Without this hack, the new child variations won't be displayed on "Variations" page and
258
		also won't be displayed in wp_terms_checklist() call below.
259
		*/
260
		clean_term_cache( $variation_set_id, 'wpsc-variation' );
261
		delete_option('wpsc-variation_children');
262
		wp_cache_set( 'last_changed', 1, 'terms' );
263
		_get_term_hierarchy('wpsc-variation');
264
		/* --- DIRTY HACK END --- */
265
266
	}
267
268
	ob_start();
269
270
	wp_terms_checklist( (int) $_POST['post_id'], array(
271
		'taxonomy'      => 'wpsc-variation',
272
		'descendants_and_self' => $variation_set_id,
273
		'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...
274
		'checked_ontop' => false,
275
	) );
276
277
	$content = ob_get_clean();
278
279
	$return = array(
280
		'variation_set_id'  => $variation_set_id,
281
		'inserted_variants' => $inserted_variants,
282
		'content'           => $content,
283
	);
284
285
	return $return;
286
}
287
288
/**
289
 * Display gateway settings form via AJAX
290
 *
291
 * @since  3.8.9
292
 * @access private
293
 *
294
 * @uses WPSC_Settings_Tab_Gateway
295
 * @uses WPSC_Settings_Tab_Gateway::display_payment_gateway_settings_form()     Displays payment gateway form
296
 *
297
 * @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...
298
 */
299
function _wpsc_ajax_payment_gateway_settings_form() {
300
301
	require_once( 'settings-page.php' );
302
	require_once( 'includes/settings-tabs/gateway.php' );
303
304
	$return = array();
305
	ob_start();
306
	$tab = new WPSC_Settings_Tab_Gateway();
307
	$tab->display_payment_gateway_settings_form();
308
	$return['content'] = ob_get_clean();
309
310
	return $return;
311
}
312
313
/**
314
 * Display shipping module settings form via AJAX
315
 *
316
 * @since  3.8.9
317
 * @access private
318
 *
319
 * @uses WPSC_Settings_Table_Shipping
320
 * @uses WPSC_Settings_Table_Shipping::display_shipping_module_settings_form()  Displays shipping module form
321
 *
322
 * @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...
323
 */
324
function _wpsc_ajax_shipping_module_settings_form() {
325
	require_once( 'settings-page.php' );
326
	require_once( 'includes/settings-tabs/shipping.php' );
327
328
	$return = array();
329
	ob_start();
330
	$tab = new WPSC_Settings_Tab_Shipping();
331
	$tab->display_shipping_module_settings_form();
332
	$return['content'] = ob_get_clean();
333
334
	return $return;
335
}
336
337
/**
338
 * Display settings tab via AJAX
339
 *
340
 * @since 3.8.9
341
 * @access private
342
 *
343
 * @uses WPSC_Settings_Page
344
 * @uses WPSC_Settings_Page::display_current_tab()  Shows current tab of settings page
345
 *
346
 * @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...
347
 */
348
function _wpsc_ajax_navigate_settings_tab() {
349
	require_once( 'settings-page.php' );
350
351
	$return = array();
352
	ob_start();
353
	$settings_page = new WPSC_Settings_Page( $_POST['tab'] );
354
	$settings_page->display_current_tab();
355
	$return['content'] = ob_get_clean();
356
357
	return $return;
358
}
359
360
/**
361
 * Display base region list in Store Settings -> General
362
 *
363
 * @since 3.8.9
364
 * @access private
365
 *
366
 * @uses WPSC_Settings_Tab_General
367
 * @uses WPSC_Settings_Tab_General::display_region_drop_down()  Shows region dropdown
368
 *
369
 * @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...
370
 */
371
function _wpsc_ajax_display_region_list() {
372
	require_once( 'settings-page.php' );
373
	require_once( 'includes/settings-tabs/general.php' );
374
375
	$return = array();
376
	ob_start();
377
	$tab = new WPSC_Settings_Tab_General();
378
	$tab->display_region_drop_down();
379
	$return['content'] = ob_get_clean();
380
381
	return $return;
382
}
383
384
/**
385
 * Save tracking ID of a sales log.
386
 *
387
 * @since 3.8.9
388
 * @access private
389
 *
390
 * @uses WP_Error   WordPress Error class
391
 *
392
 * @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...
393
 */
394
function _wpsc_ajax_purchase_log_save_tracking_id() {
395
	global $wpdb;
396
397
	$result = $wpdb->update(
398
		WPSC_TABLE_PURCHASE_LOGS,
399
		array(
400
			'track_id' => $_POST['value']
401
		),
402
		array(
403
			'id' => $_POST['log_id']
404
		),
405
		'%s',
406
		'%d'
407
	);
408
409
	if ( ! $result )
410
		return new WP_Error( 'wpsc_cannot_save_tracking_id', __( "Couldn't save tracking ID of the transaction. Please try again.", 'wp-e-commerce' ) );
411
412
	$return = array(
413
		'rows_affected' => $result,
414
		'id'            => $_POST['log_id'],
415
		'track_id'      => $_POST['value'],
416
	);
417
418
	return $return;
419
}
420
421
/**
422
 * Send sales log tracking email via AJAX
423
 *
424
 * @since 3.8.9
425
 * @access private
426
 *
427
 * @uses $wpdb              WordPress database object for queries
428
 * @uses get_option()       Gets option from DB given key
429
 * @uses add_filter()       Calls 'wp_mail_from' which can replace the from email address
430
 * @uses add_filter()       Calls 'wp_mail_from_name' allows replacement of the from name on WordPress emails
431
 * @uses wp_mail()          All the emailses in WordPress are sent through this function
432
 * @uses WP_Error           WordPress Error class
433
 *
434
 * @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...
435
 */
436
function _wpsc_ajax_purchase_log_send_tracking_email() {
437
	global $wpdb;
438
439
	$id = absint( $_POST['log_id'] );
440
	$sql = $wpdb->prepare( "SELECT `track_id` FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE `id`=%d LIMIT 1", $id );
441
	$trackingid = $wpdb->get_var( $sql );
442
443
	$message = get_option( 'wpsc_trackingid_message' );
444
	$message = str_replace( '%trackid%', $trackingid, $message );
445
	$message = str_replace( '%shop_name%', get_option( 'blogname' ), $message );
446
447
	$email = wpsc_get_buyers_email( $id );
448
449
	$subject = get_option( 'wpsc_trackingid_subject' );
450
	$subject = str_replace( '%shop_name%', get_option( 'blogname' ), $subject );
451
452
	add_filter( 'wp_mail_from', 'wpsc_replace_reply_address', 0 );
453
	add_filter( 'wp_mail_from_name', 'wpsc_replace_reply_name', 0 );
454
455
	$result = wp_mail( $email, $subject, $message);
456
457
	if ( ! $result ) {
458
		return new WP_Error( 'wpsc_cannot_send_tracking_email', __( "Couldn't send tracking email. Please try again.", 'wp-e-commerce' ) );
459
	}
460
461
	$return = array(
462
		'id'          => $id,
463
		'tracking_id' => $trackingid,
464
		'subject'     => $subject,
465
		'message'     => $message,
466
		'email'       => $email
467
	);
468
469
	return $return;
470
}
471
472
/**
473
 * Do purchase log action link via AJAX
474
 *
475
 * @since   3.9.0
476
 * @access  private
477
 *
478
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
479
 */
480
function _wpsc_ajax_purchase_log_action_link() {
481
482
	if ( isset( $_POST['log_id'] ) && isset( $_POST['purchase_log_action_link'] ) && isset( $_POST['purchase_log_action_nonce'] ) ) {
483
484
		$log_id = absint( $_POST['log_id'] );
485
		$purchase_log_action_link = sanitize_key( $_POST['purchase_log_action_link'] );
486
487
		// Verify action nonce
488
		if ( wp_verify_nonce( $_POST['purchase_log_action_nonce'], 'wpsc_purchase_log_action_ajax_' . $purchase_log_action_link ) ) {
489
490
			// Expected to receive success = true by default, or false on error.
491
			$return = apply_filters( 'wpsc_purchase_log_action_ajax-' . $purchase_log_action_link, array( 'success' => null ), $log_id );
492
493
		} else {
494
			$return = _wpsc_error_invalid_nonce();
495
		}
496
497
		if ( ! is_wp_error( $return ) ) {
498
			$return['log_id'] = $log_id;
499
			$return['purchase_log_action_link'] = $purchase_log_action_link;
500
			$return['success'] = isset( $return['success'] ) ? (bool) $return['success'] : null;
501
		}
502
503
		return $return;
504
505
	}
506
507
	return new WP_Error( 'wpsc_ajax_invalid_purchase_log_action', __( 'Purchase log action failed.', 'wp-e-commerce' ) );
508
509
}
510
511
/**
512
 * Remove purchase log item.
513
 *
514
 * @since   4.0
515
 * @access  private
516
 *
517
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
518
 */
519
function _wpsc_ajax_remove_log_item() {
520
521
	if ( isset( $_POST['item_id'], $_POST['log_id'] ) ) {
522
523
		$item_id = absint( $_POST['item_id'] );
524
		$log_id  = absint( $_POST['log_id'] );
525
		$log     = new WPSC_Purchase_Log( $log_id );
526
527
		if ( $log->remove_item( $item_id ) ) {
528
			return _wpsc_init_log_items( $log );
529
		}
530
	}
531
532
	return new WP_Error( 'wpsc_ajax_invalid_remove_log_item', __( 'Removing log item failed.', 'wp-e-commerce' ) );
533
}
534
535
/**
536
 * Update purchase log item quantity.
537
 *
538
 * @since   4.0
539
 * @access  private
540
 *
541
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
542
 */
543
function _wpsc_ajax_update_log_item_qty() {
544
545
	if ( isset( $_POST['item_id'], $_POST['log_id'], $_POST['qty'] ) ) {
546
547
		if ( empty( $_POST['qty'] ) ) {
548
			return _wpsc_ajax_remove_log_item();
549
		}
550
551
		$item_id = absint( $_POST['item_id'] );
552
		$log_id  = absint( $_POST['log_id'] );
553
		$log     = new WPSC_Purchase_Log( $log_id );
554
		$result  = $log->update_item( $item_id, array( 'quantity' => absint( $_POST['qty'] ) ) );
555
556
		if ( 0 === $result ) {
557
			return true;
558
		} elseif ( false !== $result ) {
559
			return _wpsc_init_log_items( $log );
560
		}
561
	}
562
563
	return new WP_Error( 'wpsc_ajax_invalid_update_log_item_qty', __( 'Updating log item quantity failed.', 'wp-e-commerce' ) );
564
}
565
566
/**
567
 * Add purchase log item.
568
 *
569
 * @since   4.0
570
 * @access  private
571
 *
572
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
573
 */
574
function _wpsc_ajax_add_log_item() {
575
	global $wpsc_cart;
576
577
	if (
578
		isset( $_POST['product_ids'], $_POST['log_id'] )
579
		&& is_array( $_POST['product_ids'] )
580
		&& ! empty( $_POST['product_ids'] )
581
	) {
582
583
		$existing = isset( $_POST['existing'] ) && is_array( $_POST['existing'] )
584
			? array_map( 'absint', $_POST['existing'] )
585
			: false;
586
587
		$item_ids = array();
588
		$log      = null;
589
590
		foreach ( $_POST['product_ids'] as $product_id ) {
591
			$product_id = absint( $product_id );
592
			$log_id     = absint( $_POST['log_id'] );
593
			$log        = new WPSC_Purchase_Log( $log_id );
594
595
			// Is product is already in item list?
596
			if ( $existing && in_array( $product_id, $existing, true ) ) {
597
				$item = $log->get_item_from_product_id( $product_id );
598
				if ( $item ) {
599
					// Update item quantity...
600
					$log->update_item( $item->id, array( 'quantity' => ++$item->quantity ) );
601
					// And move on.
602
					continue;
603
				}
604
			}
605
606
			$item       = new wpsc_cart_item( $product_id, array(), $wpsc_cart );
607
			$item_id    = $item->save_to_db( $log_id );
608
			$item_ids[] = absint( $item_id );
609
		}
610
611
		return _wpsc_init_log_items( $log, $item_ids );
0 ignored issues
show
Bug introduced by
It seems like $log defined by null on line 588 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...
612
	}
613
614
	return new WP_Error( 'wpsc_ajax_invalid_add_log_item', __( 'Adding log item failed.', 'wp-e-commerce' ) );
615
}
616
617
function _wpsc_init_log_items( WPSC_Purchase_Log $log, $item_ids = array() ) {
618
	$log->init_items();
619
620
	require_once( WPSC_FILE_PATH . '/wpsc-admin/display-sales-logs.php' );
621
622
	$html = '';
623
	$htmls = array();
624
	$htmls[] = array();
625
626
	while ( wpsc_have_purchaselog_details() ) {
627
		wpsc_the_purchaselog_item();
628
629
		ob_start();
630
		WPSC_Purchase_Log_Page::purchase_log_cart_item( $log->can_edit() );
631
		$cart_item = ob_get_clean();
632
633
		$htmls[ wpsc_purchaselog_details_id() ] = $cart_item;
634
		if ( ! empty( $item_ids ) && in_array( absint( wpsc_purchaselog_details_id() ), $item_ids, true ) ) {
635
			$html .= $cart_item;
636
		}
637
	}
638
639
	return array(
640
		'quantities'     => wp_list_pluck( $log->get_items(), 'quantity', 'id' ),
641
		'html'           => $html,
642
		'htmls'          => $htmls,
643
		'discount_data'  => wpsc_purchlog_has_discount_data() ? esc_html__( 'Coupon Code', 'wp-e-commerce' ) . ': ' . wpsc_display_purchlog_discount_data() : '',
644
		'discount'       => wpsc_display_purchlog_discount(),
645
		'total_taxes'    => wpsc_display_purchlog_taxes(),
646
		'total_shipping' => wpsc_display_purchlog_shipping( false, true ),
647
		'final_total'    => wpsc_display_purchlog_totalprice(),
648
	);
649
}
650
651
/**
652
 * Edit log contact details.
653
 *
654
 * @since   4.0
655
 * @access  private
656
 *
657
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
658
 */
659
function _wpsc_ajax_edit_contact_details() {
660
661
	if ( isset( $_POST['log_id'], $_POST['fields'] ) && ! empty( $_POST['fields'] ) ) {
662
663
		// Parse the URL query string of the fields array.
664
		parse_str( $_POST['fields'], $fields );
665
666
		$log_id = absint( $_POST['log_id'] );
667
		$log    = new WPSC_Purchase_Log( $log_id );
668
669
		if ( isset( $fields['wpsc_checkout_details'] ) && is_array( $fields['wpsc_checkout_details'] ) ) {
670
			$details = wp_unslash( $fields['wpsc_checkout_details'] );
671
672
			// Save the new/updated contact details.
673
			WPSC_Checkout_Form_Data::save_form(
674
				$log,
675
				WPSC_Checkout_Form::get()->get_fields(),
676
				array_map( 'sanitize_text_field', $details ),
677
				false
678
			);
679
680
			require_once( WPSC_FILE_PATH . '/wpsc-admin/display-sales-logs.php' );
681
682
			$log->init_items();
683
684
			// Fetch the shipping/billing formatted output.
685
686
			ob_start();
687
			WPSC_Purchase_Log_Page::shipping_address_output();
688
			$shipping = ob_get_clean();
689
690
			ob_start();
691
			WPSC_Purchase_Log_Page::billing_address_output();
692
			$billing = ob_get_clean();
693
694
			ob_start();
695
			WPSC_Purchase_Log_Page::payment_details_output();
696
			$payment = ob_get_clean();
697
698
			return compact( 'shipping', 'billing', 'payment' );
699
700
		}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
701
702
	}
703
704
	return new WP_Error( 'wpsc_ajax_invalid_edit_contact_details', __( 'Failed to update contact details for log.', 'wp-e-commerce' ) );
705
}
706
707
/**
708
 * Add a note to a log.
709
 *
710
 * @since   4.0
711
 * @access  private
712
 *
713
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
714
 */
715
function _wpsc_ajax_add_note() {
716
717
	if ( isset( $_POST['log_id'], $_POST['note'] ) && ! empty( $_POST['note'] ) ) {
718
719
		$result = wpsc_purchlogs_update_notes(
720
			absint( $_POST['log_id'] ),
721
			wp_kses_post( wp_unslash( $_POST['note'] ) )
722
		);
723
724
		if ( $result instanceof WPSC_Purchase_Log_Notes ) {
725
			require_once( WPSC_FILE_PATH . '/wpsc-admin/display-sales-logs.php' );
726
727
			$data      = $result->get_data();
728
			$keys      = array_keys( $data );
729
			$note_id   = end( $keys );
730
			$note_args = end( $data );
731
732
			ob_start();
733
			WPSC_Purchase_Log_Page::note_output( $result, $note_id, $note_args );
734
			$row = ob_get_clean();
735
736
			return $row;
737
		}
738
	}
739
740
	return new WP_Error( 'wpsc_ajax_invalid_add_note', __( 'Failed adding log note.', 'wp-e-commerce' ) );
741
}
742
743
/**
744
 * Delete a note from a log.
745
 *
746
 * @since   4.0
747
 * @access  private
748
 *
749
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
750
 */
751
function _wpsc_ajax_delete_note() {
752
753
	if ( isset( $_POST['log_id'], $_POST['note'] ) && is_numeric( $_POST['note'] ) ) {
754
755
		$notes = wpsc_get_order_notes( absint( $_POST['log_id'] ) );
756
		$notes->remove( absint( $_POST['note'] ) )->save();
757
758
		return true;
759
	}
760
761
	return new WP_Error( 'wpsc_ajax_invalid_delete_note', __( 'Failed to delete log note.', 'wp-e-commerce' ) );
762
}
763
764
/**
765
 * Search for products.
766
 *
767
 * @since   4.0
768
 * @access  private
769
 *
770
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
771
 */
772
function _wpsc_ajax_search_products() {
773
	$pt_object = get_post_type_object( 'wpsc-product' );
774
775
	$s = wp_unslash( $_POST['search'] );
776
	$args = array(
777
		'post_type' => 'wpsc-product',
778
		'post_status' => array( 'publish', 'inherit' ),
779
		'posts_per_page' => 50,
780
	);
781
	if ( '' !== $s ) {
782
		$args['s'] = $s;
783
	}
784
785
	$posts = get_posts( $args );
786
787
	if ( ! $posts ) {
788
		return new WP_Error( 'wpsc_ajax_invalid_search_products', __( 'No items found.', 'wp-e-commerce' ) );
789
	}
790
791
	$alt = '';
792
	foreach ( $posts as $post ) {
793
		$post->title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
794
		$alt = ( 'alternate' === $alt ) ? '' : 'alternate';
795
796
		$post->status = $post->post_status;
797
798
		switch ( $post->post_status ) {
799
			case 'publish' :
800
			case 'private' :
801
				$post->status = __( 'Published' );
802
				break;
803
			case 'future' :
804
				$post->status = __( 'Scheduled' );
805
				break;
806
			case 'pending' :
807
				$post->status = __( 'Pending Review' );
808
				break;
809
			case 'draft' :
810
				$post->status = __( 'Draft' );
811
				break;
812
			default :
813
				$post->status = $post->post_status;
814
				break;
815
		}
816
817
		if ( '0000-00-00 00:00:00' === $post->post_date ) {
818
			$post->time = '';
819
		} else {
820
			/* translators: date format in table columns, see https://secure.php.net/date */
821
			$post->time = mysql2date( __( 'Y/m/d' ), $post->post_date );
822
		}
823
824
		$post->class = $alt;
825
	}
826
827
	return $posts;
828
}
829
830
/**
831
 * Handle AJAX clear downloads lock purchase log action
832
 *
833
 * The _wpsc_ajax_purchase_log_action_link() function which triggers this function is nonce
834
 * and capability checked in _wpsc_ajax_handler().
835
 *
836
 * @since   3.9.0
837
 * @access  private
838
 *
839
 * @param  array  $response  AJAX response.
840
 * @param  int    $log_id    Purchase log ID.
841
 */
842
function wpsc_purchase_log_action_ajax_downloads_lock( $response, $log_id ) {
843
844
	$response['success'] = wpsc_purchlog_clear_download_items( $log_id );
845
846
	return $response;
847
848
}
849
add_action( 'wpsc_purchase_log_action_ajax-downloads_lock', 'wpsc_purchase_log_action_ajax_downloads_lock', 10, 2 );
850
851
852
/**
853
 * Handle AJAX email receipt purchase log action
854
 *
855
 * The _wpsc_ajax_purchase_log_action_link() function which triggers this function is nonce
856
 * and capability checked in _wpsc_ajax_handler().
857
 *
858
 * @since   3.9.0
859
 * @access  private
860
 *
861
 * @param  array  $response  AJAX response.
862
 * @param  int    $log_id    Purchase log ID.
863
 */
864
function wpsc_purchase_log_action_ajax_email_receipt( $response, $log_id ) {
865
866
	$response['success'] = wpsc_purchlog_resend_email( $log_id );
867
868
	return $response;
869
870
}
871
add_action( 'wpsc_purchase_log_action_ajax-email_receipt', 'wpsc_purchase_log_action_ajax_email_receipt', 10, 2 );
872
873
/**
874
 * Delete an attached downloadable file via AJAX.
875
 *
876
 * @since 3.8.9
877
 * @access private
878
 *
879
 * @uses _wpsc_delete_file()    Deletes files associated with a product
880
 * @uses WP_Error               WordPress error class
881
 *
882
 * @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...
883
 */
884
function _wpsc_ajax_delete_file() {
885
	$product_id = absint( $_REQUEST['product_id'] );
886
	$file_name = basename( $_REQUEST['file_name'] );
887
888
	$result = _wpsc_delete_file( $product_id, $file_name );
889
890
	if ( ! $result )
891
		return new WP_Error( 'wpsc_cannot_delete_file', __( "Couldn't delete the file. Please try again.", 'wp-e-commerce' ) );
892
893
	$return = array(
894
		'product_id' => $product_id,
895
		'file_name'  => $file_name,
896
	);
897
898
	return $return;
899
}
900
901
/**
902
 * Delete a product meta via AJAX
903
 *
904
 * @since 3.8.9
905
 * @access private
906
 *
907
 * @uses delete_meta()      Deletes metadata by meta id
908
 * @uses WP_Error           WordPress error class
909
 *
910
 * @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...
911
 */
912
function _wpsc_ajax_remove_product_meta() {
913
	$meta_id = (int) $_POST['meta_id'];
914
	if ( ! delete_meta( $meta_id ) )
915
		return new WP_Error( 'wpsc_cannot_delete_product_meta', __( "Couldn't delete product meta. Please try again.", 'wp-e-commerce' ) );
916
917
	return array( 'meta_id' => $meta_id );
918
}
919
920
/**
921
 * Modify a purchase log's status.
922
 *
923
 * @since 3.8.9
924
 * @access private
925
 *
926
 * @uses wpsc_purchlog_edit_status()                    Edits purchase log status
927
 * @uses WP_Error                                       WordPress Error class
928
 * @uses WPSC_Purchase_Log_List_Table
929
 * @uses WPSC_Purchase_Log_List_Table::prepare_items()
930
 * @uses WPSC_Purchase_Log_List_Table::views()
931
 * @uses WPSC_Purchase_Log_List_Table::display_tablenav()   @todo docs
932
 *
933
 * @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...
934
 */
935
function _wpsc_ajax_change_purchase_log_status() {
936
	$result = wpsc_purchlog_edit_status( $_POST['id'], $_POST['new_status'] );
937
	if ( ! $result )
938
		return new WP_Error( 'wpsc_cannot_edit_purchase_log_status', __( "Couldn't modify purchase log's status. Please try again.", 'wp-e-commerce' ) );
939
940
	$args = array();
941
942
	$args['screen'] = 'dashboard_page_wpsc-sales-logs';
943
944
	require_once( WPSC_FILE_PATH . '/wpsc-admin/includes/purchase-log-list-table-class.php' );
945
	$purchaselog_table = new WPSC_Purchase_Log_List_Table( $args );
946
	$purchaselog_table->prepare_items();
947
948
	ob_start();
949
	$purchaselog_table->views();
950
	$views = ob_get_clean();
951
952
	ob_start();
953
	$purchaselog_table->display_tablenav( 'top' );
954
	$tablenav_top = ob_get_clean();
955
956
	ob_start();
957
	$purchaselog_table->display_tablenav( 'bottom' );
958
	$tablenav_bottom = ob_get_clean();
959
960
	$return = array(
961
		'id'              => $_POST['id'],
962
		'new_status'      => $_POST['new_status'],
963
		'views'           => $views,
964
		'tablenav_top'    => $tablenav_top,
965
		'tablenav_bottom' => $tablenav_bottom,
966
	);
967
968
	return $return;
969
}
970
971
/**
972
 * Save product ordering after drag-and-drop sorting
973
 *
974
 * @since 3.8.9
975
 * @access private
976
 *
977
 * @uses $wpdb              WordPress database object for use in queries
978
 * @uses wp_update_post()   Updates post based on passed $args. Needs a post_id
979
 * @uses WP_Error           WordPress Error class
980
 *
981
 * @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...
982
 */
983
function _wpsc_ajax_save_product_order() {
984
985
	$products = array( );
0 ignored issues
show
introduced by
Empty array declaration must have no space between the parentheses
Loading history...
986
	foreach ( $_POST['post'] as $product ) {
987
		$products[] = (int) str_replace( 'post-', '', $product );
988
	}
989
990
	$failed = array();
991
	foreach ( $products as $order => $product_id ) {
992
		$result = wp_update_post( array(
993
			'ID' => $product_id,
994
			'menu_order' => $order,
995
		) );
996
997
		if ( ! $result )
998
			$failed[] = $product_id;
999
	}
1000
1001
	// Validate data before exposing to action
1002
	$category = isset( $_POST['category_id'] ) ? get_term_by( 'slug', $_POST['category_id'], 'wpsc_product_category' ) : false;
1003
	do_action( 'wpsc_save_product_order', $products, $category );
1004
1005
	if ( ! empty( $failed ) ) {
1006
		$error_data = array(
1007
			'failed_ids' => $failed,
1008
		);
1009
1010
		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 );
1011
	}
1012
1013
	return array(
1014
		'ids' => $products,
1015
	);
1016
}
1017
1018
/**
1019
 * Save Category Product Order
1020
 *
1021
 * Note that this uses the 'term_order' field in the 'term_relationships' table to store
1022
 * the order. Although this column presently seems to be unused by WordPress, the intention
1023
 * is it should be used to store the order of terms associates to a post, not the order
1024
 * of posts as we are doing. This shouldn't be an issue for WPEC unless WordPress adds a UI
1025
 * for this. More info at http://core.trac.wordpress.org/ticket/9547
1026
 *
1027
 * @since 3.9
1028
 * @access private
1029
 *
1030
 * @uses $wpdb   WordPress database object used for queries
1031
 */
1032
function _wpsc_save_category_product_order( $products, $category ) {
1033
	global $wpdb;
1034
1035
	// Only save category product order if in category
1036
	if ( ! $category )
1037
		return;
1038
1039
	// Save product order in term_relationships table
1040
	foreach ( $products as $order => $product_id ) {
1041
		$wpdb->update( $wpdb->term_relationships,
1042
			array( 'term_order' => $order ),
1043
			array( 'object_id' => $product_id, 'term_taxonomy_id' => $category->term_taxonomy_id ),
1044
			array( '%d' ),
1045
			array( '%d', '%d' )
1046
		);
1047
	}
1048
}
1049
add_action( 'wpsc_save_product_order', '_wpsc_save_category_product_order', 10, 2 );
1050
1051
/**
1052
 * Update Checkout fields order
1053
 *
1054
 * @since 3.8.9
1055
 * @access private
1056
 *
1057
 * @uses $wpdb      WordPress database object used for queries
1058
 * @uses WP_Error   WordPress error class
1059
 *
1060
 * @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...
1061
 */
1062
function _wpsc_ajax_update_checkout_fields_order() {
1063
	global $wpdb;
1064
1065
	$checkout_fields = $_REQUEST['sort_order'];
1066
	$order = 1;
1067
	$failed = array();
1068
	$modified = array();
1069
	foreach ( $checkout_fields as &$checkout_field ) {
1070
		// ignore new fields
1071
		if ( strpos( $checkout_field, 'new-field' ) === 0 )
1072
			continue;
1073
		$checkout_field = absint( preg_replace('/[^0-9]+/', '', $checkout_field ) );
1074
		$result = $wpdb->update(
1075
			WPSC_TABLE_CHECKOUT_FORMS,
1076
			array(
1077
				'checkout_order' => $order
1078
			),
1079
			array(
1080
				'id' => $checkout_field
1081
			),
1082
			'%d',
1083
			'%d'
1084
		);
1085
		$order ++;
1086
		if ( $result === false )
1087
			$failed[] = $checkout_field;
1088
		elseif ( $result > 0 )
1089
			$modified[] = $checkout_field;
1090
	}
1091
1092
	if ( ! empty( $failed ) )
1093
		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 ) );
1094
1095
	return array(
1096
		'modified' => $modified,
1097
	);
1098
}
1099
1100
/**
1101
 * Save a downloadable file to a product
1102
 *
1103
 * @since 3.8.9
1104
 * @access private
1105
 *
1106
 * @uses $wpdb                          WordPress database object for use in queries
1107
 * @uses _wpsc_create_ajax_nonce()      Creates nonce for an ajax action
1108
 * @uses wpsc_get_mimetype()            Returns mimetype of file
1109
 * @uses wp_insert_post()               Inserts post to WordPress database
1110
 * @uses wp_nonce_url()                 Retrieve URL with nonce added to URL query.
1111
 * @uses wpsc_convert_bytes()           Formats bytes
1112
 * @uses wpsc_get_extension()           Gets extension of file
1113
 * @uses esc_attr()                     Escapes HTML attributes
1114
 * @uses _x()                           Retrieve translated string with gettext context
1115
 *
1116
 * @return array|WP_Error Response args if successful, WP_Error if otherwise.
1117
 */
1118
function _wpsc_ajax_upload_product_file() {
1119
	global $wpdb;
1120
	$product_id = absint( $_POST["product_id"] );
1121
	$output = '';
1122
	$delete_nonce = _wpsc_create_ajax_nonce( 'delete_file' );
1123
1124
	foreach ( $_POST["select_product_file"] as $selected_file ) {
1125
		// if we already use this file, there is no point doing anything more.
1126
		$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()
1127
		$file_post_data = $wpdb->get_row( $sql, ARRAY_A );
1128
		$selected_file_path = WPSC_FILE_DIR . basename( $selected_file );
1129
		$file_url = WPSC_FILE_URL . basename( $selected_file );
1130
		$file_size = filesize( $selected_file_path );
1131
		if ( empty( $file_post_data ) ) {
1132
			$type = wpsc_get_mimetype( $selected_file_path );
1133
			$attachment = array(
1134
				'post_mime_type' => $type,
1135
				'post_parent' => $product_id,
1136
				'post_title' => $selected_file,
1137
				'post_content' => '',
1138
				'post_type' => "wpsc-product-file",
1139
				'post_status' => 'inherit'
1140
			);
1141
			$id = wp_insert_post( $attachment );
1142
		} else {
1143
			// already attached
1144
			if ( $file_post_data['post_parent'] == $product_id )
1145
				continue;
1146
			$type = $file_post_data["post_mime_type"];
1147
			$url = $file_post_data["guid"];
1148
			$title = $file_post_data["post_title"];
1149
			$content = $file_post_data["post_content"];
1150
			// Construct the attachment
1151
			$attachment = array(
1152
				'post_mime_type' => $type,
1153
				'guid' => $url,
1154
				'post_parent' => absint( $product_id ),
1155
				'post_title' => $title,
1156
				'post_content' => $content,
1157
				'post_type' => "wpsc-product-file",
1158
				'post_status' => 'inherit'
1159
			);
1160
			// Save the data
1161
			$id = wp_insert_post( $attachment );
1162
		}
1163
1164
		$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'] );
1165
1166
		$output .= '<tr class="wpsc_product_download_row">';
1167
		$output .= '<td style="padding-right: 30px;">' . $attachment['post_title'] . '</td>';
1168
		$output .= '<td>' . wpsc_convert_byte( $file_size ) . '</td>';
1169
		$output .= '<td>.' . wpsc_get_extension( $attachment['post_title'] ) . '</td>';
1170
		$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>";
1171
		$output .= '<td><a href=' .$file_url .'>' . _x( 'Download', 'Digital Download UI row', 'wp-e-commerce' ) . '</a></td>';
1172
		$output .= '</tr>';
1173
	}
1174
1175
	return array(
1176
		'content' => $output,
1177
	);
1178
}
1179
1180
/**
1181
 * Generate variations
1182
 *
1183
 * @since 3.8.9
1184
 * @access private
1185
 *
1186
 * @uses wpsc_update_variations()       Updates product variations given
1187
 * @uses wpsc_admin_product_listing()   DEPRECATED
1188
 *
1189
 * @return array|WP_Error Response args if successful, WP_Error if otherwise
1190
 */
1191
function _wpsc_ajax_update_variations() {
1192
	$product_id = absint( $_REQUEST["product_id"] );
1193
	wpsc_update_variations();
1194
1195
	ob_start();
1196
	wpsc_admin_product_listing( $product_id );
1197
	$content = ob_get_clean();
1198
1199
	return array( 'content' => $content );
1200
}
1201
1202
/**
1203
 * Display the shortcode generator.
1204
 *
1205
 * @since  3.8.9
1206
 * @access private
1207
 */
1208
function _wpsc_action_tinymce_window() {
1209
	require_once( WPSC_CORE_JS_PATH . '/tinymce3/window.php' );
1210
	exit;
1211
}
1212
add_action( 'wp_ajax_wpsc_tinymce_window', '_wpsc_action_tinymce_window' );
1213
1214
/**
1215
 * Add tax rate
1216
 * @since  3.8.9
1217
 * @access private
1218
 *
1219
 * @uses wpec_taxes_controller                                                  Contains all the logic to communicate with the taxes system
1220
 * @uses wpec_taxes_controller::wpec_taxes::wpec_taxes_get_regions()            Gets tax regions based on input country code
1221
 * @uses wpec_taxes_controller::wpec_taxes_build_select_options()               Returns HTML formatted options from input array
1222
 * @uses wpec_taxes_controller::wpec_taxes_build_form()                         Builds the tax rate form
1223
 * @uses wpec_taxes_controller::wpec_taxes::wpec_taxes_get_band_from_index()    Retrieves tax band for given name
1224
 *
1225
 * @return array|WP_Error Response args if successful, WP_Error if otherwise
1226
 */
1227
function _wpsc_ajax_add_tax_rate() {
1228
	//include taxes controller
1229
	$wpec_taxes_controller = new wpec_taxes_controller;
1230
1231
	switch ( $_REQUEST['wpec_taxes_action'] ) {
1232
		case 'wpec_taxes_get_regions':
1233
			$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...
1234
			$key = $_REQUEST['current_key'];
1235
			$type = $_REQUEST['taxes_type'];
1236
			$default_option = array( 'region_code' => 'all-markets', 'name' => 'All Markets' );
1237
			$select_settings = array(
1238
				'id' => "{$type}-region-{$key}",
1239
				'name' => "wpsc_options[wpec_taxes_{$type}][{$key}][region_code]",
1240
				'class' => 'wpsc-taxes-region-drop-down'
1241
			);
1242
			$returnable = $wpec_taxes_controller->wpec_taxes_build_select_options( $regions, 'region_code', 'name', $default_option, $select_settings );
1243
			break;
1244
	}// switch
1245
1246
	return array(
1247
		'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...
1248
	);
1249
}
1250
1251
/**
1252
 * Displays the WPSC product variations table
1253
 *
1254
 * @uses check_admin_referrer()                     Makes sure user was referred from another admin page
1255
 * @uses WPSC_Product_Variations_Page               The WPSC Product variations class
1256
 * @uses WPSC_Product_Variations_Page::display()    Displays the product variations page
1257
 */
1258
function wpsc_product_variations_table() {
1259
	check_admin_referer( 'wpsc_product_variations_table' );
1260
	set_current_screen( 'wpsc-product' );
1261
	require_once( WPSC_FILE_PATH . '/wpsc-admin/includes/product-variations-page.class.php' );
1262
	$page = new WPSC_Product_Variations_Page();
1263
	$page->display();
1264
1265
	exit;
1266
}
1267
add_action( 'wp_ajax_wpsc_product_variations_table', 'wpsc_product_variations_table' );
1268
1269
/**
1270
 * @access private
1271
 *
1272
 * @uses current_user_can()             Checks user capabilities given string
1273
 * @uses delete_post_thumbnail()        Deletes post thumbnail given thumbnail id
1274
 * @uses set_post_thumbnail()           Sets post thumbnail given post_id and thumbnail_id
1275
 * @uses wpsc_the_product_thumbnail()   Returns URL to the product thumbnail
1276
 *
1277
 * @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...
1278
 */
1279
function _wpsc_ajax_set_variation_product_thumbnail() {
1280
	$response = array(
1281
		'success' => false
1282
	);
1283
1284
	$post_ID = intval( $_POST['post_id'] );
1285
	if ( current_user_can( 'edit_post', $post_ID ) ) {
1286
		$thumbnail_id = intval( $_POST['thumbnail_id'] );
1287
1288
		if ( $thumbnail_id == '-1' )
1289
			delete_post_thumbnail( $post_ID );
1290
1291
		set_post_thumbnail( $post_ID, $thumbnail_id );
1292
1293
		$thumbnail = wpsc_the_product_thumbnail( 50, 50, $post_ID, '' );
1294
		if ( ! $thumbnail )
1295
			$thumbnail = WPSC_CORE_IMAGES_URL . '/no-image-uploaded.gif';
1296
		$response['src'] = $thumbnail;
1297
		$response['success'] = true;
1298
	}
1299
1300
	echo json_encode( $response );
1301
	exit;
1302
}
1303
add_action( 'wp_ajax_wpsc_set_variation_product_thumbnail', '_wpsc_ajax_set_variation_product_thumbnail' );
1304
1305
/**
1306
 * Delete WPSC product image from gallery
1307
 *
1308
 * @uses check_ajax_referer()		Verifies the AJAX request to prevent processing external requests
1309
 * @uses get_post_meta()		Returns meta from the specified post
1310
 * @uses update_post_meta()		Updates meta from the specified post
1311
 */
1312
function product_gallery_image_delete_action() {
1313
1314
	$product_gallery = array();
1315
	$gallery_image_id = $gallery_post_id = '';
1316
1317
	$gallery_image_id = absint($_POST['product_gallery_image_id']);
1318
	$gallery_post_id = absint($_POST['product_gallery_post_id']);
1319
1320
	check_ajax_referer( 'wpsc_gallery_nonce', 'wpsc_gallery_nonce_check' );
1321
1322
	$product_gallery = get_post_meta( $gallery_post_id, '_wpsc_product_gallery', true );
1323
1324
	foreach ( $product_gallery as $index => $image_id ) {
1325
		if ( $image_id == $gallery_image_id ) {
1326
			unset( $product_gallery[$index] );
1327
		}
1328
	}
1329
1330
	update_post_meta( $gallery_post_id, '_wpsc_product_gallery', $product_gallery );
1331
}
1332
add_action( 'wp_ajax_product_gallery_image_delete', 'product_gallery_image_delete_action' );