WC_Form_Handler

Handle frontend forms.

Defined (1)

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

/includes/class-wc-form-handler.php  
  1. class WC_Form_Handler { 
  2.  
  3. /** 
  4. * Hook in methods. 
  5. */ 
  6. public static function init() { 
  7. add_action( 'template_redirect', array( __CLASS__, 'redirect_reset_password_link' ) ); 
  8. add_action( 'template_redirect', array( __CLASS__, 'save_address' ) ); 
  9. add_action( 'template_redirect', array( __CLASS__, 'save_account_details' ) ); 
  10. add_action( 'wp_loaded', array( __CLASS__, 'checkout_action' ), 20 ); 
  11. add_action( 'wp_loaded', array( __CLASS__, 'process_login' ), 20 ); 
  12. add_action( 'wp_loaded', array( __CLASS__, 'process_registration' ), 20 ); 
  13. add_action( 'wp_loaded', array( __CLASS__, 'process_lost_password' ), 20 ); 
  14. add_action( 'wp_loaded', array( __CLASS__, 'process_reset_password' ), 20 ); 
  15. add_action( 'wp_loaded', array( __CLASS__, 'cancel_order' ), 20 ); 
  16. add_action( 'wp_loaded', array( __CLASS__, 'order_again' ), 20 ); 
  17. add_action( 'wp_loaded', array( __CLASS__, 'update_cart_action' ), 20 ); 
  18. add_action( 'wp_loaded', array( __CLASS__, 'add_to_cart_action' ), 20 ); 
  19.  
  20. // May need $wp global to access query vars. 
  21. add_action( 'wp', array( __CLASS__, 'pay_action' ), 20 ); 
  22. add_action( 'wp', array( __CLASS__, 'add_payment_method_action' ), 20 ); 
  23. add_action( 'wp', array( __CLASS__, 'delete_payment_method_action' ), 20 ); 
  24. add_action( 'wp', array( __CLASS__, 'set_default_payment_method_action' ), 20 ); 
  25.  
  26. /** 
  27. * Remove key and login from querystring, set cookie, and redirect to account page to show the form. 
  28. */ 
  29. public static function redirect_reset_password_link() { 
  30. if ( is_account_page() && ! empty( $_GET['key'] ) && ! empty( $_GET['login'] ) ) { 
  31. $value = sprintf( '%s:%s', wp_unslash( $_GET['login'] ), wp_unslash( $_GET['key'] ) ); 
  32. WC_Shortcode_My_Account::set_reset_password_cookie( $value ); 
  33.  
  34. wp_safe_redirect( add_query_arg( 'show-reset-form', 'true', wc_lostpassword_url() ) ); 
  35. exit; 
  36.  
  37. /** 
  38. * Save and and update a billing or shipping address if the 
  39. * form was submitted through the user account page. 
  40. */ 
  41. public static function save_address() { 
  42. global $wp; 
  43.  
  44. if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { 
  45. return; 
  46.  
  47. if ( empty( $_POST['action'] ) || 'edit_address' !== $_POST['action'] || empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'woocommerce-edit_address' ) ) { 
  48. return; 
  49.  
  50. $user_id = get_current_user_id(); 
  51.  
  52. if ( $user_id <= 0 ) { 
  53. return; 
  54.  
  55. $load_address = isset( $wp->query_vars['edit-address'] ) ? wc_edit_address_i18n( sanitize_title( $wp->query_vars['edit-address'] ), true ) : 'billing'; 
  56.  
  57. $address = WC()->countries->get_address_fields( esc_attr( $_POST[ $load_address . '_country' ] ), $load_address . '_' ); 
  58.  
  59. foreach ( $address as $key => $field ) { 
  60.  
  61. if ( ! isset( $field['type'] ) ) { 
  62. $field['type'] = 'text'; 
  63.  
  64. // Get Value. 
  65. switch ( $field['type'] ) { 
  66. case 'checkbox' : 
  67. $_POST[ $key ] = (int) isset( $_POST[ $key ] ); 
  68. break; 
  69. default : 
  70. $_POST[ $key ] = isset( $_POST[ $key ] ) ? wc_clean( $_POST[ $key ] ) : ''; 
  71. break; 
  72.  
  73. // Hook to allow modification of value. 
  74. $_POST[ $key ] = apply_filters( 'woocommerce_process_myaccount_field_' . $key, $_POST[ $key ] ); 
  75.  
  76. // Validation: Required fields. 
  77. if ( ! empty( $field['required'] ) && empty( $_POST[ $key ] ) ) { 
  78. wc_add_notice( sprintf( __( '%s is a required field.', 'woocommerce' ), $field['label'] ), 'error' ); 
  79.  
  80. if ( ! empty( $_POST[ $key ] ) ) { 
  81.  
  82. // Validation rules. 
  83. if ( ! empty( $field['validate'] ) && is_array( $field['validate'] ) ) { 
  84. foreach ( $field['validate'] as $rule ) { 
  85. switch ( $rule ) { 
  86. case 'postcode' : 
  87. $_POST[ $key ] = strtoupper( str_replace( ' ', '', $_POST[ $key ] ) ); 
  88.  
  89. if ( ! WC_Validation::is_postcode( $_POST[ $key ], $_POST[ $load_address . '_country' ] ) ) { 
  90. wc_add_notice( __( 'Please enter a valid postcode / ZIP.', 'woocommerce' ), 'error' ); 
  91. } else { 
  92. $_POST[ $key ] = wc_format_postcode( $_POST[ $key ], $_POST[ $load_address . '_country' ] ); 
  93. break; 
  94. case 'phone' : 
  95. $_POST[ $key ] = wc_format_phone_number( $_POST[ $key ] ); 
  96.  
  97. if ( ! WC_Validation::is_phone( $_POST[ $key ] ) ) { 
  98. wc_add_notice( sprintf( __( '%s is not a valid phone number.', 'woocommerce' ), '<strong>' . $field['label'] . '</strong>' ), 'error' ); 
  99. break; 
  100. case 'email' : 
  101. $_POST[ $key ] = strtolower( $_POST[ $key ] ); 
  102.  
  103. if ( ! is_email( $_POST[ $key ] ) ) { 
  104. wc_add_notice( sprintf( __( '%s is not a valid email address.', 'woocommerce' ), '<strong>' . $field['label'] . '</strong>' ), 'error' ); 
  105. break; 
  106.  
  107. do_action( 'woocommerce_after_save_address_validation', $user_id, $load_address, $address ); 
  108.  
  109. if ( 0 === wc_notice_count( 'error' ) ) { 
  110.  
  111. foreach ( $address as $key => $field ) { 
  112. update_user_meta( $user_id, $key, $_POST[ $key ] ); 
  113.  
  114. wc_add_notice( __( 'Address changed successfully.', 'woocommerce' ) ); 
  115.  
  116. do_action( 'woocommerce_customer_save_address', $user_id, $load_address ); 
  117.  
  118. wp_safe_redirect( wc_get_endpoint_url( 'edit-address', '', wc_get_page_permalink( 'myaccount' ) ) ); 
  119. exit; 
  120.  
  121. /** 
  122. * Save the password/account details and redirect back to the my account page. 
  123. */ 
  124. public static function save_account_details() { 
  125.  
  126. if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { 
  127. return; 
  128.  
  129. if ( empty( $_POST['action'] ) || 'save_account_details' !== $_POST['action'] || empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'save_account_details' ) ) { 
  130. return; 
  131.  
  132. $errors = new WP_Error(); 
  133. $user = new stdClass(); 
  134.  
  135. $user->ID = (int) get_current_user_id(); 
  136. $current_user = get_user_by( 'id', $user->ID ); 
  137.  
  138. if ( $user->ID <= 0 ) { 
  139. return; 
  140.  
  141. $account_first_name = ! empty( $_POST['account_first_name'] ) ? wc_clean( $_POST['account_first_name'] ) : ''; 
  142. $account_last_name = ! empty( $_POST['account_last_name'] ) ? wc_clean( $_POST['account_last_name'] ) : ''; 
  143. $account_email = ! empty( $_POST['account_email'] ) ? wc_clean( $_POST['account_email'] ) : ''; 
  144. $pass_cur = ! empty( $_POST['password_current'] ) ? $_POST['password_current'] : ''; 
  145. $pass1 = ! empty( $_POST['password_1'] ) ? $_POST['password_1'] : ''; 
  146. $pass2 = ! empty( $_POST['password_2'] ) ? $_POST['password_2'] : ''; 
  147. $save_pass = true; 
  148.  
  149. $user->first_name = $account_first_name; 
  150. $user->last_name = $account_last_name; 
  151.  
  152. // Prevent emails being displayed, or leave alone. 
  153. $user->display_name = is_email( $current_user->display_name ) ? $user->first_name : $current_user->display_name; 
  154.  
  155. // Handle required fields 
  156. $required_fields = apply_filters( 'woocommerce_save_account_details_required_fields', array( 
  157. 'account_first_name' => __( 'First name', 'woocommerce' ),  
  158. 'account_last_name' => __( 'Last name', 'woocommerce' ),  
  159. 'account_email' => __( 'Email address', 'woocommerce' ),  
  160. ) ); 
  161.  
  162. foreach ( $required_fields as $field_key => $field_name ) { 
  163. if ( empty( $_POST[ $field_key ] ) ) { 
  164. wc_add_notice( sprintf( __( '%s is a required field.', 'woocommerce' ), '<strong>' . esc_html( $field_name ) . '</strong>' ), 'error' ); 
  165.  
  166. if ( $account_email ) { 
  167. $account_email = sanitize_email( $account_email ); 
  168. if ( ! is_email( $account_email ) ) { 
  169. wc_add_notice( __( 'Please provide a valid email address.', 'woocommerce' ), 'error' ); 
  170. } elseif ( email_exists( $account_email ) && $account_email !== $current_user->user_email ) { 
  171. wc_add_notice( __( 'This email address is already registered.', 'woocommerce' ), 'error' ); 
  172. $user->user_email = $account_email; 
  173.  
  174. if ( ! empty( $pass_cur ) && empty( $pass1 ) && empty( $pass2 ) ) { 
  175. wc_add_notice( __( 'Please fill out all password fields.', 'woocommerce' ), 'error' ); 
  176. $save_pass = false; 
  177. } elseif ( ! empty( $pass1 ) && empty( $pass_cur ) ) { 
  178. wc_add_notice( __( 'Please enter your current password.', 'woocommerce' ), 'error' ); 
  179. $save_pass = false; 
  180. } elseif ( ! empty( $pass1 ) && empty( $pass2 ) ) { 
  181. wc_add_notice( __( 'Please re-enter your password.', 'woocommerce' ), 'error' ); 
  182. $save_pass = false; 
  183. } elseif ( ( ! empty( $pass1 ) || ! empty( $pass2 ) ) && $pass1 !== $pass2 ) { 
  184. wc_add_notice( __( 'New passwords do not match.', 'woocommerce' ), 'error' ); 
  185. $save_pass = false; 
  186. } elseif ( ! empty( $pass1 ) && ! wp_check_password( $pass_cur, $current_user->user_pass, $current_user->ID ) ) { 
  187. wc_add_notice( __( 'Your current password is incorrect.', 'woocommerce' ), 'error' ); 
  188. $save_pass = false; 
  189.  
  190. if ( $pass1 && $save_pass ) { 
  191. $user->user_pass = $pass1; 
  192.  
  193. // Allow plugins to return their own errors. 
  194. do_action_ref_array( 'woocommerce_save_account_details_errors', array( &$errors, &$user ) ); 
  195.  
  196. if ( $errors->get_error_messages() ) { 
  197. foreach ( $errors->get_error_messages() as $error ) { 
  198. wc_add_notice( $error, 'error' ); 
  199.  
  200. if ( wc_notice_count( 'error' ) === 0 ) { 
  201.  
  202. wp_update_user( $user ); 
  203.  
  204. wc_add_notice( __( 'Account details changed successfully.', 'woocommerce' ) ); 
  205.  
  206. do_action( 'woocommerce_save_account_details', $user->ID ); 
  207.  
  208. wp_safe_redirect( wc_get_page_permalink( 'myaccount' ) ); 
  209. exit; 
  210.  
  211. /** 
  212. * Process the checkout form. 
  213. */ 
  214. public static function checkout_action() { 
  215. if ( isset( $_POST['woocommerce_checkout_place_order'] ) || isset( $_POST['woocommerce_checkout_update_totals'] ) ) { 
  216.  
  217. if ( WC()->cart->is_empty() ) { 
  218. wp_redirect( wc_get_page_permalink( 'cart' ) ); 
  219. exit; 
  220.  
  221. if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { 
  222. define( 'WOOCOMMERCE_CHECKOUT', true ); 
  223.  
  224. WC()->checkout()->process_checkout(); 
  225.  
  226. /** 
  227. * Process the pay form. 
  228. */ 
  229. public static function pay_action() { 
  230. global $wp; 
  231.  
  232. if ( isset( $_POST['woocommerce_pay'] ) && isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'woocommerce-pay' ) ) { 
  233.  
  234. ob_start(); 
  235.  
  236. // Pay for existing order 
  237. $order_key = $_GET['key']; 
  238. $order_id = absint( $wp->query_vars['order-pay'] ); 
  239. $order = wc_get_order( $order_id ); 
  240.  
  241. if ( $order->get_id() == $order_id && $order->get_order_key() == $order_key && $order->needs_payment() ) { 
  242.  
  243. do_action( 'woocommerce_before_pay_action', $order ); 
  244.  
  245. WC()->customer->set_props( array( 
  246. 'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null,  
  247. 'billing_state' => $order->get_billing_state() ? $order->get_billing_state() : null,  
  248. 'billing_postcode' => $order->get_billing_postcode() ? $order->get_billing_postcode() : null,  
  249. 'billing_city' => $order->get_billing_city() ? $order->get_billing_city() : null,  
  250. ) ); 
  251. WC()->customer->save(); 
  252.  
  253. // Terms 
  254. if ( ! empty( $_POST['terms-field'] ) && empty( $_POST['terms'] ) ) { 
  255. wc_add_notice( __( 'You must accept our Terms & Conditions.', 'woocommerce' ), 'error' ); 
  256. return; 
  257.  
  258. // Update payment method 
  259. if ( $order->needs_payment() ) { 
  260. $payment_method = isset( $_POST['payment_method'] ) ? wc_clean( $_POST['payment_method'] ) : false; 
  261. $available_gateways = WC()->payment_gateways->get_available_payment_gateways(); 
  262.  
  263. if ( ! $payment_method ) { 
  264. wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' ); 
  265. return; 
  266.  
  267. // Update meta 
  268. update_post_meta( $order_id, '_payment_method', $payment_method ); 
  269.  
  270. if ( isset( $available_gateways[ $payment_method ] ) ) { 
  271. $payment_method_title = $available_gateways[ $payment_method ]->get_title(); 
  272. } else { 
  273. $payment_method_title = ''; 
  274.  
  275. update_post_meta( $order_id, '_payment_method_title', $payment_method_title ); 
  276.  
  277. // Validate 
  278. $available_gateways[ $payment_method ]->validate_fields(); 
  279.  
  280. // Process 
  281. if ( wc_notice_count( 'error' ) == 0 ) { 
  282.  
  283. $result = $available_gateways[ $payment_method ]->process_payment( $order_id ); 
  284.  
  285. // Redirect to success/confirmation/payment page 
  286. if ( 'success' === $result['result'] ) { 
  287. wp_redirect( $result['redirect'] ); 
  288. exit; 
  289. } else { 
  290. // No payment was required for order 
  291. $order->payment_complete(); 
  292. wp_safe_redirect( $order->get_checkout_order_received_url() ); 
  293. exit; 
  294.  
  295. do_action( 'woocommerce_after_pay_action', $order ); 
  296.  
  297.  
  298. /** 
  299. * Process the add payment method form. 
  300. */ 
  301. public static function add_payment_method_action() { 
  302. if ( isset( $_POST['woocommerce_add_payment_method'], $_POST['payment_method'], $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'woocommerce-add-payment-method' ) ) { 
  303.  
  304. ob_start(); 
  305.  
  306. $payment_method = wc_clean( $_POST['payment_method'] ); 
  307.  
  308. $available_gateways = WC()->payment_gateways->get_available_payment_gateways(); 
  309. // Validate 
  310. $available_gateways[ $payment_method ]->validate_fields(); 
  311.  
  312. // Process 
  313. if ( wc_notice_count( 'wc_errors' ) == 0 ) { 
  314. $result = $available_gateways[ $payment_method ]->add_payment_method(); 
  315. // Redirect to success/confirmation/payment page 
  316. if ( 'success' === $result['result'] ) { 
  317. wc_add_notice( __( 'Payment method added.', 'woocommerce' ) ); 
  318. wp_redirect( $result['redirect'] ); 
  319. exit(); 
  320.  
  321.  
  322. /** 
  323. * Process the delete payment method form. 
  324. */ 
  325. public static function delete_payment_method_action() { 
  326. global $wp; 
  327.  
  328. if ( isset( $wp->query_vars['delete-payment-method'] ) ) { 
  329.  
  330. $token_id = absint( $wp->query_vars['delete-payment-method'] ); 
  331. $token = WC_Payment_Tokens::get( $token_id ); 
  332.  
  333. if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'delete-payment-method-' . $token_id ) ) { 
  334. wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' ); 
  335. } else { 
  336. WC_Payment_Tokens::delete( $token_id ); 
  337. wc_add_notice( __( 'Payment method deleted.', 'woocommerce' ) ); 
  338.  
  339. wp_redirect( wc_get_account_endpoint_url( 'payment-methods' ) ); 
  340. exit(); 
  341.  
  342.  
  343. /** 
  344. * Process the delete payment method form. 
  345. */ 
  346. public static function set_default_payment_method_action() { 
  347. global $wp; 
  348.  
  349. if ( isset( $wp->query_vars['set-default-payment-method'] ) ) { 
  350.  
  351. $token_id = absint( $wp->query_vars['set-default-payment-method'] ); 
  352. $token = WC_Payment_Tokens::get( $token_id ); 
  353.  
  354. if ( is_null( $token ) || get_current_user_id() !== $token->get_user_id() || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'set-default-payment-method-' . $token_id ) ) { 
  355. wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' ); 
  356. } else { 
  357. WC_Payment_Tokens::set_users_default( $token->get_user_id(), intval( $token_id ) ); 
  358. wc_add_notice( __( 'This payment method was successfully set as your default.', 'woocommerce' ) ); 
  359.  
  360. wp_redirect( wc_get_account_endpoint_url( 'payment-methods' ) ); 
  361. exit(); 
  362.  
  363.  
  364. /** 
  365. * Remove from cart/update. 
  366. */ 
  367. public static function update_cart_action() { 
  368.  
  369. if ( ! empty( $_POST['apply_coupon'] ) && ! empty( $_POST['coupon_code'] ) ) { 
  370.  
  371. // Add Discount 
  372. WC()->cart->add_discount( sanitize_text_field( $_POST['coupon_code'] ) ); 
  373.  
  374. } elseif ( isset( $_GET['remove_coupon'] ) ) { 
  375.  
  376. // Remove Coupon Codes 
  377. WC()->cart->remove_coupon( wc_clean( $_GET['remove_coupon'] ) ); 
  378.  
  379. } elseif ( ! empty( $_GET['remove_item'] ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'woocommerce-cart' ) ) { 
  380.  
  381. // Remove from cart 
  382. $cart_item_key = sanitize_text_field( $_GET['remove_item'] ); 
  383.  
  384. if ( $cart_item = WC()->cart->get_cart_item( $cart_item_key ) ) { 
  385. WC()->cart->remove_cart_item( $cart_item_key ); 
  386.  
  387. $product = wc_get_product( $cart_item['product_id'] ); 
  388.  
  389. $item_removed_title = apply_filters( 'woocommerce_cart_item_removed_title', $product ? sprintf( _x( '“%s”', 'Item name in quotes', 'woocommerce' ), $product->get_name() ) : __( 'Item', 'woocommerce' ), $cart_item ); 
  390.  
  391. // Don't show undo link if removed item is out of stock. 
  392. if ( $product->is_in_stock() && $product->has_enough_stock( $cart_item['quantity'] ) ) { 
  393. $removed_notice = sprintf( __( '%s removed.', 'woocommerce' ), $item_removed_title ); 
  394. $removed_notice .= ' <a href="' . esc_url( WC()->cart->get_undo_url( $cart_item_key ) ) . '">' . __( 'Undo?', 'woocommerce' ) . '</a>'; 
  395. } else { 
  396. $removed_notice = sprintf( __( '%s removed.', 'woocommerce' ), $item_removed_title ); 
  397.  
  398. wc_add_notice( $removed_notice ); 
  399.  
  400. $referer = wp_get_referer() ? remove_query_arg( array( 'remove_item', 'add-to-cart', 'added-to-cart' ), add_query_arg( 'removed_item', '1', wp_get_referer() ) ) : wc_get_cart_url(); 
  401. wp_safe_redirect( $referer ); 
  402. exit; 
  403.  
  404. } elseif ( ! empty( $_GET['undo_item'] ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'woocommerce-cart' ) ) { 
  405.  
  406. // Undo Cart Item 
  407. $cart_item_key = sanitize_text_field( $_GET['undo_item'] ); 
  408.  
  409. WC()->cart->restore_cart_item( $cart_item_key ); 
  410.  
  411. $referer = wp_get_referer() ? remove_query_arg( array( 'undo_item', '_wpnonce' ), wp_get_referer() ) : wc_get_cart_url(); 
  412. wp_safe_redirect( $referer ); 
  413. exit; 
  414.  
  415.  
  416. // Update Cart - checks apply_coupon too because they are in the same form 
  417. if ( ( ! empty( $_POST['apply_coupon'] ) || ! empty( $_POST['update_cart'] ) || ! empty( $_POST['proceed'] ) ) && isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'woocommerce-cart' ) ) { 
  418.  
  419. $cart_updated = false; 
  420. $cart_totals = isset( $_POST['cart'] ) ? $_POST['cart'] : ''; 
  421.  
  422. if ( ! WC()->cart->is_empty() && is_array( $cart_totals ) ) { 
  423. foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) { 
  424.  
  425. $_product = $values['data']; 
  426.  
  427. // Skip product if no updated quantity was posted 
  428. if ( ! isset( $cart_totals[ $cart_item_key ] ) || ! isset( $cart_totals[ $cart_item_key ]['qty'] ) ) { 
  429. continue; 
  430.  
  431. // Sanitize 
  432. $quantity = apply_filters( 'woocommerce_stock_amount_cart_item', wc_stock_amount( preg_replace( "/[^0-9\.]/", '', $cart_totals[ $cart_item_key ]['qty'] ) ), $cart_item_key ); 
  433.  
  434. if ( '' === $quantity || $quantity == $values['quantity'] ) 
  435. continue; 
  436.  
  437. // Update cart validation 
  438. $passed_validation = apply_filters( 'woocommerce_update_cart_validation', true, $cart_item_key, $values, $quantity ); 
  439.  
  440. // is_sold_individually 
  441. if ( $_product->is_sold_individually() && $quantity > 1 ) { 
  442. wc_add_notice( sprintf( __( 'You can only have 1 %s in your cart.', 'woocommerce' ), $_product->get_name() ), 'error' ); 
  443. $passed_validation = false; 
  444.  
  445. if ( $passed_validation ) { 
  446. WC()->cart->set_quantity( $cart_item_key, $quantity, false ); 
  447. $cart_updated = true; 
  448.  
  449. // Trigger action - let 3rd parties update the cart if they need to and update the $cart_updated variable 
  450. $cart_updated = apply_filters( 'woocommerce_update_cart_action_cart_updated', $cart_updated ); 
  451.  
  452. if ( $cart_updated ) { 
  453. // Recalc our totals 
  454. WC()->cart->calculate_totals(); 
  455.  
  456. if ( ! empty( $_POST['proceed'] ) ) { 
  457. wp_safe_redirect( wc_get_checkout_url() ); 
  458. exit; 
  459. } elseif ( $cart_updated ) { 
  460. wc_add_notice( __( 'Cart updated.', 'woocommerce' ) ); 
  461. $referer = remove_query_arg( array( 'remove_coupon', 'add-to-cart' ), ( wp_get_referer() ? wp_get_referer() : wc_get_cart_url() ) ); 
  462. wp_safe_redirect( $referer ); 
  463. exit; 
  464.  
  465. /** 
  466. * Place a previous order again. 
  467. */ 
  468. public static function order_again() { 
  469.  
  470. // Nothing to do 
  471. if ( ! isset( $_GET['order_again'] ) || ! is_user_logged_in() || ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'woocommerce-order_again' ) ) { 
  472. return; 
  473.  
  474. // Clear current cart 
  475. WC()->cart->empty_cart(); 
  476.  
  477. // Load the previous order - Stop if the order does not exist 
  478. $order = wc_get_order( absint( $_GET['order_again'] ) ); 
  479.  
  480. if ( ! $order->get_id() ) { 
  481. return; 
  482.  
  483. if ( ! $order->has_status( apply_filters( 'woocommerce_valid_order_statuses_for_order_again', array( 'completed' ) ) ) ) { 
  484. return; 
  485.  
  486. // Make sure the user is allowed to order again. By default it check if the 
  487. // previous order belonged to the current user. 
  488. if ( ! current_user_can( 'order_again', $order->get_id() ) ) { 
  489. return; 
  490.  
  491. // Copy products from the order to the cart 
  492. $order_items = $order->get_items(); 
  493. foreach ( $order_items as $item ) { 
  494. // Load all product info including variation data 
  495. $product_id = (int) apply_filters( 'woocommerce_add_to_cart_product_id', $item->get_product_id() ); 
  496. $quantity = $item->get_quantity(); 
  497. $variation_id = $item->get_variation_id(); 
  498. $variations = array(); 
  499. $cart_item_data = apply_filters( 'woocommerce_order_again_cart_item_data', array(), $item, $order ); 
  500.  
  501. foreach ( $item->get_meta_data() as $meta ) { 
  502. if ( taxonomy_is_product_attribute( $meta->key ) ) { 
  503. $term = get_term_by( 'slug', $meta->value, $meta->key ); 
  504. $variations[ $meta->key ] = $term ? $term->name : $meta->value; 
  505. } elseif ( meta_is_product_attribute( $meta->key, $meta->value, $product_id ) ) { 
  506. $variations[ $meta->key ] = $meta->value; 
  507.  
  508. // Add to cart validation 
  509. if ( ! apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations, $cart_item_data ) ) { 
  510. continue; 
  511.  
  512. WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations, $cart_item_data ); 
  513.  
  514. do_action( 'woocommerce_ordered_again', $order->get_id() ); 
  515.  
  516. $num_items_in_cart = count( WC()->cart->get_cart() ); 
  517. $num_items_in_original_order = count( $order_items ); 
  518.  
  519. if ( $num_items_in_original_order > $num_items_in_cart ) { 
  520. wc_add_notice( 
  521. sprintf( _n( 
  522. '%d item from your previous order is currently unavailable and could not be added to your cart.',  
  523. '%d items from your previous order are currently unavailable and could not be added to your cart.',  
  524. $num_items_in_original_order - $num_items_in_cart,  
  525. 'woocommerce' 
  526. ), $num_items_in_original_order - $num_items_in_cart ),  
  527. 'error' 
  528. ); 
  529.  
  530. if ( $num_items_in_cart > 0 ) { 
  531. wc_add_notice( __( 'The cart has been filled with the items from your previous order.', 'woocommerce' ) ); 
  532.  
  533. // Redirect to cart 
  534. wp_safe_redirect( wc_get_cart_url() ); 
  535. exit; 
  536.  
  537. /** 
  538. * Cancel a pending order. 
  539. */ 
  540. public static function cancel_order() { 
  541. if ( isset( $_GET['cancel_order'] ) && isset( $_GET['order'] ) && isset( $_GET['order_id'] ) ) { 
  542.  
  543. $order_key = $_GET['order']; 
  544. $order_id = absint( $_GET['order_id'] ); 
  545. $order = wc_get_order( $order_id ); 
  546. $user_can_cancel = current_user_can( 'cancel_order', $order_id ); 
  547. $order_can_cancel = $order->has_status( apply_filters( 'woocommerce_valid_order_statuses_for_cancel', array( 'pending', 'failed' ) ) ); 
  548. $redirect = $_GET['redirect']; 
  549.  
  550. if ( $order->has_status( 'cancelled' ) ) { 
  551. // Already cancelled - take no action 
  552. } elseif ( $user_can_cancel && $order_can_cancel && $order->get_id() === $order_id && $order->get_order_key() === $order_key ) { 
  553.  
  554. // Cancel the order + restore stock 
  555. WC()->session->set( 'order_awaiting_payment', false ); 
  556. $order->update_status( 'cancelled', __( 'Order cancelled by customer.', 'woocommerce' ) ); 
  557.  
  558. // Message 
  559. wc_add_notice( apply_filters( 'woocommerce_order_cancelled_notice', __( 'Your order was cancelled.', 'woocommerce' ) ), apply_filters( 'woocommerce_order_cancelled_notice_type', 'notice' ) ); 
  560.  
  561. do_action( 'woocommerce_cancelled_order', $order->get_id() ); 
  562.  
  563. } elseif ( $user_can_cancel && ! $order_can_cancel ) { 
  564. wc_add_notice( __( 'Your order can no longer be cancelled. Please contact us if you need assistance.', 'woocommerce' ), 'error' ); 
  565. } else { 
  566. wc_add_notice( __( 'Invalid order.', 'woocommerce' ), 'error' ); 
  567.  
  568. if ( $redirect ) { 
  569. wp_safe_redirect( $redirect ); 
  570. exit; 
  571.  
  572. /** 
  573. * Add to cart action. 
  574. * Checks for a valid request, does validation (via hooks) and then redirects if valid. 
  575. * @param bool $url (default: false) 
  576. */ 
  577. public static function add_to_cart_action( $url = false ) { 
  578. if ( empty( $_REQUEST['add-to-cart'] ) || ! is_numeric( $_REQUEST['add-to-cart'] ) ) { 
  579. return; 
  580.  
  581. $product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_REQUEST['add-to-cart'] ) ); 
  582. $was_added_to_cart = false; 
  583. $adding_to_cart = wc_get_product( $product_id ); 
  584.  
  585. if ( ! $adding_to_cart ) { 
  586. return; 
  587.  
  588. $add_to_cart_handler = apply_filters( 'woocommerce_add_to_cart_handler', $adding_to_cart->get_type(), $adding_to_cart ); 
  589.  
  590. // Variable product handling 
  591. if ( 'variable' === $add_to_cart_handler ) { 
  592. $was_added_to_cart = self::add_to_cart_handler_variable( $product_id ); 
  593.  
  594. // Grouped Products 
  595. } elseif ( 'grouped' === $add_to_cart_handler ) { 
  596. $was_added_to_cart = self::add_to_cart_handler_grouped( $product_id ); 
  597.  
  598. // Custom Handler 
  599. } elseif ( has_action( 'woocommerce_add_to_cart_handler_' . $add_to_cart_handler ) ) { 
  600. do_action( 'woocommerce_add_to_cart_handler_' . $add_to_cart_handler, $url ); 
  601.  
  602. // Simple Products 
  603. } else { 
  604. $was_added_to_cart = self::add_to_cart_handler_simple( $product_id ); 
  605.  
  606. // If we added the product to the cart we can now optionally do a redirect. 
  607. if ( $was_added_to_cart && wc_notice_count( 'error' ) === 0 ) { 
  608. // If has custom URL redirect there 
  609. if ( $url = apply_filters( 'woocommerce_add_to_cart_redirect', $url ) ) { 
  610. wp_safe_redirect( $url ); 
  611. exit; 
  612. } elseif ( get_option( 'woocommerce_cart_redirect_after_add' ) === 'yes' ) { 
  613. wp_safe_redirect( wc_get_cart_url() ); 
  614. exit; 
  615.  
  616. /** 
  617. * Handle adding simple products to the cart. 
  618. * @since 2.4.6 Split from add_to_cart_action 
  619. * @param int $product_id 
  620. * @return bool success or not 
  621. */ 
  622. private static function add_to_cart_handler_simple( $product_id ) { 
  623. $quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] ); 
  624. $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity ); 
  625.  
  626. if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity ) !== false ) { 
  627. wc_add_to_cart_message( array( $product_id => $quantity ), true ); 
  628. return true; 
  629. return false; 
  630.  
  631. /** 
  632. * Handle adding grouped products to the cart. 
  633. * @since 2.4.6 Split from add_to_cart_action 
  634. * @param int $product_id 
  635. * @return bool success or not 
  636. */ 
  637. private static function add_to_cart_handler_grouped( $product_id ) { 
  638. $was_added_to_cart = false; 
  639. $added_to_cart = array(); 
  640.  
  641. if ( ! empty( $_REQUEST['quantity'] ) && is_array( $_REQUEST['quantity'] ) ) { 
  642. $quantity_set = false; 
  643.  
  644. foreach ( $_REQUEST['quantity'] as $item => $quantity ) { 
  645. if ( $quantity <= 0 ) { 
  646. continue; 
  647. $quantity_set = true; 
  648.  
  649. // Add to cart validation 
  650. $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $item, $quantity ); 
  651.  
  652. if ( $passed_validation && WC()->cart->add_to_cart( $item, $quantity ) !== false ) { 
  653. $was_added_to_cart = true; 
  654. $added_to_cart[ $item ] = $quantity; 
  655.  
  656. if ( ! $was_added_to_cart && ! $quantity_set ) { 
  657. wc_add_notice( __( 'Please choose the quantity of items you wish to add to your cart…', 'woocommerce' ), 'error' ); 
  658. } elseif ( $was_added_to_cart ) { 
  659. wc_add_to_cart_message( $added_to_cart ); 
  660. return true; 
  661. } elseif ( $product_id ) { 
  662. /** Link on product archives */ 
  663. wc_add_notice( __( 'Please choose a product to add to your cart…', 'woocommerce' ), 'error' ); 
  664. return false; 
  665.  
  666. /** 
  667. * Handle adding variable products to the cart. 
  668. * @since 2.4.6 Split from add_to_cart_action 
  669. * @param int $product_id 
  670. * @return bool success or not 
  671. */ 
  672. private static function add_to_cart_handler_variable( $product_id ) { 
  673. $adding_to_cart = wc_get_product( $product_id ); 
  674. $variation_id = empty( $_REQUEST['variation_id'] ) ? '' : absint( $_REQUEST['variation_id'] ); 
  675. $quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] ); 
  676. $missing_attributes = array(); 
  677. $variations = array(); 
  678. $attributes = $adding_to_cart->get_attributes(); 
  679.  
  680. // If no variation ID is set, attempt to get a variation ID from posted attributes. 
  681. if ( empty( $variation_id ) ) { 
  682. $data_store = WC_Data_Store::load( 'product' ); 
  683. $variation_id = $data_store->find_matching_product_variation( $adding_to_cart, wp_unslash( $_POST ) ); 
  684.  
  685. // Validate the attributes. 
  686. try { 
  687. if ( empty( $variation_id ) ) { 
  688. throw new Exception( __( 'Please choose product options…', 'woocommerce' ) ); 
  689.  
  690. $variation_data = wc_get_product_variation_attributes( $variation_id ); 
  691.  
  692. foreach ( $attributes as $attribute ) { 
  693. if ( ! $attribute['is_variation'] ) { 
  694. continue; 
  695.  
  696. $taxonomy = 'attribute_' . sanitize_title( $attribute['name'] ); 
  697.  
  698. if ( isset( $_REQUEST[ $taxonomy ] ) ) { 
  699. // Get value from post data 
  700. if ( $attribute['is_taxonomy'] ) { 
  701. // Don't use wc_clean as it destroys sanitized characters 
  702. $value = sanitize_title( stripslashes( $_REQUEST[ $taxonomy ] ) ); 
  703. } else { 
  704. $value = wc_clean( stripslashes( $_REQUEST[ $taxonomy ] ) ); 
  705.  
  706. // Get valid value from variation 
  707. $valid_value = isset( $variation_data[ $taxonomy ] ) ? $variation_data[ $taxonomy ] : ''; 
  708.  
  709. // Allow if valid or show error. 
  710. if ( '' === $valid_value || $valid_value === $value ) { 
  711. $variations[ $taxonomy ] = $value; 
  712. } else { 
  713. throw new Exception( sprintf( __( 'Invalid value posted for %s', 'woocommerce' ), wc_attribute_label( $attribute['name'] ) ) ); 
  714. } else { 
  715. $missing_attributes[] = wc_attribute_label( $attribute['name'] ); 
  716. if ( ! empty( $missing_attributes ) ) { 
  717. throw new Exception( sprintf( _n( '%s is a required field', '%s are required fields', sizeof( $missing_attributes ), 'woocommerce' ), wc_format_list_of_items( $missing_attributes ) ) ); 
  718. } catch ( Exception $e ) { 
  719. wc_add_notice( $e->getMessage(), 'error' ); 
  720. return false; 
  721.  
  722. // Add to cart validation 
  723. $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations ); 
  724.  
  725. if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) !== false ) { 
  726. wc_add_to_cart_message( array( $product_id => $quantity ), true ); 
  727. return true; 
  728.  
  729. return false; 
  730.  
  731. /** 
  732. * Process the login form. 
  733. */ 
  734. public static function process_login() { 
  735. $nonce_value = isset( $_POST['_wpnonce'] ) ? $_POST['_wpnonce'] : ''; 
  736. $nonce_value = isset( $_POST['woocommerce-login-nonce'] ) ? $_POST['woocommerce-login-nonce'] : $nonce_value; 
  737.  
  738. if ( ! empty( $_POST['login'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-login' ) ) { 
  739.  
  740. try { 
  741. $creds = array( 
  742. 'user_password' => $_POST['password'],  
  743. 'remember' => isset( $_POST['rememberme'] ),  
  744. ); 
  745.  
  746. $username = trim( $_POST['username'] ); 
  747. $validation_error = new WP_Error(); 
  748. $validation_error = apply_filters( 'woocommerce_process_login_errors', $validation_error, $_POST['username'], $_POST['password'] ); 
  749.  
  750. if ( $validation_error->get_error_code() ) { 
  751. throw new Exception( '<strong>' . __( 'Error:', 'woocommerce' ) . '</strong> ' . $validation_error->get_error_message() ); 
  752.  
  753. if ( empty( $username ) ) { 
  754. throw new Exception( '<strong>' . __( 'Error:', 'woocommerce' ) . '</strong> ' . __( 'Username is required.', 'woocommerce' ) ); 
  755.  
  756. if ( is_email( $username ) && apply_filters( 'woocommerce_get_username_from_email', true ) ) { 
  757. $user = get_user_by( 'email', $username ); 
  758.  
  759. if ( isset( $user->user_login ) ) { 
  760. $creds['user_login'] = $user->user_login; 
  761. } else { 
  762. throw new Exception( '<strong>' . __( 'Error:', 'woocommerce' ) . '</strong> ' . __( 'A user could not be found with this email address.', 'woocommerce' ) ); 
  763. } else { 
  764. $creds['user_login'] = $username; 
  765.  
  766. // On multisite, ensure user exists on current site, if not add them before allowing login. 
  767. if ( is_multisite() ) { 
  768. $user_data = get_user_by( 'login', $username ); 
  769.  
  770. if ( $user_data && ! is_user_member_of_blog( $user_data->ID, get_current_blog_id() ) ) { 
  771. add_user_to_blog( get_current_blog_id(), $user_data->ID, 'customer' ); 
  772.  
  773. // Perform the login 
  774. $user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() ); 
  775.  
  776. if ( is_wp_error( $user ) ) { 
  777. $message = $user->get_error_message(); 
  778. $message = str_replace( '<strong>' . esc_html( $creds['user_login'] ) . '</strong>', '<strong>' . esc_html( $username ) . '</strong>', $message ); 
  779. throw new Exception( $message ); 
  780. } else { 
  781.  
  782. if ( ! empty( $_POST['redirect'] ) ) { 
  783. $redirect = $_POST['redirect']; 
  784. } elseif ( wp_get_referer() ) { 
  785. $redirect = wp_get_referer(); 
  786. } else { 
  787. $redirect = wc_get_page_permalink( 'myaccount' ); 
  788.  
  789. wp_redirect( apply_filters( 'woocommerce_login_redirect', $redirect, $user ) ); 
  790. exit; 
  791. } catch ( Exception $e ) { 
  792. wc_add_notice( apply_filters( 'login_errors', $e->getMessage() ), 'error' ); 
  793. do_action( 'woocommerce_login_failed' ); 
  794.  
  795. /** 
  796. * Handle lost password form. 
  797. */ 
  798. public static function process_lost_password() { 
  799. if ( isset( $_POST['wc_reset_password'] ) && isset( $_POST['user_login'] ) && isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'lost_password' ) ) { 
  800. $success = WC_Shortcode_My_Account::retrieve_password(); 
  801.  
  802. // If successful, redirect to my account with query arg set 
  803. if ( $success ) { 
  804. wp_redirect( add_query_arg( 'reset-link-sent', 'true', remove_query_arg( array( 'key', 'login', 'reset' ) ) ) ); 
  805. exit; 
  806.  
  807. /** 
  808. * Handle reset password form. 
  809. */ 
  810. public static function process_reset_password() { 
  811. $posted_fields = array( 'wc_reset_password', 'password_1', 'password_2', 'reset_key', 'reset_login', '_wpnonce' ); 
  812.  
  813. foreach ( $posted_fields as $field ) { 
  814. if ( ! isset( $_POST[ $field ] ) ) { 
  815. return; 
  816. $posted_fields[ $field ] = $_POST[ $field ]; 
  817.  
  818. if ( ! wp_verify_nonce( $posted_fields['_wpnonce'], 'reset_password' ) ) { 
  819. return; 
  820.  
  821. $user = WC_Shortcode_My_Account::check_password_reset_key( $posted_fields['reset_key'], $posted_fields['reset_login'] ); 
  822.  
  823. if ( $user instanceof WP_User ) { 
  824. if ( empty( $posted_fields['password_1'] ) ) { 
  825. wc_add_notice( __( 'Please enter your password.', 'woocommerce' ), 'error' ); 
  826.  
  827. if ( $posted_fields['password_1'] !== $posted_fields['password_2'] ) { 
  828. wc_add_notice( __( 'Passwords do not match.', 'woocommerce' ), 'error' ); 
  829.  
  830. $errors = new WP_Error(); 
  831.  
  832. do_action( 'validate_password_reset', $errors, $user ); 
  833.  
  834. wc_add_wp_error_notices( $errors ); 
  835.  
  836. if ( 0 === wc_notice_count( 'error' ) ) { 
  837. WC_Shortcode_My_Account::reset_password( $user, $posted_fields['password_1'] ); 
  838.  
  839. do_action( 'woocommerce_customer_reset_password', $user ); 
  840.  
  841. wp_redirect( add_query_arg( 'password-reset', 'true', wc_get_page_permalink( 'myaccount' ) ) ); 
  842. exit; 
  843.  
  844. /** 
  845. * Process the registration form. 
  846. */ 
  847. public static function process_registration() { 
  848. $nonce_value = isset( $_POST['_wpnonce'] ) ? $_POST['_wpnonce'] : ''; 
  849. $nonce_value = isset( $_POST['woocommerce-register-nonce'] ) ? $_POST['woocommerce-register-nonce'] : $nonce_value; 
  850.  
  851. if ( ! empty( $_POST['register'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-register' ) ) { 
  852. $username = 'no' === get_option( 'woocommerce_registration_generate_username' ) ? $_POST['username'] : ''; 
  853. $password = 'no' === get_option( 'woocommerce_registration_generate_password' ) ? $_POST['password'] : ''; 
  854. $email = $_POST['email']; 
  855.  
  856. try { 
  857. $validation_error = new WP_Error(); 
  858. $validation_error = apply_filters( 'woocommerce_process_registration_errors', $validation_error, $username, $password, $email ); 
  859.  
  860. if ( $validation_error->get_error_code() ) { 
  861. throw new Exception( $validation_error->get_error_message() ); 
  862.  
  863. // Anti-spam trap 
  864. if ( ! empty( $_POST['email_2'] ) ) { 
  865. throw new Exception( __( 'Anti-spam field was filled in.', 'woocommerce' ) ); 
  866.  
  867. $new_customer = wc_create_new_customer( sanitize_email( $email ), wc_clean( $username ), $password ); 
  868.  
  869. if ( is_wp_error( $new_customer ) ) { 
  870. throw new Exception( $new_customer->get_error_message() ); 
  871.  
  872. if ( apply_filters( 'woocommerce_registration_auth_new_customer', true, $new_customer ) ) { 
  873. wc_set_customer_auth_cookie( $new_customer ); 
  874.  
  875. wp_safe_redirect( apply_filters( 'woocommerce_registration_redirect', wp_get_referer() ? wp_get_referer() : wc_get_page_permalink( 'myaccount' ) ) ); 
  876. exit; 
  877.  
  878. } catch ( Exception $e ) { 
  879. wc_add_notice( '<strong>' . __( 'Error:', 'woocommerce' ) . '</strong> ' . $e->getMessage(), 'error' );