WC_AJAX

WooCommerce WC_AJAX.

Defined (1)

The class is defined in the following location(s).

/includes/class-wc-ajax.php  
  1. class WC_AJAX { 
  2.  
  3. /** 
  4. * Hook in ajax handlers. 
  5. */ 
  6. public static function init() { 
  7. add_action( 'init', array( __CLASS__, 'define_ajax' ), 0 ); 
  8. add_action( 'template_redirect', array( __CLASS__, 'do_wc_ajax' ), 0 ); 
  9. self::add_ajax_events(); 
  10.  
  11. /** 
  12. * Get WC Ajax Endpoint. 
  13. * @param string $request Optional 
  14. * @return string 
  15. */ 
  16. public static function get_endpoint( $request = '' ) { 
  17. return esc_url_raw( apply_filters( 'woocommerce_ajax_get_endpoint', add_query_arg( 'wc-ajax', $request, remove_query_arg( array( 'remove_item', 'add-to-cart', 'added-to-cart' ) ) ), $request ) ); 
  18.  
  19. /** 
  20. * Set WC AJAX constant and headers. 
  21. */ 
  22. public static function define_ajax() { 
  23. if ( ! empty( $_GET['wc-ajax'] ) ) { 
  24. if ( ! defined( 'DOING_AJAX' ) ) { 
  25. define( 'DOING_AJAX', true ); 
  26. if ( ! defined( 'WC_DOING_AJAX' ) ) { 
  27. define( 'WC_DOING_AJAX', true ); 
  28. // Turn off display_errors during AJAX events to prevent malformed JSON 
  29. if ( ! WP_DEBUG || ( WP_DEBUG && ! WP_DEBUG_DISPLAY ) ) { 
  30. @ini_set( 'display_errors', 0 ); 
  31. $GLOBALS['wpdb']->hide_errors(); 
  32.  
  33. /** 
  34. * Send headers for WC Ajax Requests 
  35. * @since 2.5.0 
  36. */ 
  37. private static function wc_ajax_headers() { 
  38. send_origin_headers(); 
  39. @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); 
  40. @header( 'X-Robots-Tag: noindex' ); 
  41. send_nosniff_header(); 
  42. nocache_headers(); 
  43. status_header( 200 ); 
  44.  
  45. /** 
  46. * Check for WC Ajax request and fire action. 
  47. */ 
  48. public static function do_wc_ajax() { 
  49. global $wp_query; 
  50.  
  51. if ( ! empty( $_GET['wc-ajax'] ) ) { 
  52. $wp_query->set( 'wc-ajax', sanitize_text_field( $_GET['wc-ajax'] ) ); 
  53.  
  54. if ( $action = $wp_query->get( 'wc-ajax' ) ) { 
  55. self::wc_ajax_headers(); 
  56. do_action( 'wc_ajax_' . sanitize_text_field( $action ) ); 
  57. die(); 
  58.  
  59. /** 
  60. * Hook in methods - uses WordPress ajax handlers (admin-ajax). 
  61. */ 
  62. public static function add_ajax_events() { 
  63. // woocommerce_EVENT => nopriv 
  64. $ajax_events = array( 
  65. 'get_refreshed_fragments' => true,  
  66. 'apply_coupon' => true,  
  67. 'remove_coupon' => true,  
  68. 'update_shipping_method' => true,  
  69. 'get_cart_totals' => true,  
  70. 'update_order_review' => true,  
  71. 'add_to_cart' => true,  
  72. 'checkout' => true,  
  73. 'get_variation' => true,  
  74. 'feature_product' => false,  
  75. 'mark_order_status' => false,  
  76. 'add_attribute' => false,  
  77. 'add_new_attribute' => false,  
  78. 'remove_variation' => false,  
  79. 'remove_variations' => false,  
  80. 'save_attributes' => false,  
  81. 'add_variation' => false,  
  82. 'link_all_variations' => false,  
  83. 'revoke_access_to_download' => false,  
  84. 'grant_access_to_download' => false,  
  85. 'get_customer_details' => false,  
  86. 'add_order_item' => false,  
  87. 'add_order_fee' => false,  
  88. 'add_order_shipping' => false,  
  89. 'add_order_tax' => false,  
  90. 'remove_order_item' => false,  
  91. 'remove_order_tax' => false,  
  92. 'reduce_order_item_stock' => false,  
  93. 'increase_order_item_stock' => false,  
  94. 'add_order_item_meta' => false,  
  95. 'remove_order_item_meta' => false,  
  96. 'calc_line_taxes' => false,  
  97. 'save_order_items' => false,  
  98. 'load_order_items' => false,  
  99. 'add_order_note' => false,  
  100. 'delete_order_note' => false,  
  101. 'json_search_products' => false,  
  102. 'json_search_products_and_variations' => false,  
  103. 'json_search_grouped_products' => false,  
  104. 'json_search_downloadable_products_and_variations' => false,  
  105. 'json_search_customers' => false,  
  106. 'term_ordering' => false,  
  107. 'product_ordering' => false,  
  108. 'refund_line_items' => false,  
  109. 'delete_refund' => false,  
  110. 'rated' => false,  
  111. 'update_api_key' => false,  
  112. 'get_customer_location' => true,  
  113. 'load_variations' => false,  
  114. 'save_variations' => false,  
  115. 'bulk_edit_variations' => false,  
  116. 'tax_rates_save_changes' => false,  
  117. 'shipping_zones_save_changes' => false,  
  118. 'shipping_zone_add_method' => false,  
  119. 'shipping_zone_methods_save_changes' => false,  
  120. 'shipping_zone_methods_save_settings' => false,  
  121. 'shipping_classes_save_changes' => false,  
  122. ); 
  123.  
  124. foreach ( $ajax_events as $ajax_event => $nopriv ) { 
  125. add_action( 'wp_ajax_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) ); 
  126.  
  127. if ( $nopriv ) { 
  128. add_action( 'wp_ajax_nopriv_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) ); 
  129.  
  130. // WC AJAX can be used for frontend ajax requests 
  131. add_action( 'wc_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) ); 
  132.  
  133. /** 
  134. * Get a refreshed cart fragment. 
  135. */ 
  136. public static function get_refreshed_fragments() { 
  137.  
  138. // Get mini cart 
  139. ob_start(); 
  140.  
  141. woocommerce_mini_cart(); 
  142.  
  143. $mini_cart = ob_get_clean(); 
  144.  
  145. // Fragments and mini cart are returned 
  146. $data = array( 
  147. 'fragments' => apply_filters( 'woocommerce_add_to_cart_fragments', array( 
  148. 'div.widget_shopping_cart_content' => '<div class="widget_shopping_cart_content">' . $mini_cart . '</div>' 
  149. ),  
  150. 'cart_hash' => apply_filters( 'woocommerce_add_to_cart_hash', WC()->cart->get_cart_for_session() ? md5( json_encode( WC()->cart->get_cart_for_session() ) ) : '', WC()->cart->get_cart_for_session() ) 
  151. ); 
  152.  
  153. wp_send_json( $data ); 
  154.  
  155.  
  156. /** 
  157. * AJAX apply coupon on checkout page. 
  158. */ 
  159. public static function apply_coupon() { 
  160.  
  161. check_ajax_referer( 'apply-coupon', 'security' ); 
  162.  
  163. if ( ! empty( $_POST['coupon_code'] ) ) { 
  164. WC()->cart->add_discount( sanitize_text_field( $_POST['coupon_code'] ) ); 
  165. } else { 
  166. wc_add_notice( WC_Coupon::get_generic_coupon_error( WC_Coupon::E_WC_COUPON_PLEASE_ENTER ), 'error' ); 
  167.  
  168. wc_print_notices(); 
  169.  
  170. die(); 
  171.  
  172. /** 
  173. * AJAX remove coupon on cart and checkout page. 
  174. */ 
  175. public static function remove_coupon() { 
  176.  
  177. check_ajax_referer( 'remove-coupon', 'security' ); 
  178.  
  179. $coupon = wc_clean( $_POST['coupon'] ); 
  180.  
  181. if ( ! isset( $coupon ) || empty( $coupon ) ) { 
  182. wc_add_notice( __( 'Sorry there was a problem removing this coupon.', 'woocommerce' ), 'error' ); 
  183.  
  184. } else { 
  185.  
  186. WC()->cart->remove_coupon( $coupon ); 
  187.  
  188. wc_add_notice( __( 'Coupon has been removed.', 'woocommerce' ) ); 
  189.  
  190. wc_print_notices(); 
  191.  
  192. die(); 
  193.  
  194. /** 
  195. * AJAX update shipping method on cart page. 
  196. */ 
  197. public static function update_shipping_method() { 
  198.  
  199. check_ajax_referer( 'update-shipping-method', 'security' ); 
  200.  
  201. if ( ! defined('WOOCOMMERCE_CART') ) { 
  202. define( 'WOOCOMMERCE_CART', true ); 
  203.  
  204. $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); 
  205.  
  206. if ( isset( $_POST['shipping_method'] ) && is_array( $_POST['shipping_method'] ) ) { 
  207. foreach ( $_POST['shipping_method'] as $i => $value ) { 
  208. $chosen_shipping_methods[ $i ] = wc_clean( $value ); 
  209.  
  210. WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods ); 
  211.  
  212. WC()->cart->calculate_totals(); 
  213.  
  214. woocommerce_cart_totals(); 
  215.  
  216. die(); 
  217.  
  218. /** 
  219. * AJAX receive updated cart_totals div. 
  220. */ 
  221. public static function get_cart_totals() { 
  222.  
  223. if ( ! defined( 'WOOCOMMERCE_CART' ) ) { 
  224. define( 'WOOCOMMERCE_CART', true ); 
  225.  
  226. WC()->cart->calculate_totals(); 
  227.  
  228. woocommerce_cart_totals(); 
  229.  
  230. die(); 
  231.  
  232. /** 
  233. * AJAX update order review on checkout. 
  234. */ 
  235. public static function update_order_review() { 
  236. ob_start(); 
  237.  
  238. check_ajax_referer( 'update-order-review', 'security' ); 
  239.  
  240. if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { 
  241. define( 'WOOCOMMERCE_CHECKOUT', true ); 
  242.  
  243. if ( WC()->cart->is_empty() ) { 
  244. $data = array( 
  245. 'fragments' => apply_filters( 'woocommerce_update_order_review_fragments', array( 
  246. 'form.woocommerce-checkout' => '<div class="woocommerce-error">' . __( 'Sorry, your session has expired.', 'woocommerce' ) . ' <a href="' . esc_url( wc_get_page_permalink( 'shop' ) ) . '" class="wc-backward">' . __( 'Return to shop', 'woocommerce' ) . '</a></div>' 
  247. ) ) 
  248. ); 
  249.  
  250. wp_send_json( $data ); 
  251.  
  252. die(); 
  253.  
  254. do_action( 'woocommerce_checkout_update_order_review', $_POST['post_data'] ); 
  255.  
  256. $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); 
  257.  
  258. if ( isset( $_POST['shipping_method'] ) && is_array( $_POST['shipping_method'] ) ) { 
  259. foreach ( $_POST['shipping_method'] as $i => $value ) { 
  260. $chosen_shipping_methods[ $i ] = wc_clean( $value ); 
  261.  
  262. WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods ); 
  263. WC()->session->set( 'chosen_payment_method', empty( $_POST['payment_method'] ) ? '' : $_POST['payment_method'] ); 
  264.  
  265. if ( isset( $_POST['country'] ) ) { 
  266. WC()->customer->set_country( $_POST['country'] ); 
  267.  
  268. if ( isset( $_POST['state'] ) ) { 
  269. WC()->customer->set_state( $_POST['state'] ); 
  270.  
  271. if ( isset( $_POST['postcode'] ) ) { 
  272. WC()->customer->set_postcode( $_POST['postcode'] ); 
  273.  
  274. if ( isset( $_POST['city'] ) ) { 
  275. WC()->customer->set_city( $_POST['city'] ); 
  276.  
  277. if ( isset( $_POST['address'] ) ) { 
  278. WC()->customer->set_address( $_POST['address'] ); 
  279.  
  280. if ( isset( $_POST['address_2'] ) ) { 
  281. WC()->customer->set_address_2( $_POST['address_2'] ); 
  282.  
  283. if ( wc_ship_to_billing_address_only() ) { 
  284.  
  285. if ( ! empty( $_POST['country'] ) ) { 
  286. WC()->customer->set_shipping_country( $_POST['country'] ); 
  287. WC()->customer->calculated_shipping( true ); 
  288.  
  289. if ( isset( $_POST['state'] ) ) { 
  290. WC()->customer->set_shipping_state( $_POST['state'] ); 
  291.  
  292. if ( isset( $_POST['postcode'] ) ) { 
  293. WC()->customer->set_shipping_postcode( $_POST['postcode'] ); 
  294.  
  295. if ( isset( $_POST['city'] ) ) { 
  296. WC()->customer->set_shipping_city( $_POST['city'] ); 
  297.  
  298. if ( isset( $_POST['address'] ) ) { 
  299. WC()->customer->set_shipping_address( $_POST['address'] ); 
  300.  
  301. if ( isset( $_POST['address_2'] ) ) { 
  302. WC()->customer->set_shipping_address_2( $_POST['address_2'] ); 
  303. } else { 
  304.  
  305. if ( ! empty( $_POST['s_country'] ) ) { 
  306. WC()->customer->set_shipping_country( $_POST['s_country'] ); 
  307. WC()->customer->calculated_shipping( true ); 
  308.  
  309. if ( isset( $_POST['s_state'] ) ) { 
  310. WC()->customer->set_shipping_state( $_POST['s_state'] ); 
  311.  
  312. if ( isset( $_POST['s_postcode'] ) ) { 
  313. WC()->customer->set_shipping_postcode( $_POST['s_postcode'] ); 
  314.  
  315. if ( isset( $_POST['s_city'] ) ) { 
  316. WC()->customer->set_shipping_city( $_POST['s_city'] ); 
  317.  
  318. if ( isset( $_POST['s_address'] ) ) { 
  319. WC()->customer->set_shipping_address( $_POST['s_address'] ); 
  320.  
  321. if ( isset( $_POST['s_address_2'] ) ) { 
  322. WC()->customer->set_shipping_address_2( $_POST['s_address_2'] ); 
  323.  
  324. WC()->cart->calculate_totals(); 
  325.  
  326. // Get order review fragment 
  327. ob_start(); 
  328. woocommerce_order_review(); 
  329. $woocommerce_order_review = ob_get_clean(); 
  330.  
  331. // Get checkout payment fragment 
  332. ob_start(); 
  333. woocommerce_checkout_payment(); 
  334. $woocommerce_checkout_payment = ob_get_clean(); 
  335.  
  336. // Get messages if reload checkout is not true 
  337. $messages = ''; 
  338. if ( ! isset( WC()->session->reload_checkout ) ) { 
  339. ob_start(); 
  340. wc_print_notices(); 
  341. $messages = ob_get_clean(); 
  342.  
  343. $data = array( 
  344. 'result' => empty( $messages ) ? 'success' : 'failure',  
  345. 'messages' => $messages,  
  346. 'reload' => isset( WC()->session->reload_checkout ) ? 'true' : 'false',  
  347. 'fragments' => apply_filters( 'woocommerce_update_order_review_fragments', array( 
  348. '.woocommerce-checkout-review-order-table' => $woocommerce_order_review,  
  349. '.woocommerce-checkout-payment' => $woocommerce_checkout_payment 
  350. ) ) 
  351. ); 
  352.  
  353. unset( WC()->session->refresh_totals, WC()->session->reload_checkout ); 
  354.  
  355. wp_send_json( $data ); 
  356.  
  357. die(); 
  358.  
  359. /** 
  360. * AJAX add to cart. 
  361. */ 
  362. public static function add_to_cart() { 
  363. ob_start(); 
  364.  
  365. $product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_POST['product_id'] ) ); 
  366. $quantity = empty( $_POST['quantity'] ) ? 1 : wc_stock_amount( $_POST['quantity'] ); 
  367. $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity ); 
  368. $product_status = get_post_status( $product_id ); 
  369.  
  370. if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) && 'publish' === $product_status ) { 
  371.  
  372. do_action( 'woocommerce_ajax_added_to_cart', $product_id ); 
  373.  
  374. if ( get_option( 'woocommerce_cart_redirect_after_add' ) == 'yes' ) { 
  375. wc_add_to_cart_message( array( $product_id => $quantity ), true ); 
  376.  
  377. // Return fragments 
  378. self::get_refreshed_fragments(); 
  379.  
  380. } else { 
  381.  
  382. // If there was an error adding to the cart, redirect to the product page to show any errors 
  383. $data = array( 
  384. 'error' => true,  
  385. 'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id ) 
  386. ); 
  387.  
  388. wp_send_json( $data ); 
  389.  
  390.  
  391. die(); 
  392.  
  393. /** 
  394. * Process ajax checkout form. 
  395. */ 
  396. public static function checkout() { 
  397. if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { 
  398. define( 'WOOCOMMERCE_CHECKOUT', true ); 
  399.  
  400. WC()->checkout()->process_checkout(); 
  401.  
  402. die(0); 
  403.  
  404. /** 
  405. * Get a matching variation based on posted attributes. 
  406. */ 
  407. public static function get_variation() { 
  408. ob_start(); 
  409.  
  410. if ( empty( $_POST['product_id'] ) || ! ( $variable_product = wc_get_product( absint( $_POST['product_id'] ), array( 'product_type' => 'variable' ) ) ) ) { 
  411. die(); 
  412.  
  413. $variation_id = $variable_product->get_matching_variation( wp_unslash( $_POST ) ); 
  414.  
  415. if ( $variation_id ) { 
  416. $variation = $variable_product->get_available_variation( $variation_id ); 
  417. } else { 
  418. $variation = false; 
  419.  
  420. wp_send_json( $variation ); 
  421.  
  422. die(); 
  423.  
  424. /** 
  425. * Feature a product from admin. 
  426. */ 
  427. public static function feature_product() { 
  428. if ( current_user_can( 'edit_products' ) && check_admin_referer( 'woocommerce-feature-product' ) ) { 
  429. $product_id = absint( $_GET['product_id'] ); 
  430.  
  431. if ( 'product' === get_post_type( $product_id ) ) { 
  432. update_post_meta( $product_id, '_featured', get_post_meta( $product_id, '_featured', true ) === 'yes' ? 'no' : 'yes' ); 
  433.  
  434. delete_transient( 'wc_featured_products' ); 
  435.  
  436. wp_safe_redirect( wp_get_referer() ? remove_query_arg( array( 'trashed', 'untrashed', 'deleted', 'ids' ), wp_get_referer() ) : admin_url( 'edit.php?post_type=product' ) ); 
  437. die(); 
  438.  
  439. /** 
  440. * Mark an order with a status. 
  441. */ 
  442. public static function mark_order_status() { 
  443. if ( current_user_can( 'edit_shop_orders' ) && check_admin_referer( 'woocommerce-mark-order-status' ) ) { 
  444. $status = sanitize_text_field( $_GET['status'] ); 
  445. $order_id = absint( $_GET['order_id'] ); 
  446.  
  447. if ( wc_is_order_status( 'wc-' . $status ) && $order_id ) { 
  448. $order = wc_get_order( $order_id ); 
  449. $order->update_status( $status, '', true ); 
  450. do_action( 'woocommerce_order_edit_status', $order_id, $status ); 
  451.  
  452. wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'edit.php?post_type=shop_order' ) ); 
  453. die(); 
  454.  
  455. /** 
  456. * Add an attribute row. 
  457. */ 
  458. public static function add_attribute() { 
  459. ob_start(); 
  460.  
  461. check_ajax_referer( 'add-attribute', 'security' ); 
  462.  
  463. if ( ! current_user_can( 'edit_products' ) ) { 
  464. die(-1); 
  465.  
  466. global $wc_product_attributes; 
  467.  
  468. $thepostid = 0; 
  469. $taxonomy = sanitize_text_field( $_POST['taxonomy'] ); 
  470. $i = absint( $_POST['i'] ); 
  471. $position = 0; 
  472. $metabox_class = array(); 
  473. $attribute = array( 
  474. 'name' => $taxonomy,  
  475. 'value' => '',  
  476. 'is_visible' => apply_filters( 'woocommerce_attribute_default_visibility', 1 ),  
  477. 'is_variation' => apply_filters( 'woocommerce_attribute_default_is_variation', 0 ),  
  478. 'is_taxonomy' => $taxonomy ? 1 : 0 
  479. ); 
  480.  
  481. if ( $taxonomy ) { 
  482. $attribute_taxonomy = $wc_product_attributes[ $taxonomy ]; 
  483. $metabox_class[] = 'taxonomy'; 
  484. $metabox_class[] = $taxonomy; 
  485. $attribute_label = wc_attribute_label( $taxonomy ); 
  486. } else { 
  487. $attribute_label = ''; 
  488.  
  489. include( 'admin/meta-boxes/views/html-product-attribute.php' ); 
  490. die(); 
  491.  
  492. /** 
  493. * Add a new attribute via ajax function. 
  494. */ 
  495. public static function add_new_attribute() { 
  496. ob_start(); 
  497.  
  498. check_ajax_referer( 'add-attribute', 'security' ); 
  499.  
  500. if ( ! current_user_can( 'manage_product_terms' ) ) { 
  501. die(-1); 
  502.  
  503. $taxonomy = esc_attr( $_POST['taxonomy'] ); 
  504. $term = wc_clean( $_POST['term'] ); 
  505.  
  506. if ( taxonomy_exists( $taxonomy ) ) { 
  507.  
  508. $result = wp_insert_term( $term, $taxonomy ); 
  509.  
  510. if ( is_wp_error( $result ) ) { 
  511. wp_send_json( array( 
  512. 'error' => $result->get_error_message() 
  513. ) ); 
  514. } else { 
  515. $term = get_term_by( 'id', $result['term_id'], $taxonomy ); 
  516. wp_send_json( array( 
  517. 'term_id' => $term->term_id,  
  518. 'name' => $term->name,  
  519. 'slug' => $term->slug 
  520. ) ); 
  521.  
  522. die(); 
  523.  
  524. /** 
  525. * Delete variations via ajax function. 
  526. */ 
  527. public static function remove_variations() { 
  528. check_ajax_referer( 'delete-variations', 'security' ); 
  529.  
  530. if ( ! current_user_can( 'edit_products' ) ) { 
  531. die(-1); 
  532.  
  533. $variation_ids = (array) $_POST['variation_ids']; 
  534.  
  535. foreach ( $variation_ids as $variation_id ) { 
  536. $variation = get_post( $variation_id ); 
  537.  
  538. if ( $variation && 'product_variation' == $variation->post_type ) { 
  539. wp_delete_post( $variation_id ); 
  540.  
  541. die(); 
  542.  
  543. /** 
  544. * Save attributes via ajax. 
  545. */ 
  546. public static function save_attributes() { 
  547.  
  548. check_ajax_referer( 'save-attributes', 'security' ); 
  549.  
  550. if ( ! current_user_can( 'edit_products' ) ) { 
  551. die(-1); 
  552.  
  553. // Get post data 
  554. parse_str( $_POST['data'], $data ); 
  555. $post_id = absint( $_POST['post_id'] ); 
  556.  
  557. // Save Attributes 
  558. $attributes = array(); 
  559.  
  560. if ( isset( $data['attribute_names'] ) ) { 
  561.  
  562. $attribute_names = array_map( 'stripslashes', $data['attribute_names'] ); 
  563. $attribute_values = isset( $data['attribute_values'] ) ? $data['attribute_values'] : array(); 
  564.  
  565. if ( isset( $data['attribute_visibility'] ) ) { 
  566. $attribute_visibility = $data['attribute_visibility']; 
  567.  
  568. if ( isset( $data['attribute_variation'] ) ) { 
  569. $attribute_variation = $data['attribute_variation']; 
  570.  
  571. $attribute_is_taxonomy = $data['attribute_is_taxonomy']; 
  572. $attribute_position = $data['attribute_position']; 
  573. $attribute_names_max_key = max( array_keys( $attribute_names ) ); 
  574.  
  575. for ( $i = 0; $i <= $attribute_names_max_key; $i++ ) { 
  576. if ( empty( $attribute_names[ $i ] ) ) { 
  577. continue; 
  578.  
  579. $is_visible = isset( $attribute_visibility[ $i ] ) ? 1 : 0; 
  580. $is_variation = isset( $attribute_variation[ $i ] ) ? 1 : 0; 
  581. $is_taxonomy = $attribute_is_taxonomy[ $i ] ? 1 : 0; 
  582.  
  583. if ( $is_taxonomy ) { 
  584.  
  585. if ( isset( $attribute_values[ $i ] ) ) { 
  586.  
  587. // Select based attributes - Format values (posted values are slugs) 
  588. if ( is_array( $attribute_values[ $i ] ) ) { 
  589. $values = array_map( 'sanitize_title', $attribute_values[ $i ] ); 
  590.  
  591. // Text based attributes - Posted values are term names, wp_set_object_terms wants ids or slugs. 
  592. } else { 
  593. $values = array(); 
  594. $raw_values = array_map( 'wc_sanitize_term_text_based', explode( WC_DELIMITER, $attribute_values[ $i ] ) ); 
  595.  
  596. foreach ( $raw_values as $value ) { 
  597. $term = get_term_by( 'name', $value, $attribute_names[ $i ] ); 
  598. if ( ! $term ) { 
  599. $term = wp_insert_term( $value, $attribute_names[ $i ] ); 
  600.  
  601. if ( $term && ! is_wp_error( $term ) ) { 
  602. $values[] = $term['term_id']; 
  603. } else { 
  604. $values[] = $term->term_id; 
  605.  
  606. // Remove empty items in the array 
  607. $values = array_filter( $values, 'strlen' ); 
  608.  
  609. } else { 
  610. $values = array(); 
  611.  
  612. // Update post terms 
  613. if ( taxonomy_exists( $attribute_names[ $i ] ) ) { 
  614. wp_set_object_terms( $post_id, $values, $attribute_names[ $i ] ); 
  615.  
  616. if ( ! empty( $values ) ) { 
  617. // Add attribute to array, but don't set values 
  618. $attributes[ sanitize_title( $attribute_names[ $i ] ) ] = array( 
  619. 'name' => wc_clean( $attribute_names[ $i ] ),  
  620. 'value' => '',  
  621. 'position' => $attribute_position[ $i ],  
  622. 'is_visible' => $is_visible,  
  623. 'is_variation' => $is_variation,  
  624. 'is_taxonomy' => $is_taxonomy 
  625. ); 
  626.  
  627. } elseif ( isset( $attribute_values[ $i ] ) ) { 
  628.  
  629. // Text based, possibly separated by pipes (WC_DELIMITER). Preserve line breaks in non-variation attributes. 
  630. $values = $is_variation ? wc_clean( $attribute_values[ $i ] ) : implode( "\n", array_map( 'wc_clean', explode( "\n", $attribute_values[ $i ] ) ) ); 
  631. $values = implode( ' ' . WC_DELIMITER . ' ', wc_get_text_attributes( $values ) ); 
  632.  
  633. // Custom attribute - Add attribute to array and set the values 
  634. $attributes[ sanitize_title( $attribute_names[ $i ] ) ] = array( 
  635. 'name' => wc_clean( $attribute_names[ $i ] ),  
  636. 'value' => $values,  
  637. 'position' => $attribute_position[ $i ],  
  638. 'is_visible' => $is_visible,  
  639. 'is_variation' => $is_variation,  
  640. 'is_taxonomy' => $is_taxonomy 
  641. ); 
  642.  
  643.  
  644. uasort( $attributes, 'wc_product_attribute_uasort_comparison' ); 
  645.  
  646. update_post_meta( $post_id, '_product_attributes', $attributes ); 
  647.  
  648. die(); 
  649.  
  650. /** 
  651. * Add variation via ajax function. 
  652. */ 
  653. public static function add_variation() { 
  654.  
  655. check_ajax_referer( 'add-variation', 'security' ); 
  656.  
  657. if ( ! current_user_can( 'edit_products' ) ) { 
  658. die(-1); 
  659.  
  660. global $post; 
  661.  
  662. $post_id = intval( $_POST['post_id'] ); 
  663. $post = get_post( $post_id ); // Set $post global so its available like within the admin screens 
  664. $loop = intval( $_POST['loop'] ); 
  665.  
  666. $variation = array( 
  667. 'post_title' => 'Product #' . $post_id . ' Variation',  
  668. 'post_content' => '',  
  669. 'post_status' => 'publish',  
  670. 'post_author' => get_current_user_id(),  
  671. 'post_parent' => $post_id,  
  672. 'post_type' => 'product_variation',  
  673. 'menu_order' => -1 
  674. ); 
  675.  
  676. $variation_id = wp_insert_post( $variation ); 
  677.  
  678. do_action( 'woocommerce_create_product_variation', $variation_id ); 
  679.  
  680. if ( $variation_id ) { 
  681. $variation = get_post( $variation_id ); 
  682. $variation_meta = get_post_meta( $variation_id ); 
  683. $variation_data = array(); 
  684. $shipping_classes = get_the_terms( $variation_id, 'product_shipping_class' ); 
  685. $variation_fields = array( 
  686. '_sku' => '',  
  687. '_stock' => '',  
  688. '_regular_price' => '',  
  689. '_sale_price' => '',  
  690. '_weight' => '',  
  691. '_length' => '',  
  692. '_width' => '',  
  693. '_height' => '',  
  694. '_download_limit' => '',  
  695. '_download_expiry' => '',  
  696. '_downloadable_files' => '',  
  697. '_downloadable' => '',  
  698. '_virtual' => '',  
  699. '_thumbnail_id' => '',  
  700. '_sale_price_dates_from' => '',  
  701. '_sale_price_dates_to' => '',  
  702. '_manage_stock' => '',  
  703. '_stock_status' => '',  
  704. '_backorders' => null,  
  705. '_tax_class' => null,  
  706. '_variation_description' => '' 
  707. ); 
  708.  
  709. foreach ( $variation_fields as $field => $value ) { 
  710. $variation_data[ $field ] = isset( $variation_meta[ $field ][0] ) ? maybe_unserialize( $variation_meta[ $field ][0] ) : $value; 
  711.  
  712. // Add the variation attributes 
  713. $variation_data = array_merge( $variation_data, wc_get_product_variation_attributes( $variation_id ) ); 
  714.  
  715. // Formatting 
  716. $variation_data['_regular_price'] = wc_format_localized_price( $variation_data['_regular_price'] ); 
  717. $variation_data['_sale_price'] = wc_format_localized_price( $variation_data['_sale_price'] ); 
  718. $variation_data['_weight'] = wc_format_localized_decimal( $variation_data['_weight'] ); 
  719. $variation_data['_length'] = wc_format_localized_decimal( $variation_data['_length'] ); 
  720. $variation_data['_width'] = wc_format_localized_decimal( $variation_data['_width'] ); 
  721. $variation_data['_height'] = wc_format_localized_decimal( $variation_data['_height'] ); 
  722. $variation_data['_thumbnail_id'] = absint( $variation_data['_thumbnail_id'] ); 
  723. $variation_data['image'] = $variation_data['_thumbnail_id'] ? wp_get_attachment_thumb_url( $variation_data['_thumbnail_id'] ) : ''; 
  724. $variation_data['shipping_class'] = $shipping_classes && ! is_wp_error( $shipping_classes ) ? current( $shipping_classes )->term_id : ''; 
  725. $variation_data['menu_order'] = $variation->menu_order; 
  726. $variation_data['_stock'] = wc_stock_amount( $variation_data['_stock'] ); 
  727.  
  728. // Get tax classes 
  729. $tax_classes = WC_Tax::get_tax_classes(); 
  730. $tax_class_options = array(); 
  731. $tax_class_options[''] = __( 'Standard', 'woocommerce' ); 
  732.  
  733. if ( ! empty( $tax_classes ) ) { 
  734. foreach ( $tax_classes as $class ) { 
  735. $tax_class_options[ sanitize_title( $class ) ] = esc_attr( $class ); 
  736.  
  737. // Set backorder options 
  738. $backorder_options = array( 
  739. 'no' => __( 'Do not allow', 'woocommerce' ),  
  740. 'notify' => __( 'Allow, but notify customer', 'woocommerce' ),  
  741. 'yes' => __( 'Allow', 'woocommerce' ) 
  742. ); 
  743.  
  744. // set stock status options 
  745. $stock_status_options = array( 
  746. 'instock' => __( 'In stock', 'woocommerce' ),  
  747. 'outofstock' => __( 'Out of stock', 'woocommerce' ) 
  748. ); 
  749.  
  750. // Get attributes 
  751. $attributes = (array) maybe_unserialize( get_post_meta( $post_id, '_product_attributes', true ) ); 
  752.  
  753. $parent_data = array( 
  754. 'id' => $post_id,  
  755. 'attributes' => $attributes,  
  756. 'tax_class_options' => $tax_class_options,  
  757. 'sku' => get_post_meta( $post_id, '_sku', true ),  
  758. 'weight' => wc_format_localized_decimal( get_post_meta( $post_id, '_weight', true ) ),  
  759. 'length' => wc_format_localized_decimal( get_post_meta( $post_id, '_length', true ) ),  
  760. 'width' => wc_format_localized_decimal( get_post_meta( $post_id, '_width', true ) ),  
  761. 'height' => wc_format_localized_decimal( get_post_meta( $post_id, '_height', true ) ),  
  762. 'tax_class' => get_post_meta( $post_id, '_tax_class', true ),  
  763. 'backorder_options' => $backorder_options,  
  764. 'stock_status_options' => $stock_status_options 
  765. ); 
  766.  
  767. if ( ! $parent_data['weight'] ) { 
  768. $parent_data['weight'] = wc_format_localized_decimal( 0 ); 
  769.  
  770. if ( ! $parent_data['length'] ) { 
  771. $parent_data['length'] = wc_format_localized_decimal( 0 ); 
  772.  
  773. if ( ! $parent_data['width'] ) { 
  774. $parent_data['width'] = wc_format_localized_decimal( 0 ); 
  775.  
  776. if ( ! $parent_data['height'] ) { 
  777. $parent_data['height'] = wc_format_localized_decimal( 0 ); 
  778.  
  779. include( 'admin/meta-boxes/views/html-variation-admin.php' ); 
  780.  
  781. die(); 
  782.  
  783. /** 
  784. * Link all variations via ajax function. 
  785. */ 
  786. public static function link_all_variations() { 
  787.  
  788. if ( ! defined( 'WC_MAX_LINKED_VARIATIONS' ) ) { 
  789. define( 'WC_MAX_LINKED_VARIATIONS', 49 ); 
  790.  
  791. check_ajax_referer( 'link-variations', 'security' ); 
  792.  
  793. if ( ! current_user_can( 'edit_products' ) ) { 
  794. die(-1); 
  795.  
  796. wc_set_time_limit( 0 ); 
  797.  
  798. $post_id = intval( $_POST['post_id'] ); 
  799.  
  800. if ( ! $post_id ) { 
  801. die(); 
  802.  
  803. $variations = array(); 
  804. $_product = wc_get_product( $post_id, array( 'product_type' => 'variable' ) ); 
  805.  
  806. // Put variation attributes into an array 
  807. foreach ( $_product->get_attributes() as $attribute ) { 
  808.  
  809. if ( ! $attribute['is_variation'] ) { 
  810. continue; 
  811.  
  812. $attribute_field_name = 'attribute_' . sanitize_title( $attribute['name'] ); 
  813.  
  814. if ( $attribute['is_taxonomy'] ) { 
  815. $options = wc_get_product_terms( $post_id, $attribute['name'], array( 'fields' => 'slugs' ) ); 
  816. } else { 
  817. $options = explode( WC_DELIMITER, $attribute['value'] ); 
  818.  
  819. $options = array_map( 'trim', $options ); 
  820.  
  821. $variations[ $attribute_field_name ] = $options; 
  822.  
  823. // Quit out if none were found 
  824. if ( sizeof( $variations ) == 0 ) { 
  825. die(); 
  826.  
  827. // Get existing variations so we don't create duplicates 
  828. $available_variations = array(); 
  829.  
  830. foreach( $_product->get_children() as $child_id ) { 
  831. $child = $_product->get_child( $child_id ); 
  832.  
  833. if ( ! empty( $child->variation_id ) ) { 
  834. $available_variations[] = $child->get_variation_attributes(); 
  835.  
  836. // Created posts will all have the following data 
  837. $variation_post_data = array( 
  838. 'post_title' => 'Product #' . $post_id . ' Variation',  
  839. 'post_content' => '',  
  840. 'post_status' => 'publish',  
  841. 'post_author' => get_current_user_id(),  
  842. 'post_parent' => $post_id,  
  843. 'post_type' => 'product_variation' 
  844. ); 
  845.  
  846. $variation_ids = array(); 
  847. $added = 0; 
  848. $possible_variations = wc_array_cartesian( $variations ); 
  849.  
  850. foreach ( $possible_variations as $variation ) { 
  851.  
  852. // Check if variation already exists 
  853. if ( in_array( $variation, $available_variations ) ) { 
  854. continue; 
  855.  
  856. $variation_id = wp_insert_post( $variation_post_data ); 
  857.  
  858. $variation_ids[] = $variation_id; 
  859.  
  860. foreach ( $variation as $key => $value ) { 
  861. update_post_meta( $variation_id, $key, $value ); 
  862.  
  863. // Save stock status 
  864. update_post_meta( $variation_id, '_stock_status', 'instock' ); 
  865.  
  866. $added++; 
  867.  
  868. do_action( 'product_variation_linked', $variation_id ); 
  869.  
  870. if ( $added > WC_MAX_LINKED_VARIATIONS ) { 
  871. break; 
  872.  
  873. delete_transient( 'wc_product_children_' . $post_id ); 
  874.  
  875. echo $added; 
  876.  
  877. die(); 
  878.  
  879. /** 
  880. * Delete download permissions via ajax function. 
  881. */ 
  882. public static function revoke_access_to_download() { 
  883. check_ajax_referer( 'revoke-access', 'security' ); 
  884.  
  885. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  886. die( -1 ); 
  887.  
  888. global $wpdb; 
  889.  
  890. $download_id = $_POST['download_id']; 
  891. $product_id = intval( $_POST['product_id'] ); 
  892. $order_id = intval( $_POST['order_id'] ); 
  893. $permission_id = absint( $_POST['permission_id'] ); 
  894.  
  895. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE permission_id = %d;", $permission_id ) ); 
  896.  
  897. do_action( 'woocommerce_ajax_revoke_access_to_product_download', $download_id, $product_id, $order_id, $permission_id ); 
  898.  
  899. die(); 
  900.  
  901. /** 
  902. * Grant download permissions via ajax function. 
  903. */ 
  904. public static function grant_access_to_download() { 
  905.  
  906. check_ajax_referer( 'grant-access', 'security' ); 
  907.  
  908. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  909. die(-1); 
  910.  
  911. global $wpdb; 
  912.  
  913. $wpdb->hide_errors(); 
  914.  
  915. $order_id = intval( $_POST['order_id'] ); 
  916. $product_ids = $_POST['product_ids']; 
  917. $loop = intval( $_POST['loop'] ); 
  918. $file_counter = 0; 
  919. $order = wc_get_order( $order_id ); 
  920.  
  921. if ( ! is_array( $product_ids ) ) { 
  922. $product_ids = array( $product_ids ); 
  923.  
  924. foreach ( $product_ids as $product_id ) { 
  925. $product = wc_get_product( $product_id ); 
  926. $files = $product->get_files(); 
  927.  
  928. if ( ! $order->billing_email ) { 
  929. die(); 
  930.  
  931. if ( ! empty( $files ) ) { 
  932. foreach ( $files as $download_id => $file ) { 
  933. if ( $inserted_id = wc_downloadable_file_permission( $download_id, $product_id, $order ) ) { 
  934.  
  935. // insert complete - get inserted data 
  936. $download = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE permission_id = %d", $inserted_id ) ); 
  937.  
  938. $loop ++; 
  939. $file_counter ++; 
  940.  
  941. if ( isset( $file['name'] ) ) { 
  942. $file_count = $file['name']; 
  943. } else { 
  944. $file_count = sprintf( __( 'File %d', 'woocommerce' ), $file_counter ); 
  945. include( 'admin/meta-boxes/views/html-order-download-permission.php' ); 
  946.  
  947. die(); 
  948.  
  949. /** 
  950. * Get customer details via ajax. 
  951. */ 
  952. public static function get_customer_details() { 
  953. ob_start(); 
  954.  
  955. check_ajax_referer( 'get-customer-details', 'security' ); 
  956.  
  957. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  958. die(-1); 
  959.  
  960. $user_id = (int) trim(stripslashes($_POST['user_id'])); 
  961. $type_to_load = esc_attr(trim(stripslashes($_POST['type_to_load']))); 
  962.  
  963. $customer_data = array( 
  964. $type_to_load . '_first_name' => get_user_meta( $user_id, $type_to_load . '_first_name', true ),  
  965. $type_to_load . '_last_name' => get_user_meta( $user_id, $type_to_load . '_last_name', true ),  
  966. $type_to_load . '_company' => get_user_meta( $user_id, $type_to_load . '_company', true ),  
  967. $type_to_load . '_address_1' => get_user_meta( $user_id, $type_to_load . '_address_1', true ),  
  968. $type_to_load . '_address_2' => get_user_meta( $user_id, $type_to_load . '_address_2', true ),  
  969. $type_to_load . '_city' => get_user_meta( $user_id, $type_to_load . '_city', true ),  
  970. $type_to_load . '_postcode' => get_user_meta( $user_id, $type_to_load . '_postcode', true ),  
  971. $type_to_load . '_country' => get_user_meta( $user_id, $type_to_load . '_country', true ),  
  972. $type_to_load . '_state' => get_user_meta( $user_id, $type_to_load . '_state', true ),  
  973. $type_to_load . '_email' => get_user_meta( $user_id, $type_to_load . '_email', true ),  
  974. $type_to_load . '_phone' => get_user_meta( $user_id, $type_to_load . '_phone', true ),  
  975. ); 
  976.  
  977. $customer_data = apply_filters( 'woocommerce_found_customer_details', $customer_data, $user_id, $type_to_load ); 
  978.  
  979. wp_send_json( $customer_data ); 
  980.  
  981. /** 
  982. * Add order item via ajax. 
  983. */ 
  984. public static function add_order_item() { 
  985. check_ajax_referer( 'order-item', 'security' ); 
  986.  
  987. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  988. die(-1); 
  989.  
  990. $item_to_add = sanitize_text_field( $_POST['item_to_add'] ); 
  991. $order_id = absint( $_POST['order_id'] ); 
  992.  
  993. // Find the item 
  994. if ( ! is_numeric( $item_to_add ) ) { 
  995. die(); 
  996.  
  997. $post = get_post( $item_to_add ); 
  998.  
  999. if ( ! $post || ( 'product' !== $post->post_type && 'product_variation' !== $post->post_type ) ) { 
  1000. die(); 
  1001.  
  1002. $_product = wc_get_product( $post->ID ); 
  1003. $order = wc_get_order( $order_id ); 
  1004. $order_taxes = $order->get_taxes(); 
  1005. $class = 'new_row'; 
  1006.  
  1007. // Set values 
  1008. $item = array(); 
  1009.  
  1010. $item['product_id'] = $_product->id; 
  1011. $item['variation_id'] = isset( $_product->variation_id ) ? $_product->variation_id : ''; 
  1012. $item['variation_data'] = $item['variation_id'] ? $_product->get_variation_attributes() : ''; 
  1013. $item['name'] = $_product->get_title(); 
  1014. $item['tax_class'] = $_product->get_tax_class(); 
  1015. $item['qty'] = 1; 
  1016. $item['line_subtotal'] = wc_format_decimal( $_product->get_price_excluding_tax() ); 
  1017. $item['line_subtotal_tax'] = ''; 
  1018. $item['line_total'] = wc_format_decimal( $_product->get_price_excluding_tax() ); 
  1019. $item['line_tax'] = ''; 
  1020. $item['type'] = 'line_item'; 
  1021.  
  1022. // Add line item 
  1023. $item_id = wc_add_order_item( $order_id, array( 
  1024. 'order_item_name' => $item['name'],  
  1025. 'order_item_type' => 'line_item' 
  1026. ) ); 
  1027.  
  1028. // Add line item meta 
  1029. if ( $item_id ) { 
  1030. wc_add_order_item_meta( $item_id, '_qty', $item['qty'] ); 
  1031. wc_add_order_item_meta( $item_id, '_tax_class', $item['tax_class'] ); 
  1032. wc_add_order_item_meta( $item_id, '_product_id', $item['product_id'] ); 
  1033. wc_add_order_item_meta( $item_id, '_variation_id', $item['variation_id'] ); 
  1034. wc_add_order_item_meta( $item_id, '_line_subtotal', $item['line_subtotal'] ); 
  1035. wc_add_order_item_meta( $item_id, '_line_subtotal_tax', $item['line_subtotal_tax'] ); 
  1036. wc_add_order_item_meta( $item_id, '_line_total', $item['line_total'] ); 
  1037. wc_add_order_item_meta( $item_id, '_line_tax', $item['line_tax'] ); 
  1038.  
  1039. // Since 2.2 
  1040. wc_add_order_item_meta( $item_id, '_line_tax_data', array( 'total' => array(), 'subtotal' => array() ) ); 
  1041.  
  1042. // Store variation data in meta 
  1043. if ( $item['variation_data'] && is_array( $item['variation_data'] ) ) { 
  1044. foreach ( $item['variation_data'] as $key => $value ) { 
  1045. wc_add_order_item_meta( $item_id, str_replace( 'attribute_', '', $key ), $value ); 
  1046.  
  1047. do_action( 'woocommerce_ajax_add_order_item_meta', $item_id, $item ); 
  1048.  
  1049. $item['item_meta'] = $order->get_item_meta( $item_id ); 
  1050. $item['item_meta_array'] = $order->get_item_meta_array( $item_id ); 
  1051. $item = $order->expand_item_meta( $item ); 
  1052. $item = apply_filters( 'woocommerce_ajax_order_item', $item, $item_id ); 
  1053.  
  1054. include( 'admin/meta-boxes/views/html-order-item.php' ); 
  1055.  
  1056. // Quit out 
  1057. die(); 
  1058.  
  1059. /** 
  1060. * Add order fee via ajax. 
  1061. */ 
  1062. public static function add_order_fee() { 
  1063.  
  1064. check_ajax_referer( 'order-item', 'security' ); 
  1065.  
  1066. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1067. die(-1); 
  1068.  
  1069. $order_id = absint( $_POST['order_id'] ); 
  1070. $order = wc_get_order( $order_id ); 
  1071. $order_taxes = $order->get_taxes(); 
  1072. $item = array(); 
  1073.  
  1074. // Add new fee 
  1075. $fee = new stdClass(); 
  1076. $fee->name = ''; 
  1077. $fee->tax_class = ''; 
  1078. $fee->taxable = $fee->tax_class !== '0'; 
  1079. $fee->amount = ''; 
  1080. $fee->tax = ''; 
  1081. $fee->tax_data = array(); 
  1082. $item_id = $order->add_fee( $fee ); 
  1083.  
  1084. include( 'admin/meta-boxes/views/html-order-fee.php' ); 
  1085.  
  1086. // Quit out 
  1087. die(); 
  1088.  
  1089. /** 
  1090. * Add order shipping cost via ajax. 
  1091. */ 
  1092. public static function add_order_shipping() { 
  1093.  
  1094. check_ajax_referer( 'order-item', 'security' ); 
  1095.  
  1096. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1097. die(-1); 
  1098.  
  1099. $order_id = absint( $_POST['order_id'] ); 
  1100. $order = wc_get_order( $order_id ); 
  1101. $order_taxes = $order->get_taxes(); 
  1102. $shipping_methods = WC()->shipping() ? WC()->shipping->load_shipping_methods() : array(); 
  1103. $item = array(); 
  1104.  
  1105. // Add new shipping 
  1106. $shipping = new WC_Shipping_Rate(); 
  1107. $item_id = $order->add_shipping( $shipping ); 
  1108.  
  1109. include( 'admin/meta-boxes/views/html-order-shipping.php' ); 
  1110.  
  1111. // Quit out 
  1112. die(); 
  1113.  
  1114. /** 
  1115. * Add order tax column via ajax. 
  1116. */ 
  1117. public static function add_order_tax() { 
  1118. global $wpdb; 
  1119.  
  1120. check_ajax_referer( 'order-item', 'security' ); 
  1121.  
  1122. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1123. die(-1); 
  1124.  
  1125. $order_id = absint( $_POST['order_id'] ); 
  1126. $rate_id = absint( $_POST['rate_id'] ); 
  1127. $order = wc_get_order( $order_id ); 
  1128. $data = get_post_meta( $order_id ); 
  1129.  
  1130. // Add new tax 
  1131. $order->add_tax( $rate_id, 0, 0 ); 
  1132.  
  1133. // Return HTML items 
  1134. include( 'admin/meta-boxes/views/html-order-items.php' ); 
  1135.  
  1136. die(); 
  1137.  
  1138. /** 
  1139. * Remove an order item. 
  1140. */ 
  1141. public static function remove_order_item() { 
  1142. check_ajax_referer( 'order-item', 'security' ); 
  1143.  
  1144. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1145. die(-1); 
  1146.  
  1147. $order_item_ids = $_POST['order_item_ids']; 
  1148.  
  1149. if ( ! is_array( $order_item_ids ) && is_numeric( $order_item_ids ) ) { 
  1150. $order_item_ids = array( $order_item_ids ); 
  1151.  
  1152. if ( sizeof( $order_item_ids ) > 0 ) { 
  1153. foreach( $order_item_ids as $id ) { 
  1154. wc_delete_order_item( absint( $id ) ); 
  1155.  
  1156. die(); 
  1157.  
  1158. /** 
  1159. * Remove an order tax. 
  1160. */ 
  1161. public static function remove_order_tax() { 
  1162.  
  1163. check_ajax_referer( 'order-item', 'security' ); 
  1164.  
  1165. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1166. die(-1); 
  1167.  
  1168. $order_id = absint( $_POST['order_id'] ); 
  1169. $rate_id = absint( $_POST['rate_id'] ); 
  1170.  
  1171. wc_delete_order_item( $rate_id ); 
  1172.  
  1173. // Return HTML items 
  1174. $order = wc_get_order( $order_id ); 
  1175. $data = get_post_meta( $order_id ); 
  1176. include( 'admin/meta-boxes/views/html-order-items.php' ); 
  1177.  
  1178. die(); 
  1179.  
  1180. /** 
  1181. * Reduce order item stock. 
  1182. */ 
  1183. public static function reduce_order_item_stock() { 
  1184. check_ajax_referer( 'order-item', 'security' ); 
  1185. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1186. die(-1); 
  1187. $order_id = absint( $_POST['order_id'] ); 
  1188. $order_item_ids = isset( $_POST['order_item_ids'] ) ? $_POST['order_item_ids'] : array(); 
  1189. $order_item_qty = isset( $_POST['order_item_qty'] ) ? $_POST['order_item_qty'] : array(); 
  1190. $order = wc_get_order( $order_id ); 
  1191. $order_items = $order->get_items(); 
  1192. $return = array(); 
  1193. if ( $order && ! empty( $order_items ) && sizeof( $order_item_ids ) > 0 ) { 
  1194. foreach ( $order_items as $item_id => $order_item ) { 
  1195. // Only reduce checked items 
  1196. if ( ! in_array( $item_id, $order_item_ids ) ) { 
  1197. continue; 
  1198. $_product = $order->get_product_from_item( $order_item ); 
  1199. if ( $_product->exists() && $_product->managing_stock() && isset( $order_item_qty[ $item_id ] ) && $order_item_qty[ $item_id ] > 0 ) { 
  1200. $stock_change = apply_filters( 'woocommerce_reduce_order_stock_quantity', $order_item_qty[ $item_id ], $item_id ); 
  1201. $new_stock = $_product->reduce_stock( $stock_change ); 
  1202. $item_name = $_product->get_sku() ? $_product->get_sku() : $order_item['product_id']; 
  1203. $note = sprintf( __( 'Item %s stock reduced from %s to %s.', 'woocommerce' ), $item_name, $new_stock + $stock_change, $new_stock ); 
  1204. $return[] = $note; 
  1205. $order->add_order_note( $note ); 
  1206. $order->send_stock_notifications( $_product, $new_stock, $order_item_qty[ $item_id ] ); 
  1207. do_action( 'woocommerce_reduce_order_stock', $order ); 
  1208. if ( empty( $return ) ) { 
  1209. $return[] = __( 'No products had their stock reduced - they may not have stock management enabled.', 'woocommerce' ); 
  1210. echo implode( ', ', $return ); 
  1211. die(); 
  1212.  
  1213. /** 
  1214. * Increase order item stock. 
  1215. */ 
  1216. public static function increase_order_item_stock() { 
  1217. check_ajax_referer( 'order-item', 'security' ); 
  1218. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1219. die(-1); 
  1220. $order_id = absint( $_POST['order_id'] ); 
  1221. $order_item_ids = isset( $_POST['order_item_ids'] ) ? $_POST['order_item_ids'] : array(); 
  1222. $order_item_qty = isset( $_POST['order_item_qty'] ) ? $_POST['order_item_qty'] : array(); 
  1223. $order = wc_get_order( $order_id ); 
  1224. $order_items = $order->get_items(); 
  1225. $return = array(); 
  1226. if ( $order && ! empty( $order_items ) && sizeof( $order_item_ids ) > 0 ) { 
  1227. foreach ( $order_items as $item_id => $order_item ) { 
  1228. // Only reduce checked items 
  1229. if ( ! in_array( $item_id, $order_item_ids ) ) { 
  1230. continue; 
  1231. $_product = $order->get_product_from_item( $order_item ); 
  1232. if ( $_product->exists() && $_product->managing_stock() && isset( $order_item_qty[ $item_id ] ) && $order_item_qty[ $item_id ] > 0 ) { 
  1233. $old_stock = $_product->get_stock_quantity(); 
  1234. $stock_change = apply_filters( 'woocommerce_restore_order_stock_quantity', $order_item_qty[ $item_id ], $item_id ); 
  1235. $new_quantity = $_product->increase_stock( $stock_change ); 
  1236. $item_name = $_product->get_sku() ? $_product->get_sku(): $order_item['product_id']; 
  1237. $note = sprintf( __( 'Item %s stock increased from %s to %s.', 'woocommerce' ), $item_name, $old_stock, $new_quantity ); 
  1238. $return[] = $note; 
  1239. $order->add_order_note( $note ); 
  1240. do_action( 'woocommerce_restore_order_stock', $order ); 
  1241. if ( empty( $return ) ) { 
  1242. $return[] = __( 'No products had their stock increased - they may not have stock management enabled.', 'woocommerce' ); 
  1243. echo implode( ', ', $return ); 
  1244. die(); 
  1245.  
  1246. /** 
  1247. * Add some meta to a line item. 
  1248. */ 
  1249. public static function add_order_item_meta() { 
  1250. check_ajax_referer( 'order-item', 'security' ); 
  1251.  
  1252. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1253. die(-1); 
  1254.  
  1255. $meta_id = wc_add_order_item_meta( absint( $_POST['order_item_id'] ), __( 'Name', 'woocommerce' ), __( 'Value', 'woocommerce' ) ); 
  1256.  
  1257. if ( $meta_id ) { 
  1258. echo '<tr data-meta_id="' . esc_attr( $meta_id ) . '"><td><input type="text" name="meta_key[' . $meta_id . ']" /><textarea name="meta_value[' . $meta_id . ']"></textarea></td><td width="1%"><button class="remove_order_item_meta button">×</button></td></tr>'; 
  1259.  
  1260. die(); 
  1261.  
  1262. /** 
  1263. * Remove meta from a line item. 
  1264. */ 
  1265. public static function remove_order_item_meta() { 
  1266. check_ajax_referer( 'order-item', 'security' ); 
  1267.  
  1268. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1269. die(-1); 
  1270.  
  1271. global $wpdb; 
  1272.  
  1273. $wpdb->delete( "{$wpdb->prefix}woocommerce_order_itemmeta", array( 
  1274. 'meta_id' => absint( $_POST['meta_id'] ),  
  1275. ) ); 
  1276.  
  1277. die(); 
  1278.  
  1279. /** 
  1280. * Calc line tax. 
  1281. */ 
  1282. public static function calc_line_taxes() { 
  1283. global $wpdb; 
  1284.  
  1285. check_ajax_referer( 'calc-totals', 'security' ); 
  1286.  
  1287. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1288. die(-1); 
  1289.  
  1290. $tax = new WC_Tax(); 
  1291. $tax_based_on = get_option( 'woocommerce_tax_based_on' ); 
  1292. $order_id = absint( $_POST['order_id'] ); 
  1293. $items = array(); 
  1294. $country = strtoupper( esc_attr( $_POST['country'] ) ); 
  1295. $state = strtoupper( esc_attr( $_POST['state'] ) ); 
  1296. $postcode = strtoupper( esc_attr( $_POST['postcode'] ) ); 
  1297. $city = wc_clean( esc_attr( $_POST['city'] ) ); 
  1298. $order = wc_get_order( $order_id ); 
  1299. $taxes = array(); 
  1300. $shipping_taxes = array(); 
  1301. $order_item_tax_classes = array(); 
  1302.  
  1303. // Default to base 
  1304. if ( 'base' === $tax_based_on || empty( $country ) ) { 
  1305. $default = wc_get_base_location(); 
  1306. $country = $default['country']; 
  1307. $state = $default['state']; 
  1308. $postcode = ''; 
  1309. $city = ''; 
  1310.  
  1311. // Parse the jQuery serialized items 
  1312. parse_str( $_POST['items'], $items ); 
  1313.  
  1314. // Prevent undefined warnings 
  1315. if ( ! isset( $items['line_tax'] ) ) { 
  1316. $items['line_tax'] = array(); 
  1317. if ( ! isset( $items['line_subtotal_tax'] ) ) { 
  1318. $items['line_subtotal_tax'] = array(); 
  1319. $items['order_taxes'] = array(); 
  1320.  
  1321. // Action 
  1322. $items = apply_filters( 'woocommerce_ajax_calc_line_taxes', $items, $order_id, $country, $_POST ); 
  1323.  
  1324. $is_vat_exempt = get_post_meta( $order_id, '_is_vat_exempt', true ); 
  1325.  
  1326. // Tax is calculated only if tax is enabled and order is not vat exempted 
  1327. if ( wc_tax_enabled() && $is_vat_exempt !== 'yes' ) { 
  1328.  
  1329. // Get items and fees taxes 
  1330. if ( isset( $items['order_item_id'] ) ) { 
  1331. $line_total = $line_subtotal = array(); 
  1332.  
  1333. foreach ( $items['order_item_id'] as $item_id ) { 
  1334. $item_id = absint( $item_id ); 
  1335. $line_total[ $item_id ] = isset( $items['line_total'][ $item_id ] ) ? wc_format_decimal( $items['line_total'][ $item_id ] ) : 0; 
  1336. $line_subtotal[ $item_id ] = isset( $items['line_subtotal'][ $item_id ] ) ? wc_format_decimal( $items['line_subtotal'][ $item_id ] ) : $line_total[ $item_id ]; 
  1337. $order_item_tax_classes[ $item_id ] = isset( $items['order_item_tax_class'][ $item_id ] ) ? sanitize_text_field( $items['order_item_tax_class'][ $item_id ] ) : ''; 
  1338. $product_id = $order->get_item_meta( $item_id, '_product_id', true ); 
  1339.  
  1340. // Get product details 
  1341. if ( get_post_type( $product_id ) == 'product' ) { 
  1342. $_product = wc_get_product( $product_id ); 
  1343. $item_tax_status = $_product->get_tax_status(); 
  1344. } else { 
  1345. $item_tax_status = 'taxable'; 
  1346.  
  1347. if ( '0' !== $order_item_tax_classes[ $item_id ] && 'taxable' === $item_tax_status ) { 
  1348. $tax_rates = WC_Tax::find_rates( array( 
  1349. 'country' => $country,  
  1350. 'state' => $state,  
  1351. 'postcode' => $postcode,  
  1352. 'city' => $city,  
  1353. 'tax_class' => $order_item_tax_classes[ $item_id ] 
  1354. ) ); 
  1355.  
  1356. $line_taxes = WC_Tax::calc_tax( $line_total[ $item_id ], $tax_rates, false ); 
  1357. $line_subtotal_taxes = WC_Tax::calc_tax( $line_subtotal[ $item_id ], $tax_rates, false ); 
  1358.  
  1359. // Set the new line_tax 
  1360. foreach ( $line_taxes as $_tax_id => $_tax_value ) { 
  1361. $items['line_tax'][ $item_id ][ $_tax_id ] = $_tax_value; 
  1362.  
  1363. // Set the new line_subtotal_tax 
  1364. foreach ( $line_subtotal_taxes as $_tax_id => $_tax_value ) { 
  1365. $items['line_subtotal_tax'][ $item_id ][ $_tax_id ] = $_tax_value; 
  1366.  
  1367. // Sum the item taxes 
  1368. foreach ( array_keys( $taxes + $line_taxes ) as $key ) { 
  1369. $taxes[ $key ] = ( isset( $line_taxes[ $key ] ) ? $line_taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 ); 
  1370.  
  1371. // Get shipping taxes 
  1372. if ( isset( $items['shipping_method_id'] ) ) { 
  1373. $matched_tax_rates = array(); 
  1374. $order_item_tax_classes = array_unique( array_values( $order_item_tax_classes ) ); 
  1375.  
  1376. // If multiple classes are found, use the first one. Don't bother with standard rate, we can get that later. 
  1377. if ( sizeof( $order_item_tax_classes ) > 1 && ! in_array( '', $order_item_tax_classes ) ) { 
  1378. $tax_classes = WC_Tax::get_tax_classes(); 
  1379.  
  1380. foreach ( $tax_classes as $tax_class ) { 
  1381. $tax_class = sanitize_title( $tax_class ); 
  1382. if ( in_array( $tax_class, $order_item_tax_classes ) ) { 
  1383. $matched_tax_rates = WC_Tax::find_shipping_rates( array( 
  1384. 'country' => $country,  
  1385. 'state' => $state,  
  1386. 'postcode' => $postcode,  
  1387. 'city' => $city,  
  1388. 'tax_class' => $tax_class,  
  1389. ) ); 
  1390. break; 
  1391. // If a single tax class is found, use it 
  1392. } elseif ( sizeof( $order_item_tax_classes ) === 1 ) { 
  1393. $matched_tax_rates = WC_Tax::find_shipping_rates( array( 
  1394. 'country' => $country,  
  1395. 'state' => $state,  
  1396. 'postcode' => $postcode,  
  1397. 'city' => $city,  
  1398. 'tax_class' => $order_item_tax_classes[0] 
  1399. ) ); 
  1400.  
  1401. // Get standard rate if no taxes were found 
  1402. if ( ! sizeof( $matched_tax_rates ) ) { 
  1403. $matched_tax_rates = WC_Tax::find_shipping_rates( array( 
  1404. 'country' => $country,  
  1405. 'state' => $state,  
  1406. 'postcode' => $postcode,  
  1407. 'city' => $city 
  1408. ) ); 
  1409.  
  1410. $shipping_cost = $shipping_taxes = array(); 
  1411.  
  1412. foreach ( $items['shipping_method_id'] as $item_id ) { 
  1413. $item_id = absint( $item_id ); 
  1414. $shipping_cost[ $item_id ] = isset( $items['shipping_cost'][ $item_id ] ) ? wc_format_decimal( $items['shipping_cost'][ $item_id ] ) : 0; 
  1415. $_shipping_taxes = WC_Tax::calc_shipping_tax( $shipping_cost[ $item_id ], $matched_tax_rates ); 
  1416.  
  1417. // Set the new shipping_taxes 
  1418. foreach ( $_shipping_taxes as $_tax_id => $_tax_value ) { 
  1419. $items['shipping_taxes'][ $item_id ][ $_tax_id ] = $_tax_value; 
  1420.  
  1421. $shipping_taxes[ $_tax_id ] = isset( $shipping_taxes[ $_tax_id ] ) ? $shipping_taxes[ $_tax_id ] + $_tax_value : $_tax_value; 
  1422.  
  1423. // Remove old tax rows 
  1424. $order->remove_order_items( 'tax' ); 
  1425.  
  1426. // Add tax rows 
  1427. foreach ( array_keys( $taxes + $shipping_taxes ) as $tax_rate_id ) { 
  1428. $order->add_tax( $tax_rate_id, isset( $taxes[ $tax_rate_id ] ) ? $taxes[ $tax_rate_id ] : 0, isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 ); 
  1429.  
  1430. // Create the new order_taxes 
  1431. foreach ( $order->get_taxes() as $tax_id => $tax_item ) { 
  1432. $items['order_taxes'][ $tax_id ] = absint( $tax_item['rate_id'] ); 
  1433.  
  1434. $items = apply_filters( 'woocommerce_ajax_after_calc_line_taxes', $items, $order_id, $country, $_POST ); 
  1435.  
  1436. // Save order items 
  1437. wc_save_order_items( $order_id, $items ); 
  1438.  
  1439. // Return HTML items 
  1440. $order = wc_get_order( $order_id ); 
  1441. $data = get_post_meta( $order_id ); 
  1442. include( 'admin/meta-boxes/views/html-order-items.php' ); 
  1443.  
  1444. die(); 
  1445.  
  1446. /** 
  1447. * Save order items via ajax. 
  1448. */ 
  1449. public static function save_order_items() { 
  1450. check_ajax_referer( 'order-item', 'security' ); 
  1451.  
  1452. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1453. die(-1); 
  1454.  
  1455. if ( isset( $_POST['order_id'] ) && isset( $_POST['items'] ) ) { 
  1456. $order_id = absint( $_POST['order_id'] ); 
  1457.  
  1458. // Parse the jQuery serialized items 
  1459. $items = array(); 
  1460. parse_str( $_POST['items'], $items ); 
  1461.  
  1462. // Save order items 
  1463. wc_save_order_items( $order_id, $items ); 
  1464.  
  1465. // Return HTML items 
  1466. $order = wc_get_order( $order_id ); 
  1467. $data = get_post_meta( $order_id ); 
  1468. include( 'admin/meta-boxes/views/html-order-items.php' ); 
  1469.  
  1470. die(); 
  1471.  
  1472. /** 
  1473. * Load order items via ajax. 
  1474. */ 
  1475. public static function load_order_items() { 
  1476. check_ajax_referer( 'order-item', 'security' ); 
  1477.  
  1478. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1479. die(-1); 
  1480.  
  1481. // Return HTML items 
  1482. $order_id = absint( $_POST['order_id'] ); 
  1483. $order = wc_get_order( $order_id ); 
  1484. $data = get_post_meta( $order_id ); 
  1485. include( 'admin/meta-boxes/views/html-order-items.php' ); 
  1486.  
  1487. die(); 
  1488.  
  1489. /** 
  1490. * Add order note via ajax. 
  1491. */ 
  1492. public static function add_order_note() { 
  1493.  
  1494. check_ajax_referer( 'add-order-note', 'security' ); 
  1495.  
  1496. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1497. die(-1); 
  1498.  
  1499. $post_id = absint( $_POST['post_id'] ); 
  1500. $note = wp_kses_post( trim( stripslashes( $_POST['note'] ) ) ); 
  1501. $note_type = $_POST['note_type']; 
  1502.  
  1503. $is_customer_note = $note_type == 'customer' ? 1 : 0; 
  1504.  
  1505. if ( $post_id > 0 ) { 
  1506. $order = wc_get_order( $post_id ); 
  1507. $comment_id = $order->add_order_note( $note, $is_customer_note, true ); 
  1508.  
  1509. echo '<li rel="' . esc_attr( $comment_id ) . '" class="note '; 
  1510. if ( $is_customer_note ) { 
  1511. echo 'customer-note'; 
  1512. echo '"><div class="note_content">'; 
  1513. echo wpautop( wptexturize( $note ) ); 
  1514. echo '</div><p class="meta"><a href="#" class="delete_note">'.__( 'Delete note', 'woocommerce' ).'</a></p>'; 
  1515. echo '</li>'; 
  1516.  
  1517. // Quit out 
  1518. die(); 
  1519.  
  1520. /** 
  1521. * Delete order note via ajax. 
  1522. */ 
  1523. public static function delete_order_note() { 
  1524.  
  1525. check_ajax_referer( 'delete-order-note', 'security' ); 
  1526.  
  1527. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1528. die(-1); 
  1529.  
  1530. $note_id = (int) $_POST['note_id']; 
  1531.  
  1532. if ( $note_id > 0 ) { 
  1533. wp_delete_comment( $note_id ); 
  1534.  
  1535. // Quit out 
  1536. die(); 
  1537.  
  1538. /** 
  1539. * Search for products and echo json. 
  1540. * @param string $term (default: '') 
  1541. * @param string $post_types (default: array('product')) 
  1542. */ 
  1543. public static function json_search_products( $term = '', $post_types = array( 'product' ) ) { 
  1544. global $wpdb; 
  1545.  
  1546. ob_start(); 
  1547.  
  1548. check_ajax_referer( 'search-products', 'security' ); 
  1549.  
  1550. if ( empty( $term ) ) { 
  1551. $term = wc_clean( stripslashes( $_GET['term'] ) ); 
  1552. } else { 
  1553. $term = wc_clean( $term ); 
  1554.  
  1555. if ( empty( $term ) ) { 
  1556. die(); 
  1557.  
  1558. $like_term = '%' . $wpdb->esc_like( $term ) . '%'; 
  1559.  
  1560. if ( is_numeric( $term ) ) { 
  1561. $query = $wpdb->prepare( " 
  1562. SELECT ID FROM {$wpdb->posts} posts LEFT JOIN {$wpdb->postmeta} postmeta ON posts.ID = postmeta.post_id 
  1563. WHERE posts.post_status = 'publish' 
  1564. AND ( 
  1565. posts.post_parent = %s 
  1566. OR posts.ID = %s 
  1567. OR posts.post_title LIKE %s 
  1568. OR ( 
  1569. postmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s 
  1570. ", $term, $term, $term, $like_term ); 
  1571. } else { 
  1572. $query = $wpdb->prepare( " 
  1573. SELECT ID FROM {$wpdb->posts} posts LEFT JOIN {$wpdb->postmeta} postmeta ON posts.ID = postmeta.post_id 
  1574. WHERE posts.post_status = 'publish' 
  1575. AND ( 
  1576. posts.post_title LIKE %s 
  1577. or posts.post_content LIKE %s 
  1578. OR ( 
  1579. postmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s 
  1580. ", $like_term, $like_term, $like_term ); 
  1581.  
  1582. $query .= " AND posts.post_type IN ('" . implode( "', '", array_map( 'esc_sql', $post_types ) ) . "')"; 
  1583.  
  1584. if ( ! empty( $_GET['exclude'] ) ) { 
  1585. $query .= " AND posts.ID NOT IN (" . implode( ', ', array_map( 'intval', explode( ', ', $_GET['exclude'] ) ) ) . ")"; 
  1586.  
  1587. if ( ! empty( $_GET['include'] ) ) { 
  1588. $query .= " AND posts.ID IN (" . implode( ', ', array_map( 'intval', explode( ', ', $_GET['include'] ) ) ) . ")"; 
  1589.  
  1590. if ( ! empty( $_GET['limit'] ) ) { 
  1591. $query .= " LIMIT " . intval( $_GET['limit'] ); 
  1592.  
  1593. $posts = array_unique( $wpdb->get_col( $query ) ); 
  1594. $found_products = array(); 
  1595.  
  1596. if ( ! empty( $posts ) ) { 
  1597. foreach ( $posts as $post ) { 
  1598. $product = wc_get_product( $post ); 
  1599.  
  1600. if ( ! current_user_can( 'read_product', $post ) ) { 
  1601. continue; 
  1602.  
  1603. if ( ! $product || ( $product->is_type( 'variation' ) && empty( $product->parent ) ) ) { 
  1604. continue; 
  1605.  
  1606. $found_products[ $post ] = rawurldecode( $product->get_formatted_name() ); 
  1607.  
  1608. $found_products = apply_filters( 'woocommerce_json_search_found_products', $found_products ); 
  1609.  
  1610. wp_send_json( $found_products ); 
  1611.  
  1612. /** 
  1613. * Search for product variations and return json. 
  1614. * @see WC_AJAX::json_search_products() 
  1615. */ 
  1616. public static function json_search_products_and_variations() { 
  1617. self::json_search_products( '', array( 'product', 'product_variation' ) ); 
  1618.  
  1619. /** 
  1620. * Search for grouped products and return json. 
  1621. */ 
  1622. public static function json_search_grouped_products() { 
  1623. ob_start(); 
  1624.  
  1625. check_ajax_referer( 'search-products', 'security' ); 
  1626.  
  1627. $term = (string) wc_clean( stripslashes( $_GET['term'] ) ); 
  1628. $exclude = array(); 
  1629.  
  1630. if ( empty( $term ) ) { 
  1631. die(); 
  1632.  
  1633. if ( ! empty( $_GET['exclude'] ) ) { 
  1634. $exclude = array_map( 'intval', explode( ', ', $_GET['exclude'] ) ); 
  1635.  
  1636. $found_products = array(); 
  1637.  
  1638. if ( $grouped_term = get_term_by( 'slug', 'grouped', 'product_type' ) ) { 
  1639.  
  1640. $posts_in = array_unique( (array) get_objects_in_term( $grouped_term->term_id, 'product_type' ) ); 
  1641.  
  1642. if ( sizeof( $posts_in ) > 0 ) { 
  1643.  
  1644. $args = array( 
  1645. 'post_type' => 'product',  
  1646. 'post_status' => 'any',  
  1647. 'numberposts' => -1,  
  1648. 'orderby' => 'title',  
  1649. 'order' => 'asc',  
  1650. 'post_parent' => 0,  
  1651. 'suppress_filters' => 0,  
  1652. 'include' => $posts_in,  
  1653. 's' => $term,  
  1654. 'fields' => 'ids',  
  1655. 'exclude' => $exclude 
  1656. ); 
  1657.  
  1658. $posts = get_posts( $args ); 
  1659.  
  1660. if ( ! empty( $posts ) ) { 
  1661. foreach ( $posts as $post ) { 
  1662. $product = wc_get_product( $post ); 
  1663.  
  1664. if ( ! current_user_can( 'read_product', $post ) ) { 
  1665. continue; 
  1666.  
  1667. $found_products[ $post ] = rawurldecode( $product->get_formatted_name() ); 
  1668.  
  1669. $found_products = apply_filters( 'woocommerce_json_search_found_grouped_products', $found_products ); 
  1670.  
  1671. wp_send_json( $found_products ); 
  1672.  
  1673. /** 
  1674. * Search for downloadable product variations and return json. 
  1675. * @see WC_AJAX::json_search_products() 
  1676. */ 
  1677. public static function json_search_downloadable_products_and_variations() { 
  1678. ob_start(); 
  1679.  
  1680. check_ajax_referer( 'search-products', 'security' ); 
  1681.  
  1682. $term = (string) wc_clean( stripslashes( $_GET['term'] ) ); 
  1683. $exclude = array(); 
  1684.  
  1685. if ( ! empty( $_GET['exclude'] ) ) { 
  1686. $exclude = array_map( 'intval', explode( ', ', $_GET['exclude'] ) ); 
  1687.  
  1688. $args = array( 
  1689. 'post_type' => array( 'product', 'product_variation' ),  
  1690. 'posts_per_page' => -1,  
  1691. 'post_status' => 'publish',  
  1692. 'order' => 'ASC',  
  1693. 'orderby' => 'parent title',  
  1694. 'meta_query' => array( 
  1695. array( 
  1696. 'key' => '_downloadable',  
  1697. 'value' => 'yes' 
  1698. ),  
  1699. 's' => $term,  
  1700. 'exclude' => $exclude 
  1701. ); 
  1702.  
  1703. $posts = get_posts( $args ); 
  1704. $found_products = array(); 
  1705.  
  1706. if ( ! empty( $posts ) ) { 
  1707. foreach ( $posts as $post ) { 
  1708. $product = wc_get_product( $post->ID ); 
  1709.  
  1710. if ( ! current_user_can( 'read_product', $post->ID ) ) { 
  1711. continue; 
  1712.  
  1713. $found_products[ $post->ID ] = $product->get_formatted_name(); 
  1714.  
  1715. wp_send_json( $found_products ); 
  1716.  
  1717. /** 
  1718. * Search for customers and return json. 
  1719. */ 
  1720. public static function json_search_customers() { 
  1721. ob_start(); 
  1722.  
  1723. check_ajax_referer( 'search-customers', 'security' ); 
  1724.  
  1725. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1726. die(-1); 
  1727.  
  1728. $term = wc_clean( stripslashes( $_GET['term'] ) ); 
  1729. $exclude = array(); 
  1730.  
  1731. if ( empty( $term ) ) { 
  1732. die(); 
  1733.  
  1734. if ( ! empty( $_GET['exclude'] ) ) { 
  1735. $exclude = array_map( 'intval', explode( ', ', $_GET['exclude'] ) ); 
  1736.  
  1737. $found_customers = array(); 
  1738.  
  1739. add_action( 'pre_user_query', array( __CLASS__, 'json_search_customer_name' ) ); 
  1740.  
  1741. $customers_query = new WP_User_Query( apply_filters( 'woocommerce_json_search_customers_query', array( 
  1742. 'fields' => 'all',  
  1743. 'orderby' => 'display_name',  
  1744. 'search' => '*' . $term . '*',  
  1745. 'search_columns' => array( 'ID', 'user_login', 'user_email', 'user_nicename' ) 
  1746. ) ) ); 
  1747.  
  1748. remove_action( 'pre_user_query', array( __CLASS__, 'json_search_customer_name' ) ); 
  1749.  
  1750. $customers = $customers_query->get_results(); 
  1751.  
  1752. if ( ! empty( $customers ) ) { 
  1753. foreach ( $customers as $customer ) { 
  1754. if ( ! in_array( $customer->ID, $exclude ) ) { 
  1755. $found_customers[ $customer->ID ] = $customer->display_name . ' (#' . $customer->ID . ' – ' . sanitize_email( $customer->user_email ) . ')'; 
  1756.  
  1757. $found_customers = apply_filters( 'woocommerce_json_search_found_customers', $found_customers ); 
  1758.  
  1759. wp_send_json( $found_customers ); 
  1760.  
  1761. /** 
  1762. * When searching using the WP_User_Query, search names (user meta) too. 
  1763. * @param object $query 
  1764. * @return object 
  1765. */ 
  1766. public static function json_search_customer_name( $query ) { 
  1767. global $wpdb; 
  1768.  
  1769. $term = wc_clean( stripslashes( $_GET['term'] ) ); 
  1770. if ( method_exists( $wpdb, 'esc_like' ) ) { 
  1771. $term = $wpdb->esc_like( $term ); 
  1772. } else { 
  1773. $term = like_escape( $term ); 
  1774.  
  1775. $query->query_from .= " INNER JOIN {$wpdb->usermeta} AS user_name ON {$wpdb->users}.ID = user_name.user_id AND ( user_name.meta_key = 'first_name' OR user_name.meta_key = 'last_name' ) "; 
  1776. $query->query_where .= $wpdb->prepare( " OR user_name.meta_value LIKE %s ", '%' . $term . '%' ); 
  1777.  
  1778. /** 
  1779. * Ajax request handling for categories ordering. 
  1780. */ 
  1781. public static function term_ordering() { 
  1782.  
  1783. // check permissions again and make sure we have what we need 
  1784. if ( ! current_user_can( 'edit_products' ) || empty( $_POST['id'] ) ) { 
  1785. die(-1); 
  1786.  
  1787. $id = (int) $_POST['id']; 
  1788. $next_id = isset( $_POST['nextid'] ) && (int) $_POST['nextid'] ? (int) $_POST['nextid'] : null; 
  1789. $taxonomy = isset( $_POST['thetaxonomy'] ) ? esc_attr( $_POST['thetaxonomy'] ) : null; 
  1790. $term = get_term_by( 'id', $id, $taxonomy ); 
  1791.  
  1792. if ( ! $id || ! $term || ! $taxonomy ) { 
  1793. die(0); 
  1794.  
  1795. wc_reorder_terms( $term, $next_id, $taxonomy ); 
  1796.  
  1797. $children = get_terms( $taxonomy, "child_of=$id&menu_order=ASC&hide_empty=0" ); 
  1798.  
  1799. if ( $term && sizeof( $children ) ) { 
  1800. echo 'children'; 
  1801. die(); 
  1802.  
  1803. /** 
  1804. * Ajax request handling for product ordering. 
  1805. * Based on Simple Page Ordering by 10up (https://wordpress.org/extend/plugins/simple-page-ordering/). 
  1806. */ 
  1807. public static function product_ordering() { 
  1808. global $wpdb; 
  1809.  
  1810. ob_start(); 
  1811.  
  1812. // check permissions again and make sure we have what we need 
  1813. if ( ! current_user_can('edit_products') || empty( $_POST['id'] ) || ( ! isset( $_POST['previd'] ) && ! isset( $_POST['nextid'] ) ) ) { 
  1814. die(-1); 
  1815.  
  1816. // real post? 
  1817. if ( ! $post = get_post( $_POST['id'] ) ) { 
  1818. die(-1); 
  1819.  
  1820. $previd = isset( $_POST['previd'] ) ? $_POST['previd'] : false; 
  1821. $nextid = isset( $_POST['nextid'] ) ? $_POST['nextid'] : false; 
  1822. $new_pos = array(); // store new positions for ajax 
  1823.  
  1824. $siblings = $wpdb->get_results( $wpdb->prepare( " 
  1825. SELECT ID, menu_order FROM {$wpdb->posts} AS posts 
  1826. WHERE posts.post_type = 'product' 
  1827. AND posts.post_status IN ( 'publish', 'pending', 'draft', 'future', 'private' ) 
  1828. AND posts.ID NOT IN (%d) 
  1829. ORDER BY posts.menu_order ASC, posts.ID DESC 
  1830. ", $post->ID ) ); 
  1831.  
  1832. $menu_order = 0; 
  1833.  
  1834. foreach ( $siblings as $sibling ) { 
  1835.  
  1836. // if this is the post that comes after our repositioned post, set our repositioned post position and increment menu order 
  1837. if ( $nextid == $sibling->ID ) { 
  1838. $wpdb->update( 
  1839. $wpdb->posts,  
  1840. array( 
  1841. 'menu_order' => $menu_order 
  1842. ),  
  1843. array( 'ID' => $post->ID ),  
  1844. array( '%d' ),  
  1845. array( '%d' ) 
  1846. ); 
  1847. $new_pos[ $post->ID ] = $menu_order; 
  1848. $menu_order++; 
  1849.  
  1850. // if repositioned post has been set, and new items are already in the right order, we can stop 
  1851. if ( isset( $new_pos[ $post->ID ] ) && $sibling->menu_order >= $menu_order ) { 
  1852. break; 
  1853.  
  1854. // set the menu order of the current sibling and increment the menu order 
  1855. $wpdb->update( 
  1856. $wpdb->posts,  
  1857. array( 
  1858. 'menu_order' => $menu_order 
  1859. ),  
  1860. array( 'ID' => $sibling->ID ),  
  1861. array( '%d' ),  
  1862. array( '%d' ) 
  1863. ); 
  1864. $new_pos[ $sibling->ID ] = $menu_order; 
  1865. $menu_order++; 
  1866.  
  1867. if ( ! $nextid && $previd == $sibling->ID ) { 
  1868. $wpdb->update( 
  1869. $wpdb->posts,  
  1870. array( 
  1871. 'menu_order' => $menu_order 
  1872. ),  
  1873. array( 'ID' => $post->ID ),  
  1874. array( '%d' ),  
  1875. array( '%d' ) 
  1876. ); 
  1877. $new_pos[$post->ID] = $menu_order; 
  1878. $menu_order++; 
  1879.  
  1880.  
  1881. do_action( 'woocommerce_after_product_ordering' ); 
  1882.  
  1883. wp_send_json( $new_pos ); 
  1884.  
  1885. /** 
  1886. * Handle a refund via the edit order screen. 
  1887. */ 
  1888. public static function refund_line_items() { 
  1889. ob_start(); 
  1890.  
  1891. check_ajax_referer( 'order-item', 'security' ); 
  1892.  
  1893. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  1894. die(-1); 
  1895.  
  1896. $order_id = absint( $_POST['order_id'] ); 
  1897. $refund_amount = wc_format_decimal( sanitize_text_field( $_POST['refund_amount'] ), wc_get_price_decimals() ); 
  1898. $refund_reason = sanitize_text_field( $_POST['refund_reason'] ); 
  1899. $line_item_qtys = json_decode( sanitize_text_field( stripslashes( $_POST['line_item_qtys'] ) ), true ); 
  1900. $line_item_totals = json_decode( sanitize_text_field( stripslashes( $_POST['line_item_totals'] ) ), true ); 
  1901. $line_item_tax_totals = json_decode( sanitize_text_field( stripslashes( $_POST['line_item_tax_totals'] ) ), true ); 
  1902. $api_refund = $_POST['api_refund'] === 'true' ? true : false; 
  1903. $restock_refunded_items = $_POST['restock_refunded_items'] === 'true' ? true : false; 
  1904. $refund = false; 
  1905. $response_data = array(); 
  1906.  
  1907. try { 
  1908. // Validate that the refund can occur 
  1909. $order = wc_get_order( $order_id ); 
  1910. $order_items = $order->get_items(); 
  1911. $max_refund = wc_format_decimal( $order->get_total() - $order->get_total_refunded(), wc_get_price_decimals() ); 
  1912.  
  1913. if ( ! $refund_amount || $max_refund < $refund_amount || 0 > $refund_amount ) { 
  1914. throw new exception( __( 'Invalid refund amount', 'woocommerce' ) ); 
  1915.  
  1916. // Prepare line items which we are refunding 
  1917. $line_items = array(); 
  1918. $item_ids = array_unique( array_merge( array_keys( $line_item_qtys, $line_item_totals ) ) ); 
  1919.  
  1920. foreach ( $item_ids as $item_id ) { 
  1921. $line_items[ $item_id ] = array( 'qty' => 0, 'refund_total' => 0, 'refund_tax' => array() ); 
  1922. foreach ( $line_item_qtys as $item_id => $qty ) { 
  1923. $line_items[ $item_id ]['qty'] = max( $qty, 0 ); 
  1924. foreach ( $line_item_totals as $item_id => $total ) { 
  1925. $line_items[ $item_id ]['refund_total'] = wc_format_decimal( $total ); 
  1926. foreach ( $line_item_tax_totals as $item_id => $tax_totals ) { 
  1927. $line_items[ $item_id ]['refund_tax'] = array_map( 'wc_format_decimal', $tax_totals ); 
  1928.  
  1929. // Create the refund object 
  1930. $refund = wc_create_refund( array( 
  1931. 'amount' => $refund_amount,  
  1932. 'reason' => $refund_reason,  
  1933. 'order_id' => $order_id,  
  1934. 'line_items' => $line_items,  
  1935. ) ); 
  1936.  
  1937. if ( is_wp_error( $refund ) ) { 
  1938. throw new Exception( $refund->get_error_message() ); 
  1939.  
  1940. // Refund via API 
  1941. if ( $api_refund ) { 
  1942. if ( WC()->payment_gateways() ) { 
  1943. $payment_gateways = WC()->payment_gateways->payment_gateways(); 
  1944. if ( isset( $payment_gateways[ $order->payment_method ] ) && $payment_gateways[ $order->payment_method ]->supports( 'refunds' ) ) { 
  1945. $result = $payment_gateways[ $order->payment_method ]->process_refund( $order_id, $refund_amount, $refund_reason ); 
  1946.  
  1947. do_action( 'woocommerce_refund_processed', $refund, $result ); 
  1948.  
  1949. if ( is_wp_error( $result ) ) { 
  1950. throw new Exception( $result->get_error_message() ); 
  1951. } elseif ( ! $result ) { 
  1952. throw new Exception( __( 'Refund failed', 'woocommerce' ) ); 
  1953.  
  1954. // restock items 
  1955. foreach ( $line_item_qtys as $item_id => $qty ) { 
  1956. if ( $restock_refunded_items && $qty && isset( $order_items[ $item_id ] ) ) { 
  1957. $order_item = $order_items[ $item_id ]; 
  1958. $_product = $order->get_product_from_item( $order_item ); 
  1959.  
  1960. if ( $_product && $_product->exists() && $_product->managing_stock() ) { 
  1961. $old_stock = wc_stock_amount( $_product->stock ); 
  1962. $new_quantity = $_product->increase_stock( $qty ); 
  1963.  
  1964. $order->add_order_note( sprintf( __( 'Item #%s stock increased from %s to %s.', 'woocommerce' ), $order_item['product_id'], $old_stock, $new_quantity ) ); 
  1965.  
  1966. do_action( 'woocommerce_restock_refunded_item', $_product->id, $old_stock, $new_quantity, $order, $_product ); 
  1967.  
  1968. // Trigger notifications and status changes 
  1969. if ( $order->get_remaining_refund_amount() > 0 || ( $order->has_free_item() && $order->get_remaining_refund_items() > 0 ) ) { 
  1970. /** 
  1971. * woocommerce_order_partially_refunded. 
  1972. * @since 2.4.0 
  1973. * Note: 3rd arg was added in err. Kept for bw compat. 2.4.3. 
  1974. */ 
  1975. do_action( 'woocommerce_order_partially_refunded', $order_id, $refund->id, $refund->id ); 
  1976. } else { 
  1977. do_action( 'woocommerce_order_fully_refunded', $order_id, $refund->id ); 
  1978.  
  1979. $order->update_status( apply_filters( 'woocommerce_order_fully_refunded_status', 'refunded', $order_id, $refund->id ) ); 
  1980. $response_data['status'] = 'fully_refunded'; 
  1981.  
  1982. do_action( 'woocommerce_order_refunded', $order_id, $refund->id ); 
  1983.  
  1984. // Clear transients 
  1985. wc_delete_shop_order_transients( $order_id ); 
  1986. wp_send_json_success( $response_data ); 
  1987.  
  1988. } catch ( Exception $e ) { 
  1989. if ( $refund && is_a( $refund, 'WC_Order_Refund' ) ) { 
  1990. wp_delete_post( $refund->id, true ); 
  1991.  
  1992. wp_send_json_error( array( 'error' => $e->getMessage() ) ); 
  1993.  
  1994. /** 
  1995. * Delete a refund. 
  1996. */ 
  1997. public static function delete_refund() { 
  1998. check_ajax_referer( 'order-item', 'security' ); 
  1999.  
  2000. if ( ! current_user_can( 'edit_shop_orders' ) ) { 
  2001. die(-1); 
  2002.  
  2003. $refund_ids = array_map( 'absint', is_array( $_POST['refund_id'] ) ? $_POST['refund_id'] : array( $_POST['refund_id'] ) ); 
  2004. foreach ( $refund_ids as $refund_id ) { 
  2005. if ( $refund_id && 'shop_order_refund' === get_post_type( $refund_id ) ) { 
  2006. $order_id = wp_get_post_parent_id( $refund_id ); 
  2007. wc_delete_shop_order_transients( $order_id ); 
  2008. wp_delete_post( $refund_id ); 
  2009. do_action( 'woocommerce_refund_deleted', $refund_id, $order_id ); 
  2010. die(); 
  2011.  
  2012. /** 
  2013. * Triggered when clicking the rating footer. 
  2014. */ 
  2015. public static function rated() { 
  2016. if ( ! current_user_can( 'manage_woocommerce' ) ) { 
  2017. die(-1); 
  2018.  
  2019. update_option( 'woocommerce_admin_footer_text_rated', 1 ); 
  2020. die(); 
  2021.  
  2022. /** 
  2023. * Create/Update API key. 
  2024. */ 
  2025. public static function update_api_key() { 
  2026. ob_start(); 
  2027.  
  2028. global $wpdb; 
  2029.  
  2030. check_ajax_referer( 'update-api-key', 'security' ); 
  2031.  
  2032. if ( ! current_user_can( 'manage_woocommerce' ) ) { 
  2033. die(-1); 
  2034.  
  2035. try { 
  2036. if ( empty( $_POST['description'] ) ) { 
  2037. throw new Exception( __( 'Description is missing.', 'woocommerce' ) ); 
  2038. if ( empty( $_POST['user'] ) ) { 
  2039. throw new Exception( __( 'User is missing.', 'woocommerce' ) ); 
  2040. if ( empty( $_POST['permissions'] ) ) { 
  2041. throw new Exception( __( 'Permissions is missing.', 'woocommerce' ) ); 
  2042.  
  2043. $key_id = absint( $_POST['key_id'] ); 
  2044. $description = sanitize_text_field( wp_unslash( $_POST['description'] ) ); 
  2045. $permissions = ( in_array( $_POST['permissions'], array( 'read', 'write', 'read_write' ) ) ) ? sanitize_text_field( $_POST['permissions'] ) : 'read'; 
  2046. $user_id = absint( $_POST['user'] ); 
  2047.  
  2048. if ( 0 < $key_id ) { 
  2049. $data = array( 
  2050. 'user_id' => $user_id,  
  2051. 'description' => $description,  
  2052. 'permissions' => $permissions 
  2053. ); 
  2054.  
  2055. $wpdb->update( 
  2056. $wpdb->prefix . 'woocommerce_api_keys',  
  2057. $data,  
  2058. array( 'key_id' => $key_id ),  
  2059. array( 
  2060. '%d',  
  2061. '%s',  
  2062. '%s' 
  2063. ),  
  2064. array( '%d' ) 
  2065. ); 
  2066.  
  2067. $data['consumer_key'] = ''; 
  2068. $data['consumer_secret'] = ''; 
  2069. $data['message'] = __( 'API Key updated successfully.', 'woocommerce' ); 
  2070. } else { 
  2071. $consumer_key = 'ck_' . wc_rand_hash(); 
  2072. $consumer_secret = 'cs_' . wc_rand_hash(); 
  2073.  
  2074. $data = array( 
  2075. 'user_id' => $user_id,  
  2076. 'description' => $description,  
  2077. 'permissions' => $permissions,  
  2078. 'consumer_key' => wc_api_hash( $consumer_key ),  
  2079. 'consumer_secret' => $consumer_secret,  
  2080. 'truncated_key' => substr( $consumer_key, -7 ) 
  2081. ); 
  2082.  
  2083. $wpdb->insert( 
  2084. $wpdb->prefix . 'woocommerce_api_keys',  
  2085. $data,  
  2086. array( 
  2087. '%d',  
  2088. '%s',  
  2089. '%s',  
  2090. '%s',  
  2091. '%s',  
  2092. '%s' 
  2093. ); 
  2094.  
  2095. $key_id = $wpdb->insert_id; 
  2096. $data['consumer_key'] = $consumer_key; 
  2097. $data['consumer_secret'] = $consumer_secret; 
  2098. $data['message'] = __( 'API Key generated successfully. Make sure to copy your new API keys now. You won\'t be able to see it again!', 'woocommerce' ); 
  2099. $data['revoke_url'] = '<a style="color: #a00; text-decoration: none;" href="' . esc_url( wp_nonce_url( add_query_arg( array( 'revoke-key' => $key_id ), admin_url( 'admin.php?page=wc-settings&tab=api§ion=keys' ) ), 'revoke' ) ). '">' . __( 'Revoke Key', 'woocommerce' ) . '</a>'; 
  2100.  
  2101. wp_send_json_success( $data ); 
  2102. } catch ( Exception $e ) { 
  2103. wp_send_json_error( array( 'message' => $e->getMessage() ) ); 
  2104.  
  2105. /** 
  2106. * Locate user via AJAX. 
  2107. */ 
  2108. public static function get_customer_location() { 
  2109. $location_hash = WC_Cache_Helper::geolocation_ajax_get_location_hash(); 
  2110. wp_send_json_success( array( 'hash' => $location_hash ) ); 
  2111.  
  2112. /** 
  2113. * Load variations via AJAX. 
  2114. */ 
  2115. public static function load_variations() { 
  2116. ob_start(); 
  2117.  
  2118. check_ajax_referer( 'load-variations', 'security' ); 
  2119.  
  2120. // Check permissions again and make sure we have what we need 
  2121. if ( ! current_user_can( 'edit_products' ) || empty( $_POST['product_id'] ) || empty( $_POST['attributes'] ) ) { 
  2122. die( -1 ); 
  2123.  
  2124. global $post; 
  2125.  
  2126. $product_id = absint( $_POST['product_id'] ); 
  2127. $post = get_post( $product_id ); // Set $post global so its available like within the admin screens 
  2128. $per_page = ! empty( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : 10; 
  2129. $page = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1; 
  2130.  
  2131. // Get attributes 
  2132. $attributes = array(); 
  2133. $posted_attributes = wp_unslash( $_POST['attributes'] ); 
  2134.  
  2135. foreach ( $posted_attributes as $key => $value ) { 
  2136. $attributes[ $key ] = array_map( 'wc_clean', $value ); 
  2137.  
  2138. // Get tax classes 
  2139. $tax_classes = WC_Tax::get_tax_classes(); 
  2140. $tax_class_options = array(); 
  2141. $tax_class_options[''] = __( 'Standard', 'woocommerce' ); 
  2142.  
  2143. if ( ! empty( $tax_classes ) ) { 
  2144. foreach ( $tax_classes as $class ) { 
  2145. $tax_class_options[ sanitize_title( $class ) ] = esc_attr( $class ); 
  2146.  
  2147. // Set backorder options 
  2148. $backorder_options = array( 
  2149. 'no' => __( 'Do not allow', 'woocommerce' ),  
  2150. 'notify' => __( 'Allow, but notify customer', 'woocommerce' ),  
  2151. 'yes' => __( 'Allow', 'woocommerce' ) 
  2152. ); 
  2153.  
  2154. // set stock status options 
  2155. $stock_status_options = array( 
  2156. 'instock' => __( 'In stock', 'woocommerce' ),  
  2157. 'outofstock' => __( 'Out of stock', 'woocommerce' ) 
  2158. ); 
  2159.  
  2160. $parent_data = array( 
  2161. 'id' => $product_id,  
  2162. 'attributes' => $attributes,  
  2163. 'tax_class_options' => $tax_class_options,  
  2164. 'sku' => get_post_meta( $product_id, '_sku', true ),  
  2165. 'weight' => wc_format_localized_decimal( get_post_meta( $product_id, '_weight', true ) ),  
  2166. 'length' => wc_format_localized_decimal( get_post_meta( $product_id, '_length', true ) ),  
  2167. 'width' => wc_format_localized_decimal( get_post_meta( $product_id, '_width', true ) ),  
  2168. 'height' => wc_format_localized_decimal( get_post_meta( $product_id, '_height', true ) ),  
  2169. 'tax_class' => get_post_meta( $product_id, '_tax_class', true ),  
  2170. 'backorder_options' => $backorder_options,  
  2171. 'stock_status_options' => $stock_status_options 
  2172. ); 
  2173.  
  2174. if ( ! $parent_data['weight'] ) { 
  2175. $parent_data['weight'] = wc_format_localized_decimal( 0 ); 
  2176.  
  2177. if ( ! $parent_data['length'] ) { 
  2178. $parent_data['length'] = wc_format_localized_decimal( 0 ); 
  2179.  
  2180. if ( ! $parent_data['width'] ) { 
  2181. $parent_data['width'] = wc_format_localized_decimal( 0 ); 
  2182.  
  2183. if ( ! $parent_data['height'] ) { 
  2184. $parent_data['height'] = wc_format_localized_decimal( 0 ); 
  2185.  
  2186. // Get variations 
  2187. $args = apply_filters( 'woocommerce_ajax_admin_get_variations_args', array( 
  2188. 'post_type' => 'product_variation',  
  2189. 'post_status' => array( 'private', 'publish' ),  
  2190. 'posts_per_page' => $per_page,  
  2191. 'paged' => $page,  
  2192. 'orderby' => array( 'menu_order' => 'ASC', 'ID' => 'DESC' ),  
  2193. 'post_parent' => $product_id 
  2194. ), $product_id ); 
  2195.  
  2196. $variations = get_posts( $args ); 
  2197. $loop = 0; 
  2198.  
  2199. if ( $variations ) { 
  2200.  
  2201. foreach ( $variations as $variation ) { 
  2202. $variation_id = absint( $variation->ID ); 
  2203. $variation_meta = get_post_meta( $variation_id ); 
  2204. $variation_data = array(); 
  2205. $shipping_classes = get_the_terms( $variation_id, 'product_shipping_class' ); 
  2206. $variation_fields = array( 
  2207. '_sku' => '',  
  2208. '_stock' => '',  
  2209. '_regular_price' => '',  
  2210. '_sale_price' => '',  
  2211. '_weight' => '',  
  2212. '_length' => '',  
  2213. '_width' => '',  
  2214. '_height' => '',  
  2215. '_download_limit' => '',  
  2216. '_download_expiry' => '',  
  2217. '_downloadable_files' => '',  
  2218. '_downloadable' => '',  
  2219. '_virtual' => '',  
  2220. '_thumbnail_id' => '',  
  2221. '_sale_price_dates_from' => '',  
  2222. '_sale_price_dates_to' => '',  
  2223. '_manage_stock' => '',  
  2224. '_stock_status' => '',  
  2225. '_backorders' => null,  
  2226. '_tax_class' => null,  
  2227. '_variation_description' => '' 
  2228. ); 
  2229.  
  2230. foreach ( $variation_fields as $field => $value ) { 
  2231. $variation_data[ $field ] = isset( $variation_meta[ $field ][0] ) ? maybe_unserialize( $variation_meta[ $field ][0] ) : $value; 
  2232.  
  2233. // Add the variation attributes 
  2234. $variation_data = array_merge( $variation_data, wc_get_product_variation_attributes( $variation_id ) ); 
  2235.  
  2236. // Formatting 
  2237. $variation_data['_regular_price'] = wc_format_localized_price( $variation_data['_regular_price'] ); 
  2238. $variation_data['_sale_price'] = wc_format_localized_price( $variation_data['_sale_price'] ); 
  2239. $variation_data['_weight'] = wc_format_localized_decimal( $variation_data['_weight'] ); 
  2240. $variation_data['_length'] = wc_format_localized_decimal( $variation_data['_length'] ); 
  2241. $variation_data['_width'] = wc_format_localized_decimal( $variation_data['_width'] ); 
  2242. $variation_data['_height'] = wc_format_localized_decimal( $variation_data['_height'] ); 
  2243. $variation_data['_thumbnail_id'] = absint( $variation_data['_thumbnail_id'] ); 
  2244. $variation_data['image'] = $variation_data['_thumbnail_id'] ? wp_get_attachment_thumb_url( $variation_data['_thumbnail_id'] ) : ''; 
  2245. $variation_data['shipping_class'] = $shipping_classes && ! is_wp_error( $shipping_classes ) ? current( $shipping_classes )->term_id : ''; 
  2246. $variation_data['menu_order'] = $variation->menu_order; 
  2247. $variation_data['_stock'] = '' === $variation_data['_stock'] ? '' : wc_stock_amount( $variation_data['_stock'] ); 
  2248.  
  2249. include( 'admin/meta-boxes/views/html-variation-admin.php' ); 
  2250.  
  2251. $loop++; 
  2252.  
  2253. die(); 
  2254.  
  2255. /** 
  2256. * Save variations via AJAX. 
  2257. */ 
  2258. public static function save_variations() { 
  2259. ob_start(); 
  2260.  
  2261. check_ajax_referer( 'save-variations', 'security' ); 
  2262.  
  2263. // Check permissions again and make sure we have what we need 
  2264. if ( ! current_user_can( 'edit_products' ) || empty( $_POST ) || empty( $_POST['product_id'] ) ) { 
  2265. die( -1 ); 
  2266.  
  2267. // Remove previous meta box errors 
  2268. WC_Admin_Meta_Boxes::$meta_box_errors = array(); 
  2269.  
  2270. $product_id = absint( $_POST['product_id'] ); 
  2271. $product_type = empty( $_POST['product-type'] ) ? 'simple' : sanitize_title( stripslashes( $_POST['product-type'] ) ); 
  2272.  
  2273. $product_type_terms = wp_get_object_terms( $product_id, 'product_type' ); 
  2274.  
  2275. // If the product type hasn't been set or it has changed, update it before saving variations 
  2276. if ( empty( $product_type_terms ) || $product_type !== sanitize_title( current( $product_type_terms )->name ) ) { 
  2277. wp_set_object_terms( $product_id, $product_type, 'product_type' ); 
  2278.  
  2279. WC_Meta_Box_Product_Data::save_variations( $product_id, get_post( $product_id ) ); 
  2280.  
  2281. do_action( 'woocommerce_ajax_save_product_variations', $product_id ); 
  2282.  
  2283. // Clear cache/transients 
  2284. wc_delete_product_transients( $product_id ); 
  2285.  
  2286. if ( $errors = WC_Admin_Meta_Boxes::$meta_box_errors ) { 
  2287. echo '<div class="error notice is-dismissible">'; 
  2288.  
  2289. foreach ( $errors as $error ) { 
  2290. echo '<p>' . wp_kses_post( $error ) . '</p>'; 
  2291.  
  2292. echo '<button type="button" class="notice-dismiss"><span class="screen-reader-text">' . __( 'Dismiss this notice.', 'woocommerce' ) . '</span></button>'; 
  2293. echo '</div>'; 
  2294.  
  2295. delete_option( 'woocommerce_meta_box_errors' ); 
  2296.  
  2297. die(); 
  2298.  
  2299. /** 
  2300. * Bulk action - Toggle Enabled. 
  2301. * @access private 
  2302. * @used-by bulk_edit_variations 
  2303. * @param array $variations 
  2304. * @param array $data 
  2305. */ 
  2306. private static function variation_bulk_action_toggle_enabled( $variations, $data ) { 
  2307. global $wpdb; 
  2308.  
  2309. foreach ( $variations as $variation_id ) { 
  2310. $post_status = get_post_status( $variation_id ); 
  2311. $new_status = 'private' === $post_status ? 'publish' : 'private'; 
  2312. $wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $variation_id ) ); 
  2313.  
  2314. /** 
  2315. * Bulk action - Toggle Downloadable Checkbox. 
  2316. * @access private 
  2317. * @used-by bulk_edit_variations 
  2318. * @param array $variations 
  2319. * @param array $data 
  2320. */ 
  2321. private static function variation_bulk_action_toggle_downloadable( $variations, $data ) { 
  2322. foreach ( $variations as $variation_id ) { 
  2323. $_downloadable = get_post_meta( $variation_id, '_downloadable', true ); 
  2324. $is_downloadable = 'no' === $_downloadable ? 'yes' : 'no'; 
  2325. update_post_meta( $variation_id, '_downloadable', wc_clean( $is_downloadable ) ); 
  2326.  
  2327. /** 
  2328. * Bulk action - Toggle Virtual Checkbox. 
  2329. * @access private 
  2330. * @used-by bulk_edit_variations 
  2331. * @param array $variations 
  2332. * @param array $data 
  2333. */ 
  2334. private static function variation_bulk_action_toggle_virtual( $variations, $data ) { 
  2335. foreach ( $variations as $variation_id ) { 
  2336. $_virtual = get_post_meta( $variation_id, '_virtual', true ); 
  2337. $is_virtual = 'no' === $_virtual ? 'yes' : 'no'; 
  2338. update_post_meta( $variation_id, '_virtual', wc_clean( $is_virtual ) ); 
  2339.  
  2340. /** 
  2341. * Bulk action - Toggle Manage Stock Checkbox. 
  2342. * @access private 
  2343. * @used-by bulk_edit_variations 
  2344. * @param array $variations 
  2345. * @param array $data 
  2346. */ 
  2347. private static function variation_bulk_action_toggle_manage_stock( $variations, $data ) { 
  2348. foreach ( $variations as $variation_id ) { 
  2349. $_manage_stock = get_post_meta( $variation_id, '_manage_stock', true ); 
  2350. $is_manage_stock = 'no' === $_manage_stock || '' === $_manage_stock ? 'yes' : 'no'; 
  2351. update_post_meta( $variation_id, '_manage_stock', $is_manage_stock ); 
  2352.  
  2353. /** 
  2354. * Bulk action - Set Regular Prices. 
  2355. * @access private 
  2356. * @used-by bulk_edit_variations 
  2357. * @param array $variations 
  2358. * @param array $data 
  2359. */ 
  2360. private static function variation_bulk_action_variable_regular_price( $variations, $data ) { 
  2361. if ( ! isset( $data['value'] ) ) { 
  2362. return; 
  2363.  
  2364. foreach ( $variations as $variation_id ) { 
  2365. // Price fields 
  2366. $regular_price = wc_clean( $data['value'] ); 
  2367. $sale_price = get_post_meta( $variation_id, '_sale_price', true ); 
  2368.  
  2369. // Date fields 
  2370. $date_from = get_post_meta( $variation_id, '_sale_price_dates_from', true ); 
  2371. $date_to = get_post_meta( $variation_id, '_sale_price_dates_to', true ); 
  2372. $date_from = ! empty( $date_from ) ? date( 'Y-m-d', $date_from ) : ''; 
  2373. $date_to = ! empty( $date_to ) ? date( 'Y-m-d', $date_to ) : ''; 
  2374.  
  2375. _wc_save_product_price( $variation_id, $regular_price, $sale_price, $date_from, $date_to ); 
  2376.  
  2377. /** 
  2378. * Bulk action - Set Sale Prices. 
  2379. * @access private 
  2380. * @used-by bulk_edit_variations 
  2381. * @param array $variations 
  2382. * @param array $data 
  2383. */ 
  2384. private static function variation_bulk_action_variable_sale_price( $variations, $data ) { 
  2385. if ( ! isset( $data['value'] ) ) { 
  2386. return; 
  2387.  
  2388. foreach ( $variations as $variation_id ) { 
  2389. // Price fields 
  2390. $regular_price = get_post_meta( $variation_id, '_regular_price', true ); 
  2391. $sale_price = wc_clean( $data['value'] ); 
  2392.  
  2393. // Date fields 
  2394. $date_from = get_post_meta( $variation_id, '_sale_price_dates_from', true ); 
  2395. $date_to = get_post_meta( $variation_id, '_sale_price_dates_to', true ); 
  2396. $date_from = ! empty( $date_from ) ? date( 'Y-m-d', $date_from ) : ''; 
  2397. $date_to = ! empty( $date_to ) ? date( 'Y-m-d', $date_to ) : ''; 
  2398.  
  2399. _wc_save_product_price( $variation_id, $regular_price, $sale_price, $date_from, $date_to ); 
  2400.  
  2401. /** 
  2402. * Bulk action - Set Stock. 
  2403. * @access private 
  2404. * @used-by bulk_edit_variations 
  2405. * @param array $variations 
  2406. * @param array $data 
  2407. */ 
  2408. private static function variation_bulk_action_variable_stock( $variations, $data ) { 
  2409. if ( ! isset( $data['value'] ) ) { 
  2410. return; 
  2411.  
  2412. $value = wc_clean( $data['value'] ); 
  2413.  
  2414. foreach ( $variations as $variation_id ) { 
  2415. if ( 'yes' === get_post_meta( $variation_id, '_manage_stock', true ) ) { 
  2416. wc_update_product_stock( $variation_id, wc_stock_amount( $value ) ); 
  2417. } else { 
  2418. delete_post_meta( $variation_id, '_stock' ); 
  2419.  
  2420. /** 
  2421. * Bulk action - Set Weight. 
  2422. * @access private 
  2423. * @used-by bulk_edit_variations 
  2424. * @param array $variations 
  2425. * @param array $data 
  2426. */ 
  2427. private static function variation_bulk_action_variable_weight( $variations, $data ) { 
  2428. self::variation_bulk_set_meta( $variations, '_weight', wc_clean( $data['value'] ) ); 
  2429.  
  2430. /** 
  2431. * Bulk action - Set Length. 
  2432. * @access private 
  2433. * @used-by bulk_edit_variations 
  2434. * @param array $variations 
  2435. * @param array $data 
  2436. */ 
  2437. private static function variation_bulk_action_variable_length( $variations, $data ) { 
  2438. self::variation_bulk_set_meta( $variations, '_length', wc_clean( $data['value'] ) ); 
  2439.  
  2440. /** 
  2441. * Bulk action - Set Width. 
  2442. * @access private 
  2443. * @used-by bulk_edit_variations 
  2444. * @param array $variations 
  2445. * @param array $data 
  2446. */ 
  2447. private static function variation_bulk_action_variable_width( $variations, $data ) { 
  2448. self::variation_bulk_set_meta( $variations, '_width', wc_clean( $data['value'] ) ); 
  2449.  
  2450. /** 
  2451. * Bulk action - Set Height. 
  2452. * @access private 
  2453. * @used-by bulk_edit_variations 
  2454. * @param array $variations 
  2455. * @param array $data 
  2456. */ 
  2457. private static function variation_bulk_action_variable_height( $variations, $data ) { 
  2458. self::variation_bulk_set_meta( $variations, '_height', wc_clean( $data['value'] ) ); 
  2459.  
  2460. /** 
  2461. * Bulk action - Set Download Limit. 
  2462. * @access private 
  2463. * @used-by bulk_edit_variations 
  2464. * @param array $variations 
  2465. * @param array $data 
  2466. */ 
  2467. private static function variation_bulk_action_variable_download_limit( $variations, $data ) { 
  2468. self::variation_bulk_set_meta( $variations, '_download_limit', wc_clean( $data['value'] ) ); 
  2469.  
  2470. /** 
  2471. * Bulk action - Set Download Expiry. 
  2472. * @access private 
  2473. * @used-by bulk_edit_variations 
  2474. * @param array $variations 
  2475. * @param array $data 
  2476. */ 
  2477. private static function variation_bulk_action_variable_download_expiry( $variations, $data ) { 
  2478. self::variation_bulk_set_meta( $variations, '_download_expiry', wc_clean( $data['value'] ) ); 
  2479.  
  2480. /** 
  2481. * Bulk action - Delete all. 
  2482. * @access private 
  2483. * @used-by bulk_edit_variations 
  2484. * @param array $variations 
  2485. * @param array $data 
  2486. */ 
  2487. private static function variation_bulk_action_delete_all( $variations, $data ) { 
  2488. if ( isset( $data['allowed'] ) && 'true' === $data['allowed'] ) { 
  2489. foreach ( $variations as $variation_id ) { 
  2490. wp_delete_post( $variation_id ); 
  2491.  
  2492. /** 
  2493. * Bulk action - Sale Schedule. 
  2494. * @access private 
  2495. * @used-by bulk_edit_variations 
  2496. * @param array $variations 
  2497. * @param array $data 
  2498. */ 
  2499. private static function variation_bulk_action_variable_sale_schedule( $variations, $data ) { 
  2500. if ( ! isset( $data['date_from'] ) && ! isset( $data['date_to'] ) ) { 
  2501. return; 
  2502.  
  2503. foreach ( $variations as $variation_id ) { 
  2504. // Price fields 
  2505. $regular_price = get_post_meta( $variation_id, '_regular_price', true ); 
  2506. $sale_price = get_post_meta( $variation_id, '_sale_price', true ); 
  2507.  
  2508. // Date fields 
  2509. $date_from = get_post_meta( $variation_id, '_sale_price_dates_from', true ); 
  2510. $date_to = get_post_meta( $variation_id, '_sale_price_dates_to', true ); 
  2511.  
  2512. if ( 'false' === $data['date_from'] ) { 
  2513. $date_from = ! empty( $date_from ) ? date( 'Y-m-d', $date_from ) : ''; 
  2514. } else { 
  2515. $date_from = $data['date_from']; 
  2516.  
  2517. if ( 'false' === $data['date_to'] ) { 
  2518. $date_to = ! empty( $date_to ) ? date( 'Y-m-d', $date_to ) : ''; 
  2519. } else { 
  2520. $date_to = $data['date_to']; 
  2521.  
  2522. _wc_save_product_price( $variation_id, $regular_price, $sale_price, $date_from, $date_to ); 
  2523.  
  2524. /** 
  2525. * Bulk action - Increase Regular Prices. 
  2526. * @access private 
  2527. * @used-by bulk_edit_variations 
  2528. * @param array $variations 
  2529. * @param array $data 
  2530. */ 
  2531. private static function variation_bulk_action_variable_regular_price_increase( $variations, $data ) { 
  2532. self::variation_bulk_adjust_price( $variations, '_regular_price', '+', wc_clean( $data['value'] ) ); 
  2533.  
  2534. /** 
  2535. * Bulk action - Decrease Regular Prices. 
  2536. * @access private 
  2537. * @used-by bulk_edit_variations 
  2538. * @param array $variations 
  2539. * @param array $data 
  2540. */ 
  2541. private static function variation_bulk_action_variable_regular_price_decrease( $variations, $data ) { 
  2542. self::variation_bulk_adjust_price( $variations, '_regular_price', '-', wc_clean( $data['value'] ) ); 
  2543.  
  2544. /** 
  2545. * Bulk action - Increase Sale Prices. 
  2546. * @access private 
  2547. * @used-by bulk_edit_variations 
  2548. * @param array $variations 
  2549. * @param array $data 
  2550. */ 
  2551. private static function variation_bulk_action_variable_sale_price_increase( $variations, $data ) { 
  2552. self::variation_bulk_adjust_price( $variations, '_sale_price', '+', wc_clean( $data['value'] ) ); 
  2553.  
  2554. /** 
  2555. * Bulk action - Decrease Sale Prices. 
  2556. * @access private 
  2557. * @used-by bulk_edit_variations 
  2558. * @param array $variations 
  2559. * @param array $data 
  2560. */ 
  2561. private static function variation_bulk_action_variable_sale_price_decrease( $variations, $data ) { 
  2562. self::variation_bulk_adjust_price( $variations, '_sale_price', '-', wc_clean( $data['value'] ) ); 
  2563.  
  2564. /** 
  2565. * Bulk action - Set Price. 
  2566. * @access private 
  2567. * @used-by bulk_edit_variations 
  2568. * @param array $variations 
  2569. * @param string $operator + or - 
  2570. * @param string $field price being adjusted 
  2571. * @param string $value Price or Percent 
  2572. */ 
  2573. private static function variation_bulk_adjust_price( $variations, $field, $operator, $value ) { 
  2574. foreach ( $variations as $variation_id ) { 
  2575. // Get existing data 
  2576. $_regular_price = get_post_meta( $variation_id, '_regular_price', true ); 
  2577. $_sale_price = get_post_meta( $variation_id, '_sale_price', true ); 
  2578. $date_from = get_post_meta( $variation_id, '_sale_price_dates_from', true ); 
  2579. $date_to = get_post_meta( $variation_id, '_sale_price_dates_to', true ); 
  2580. $date_from = ! empty( $date_from ) ? date( 'Y-m-d', $date_from ) : ''; 
  2581. $date_to = ! empty( $date_to ) ? date( 'Y-m-d', $date_to ) : ''; 
  2582.  
  2583. if ( '%' === substr( $value, -1 ) ) { 
  2584. $percent = wc_format_decimal( substr( $value, 0, -1 ) ); 
  2585. $$field += ( ( $$field / 100 ) * $percent ) * "{$operator}1"; 
  2586. } else { 
  2587. $$field += $value * "{$operator}1"; 
  2588. _wc_save_product_price( $variation_id, $_regular_price, $_sale_price, $date_from, $date_to ); 
  2589.  
  2590. /** 
  2591. * Bulk action - Set Meta. 
  2592. * @access private 
  2593. * @param array $variations 
  2594. * @param string $field 
  2595. * @param string $value 
  2596. */ 
  2597. private static function variation_bulk_set_meta( $variations, $field, $value ) { 
  2598. foreach ( $variations as $variation_id ) { 
  2599. update_post_meta( $variation_id, $field, $value ); 
  2600.  
  2601. /** 
  2602. * Bulk edit variations via AJAX. 
  2603. * @uses WC_AJAX::variation_bulk_set_meta() 
  2604. * @uses WC_AJAX::variation_bulk_adjust_price() 
  2605. * @uses WC_AJAX::variation_bulk_action_variable_sale_price_decrease() 
  2606. * @uses WC_AJAX::variation_bulk_action_variable_sale_price_increase() 
  2607. * @uses WC_AJAX::variation_bulk_action_variable_regular_price_decrease() 
  2608. * @uses WC_AJAX::variation_bulk_action_variable_regular_price_increase() 
  2609. * @uses WC_AJAX::variation_bulk_action_variable_sale_schedule() 
  2610. * @uses WC_AJAX::variation_bulk_action_delete_all() 
  2611. * @uses WC_AJAX::variation_bulk_action_variable_download_expiry() 
  2612. * @uses WC_AJAX::variation_bulk_action_variable_download_limit() 
  2613. * @uses WC_AJAX::variation_bulk_action_variable_height() 
  2614. * @uses WC_AJAX::variation_bulk_action_variable_width() 
  2615. * @uses WC_AJAX::variation_bulk_action_variable_length() 
  2616. * @uses WC_AJAX::variation_bulk_action_variable_weight() 
  2617. * @uses WC_AJAX::variation_bulk_action_variable_stock() 
  2618. * @uses WC_AJAX::variation_bulk_action_variable_sale_price() 
  2619. * @uses WC_AJAX::variation_bulk_action_variable_regular_price() 
  2620. * @uses WC_AJAX::variation_bulk_action_toggle_manage_stock() 
  2621. * @uses WC_AJAX::variation_bulk_action_toggle_virtual() 
  2622. * @uses WC_AJAX::variation_bulk_action_toggle_downloadable() 
  2623. * @uses WC_AJAX::variation_bulk_action_toggle_enabled 
  2624. */ 
  2625. public static function bulk_edit_variations() { 
  2626. ob_start(); 
  2627.  
  2628. check_ajax_referer( 'bulk-edit-variations', 'security' ); 
  2629.  
  2630. // Check permissions again and make sure we have what we need 
  2631. if ( ! current_user_can( 'edit_products' ) || empty( $_POST['product_id'] ) || empty( $_POST['bulk_action'] ) ) { 
  2632. die( -1 ); 
  2633.  
  2634. $product_id = absint( $_POST['product_id'] ); 
  2635. $bulk_action = wc_clean( $_POST['bulk_action'] ); 
  2636. $data = ! empty( $_POST['data'] ) ? array_map( 'wc_clean', $_POST['data'] ) : array(); 
  2637. $variations = array(); 
  2638.  
  2639. if ( apply_filters( 'woocommerce_bulk_edit_variations_need_children', true ) ) { 
  2640. $variations = get_posts( array( 
  2641. 'post_parent' => $product_id,  
  2642. 'posts_per_page' => -1,  
  2643. 'post_type' => 'product_variation',  
  2644. 'fields' => 'ids',  
  2645. 'post_status' => array( 'publish', 'private' ) 
  2646. ) ); 
  2647.  
  2648. if ( method_exists( __CLASS__, "variation_bulk_action_$bulk_action" ) ) { 
  2649. call_user_func( array( __CLASS__, "variation_bulk_action_$bulk_action" ), $variations, $data ); 
  2650. } else { 
  2651. do_action( 'woocommerce_bulk_edit_variations_default', $bulk_action, $data, $product_id, $variations ); 
  2652.  
  2653. do_action( 'woocommerce_bulk_edit_variations', $bulk_action, $data, $product_id, $variations ); 
  2654.  
  2655. // Sync and update transients 
  2656. WC_Product_Variable::sync( $product_id ); 
  2657. wc_delete_product_transients( $product_id ); 
  2658. die(); 
  2659.  
  2660. /** 
  2661. * Handle submissions from assets/js/settings-views-html-settings-tax.js Backbone model. 
  2662. */ 
  2663. public static function tax_rates_save_changes() { 
  2664. if ( ! isset( $_POST['wc_tax_nonce'], $_POST['changes'] ) ) { 
  2665. wp_send_json_error( 'missing_fields' ); 
  2666. exit; 
  2667.  
  2668. $current_class = ! empty( $_POST['current_class'] ) ? $_POST['current_class'] : ''; // This is sanitized seven lines later. 
  2669.  
  2670. if ( ! wp_verify_nonce( $_POST['wc_tax_nonce'], 'wc_tax_nonce-class:' . $current_class ) ) { 
  2671. wp_send_json_error( 'bad_nonce' ); 
  2672. exit; 
  2673.  
  2674. $current_class = WC_Tax::format_tax_rate_class( $current_class ); 
  2675.  
  2676. // Check User Caps 
  2677. if ( ! current_user_can( 'manage_woocommerce' ) ) { 
  2678. wp_send_json_error( 'missing_capabilities' ); 
  2679. exit; 
  2680.  
  2681. $changes = $_POST['changes']; 
  2682. foreach ( $changes as $tax_rate_id => $data ) { 
  2683. if ( isset( $data['deleted'] ) ) { 
  2684. if ( isset( $data['newRow'] ) ) { 
  2685. // So the user added and deleted a new row. 
  2686. // That's fine, it's not in the database anyways. NEXT! 
  2687. continue; 
  2688. WC_Tax::_delete_tax_rate( $tax_rate_id ); 
  2689.  
  2690. $tax_rate = array_intersect_key( $data, array( 
  2691. 'tax_rate_country' => 1,  
  2692. 'tax_rate_state' => 1,  
  2693. 'tax_rate' => 1,  
  2694. 'tax_rate_name' => 1,  
  2695. 'tax_rate_priority' => 1,  
  2696. 'tax_rate_compound' => 1,  
  2697. 'tax_rate_shipping' => 1,  
  2698. 'tax_rate_order' => 1,  
  2699. ) ); 
  2700.  
  2701. if ( isset( $data['newRow'] ) ) { 
  2702. // Hurrah, shiny and new! 
  2703. $tax_rate['tax_rate_class'] = $current_class; 
  2704. $tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate ); 
  2705. } else { 
  2706. // Updating an existing rate ... 
  2707. if ( ! empty( $tax_rate ) ) { 
  2708. WC_Tax::_update_tax_rate( $tax_rate_id, $tax_rate ); 
  2709.  
  2710. if ( isset( $data['postcode'] ) ) { 
  2711. WC_Tax::_update_tax_rate_postcodes( $tax_rate_id, array_map( 'wc_clean', $data['postcode'] ) ); 
  2712. if ( isset( $data['city'] ) ) { 
  2713. WC_Tax::_update_tax_rate_cities( $tax_rate_id, array_map( 'wc_clean', $data['city'] ) ); 
  2714.  
  2715. wp_send_json_success( array( 
  2716. 'rates' => WC_Tax::get_rates_for_tax_class( $current_class ),  
  2717. ) ); 
  2718.  
  2719. /** 
  2720. * Handle submissions from assets/js/wc-shipping-zones.js Backbone model. 
  2721. */ 
  2722. public static function shipping_zones_save_changes() { 
  2723. if ( ! isset( $_POST['wc_shipping_zones_nonce'], $_POST['changes'] ) ) { 
  2724. wp_send_json_error( 'missing_fields' ); 
  2725. exit; 
  2726.  
  2727. if ( ! wp_verify_nonce( $_POST['wc_shipping_zones_nonce'], 'wc_shipping_zones_nonce' ) ) { 
  2728. wp_send_json_error( 'bad_nonce' ); 
  2729. exit; 
  2730.  
  2731. // Check User Caps 
  2732. if ( ! current_user_can( 'manage_woocommerce' ) ) { 
  2733. wp_send_json_error( 'missing_capabilities' ); 
  2734. exit; 
  2735.  
  2736. $changes = $_POST['changes']; 
  2737. foreach ( $changes as $zone_id => $data ) { 
  2738. if ( isset( $data['deleted'] ) ) { 
  2739. if ( isset( $data['newRow'] ) ) { 
  2740. // So the user added and deleted a new row. 
  2741. // That's fine, it's not in the database anyways. NEXT! 
  2742. continue; 
  2743. WC_Shipping_Zones::delete_zone( $zone_id ); 
  2744. continue; 
  2745.  
  2746. $zone_data = array_intersect_key( $data, array( 
  2747. 'zone_id' => 1,  
  2748. 'zone_name' => 1,  
  2749. 'zone_order' => 1,  
  2750. 'zone_locations' => 1,  
  2751. 'zone_postcodes' => 1 
  2752. ) ); 
  2753.  
  2754. if ( isset( $zone_data['zone_id'] ) ) { 
  2755. $zone = new WC_Shipping_Zone( $zone_data['zone_id'] ); 
  2756.  
  2757. if ( isset( $zone_data['zone_name'] ) ) { 
  2758. $zone->set_zone_name( $zone_data['zone_name'] ); 
  2759.  
  2760. if ( isset( $zone_data['zone_order'] ) ) { 
  2761. $zone->set_zone_order( $zone_data['zone_order'] ); 
  2762.  
  2763. if ( isset( $zone_data['zone_locations'] ) ) { 
  2764. $zone->clear_locations( array( 'state', 'country', 'continent' ) ); 
  2765. $locations = array_filter( array_map( 'wc_clean', (array) $zone_data['zone_locations'] ) ); 
  2766. foreach ( $locations as $location ) { 
  2767. // Each posted location will be in the format type:code 
  2768. $location_parts = explode( ':', $location ); 
  2769. switch ( $location_parts[0] ) { 
  2770. case 'state' : 
  2771. $zone->add_location( $location_parts[1] . ':' . $location_parts[2], 'state' ); 
  2772. break; 
  2773. case 'country' : 
  2774. $zone->add_location( $location_parts[1], 'country' ); 
  2775. break; 
  2776. case 'continent' : 
  2777. $zone->add_location( $location_parts[1], 'continent' ); 
  2778. break; 
  2779.  
  2780. if ( isset( $zone_data['zone_postcodes'] ) ) { 
  2781. $zone->clear_locations( 'postcode' ); 
  2782. $postcodes = array_filter( array_map( 'strtoupper', array_map( 'wc_clean', explode( "\n", $zone_data['zone_postcodes'] ) ) ) ); 
  2783. foreach ( $postcodes as $postcode ) { 
  2784. $zone->add_location( $postcode, 'postcode' ); 
  2785.  
  2786. $zone->save(); 
  2787.  
  2788. wp_send_json_success( array( 
  2789. 'zones' => WC_Shipping_Zones::get_zones() 
  2790. ) ); 
  2791.  
  2792. /** 
  2793. * Handle submissions from assets/js/wc-shipping-zone-methods.js Backbone model. 
  2794. */ 
  2795. public static function shipping_zone_add_method() { 
  2796. if ( ! isset( $_POST['wc_shipping_zones_nonce'], $_POST['zone_id'], $_POST['method_id'] ) ) { 
  2797. wp_send_json_error( 'missing_fields' ); 
  2798. exit; 
  2799.  
  2800. if ( ! wp_verify_nonce( $_POST['wc_shipping_zones_nonce'], 'wc_shipping_zones_nonce' ) ) { 
  2801. wp_send_json_error( 'bad_nonce' ); 
  2802. exit; 
  2803.  
  2804. // Check User Caps 
  2805. if ( ! current_user_can( 'manage_woocommerce' ) ) { 
  2806. wp_send_json_error( 'missing_capabilities' ); 
  2807. exit; 
  2808.  
  2809. $zone_id = absint( $_POST['zone_id'] ); 
  2810. $zone = WC_Shipping_Zones::get_zone( $zone_id ); 
  2811. $instance_id = $zone->add_shipping_method( wc_clean( $_POST['method_id'] ) ); 
  2812.  
  2813. wp_send_json_success( array( 
  2814. 'instance_id' => $instance_id,  
  2815. 'zone_id' => $zone_id,  
  2816. 'methods' => $zone->get_shipping_methods() 
  2817. ) ); 
  2818.  
  2819. /** 
  2820. * Handle submissions from assets/js/wc-shipping-zone-methods.js Backbone model. 
  2821. */ 
  2822. public static function shipping_zone_methods_save_changes() { 
  2823. if ( ! isset( $_POST['wc_shipping_zones_nonce'], $_POST['zone_id'], $_POST['changes'] ) ) { 
  2824. wp_send_json_error( 'missing_fields' ); 
  2825. exit; 
  2826.  
  2827. if ( ! wp_verify_nonce( $_POST['wc_shipping_zones_nonce'], 'wc_shipping_zones_nonce' ) ) { 
  2828. wp_send_json_error( 'bad_nonce' ); 
  2829. exit; 
  2830.  
  2831. if ( ! current_user_can( 'manage_woocommerce' ) ) { 
  2832. wp_send_json_error( 'missing_capabilities' ); 
  2833. exit; 
  2834.  
  2835. global $wpdb; 
  2836.  
  2837. $zone_id = absint( $_POST['zone_id'] ); 
  2838. $zone = new WC_Shipping_Zone( $zone_id ); 
  2839. $changes = $_POST['changes']; 
  2840.  
  2841. foreach ( $changes as $instance_id => $data ) { 
  2842. $method_id = $wpdb->get_var( $wpdb->prepare( "SELECT method_id FROM {$wpdb->prefix}woocommerce_shipping_zone_methods WHERE instance_id = %d", $instance_id ) ); 
  2843.  
  2844. if ( isset( $data['deleted'] ) ) { 
  2845. if ( $wpdb->delete( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'instance_id' => $instance_id ) ) ) { 
  2846. do_action( 'woocommerce_shipping_zone_method_deleted', $instance_id, $method_id, $zone_id ); 
  2847. continue; 
  2848.  
  2849. $method_data = array_intersect_key( $data, array( 
  2850. 'method_order' => 1,  
  2851. 'enabled' => 1 
  2852. ) ); 
  2853.  
  2854. if ( isset( $method_data['method_order'] ) ) { 
  2855. $wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'method_order' => absint( $method_data['method_order'] ) ), array( 'instance_id' => absint( $instance_id ) ) ); 
  2856.  
  2857. if ( isset( $method_data['enabled'] ) ) { 
  2858. $is_enabled = absint( 'yes' === $method_data['enabled'] ); 
  2859. if ( $wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'is_enabled' => $is_enabled ), array( 'instance_id' => absint( $instance_id ) ) ) ) { 
  2860. do_action( 'woocommerce_shipping_zone_method_status_toggled', $instance_id, $method_id, $zone_id, $is_enabled ); 
  2861.  
  2862. wp_send_json_success( array( 
  2863. 'methods' => $zone->get_shipping_methods() 
  2864. ) ); 
  2865.  
  2866. /** 
  2867. * Save method settings 
  2868. */ 
  2869. public static function shipping_zone_methods_save_settings() { 
  2870. if ( ! isset( $_POST['wc_shipping_zones_nonce'], $_POST['instance_id'], $_POST['data'] ) ) { 
  2871. wp_send_json_error( 'missing_fields' ); 
  2872. exit; 
  2873.  
  2874. if ( ! wp_verify_nonce( $_POST['wc_shipping_zones_nonce'], 'wc_shipping_zones_nonce' ) ) { 
  2875. wp_send_json_error( 'bad_nonce' ); 
  2876. exit; 
  2877.  
  2878. if ( ! current_user_can( 'manage_woocommerce' ) ) { 
  2879. wp_send_json_error( 'missing_capabilities' ); 
  2880. exit; 
  2881.  
  2882. $instance_id = absint( $_POST['instance_id'] ); 
  2883. $zone = WC_Shipping_Zones::get_zone_by( 'instance_id', $instance_id ); 
  2884. $shipping_method = WC_Shipping_Zones::get_shipping_method( $instance_id ); 
  2885. $shipping_method->set_post_data( $_POST['data'] ); 
  2886. $shipping_method->process_admin_options(); 
  2887.  
  2888. wp_send_json_success( array( 
  2889. 'methods' => $zone->get_shipping_methods(),  
  2890. 'errors' => $shipping_method->get_errors(),  
  2891. ) ); 
  2892.  
  2893. /** 
  2894. * Handle submissions from assets/js/wc-shipping-classes.js Backbone model. 
  2895. */ 
  2896. public static function shipping_classes_save_changes() { 
  2897. if ( ! isset( $_POST['wc_shipping_classes_nonce'], $_POST['changes'] ) ) { 
  2898. wp_send_json_error( 'missing_fields' ); 
  2899. exit; 
  2900.  
  2901. if ( ! wp_verify_nonce( $_POST['wc_shipping_classes_nonce'], 'wc_shipping_classes_nonce' ) ) { 
  2902. wp_send_json_error( 'bad_nonce' ); 
  2903. exit; 
  2904.  
  2905. if ( ! current_user_can( 'manage_woocommerce' ) ) { 
  2906. wp_send_json_error( 'missing_capabilities' ); 
  2907. exit; 
  2908.  
  2909. $changes = $_POST['changes']; 
  2910.  
  2911. foreach ( $changes as $term_id => $data ) { 
  2912. $term_id = absint( $term_id ); 
  2913.  
  2914. if ( isset( $data['deleted'] ) ) { 
  2915. if ( isset( $data['newRow'] ) ) { 
  2916. // So the user added and deleted a new row. 
  2917. // That's fine, it's not in the database anyways. NEXT! 
  2918. continue; 
  2919. wp_delete_term( $term_id, 'product_shipping_class' ); 
  2920. continue; 
  2921.  
  2922. $update_args = array(); 
  2923.  
  2924. if ( isset( $data['name'] ) ) { 
  2925. $update_args['name'] = wc_clean( $data['name'] ); 
  2926.  
  2927. if ( isset( $data['slug'] ) ) { 
  2928. $update_args['slug'] = wc_clean( $data['slug'] ); 
  2929.  
  2930. if ( isset( $data['description'] ) ) { 
  2931. $update_args['description'] = wc_clean( $data['description'] ); 
  2932.  
  2933. if ( isset( $data['newRow'] ) ) { 
  2934. $update_args = array_filter( $update_args ); 
  2935. if ( empty( $update_args['name'] ) ) { 
  2936. continue; 
  2937. $term_id = wp_insert_term( $update_args['name'], 'product_shipping_class', $update_args ); 
  2938. } else { 
  2939. wp_update_term( $term_id, 'product_shipping_class', $update_args ); 
  2940.  
  2941. do_action( 'woocommerce_shipping_classes_save_class', $term_id, $data ); 
  2942.  
  2943. $wc_shipping = WC_Shipping::instance(); 
  2944.  
  2945. wp_send_json_success( array( 
  2946. 'shipping_classes' => $wc_shipping->get_shipping_classes() 
  2947. ) );