Completed
Push — master ( 9a3ee6...bca146 )
by Justin
05:42
created

ajax.php ➔ _wpsc_ajax_purchase_log_capture_payment()   C

Complexity

Conditions 9
Paths 14

Size

Total Lines 51
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 26
nc 14
nop 0
dl 0
loc 51
rs 6.2727
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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