WJECF_Controller

The main controller for WooCommerce Extended Coupon Features.

Defined (1)

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

/includes/wjecf-controller.php  
  1. class WJECF_Controller { 
  2.  
  3. // Coupon message codes 
  4. //NOTE: I use prefix 79 for this plugin; there's no guarantee that other plugins don't use the same values! 
  5. const E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET = 79100; 
  6. const E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET = 79101; 
  7. const E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET = 79102; 
  8. const E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET = 79103; 
  9. const E_WC_COUPON_SHIPPING_METHOD_NOT_MET = 79104; 
  10. const E_WC_COUPON_PAYMENT_METHOD_NOT_MET = 79105; 
  11. const E_WC_COUPON_NOT_FOR_THIS_USER = 79106; 
  12.  
  13. protected $debug_mode = false; 
  14. protected $log = array(); 
  15. public $options = false; 
  16.  
  17. //Plugin data 
  18. public $plugin_path; // Has trailing slash 
  19. public $plugin_url; // Has trailing slash 
  20. public $version; // Use for js 
  21.  
  22. /** 
  23. * Singleton Instance 
  24. * @static 
  25. * @return Singleton Instance 
  26. */ 
  27. public static function instance() { 
  28. if ( is_null( self::$_instance ) ) { 
  29. self::$_instance = new self(); 
  30. return self::$_instance; 
  31. protected static $_instance = null; 
  32.  
  33.  
  34. public function __construct() {  
  35. $this->debug_mode = false && defined( 'WP_DEBUG' ) && WP_DEBUG; 
  36. //Paths 
  37. $this->plugin_path = plugin_dir_path( dirname(__FILE__) ); 
  38. $this->plugin_url = plugins_url( '/', dirname( __FILE__ ) ); 
  39.  
  40. $filename = $this->is_pro() ? "woocommerce-jos-autocoupon-pro.php" : "woocommerce-jos-autocoupon.php" ; 
  41.  
  42. //Version 
  43. $default_headers = array( 
  44. 'Version' => 'Version',  
  45. ); 
  46. $plugin_data = get_file_data( $this->plugin_path . $filename, $default_headers, 'plugin' ); 
  47. $this->version = $plugin_data['Version']; 
  48.  
  49. public function start() { 
  50. add_action('init', array( $this, 'init_hook' )); 
  51.  
  52. public function init_hook() { 
  53. if ( ! class_exists('WC_Coupon') ) { 
  54. add_action( 'admin_notices', array( $this, 'admin_notice_woocommerce_not_found' ) ); 
  55. return; 
  56.  
  57. $this->controller_init(); 
  58.  
  59. /** 
  60. * Fires before the WJECF plugins are initialised. 
  61. *  
  62. * Perfect hook for themes or plugins to load custom WJECF plugins. 
  63. *  
  64. * @since 2.3.7 
  65. **/ 
  66. do_action( 'wjecf_init_plugins'); 
  67.  
  68. //Start the plugins 
  69. foreach ( WJECF()->get_plugins() as $name => $plugin ) { 
  70. if ( $plugin->plugin_is_enabled() ) { 
  71.  
  72. foreach( $plugin->get_plugin_dependencies() as $dependency_name ) { 
  73. $dependency = $this->get_plugin( $dependency_name ); 
  74. if ( ! $dependency || ! $dependency->plugin_is_enabled() ) { 
  75. error_log('Unable to initialize ' . $name . ' because it requires ' . $dependency_name ); 
  76. continue; 
  77.  
  78. $plugin->init_hook(); 
  79. if ( is_admin() ) { 
  80. $plugin->init_admin_hook(); 
  81.  
  82. public function controller_init() { 
  83.  
  84. $this->log( "INIT " . ( is_ajax() ? "AJAX" : is_admin() ? "ADMIN" : "FRONTEND" ) . " " . $_SERVER['REQUEST_URI'] ); 
  85.  
  86. $this->init_options(); 
  87.  
  88. //Frontend hooks 
  89.  
  90. //assert_coupon_is_valid (which raises exception on invalid coupon) can only be used on WC 2.3.0 and up 
  91. if ( WJECF_WC()->check_woocommerce_version('2.3.0') ) { 
  92. add_filter('woocommerce_coupon_is_valid', array( $this, 'assert_coupon_is_valid' ), 10, 2 ); 
  93. } else { 
  94. add_filter('woocommerce_coupon_is_valid', array( $this, 'coupon_is_valid' ), 10, 2 ); 
  95.  
  96. add_filter('woocommerce_coupon_error', array( $this, 'woocommerce_coupon_error' ), 10, 3 ); 
  97. add_action('woocommerce_coupon_loaded', array( $this, 'woocommerce_coupon_loaded' ), 10, 1); 
  98. add_filter('woocommerce_coupon_get_discount_amount', array( $this, 'woocommerce_coupon_get_discount_amount' ), 10, 5); 
  99. add_action( 'wp_footer', array( $this, 'render_log' ) ); //Log 
  100.  
  101. protected $plugins = array(); 
  102.  
  103. /** 
  104. * Load a WJECF Plugin (class name) 
  105. * @param string $class_name The class name of the plugin 
  106. * @return bool True if succeeded, otherwise false 
  107. */ 
  108. public function add_plugin( $class_name ) { 
  109. if ( isset( $this->plugins[ $class_name ] ) ) { 
  110. return false; //Already loaded 
  111.  
  112. if ( ! class_exists( $class_name ) ) { 
  113. return false; //Not found 
  114.  
  115. $the_plugin = new $class_name(); 
  116. foreach( $the_plugin->get_plugin_dependencies() as $dependency ) { 
  117. if ( ! class_exists( $dependency ) ) { 
  118. $this->log( 'Unknown dependency: ' . $dependency . ' for plugin ' . $class_name ); 
  119. return false; 
  120.  
  121. if ( isset( $this->plugins[ $class_name ] ) ) { 
  122. continue; //dependency is al geladen 
  123.  
  124. $this->add_plugin( $dependency ); 
  125.  
  126. //Assert dependencies 
  127. try { 
  128. $the_plugin->assert_dependencies(); 
  129. } catch (Exception $ex) { 
  130. $msg = sprintf('Failed loading %s: %s', $class_name, $ex->getMessage() ); 
  131. if ( $wjecf_admin = WJECF()->get_plugin('WJECF_Admin') ) { 
  132. $wjecf_admin->enqueue_notice( $msg, 'error' ); 
  133. } else { 
  134. error_log("PRINTR".print_r($wjecf_admin, true)); 
  135. error_log( $msg ); 
  136. return false; 
  137.  
  138. $this->plugins[ $class_name ] = $the_plugin; 
  139. $this->log( 'Loaded plugin: ' . $class_name ); 
  140.  
  141. return true; 
  142.  
  143. public function get_plugins() { 
  144. return $this->plugins; 
  145.  
  146. /** 
  147. * Retrieves the WJECF Plugin 
  148. * @param string $class_name  
  149. * @return object|bool The plugin if found, otherwise returns false 
  150. */ 
  151. public function get_plugin( $class_name ) { 
  152. if ( isset( $this->plugins[ $class_name ] ) ) { 
  153. return $this->plugins[ $class_name ]; 
  154. } else { 
  155. return false; 
  156. }  
  157.  
  158. public function init_options() { 
  159. $this->options = get_option( 'wjecf_options' ); 
  160. if (false === $this->options) { 
  161. $this->options = array( 'db_version' => 0 ); 
  162.  
  163. /** FRONTEND HOOKS */ 
  164.  
  165. /** 
  166. * Notifies that WooCommerce has not been detected. 
  167. * @return void 
  168. */ 
  169. public function admin_notice_woocommerce_not_found() { 
  170. $msg = __( 'WooCommerce Extended Coupon Features is disabled because WooCommerce could not be detected.', 'woocommerce-jos-autocoupon' ); 
  171. echo '<div class="error"><p>' . $msg . '</p></div>'; 
  172.  
  173. //2.2.2 
  174. public function woocommerce_coupon_loaded ( $coupon ) { 
  175. if ( ! is_admin() ) { 
  176. //2.2.2 Allow coupon even if minimum spend not reached 
  177. if ( WJECF_Wrap( $coupon )->get_meta( '_wjecf_allow_below_minimum_spend' ) == 'yes' ) { 
  178. //HACK: Overwrite the minimum amount with 0 so WooCommerce will allow the coupon 
  179. $coupon->wjecf_minimum_amount_for_discount = WJECF_Wrap( $coupon )->get_minimum_amount(); 
  180. $coupon->minimum_amount = 0; 
  181.  
  182. //2.2.2 
  183. public function woocommerce_coupon_get_discount_amount ( $discount, $discounting_amount, $cart_item, $single, $coupon ) { 
  184. //2.2.2 No value if minimum spend not reached 
  185. if (isset( $coupon->wjecf_minimum_amount_for_discount ) ) { 
  186. if ( wc_format_decimal( $coupon->wjecf_minimum_amount_for_discount ) > wc_format_decimal( WC()->cart->subtotal ) ) { 
  187. return 0; 
  188. }; 
  189. return $discount; 
  190.  
  191. /** 
  192. * Overwrite coupon error message, if $err_code is an error code of this plugin 
  193. * @param string $err Original error message 
  194. * @param int $err_code Error code 
  195. * @param WC_Coupon $coupon The coupon 
  196. * @return string Overwritten error message 
  197. */ 
  198. public function woocommerce_coupon_error( $err, $err_code, $coupon ) { 
  199. switch ( $err_code ) { 
  200. case self::E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET: 
  201. $min_price = wc_price( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_subtotal' ) ); 
  202. $err = sprintf( __( 'The minimum subtotal of the matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $min_price ); 
  203. break; 
  204. case self::E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET: 
  205. $max_price = wc_price( WJECF_Wrap( $coupon )->get_meta( '_wjecf_max_matching_product_subtotal' ) ); 
  206. $err = sprintf( __( 'The maximum subtotal of the matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $max_price ); 
  207. break; 
  208. case self::E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET: 
  209. $min_matching_product_qty = intval( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_qty' ) ); 
  210. $err = sprintf( __( 'The minimum quantity of matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $min_matching_product_qty ); 
  211. break; 
  212. case self::E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET: 
  213. $max_matching_product_qty = intval( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_qty' ) ); 
  214. $err = sprintf( __( 'The maximum quantity of matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $max_matching_product_qty ); 
  215. break; 
  216. case self::E_WC_COUPON_SHIPPING_METHOD_NOT_MET: 
  217. $err = __( 'The coupon is not valid for the currently selected shipping method.', 'woocommerce-jos-autocoupon' ); 
  218. break; 
  219. case self::E_WC_COUPON_PAYMENT_METHOD_NOT_MET: 
  220. $err = __( 'The coupon is not valid for the currently selected payment method.', 'woocommerce-jos-autocoupon' ); 
  221. break; 
  222. case self::E_WC_COUPON_NOT_FOR_THIS_USER: 
  223. $err = sprintf( __( 'Sorry, it seems the coupon "%s" is not yours.', 'woocommerce-jos-autocoupon' ), WJECF_Wrap( $coupon )->get_code() ); 
  224. break; 
  225. default: 
  226. //Do nothing 
  227. break; 
  228. return $err; 
  229.  
  230. /** 
  231. * Extra validation rules for coupons. 
  232. * @param bool $valid  
  233. * @param WC_Coupon $coupon  
  234. * @return bool True if valid; False if not valid. 
  235. */ 
  236. public function coupon_is_valid ( $valid, $coupon ) { 
  237. try { 
  238. return $this->assert_coupon_is_valid( $valid, $coupon ); 
  239. } catch ( Exception $e ) { 
  240. return false; 
  241. }  
  242.  
  243. /** 
  244. * Extra validation rules for coupons. Throw an exception when not valid. 
  245. * @param bool $valid  
  246. * @param WC_Coupon $coupon  
  247. * @return bool True if valid; False if already invalid on function call. In any other case an Exception will be thrown. 
  248. */ 
  249. public function assert_coupon_is_valid ( $valid, $coupon ) { 
  250.  
  251. $wrap_coupon = WJECF_Wrap( $coupon ); 
  252.  
  253. //Not valid? Then it will never validate, so get out of here 
  254. if ( ! $valid ) { 
  255. return false; 
  256.  
  257. //============================ 
  258. //Test if ALL products are in the cart (if AND-operator selected instead of the default OR) 
  259. $products_and = $wrap_coupon->get_meta( '_wjecf_products_and' ) == 'yes'; 
  260. if ( $products_and && sizeof( $wrap_coupon->get_product_ids() ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference  
  261. //Get array of all cart product and variation ids 
  262. $cart_item_ids = array(); 
  263. $cart = WC()->cart->get_cart(); 
  264. foreach( $cart as $cart_item_key => $cart_item ) { 
  265. $cart_item_ids[] = $cart_item['product_id']; 
  266. $cart_item_ids[] = $cart_item['variation_id']; 
  267. //Filter used by WJECF_WPML hook 
  268. $cart_item_ids = apply_filters( 'wjecf_get_product_ids', $cart_item_ids ); 
  269.  
  270. //check if every single product is in the cart 
  271. foreach( apply_filters( 'wjecf_get_product_ids', $wrap_coupon->get_product_ids() ) as $product_id ) { 
  272. if ( ! in_array( $product_id, $cart_item_ids ) ) { 
  273. throw new Exception( WC_Coupon::E_WC_COUPON_NOT_APPLICABLE ); 
  274. }  
  275.  
  276. //============================ 
  277. //Test if products form ALL categories are in the cart (if AND-operator selected instead of the default OR) 
  278. $categories_and = $wrap_coupon->get_meta( '_wjecf_categories_and' ) == 'yes'; 
  279. if ( $categories_and && sizeof( $wrap_coupon->get_product_categories() ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference  
  280. //Get array of all cart product and variation ids 
  281. $cart_product_cats = array(); 
  282. $cart = WC()->cart->get_cart(); 
  283. foreach( $cart as $cart_item_key => $cart_item ) { 
  284. $cart_product_cats = array_merge ( $cart_product_cats, wp_get_post_terms( $cart_item['product_id'], 'product_cat', array( "fields" => "ids" ) ) ); 
  285. //Filter used by WJECF_WPML hook 
  286. $cart_product_cats = apply_filters( 'wjecf_get_product_cat_ids', $cart_product_cats ); 
  287. //check if every single category is in the cart 
  288. foreach( apply_filters( 'wjecf_get_product_cat_ids', $wrap_coupon->get_product_categories() ) as $cat_id ) { 
  289. if ( ! in_array( $cat_id, $cart_product_cats ) ) { 
  290. $this->log( $cat_id . " is not in " . print_r($cart_product_cats, true)); 
  291. throw new Exception( WC_Coupon::E_WC_COUPON_NOT_APPLICABLE ); 
  292.  
  293. //============================ 
  294. //Test min/max quantity of matching products 
  295. // 
  296. //For all items in the cart: 
  297. // If coupon contains both a product AND category inclusion filter: the item is counted if it matches either one of them 
  298. // If coupon contains either a product OR category exclusion filter: the item will NOT be counted if it matches either one of them 
  299. // If sale items are excluded by the coupon: the item will NOT be counted if it is a sale item 
  300. // If no filter exist, all items will be counted 
  301.  
  302. $multiplier = null; //null = not initialized 
  303.  
  304. //Validate quantity 
  305. $min_matching_product_qty = intval( $wrap_coupon->get_meta( '_wjecf_min_matching_product_qty' ) ); 
  306. $max_matching_product_qty = intval( $wrap_coupon->get_meta( '_wjecf_max_matching_product_qty' ) ); 
  307. if ( $min_matching_product_qty > 0 || $max_matching_product_qty > 0 ) { 
  308. //Count the products 
  309. $qty = $this->get_quantity_of_matching_products( $coupon ); 
  310. if ( $min_matching_product_qty > 0 && $qty < $min_matching_product_qty ) throw new Exception( self::E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET ); 
  311. if ( $max_matching_product_qty > 0 && $qty > $max_matching_product_qty ) throw new Exception( self::E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET ); 
  312.  
  313. if ( $min_matching_product_qty > 0 ) { 
  314. $multiplier = self::min_value( floor( $qty / $min_matching_product_qty ), $multiplier ); 
  315. }  
  316.  
  317. //Validate subtotal (2.2.2) 
  318. $min_matching_product_subtotal = floatval( $wrap_coupon->get_meta( '_wjecf_min_matching_product_subtotal' ) ); 
  319. $max_matching_product_subtotal = floatval( $wrap_coupon->get_meta( '_wjecf_max_matching_product_subtotal' ) ); 
  320. if ( $min_matching_product_subtotal > 0 || $max_matching_product_subtotal > 0 ) {  
  321. $subtotal = $this->get_subtotal_of_matching_products( $coupon ); 
  322. if ( $min_matching_product_subtotal > 0 && $subtotal < $min_matching_product_subtotal ) throw new Exception( self::E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET ); 
  323. if ( $max_matching_product_subtotal > 0 && $subtotal > $max_matching_product_subtotal ) throw new Exception( self::E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET ); 
  324.  
  325. if ( $min_matching_product_subtotal > 0 ) { 
  326. $multiplier = self::min_value( floor( $subtotal / $min_matching_product_subtotal ), $multiplier ); 
  327.  
  328. //============================ 
  329. //Test restricted shipping methods 
  330. $shipping_method_ids = $this->get_coupon_shipping_method_ids( $coupon ); 
  331. if ( sizeof( $shipping_method_ids ) > 0 ) { 
  332. $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); 
  333. $chosen_shipping = empty( $chosen_shipping_methods ) ? '' : $chosen_shipping_methods[0];  
  334. $chosen_shipping = explode( ':', $chosen_shipping); //UPS and USPS stores extra data, seperated by colon 
  335. $chosen_shipping = $chosen_shipping[0]; 
  336.  
  337. if ( ! in_array( $chosen_shipping, $shipping_method_ids ) ) { 
  338. throw new Exception( self::E_WC_COUPON_SHIPPING_METHOD_NOT_MET ); 
  339.  
  340. //============================ 
  341. //Test restricted payment methods 
  342. $payment_method_ids = $this->get_coupon_payment_method_ids( $coupon ); 
  343. if ( sizeof( $payment_method_ids ) > 0 ) {  
  344. $chosen_payment_method = isset( WC()->session->chosen_payment_method ) ? WC()->session->chosen_payment_method : array();  
  345.  
  346. if ( ! in_array( $chosen_payment_method, $payment_method_ids ) ) { 
  347. throw new Exception( self::E_WC_COUPON_PAYMENT_METHOD_NOT_MET ); 
  348. }  
  349.  
  350.  
  351. //============================ 
  352. //Test restricted user ids and roles 
  353. //NOTE: If both customer id and role restrictions are provided, the coupon matches if either the id or the role matches 
  354. $coupon_customer_ids = $this->get_coupon_customer_ids( $coupon ); 
  355. $coupon_customer_roles = $this->get_coupon_customer_roles( $coupon ); 
  356. if ( sizeof( $coupon_customer_ids ) > 0 || sizeof( $coupon_customer_roles ) > 0 ) {  
  357. $user = wp_get_current_user(); 
  358.  
  359. //If both fail we invalidate. Otherwise it's ok 
  360. if ( ! in_array( $user->ID, $coupon_customer_ids ) && ! array_intersect( $user->roles, $coupon_customer_roles ) ) { 
  361. throw new Exception( self::E_WC_COUPON_NOT_FOR_THIS_USER ); 
  362.  
  363. //============================ 
  364. //Test excluded user roles 
  365. $coupon_excluded_customer_roles = $this->get_coupon_excluded_customer_roles( $coupon ); 
  366. if ( sizeof( $coupon_excluded_customer_roles ) > 0 ) {  
  367. $user = wp_get_current_user(); 
  368.  
  369. //Excluded customer roles 
  370. if ( array_intersect( $user->roles, $coupon_excluded_customer_roles ) ) { 
  371. throw new Exception( self::E_WC_COUPON_NOT_FOR_THIS_USER ); 
  372.  
  373. //We use our own filter (instead of woocommerce_coupon_is_valid) for easier compatibility management 
  374. //e.g. WC prior to 2.3.0 can't handle Exceptions; while 2.3.0 and above require exceptions 
  375. do_action( 'wjecf_assert_coupon_is_valid', $coupon ); 
  376.  
  377. if ( $wrap_coupon->get_minimum_amount() ) { 
  378. $multiplier = self::min_value( floor( WC()->cart->subtotal / $wrap_coupon->get_minimum_amount() ), $multiplier ); 
  379.  
  380.  
  381. $this->coupon_multiplier_values[ $wrap_coupon->get_code() ] = $multiplier; 
  382. //error_log("multiplier " . $wrap_coupon->get_code() . " = " . $multiplier ); 
  383.  
  384. return true; // VALID!  
  385.  
  386. /** 
  387. * Return the lowest multiplier value 
  388. */ 
  389. private static function min_value( $value, $current_multiplier_value = null ) { 
  390. return ( $current_multiplier_value === null || $value < $current_multiplier_value ) ? $value : $current_multiplier_value; 
  391.  
  392. /** 
  393. * The amount of times the minimum spend / quantity / subtotal values are reached 
  394. * @return int 1 or more if coupon is valid, otherwise 0 
  395. */ 
  396. public function get_coupon_multiplier_value( $coupon ) { 
  397. $coupon = WJECF_WC()->get_coupon( $coupon ); 
  398.  
  399. //If coupon validation was not executed, the value is unknown 
  400. if ( ! isset( $this->coupon_multiplier_values[ WJECF_Wrap( $coupon )->get_code() ] ) ) { 
  401. if ( ! $this->coupon_is_valid( true, $coupon ) ) { 
  402. return 0; 
  403. $multiplier = $this->coupon_multiplier_values[ WJECF_Wrap( $coupon )->get_code() ]; 
  404.  
  405. //error_log("get multiplier " . WJECF_Wrap( $coupon )->get_code() . " = " . $multiplier ); 
  406. return $multiplier; 
  407.  
  408. //Temporary storage 
  409. private $coupon_multiplier_values = array(); 
  410.  
  411.  
  412. /** 
  413. * (API FUNCTION) 
  414. * The total amount of the products in the cart that match the coupon restrictions 
  415. * since 2.2.2-b3 
  416. */ 
  417. public function get_quantity_of_matching_products( $coupon ) { 
  418. $coupon = WJECF_WC()->get_coupon( $coupon ); 
  419.  
  420. $qty = 0; 
  421. foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { 
  422. $_product = $cart_item['data'];  
  423. if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) { 
  424. $qty += $cart_item['quantity']; 
  425. return $qty; 
  426.  
  427. /** 
  428. * (API FUNCTION) 
  429. * The total value of the products in the cart that match the coupon restrictions 
  430. * since 2.2.2-b3 
  431. */ 
  432. public function get_subtotal_of_matching_products( $coupon ) { 
  433. $coupon = WJECF_WC()->get_coupon( $coupon ); 
  434.  
  435. $subtotal = 0; 
  436. foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { 
  437. $_product = $cart_item['data']; 
  438. if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) { 
  439. $subtotal += $_product->get_price() * $cart_item['quantity']; 
  440. return $subtotal; 
  441.  
  442. /** 
  443. * (API FUNCTION) 
  444. * Test if coupon is valid for the product  
  445. * (this function is used to count the quantity of matching products) 
  446. */ 
  447. public function coupon_is_valid_for_product( $coupon, $product, $values = array() ) { 
  448. //Do not count the free products 
  449. if ( isset( $values['_wjecf_free_product_coupon'] ) ) { 
  450. return false; 
  451.  
  452. if ( ! $coupon->is_type( WJECF_WC()->wc_get_cart_coupon_types() ) ) { 
  453. return $coupon->is_valid_for_product( $product, $values );  
  454.  
  455. //$coupon->is_valid_for_product() only works for fixed_product or percent_product discounts 
  456. //It's not, so we create a temporary duplicate 
  457. $duplicate_coupon = WJECF_WC()->get_coupon( WJECF_Wrap( $coupon )->get_code() ); 
  458. WJECF_Wrap( $duplicate_coupon )->set_discount_type( 'fixed_product' ); 
  459. return $duplicate_coupon->is_valid_for_product( $product, $values ); 
  460.  
  461.  
  462.  
  463. // ===================== 
  464.  
  465. /** 
  466. * Get array of the selected shipping methods ids. 
  467. * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object 
  468. * @return array Id's of the shipping methods or an empty array. 
  469. */  
  470. public function get_coupon_shipping_method_ids( $coupon ) { 
  471. $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_shipping_methods' ); 
  472. if ($v == '') { 
  473. $v = array(); 
  474.  
  475. return $v; 
  476.  
  477. /** 
  478. * Get array of the selected payment method ids. 
  479. * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object 
  480. * @return array Id's of the payment methods or an empty array. 
  481. */  
  482. public function get_coupon_payment_method_ids( $coupon ) { 
  483. $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_payment_methods' ); 
  484. if ($v == '') { 
  485. $v = array(); 
  486.  
  487. return $v; 
  488.  
  489. /** 
  490. * Get array of the selected customer ids. 
  491. * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object 
  492. * @return array Id's of the customers (users) or an empty array. 
  493. */  
  494. public function get_coupon_customer_ids( $coupon ) { 
  495. $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_customer_ids' ); 
  496.  
  497. if ($v == '') { 
  498. $v = array(); 
  499. } else { 
  500. $v = array_map( 'intval', explode(", ", $v ) ); 
  501.  
  502. return $v; 
  503.  
  504. /** 
  505. * Get array of the selected customer role ids. 
  506. * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object 
  507. * @return array Id's (string) of the customer roles or an empty array. 
  508. */  
  509. public function get_coupon_customer_roles( $coupon ) { 
  510. $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_customer_roles' ); 
  511. if ($v == '') { 
  512. $v = array(); 
  513.  
  514. return $v; 
  515. }  
  516.  
  517. /** 
  518. * Get array of the excluded customer role ids. 
  519. * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object 
  520. * @return array Id's (string) of the excluded customer roles or an empty array. 
  521. */  
  522. public function get_coupon_excluded_customer_roles( $coupon ) { 
  523. $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_excluded_customer_roles' ); 
  524. if ($v == '') { 
  525. $v = array(); 
  526.  
  527. return $v; 
  528. }  
  529.  
  530. public function is_pro() { 
  531. return $this instanceof WJECF_Pro_Controller; 
  532.  
  533. // =========================================================================== 
  534. // START - OVERWRITE INFO MESSAGES 
  535. // =========================================================================== 
  536.  
  537. /** 
  538. * 2.3.4 
  539. * If a 'Coupon applied' message is displayed by WooCommerce, replace it by another message (or no message) 
  540. * @param WC_Coupon $coupon The coupon to replace the message for 
  541. * @param string $new_message The new message. Set to empty string if no message must be displayed 
  542. */ 
  543. public function start_overwrite_success_message( $coupon, $new_message = '' ) { 
  544. $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ] = array( $coupon->get_coupon_message( WC_Coupon::WC_COUPON_SUCCESS ) => $new_message ); 
  545. add_filter( 'woocommerce_coupon_message', array( $this, 'filter_woocommerce_coupon_message' ), 10, 3 ); 
  546.  
  547. /** 
  548. * 2.3.4 
  549. * Stop overwriting messages 
  550. */ 
  551. public function stop_overwrite_success_message() { 
  552. remove_filter( 'woocommerce_coupon_message', array( $this, 'filter_woocommerce_coupon_message' ), 10 ); 
  553. $this->overwrite_coupon_message = array(); 
  554.  
  555. private $overwrite_coupon_message = array(); /** [ 'coupon_code' => [ old_message' => 'new_message' ] ] */ 
  556.  
  557. function filter_woocommerce_coupon_message( $msg, $msg_code, $coupon ) { 
  558. if ( isset( $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ][ $msg ] ) ) { 
  559. $msg = $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ][ $msg ]; 
  560. return $msg; 
  561.  
  562. // =========================================================================== 
  563. // END - OVERWRITE INFO MESSAGES 
  564. // =========================================================================== 
  565.  
  566.  
  567. private $_session_data = null; 
  568. /** 
  569. * Read something from the session 
  570. * @param string $key The key for identification 
  571. * @param any $default The default value (Default: false) 
  572. *  
  573. * @return The saved value if found, otherwise the default value 
  574. */ 
  575. public function get_session( $key, $default = false ) { 
  576. if ( $this->_session_data == null) { 
  577. $this->_session_data = WC()->session->get( '_wjecf_session_data', array() ); 
  578. return isset( $this->_session_data[ $key ] ) ? $this->_session_data[ $key ] : $default; 
  579.  
  580. /** 
  581. * Save something in the session 
  582. *  
  583. * @param string $key The key for identification 
  584. * @param anything $value The value to store 
  585. */ 
  586. public function set_session( $key, $value ) { 
  587. if ( $this->_session_data == null) { 
  588. $this->_session_data = WC()->session->get( '_wjecf_session_data', array() ); 
  589. $this->_session_data[ $key ] = $value; 
  590. if ( $value !== null ) { 
  591. WC()->session->set( '_wjecf_session_data', $this->_session_data ); 
  592. } else { 
  593.  
  594.  
  595.  
  596. /** 
  597. * Get overwritable template filename 
  598. * @param string $template_name  
  599. * @return string Template filename 
  600. */ 
  601. public function get_template_filename( $template_name ) { 
  602. $template_path = 'woocommerce-auto-added-coupons'; 
  603.  
  604. $plugin_template_path = plugin_dir_path( dirname(__FILE__) ) . 'templates/'; 
  605.  
  606. //Get template overwritten file 
  607. $template = locate_template( trailingslashit( $template_path ) . $template_name ); 
  608.  
  609. // Get default template 
  610. if ( ! $template ) { 
  611. $template = $plugin_template_path . $template_name; 
  612.  
  613. return $template; 
  614.  
  615. /** 
  616. * Include a template file, either from this plugins directory or overwritten in the themes directory 
  617. * @param type $template_name  
  618. * @return type 
  619. */ 
  620. public function include_template( $template_name, $variables = array() ) { 
  621. extract( $variables ); 
  622. include( $this->get_template_filename( $template_name ) ); 
  623.  
  624. /** 
  625. * Log message for debugging 
  626. * @param string $string The message to log 
  627. * @param int $skip_backtrace Defaults to 0, amount of items to skip in backtrace to fetch class and method name 
  628. */ 
  629. public function log( $string, $skip_backtrace = 0) { 
  630. if ( $this->debug_mode ) { 
  631. $nth = 1 + $skip_backtrace; 
  632. $bt = debug_backtrace(); 
  633. $class = $bt[$nth]['class']; 
  634. $function = $bt[$nth]['function']; 
  635.  
  636. $row = array( 
  637. 'time' => time(),  
  638. 'class' => $class,  
  639. 'function' => $function,  
  640. 'filter' => current_filter(),  
  641. 'message' => $string,  
  642. ); 
  643. $this->log[] = $row; 
  644. error_log( $row['filter'] . ' ' . $row['class'] . '::' . $row['function'] . ' ' . $row['message'] ); 
  645.  
  646. /** 
  647. * Output the log as html 
  648. */ 
  649. public function render_log() { 
  650. if ( $this->debug_mode && current_user_can( 'manage_options' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) { 
  651. echo "<table class='soft79_wjecf_log'>"; 
  652. foreach( $this->log as $row ) { 
  653. $cells = array( 
  654. date("H:i:s", $row['time']),  
  655. esc_html( $row['filter'] ),  
  656. esc_html( $row['class'] . '::' . $row['function'] ),  
  657. esc_html( $row['message'] ),  
  658. ); 
  659. echo "<tr><td>" . implode( "</td><td>", $cells ) . "</td></tr>"; 
  660. $colspan = isset( $cells ) ? count( $cells ) : 1; 
  661. echo "<tr><td colspan='" . $colspan . "'>Current coupons in cart: " . implode( ", ", WC()->cart->applied_coupons ) . "</td></tr>"; 
  662. echo "</table>"; 
  663.