Completed
Pull Request — master (#2185)
by
unknown
06:14
created

ajax.php ➔ _wpsc_ajax_purchase_log_refund_items()   D

Complexity

Conditions 11
Paths 290

Size

Total Lines 50
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 29
nc 290
nop 0
dl 0
loc 50
rs 4
c 0
b 0
f 0

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
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
		try {
19
			// Validate that the refund can occur
20
			$log            = new WPSC_Purchase_Log( $order_id );
21
			$order_items    = $log->get_items();
22
			$refund_amount  = $refund_amount ? $refund_amount : $log->get( 'totalprice' );
23
24
			if ( wpsc_payment_gateway_supports( $log->get( 'gateway' ), 'refunds' ) ) {
25
				// Send api request to process refund. Returns Refund transaction ID
26
				$result = wpsc_get_payment_gateway( $log->get( 'gateway' ) )->process_refund( $order_id, $refund_amount, $refund_reason, $manual );
27
28
				do_action( 'wpec_refund_processed', $log, $result );
29
30
				if ( is_wp_error( $result ) ) {
31
					throw new Exception( $result->get_error_message() );
32
				} elseif ( ! $result ) {
33
					throw new Exception( __( 'Refund failed', 'wp-e-commerce' ) );
34
				}
35
			}
36
37
			// Trigger notifications and status changes
38
			do_action( 'wpec_order_fully_refunded', $order_id );
39
40
			$response_data['status'] = 'fully_refunded';
41
42
			do_action( 'wpec_order_refunded', $order_id );
43
44
			wp_send_json_success( $response_data );
45
46
		} catch ( Exception $e ) {
47
			wp_send_json_error( array( 'error' => $e->getMessage() ) );
48
		}
49
	}
50
	return new WP_Error( 'wpsc_ajax_invalid_purchase_log_refund_items', __( 'Refund failed.', 'wp-e-commerce' ) );	
51
}
52
53
/**
54
 * Verify nonce of an AJAX request
55
 *
56
 * @since  3.8.9
57
 * @access private
58
 *
59
 * @uses WP_Error           WordPress Error Class
60
 * @uses wp_verify_nonce()    Verify that correct nonce was used with time limit.
61
 *
62
 * @param string $ajax_action Name of AJAX action
63
 * @return WP_Error|boolean True if nonce is valid. WP_Error if otherwise.
64
 */
65
function _wpsc_ajax_verify_nonce( $ajax_action ) {
66
	// nonce can be passed with name wpsc_nonce or _wpnonce
67
	$nonce = '';
68
69
	if ( isset( $_REQUEST['nonce'] ) )
70
		$nonce = $_REQUEST['nonce'];
71
	elseif ( isset( $_REQUEST['_wpnonce'] ) )
72
		$nonce = $_REQUEST['_wpnonce'];
73
	else
74
		return _wpsc_error_invalid_nonce();
75
76
	// validate nonce
77
	if ( ! wp_verify_nonce( $nonce, 'wpsc_ajax_' . $ajax_action ) )
78
		return _wpsc_error_invalid_nonce();
79
80
	return true;
81
}
82
83
function _wpsc_error_invalid_nonce() {
84
	return new WP_Error( 'wpsc_ajax_invalid_nonce', __( 'Your session has expired. Please refresh the page and try again.', 'wp-e-commerce' ) );
85
}
86
87
/**
88
 * Verify AJAX callback and call it if it exists.
89
 *
90
 * @since  3.8.9
91
 * @access private
92
 *
93
 * @uses WP_Error   WordPress Error object
94
 *
95
 * @param  string $ajax_action Name of AJAX action
96
 * @return WP_Error|array Array of response args if callback is valid. WP_Error if otherwise.
97
 */
98
function _wpsc_ajax_fire_callback( $ajax_action ) {
99
	// if callback exists, call it and output JSON response
100
	$callback = "_wpsc_ajax_{$ajax_action}";
101
102
	if ( is_callable( $callback ) )
103
		$result = call_user_func( $callback );
104
	else
105
		$result = new WP_Error( 'wpsc_invalid_ajax_callback', __( 'Invalid AJAX callback.', 'wp-e-commerce' ) );
106
107
	return $result;
108
}
109
110
/**
111
 * AJAX handler for all WPEC ajax requests.
112
 *
113
 * This function automates nonce checking and outputs JSON response.
114
 *
115
 * @since 3.8.9
116
 * @access private
117
 *
118
 * @uses _wpsc_ajax_fire_callback()     Verify ajax callback if it exists
119
 * @uses _wpsc_ajax_verify_nonce()      Verify nonce of an ajax request
120
 * @uses is_wp_error()                  Check whether variable is a WordPress Error.
121
 *
122
 * @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...
123
 */
124
function _wpsc_ajax_handler() {
125
	$ajax_action = str_replace( '-', '_', $_REQUEST['wpsc_action'] );
126
127
	if ( is_callable( '_wpsc_ajax_verify_' . $ajax_action ) ) {
128
		$result = call_user_func( '_wpsc_ajax_verify_' . $ajax_action );
129
	} else {
130
		$result = _wpsc_ajax_verify_nonce( $ajax_action );
131
	}
132
133
	if ( ! is_wp_error( $result ) ) {
134
		$result = _wpsc_ajax_fire_callback( $ajax_action );
135
	}
136
137
	$output = array(
138
		'is_successful' => false,
139
	);
140
141
	if ( is_wp_error( $result ) ) {
142
		$output['error'] = array(
143
			'code'     => $result->get_error_code(),
144
			'messages' => $result->get_error_messages(),
145
			'data'     => $result->get_error_data(),
146
		);
147
	} else {
148
		$output['is_successful'] = true;
149
		$output['obj'] = $result;
150
	}
151
152
	echo json_encode( $output );
153
	exit;
154
}
155
add_action( 'wp_ajax_wpsc_ajax', '_wpsc_ajax_handler' );
156
157
/**
158
 * Checks if WPSC is doing ajax
159
 *
160
 * @param   string  $action     req     The action we're checking
161
 * @return  bool    True if doing ajax
162
 */
163
function wpsc_is_doing_ajax( $action = '' ) {
164
	$ajax = defined( 'DOING_AJAX' ) && DOING_AJAX && ! empty( $_REQUEST['action'] ) && $_REQUEST['action'] == 'wpsc_ajax';
165
166
	if ( $action ) {
167
		$ajax = $ajax && ! empty( $_REQUEST['wpsc_action'] ) && $action == str_replace( '-', '_', $_REQUEST['wpsc_action'] );
168
	}
169
170
	return $ajax;
171
}
172
173
/**
174
 * Helper function that generates nonce for an AJAX action. Basically just a wrapper of
175
 * wp_create_nonce() but automatically add prefix.
176
 *
177
 * @since  3.8.9
178
 * @access private
179
 *
180
 * @uses wp_create_nonce()  Creates a random one time use token
181
 *
182
 * @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...
183
 * @return string         The generated nonce.
184
 */
185
function _wpsc_create_ajax_nonce( $ajax_action ) {
186
	return wp_create_nonce( "wpsc_ajax_{$ajax_action}" );
187
}
188
189
/**
190
 * Add new variation set via AJAX.
191
 *
192
 * If the variation set name is the same as an existing variation set,
193
 * the children variant terms will be added inside that existing set.
194
 *
195
 * @since 3.8.8
196
 * @access private
197
 *
198
 * @uses term_exists()                      Returns true if term exists
199
 * @uses get_term()                         Gets all term data by term_id
200
 * @uses wp_insert_term()                   Inserts a term to the WordPress database
201
 * @uses is_wp_error()                      Checks whether variable is a WordPress error
202
 * @uses WP_Error                           WordPress Error class
203
 * @uses clean_term_cache()                 Will remove all of the term ids from the cache.
204
 * @uses delete_option()                    Deletes option from the database
205
 * @uses wp_cache_set()                     Saves the data to the cache.
206
 * @uses _get_term_hierarchy()              Retrieves children of taxonomy as Term IDs.
207
 * @uses wp_terms_checklist()               Output an unordered list of checkbox <input> elements labelled
208
 * @uses WPSC_Walker_Variation_Checklist    Walker variation checklist
209
 *
210
 * @return array Response args
211
 */
212
function _wpsc_ajax_add_variation_set() {
213
	$new_variation_set = $_POST['variation_set'];
214
	$variants = preg_split( '/\s*,\s*/', $_POST['variants'] );
215
216
	$return = array();
217
218
	$parent_term_exists = term_exists( $new_variation_set, 'wpsc-variation' );
219
220
	// only use an existing parent ID if the term is not a child term
221
	if ( $parent_term_exists ) {
222
		$parent_term = get_term( $parent_term_exists['term_id'], 'wpsc-variation' );
223
		if ( $parent_term->parent == '0' )
224
			$variation_set_id = $parent_term_exists['term_id'];
225
	}
226
227
	if ( empty( $variation_set_id ) ) {
228
		$results = wp_insert_term( apply_filters( 'wpsc_new_variation_set', $new_variation_set ), 'wpsc-variation' );
229
		if ( is_wp_error( $results ) )
230
			return $results;
231
		$variation_set_id = $results['term_id'];
232
	}
233
234
	if ( empty( $variation_set_id ) )
235
		return new WP_Error( 'wpsc_invalid_variation_id', __( 'Cannot retrieve the variation set in order to proceed.', 'wp-e-commerce' ) );
236
237
	foreach ( $variants as $variant ) {
238
		$results = wp_insert_term( apply_filters( 'wpsc_new_variant', $variant, $variation_set_id ), 'wpsc-variation', array( 'parent' => $variation_set_id ) );
239
240
		if ( is_wp_error( $results ) )
241
			return $results;
242
243
		$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...
244
	}
245
246
	require_once( 'includes/walker-variation-checklist.php' );
247
248
	if ( ! version_compare( $GLOBALS['wp_version'], '3.8.3', '>' ) ) {
249
250
		/* --- DIRTY HACK START --- */
251
		/*
252
		There's a bug with term cache in WordPress core. See http://core.trac.wordpress.org/ticket/14485. Fixed in 3.9.
253
		The next 3 lines will delete children term cache for wpsc-variation.
254
		Without this hack, the new child variations won't be displayed on "Variations" page and
255
		also won't be displayed in wp_terms_checklist() call below.
256
		*/
257
		clean_term_cache( $variation_set_id, 'wpsc-variation' );
258
		delete_option('wpsc-variation_children');
259
		wp_cache_set( 'last_changed', 1, 'terms' );
260
		_get_term_hierarchy('wpsc-variation');
261
		/* --- DIRTY HACK END --- */
262
263
	}
264
265
	ob_start();
266
267
	wp_terms_checklist( (int) $_POST['post_id'], array(
268
		'taxonomy'      => 'wpsc-variation',
269
		'descendants_and_self' => $variation_set_id,
270
		'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...
271
		'checked_ontop' => false,
272
	) );
273
274
	$content = ob_get_clean();
275
276
	$return = array(
277
		'variation_set_id'  => $variation_set_id,
278
		'inserted_variants' => $inserted_variants,
279
		'content'           => $content,
280
	);
281
282
	return $return;
283
}
284
285
/**
286
 * Display gateway settings form via AJAX
287
 *
288
 * @since  3.8.9
289
 * @access private
290
 *
291
 * @uses WPSC_Settings_Tab_Gateway
292
 * @uses WPSC_Settings_Tab_Gateway::display_payment_gateway_settings_form()     Displays payment gateway form
293
 *
294
 * @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...
295
 */
296
function _wpsc_ajax_payment_gateway_settings_form() {
297
298
	require_once( 'settings-page.php' );
299
	require_once( 'includes/settings-tabs/gateway.php' );
300
301
	$return = array();
302
	ob_start();
303
	$tab = new WPSC_Settings_Tab_Gateway();
304
	$tab->display_payment_gateway_settings_form();
305
	$return['content'] = ob_get_clean();
306
307
	return $return;
308
}
309
310
/**
311
 * Display shipping module settings form via AJAX
312
 *
313
 * @since  3.8.9
314
 * @access private
315
 *
316
 * @uses WPSC_Settings_Table_Shipping
317
 * @uses WPSC_Settings_Table_Shipping::display_shipping_module_settings_form()  Displays shipping module form
318
 *
319
 * @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...
320
 */
321
function _wpsc_ajax_shipping_module_settings_form() {
322
	require_once( 'settings-page.php' );
323
	require_once( 'includes/settings-tabs/shipping.php' );
324
325
	$return = array();
326
	ob_start();
327
	$tab = new WPSC_Settings_Tab_Shipping();
328
	$tab->display_shipping_module_settings_form();
329
	$return['content'] = ob_get_clean();
330
331
	return $return;
332
}
333
334
/**
335
 * Display settings tab via AJAX
336
 *
337
 * @since 3.8.9
338
 * @access private
339
 *
340
 * @uses WPSC_Settings_Page
341
 * @uses WPSC_Settings_Page::display_current_tab()  Shows current tab of settings page
342
 *
343
 * @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...
344
 */
345
function _wpsc_ajax_navigate_settings_tab() {
346
	require_once( 'settings-page.php' );
347
348
	$return = array();
349
	ob_start();
350
	$settings_page = new WPSC_Settings_Page( $_POST['tab'] );
351
	$settings_page->display_current_tab();
352
	$return['content'] = ob_get_clean();
353
354
	return $return;
355
}
356
357
/**
358
 * Display base region list in Store Settings -> General
359
 *
360
 * @since 3.8.9
361
 * @access private
362
 *
363
 * @uses WPSC_Settings_Tab_General
364
 * @uses WPSC_Settings_Tab_General::display_region_drop_down()  Shows region dropdown
365
 *
366
 * @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...
367
 */
368
function _wpsc_ajax_display_region_list() {
369
	require_once( 'settings-page.php' );
370
	require_once( 'includes/settings-tabs/general.php' );
371
372
	$return = array();
373
	ob_start();
374
	$tab = new WPSC_Settings_Tab_General();
375
	$tab->display_region_drop_down();
376
	$return['content'] = ob_get_clean();
377
378
	return $return;
379
}
380
381
/**
382
 * Save tracking ID of a sales log.
383
 *
384
 * @since 3.8.9
385
 * @access private
386
 *
387
 * @uses WP_Error   WordPress Error class
388
 *
389
 * @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...
390
 */
391
function _wpsc_ajax_purchase_log_save_tracking_id() {
392
	global $wpdb;
393
394
	$result = $wpdb->update(
395
		WPSC_TABLE_PURCHASE_LOGS,
396
		array(
397
			'track_id' => $_POST['value']
398
		),
399
		array(
400
			'id' => $_POST['log_id']
401
		),
402
		'%s',
403
		'%d'
404
	);
405
406
	if ( ! $result )
407
		return new WP_Error( 'wpsc_cannot_save_tracking_id', __( "Couldn't save tracking ID of the transaction. Please try again.", 'wp-e-commerce' ) );
408
409
	$return = array(
410
		'rows_affected' => $result,
411
		'id'            => $_POST['log_id'],
412
		'track_id'      => $_POST['value'],
413
	);
414
415
	return $return;
416
}
417
418
/**
419
 * Send sales log tracking email via AJAX
420
 *
421
 * @since 3.8.9
422
 * @access private
423
 *
424
 * @uses $wpdb              WordPress database object for queries
425
 * @uses get_option()       Gets option from DB given key
426
 * @uses add_filter()       Calls 'wp_mail_from' which can replace the from email address
427
 * @uses add_filter()       Calls 'wp_mail_from_name' allows replacement of the from name on WordPress emails
428
 * @uses wp_mail()          All the emailses in WordPress are sent through this function
429
 * @uses WP_Error           WordPress Error class
430
 *
431
 * @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...
432
 */
433
function _wpsc_ajax_purchase_log_send_tracking_email() {
434
	global $wpdb;
435
436
	$id = absint( $_POST['log_id'] );
437
	$sql = $wpdb->prepare( "SELECT `track_id` FROM " . WPSC_TABLE_PURCHASE_LOGS . " WHERE `id`=%d LIMIT 1", $id );
438
	$trackingid = $wpdb->get_var( $sql );
439
440
	$message = get_option( 'wpsc_trackingid_message' );
441
	$message = str_replace( '%trackid%', $trackingid, $message );
442
	$message = str_replace( '%shop_name%', get_option( 'blogname' ), $message );
443
444
	$email = wpsc_get_buyers_email( $id );
445
446
	$subject = get_option( 'wpsc_trackingid_subject' );
447
	$subject = str_replace( '%shop_name%', get_option( 'blogname' ), $subject );
448
449
	add_filter( 'wp_mail_from', 'wpsc_replace_reply_address', 0 );
450
	add_filter( 'wp_mail_from_name', 'wpsc_replace_reply_name', 0 );
451
452
	$result = wp_mail( $email, $subject, $message);
453
454
	if ( ! $result ) {
455
		return new WP_Error( 'wpsc_cannot_send_tracking_email', __( "Couldn't send tracking email. Please try again.", 'wp-e-commerce' ) );
456
	}
457
458
	$return = array(
459
		'id'          => $id,
460
		'tracking_id' => $trackingid,
461
		'subject'     => $subject,
462
		'message'     => $message,
463
		'email'       => $email
464
	);
465
466
	return $return;
467
}
468
469
/**
470
 * Do purchase log action link via AJAX
471
 *
472
 * @since   3.9.0
473
 * @access  private
474
 *
475
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
476
 */
477
function _wpsc_ajax_purchase_log_action_link() {
478
479
	if ( isset( $_POST['log_id'] ) && isset( $_POST['purchase_log_action_link'] ) && isset( $_POST['purchase_log_action_nonce'] ) ) {
480
481
		$log_id = absint( $_POST['log_id'] );
482
		$purchase_log_action_link = sanitize_key( $_POST['purchase_log_action_link'] );
483
484
		// Verify action nonce
485
		if ( wp_verify_nonce( $_POST['purchase_log_action_nonce'], 'wpsc_purchase_log_action_ajax_' . $purchase_log_action_link ) ) {
486
487
			// Expected to receive success = true by default, or false on error.
488
			$return = apply_filters( 'wpsc_purchase_log_action_ajax-' . $purchase_log_action_link, array( 'success' => null ), $log_id );
489
490
		} else {
491
			$return = _wpsc_error_invalid_nonce();
492
		}
493
494
		if ( ! is_wp_error( $return ) ) {
495
			$return['log_id'] = $log_id;
496
			$return['purchase_log_action_link'] = $purchase_log_action_link;
497
			$return['success'] = isset( $return['success'] ) ? (bool) $return['success'] : null;
498
		}
499
500
		return $return;
501
502
	}
503
504
	return new WP_Error( 'wpsc_ajax_invalid_purchase_log_action', __( 'Purchase log action failed.', 'wp-e-commerce' ) );
505
506
}
507
508
/**
509
 * Remove purchase log item.
510
 *
511
 * @since   4.0
512
 * @access  private
513
 *
514
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
515
 */
516
function _wpsc_ajax_remove_log_item() {
517
518
	if ( isset( $_POST['item_id'], $_POST['log_id'] ) ) {
519
520
		$item_id = absint( $_POST['item_id'] );
521
		$log_id  = absint( $_POST['log_id'] );
522
		$log     = new WPSC_Purchase_Log( $log_id );
523
524
		if ( $log->remove_item( $item_id ) ) {
525
			return _wpsc_init_log_items( $log );
526
		}
527
	}
528
529
	return new WP_Error( 'wpsc_ajax_invalid_remove_log_item', __( 'Removing log item failed.', 'wp-e-commerce' ) );
530
}
531
532
/**
533
 * Update purchase log item quantity.
534
 *
535
 * @since   4.0
536
 * @access  private
537
 *
538
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
539
 */
540
function _wpsc_ajax_update_log_item_qty() {
541
542
	if ( isset( $_POST['item_id'], $_POST['log_id'], $_POST['qty'] ) ) {
543
544
		if ( empty( $_POST['qty'] ) ) {
545
			return _wpsc_ajax_remove_log_item();
546
		}
547
548
		$item_id = absint( $_POST['item_id'] );
549
		$log_id  = absint( $_POST['log_id'] );
550
		$log     = new WPSC_Purchase_Log( $log_id );
551
		$result  = $log->update_item( $item_id, array( 'quantity' => absint( $_POST['qty'] ) ) );
552
553
		if ( 0 === $result ) {
554
			return true;
555
		} elseif ( false !== $result ) {
556
			return _wpsc_init_log_items( $log );
557
		}
558
	}
559
560
	return new WP_Error( 'wpsc_ajax_invalid_update_log_item_qty', __( 'Updating log item quantity failed.', 'wp-e-commerce' ) );
561
}
562
563
/**
564
 * Add purchase log item.
565
 *
566
 * @since   4.0
567
 * @access  private
568
 *
569
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
570
 */
571
function _wpsc_ajax_add_log_item() {
572
	global $wpsc_cart;
573
574
	if (
575
		isset( $_POST['product_ids'], $_POST['log_id'] )
576
		&& is_array( $_POST['product_ids'] )
577
		&& ! empty( $_POST['product_ids'] )
578
	) {
579
580
		$existing = isset( $_POST['existing'] ) && is_array( $_POST['existing'] )
581
			? array_map( 'absint', $_POST['existing'] )
582
			: false;
583
584
		$item_ids = array();
585
		$log      = null;
586
587
		foreach ( $_POST['product_ids'] as $product_id ) {
588
			$product_id = absint( $product_id );
589
			$log_id     = absint( $_POST['log_id'] );
590
			$log        = new WPSC_Purchase_Log( $log_id );
591
592
			// Is product is already in item list?
593
			if ( $existing && in_array( $product_id, $existing, true ) ) {
594
				$item = $log->get_item_from_product_id( $product_id );
595
				if ( $item ) {
596
					// Update item quantity...
597
					$log->update_item( $item->id, array( 'quantity' => ++$item->quantity ) );
598
					// And move on.
599
					continue;
600
				}
601
			}
602
603
			$item       = new wpsc_cart_item( $product_id, array(), $wpsc_cart );
604
			$item_id    = $item->save_to_db( $log_id );
605
			$item_ids[] = absint( $item_id );
606
		}
607
608
		return _wpsc_init_log_items( $log, $item_ids );
0 ignored issues
show
Bug introduced by
It seems like $log defined by null on line 585 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...
609
	}
610
611
	return new WP_Error( 'wpsc_ajax_invalid_add_log_item', __( 'Adding log item failed.', 'wp-e-commerce' ) );
612
}
613
614
function _wpsc_init_log_items( WPSC_Purchase_Log $log, $item_ids = array() ) {
615
	$log->init_items();
616
617
	require_once( WPSC_FILE_PATH . '/wpsc-admin/display-sales-logs.php' );
618
619
	$html = '';
620
	$htmls = array();
621
	$htmls[] = array();
622
623
	while ( wpsc_have_purchaselog_details() ) {
624
		wpsc_the_purchaselog_item();
625
626
		ob_start();
627
		WPSC_Purchase_Log_Page::purchase_log_cart_item( $log->can_edit() );
628
		$cart_item = ob_get_clean();
629
630
		$htmls[ wpsc_purchaselog_details_id() ] = $cart_item;
631
		if ( ! empty( $item_ids ) && in_array( absint( wpsc_purchaselog_details_id() ), $item_ids, true ) ) {
632
			$html .= $cart_item;
633
		}
634
	}
635
636
	return array(
637
		'quantities'     => wp_list_pluck( $log->get_items(), 'quantity', 'id' ),
638
		'html'           => $html,
639
		'htmls'          => $htmls,
640
		'discount_data'  => wpsc_purchlog_has_discount_data() ? esc_html__( 'Coupon Code', 'wp-e-commerce' ) . ': ' . wpsc_display_purchlog_discount_data() : '',
641
		'discount'       => wpsc_display_purchlog_discount(),
642
		'total_taxes'    => wpsc_display_purchlog_taxes(),
643
		'total_shipping' => wpsc_display_purchlog_shipping( false, true ),
644
		'final_total'    => wpsc_display_purchlog_totalprice(),
645
	);
646
}
647
648
/**
649
 * Edit log contact details.
650
 *
651
 * @since   4.0
652
 * @access  private
653
 *
654
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
655
 */
656
function _wpsc_ajax_edit_contact_details() {
657
658
	if ( isset( $_POST['log_id'], $_POST['fields'] ) && ! empty( $_POST['fields'] ) ) {
659
660
		// Parse the URL query string of the fields array.
661
		parse_str( $_POST['fields'], $fields );
662
663
		$log_id = absint( $_POST['log_id'] );
664
		$log    = new WPSC_Purchase_Log( $log_id );
665
666
		if ( isset( $fields['wpsc_checkout_details'] ) && is_array( $fields['wpsc_checkout_details'] ) ) {
667
			$details = wp_unslash( $fields['wpsc_checkout_details'] );
668
669
			// Save the new/updated contact details.
670
			WPSC_Checkout_Form_Data::save_form(
671
				$log,
672
				WPSC_Checkout_Form::get()->get_fields(),
673
				array_map( 'sanitize_text_field', $details ),
674
				false
675
			);
676
677
			require_once( WPSC_FILE_PATH . '/wpsc-admin/display-sales-logs.php' );
678
679
			$log->init_items();
680
681
			// Fetch the shipping/billing formatted output.
682
683
			ob_start();
684
			WPSC_Purchase_Log_Page::shipping_address_output();
685
			$shipping = ob_get_clean();
686
687
			ob_start();
688
			WPSC_Purchase_Log_Page::billing_address_output();
689
			$billing = ob_get_clean();
690
691
			ob_start();
692
			WPSC_Purchase_Log_Page::payment_details_output();
693
			$payment = ob_get_clean();
694
695
			return compact( 'shipping', 'billing', 'payment' );
696
697
		}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
698
699
	}
700
701
	return new WP_Error( 'wpsc_ajax_invalid_edit_contact_details', __( 'Failed to update contact details for log.', 'wp-e-commerce' ) );
702
}
703
704
/**
705
 * Add a note to a log.
706
 *
707
 * @since   4.0
708
 * @access  private
709
 *
710
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
711
 */
712
function _wpsc_ajax_add_note() {
713
714
	if ( isset( $_POST['log_id'], $_POST['note'] ) && ! empty( $_POST['note'] ) ) {
715
716
		$result = wpsc_purchlogs_update_notes(
717
			absint( $_POST['log_id'] ),
718
			wp_kses_post( wp_unslash( $_POST['note'] ) )
719
		);
720
721
		if ( $result instanceof WPSC_Purchase_Log_Notes ) {
722
			require_once( WPSC_FILE_PATH . '/wpsc-admin/display-sales-logs.php' );
723
724
			$data      = $result->get_data();
725
			$keys      = array_keys( $data );
726
			$note_id   = end( $keys );
727
			$note_args = end( $data );
728
729
			ob_start();
730
			WPSC_Purchase_Log_Page::note_output( $result, $note_id, $note_args );
731
			$row = ob_get_clean();
732
733
			return $row;
734
		}
735
	}
736
737
	return new WP_Error( 'wpsc_ajax_invalid_add_note', __( 'Failed adding log note.', 'wp-e-commerce' ) );
738
}
739
740
/**
741
 * Delete a note from a log.
742
 *
743
 * @since   4.0
744
 * @access  private
745
 *
746
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
747
 */
748
function _wpsc_ajax_delete_note() {
749
750
	if ( isset( $_POST['log_id'], $_POST['note'] ) && is_numeric( $_POST['note'] ) ) {
751
752
		$notes = wpsc_get_order_notes( absint( $_POST['log_id'] ) );
753
		$notes->remove( absint( $_POST['note'] ) )->save();
754
755
		return true;
756
	}
757
758
	return new WP_Error( 'wpsc_ajax_invalid_delete_note', __( 'Failed to delete log note.', 'wp-e-commerce' ) );
759
}
760
761
/**
762
 * Search for products.
763
 *
764
 * @since   4.0
765
 * @access  private
766
 *
767
 * @return  array|WP_Error  $return  Response args if successful, WP_Error if otherwise
768
 */
769
function _wpsc_ajax_search_products() {
770
	$pt_object = get_post_type_object( 'wpsc-product' );
771
772
	$s = wp_unslash( $_POST['search'] );
773
	$args = array(
774
		'post_type' => 'wpsc-product',
775
		'post_status' => array( 'publish', 'inherit' ),
776
		'posts_per_page' => 50,
777
	);
778
	if ( '' !== $s ) {
779
		$args['s'] = $s;
780
	}
781
782
	$posts = get_posts( $args );
783
784
	if ( ! $posts ) {
785
		return new WP_Error( 'wpsc_ajax_invalid_search_products', __( 'No items found.', 'wp-e-commerce' ) );
786
	}
787
788
	$alt = '';
789
	foreach ( $posts as $post ) {
790
		$post->title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
791
		$alt = ( 'alternate' === $alt ) ? '' : 'alternate';
792
793
		$post->status = $post->post_status;
794
795
		switch ( $post->post_status ) {
796
			case 'publish' :
797
			case 'private' :
798
				$post->status = __( 'Published' );
799
				break;
800
			case 'future' :
801
				$post->status = __( 'Scheduled' );
802
				break;
803
			case 'pending' :
804
				$post->status = __( 'Pending Review' );
805
				break;
806
			case 'draft' :
807
				$post->status = __( 'Draft' );
808
				break;
809
			default :
810
				$post->status = $post->post_status;
811
				break;
812
		}
813
814
		if ( '0000-00-00 00:00:00' === $post->post_date ) {
815
			$post->time = '';
816
		} else {
817
			/* translators: date format in table columns, see https://secure.php.net/date */
818
			$post->time = mysql2date( __( 'Y/m/d' ), $post->post_date );
819
		}
820
821
		$post->class = $alt;
822
	}
823
824
	return $posts;
825
}
826
827
/**
828
 * Handle AJAX clear downloads lock purchase log action
829
 *
830
 * The _wpsc_ajax_purchase_log_action_link() function which triggers this function is nonce
831
 * and capability checked in _wpsc_ajax_handler().
832
 *
833
 * @since   3.9.0
834
 * @access  private
835
 *
836
 * @param  array  $response  AJAX response.
837
 * @param  int    $log_id    Purchase log ID.
838
 */
839
function wpsc_purchase_log_action_ajax_downloads_lock( $response, $log_id ) {
840
841
	$response['success'] = wpsc_purchlog_clear_download_items( $log_id );
842
843
	return $response;
844
845
}
846
add_action( 'wpsc_purchase_log_action_ajax-downloads_lock', 'wpsc_purchase_log_action_ajax_downloads_lock', 10, 2 );
847
848
849
/**
850
 * Handle AJAX email receipt purchase log action
851
 *
852
 * The _wpsc_ajax_purchase_log_action_link() function which triggers this function is nonce
853
 * and capability checked in _wpsc_ajax_handler().
854
 *
855
 * @since   3.9.0
856
 * @access  private
857
 *
858
 * @param  array  $response  AJAX response.
859
 * @param  int    $log_id    Purchase log ID.
860
 */
861
function wpsc_purchase_log_action_ajax_email_receipt( $response, $log_id ) {
862
863
	$response['success'] = wpsc_purchlog_resend_email( $log_id );
864
865
	return $response;
866
867
}
868
add_action( 'wpsc_purchase_log_action_ajax-email_receipt', 'wpsc_purchase_log_action_ajax_email_receipt', 10, 2 );
869
870
/**
871
 * Delete an attached downloadable file via AJAX.
872
 *
873
 * @since 3.8.9
874
 * @access private
875
 *
876
 * @uses _wpsc_delete_file()    Deletes files associated with a product
877
 * @uses WP_Error               WordPress error class
878
 *
879
 * @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...
880
 */
881
function _wpsc_ajax_delete_file() {
882
	$product_id = absint( $_REQUEST['product_id'] );
883
	$file_name = basename( $_REQUEST['file_name'] );
884
885
	$result = _wpsc_delete_file( $product_id, $file_name );
886
887
	if ( ! $result )
888
		return new WP_Error( 'wpsc_cannot_delete_file', __( "Couldn't delete the file. Please try again.", 'wp-e-commerce' ) );
889
890
	$return = array(
891
		'product_id' => $product_id,
892
		'file_name'  => $file_name,
893
	);
894
895
	return $return;
896
}
897
898
/**
899
 * Delete a product meta via AJAX
900
 *
901
 * @since 3.8.9
902
 * @access private
903
 *
904
 * @uses delete_meta()      Deletes metadata by meta id
905
 * @uses WP_Error           WordPress error class
906
 *
907
 * @return  array|WP_Error  $return     Response args if successful, WP_Error if otherwise
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array<string,integer>.

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

Loading history...
908
 */
909
function _wpsc_ajax_remove_product_meta() {
910
	$meta_id = (int) $_POST['meta_id'];
911
	if ( ! delete_meta( $meta_id ) )
912
		return new WP_Error( 'wpsc_cannot_delete_product_meta', __( "Couldn't delete product meta. Please try again.", 'wp-e-commerce' ) );
913
914
	return array( 'meta_id' => $meta_id );
915
}
916
917
/**
918
 * Modify a purchase log's status.
919
 *
920
 * @since 3.8.9
921
 * @access private
922
 *
923
 * @uses wpsc_purchlog_edit_status()                    Edits purchase log status
924
 * @uses WP_Error                                       WordPress Error class
925
 * @uses WPSC_Purchase_Log_List_Table
926
 * @uses WPSC_Purchase_Log_List_Table::prepare_items()
927
 * @uses WPSC_Purchase_Log_List_Table::views()
928
 * @uses WPSC_Purchase_Log_List_Table::display_tablenav()   @todo docs
929
 *
930
 * @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...
931
 */
932
function _wpsc_ajax_change_purchase_log_status() {
933
	$result = wpsc_purchlog_edit_status( $_POST['id'], $_POST['new_status'] );
934
	if ( ! $result )
935
		return new WP_Error( 'wpsc_cannot_edit_purchase_log_status', __( "Couldn't modify purchase log's status. Please try again.", 'wp-e-commerce' ) );
936
937
	$args = array();
938
939
	$args['screen'] = 'dashboard_page_wpsc-sales-logs';
940
941
	require_once( WPSC_FILE_PATH . '/wpsc-admin/includes/purchase-log-list-table-class.php' );
942
	$purchaselog_table = new WPSC_Purchase_Log_List_Table( $args );
943
	$purchaselog_table->prepare_items();
944
945
	ob_start();
946
	$purchaselog_table->views();
947
	$views = ob_get_clean();
948
949
	ob_start();
950
	$purchaselog_table->display_tablenav( 'top' );
951
	$tablenav_top = ob_get_clean();
952
953
	ob_start();
954
	$purchaselog_table->display_tablenav( 'bottom' );
955
	$tablenav_bottom = ob_get_clean();
956
957
	$return = array(
958
		'id'              => $_POST['id'],
959
		'new_status'      => $_POST['new_status'],
960
		'views'           => $views,
961
		'tablenav_top'    => $tablenav_top,
962
		'tablenav_bottom' => $tablenav_bottom,
963
	);
964
965
	return $return;
966
}
967
968
/**
969
 * Save product ordering after drag-and-drop sorting
970
 *
971
 * @since 3.8.9
972
 * @access private
973
 *
974
 * @uses $wpdb              WordPress database object for use in queries
975
 * @uses wp_update_post()   Updates post based on passed $args. Needs a post_id
976
 * @uses WP_Error           WordPress Error class
977
 *
978
 * @return array|WP_Error Response args if successful, WP_Error if otherwise
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|array<string,array>.

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

Loading history...
979
 */
980
function _wpsc_ajax_save_product_order() {
981
982
	$products = array( );
0 ignored issues
show
introduced by
Empty array declaration must have no space between the parentheses
Loading history...
983
	foreach ( $_POST['post'] as $product ) {
984
		$products[] = (int) str_replace( 'post-', '', $product );
985
	}
986
987
	$failed = array();
988
	foreach ( $products as $order => $product_id ) {
989
		$result = wp_update_post( array(
990
			'ID' => $product_id,
991
			'menu_order' => $order,
992
		) );
993
994
		if ( ! $result )
995
			$failed[] = $product_id;
996
	}
997
998
	// Validate data before exposing to action
999
	$category = isset( $_POST['category_id'] ) ? get_term_by( 'slug', $_POST['category_id'], 'wpsc_product_category' ) : false;
1000
	do_action( 'wpsc_save_product_order', $products, $category );
1001
1002
	if ( ! empty( $failed ) ) {
1003
		$error_data = array(
1004
			'failed_ids' => $failed,
1005
		);
1006
1007
		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 );
1008
	}
1009
1010
	return array(
1011
		'ids' => $products,
1012
	);
1013
}
1014
1015
/**
1016
 * Save Category Product Order
1017
 *
1018
 * Note that this uses the 'term_order' field in the 'term_relationships' table to store
1019
 * the order. Although this column presently seems to be unused by WordPress, the intention
1020
 * is it should be used to store the order of terms associates to a post, not the order
1021
 * of posts as we are doing. This shouldn't be an issue for WPEC unless WordPress adds a UI
1022
 * for this. More info at http://core.trac.wordpress.org/ticket/9547
1023
 *
1024
 * @since 3.9
1025
 * @access private
1026
 *
1027
 * @uses $wpdb   WordPress database object used for queries
1028
 */
1029
function _wpsc_save_category_product_order( $products, $category ) {
1030
	global $wpdb;
1031
1032
	// Only save category product order if in category
1033
	if ( ! $category )
1034
		return;
1035
1036
	// Save product order in term_relationships table
1037
	foreach ( $products as $order => $product_id ) {
1038
		$wpdb->update( $wpdb->term_relationships,
1039
			array( 'term_order' => $order ),
1040
			array( 'object_id' => $product_id, 'term_taxonomy_id' => $category->term_taxonomy_id ),
1041
			array( '%d' ),
1042
			array( '%d', '%d' )
1043
		);
1044
	}
1045
}
1046
add_action( 'wpsc_save_product_order', '_wpsc_save_category_product_order', 10, 2 );
1047
1048
/**
1049
 * Update Checkout fields order
1050
 *
1051
 * @since 3.8.9
1052
 * @access private
1053
 *
1054
 * @uses $wpdb      WordPress database object used for queries
1055
 * @uses WP_Error   WordPress error class
1056
 *
1057
 * @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...
1058
 */
1059
function _wpsc_ajax_update_checkout_fields_order() {
1060
	global $wpdb;
1061
1062
	$checkout_fields = $_REQUEST['sort_order'];
1063
	$order = 1;
1064
	$failed = array();
1065
	$modified = array();
1066
	foreach ( $checkout_fields as &$checkout_field ) {
1067
		// ignore new fields
1068
		if ( strpos( $checkout_field, 'new-field' ) === 0 )
1069
			continue;
1070
		$checkout_field = absint( preg_replace('/[^0-9]+/', '', $checkout_field ) );
1071
		$result = $wpdb->update(
1072
			WPSC_TABLE_CHECKOUT_FORMS,
1073
			array(
1074
				'checkout_order' => $order
1075
			),
1076
			array(
1077
				'id' => $checkout_field
1078
			),
1079
			'%d',
1080
			'%d'
1081
		);
1082
		$order ++;
1083
		if ( $result === false )
1084
			$failed[] = $checkout_field;
1085
		elseif ( $result > 0 )
1086
			$modified[] = $checkout_field;
1087
	}
1088
1089
	if ( ! empty( $failed ) )
1090
		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 ) );
1091
1092
	return array(
1093
		'modified' => $modified,
1094
	);
1095
}
1096
1097
/**
1098
 * Save a downloadable file to a product
1099
 *
1100
 * @since 3.8.9
1101
 * @access private
1102
 *
1103
 * @uses $wpdb                          WordPress database object for use in queries
1104
 * @uses _wpsc_create_ajax_nonce()      Creates nonce for an ajax action
1105
 * @uses wpsc_get_mimetype()            Returns mimetype of file
1106
 * @uses wp_insert_post()               Inserts post to WordPress database
1107
 * @uses wp_nonce_url()                 Retrieve URL with nonce added to URL query.
1108
 * @uses wpsc_convert_bytes()           Formats bytes
1109
 * @uses wpsc_get_extension()           Gets extension of file
1110
 * @uses esc_attr()                     Escapes HTML attributes
1111
 * @uses _x()                           Retrieve translated string with gettext context
1112
 *
1113
 * @return array|WP_Error Response args if successful, WP_Error if otherwise.
1114
 */
1115
function _wpsc_ajax_upload_product_file() {
1116
	global $wpdb;
1117
	$product_id = absint( $_POST["product_id"] );
1118
	$output = '';
1119
	$delete_nonce = _wpsc_create_ajax_nonce( 'delete_file' );
1120
1121
	foreach ( $_POST["select_product_file"] as $selected_file ) {
1122
		// if we already use this file, there is no point doing anything more.
1123
		$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()
1124
		$file_post_data = $wpdb->get_row( $sql, ARRAY_A );
1125
		$selected_file_path = WPSC_FILE_DIR . basename( $selected_file );
1126
		$file_url = WPSC_FILE_URL . basename( $selected_file );
1127
		$file_size = filesize( $selected_file_path );
1128
		if ( empty( $file_post_data ) ) {
1129
			$type = wpsc_get_mimetype( $selected_file_path );
1130
			$attachment = array(
1131
				'post_mime_type' => $type,
1132
				'post_parent' => $product_id,
1133
				'post_title' => $selected_file,
1134
				'post_content' => '',
1135
				'post_type' => "wpsc-product-file",
1136
				'post_status' => 'inherit'
1137
			);
1138
			$id = wp_insert_post( $attachment );
1139
		} else {
1140
			// already attached
1141
			if ( $file_post_data['post_parent'] == $product_id )
1142
				continue;
1143
			$type = $file_post_data["post_mime_type"];
1144
			$url = $file_post_data["guid"];
1145
			$title = $file_post_data["post_title"];
1146
			$content = $file_post_data["post_content"];
1147
			// Construct the attachment
1148
			$attachment = array(
1149
				'post_mime_type' => $type,
1150
				'guid' => $url,
1151
				'post_parent' => absint( $product_id ),
1152
				'post_title' => $title,
1153
				'post_content' => $content,
1154
				'post_type' => "wpsc-product-file",
1155
				'post_status' => 'inherit'
1156
			);
1157
			// Save the data
1158
			$id = wp_insert_post( $attachment );
1159
		}
1160
1161
		$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'] );
1162
1163
		$output .= '<tr class="wpsc_product_download_row">';
1164
		$output .= '<td style="padding-right: 30px;">' . $attachment['post_title'] . '</td>';
1165
		$output .= '<td>' . wpsc_convert_byte( $file_size ) . '</td>';
1166
		$output .= '<td>.' . wpsc_get_extension( $attachment['post_title'] ) . '</td>';
1167
		$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>";
1168
		$output .= '<td><a href=' .$file_url .'>' . _x( 'Download', 'Digital Download UI row', 'wp-e-commerce' ) . '</a></td>';
1169
		$output .= '</tr>';
1170
	}
1171
1172
	return array(
1173
		'content' => $output,
1174
	);
1175
}
1176
1177
/**
1178
 * Generate variations
1179
 *
1180
 * @since 3.8.9
1181
 * @access private
1182
 *
1183
 * @uses wpsc_update_variations()       Updates product variations given
1184
 * @uses wpsc_admin_product_listing()   DEPRECATED
1185
 *
1186
 * @return array|WP_Error Response args if successful, WP_Error if otherwise
1187
 */
1188
function _wpsc_ajax_update_variations() {
1189
	$product_id = absint( $_REQUEST["product_id"] );
1190
	wpsc_update_variations();
1191
1192
	ob_start();
1193
	wpsc_admin_product_listing( $product_id );
1194
	$content = ob_get_clean();
1195
1196
	return array( 'content' => $content );
1197
}
1198
1199
/**
1200
 * Display the shortcode generator.
1201
 *
1202
 * @since  3.8.9
1203
 * @access private
1204
 */
1205
function _wpsc_action_tinymce_window() {
1206
	require_once( WPSC_CORE_JS_PATH . '/tinymce3/window.php' );
1207
	exit;
1208
}
1209
add_action( 'wp_ajax_wpsc_tinymce_window', '_wpsc_action_tinymce_window' );
1210
1211
/**
1212
 * Add tax rate
1213
 * @since  3.8.9
1214
 * @access private
1215
 *
1216
 * @uses wpec_taxes_controller                                                  Contains all the logic to communicate with the taxes system
1217
 * @uses wpec_taxes_controller::wpec_taxes::wpec_taxes_get_regions()            Gets tax regions based on input country code
1218
 * @uses wpec_taxes_controller::wpec_taxes_build_select_options()               Returns HTML formatted options from input array
1219
 * @uses wpec_taxes_controller::wpec_taxes_build_form()                         Builds the tax rate form
1220
 * @uses wpec_taxes_controller::wpec_taxes::wpec_taxes_get_band_from_index()    Retrieves tax band for given name
1221
 *
1222
 * @return array|WP_Error Response args if successful, WP_Error if otherwise
1223
 */
1224
function _wpsc_ajax_add_tax_rate() {
1225
	//include taxes controller
1226
	$wpec_taxes_controller = new wpec_taxes_controller;
1227
1228
	switch ( $_REQUEST['wpec_taxes_action'] ) {
1229
		case 'wpec_taxes_get_regions':
1230
			$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...
1231
			$key = $_REQUEST['current_key'];
1232
			$type = $_REQUEST['taxes_type'];
1233
			$default_option = array( 'region_code' => 'all-markets', 'name' => 'All Markets' );
1234
			$select_settings = array(
1235
				'id' => "{$type}-region-{$key}",
1236
				'name' => "wpsc_options[wpec_taxes_{$type}][{$key}][region_code]",
1237
				'class' => 'wpsc-taxes-region-drop-down'
1238
			);
1239
			$returnable = $wpec_taxes_controller->wpec_taxes_build_select_options( $regions, 'region_code', 'name', $default_option, $select_settings );
1240
			break;
1241
	}// switch
1242
1243
	return array(
1244
		'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...
1245
	);
1246
}
1247
1248
/**
1249
 * Displays the WPSC product variations table
1250
 *
1251
 * @uses check_admin_referrer()                     Makes sure user was referred from another admin page
1252
 * @uses WPSC_Product_Variations_Page               The WPSC Product variations class
1253
 * @uses WPSC_Product_Variations_Page::display()    Displays the product variations page
1254
 */
1255
function wpsc_product_variations_table() {
1256
	check_admin_referer( 'wpsc_product_variations_table' );
1257
	set_current_screen( 'wpsc-product' );
1258
	require_once( WPSC_FILE_PATH . '/wpsc-admin/includes/product-variations-page.class.php' );
1259
	$page = new WPSC_Product_Variations_Page();
1260
	$page->display();
1261
1262
	exit;
1263
}
1264
add_action( 'wp_ajax_wpsc_product_variations_table', 'wpsc_product_variations_table' );
1265
1266
/**
1267
 * @access private
1268
 *
1269
 * @uses current_user_can()             Checks user capabilities given string
1270
 * @uses delete_post_thumbnail()        Deletes post thumbnail given thumbnail id
1271
 * @uses set_post_thumbnail()           Sets post thumbnail given post_id and thumbnail_id
1272
 * @uses wpsc_the_product_thumbnail()   Returns URL to the product thumbnail
1273
 *
1274
 * @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...
1275
 */
1276
function _wpsc_ajax_set_variation_product_thumbnail() {
1277
	$response = array(
1278
		'success' => false
1279
	);
1280
1281
	$post_ID = intval( $_POST['post_id'] );
1282
	if ( current_user_can( 'edit_post', $post_ID ) ) {
1283
		$thumbnail_id = intval( $_POST['thumbnail_id'] );
1284
1285
		if ( $thumbnail_id == '-1' )
1286
			delete_post_thumbnail( $post_ID );
1287
1288
		set_post_thumbnail( $post_ID, $thumbnail_id );
1289
1290
		$thumbnail = wpsc_the_product_thumbnail( 50, 50, $post_ID, '' );
1291
		if ( ! $thumbnail )
1292
			$thumbnail = WPSC_CORE_IMAGES_URL . '/no-image-uploaded.gif';
1293
		$response['src'] = $thumbnail;
1294
		$response['success'] = true;
1295
	}
1296
1297
	echo json_encode( $response );
1298
	exit;
1299
}
1300
add_action( 'wp_ajax_wpsc_set_variation_product_thumbnail', '_wpsc_ajax_set_variation_product_thumbnail' );
1301
1302
/**
1303
 * Delete WPSC product image from gallery
1304
 *
1305
 * @uses check_ajax_referer()		Verifies the AJAX request to prevent processing external requests
1306
 * @uses get_post_meta()		Returns meta from the specified post
1307
 * @uses update_post_meta()		Updates meta from the specified post
1308
 */
1309
function product_gallery_image_delete_action() {
1310
1311
	$product_gallery = array();
1312
	$gallery_image_id = $gallery_post_id = '';
1313
1314
	$gallery_image_id = absint($_POST['product_gallery_image_id']);
1315
	$gallery_post_id = absint($_POST['product_gallery_post_id']);
1316
1317
	check_ajax_referer( 'wpsc_gallery_nonce', 'wpsc_gallery_nonce_check' );
1318
1319
	$product_gallery = get_post_meta( $gallery_post_id, '_wpsc_product_gallery', true );
1320
1321
	foreach ( $product_gallery as $index => $image_id ) {
1322
		if ( $image_id == $gallery_image_id ) {
1323
			unset( $product_gallery[$index] );
1324
		}
1325
	}
1326
1327
	update_post_meta( $gallery_post_id, '_wpsc_product_gallery', $product_gallery );
1328
}
1329
add_action( 'wp_ajax_product_gallery_image_delete', 'product_gallery_image_delete_action' );