/inc/class-wcml-cart.php

  1. <?php 
  2. class WCML_Cart 
  3.  
  4. private $woocommerce_wpml; 
  5. private $sitepress; 
  6. private $woocommerce; 
  7.  
  8. public function __construct( &$woocommerce_wpml, &$sitepress, &$woocommerce ) 
  9. $this->woocommerce_wpml = $woocommerce_wpml; 
  10. $this->sitepress = $sitepress; 
  11. $this->woocommerce = $woocommerce; 
  12.  
  13. if( $this->woocommerce_wpml->settings[ 'cart_sync' ][ 'currency_switch' ] == WCML_CART_CLEAR || $this->woocommerce_wpml->settings[ 'cart_sync' ][ 'lang_switch' ] == WCML_CART_CLEAR ) { 
  14. $this->enqueue_dialog_ui(); 
  15.  
  16. add_action( 'wcml_removed_cart_items', array( $this, 'wcml_removed_cart_items_widget' ) ); 
  17. add_action( 'wp_ajax_wcml_cart_clear_removed_items', array( $this, 'wcml_cart_clear_removed_items' ) ); 
  18. add_action( 'wp_ajax_nopriv_wcml_cart_clear_removed_items', array( $this, 'wcml_cart_clear_removed_items' ) ); 
  19.  
  20. add_filter( 'wcml_switch_currency_exception', array( $this, 'cart_switching_currency' ), 10, 3 ); 
  21. add_action( 'wcml_before_switch_currency', array( $this, 'switching_currency_empty_cart_if_needed' ), 10, 2 ); 
  22. else{ 
  23. //cart widget 
  24. add_action( 'wp_ajax_woocommerce_get_refreshed_fragments', array( $this, 'wcml_refresh_fragments' ), 0 ); 
  25. add_action( 'wp_ajax_woocommerce_add_to_cart', array( $this, 'wcml_refresh_fragments' ), 0 ); 
  26. add_action( 'wp_ajax_nopriv_woocommerce_get_refreshed_fragments', array( $this, 'wcml_refresh_fragments' ), 0 ); 
  27. add_action( 'wp_ajax_nopriv_woocommerce_add_to_cart', array( $this, 'wcml_refresh_fragments' ), 0 ); 
  28.  
  29. //cart 
  30. add_action( 'woocommerce_before_calculate_totals', array( $this, 'woocommerce_calculate_totals' ), 100 ); 
  31. add_action( 'woocommerce_get_cart_item_from_session', array( $this, 'translate_cart_contents' ), 10 ); 
  32. add_action( 'woocommerce_cart_loaded_from_session', array( $this, 'translate_cart_subtotal' ) ); 
  33. add_action( 'woocommerce_before_checkout_process', array( $this, 'wcml_refresh_cart_total' ) ); 
  34.  
  35. add_filter('woocommerce_paypal_args', array($this, 'filter_paypal_args')); 
  36. add_filter( 'woocommerce_add_to_cart_sold_individually_quantity', array( $this, 'woocommerce_add_to_cart_sold_individually_quantity' ), 10, 5 ); 
  37.  
  38. $this->localize_flat_rates_shipping_classes(); 
  39.  
  40.  
  41. public function enqueue_dialog_ui() { 
  42.  
  43. wp_enqueue_script( 'jquery-ui-dialog' ); 
  44. wp_enqueue_style( 'wp-jquery-ui-dialog' ); 
  45.  
  46.  
  47. public function wcml_removed_cart_items_widget( $args = array() ) { 
  48.  
  49. if( !empty( $this->woocommerce->session ) ) { 
  50. $removed_cart_items = new WCML_Removed_Cart_Items_UI( $args, $this->woocommerce_wpml, $this->sitepress, $this->woocommerce ); 
  51. $preview = $removed_cart_items->get_view(); 
  52.  
  53. if ( !isset($args['echo']) || $args['echo'] ) { 
  54. echo $preview; 
  55. } else { 
  56. return $preview; 
  57.  
  58.  
  59. public function switching_currency_empty_cart_if_needed( $currency, $force_switch ) { 
  60. if( $force_switch && $this->woocommerce_wpml->settings[ 'cart_sync' ][ 'currency_switch' ] == WCML_CART_CLEAR ) { 
  61. $this->empty_cart_if_needed('currency_switch'); 
  62. $this->woocommerce->session->set('wcml_switched_type', 'currency'); 
  63.  
  64. public function empty_cart_if_needed( $switching_type ) { 
  65.  
  66. if( $this->woocommerce_wpml->settings[ 'cart_sync' ][ $switching_type ] == WCML_CART_CLEAR ) { 
  67. $removed_products = $this->woocommerce->session->get( 'wcml_removed_items' ) ? maybe_unserialize( $this->woocommerce->session->get( 'wcml_removed_items' ) ) : array(); 
  68.  
  69. foreach( WC()->cart->get_cart_for_session() as $item_key => $cart ) { 
  70. if( !in_array( $cart[ 'product_id' ], $removed_products ) ) { 
  71. $removed_products[] = $cart[ 'product_id' ]; 
  72. WC()->cart->remove_cart_item( $item_key ); 
  73.  
  74. if( !empty( $this->woocommerce->session ) ) { 
  75. $this->woocommerce->session->set( 'wcml_removed_items', serialize( $removed_products ) ); 
  76.  
  77. public function wcml_cart_clear_removed_items( ) { 
  78.  
  79. $nonce = filter_input( INPUT_POST, 'wcml_nonce', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); 
  80. if(!$nonce || !wp_verify_nonce($nonce, 'wcml_clear_removed_items')) { 
  81. die('Invalid nonce'); 
  82.  
  83. $this->woocommerce->session->__unset( 'wcml_removed_items' ); 
  84. $this->woocommerce->session->__unset( 'wcml_switched_type' ); 
  85.  
  86. public function cart_switching_currency( $exc, $current_currency, $new_currency, $return = false ) { 
  87.  
  88. $cart_for_session = WC()->cart->get_cart_for_session(); 
  89. if( $this->woocommerce_wpml->settings[ 'cart_sync' ][ 'currency_switch' ] == WCML_CART_SYNC || empty( $cart_for_session ) ) { 
  90. return $exc; 
  91.  
  92. $dialog_title = __( 'Switching currency?', 'woocommerce-multilingual'); 
  93. $confirmation_message = __( 'Your cart is not empty! After you switched the currency, all items from the cart will be removed and you have to add them again.', 'woocommerce-multilingual'); 
  94. $stay_in = sprintf( __( 'Keep using %s', 'woocommerce-multilingual'), $current_currency ); 
  95. $switch_to = __( 'Proceed', 'woocommerce-multilingual'); 
  96.  
  97. ob_start(); 
  98. $this->cart_alert( $dialog_title, $confirmation_message, $switch_to, $stay_in, $new_currency, $current_currency ); 
  99. $html = ob_get_contents(); 
  100. ob_end_clean(); 
  101.  
  102. if( $return ) { 
  103. return array( 'prevent_switching' => $html ); 
  104. }else{ 
  105. echo json_encode( array( 'prevent_switching' => $html ) ); 
  106.  
  107. return true; 
  108.  
  109. public function cart_alert( $dialog_title, $confirmation_message, $switch_to, $stay_in, $switch_to_value, $stay_in_value = false, $language_switch = false ) { 
  110. ?> 
  111. <div id="wcml-cart-dialog-confirm" title="<?php echo $dialog_title ?>"> 
  112. <p><?php echo $confirmation_message; ?></p> 
  113. </div> 
  114.  
  115. <script type="text/javascript"> 
  116. jQuery( document).ready( function() { 
  117. jQuery( "#wcml-cart-dialog-confirm" ).dialog({ 
  118. resizable: false,  
  119. draggable: false,  
  120. height: "auto",  
  121. width: 500,  
  122. modal: true,  
  123. closeOnEscape: false,  
  124. dialogClass: "wcml-cart-dialog",  
  125. open: function(event, ui) { 
  126. jQuery(".ui-dialog-titlebar-close", ui.dialog | ui).hide(); 
  127. },  
  128. buttons: { 
  129. "<?php echo $switch_to; ?>": function() { 
  130. jQuery( this ).dialog( "close" ); 
  131. <?php if( $language_switch ): ?> 
  132. window.location = '<?php echo $switch_to_value; ?>'; 
  133. <?php else: ?> 
  134. jQuery('.wcml_currency_switcher').parent().find('img').remove(); 
  135. wcml_load_currency( "<?php echo $switch_to_value; ?>", true ); 
  136. <?php endif; ?> 
  137.  
  138. },  
  139. "<?php echo $stay_in; ?>": function() { 
  140. jQuery( this ).dialog( "close" ); 
  141. <?php if( $language_switch ): ?> 
  142. window.location = '<?php echo $stay_in_value; ?>'; 
  143. <?php else: ?> 
  144. jQuery('.wcml_currency_switcher').parent().find('img').remove(); 
  145. jQuery('.wcml_currency_switcher').removeAttr('disabled'); 
  146. jQuery('.wcml_currency_switcher').val( '<?php echo $stay_in_value; ?>' ); 
  147. <?php endif; ?> 
  148. }); 
  149. }); 
  150. </script> 
  151. <?php 
  152.  
  153. public function wcml_refresh_fragments() { 
  154. WC()->cart->calculate_totals(); 
  155. $this->woocommerce_wpml->locale->wcml_refresh_text_domain(); 
  156.  
  157. /** 
  158. * Update cart and cart session when switch language 
  159. */ 
  160. public function woocommerce_calculate_totals( $cart, $currency = false ) { 
  161.  
  162. $current_language = $this->sitepress->get_current_language(); 
  163. $new_cart_data = array(); 
  164.  
  165. foreach( $cart->cart_contents as $key => $cart_item ) { 
  166. $tr_product_id = apply_filters( 'translate_object_id', $cart_item[ 'product_id' ], 'product', false, $current_language ); 
  167. //translate custom attr labels in cart object 
  168.  
  169. if( version_compare( WC_VERSION , '3.0.0', '<' ) && isset( $cart_item[ 'data' ]->product_attributes ) ) { 
  170. foreach( $cart_item[ 'data' ]->product_attributes as $attr_key => $product_attribute ) { 
  171. if( isset( $product_attribute[ 'is_taxonomy' ]) && !$product_attribute[ 'is_taxonomy' ] ) { 
  172. $cart->cart_contents[ $key ][ 'data' ]->product_attributes[ $attr_key ][ 'name' ] = $this->woocommerce_wpml->strings->translated_attribute_label( 
  173. $product_attribute[ 'name' ],  
  174. $product_attribute[ 'name' ],  
  175. $tr_product_id ); 
  176.  
  177. //translate custom attr value in cart object 
  178. if( isset( $cart_item[ 'variation' ] ) && is_array( $cart_item[ 'variation' ] ) ) { 
  179. foreach( $cart_item[ 'variation' ] as $attr_key => $attribute ) { 
  180. $cart->cart_contents[ $key ][ 'variation' ][ $attr_key ] = $this->get_cart_attribute_translation( 
  181. $attr_key,  
  182. $attribute,  
  183. $cart_item[ 'variation_id' ],  
  184. $current_language,  
  185. $cart_item[ 'product_id' ],  
  186. $tr_product_id 
  187. ); 
  188.  
  189. if( $currency !== false ) { 
  190. $cart->cart_contents[ $key ][ 'data' ]->price = get_post_meta( $cart_item['product_id'], '_price', 1 ); 
  191.  
  192. if( $cart_item[ 'product_id' ] == $tr_product_id ) { 
  193. $new_cart_data[ $key ] = apply_filters( 'wcml_cart_contents_not_changed', $cart->cart_contents[$key], $key, $current_language ); 
  194. continue; 
  195.  
  196. if( isset( $cart->cart_contents[ $key ][ 'variation_id' ] ) && $cart->cart_contents[ $key ][ 'variation_id' ] ) { 
  197. $tr_variation_id = apply_filters( 'translate_object_id', $cart_item[ 'variation_id' ], 'product_variation', false, $current_language ); 
  198. if( !is_null( $tr_variation_id ) ) { 
  199. $cart->cart_contents[ $key ][ 'product_id' ] = intval( $tr_product_id ); 
  200. $cart->cart_contents[ $key ][ 'variation_id' ] = intval( $tr_variation_id ); 
  201. if( defined('WC_VERSION') && version_compare( WC_VERSION , '2.7', '<' ) ) { 
  202. $cart->cart_contents[ $key ][ 'data' ]->id = intval( $tr_product_id ); 
  203. }else{ 
  204. $cart->cart_contents[ $key ][ 'data' ]->set_id( intval( $tr_product_id ) ); 
  205. $cart->cart_contents[ $key ][ 'data' ]->post = get_post( $tr_product_id ); 
  206. }else{ 
  207. if( !is_null( $tr_product_id ) ) { 
  208. $cart->cart_contents[ $key ][ 'product_id' ] = intval( $tr_product_id ); 
  209. if( defined('WC_VERSION') && version_compare( WC_VERSION , '2.7', '<' ) ) { 
  210. $cart->cart_contents[ $key ][ 'data' ]->id = intval( $tr_product_id ); 
  211. }else{ 
  212. $cart->cart_contents[ $key ][ 'data' ]->set_id( intval( $tr_product_id ) ); 
  213. $cart->cart_contents[ $key ][ 'data' ]->post = get_post( $tr_product_id ); 
  214.  
  215. if( !is_null( $tr_product_id ) ) { 
  216.  
  217. $new_key = $this->wcml_generate_cart_key( $cart->cart_contents, $key ); 
  218. $cart->cart_contents = apply_filters( 'wcml_update_cart_contents_lang_switch', $cart->cart_contents, $key, $new_key, $current_language ); 
  219. $new_cart_data[ $new_key ] = $cart->cart_contents[ $key ]; 
  220.  
  221. $new_cart_data = apply_filters( 'wcml_cart_contents', $new_cart_data, $cart->cart_contents, $key, $new_key ); 
  222.  
  223. $cart->cart_contents = $this->wcml_check_on_duplicate_products_in_cart( $new_cart_data ); 
  224. $this->woocommerce->session->cart = $cart->cart_contents; 
  225. return $cart->cart_contents; 
  226.  
  227. public function wcml_check_on_duplicate_products_in_cart( $cart_contents ) { 
  228.  
  229. $exists_products = array(); 
  230. remove_action( 'woocommerce_before_calculate_totals', array( $this, 'woocommerce_calculate_totals' ), 100 ); 
  231.  
  232. foreach( $cart_contents as $key => $cart_content ) { 
  233. $cart_contents = apply_filters( 'wcml_check_on_duplicated_products_in_cart', $cart_contents, $key, $cart_content ); 
  234. if( apply_filters( 'wcml_exception_duplicate_products_in_cart', false, $cart_content ) ) { 
  235. continue; 
  236.  
  237. $quantity = $cart_content['quantity']; 
  238.  
  239. $search_key = $this->wcml_generate_cart_key( $cart_contents, $key ); 
  240. if( array_key_exists( $search_key, $exists_products ) ) { 
  241. unset( $cart_contents[ $key ] ); 
  242. $cart_contents[ $exists_products[ $search_key ] ][ 'quantity' ] = $cart_contents[ $exists_products[ $search_key ] ][ 'quantity' ] + $quantity; 
  243. $this->woocommerce->cart->calculate_totals(); 
  244. }else{ 
  245. $exists_products[ $search_key ] = $key; 
  246.  
  247. add_action( 'woocommerce_before_calculate_totals', array( $this, 'woocommerce_calculate_totals' ), 100 ); 
  248. return $cart_contents; 
  249.  
  250. public function get_cart_attribute_translation( $attr_key, $attribute, $variation_id, $current_language, $product_id, $tr_product_id ) { 
  251.  
  252. $attr_translation = $attribute; 
  253.  
  254. if( !empty( $attribute ) ) { 
  255. //delete 'attribute_' at the beginning 
  256. $taxonomy = substr( $attr_key, 10, strlen( $attr_key ) - 1 ); 
  257.  
  258. if( taxonomy_exists( $taxonomy ) ) { 
  259. if( $this->woocommerce_wpml->attributes->is_translatable_attribute( $taxonomy ) ) { 
  260. $term_id = $this->woocommerce_wpml->terms->wcml_get_term_id_by_slug( $taxonomy, $attribute ); 
  261. $trnsl_term_id = apply_filters( 'translate_object_id', $term_id, $taxonomy, true, $current_language ); 
  262. $term = $this->woocommerce_wpml->terms->wcml_get_term_by_id( $trnsl_term_id, $taxonomy ); 
  263. $attr_translation = $term->slug; 
  264. }else{ 
  265.  
  266. $trnsl_attr = get_post_meta( $variation_id, $attr_key, true ); 
  267.  
  268. if( $trnsl_attr ) { 
  269. $attr_translation = $trnsl_attr; 
  270. }else{ 
  271. $attr_translation = $this->woocommerce_wpml->attributes->get_custom_attr_translation( $product_id, $tr_product_id, $taxonomy, $attribute ); 
  272.  
  273. return $attr_translation; 
  274.  
  275. public function wcml_generate_cart_key( $cart_contents, $key ) { 
  276. $cart_item_data = $this->get_cart_item_data_from_cart( $cart_contents[ $key ] ); 
  277.  
  278. return $this->woocommerce->cart->generate_cart_id( 
  279. $cart_contents[ $key ][ 'product_id' ],  
  280. $cart_contents[ $key ][ 'variation_id' ],  
  281. $cart_contents[ $key ][ 'variation' ],  
  282. $cart_item_data 
  283. ); 
  284.  
  285. //get cart_item_data from existing cart array ( from session ) 
  286. public function get_cart_item_data_from_cart( $cart_contents ) { 
  287. unset( $cart_contents[ 'product_id' ] ); 
  288. unset( $cart_contents[ 'variation_id' ] ); 
  289. unset( $cart_contents[ 'variation' ] ); 
  290. unset( $cart_contents[ 'quantity' ] ); 
  291. unset( $cart_contents[ 'line_total' ] ); 
  292. unset( $cart_contents[ 'line_subtotal' ] ); 
  293. unset( $cart_contents[ 'line_tax' ] ); 
  294. unset( $cart_contents[ 'line_subtotal_tax' ] ); 
  295. unset( $cart_contents[ 'line_tax_data' ] ); 
  296. unset( $cart_contents[ 'data' ] ); 
  297.  
  298. return apply_filters( 'wcml_filter_cart_item_data', $cart_contents ); 
  299.  
  300. public function translate_cart_contents( $item ) { 
  301.  
  302. // translate the product id and product data 
  303. $item[ 'product_id' ] = apply_filters( 'translate_object_id', $item[ 'product_id' ], 'product', true ); 
  304. if ($item[ 'variation_id' ]) { 
  305. $item[ 'variation_id' ] = apply_filters( 'translate_object_id', $item[ 'variation_id' ], 'product_variation', true ); 
  306.  
  307. if( version_compare( WC()->version, '3.0.0', '>=' ) ) { 
  308. $item[ 'data' ]->set_name( get_the_title( $item[ 'product_id' ] ) ); 
  309. } else { 
  310. $item[ 'data' ]->post->post_title = get_the_title( $item[ 'product_id' ] ); 
  311.  
  312. return $item; 
  313.  
  314. public function translate_cart_subtotal( $cart ) { 
  315.  
  316. if( isset( $_SERVER['REQUEST_URI'] ) ) { 
  317. //special case: check if attachment loading 
  318. $attachments = array( 'png', 'jpg', 'jpeg', 'gif', 'js', 'css' ); 
  319.  
  320. foreach( $attachments as $attachment ) { 
  321. $match = preg_match( '/\.'.$attachment.'$/', $_SERVER['REQUEST_URI'] ); 
  322. if( !empty( $match ) ) { 
  323. return false; 
  324.  
  325. if( apply_filters( 'wcml_calculate_totals_exception', true, $cart ) ) { 
  326. $cart->calculate_totals(); 
  327.  
  328.  
  329. // refresh cart total to return correct price from WC object 
  330. public function wcml_refresh_cart_total() { 
  331. WC()->cart->calculate_totals(); 
  332.  
  333.  
  334. public function localize_flat_rates_shipping_classes() { 
  335.  
  336. if(is_ajax() && isset($_POST['action']) && $_POST['action'] == 'woocommerce_update_order_review') { 
  337. $this->woocommerce->shipping->load_shipping_methods(); 
  338. $shipping_methods = $this->woocommerce->shipping->get_shipping_methods(); 
  339. foreach($shipping_methods as $method) { 
  340. if(isset($method->flat_rate_option)) { 
  341. add_filter('option_' . $method->flat_rate_option, array($this, 'translate_shipping_class')); 
  342.  
  343.  
  344. public function translate_shipping_class($rates) { 
  345.  
  346. if(is_array($rates)) { 
  347. foreach($rates as $shipping_class => $value) { 
  348. $term_id = $this->woocommerce_wpml->terms->wcml_get_term_id_by_slug('product_shipping_class', $shipping_class ); 
  349.  
  350. if($term_id && !is_wp_error($term_id)) { 
  351. $translated_term_id = apply_filters( 'translate_object_id', $term_id, 'product_shipping_class', true); 
  352. if($translated_term_id != $term_id) { 
  353. $term = $this->woocommerce_wpml->terms->wcml_get_term_by_id( $translated_term_id, 'product_shipping_class' ); 
  354. unset($rates[$shipping_class]); 
  355. $rates[$term->slug] = $value; 
  356.  
  357. return $rates; 
  358.  
  359. public function filter_paypal_args( $args ) { 
  360. $args['lc'] = $this->sitepress->get_current_language(); 
  361.  
  362. //filter URL when default permalinks uses 
  363. $wpml_settings = $this->sitepress->get_settings(); 
  364. if( $wpml_settings[ 'language_negotiation_type' ] == 3 ) { 
  365. $args[ 'notify_url' ] = str_replace( '%2F&', '&', $args[ 'notify_url' ] ); 
  366.  
  367. return $args; 
  368.  
  369. public function woocommerce_add_to_cart_sold_individually_quantity( $qt, $quantity, $product_id, $variation_id, $cart_item_data ) { 
  370.  
  371. //check if product already added to cart in another language 
  372. $current_product_trid = $this->sitepress->get_element_trid( $product_id, 'post_product' ); 
  373.  
  374. foreach( WC()->cart->cart_contents as $cart_item ) { 
  375. $cart_element_trid = $this->sitepress->get_element_trid( $cart_item[ 'product_id' ], 'post_product' ); 
  376. if( apply_filters( 'wcml_add_to_cart_sold_individually', true, $cart_item_data, $product_id, $quantity ) && $current_product_trid == $cart_element_trid && $cart_item[ 'quantity' ] > 0 ) { 
  377. throw new Exception( sprintf( '<a href="%s" class="button wc-forward">%s</a> %s', esc_url( wc_get_cart_url() ), __( 'View Cart', 'woocommerce' ), sprintf( __( 'You cannot add another "%s" to your cart.', 'woocommerce' ), get_the_title( $product_id ) ) ) ); 
  378.  
  379. return $qt; 
.