Completed
Pull Request — master (#2185)
by Justin
05:40
created

ajax.php ➔ _wpsc_ajax_purchase_log_refund_items()   D

Complexity

Conditions 12
Paths 410

Size

Total Lines 66
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

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