Completed
Push — master ( 30b9a3...a81e2b )
by Justin
11:10 queued 05:29
created

ajax.php ➔ _wpsc_ajax_purchase_log_refund_items()   D

Complexity

Conditions 12
Paths 410

Size

Total Lines 65
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

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