WooCommerce Extended Coupon Features - Version 2.4.3

Version Description

  • FIX: Customer selector WooCommerce 3.0.0 compatibility
Download this release

Release Info

Developer josk79
Plugin Icon 128x128 WooCommerce Extended Coupon Features
Version 2.4.3
Comparing to
See all releases

Code changes from version 2.4.2.1 to 2.4.3

includes/WJECF_Debug_CLI.php CHANGED
@@ -4,138 +4,138 @@ defined('ABSPATH') or die();
4
 
5
  class WJECF_Debug_CLI extends WP_CLI_Command {
6
 
7
- /**
8
- * Display some debugging information
9
- */
10
- public function debug() {
11
- WP_CLI::log( sprintf("WJECF Version: %s", WJECF()->version ) );
12
-
13
- $coupon = WJECF_WC()->get_coupon('auto-cheapest-item');
14
-
15
- $args = array(
16
- 'posts_per_page' => -1,
17
- 'orderby' => 'title',
18
- 'order' => 'asc',
19
- 'post_type' => 'shop_coupon',
20
- 'post_status' => 'publish',
21
- );
22
- $posts = get_posts( $args );
23
- foreach ( $posts as $post ) {
24
- $coupon = WJECF_WC()->get_coupon( $post->ID );
25
- $this->execute_test_for_coupon( $coupon );
26
- }
27
-
28
- $args = array(
29
- 'posts_per_page' => -1,
30
- 'orderby' => 'title',
31
- 'order' => 'asc',
32
- 'post_type' => array( 'product', 'product_variation' ),
33
- 'post_status' => 'publish',
34
- );
35
- $posts = get_posts( $args );
36
- foreach ( $posts as $post ) {
37
- $product = wc_get_product( $post->ID );
38
- $this->execute_test_for_product( $product );
39
- }
40
-
41
- }
42
-
43
- protected function execute_test_for_coupon( $coupon ) {
44
- //WP_CLI::log( sprintf("Coupon fields: %s", print_r( $coupon->coupon_custom_fields ) ) );
45
- //WP_CLI::log( sprintf("Coupon fields: %s", print_r( $coupon->get_meta_data() ) ) );
46
-
47
- $meta_keys = array_keys( $coupon->coupon_custom_fields );
48
- $meta_keys[] = '__non_existing__';
49
-
50
- //WP_CLI::log( sprintf("Coupon fields: %s", print_r( $coupon->coupon_custom_fields ) ) );
51
- //WP_CLI::log( sprintf("Coupon fields: %s", print_r( $coupon->get_meta_data() ) ) );
52
-
53
- $wrap_leg = WJECF_WC()->wrap( $coupon, false ); $wrap_leg->use_wc27 = false;
54
- $wrap_new = WJECF_WC()->wrap( $coupon, false ); $wrap_new->use_wc27 = true;
55
-
56
- $results = array();
57
- $results['new'] = $wrap_new->get_id();
58
- $results['legacy'] = $wrap_leg->get_id();
59
- $results['old'] = $coupon->id;
60
- $this->assert_same( $results, sprintf('Same coupon id %s', current( $results ) ) );
61
-
62
- foreach( $meta_keys as $meta_key ) {
63
- for($i=1; $i>=0; $i--) {
64
- $single = $i>0;
65
-
66
- $results = array();
67
- $results['new'] = $wrap_new->get_meta( $meta_key, $single );
68
- $results['legacy'] = $wrap_leg->get_meta( $meta_key, $single );
69
- $results['old'] = get_post_meta( $coupon->id, $meta_key, $single );
70
- $this->assert_same( $results, sprintf('%s: Same value %s', $meta_key, $single ? 'single' : 'multi' ) );
71
-
72
- }
73
- }
74
- }
75
-
76
- protected function execute_test_for_product( $product ) {
77
- $wrap_leg = WJECF_WC()->wrap( $product, false ); $wrap_leg->use_wc27 = false;
78
- $wrap_new = WJECF_WC()->wrap( $product, false ); $wrap_new->use_wc27 = true;
79
-
80
- if ($product instanceof WC_Product_Variation) {
81
- $results = array();
82
- $results['new'] = $wrap_new->get_product_or_variation_id();
83
- $results['legacy'] = $wrap_leg->get_product_or_variation_id();
84
- $results['old'] = $product->variation_id;
85
- $this->assert_same( $results, sprintf('Same variation id %s', current( $results ) ) );
86
-
87
- $results = array();
88
- $results['new'] = $wrap_new->get_variable_product_id();
89
- $results['legacy'] = $wrap_leg->get_variable_product_id();
90
- $results['old'] = $wrap_leg->get_variable_product_id();
91
- $this->assert_same( $results, sprintf('Same variable product (parent) id %s', current( $results ) ) );
92
- } else {
93
- $results = array();
94
- $results['new'] = $wrap_new->get_id();
95
- $results['legacy'] = $wrap_leg->get_id();
96
- $results['old'] = $product->id;
97
- $this->assert_same( $results, sprintf('Same product id %s', current( $results ) ) );
98
- }
99
-
100
-
101
-
102
- }
103
-
104
- protected function assert_same( $results, $test_description ) {
105
- $success = true;
106
- foreach( $results as $result ) {
107
- if ( isset( $prev_result ) && $result !== $prev_result ) {
108
- $success = false;
109
- break;
110
- }
111
- $prev_result = $result;
112
- }
113
-
114
- if ( $success ) {
115
- WP_CLI::success( $test_description );
116
- } else {
117
- foreach( $results as $key => $result ) {
118
- WP_CLI::log( sprintf("%s : %s", $key, $this->dd( $result ) ) );
119
- }
120
- WP_CLI::error( $test_description );
121
- }
122
- }
123
-
124
- protected function dd( $variable ) {
125
- return print_r( $variable, true );
126
- }
127
-
128
-
129
- // /**
130
- // * Display expiry information for the given product.
131
- // *
132
- // * ## OPTIONS
133
- // *
134
- // * <product_id>...
135
- // * : The product ID.
136
- // *
137
- // */
138
- // public function product( $args ) {
139
- // }
140
-
141
  }
4
 
5
  class WJECF_Debug_CLI extends WP_CLI_Command {
6
 
7
+ /**
8
+ * Display some debugging information
9
+ */
10
+ public function debug() {
11
+ WP_CLI::log( sprintf("WJECF Version: %s", WJECF()->version ) );
12
+
13
+ $coupon = WJECF_WC()->get_coupon('auto-cheapest-item');
14
+
15
+ $args = array(
16
+ 'posts_per_page' => -1,
17
+ 'orderby' => 'title',
18
+ 'order' => 'asc',
19
+ 'post_type' => 'shop_coupon',
20
+ 'post_status' => 'publish',
21
+ );
22
+ $posts = get_posts( $args );
23
+ foreach ( $posts as $post ) {
24
+ $coupon = WJECF_WC()->get_coupon( $post->ID );
25
+ $this->execute_test_for_coupon( $coupon );
26
+ }
27
+
28
+ $args = array(
29
+ 'posts_per_page' => -1,
30
+ 'orderby' => 'title',
31
+ 'order' => 'asc',
32
+ 'post_type' => array( 'product', 'product_variation' ),
33
+ 'post_status' => 'publish',
34
+ );
35
+ $posts = get_posts( $args );
36
+ foreach ( $posts as $post ) {
37
+ $product = wc_get_product( $post->ID );
38
+ $this->execute_test_for_product( $product );
39
+ }
40
+
41
+ }
42
+
43
+ protected function execute_test_for_coupon( $coupon ) {
44
+ //WP_CLI::log( sprintf("Coupon fields: %s", print_r( $coupon->coupon_custom_fields ) ) );
45
+ //WP_CLI::log( sprintf("Coupon fields: %s", print_r( $coupon->get_meta_data() ) ) );
46
+
47
+ $meta_keys = array_keys( $coupon->coupon_custom_fields );
48
+ $meta_keys[] = '__non_existing__';
49
+
50
+ //WP_CLI::log( sprintf("Coupon fields: %s", print_r( $coupon->coupon_custom_fields ) ) );
51
+ //WP_CLI::log( sprintf("Coupon fields: %s", print_r( $coupon->get_meta_data() ) ) );
52
+
53
+ $wrap_leg = WJECF_WC()->wrap( $coupon, false ); $wrap_leg->use_wc27 = false;
54
+ $wrap_new = WJECF_WC()->wrap( $coupon, false ); $wrap_new->use_wc27 = true;
55
+
56
+ $results = array();
57
+ $results['new'] = $wrap_new->get_id();
58
+ $results['legacy'] = $wrap_leg->get_id();
59
+ $results['old'] = $coupon->id;
60
+ $this->assert_same( $results, sprintf('Same coupon id %s', current( $results ) ) );
61
+
62
+ foreach( $meta_keys as $meta_key ) {
63
+ for($i=1; $i>=0; $i--) {
64
+ $single = $i>0;
65
+
66
+ $results = array();
67
+ $results['new'] = $wrap_new->get_meta( $meta_key, $single );
68
+ $results['legacy'] = $wrap_leg->get_meta( $meta_key, $single );
69
+ $results['old'] = get_post_meta( $coupon->id, $meta_key, $single );
70
+ $this->assert_same( $results, sprintf('%s: Same value %s', $meta_key, $single ? 'single' : 'multi' ) );
71
+
72
+ }
73
+ }
74
+ }
75
+
76
+ protected function execute_test_for_product( $product ) {
77
+ $wrap_leg = WJECF_WC()->wrap( $product, false ); $wrap_leg->use_wc27 = false;
78
+ $wrap_new = WJECF_WC()->wrap( $product, false ); $wrap_new->use_wc27 = true;
79
+
80
+ if ($product instanceof WC_Product_Variation) {
81
+ $results = array();
82
+ $results['new'] = $wrap_new->get_product_or_variation_id();
83
+ $results['legacy'] = $wrap_leg->get_product_or_variation_id();
84
+ $results['old'] = $product->variation_id;
85
+ $this->assert_same( $results, sprintf('Same variation id %s', current( $results ) ) );
86
+
87
+ $results = array();
88
+ $results['new'] = $wrap_new->get_variable_product_id();
89
+ $results['legacy'] = $wrap_leg->get_variable_product_id();
90
+ $results['old'] = $wrap_leg->get_variable_product_id();
91
+ $this->assert_same( $results, sprintf('Same variable product (parent) id %s', current( $results ) ) );
92
+ } else {
93
+ $results = array();
94
+ $results['new'] = $wrap_new->get_id();
95
+ $results['legacy'] = $wrap_leg->get_id();
96
+ $results['old'] = $product->id;
97
+ $this->assert_same( $results, sprintf('Same product id %s', current( $results ) ) );
98
+ }
99
+
100
+
101
+
102
+ }
103
+
104
+ protected function assert_same( $results, $test_description ) {
105
+ $success = true;
106
+ foreach( $results as $result ) {
107
+ if ( isset( $prev_result ) && $result !== $prev_result ) {
108
+ $success = false;
109
+ break;
110
+ }
111
+ $prev_result = $result;
112
+ }
113
+
114
+ if ( $success ) {
115
+ WP_CLI::success( $test_description );
116
+ } else {
117
+ foreach( $results as $key => $result ) {
118
+ WP_CLI::log( sprintf("%s : %s", $key, $this->dd( $result ) ) );
119
+ }
120
+ WP_CLI::error( $test_description );
121
+ }
122
+ }
123
+
124
+ protected function dd( $variable ) {
125
+ return print_r( $variable, true );
126
+ }
127
+
128
+
129
+ // /**
130
+ // * Display expiry information for the given product.
131
+ // *
132
+ // * ## OPTIONS
133
+ // *
134
+ // * <product_id>...
135
+ // * : The product ID.
136
+ // *
137
+ // */
138
+ // public function product( $args ) {
139
+ // }
140
+
141
  }
includes/abstract-wjecf-plugin.php CHANGED
@@ -12,15 +12,15 @@ abstract class Abstract_WJECF_Plugin {
12
  * @return void
13
  */
14
  public function assert_dependencies() {
15
- foreach( $this->get_plugin_dependencies() as $dependency ) {
16
- if ( ! isset( $this->plugins[ $dependency ] ) ) {
17
- throw new Exception( sprintf( 'Missing dependency %s', $dependency) );
18
- }
19
- }
20
-
21
- if ( ! empty ( $this->plugin_data['required_version_wjecf'] ) ) {
22
- $this->assert_wjecf_version( $this->plugin_data['required_version_wjecf'] );
23
- }
24
  }
25
 
26
  /**
@@ -29,68 +29,68 @@ abstract class Abstract_WJECF_Plugin {
29
  * @return void
30
  */
31
  protected function assert_wjecf_version( $required_version ) {
32
- if ( version_compare( WJECF()->version, $required_version, '<' ) ) {
33
- throw new Exception( sprintf( __( 'WooCommerce Extended Coupon Features version %s is required. You have version %s', 'woocommerce-jos-autocoupon' ), $required_version, WJECF()->version ) );
34
- }
35
  }
36
 
37
  //
38
 
39
- /**
40
- * Log a message (for debugging)
41
- *
42
- * @param string $message The message to log
43
- *
44
- */
45
- protected function log ( $message ) {
46
- WJECF()->log( $message, 1 );
47
- }
48
-
49
- private $plugin_data = array();
50
-
51
- /**
52
- * Information about the WJECF plugin
53
- * @param string|null $key The data to look up. Will return an array with all data when omitted
54
- * @return mixed
55
- */
56
- protected function get_plugin_data( $key = null ) {
57
- $default_data = array(
58
- 'description' => '',
59
- 'can_be_disabled' => false,
60
- 'dependencies' => array(),
61
- 'minimal_wjecf_version' => ''
62
- );
63
- $plugin_data = array_merge( $default_data, $this->plugin_data );
64
- if ( $key === null ) {
65
- return $plugin_data;
66
- }
67
- return $plugin_data[$key];
68
- }
69
-
70
- /**
71
- * Set information about the WJECF plugin
72
- * @param array $plugin_data The data for this plugin
73
- * @return void
74
- */
75
- protected function set_plugin_data( $plugin_data ) {
76
- $this->plugin_data = $plugin_data;
77
- }
78
 
79
  /**
80
  * Get the description if this WJECF plugin.
81
  * @return string
82
  */
83
- public function get_plugin_description() {
84
- return $this->get_plugin_data( 'description' );
85
- }
86
 
87
  /**
88
  * Get the class name of this WJECF plugin.
89
  * @return string
90
  */
91
- public function get_plugin_class_name() {
92
- return get_class( $this );
93
- }
94
 
95
  public function get_plugin_dependencies() {
96
  return $this->get_plugin_data( 'dependencies' );
12
  * @return void
13
  */
14
  public function assert_dependencies() {
15
+ foreach( $this->get_plugin_dependencies() as $dependency ) {
16
+ if ( ! isset( $this->plugins[ $dependency ] ) ) {
17
+ throw new Exception( sprintf( 'Missing dependency %s', $dependency) );
18
+ }
19
+ }
20
+
21
+ if ( ! empty ( $this->plugin_data['required_version_wjecf'] ) ) {
22
+ $this->assert_wjecf_version( $this->plugin_data['required_version_wjecf'] );
23
+ }
24
  }
25
 
26
  /**
29
  * @return void
30
  */
31
  protected function assert_wjecf_version( $required_version ) {
32
+ if ( version_compare( WJECF()->version, $required_version, '<' ) ) {
33
+ throw new Exception( sprintf( __( 'WooCommerce Extended Coupon Features version %s is required. You have version %s', 'woocommerce-jos-autocoupon' ), $required_version, WJECF()->version ) );
34
+ }
35
  }
36
 
37
  //
38
 
39
+ /**
40
+ * Log a message (for debugging)
41
+ *
42
+ * @param string $message The message to log
43
+ *
44
+ */
45
+ protected function log ( $message ) {
46
+ WJECF()->log( $message, 1 );
47
+ }
48
+
49
+ private $plugin_data = array();
50
+
51
+ /**
52
+ * Information about the WJECF plugin
53
+ * @param string|null $key The data to look up. Will return an array with all data when omitted
54
+ * @return mixed
55
+ */
56
+ protected function get_plugin_data( $key = null ) {
57
+ $default_data = array(
58
+ 'description' => '',
59
+ 'can_be_disabled' => false,
60
+ 'dependencies' => array(),
61
+ 'minimal_wjecf_version' => ''
62
+ );
63
+ $plugin_data = array_merge( $default_data, $this->plugin_data );
64
+ if ( $key === null ) {
65
+ return $plugin_data;
66
+ }
67
+ return $plugin_data[$key];
68
+ }
69
+
70
+ /**
71
+ * Set information about the WJECF plugin
72
+ * @param array $plugin_data The data for this plugin
73
+ * @return void
74
+ */
75
+ protected function set_plugin_data( $plugin_data ) {
76
+ $this->plugin_data = $plugin_data;
77
+ }
78
 
79
  /**
80
  * Get the description if this WJECF plugin.
81
  * @return string
82
  */
83
+ public function get_plugin_description() {
84
+ return $this->get_plugin_data( 'description' );
85
+ }
86
 
87
  /**
88
  * Get the class name of this WJECF plugin.
89
  * @return string
90
  */
91
+ public function get_plugin_class_name() {
92
+ return get_class( $this );
93
+ }
94
 
95
  public function get_plugin_dependencies() {
96
  return $this->get_plugin_data( 'dependencies' );
includes/admin/wjecf-admin-auto-upgrade.php CHANGED
@@ -1,78 +1,78 @@
1
  <?php
2
 
3
  class WJECF_Admin_Auto_Upgrade extends Abstract_WJECF_Plugin {
4
-
5
- public function __construct() {
6
- $this->set_plugin_data( array(
7
- 'description' => __( 'Automatically upgrade data when a new version of this plugin is installed.', 'woocommerce-jos-autocoupon' ),
8
- 'dependencies' => array(),
9
- 'can_be_disabled' => true
10
- ) );
11
- }
12
 
13
- public function init_admin_hook() {
14
- $this->auto_upgrade();
15
- }
16
 
17
- //Upgrade options on version change
18
- public function auto_upgrade() {
19
- if ( ! class_exists('WC_Coupon') ) {
20
- return;
21
- }
22
-
23
- //WJECF()->options['db_version'] = 1;update_option( 'wjecf_options' , WJECF()->options, false ); // Will force all upgrades
24
- global $wpdb;
25
- $prev_version = WJECF()->options['db_version'];
26
-
27
- //DB_VERSION 1: Since 2.1.0-b5
28
- if ( WJECF()->options['db_version'] < 1 ) {
29
- //RENAME meta_key _wjecf_matching_product_qty TO _wjecf_min_matching_product_qty
30
  $where = array("meta_key" => "_wjecf_matching_product_qty");
31
- $set = array('meta_key' => "_wjecf_min_matching_product_qty");
32
- $wpdb->update( _get_meta_table('post'), $set, $where );
33
-
34
- //RENAME meta_key woocommerce-jos-autocoupon TO _wjecf_is_auto_coupon
35
  $where = array("meta_key" => "woocommerce-jos-autocoupon");
36
- $set = array('meta_key' => "_wjecf_is_auto_coupon");
37
- $wpdb->update( _get_meta_table('post'), $set, $where );
38
- //Now we're version 1
39
- WJECF()->options['db_version'] = 1;
40
- } //DB VERSION 1
41
 
42
- //DB_VERSION 2: Since 2.3.3-b3 No changes; but used to omit message if 2.3.3-b3 has been installed before
43
- if ( WJECF()->options['db_version'] < 2 ) {
44
- WJECF()->options['db_version'] = 2;
45
- }
46
 
47
- if ( WJECF()->options['db_version'] > 2 ) {
48
- WJECF_ADMIN()->enqueue_notice( '<p>' . __( '<strong>WooCommerce Extended Coupon Features:</strong> Please note, you\'re using an older version of this plugin, while the data was upgraded to a newer version.' , 'woocommerce-jos-autocoupon' ) . '</p>', 'notice-warning');
49
- }
50
 
51
- //An upgrade took place?
52
- if ( WJECF()->options['db_version'] != $prev_version ) {
53
- // Set version and write options to database
54
- update_option( 'wjecf_options' , WJECF()->options, false );
55
 
56
- WJECF_ADMIN()->enqueue_notice( '<p>' . __( '<strong>WooCommerce Extended Coupon Features:</strong> Data succesfully upgraded to the newest version.', 'woocommerce-jos-autocoupon' ) . '</p>', 'notice-success');
57
- }
58
- }
59
 
60
- // function admin_notice_allow_cart_excluded() {
61
 
62
- // if( ! empty( $this->admin_notice_allow_cart_excluded_couponcodes ) )
63
- // {
64
- // $html = '<div class="notice notice-warning">';
65
- // $html .= '<p>';
66
- // $html .= __( '<strong>WooCommerce Extended Coupon Features:</strong> The following coupons use the deprecated option \'Allow discount on cart with excluded items\'. Please review and save them.', 'woocommerce-jos-autocoupon' );
67
- // $html .= '<ul>';
68
- // foreach( $this->admin_notice_allow_cart_excluded_couponcodes as $post_id => $coupon_code ) {
69
- // $html .= '<li><a class="post-edit-link" href="' . esc_url( get_edit_post_link( $post_id ) ) . '">' . $coupon_code . '</a></li>';
70
- // }
71
- // $html .= '</ul>';
72
- // $html .= '</p>';
73
- // $html .= '</div>';
74
- // echo $html;
75
- // }
76
- // }
77
  }
78
  ?>
1
  <?php
2
 
3
  class WJECF_Admin_Auto_Upgrade extends Abstract_WJECF_Plugin {
4
+
5
+ public function __construct() {
6
+ $this->set_plugin_data( array(
7
+ 'description' => __( 'Automatically upgrade data when a new version of this plugin is installed.', 'woocommerce-jos-autocoupon' ),
8
+ 'dependencies' => array(),
9
+ 'can_be_disabled' => true
10
+ ) );
11
+ }
12
 
13
+ public function init_admin_hook() {
14
+ $this->auto_upgrade();
15
+ }
16
 
17
+ //Upgrade options on version change
18
+ public function auto_upgrade() {
19
+ if ( ! class_exists('WC_Coupon') ) {
20
+ return;
21
+ }
22
+
23
+ //WJECF()->options['db_version'] = 1;update_option( 'wjecf_options' , WJECF()->options, false ); // Will force all upgrades
24
+ global $wpdb;
25
+ $prev_version = WJECF()->options['db_version'];
26
+
27
+ //DB_VERSION 1: Since 2.1.0-b5
28
+ if ( WJECF()->options['db_version'] < 1 ) {
29
+ //RENAME meta_key _wjecf_matching_product_qty TO _wjecf_min_matching_product_qty
30
  $where = array("meta_key" => "_wjecf_matching_product_qty");
31
+ $set = array('meta_key' => "_wjecf_min_matching_product_qty");
32
+ $wpdb->update( _get_meta_table('post'), $set, $where );
33
+
34
+ //RENAME meta_key woocommerce-jos-autocoupon TO _wjecf_is_auto_coupon
35
  $where = array("meta_key" => "woocommerce-jos-autocoupon");
36
+ $set = array('meta_key' => "_wjecf_is_auto_coupon");
37
+ $wpdb->update( _get_meta_table('post'), $set, $where );
38
+ //Now we're version 1
39
+ WJECF()->options['db_version'] = 1;
40
+ } //DB VERSION 1
41
 
42
+ //DB_VERSION 2: Since 2.3.3-b3 No changes; but used to omit message if 2.3.3-b3 has been installed before
43
+ if ( WJECF()->options['db_version'] < 2 ) {
44
+ WJECF()->options['db_version'] = 2;
45
+ }
46
 
47
+ if ( WJECF()->options['db_version'] > 2 ) {
48
+ WJECF_ADMIN()->enqueue_notice( '<p>' . __( '<strong>WooCommerce Extended Coupon Features:</strong> Please note, you\'re using an older version of this plugin, while the data was upgraded to a newer version.' , 'woocommerce-jos-autocoupon' ) . '</p>', 'notice-warning');
49
+ }
50
 
51
+ //An upgrade took place?
52
+ if ( WJECF()->options['db_version'] != $prev_version ) {
53
+ // Set version and write options to database
54
+ update_option( 'wjecf_options' , WJECF()->options, false );
55
 
56
+ WJECF_ADMIN()->enqueue_notice( '<p>' . __( '<strong>WooCommerce Extended Coupon Features:</strong> Data succesfully upgraded to the newest version.', 'woocommerce-jos-autocoupon' ) . '</p>', 'notice-success');
57
+ }
58
+ }
59
 
60
+ // function admin_notice_allow_cart_excluded() {
61
 
62
+ // if( ! empty( $this->admin_notice_allow_cart_excluded_couponcodes ) )
63
+ // {
64
+ // $html = '<div class="notice notice-warning">';
65
+ // $html .= '<p>';
66
+ // $html .= __( '<strong>WooCommerce Extended Coupon Features:</strong> The following coupons use the deprecated option \'Allow discount on cart with excluded items\'. Please review and save them.', 'woocommerce-jos-autocoupon' );
67
+ // $html .= '<ul>';
68
+ // foreach( $this->admin_notice_allow_cart_excluded_couponcodes as $post_id => $coupon_code ) {
69
+ // $html .= '<li><a class="post-edit-link" href="' . esc_url( get_edit_post_link( $post_id ) ) . '">' . $coupon_code . '</a></li>';
70
+ // }
71
+ // $html .= '</ul>';
72
+ // $html .= '</p>';
73
+ // $html .= '</div>';
74
+ // echo $html;
75
+ // }
76
+ // }
77
  }
78
  ?>
includes/admin/wjecf-admin.php CHANGED
@@ -3,70 +3,70 @@
3
  defined('ABSPATH') or die();
4
 
5
  if ( class_exists('WJECF_Admin') ) {
6
- return;
7
  }
8
 
9
  class WJECF_Admin extends Abstract_WJECF_Plugin {
10
-
11
- public function __construct() {
12
- $this->set_plugin_data( array(
13
- 'description' => __( 'Admin interface of WooCommerce Extended Coupon Features.', 'woocommerce-jos-autocoupon' ),
14
- 'dependencies' => array(),
15
- 'can_be_disabled' => false
16
- ) );
17
- }
18
-
19
- public function init_admin_hook() {
20
  add_action( 'admin_notices', array( $this, 'admin_notices'));
21
 
22
- if ( ! WJECF_WC()->check_woocommerce_version('2.3.0') ) {
23
- $this->enqueue_notice( '<p>' .
24
- __( '<strong>WooCommerce Extended Coupon Features:</strong> You are using an old version of WooCommerce. Updating of WooCommerce is recommended as using an outdated version might cause unexpected behaviour in combination with modern plugins.' )
25
- . '</p>', 'notice-warning' );
26
- }
27
- //Admin hooks
28
- add_filter( 'plugin_row_meta', array( $this, 'wjecf_plugin_meta' ), 10, 2 );
29
  add_action( 'admin_head', array( $this, 'on_admin_head'));
30
 
31
- add_filter( 'woocommerce_coupon_data_tabs', array( $this, 'admin_coupon_options_tabs' ), 10, 1);
32
- add_action( 'woocommerce_coupon_data_panels', array( $this, 'admin_coupon_options_panels' ), 10, 0 );
33
- add_action( 'woocommerce_process_shop_coupon_meta', array( $this, 'process_shop_coupon_meta' ), 10, 2 );
34
-
35
- add_action( 'wjecf_coupon_metabox_products', array( $this, 'admin_coupon_metabox_products' ), 10, 2 );
36
- add_action( 'wjecf_coupon_metabox_checkout', array( $this, 'admin_coupon_metabox_checkout' ), 10, 2 );
37
- add_action( 'wjecf_coupon_metabox_customer', array( $this, 'admin_coupon_metabox_customer' ), 10, 2 );
38
- add_action( 'wjecf_coupon_metabox_misc', array( $this, 'admin_coupon_metabox_misc' ), 10, 2 );
39
 
40
- WJECF_ADMIN()->add_inline_style( '
41
- #woocommerce-coupon-data .wjecf-not-wide { width:50% }
42
- ');
43
 
44
  //WORK IN PROGRESS: add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
45
- }
46
 
47
  // ===========================================================================
48
  // START - ADMIN NOTICES
49
  // Allows notices to be displayed on the admin pages
50
  // ===========================================================================
51
 
52
- private $notices = array();
53
-
54
- /**
55
- * Enqueue a notice to display on the admin page
56
- * @param stirng $html Please embed in <p> tags
57
- * @param string $class
58
- */
59
- public function enqueue_notice( $html, $class = 'notice-info' ) {
60
- $this->notices[] = array( 'class' => $class, 'html' => $html );
61
- }
62
-
63
- public function admin_notices() {
64
- foreach( $this->notices as $notice ) {
65
- echo '<div class="notice ' . $notice['class'] . '">';
66
- echo $notice['html'];
67
- echo '</div>';
68
- }
69
- }
70
 
71
  // ===========================================================================
72
  // END - ADMIN NOTICES
@@ -90,447 +90,438 @@ class WJECF_Admin extends Abstract_WJECF_Plugin {
90
  <ul>
91
  <?php
92
  foreach ( WJECF()->get_plugins() as $name => $plugin ) {
93
- echo "<li><h3>" . $plugin->get_plugin_class_name() . "</h3>\n";
94
- echo "<p>" . $plugin->get_plugin_description() . "</p>\n";
95
- echo "</li>\n";
96
  }
97
  ?>
98
- </ul>
99
  <?php
100
 
101
  }
102
 
103
- //2.3.6 Inline css
104
- private $admin_css = '';
105
-
106
- /**
107
- * 2.3.6
108
- * @return void
109
- */
110
- function on_admin_head() {
111
- //Output inline style for the admin pages
112
- if ( ! empty( $this->admin_css ) ) {
113
- echo '<style type="text/css">' . $this->admin_css . '</style>';
114
- $this->admin_css = '';
115
- }
116
-
117
- //Enqueue scripts
118
- wp_enqueue_script( "wjecf-admin", WJECF()->plugin_url . "assets/js/wjecf-admin.js", array( 'jquery' ), WJECF()->version );
119
- wp_localize_script( 'wjecf-admin', 'wjecf_admin_i18n', array(
120
- 'label_and' => __( '(AND)', 'woocommerce-jos-autocoupon' ),
121
- 'label_or' => __( '(OR)', 'woocommerce-jos-autocoupon' )
122
- ) );
123
-
124
- }
125
-
126
-
127
- //Add tabs to the coupon option page
128
- public function admin_coupon_options_tabs( $tabs ) {
129
-
130
- $tabs['extended_features_products'] = array(
131
- 'label' => __( 'Products', 'woocommerce-jos-autocoupon' ),
132
- 'target' => 'wjecf_coupondata_products',
133
- 'class' => 'wjecf_coupondata_products',
134
- );
135
-
136
- $tabs['extended_features_checkout'] = array(
137
- 'label' => __( 'Checkout', 'woocommerce-jos-autocoupon' ),
138
- 'target' => 'wjecf_coupondata_checkout',
139
- 'class' => 'wjecf_coupondata_checkout',
140
- );
141
-
142
- $tabs['extended_features_misc'] = array(
143
- 'label' => __( 'Miscellaneous', 'woocommerce-jos-autocoupon' ),
144
- 'target' => 'wjecf_coupondata_misc',
145
- 'class' => 'wjecf_coupondata_misc',
146
- );
147
-
148
- return $tabs;
149
- }
150
-
151
- //Add panels to the coupon option page
152
- public function admin_coupon_options_panels() {
153
- global $thepostid, $post;
154
- $thepostid = empty( $thepostid ) ? $post->ID : $thepostid;
155
- ?>
156
- <div id="wjecf_coupondata_products" class="panel woocommerce_options_panel">
157
- <?php
158
- //Feed the panel with options
159
- do_action( 'wjecf_coupon_metabox_products', $thepostid, $post );
160
- $this->admin_coupon_data_footer();
161
- ?>
162
- </div>
163
- <div id="wjecf_coupondata_checkout" class="panel woocommerce_options_panel">
164
- <?php
165
- do_action( 'wjecf_coupon_metabox_checkout', $thepostid, $post );
166
- do_action( 'wjecf_coupon_metabox_customer', $thepostid, $post );
167
- $this->admin_coupon_data_footer();
168
- ?>
169
- </div>
170
- <div id="wjecf_coupondata_misc" class="panel woocommerce_options_panel">
171
- <?php
172
- //Allow other classes to inject options
173
- do_action( 'wjecf_woocommerce_coupon_options_extended_features', $thepostid, $post );
174
- do_action( 'wjecf_coupon_metabox_misc', $thepostid, $post );
175
- $this->admin_coupon_data_footer();
176
- ?>
177
- </div>
178
- <?php
179
- }
180
-
181
- public function admin_coupon_data_footer() {
182
- $documentation_url = plugins_url( 'docs/index.html', dirname( __FILE__ ) );
183
- if ( ! WJECF()->is_pro() ) {
184
- $documentation_url = 'http://www.soft79.nl/documentation/wjecf';
185
- ?>
186
- <h3><?php _e( 'Do you find WooCommerce Extended Coupon Features useful?', 'woocommerce-jos-autocoupon'); ?></h3>
187
- <p class="form-field"><label for="wjecf_donate_button"><?php
188
- echo esc_html( __('Express your gratitude', 'woocommerce-jos-autocoupon' ) );
189
- ?></label>
190
- <a id="wjecf_donate_button" href="<?php echo $this->get_donate_url(); ?>" target="_blank" class="button button-primary">
191
- <?php
192
- echo esc_html( __('Donate to the developer', 'woocommerce-jos-autocoupon' ) );
193
- ?></a><br>
194
- Or get the PRO version at <a href="http://www.soft79.nl" target="_blank">www.soft79.nl</a>.
195
- </p>
196
- <?php
197
- }
198
- //Documentation link
199
- echo '<h3>' . __( 'Documentation', 'woocommerce-jos-autocoupon' ) . '</h3>';
200
- echo '<p><a href="' . $documentation_url . '" target="_blank">' .
201
- __( 'WooCommerce Extended Coupon Features Documentation', 'woocommerce-jos-autocoupon' ) . '</a></p>';
202
-
203
- }
204
-
205
- //Tab 'extended features'
206
- public function admin_coupon_metabox_products( $thepostid, $post ) {
207
- //See WooCommerce class-wc-meta-box-coupon-data.php function ouput
208
-
209
- echo "<h3>" . esc_html( __( 'Matching products', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
210
- //=============================
211
- // AND instead of OR the products
212
- $this->render_select_with_default( array(
213
- 'id' => '_wjecf_products_and',
214
- 'label' => __( 'Products Operator', 'woocommerce-jos-autocoupon' ),
215
- 'options' => array( 'no' => __( 'OR', 'woocommerce-jos-autocoupon' ), 'yes' => __( 'AND', 'woocommerce-jos-autocoupon' ) ),
216
- 'default_value' => 'no',
217
- 'class' => 'wjecf-not-wide',
218
- /* translators: OLD TEXT: 'Check this box if ALL of the products (see tab \'usage restriction\') must be in the cart to use this coupon (instead of only one of the products).' */
219
- 'description' => __( 'Use AND if ALL of the products must be in the cart to use this coupon (instead of only one of the products).', 'woocommerce-jos-autocoupon' ),
220
- 'desc_tip' => true
221
- ) );
222
-
223
- //=============================
224
- // 2.2.3.1 AND instead of OR the categories
225
- $this->render_select_with_default( array(
226
- 'id' => '_wjecf_categories_and',
227
- 'label' => __( 'Categories Operator', 'woocommerce-jos-autocoupon' ),
228
- 'options' => array( 'no' => __( 'OR', 'woocommerce-jos-autocoupon' ), 'yes' => __( 'AND', 'woocommerce-jos-autocoupon' ) ),
229
- 'default_value' => 'no',
230
- 'class' => 'wjecf-not-wide',
231
- /* translators: OLD TEXT: 'Check this box if products from ALL of the categories (see tab \'usage restriction\') must be in the cart to use this coupon (instead of only one from one of the categories).' */
232
- 'description' => __( 'Use AND if products from ALL of the categories must be in the cart to use this coupon (instead of only one from one of the categories).', 'woocommerce-jos-autocoupon' ),
233
- 'desc_tip' => true
234
- ) );
235
-
236
-
237
-
238
- // Minimum quantity of matching products (product/category)
239
- woocommerce_wp_text_input( array(
240
- 'id' => '_wjecf_min_matching_product_qty',
241
- 'label' => __( 'Minimum quantity of matching products', 'woocommerce-jos-autocoupon' ),
242
- 'placeholder' => __( 'No minimum', 'woocommerce' ),
243
- 'description' => __( 'Minimum quantity of the products that match the given product or category restrictions (see tab \'usage restriction\'). If no product or category restrictions are specified, the total number of products is used.', 'woocommerce-jos-autocoupon' ),
244
- 'data_type' => 'decimal',
245
- 'desc_tip' => true
246
- ) );
247
-
248
- // Maximum quantity of matching products (product/category)
249
- woocommerce_wp_text_input( array(
250
- 'id' => '_wjecf_max_matching_product_qty',
251
- 'label' => __( 'Maximum quantity of matching products', 'woocommerce-jos-autocoupon' ),
252
- 'placeholder' => __( 'No maximum', 'woocommerce' ),
253
- 'description' => __( 'Maximum quantity of the products that match the given product or category restrictions (see tab \'usage restriction\'). If no product or category restrictions are specified, the total number of products is used.', 'woocommerce-jos-autocoupon' ),
254
- 'data_type' => 'decimal',
255
- 'desc_tip' => true
256
- ) );
257
-
258
- // Minimum subtotal of matching products (product/category)
259
- woocommerce_wp_text_input( array(
260
- 'id' => '_wjecf_min_matching_product_subtotal',
261
- 'label' => __( 'Minimum subtotal of matching products', 'woocommerce-jos-autocoupon' ),
262
- 'placeholder' => __( 'No minimum', 'woocommerce' ),
263
- 'description' => __( 'Minimum price subtotal of the products that match the given product or category restrictions (see tab \'usage restriction\').', 'woocommerce-jos-autocoupon' ),
264
- 'data_type' => 'price',
265
- 'desc_tip' => true
266
- ) );
267
-
268
- // Maximum subtotal of matching products (product/category)
269
- woocommerce_wp_text_input( array(
270
- 'id' => '_wjecf_max_matching_product_subtotal',
271
- 'label' => __( 'Maximum subtotal of matching products', 'woocommerce-jos-autocoupon' ),
272
- 'placeholder' => __( 'No maximum', 'woocommerce' ),
273
- 'description' => __( 'Maximum price subtotal of the products that match the given product or category restrictions (see tab \'usage restriction\').', 'woocommerce-jos-autocoupon' ),
274
- 'data_type' => 'price',
275
- 'desc_tip' => true
276
- ) );
277
- }
278
-
279
- public function admin_coupon_metabox_checkout( $thepostid, $post ) {
280
-
281
- echo "<h3>" . esc_html( __( 'Checkout', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
282
-
283
- //=============================
284
- // Shipping methods
285
- ?>
286
- <p class="form-field"><label for="wjecf_shipping_methods"><?php _e( 'Shipping methods', 'woocommerce-jos-autocoupon' ); ?></label>
287
- <select id="wjecf_shipping_methods" name="wjecf_shipping_methods[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any shipping method', 'woocommerce-jos-autocoupon' ); ?>">
288
- <?php
289
- $coupon_shipping_method_ids = WJECF()->get_coupon_shipping_method_ids( $thepostid );
290
- $shipping_methods = WC()->shipping->load_shipping_methods();
291
-
292
- if ( $shipping_methods ) foreach ( $shipping_methods as $shipping_method ) {
293
- echo '<option value="' . esc_attr( $shipping_method->id ) . '"' . selected( in_array( $shipping_method->id, $coupon_shipping_method_ids ), true, false ) . '>' . esc_html( $shipping_method->method_title ) . '</option>';
294
- }
295
- ?>
296
- </select><?php echo WJECF_WC()->wc_help_tip( __( 'One of these shipping methods must be selected in order for this coupon to be valid.', 'woocommerce-jos-autocoupon' ) ); ?>
297
- </p>
298
- <?php
299
-
300
- //=============================
301
- // Payment methods
302
- ?>
303
- <p class="form-field"><label for="wjecf_payment_methods"><?php _e( 'Payment methods', 'woocommerce-jos-autocoupon' ); ?></label>
304
- <select id="wjecf_payment_methods" name="wjecf_payment_methods[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any payment method', 'woocommerce-jos-autocoupon' ); ?>">
305
- <?php
306
- $coupon_payment_method_ids = WJECF()->get_coupon_payment_method_ids( $thepostid );
307
- //DONT USE WC()->payment_gateways->available_payment_gateways() AS IT CAN CRASH IN UNKNOWN OCCASIONS
308
- $payment_methods = WC()->payment_gateways->payment_gateways();
309
- if ( $payment_methods ) foreach ( $payment_methods as $payment_method ) {
310
- if ('yes' === $payment_method->enabled) {
311
- echo '<option value="' . esc_attr( $payment_method->id ) . '"' . selected( in_array( $payment_method->id, $coupon_payment_method_ids ), true, false ) . '>' . esc_html( $payment_method->title ) . '</option>';
312
- }
313
- }
314
- ?>
315
- </select><?php echo WJECF_WC()->wc_help_tip( __( 'One of these payment methods must be selected in order for this coupon to be valid.', 'woocommerce-jos-autocoupon' ) ); ?>
316
- </p>
317
- <?php
318
- }
319
-
320
- public function admin_coupon_metabox_customer( $thepostid, $post ) {
321
-
322
- //=============================
323
- //Title: "CUSTOMER RESTRICTIONS"
324
- echo "<h3>" . esc_html( __( 'Customer restrictions', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
325
- echo "<p><span class='description'>" . __( 'If both a customer and a role restriction are supplied, matching either one of them will suffice.' , 'woocommerce-jos-autocoupon' ) . "</span></p>\n";
326
-
327
- //=============================
328
- // User ids
329
- ?>
330
- <p class="form-field"><label><?php _e( 'Allowed Customers', 'woocommerce-jos-autocoupon' ); ?></label>
331
- <input type="hidden" class="wc-customer-search" data-multiple="true" style="width: 50%;" name="wjecf_customer_ids" data-placeholder="<?php _e( 'Any customer', 'woocommerce-jos-autocoupon' ); ?>" data-action="woocommerce_json_search_customers" data-selected="<?php
332
- $coupon_customer_ids = WJECF()->get_coupon_customer_ids( $thepostid );
333
- $json_ids = array();
334
-
335
- foreach ( $coupon_customer_ids as $customer_id ) {
336
- $customer = get_userdata( $customer_id );
337
- if ( is_object( $customer ) ) {
338
- $json_ids[ $customer_id ] = $customer->display_name . ' (#' . $customer->ID . ' &ndash; ' . sanitize_email( $customer->user_email ) . ')';
339
- }
340
- }
341
-
342
- echo esc_attr( json_encode( $json_ids ) );
343
- ?>" value="<?php echo implode( ',', array_keys( $json_ids ) ); ?>" />
344
- <?php echo WJECF_WC()->wc_help_tip( __( 'Only these customers may use this coupon.', 'woocommerce-jos-autocoupon' ) ); ?>
345
- </p>
346
- <?php
347
-
348
- //=============================
349
- // User roles
350
- ?>
351
- <p class="form-field"><label for="wjecf_customer_roles"><?php _e( 'Allowed User Roles', 'woocommerce-jos-autocoupon' ); ?></label>
352
- <select id="wjecf_customer_roles" name="wjecf_customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any role', 'woocommerce-jos-autocoupon' ); ?>">
353
- <?php
354
- $coupon_customer_roles = WJECF()->get_coupon_customer_roles( $thepostid );
355
-
356
- $available_customer_roles = array_reverse( get_editable_roles() );
357
- foreach ( $available_customer_roles as $role_id => $role ) {
358
- $role_name = translate_user_role($role['name'] );
359
-
360
- echo '<option value="' . esc_attr( $role_id ) . '"'
361
- . selected( in_array( $role_id, $coupon_customer_roles ), true, false ) . '>'
362
- . esc_html( $role_name ) . '</option>';
363
- }
364
- ?>
365
- </select>
366
- <?php echo WJECF_WC()->wc_help_tip( __( 'Only these User Roles may use this coupon.', 'woocommerce-jos-autocoupon' ) ); ?>
367
- </p>
368
- <?php
369
-
370
- //=============================
371
- // Excluded user roles
372
- ?>
373
- <p class="form-field"><label for="wjecf_excluded_customer_roles"><?php _e( 'Disallowed User Roles', 'woocommerce-jos-autocoupon' ); ?></label>
374
- <select id="wjecf_customer_roles" name="wjecf_excluded_customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any role', 'woocommerce-jos-autocoupon' ); ?>">
375
- <?php
376
- $coupon_excluded_customer_roles = WJECF()->get_coupon_excluded_customer_roles( $thepostid );
377
-
378
- foreach ( $available_customer_roles as $role_id => $role ) {
379
- $role_name = translate_user_role($role['name'] );
380
-
381
- echo '<option value="' . esc_attr( $role_id ) . '"'
382
- . selected( in_array( $role_id, $coupon_excluded_customer_roles ), true, false ) . '>'
383
- . esc_html( $role_name ) . '</option>';
384
- }
385
- ?>
386
- </select>
387
- <?php echo WJECF_WC()->wc_help_tip( __( 'These User Roles will be specifically excluded from using this coupon.', 'woocommerce-jos-autocoupon' ) ); ?>
388
- </p>
389
- <?php
390
- }
391
-
392
- public function admin_coupon_metabox_misc( $thepostid, $post ) {
393
- echo "<h3>" . esc_html( __( 'Miscellaneous', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
394
- //=============================
395
- //2.2.2 Allow if minimum spend not met
396
- woocommerce_wp_checkbox( array(
397
- 'id' => '_wjecf_allow_below_minimum_spend',
398
- 'label' => __( 'Allow when minimum spend not reached', 'woocommerce-jos-autocoupon' ),
399
- 'description' => '<b>' . __( 'EXPERIMENTAL: ', 'woocommerce-jos-autocoupon' ) . '</b>' . __( 'Check this box to allow the coupon to be in the cart even when minimum spend (see tab \'usage restriction\') is not reached. Value of the discount will be 0 until minimum spend is reached.', 'woocommerce-jos-autocoupon' ),
400
- ) );
401
- }
402
-
403
- public function process_shop_coupon_meta( $post_id, $post ) {
404
- $wrap_coupon = WJECF_Wrap( $post_id );
405
-
406
- $wjecf_min_matching_product_qty = isset( $_POST['_wjecf_min_matching_product_qty'] ) ? $_POST['_wjecf_min_matching_product_qty'] : '';
407
- $wrap_coupon->set_meta( '_wjecf_min_matching_product_qty', $wjecf_min_matching_product_qty );
408
-
409
- $wjecf_max_matching_product_qty = isset( $_POST['_wjecf_max_matching_product_qty'] ) ? $_POST['_wjecf_max_matching_product_qty'] : '';
410
- $wrap_coupon->set_meta( '_wjecf_max_matching_product_qty', $wjecf_max_matching_product_qty );
411
-
412
- //2.2.2
413
- $wjecf_min_matching_product_subtotal = isset( $_POST['_wjecf_min_matching_product_subtotal'] ) ? $_POST['_wjecf_min_matching_product_subtotal'] : '';
414
- $wrap_coupon->set_meta( '_wjecf_min_matching_product_subtotal', $wjecf_min_matching_product_subtotal );
415
-
416
- $wjecf_max_matching_product_subtotal = isset( $_POST['_wjecf_max_matching_product_subtotal'] ) ? $_POST['_wjecf_max_matching_product_subtotal'] : '';
417
- $wrap_coupon->set_meta( '_wjecf_max_matching_product_subtotal', $wjecf_max_matching_product_subtotal );
418
-
419
- $wjecf_products_and = $_POST['_wjecf_products_and'] == 'yes' ? 'yes' : 'no';
420
- $wrap_coupon->set_meta( '_wjecf_products_and', $wjecf_products_and );
421
-
422
- //2.2.3.1
423
- $wjecf_categories_and = $_POST['_wjecf_categories_and'] == 'yes' ? 'yes' : 'no';
424
- $wrap_coupon->set_meta( '_wjecf_categories_and', $wjecf_categories_and );
425
-
426
- //2.2.2
427
- $wjecf_allow_below_minimum_spend = isset( $_POST['_wjecf_allow_below_minimum_spend'] ) ? 'yes' : 'no';
428
- $wrap_coupon->set_meta( '_wjecf_allow_below_minimum_spend', $wjecf_allow_below_minimum_spend );
429
-
430
- $wjecf_shipping_methods = isset( $_POST['wjecf_shipping_methods'] ) ? $_POST['wjecf_shipping_methods'] : '';
431
- $wrap_coupon->set_meta( '_wjecf_shipping_methods', $wjecf_shipping_methods );
432
-
433
- $wjecf_payment_methods = isset( $_POST['wjecf_payment_methods'] ) ? $_POST['wjecf_payment_methods'] : '';
434
- $wrap_coupon->set_meta( '_wjecf_payment_methods', $wjecf_payment_methods );
435
-
436
- $wjecf_customer_ids = $this->comma_separated_int_array( $_POST['wjecf_customer_ids'] );
437
- $wrap_coupon->set_meta( '_wjecf_customer_ids', $wjecf_customer_ids );
438
-
439
- $wjecf_customer_roles = isset( $_POST['wjecf_customer_roles'] ) ? $_POST['wjecf_customer_roles'] : '';
440
- $wrap_coupon->set_meta( '_wjecf_customer_roles', $wjecf_customer_roles );
441
-
442
- $wjecf_excluded_customer_roles = isset( $_POST['wjecf_excluded_customer_roles'] ) ? $_POST['wjecf_excluded_customer_roles'] : '';
443
- $wrap_coupon->set_meta( '_wjecf_excluded_customer_roles', $wjecf_excluded_customer_roles );
444
-
445
- $wrap_coupon->save();
446
- }
447
-
448
- /**
449
- * 2.3.6
450
- * Add inline style (css) to the admin page. Must be called BEFORE admin_head !
451
- * @param string $css
452
- * @return void
453
- */
454
- public function add_inline_style( $css ) {
455
- $this->admin_css .= $css;
456
- }
457
-
458
-
459
- /**
460
- * 2.3.4
461
- * Parse an array or comma separated string; make sure they are valid ints and return as comma separated string
462
- * @param array|string $int_array
463
- * @return string comma separated int array
464
- */
465
- public function comma_separated_int_array( $int_array ) {
466
- //Source can be a comma separated string (select2) , or an int array (chosen)
467
- if ( ! is_array( $int_array) ) {
468
- $int_array = explode( ',', $int_array );
469
- }
470
  return implode( ',', array_filter( array_map( 'intval', $int_array ) ) );
471
- }
472
-
473
- /**
474
- * 2.3.6
475
- * Renders a <SELECT> that has a default value. Relies on woocommerce_wp_select
476
- *
477
- * field['default_value']: The default value (if omitted the first option will be default)
478
- * field['append_default_label']: If true or omitted the text '(DEFAULT)' will be appended to the default option caption
479
- *
480
- * @param array $field see wc-meta-box-functions.php:woocommerce_wp_select
481
- * @return void
482
- */
483
- public function render_select_with_default( $field ) {
484
- global $thepostid, $post;
485
- $thepostid = empty( $thepostid ) ? $post->ID : $thepostid;
486
-
487
- reset( $field['options'] ); //move to first for key()
488
- $default_value = isset( $field['default_value'] ) ? $field['default_value'] : key( $field['options'] );
489
- $append_default_label = isset( $field['append_default_label'] ) ? $field['append_default_label'] : true;
490
-
491
- if ( $append_default_label ) {
492
- $field['options'][$default_value] = sprintf( __( '%s (Default)', 'woocommerce-jos-autocoupon' ), $field['options'][$default_value] );
493
- }
494
-
495
- if ( ! isset( $field['value'] ) ) {
496
- $field['value'] = get_post_meta( $thepostid, $field['id'], true );
497
- }
498
- if ( empty( $field['value'] ) ) {
499
- $field['value'] = $default_value;
500
- }
501
-
502
- woocommerce_wp_select( $field );
503
- }
504
-
505
- public function render_admin_cat_selector( $dom_id, $field_name, $selected_ids, $placeholder = null ) {
506
- if ( $placeholder === null ) $placeholder = __( 'Search for a product…', 'woocommerce' );
507
-
508
- // Categories
509
- ?>
510
- <select id="<?php esc_attr_e( $dom_id ) ?>" name="<?php esc_attr_e( $field_name ) ?>[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php esc_attr_e( $placeholder ); ?>">
511
- <?php
512
- $categories = get_terms( 'product_cat', 'orderby=name&hide_empty=0' );
513
-
514
- if ( $categories ) foreach ( $categories as $cat ) {
515
- echo '<option value="' . esc_attr( $cat->term_id ) . '"' . selected( in_array( $cat->term_id, $selected_ids ), true, false ) . '>' . esc_html( $cat->name ) . '</option>';
516
- }
517
- ?>
518
- </select>
519
- <?php
520
- }
521
-
522
- /**
523
- * Add donate-link to plugin page
524
- */
525
- function wjecf_plugin_meta( $links, $file ) {
526
- if ( strpos( $file, 'woocommerce-jos-autocoupon.php' ) !== false ) {
527
- $links = array_merge( $links, array( '<a href="' . WJECF_Admin::get_donate_url() . '" title="Support the development" target="_blank">Donate</a>' ) );
528
- }
529
- return $links;
530
- }
531
-
532
-
533
- public static function get_donate_url() {
534
- return "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5T9XQBCS2QHRY&lc=NL&item_name=Jos%20Koenis&item_number=wordpress%2dplugin&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted";
535
- }
536
  }
3
  defined('ABSPATH') or die();
4
 
5
  if ( class_exists('WJECF_Admin') ) {
6
+ return;
7
  }
8
 
9
  class WJECF_Admin extends Abstract_WJECF_Plugin {
10
+
11
+ public function __construct() {
12
+ $this->set_plugin_data( array(
13
+ 'description' => __( 'Admin interface of WooCommerce Extended Coupon Features.', 'woocommerce-jos-autocoupon' ),
14
+ 'dependencies' => array(),
15
+ 'can_be_disabled' => false
16
+ ) );
17
+ }
18
+
19
+ public function init_admin_hook() {
20
  add_action( 'admin_notices', array( $this, 'admin_notices'));
21
 
22
+ if ( ! WJECF_WC()->check_woocommerce_version('2.3.0') ) {
23
+ $this->enqueue_notice( '<p>' .
24
+ __( '<strong>WooCommerce Extended Coupon Features:</strong> You are using an old version of WooCommerce. Updating of WooCommerce is recommended as using an outdated version might cause unexpected behaviour in combination with modern plugins.' )
25
+ . '</p>', 'notice-warning' );
26
+ }
27
+ //Admin hooks
28
+ add_filter( 'plugin_row_meta', array( $this, 'wjecf_plugin_meta' ), 10, 2 );
29
  add_action( 'admin_head', array( $this, 'on_admin_head'));
30
 
31
+ add_filter( 'woocommerce_coupon_data_tabs', array( $this, 'admin_coupon_options_tabs' ), 10, 1);
32
+ add_action( 'woocommerce_coupon_data_panels', array( $this, 'admin_coupon_options_panels' ), 10, 0 );
33
+ add_action( 'woocommerce_process_shop_coupon_meta', array( $this, 'process_shop_coupon_meta' ), 10, 2 );
34
+
35
+ add_action( 'wjecf_coupon_metabox_products', array( $this, 'admin_coupon_metabox_products' ), 10, 2 );
36
+ add_action( 'wjecf_coupon_metabox_checkout', array( $this, 'admin_coupon_metabox_checkout' ), 10, 2 );
37
+ add_action( 'wjecf_coupon_metabox_customer', array( $this, 'admin_coupon_metabox_customer' ), 10, 2 );
38
+ add_action( 'wjecf_coupon_metabox_misc', array( $this, 'admin_coupon_metabox_misc' ), 10, 2 );
39
 
40
+ WJECF_ADMIN()->add_inline_style( '
41
+ #woocommerce-coupon-data .wjecf-not-wide { width:50% }
42
+ ');
43
 
44
  //WORK IN PROGRESS: add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
45
+ }
46
 
47
  // ===========================================================================
48
  // START - ADMIN NOTICES
49
  // Allows notices to be displayed on the admin pages
50
  // ===========================================================================
51
 
52
+ private $notices = array();
53
+
54
+ /**
55
+ * Enqueue a notice to display on the admin page
56
+ * @param stirng $html Please embed in <p> tags
57
+ * @param string $class
58
+ */
59
+ public function enqueue_notice( $html, $class = 'notice-info' ) {
60
+ $this->notices[] = array( 'class' => $class, 'html' => $html );
61
+ }
62
+
63
+ public function admin_notices() {
64
+ foreach( $this->notices as $notice ) {
65
+ echo '<div class="notice ' . $notice['class'] . '">';
66
+ echo $notice['html'];
67
+ echo '</div>';
68
+ }
69
+ }
70
 
71
  // ===========================================================================
72
  // END - ADMIN NOTICES
90
  <ul>
91
  <?php
92
  foreach ( WJECF()->get_plugins() as $name => $plugin ) {
93
+ echo "<li><h3>" . $plugin->get_plugin_class_name() . "</h3>\n";
94
+ echo "<p>" . $plugin->get_plugin_description() . "</p>\n";
95
+ echo "</li>\n";
96
  }
97
  ?>
98
+ </ul>
99
  <?php
100
 
101
  }
102
 
103
+ //2.3.6 Inline css
104
+ private $admin_css = '';
105
+
106
+ /**
107
+ * 2.3.6
108
+ * @return void
109
+ */
110
+ function on_admin_head() {
111
+ //Output inline style for the admin pages
112
+ if ( ! empty( $this->admin_css ) ) {
113
+ echo '<style type="text/css">' . $this->admin_css . '</style>';
114
+ $this->admin_css = '';
115
+ }
116
+
117
+ //Enqueue scripts
118
+ wp_enqueue_script( "wjecf-admin", WJECF()->plugin_url . "assets/js/wjecf-admin.js", array( 'jquery' ), WJECF()->version );
119
+ wp_localize_script( 'wjecf-admin', 'wjecf_admin_i18n', array(
120
+ 'label_and' => __( '(AND)', 'woocommerce-jos-autocoupon' ),
121
+ 'label_or' => __( '(OR)', 'woocommerce-jos-autocoupon' )
122
+ ) );
123
+
124
+ }
125
+
126
+
127
+ //Add tabs to the coupon option page
128
+ public function admin_coupon_options_tabs( $tabs ) {
129
+
130
+ $tabs['extended_features_products'] = array(
131
+ 'label' => __( 'Products', 'woocommerce-jos-autocoupon' ),
132
+ 'target' => 'wjecf_coupondata_products',
133
+ 'class' => 'wjecf_coupondata_products',
134
+ );
135
+
136
+ $tabs['extended_features_checkout'] = array(
137
+ 'label' => __( 'Checkout', 'woocommerce-jos-autocoupon' ),
138
+ 'target' => 'wjecf_coupondata_checkout',
139
+ 'class' => 'wjecf_coupondata_checkout',
140
+ );
141
+
142
+ $tabs['extended_features_misc'] = array(
143
+ 'label' => __( 'Miscellaneous', 'woocommerce-jos-autocoupon' ),
144
+ 'target' => 'wjecf_coupondata_misc',
145
+ 'class' => 'wjecf_coupondata_misc',
146
+ );
147
+
148
+ return $tabs;
149
+ }
150
+
151
+ //Add panels to the coupon option page
152
+ public function admin_coupon_options_panels() {
153
+ global $thepostid, $post;
154
+ $thepostid = empty( $thepostid ) ? $post->ID : $thepostid;
155
+ ?>
156
+ <div id="wjecf_coupondata_products" class="panel woocommerce_options_panel">
157
+ <?php
158
+ //Feed the panel with options
159
+ do_action( 'wjecf_coupon_metabox_products', $thepostid, $post );
160
+ $this->admin_coupon_data_footer();
161
+ ?>
162
+ </div>
163
+ <div id="wjecf_coupondata_checkout" class="panel woocommerce_options_panel">
164
+ <?php
165
+ do_action( 'wjecf_coupon_metabox_checkout', $thepostid, $post );
166
+ do_action( 'wjecf_coupon_metabox_customer', $thepostid, $post );
167
+ $this->admin_coupon_data_footer();
168
+ ?>
169
+ </div>
170
+ <div id="wjecf_coupondata_misc" class="panel woocommerce_options_panel">
171
+ <?php
172
+ //Allow other classes to inject options
173
+ do_action( 'wjecf_woocommerce_coupon_options_extended_features', $thepostid, $post );
174
+ do_action( 'wjecf_coupon_metabox_misc', $thepostid, $post );
175
+ $this->admin_coupon_data_footer();
176
+ ?>
177
+ </div>
178
+ <?php
179
+ }
180
+
181
+ public function admin_coupon_data_footer() {
182
+ $documentation_url = plugins_url( 'docs/index.html', dirname( __FILE__ ) );
183
+ if ( ! WJECF()->is_pro() ) {
184
+ $documentation_url = 'http://www.soft79.nl/documentation/wjecf';
185
+ ?>
186
+ <h3><?php _e( 'Do you find WooCommerce Extended Coupon Features useful?', 'woocommerce-jos-autocoupon'); ?></h3>
187
+ <p class="form-field"><label for="wjecf_donate_button"><?php
188
+ echo esc_html( __('Express your gratitude', 'woocommerce-jos-autocoupon' ) );
189
+ ?></label>
190
+ <a id="wjecf_donate_button" href="<?php echo $this->get_donate_url(); ?>" target="_blank" class="button button-primary">
191
+ <?php
192
+ echo esc_html( __('Donate to the developer', 'woocommerce-jos-autocoupon' ) );
193
+ ?></a><br>
194
+ Or get the PRO version at <a href="http://www.soft79.nl" target="_blank">www.soft79.nl</a>.
195
+ </p>
196
+ <?php
197
+ }
198
+ //Documentation link
199
+ echo '<h3>' . __( 'Documentation', 'woocommerce-jos-autocoupon' ) . '</h3>';
200
+ echo '<p><a href="' . $documentation_url . '" target="_blank">' .
201
+ __( 'WooCommerce Extended Coupon Features Documentation', 'woocommerce-jos-autocoupon' ) . '</a></p>';
202
+
203
+ }
204
+
205
+ //Tab 'extended features'
206
+ public function admin_coupon_metabox_products( $thepostid, $post ) {
207
+ //See WooCommerce class-wc-meta-box-coupon-data.php function ouput
208
+
209
+ echo "<h3>" . esc_html( __( 'Matching products', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
210
+ //=============================
211
+ // AND instead of OR the products
212
+ $this->render_select_with_default( array(
213
+ 'id' => '_wjecf_products_and',
214
+ 'label' => __( 'Products Operator', 'woocommerce-jos-autocoupon' ),
215
+ 'options' => array( 'no' => __( 'OR', 'woocommerce-jos-autocoupon' ), 'yes' => __( 'AND', 'woocommerce-jos-autocoupon' ) ),
216
+ 'default_value' => 'no',
217
+ 'class' => 'wjecf-not-wide',
218
+ /* translators: OLD TEXT: 'Check this box if ALL of the products (see tab \'usage restriction\') must be in the cart to use this coupon (instead of only one of the products).' */
219
+ 'description' => __( 'Use AND if ALL of the products must be in the cart to use this coupon (instead of only one of the products).', 'woocommerce-jos-autocoupon' ),
220
+ 'desc_tip' => true
221
+ ) );
222
+
223
+ //=============================
224
+ // 2.2.3.1 AND instead of OR the categories
225
+ $this->render_select_with_default( array(
226
+ 'id' => '_wjecf_categories_and',
227
+ 'label' => __( 'Categories Operator', 'woocommerce-jos-autocoupon' ),
228
+ 'options' => array( 'no' => __( 'OR', 'woocommerce-jos-autocoupon' ), 'yes' => __( 'AND', 'woocommerce-jos-autocoupon' ) ),
229
+ 'default_value' => 'no',
230
+ 'class' => 'wjecf-not-wide',
231
+ /* translators: OLD TEXT: 'Check this box if products from ALL of the categories (see tab \'usage restriction\') must be in the cart to use this coupon (instead of only one from one of the categories).' */
232
+ 'description' => __( 'Use AND if products from ALL of the categories must be in the cart to use this coupon (instead of only one from one of the categories).', 'woocommerce-jos-autocoupon' ),
233
+ 'desc_tip' => true
234
+ ) );
235
+
236
+
237
+
238
+ // Minimum quantity of matching products (product/category)
239
+ woocommerce_wp_text_input( array(
240
+ 'id' => '_wjecf_min_matching_product_qty',
241
+ 'label' => __( 'Minimum quantity of matching products', 'woocommerce-jos-autocoupon' ),
242
+ 'placeholder' => __( 'No minimum', 'woocommerce' ),
243
+ 'description' => __( 'Minimum quantity of the products that match the given product or category restrictions (see tab \'usage restriction\'). If no product or category restrictions are specified, the total number of products is used.', 'woocommerce-jos-autocoupon' ),
244
+ 'data_type' => 'decimal',
245
+ 'desc_tip' => true
246
+ ) );
247
+
248
+ // Maximum quantity of matching products (product/category)
249
+ woocommerce_wp_text_input( array(
250
+ 'id' => '_wjecf_max_matching_product_qty',
251
+ 'label' => __( 'Maximum quantity of matching products', 'woocommerce-jos-autocoupon' ),
252
+ 'placeholder' => __( 'No maximum', 'woocommerce' ),
253
+ 'description' => __( 'Maximum quantity of the products that match the given product or category restrictions (see tab \'usage restriction\'). If no product or category restrictions are specified, the total number of products is used.', 'woocommerce-jos-autocoupon' ),
254
+ 'data_type' => 'decimal',
255
+ 'desc_tip' => true
256
+ ) );
257
+
258
+ // Minimum subtotal of matching products (product/category)
259
+ woocommerce_wp_text_input( array(
260
+ 'id' => '_wjecf_min_matching_product_subtotal',
261
+ 'label' => __( 'Minimum subtotal of matching products', 'woocommerce-jos-autocoupon' ),
262
+ 'placeholder' => __( 'No minimum', 'woocommerce' ),
263
+ 'description' => __( 'Minimum price subtotal of the products that match the given product or category restrictions (see tab \'usage restriction\').', 'woocommerce-jos-autocoupon' ),
264
+ 'data_type' => 'price',
265
+ 'desc_tip' => true
266
+ ) );
267
+
268
+ // Maximum subtotal of matching products (product/category)
269
+ woocommerce_wp_text_input( array(
270
+ 'id' => '_wjecf_max_matching_product_subtotal',
271
+ 'label' => __( 'Maximum subtotal of matching products', 'woocommerce-jos-autocoupon' ),
272
+ 'placeholder' => __( 'No maximum', 'woocommerce' ),
273
+ 'description' => __( 'Maximum price subtotal of the products that match the given product or category restrictions (see tab \'usage restriction\').', 'woocommerce-jos-autocoupon' ),
274
+ 'data_type' => 'price',
275
+ 'desc_tip' => true
276
+ ) );
277
+ }
278
+
279
+ public function admin_coupon_metabox_checkout( $thepostid, $post ) {
280
+
281
+ echo "<h3>" . esc_html( __( 'Checkout', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
282
+
283
+ //=============================
284
+ // Shipping methods
285
+ ?>
286
+ <p class="form-field"><label for="wjecf_shipping_methods"><?php _e( 'Shipping methods', 'woocommerce-jos-autocoupon' ); ?></label>
287
+ <select id="wjecf_shipping_methods" name="wjecf_shipping_methods[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any shipping method', 'woocommerce-jos-autocoupon' ); ?>">
288
+ <?php
289
+ $coupon_shipping_method_ids = WJECF()->get_coupon_shipping_method_ids( $thepostid );
290
+ $shipping_methods = WC()->shipping->load_shipping_methods();
291
+
292
+ if ( $shipping_methods ) foreach ( $shipping_methods as $shipping_method ) {
293
+ echo '<option value="' . esc_attr( $shipping_method->id ) . '"' . selected( in_array( $shipping_method->id, $coupon_shipping_method_ids ), true, false ) . '>' . esc_html( $shipping_method->method_title ) . '</option>';
294
+ }
295
+ ?>
296
+ </select><?php echo WJECF_WC()->wc_help_tip( __( 'One of these shipping methods must be selected in order for this coupon to be valid.', 'woocommerce-jos-autocoupon' ) ); ?>
297
+ </p>
298
+ <?php
299
+
300
+ //=============================
301
+ // Payment methods
302
+ ?>
303
+ <p class="form-field"><label for="wjecf_payment_methods"><?php _e( 'Payment methods', 'woocommerce-jos-autocoupon' ); ?></label>
304
+ <select id="wjecf_payment_methods" name="wjecf_payment_methods[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any payment method', 'woocommerce-jos-autocoupon' ); ?>">
305
+ <?php
306
+ $coupon_payment_method_ids = WJECF()->get_coupon_payment_method_ids( $thepostid );
307
+ //DONT USE WC()->payment_gateways->available_payment_gateways() AS IT CAN CRASH IN UNKNOWN OCCASIONS
308
+ $payment_methods = WC()->payment_gateways->payment_gateways();
309
+ if ( $payment_methods ) foreach ( $payment_methods as $payment_method ) {
310
+ if ('yes' === $payment_method->enabled) {
311
+ echo '<option value="' . esc_attr( $payment_method->id ) . '"' . selected( in_array( $payment_method->id, $coupon_payment_method_ids ), true, false ) . '>' . esc_html( $payment_method->title ) . '</option>';
312
+ }
313
+ }
314
+ ?>
315
+ </select><?php echo WJECF_WC()->wc_help_tip( __( 'One of these payment methods must be selected in order for this coupon to be valid.', 'woocommerce-jos-autocoupon' ) ); ?>
316
+ </p>
317
+ <?php
318
+ }
319
+
320
+ public function admin_coupon_metabox_customer( $thepostid, $post ) {
321
+
322
+ //=============================
323
+ //Title: "CUSTOMER RESTRICTIONS"
324
+ echo "<h3>" . esc_html( __( 'Customer restrictions', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
325
+ echo "<p><span class='description'>" . __( 'If both a customer and a role restriction are supplied, matching either one of them will suffice.' , 'woocommerce-jos-autocoupon' ) . "</span></p>\n";
326
+
327
+ //=============================
328
+ // User ids
329
+ ?>
330
+ <p class="form-field"><label><?php _e( 'Allowed Customers', 'woocommerce-jos-autocoupon' ); ?></label>
331
+ <?php
332
+ $coupon_customer_ids = WJECF()->get_coupon_customer_ids( $thepostid );
333
+ WJECF_WC()->render_admin_customer_selector( 'wjecf_customer_ids', 'wjecf_customer_ids', $coupon_customer_ids );
334
+ echo WJECF_WC()->wc_help_tip( __( 'Only these customers may use this coupon.', 'woocommerce-jos-autocoupon' ) );
335
+ ?>
336
+ </p>
337
+ <?php
338
+
339
+ //=============================
340
+ // User roles
341
+ ?>
342
+ <p class="form-field"><label for="wjecf_customer_roles"><?php _e( 'Allowed User Roles', 'woocommerce-jos-autocoupon' ); ?></label>
343
+ <select id="wjecf_customer_roles" name="wjecf_customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any role', 'woocommerce-jos-autocoupon' ); ?>">
344
+ <?php
345
+ $coupon_customer_roles = WJECF()->get_coupon_customer_roles( $thepostid );
346
+
347
+ $available_customer_roles = array_reverse( get_editable_roles() );
348
+ foreach ( $available_customer_roles as $role_id => $role ) {
349
+ $role_name = translate_user_role($role['name'] );
350
+
351
+ echo '<option value="' . esc_attr( $role_id ) . '"'
352
+ . selected( in_array( $role_id, $coupon_customer_roles ), true, false ) . '>'
353
+ . esc_html( $role_name ) . '</option>';
354
+ }
355
+ ?>
356
+ </select>
357
+ <?php echo WJECF_WC()->wc_help_tip( __( 'Only these User Roles may use this coupon.', 'woocommerce-jos-autocoupon' ) ); ?>
358
+ </p>
359
+ <?php
360
+
361
+ //=============================
362
+ // Excluded user roles
363
+ ?>
364
+ <p class="form-field"><label for="wjecf_excluded_customer_roles"><?php _e( 'Disallowed User Roles', 'woocommerce-jos-autocoupon' ); ?></label>
365
+ <select id="wjecf_customer_roles" name="wjecf_excluded_customer_roles[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php _e( 'Any role', 'woocommerce-jos-autocoupon' ); ?>">
366
+ <?php
367
+ $coupon_excluded_customer_roles = WJECF()->get_coupon_excluded_customer_roles( $thepostid );
368
+
369
+ foreach ( $available_customer_roles as $role_id => $role ) {
370
+ $role_name = translate_user_role($role['name'] );
371
+
372
+ echo '<option value="' . esc_attr( $role_id ) . '"'
373
+ . selected( in_array( $role_id, $coupon_excluded_customer_roles ), true, false ) . '>'
374
+ . esc_html( $role_name ) . '</option>';
375
+ }
376
+ ?>
377
+ </select>
378
+ <?php echo WJECF_WC()->wc_help_tip( __( 'These User Roles will be specifically excluded from using this coupon.', 'woocommerce-jos-autocoupon' ) ); ?>
379
+ </p>
380
+ <?php
381
+ }
382
+
383
+ public function admin_coupon_metabox_misc( $thepostid, $post ) {
384
+ echo "<h3>" . esc_html( __( 'Miscellaneous', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
385
+ //=============================
386
+ //2.2.2 Allow if minimum spend not met
387
+ woocommerce_wp_checkbox( array(
388
+ 'id' => '_wjecf_allow_below_minimum_spend',
389
+ 'label' => __( 'Allow when minimum spend not reached', 'woocommerce-jos-autocoupon' ),
390
+ 'description' => '<b>' . __( 'EXPERIMENTAL: ', 'woocommerce-jos-autocoupon' ) . '</b>' . __( 'Check this box to allow the coupon to be in the cart even when minimum spend (see tab \'usage restriction\') is not reached. Value of the discount will be 0 until minimum spend is reached.', 'woocommerce-jos-autocoupon' ),
391
+ ) );
392
+ }
393
+
394
+ public function process_shop_coupon_meta( $post_id, $post ) {
395
+ $wrap_coupon = WJECF_Wrap( $post_id );
396
+
397
+ $wjecf_min_matching_product_qty = isset( $_POST['_wjecf_min_matching_product_qty'] ) ? $_POST['_wjecf_min_matching_product_qty'] : '';
398
+ $wrap_coupon->set_meta( '_wjecf_min_matching_product_qty', $wjecf_min_matching_product_qty );
399
+
400
+ $wjecf_max_matching_product_qty = isset( $_POST['_wjecf_max_matching_product_qty'] ) ? $_POST['_wjecf_max_matching_product_qty'] : '';
401
+ $wrap_coupon->set_meta( '_wjecf_max_matching_product_qty', $wjecf_max_matching_product_qty );
402
+
403
+ //2.2.2
404
+ $wjecf_min_matching_product_subtotal = isset( $_POST['_wjecf_min_matching_product_subtotal'] ) ? $_POST['_wjecf_min_matching_product_subtotal'] : '';
405
+ $wrap_coupon->set_meta( '_wjecf_min_matching_product_subtotal', $wjecf_min_matching_product_subtotal );
406
+
407
+ $wjecf_max_matching_product_subtotal = isset( $_POST['_wjecf_max_matching_product_subtotal'] ) ? $_POST['_wjecf_max_matching_product_subtotal'] : '';
408
+ $wrap_coupon->set_meta( '_wjecf_max_matching_product_subtotal', $wjecf_max_matching_product_subtotal );
409
+
410
+ $wjecf_products_and = $_POST['_wjecf_products_and'] == 'yes' ? 'yes' : 'no';
411
+ $wrap_coupon->set_meta( '_wjecf_products_and', $wjecf_products_and );
412
+
413
+ //2.2.3.1
414
+ $wjecf_categories_and = $_POST['_wjecf_categories_and'] == 'yes' ? 'yes' : 'no';
415
+ $wrap_coupon->set_meta( '_wjecf_categories_and', $wjecf_categories_and );
416
+
417
+ //2.2.2
418
+ $wjecf_allow_below_minimum_spend = isset( $_POST['_wjecf_allow_below_minimum_spend'] ) ? 'yes' : 'no';
419
+ $wrap_coupon->set_meta( '_wjecf_allow_below_minimum_spend', $wjecf_allow_below_minimum_spend );
420
+
421
+ $wjecf_shipping_methods = isset( $_POST['wjecf_shipping_methods'] ) ? $_POST['wjecf_shipping_methods'] : '';
422
+ $wrap_coupon->set_meta( '_wjecf_shipping_methods', $wjecf_shipping_methods );
423
+
424
+ $wjecf_payment_methods = isset( $_POST['wjecf_payment_methods'] ) ? $_POST['wjecf_payment_methods'] : '';
425
+ $wrap_coupon->set_meta( '_wjecf_payment_methods', $wjecf_payment_methods );
426
+
427
+ $wjecf_customer_ids = $this->comma_separated_int_array( $_POST['wjecf_customer_ids'] );
428
+ $wrap_coupon->set_meta( '_wjecf_customer_ids', $wjecf_customer_ids );
429
+
430
+ $wjecf_customer_roles = isset( $_POST['wjecf_customer_roles'] ) ? $_POST['wjecf_customer_roles'] : '';
431
+ $wrap_coupon->set_meta( '_wjecf_customer_roles', $wjecf_customer_roles );
432
+
433
+ $wjecf_excluded_customer_roles = isset( $_POST['wjecf_excluded_customer_roles'] ) ? $_POST['wjecf_excluded_customer_roles'] : '';
434
+ $wrap_coupon->set_meta( '_wjecf_excluded_customer_roles', $wjecf_excluded_customer_roles );
435
+
436
+ $wrap_coupon->save();
437
+ }
438
+
439
+ /**
440
+ * 2.3.6
441
+ * Add inline style (css) to the admin page. Must be called BEFORE admin_head !
442
+ * @param string $css
443
+ * @return void
444
+ */
445
+ public function add_inline_style( $css ) {
446
+ $this->admin_css .= $css;
447
+ }
448
+
449
+
450
+ /**
451
+ * 2.3.4
452
+ * Parse an array or comma separated string; make sure they are valid ints and return as comma separated string
453
+ * @param array|string $int_array
454
+ * @return string comma separated int array
455
+ */
456
+ public function comma_separated_int_array( $int_array ) {
457
+ //Source can be a comma separated string (select2) , or an int array (chosen)
458
+ if ( ! is_array( $int_array) ) {
459
+ $int_array = explode( ',', $int_array );
460
+ }
 
 
 
 
 
 
 
 
 
461
  return implode( ',', array_filter( array_map( 'intval', $int_array ) ) );
462
+ }
463
+
464
+ /**
465
+ * 2.3.6
466
+ * Renders a <SELECT> that has a default value. Relies on woocommerce_wp_select
467
+ *
468
+ * field['default_value']: The default value (if omitted the first option will be default)
469
+ * field['append_default_label']: If true or omitted the text '(DEFAULT)' will be appended to the default option caption
470
+ *
471
+ * @param array $field see wc-meta-box-functions.php:woocommerce_wp_select
472
+ * @return void
473
+ */
474
+ public function render_select_with_default( $field ) {
475
+ global $thepostid, $post;
476
+ $thepostid = empty( $thepostid ) ? $post->ID : $thepostid;
477
+
478
+ reset( $field['options'] ); //move to first for key()
479
+ $default_value = isset( $field['default_value'] ) ? $field['default_value'] : key( $field['options'] );
480
+ $append_default_label = isset( $field['append_default_label'] ) ? $field['append_default_label'] : true;
481
+
482
+ if ( $append_default_label ) {
483
+ $field['options'][$default_value] = sprintf( __( '%s (Default)', 'woocommerce-jos-autocoupon' ), $field['options'][$default_value] );
484
+ }
485
+
486
+ if ( ! isset( $field['value'] ) ) {
487
+ $field['value'] = get_post_meta( $thepostid, $field['id'], true );
488
+ }
489
+ if ( empty( $field['value'] ) ) {
490
+ $field['value'] = $default_value;
491
+ }
492
+
493
+ woocommerce_wp_select( $field );
494
+ }
495
+
496
+ public function render_admin_cat_selector( $dom_id, $field_name, $selected_ids, $placeholder = null ) {
497
+ if ( $placeholder === null ) $placeholder = __( 'Search for a product…', 'woocommerce' );
498
+
499
+ // Categories
500
+ ?>
501
+ <select id="<?php esc_attr_e( $dom_id ) ?>" name="<?php esc_attr_e( $field_name ) ?>[]" style="width: 50%;" class="wc-enhanced-select" multiple="multiple" data-placeholder="<?php esc_attr_e( $placeholder ); ?>">
502
+ <?php
503
+ $categories = get_terms( 'product_cat', 'orderby=name&hide_empty=0' );
504
+
505
+ if ( $categories ) foreach ( $categories as $cat ) {
506
+ echo '<option value="' . esc_attr( $cat->term_id ) . '"' . selected( in_array( $cat->term_id, $selected_ids ), true, false ) . '>' . esc_html( $cat->name ) . '</option>';
507
+ }
508
+ ?>
509
+ </select>
510
+ <?php
511
+ }
512
+
513
+ /**
514
+ * Add donate-link to plugin page
515
+ */
516
+ function wjecf_plugin_meta( $links, $file ) {
517
+ if ( strpos( $file, 'woocommerce-jos-autocoupon.php' ) !== false ) {
518
+ $links = array_merge( $links, array( '<a href="' . WJECF_Admin::get_donate_url() . '" title="Support the development" target="_blank">Donate</a>' ) );
519
+ }
520
+ return $links;
521
+ }
522
+
523
+
524
+ public static function get_donate_url() {
525
+ return "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5T9XQBCS2QHRY&lc=NL&item_name=Jos%20Koenis&item_number=wordpress%2dplugin&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted";
526
+ }
527
  }
includes/wjecf-autocoupon.php CHANGED
@@ -4,612 +4,612 @@ defined('ABSPATH') or die();
4
 
5
  class WJECF_AutoCoupon extends Abstract_WJECF_Plugin {
6
 
7
- private $_autocoupons = null;
8
-
9
- private $_user_emails = null;
10
-
11
- protected $_executed_coupon_by_url = false;
12
-
13
- public function __construct() {
14
- $this->set_plugin_data( array(
15
- 'description' => __( 'Allow coupons to be automatically applied to the cart when restrictions are met or by url.', 'woocommerce-jos-autocoupon' ),
16
- 'dependencies' => array(),
17
- 'can_be_disabled' => true
18
- ) );
19
- }
20
-
21
- public function init_hook() {
22
- if ( ! class_exists('WC_Coupon') ) {
23
- return;
24
- }
25
-
26
- //Frontend hooks - logic
27
- if ( WJECF_WC()->check_woocommerce_version('2.3.0')) {
28
- add_action( 'woocommerce_after_calculate_totals', array( &$this, 'update_matched_autocoupons' ) );
29
- } else {
30
- //WC Versions prior to 2.3.0 don't have after_calculate_totals hook, this is a fallback
31
- add_action( 'woocommerce_cart_updated', array( &$this, 'update_matched_autocoupons' ) );
32
- }
33
- add_action( 'woocommerce_check_cart_items', array( &$this, 'remove_unmatched_autocoupons' ) , 0, 0 ); //Remove coupon before WC does it and shows a message
34
- //Last check for coupons with restricted_emails
35
- add_action( 'woocommerce_checkout_update_order_review', array( &$this, 'fetch_billing_email' ), 10 ); // AJAX One page checkout
36
-
37
- //Frontend hooks - visualisation
38
- add_filter('woocommerce_cart_totals_coupon_label', array( &$this, 'coupon_label' ), 10, 2 );
39
- add_filter('woocommerce_cart_totals_coupon_html', array( &$this, 'coupon_html' ), 10, 2 );
40
-
41
- //Inhibit redirect to cart when apply_coupon supplied
42
- add_filter('option_woocommerce_cart_redirect_after_add', array ( &$this, 'option_woocommerce_cart_redirect_after_add') );
43
-
44
- if ( ! is_ajax() ) {
45
- //Get cart should not be called before the wp_loaded action nor the add_to_cart_action (class-wc-form-handler)
46
- add_action( 'wp_loaded', array( &$this, 'coupon_by_url' ), 90 ); //Coupon through url
47
- }
48
- }
49
 
50
  /* ADMIN HOOKS */
51
- public function init_admin_hook() {
52
- add_action( 'wjecf_woocommerce_coupon_options_extended_features', array( $this, 'admin_coupon_options_extended_features' ), 20, 2 );
53
- add_action( 'woocommerce_process_shop_coupon_meta', array( $this, 'process_shop_coupon_meta' ), 10, 2 );
54
-
55
- //Inject columns
56
- if ( WJECF()->is_pro() ) {
57
- WJECF()->inject_coupon_column(
58
- '_wjecf_auto_coupon',
59
- __( 'Auto coupon', 'woocommerce-jos-autocoupon' ),
60
- array( $this, 'admin_render_shop_coupon_columns' ), 'coupon_code'
61
- );
62
- WJECF()->inject_coupon_column(
63
- '_wjecf_individual_use',
64
- __( 'Individual use', 'woocommerce-jos-autocoupon' ),
65
- array( $this, 'admin_render_shop_coupon_columns' ), 'coupon_code'
66
- );
67
- }
68
-
69
- add_filter( 'views_edit-shop_coupon', array( $this, 'admin_views_edit_coupon' ) );
70
- add_filter( 'request', array( $this, 'admin_request_query' ) );
71
- }
72
-
73
- /**
74
- * Output a coupon custom column value
75
- *
76
- * @param string $column
77
- * @param WP_Post The coupon post object
78
- */
79
- public function admin_render_shop_coupon_columns( $column, $post ) {
80
- $wrap_coupon = WJECF_Wrap( $post->ID );
81
-
82
- switch ( $column ) {
83
- case '_wjecf_auto_coupon' :
84
- $is_auto_coupon = $wrap_coupon->get_meta( '_wjecf_is_auto_coupon', true ) == 'yes';
85
- echo $is_auto_coupon ? __( 'Yes', 'woocommerce' ) : __( 'No', 'woocommerce' );
86
- if ( $is_auto_coupon ) {
87
- $prio = $wrap_coupon->get_meta( '_wjecf_coupon_priority', true );
88
- if ( $prio ) echo " (" . intval( $prio ) . ")";
89
- }
90
- break;
91
- case '_wjecf_individual_use' :
92
- $individual = $wrap_coupon->get_individual_use();
93
- echo $individual ? __( 'Yes', 'woocommerce' ) : __( 'No', 'woocommerce' );
94
- break;
95
- }
96
- }
97
-
98
- public function admin_views_edit_coupon( $views ) {
99
- global $post_type, $wp_query;
100
-
101
- $class = ( isset( $wp_query->query['meta_key'] ) && $wp_query->query['meta_key'] == '_wjecf_is_auto_coupon' ) ? 'current' : '';
102
- $query_string = remove_query_arg(array( 'wjecf_is_auto_coupon' ));
103
- $query_string = add_query_arg( 'wjecf_is_auto_coupon', '1', $query_string );
104
- $views['wjecf_is_auto_coupon'] = '<a href="' . esc_url( $query_string ) . '" class="' . esc_attr( $class ) . '">' . __( 'Auto coupons', 'woocommerce' ) . '</a>';
105
-
106
- return $views;
107
- }
108
-
109
- /**
110
- * Filters and sorting handler
111
- *
112
- * @param array $vars
113
- * @return array
114
- */
115
- public function admin_request_query( $vars ) {
116
- global $typenow, $wp_query, $wp_post_statuses;
117
-
118
- if ( 'shop_coupon' === $typenow ) {
119
- if ( isset( $_GET['wjecf_is_auto_coupon'] ) ) {
120
- $vars['meta_key'] = '_wjecf_is_auto_coupon';
121
- $vars['meta_value'] = $_GET['wjecf_is_auto_coupon'] == '1' ? 'yes' : 'no';
122
- }
123
- }
124
-
125
- return $vars;
126
- }
127
-
128
- public function admin_coupon_options_extended_features( $thepostid, $post ) {
129
-
130
- //=============================
131
- //Title
132
- echo "<h3>" . esc_html( __( 'Auto coupon', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
133
-
134
-
135
- //=============================
136
- // Auto coupon checkbox
137
- woocommerce_wp_checkbox( array(
138
- 'id' => '_wjecf_is_auto_coupon',
139
- 'label' => __( 'Auto coupon', 'woocommerce-jos-autocoupon' ),
140
- 'description' => __( "Automatically add the coupon to the cart if the restrictions are met. Please enter a description when you check this box, the description will be shown in the customer's cart if the coupon is applied.", 'woocommerce-jos-autocoupon' )
141
- ) );
142
-
143
- echo '<div class="_wjecf_show_if_autocoupon">';
144
- if ( WJECF()->is_pro() ) {
145
- // Maximum quantity of matching products (product/category)
146
- woocommerce_wp_text_input( array(
147
- 'id' => '_wjecf_coupon_priority',
148
- 'label' => __( 'Priority', 'woocommerce-jos-autocoupon' ),
149
- 'placeholder' => __( 'No priority', 'woocommerce' ),
150
- 'description' => __( 'When \'individual use\' is checked, auto coupons with a higher value will have priority over other auto coupons.', 'woocommerce-jos-autocoupon' ),
151
- 'data_type' => 'decimal',
152
- 'desc_tip' => true
153
- ) );
154
- }
155
-
156
- //=============================
157
- // Apply without notice
158
- woocommerce_wp_checkbox( array(
159
- 'id' => '_wjecf_apply_silently',
160
- 'label' => __( 'Apply silently', 'woocommerce-jos-autocoupon' ),
161
- 'description' => __( "Don't display a message when this coupon is automatically applied.", 'woocommerce-jos-autocoupon' ),
162
- ) );
163
- echo '</div>';
164
-
165
- ?>
166
- <script type="text/javascript">
167
- //Hide/show when AUTO-COUPON value changes
168
- function update_wjecf_apply_silently_field( animation ) {
169
- if ( animation === undefined ) animation = 'slow';
170
-
171
- if (jQuery("#_wjecf_is_auto_coupon").prop('checked')) {
172
- jQuery("._wjecf_show_if_autocoupon").show( animation );
173
- } else {
174
- jQuery("._wjecf_show_if_autocoupon").hide( animation );
175
- }
176
- }
177
- update_wjecf_apply_silently_field( 0 );
178
-
179
- jQuery("#_wjecf_is_auto_coupon").click( update_wjecf_apply_silently_field );
180
- </script>
181
- <?php
182
-
183
- }
184
-
185
- public function process_shop_coupon_meta( $post_id, $post ) {
186
- $wrap_coupon = WJECF_Wrap( $post_id );
187
-
188
- $autocoupon = isset( $_POST['_wjecf_is_auto_coupon'] );
189
- $wrap_coupon->set_meta( '_wjecf_is_auto_coupon', $autocoupon ? 'yes' : 'no' );
190
- $wrap_coupon->set_meta( '_wjecf_apply_silently', isset( $_POST['_wjecf_apply_silently'] ) ? 'yes' : 'no' );
191
- if ( WJECF()->is_pro() ) {
192
- $wrap_coupon->set_meta( '_wjecf_coupon_priority', intval( $_POST['_wjecf_coupon_priority'] ) );
193
- }
194
-
195
- $wrap_coupon->save();
196
- }
197
 
198
  /* FRONTEND HOOKS */
199
 
200
- /**
201
- ** Inhibit redirect to cart when apply_coupon supplied
202
- */
203
- public function option_woocommerce_cart_redirect_after_add ( $value ) {
204
- if ( ! $this->_executed_coupon_by_url && isset( $_GET['apply_coupon'] ) ) {
205
- $value = 'no';
206
- }
207
- return $value;
208
- }
209
-
210
- /**
211
- * Add coupon through url
212
- */
213
- public function coupon_by_url() {
214
- $must_redirect = false;
215
-
216
- //Apply coupon by url
217
- if ( isset( $_GET['apply_coupon'] ) ) {
218
- $must_redirect = true;
219
- $this->_executed_coupon_by_url = true;
220
- $split = explode( ",", wc_clean( $_GET['apply_coupon'] ) );
221
- //2.2.2 Make sure a session cookie is set
222
- if( ! WC()->session->has_session() )
223
- {
224
- WC()->session->set_customer_session_cookie( true );
225
- }
226
-
227
- $cart = WC()->cart;
228
- foreach ( $split as $coupon_code ) {
229
- $coupon = WJECF_WC()->get_coupon( $coupon_code );
230
- if ( WJECF_WC()->check_woocommerce_version('2.3.0') && ! WJECF_Wrap( $coupon )->exists() ) {
231
- wc_add_notice( $coupon->get_coupon_error( WC_Coupon::E_WC_COUPON_NOT_EXIST ), 'error' );
232
- } else {
233
- $valid = $coupon->is_valid();
234
- if ( $valid ) {
235
- $cart->add_discount( $coupon_code );
236
- }
237
- }
238
- }
239
- }
240
-
241
- //Redirect to page without autocoupon query args
242
- if ( $must_redirect ) {
243
- $requested_url = is_ssl() ? 'https://' : 'http://';
244
- $requested_url .= $_SERVER['HTTP_HOST'];
245
- $requested_url .= $_SERVER['REQUEST_URI'];
246
-
247
- wp_safe_redirect( remove_query_arg( array( 'apply_coupon', 'add-to-cart' ), ( $requested_url ) ) );
248
- exit;
249
- }
250
- }
251
-
252
  /**
253
  * Overwrite the html created by wc_cart_totals_coupon_label() so a descriptive text will be shown for the discount.
254
  * @param string $originaltext The default text created by wc_cart_totals_coupon_label()
255
  * @param WC_Coupon $coupon The coupon data
256
  * @return string The overwritten text
257
- */
258
- function coupon_label( $originaltext, $coupon ) {
259
-
260
- if ( $this->is_auto_coupon($coupon) ) {
261
-
262
- return $this->coupon_excerpt($coupon); //__($this->autocoupons[$coupon->code], 'woocommerce-jos-autocoupon');
263
- } else {
264
- return $originaltext;
265
- }
266
- }
267
-
268
  /**
269
  * Overwrite the html created by wc_cart_totals_coupon_html(). This function is required to remove the "Remove" link.
270
  * @param string $originaltext The html created by wc_cart_totals_coupon_html()
271
  * @param WC_Coupon $coupon The coupon data
272
  * @return string The overwritten html
273
  */
274
- function coupon_html( $originaltext, $coupon ) {
275
- if ( $this->is_auto_coupon($coupon) ) {
276
- $value = array();
277
-
278
- if ( $amount = WC()->cart->get_coupon_discount_amount( WJECF_Wrap( $coupon )->get_code(), WC()->cart->display_cart_ex_tax ) ) {
279
- $discount_html = '-' . wc_price( $amount );
280
- } else {
281
- $discount_html = '';
282
- }
283
-
284
- $value[] = apply_filters( 'woocommerce_coupon_discount_amount_html', $discount_html, $coupon );
285
-
286
- if ( WJECF_Wrap( $coupon )->get_free_shipping() ) {
287
- $value[] = __( 'Free shipping coupon', 'woocommerce' );
288
- }
289
-
290
- return implode(', ', array_filter($value)); //Remove empty array elements
291
- } else {
292
- return $originaltext;
293
- }
294
- }
295
-
296
-
297
- function remove_unmatched_autocoupons( $valid_coupon_codes = null ) {
298
- if ( $valid_coupon_codes === null ) {
299
- //Get the coupons that should be in the cart
300
- $valid_coupons = $this->get_valid_auto_coupons();
301
- $valid_coupons = $this->individual_use_filter( $valid_coupons );
302
- $valid_coupon_codes = array();
303
- foreach ( $valid_coupons as $coupon ) {
304
- $valid_coupon_codes[] = WJECF_Wrap( $coupon )->get_code();
305
- }
306
- }
307
-
308
- //Remove invalids
309
- $calc_needed = false;
310
- foreach ( $this->get_all_auto_coupons() as $coupon ) {
311
- $coupon_code = WJECF_Wrap( $coupon )->get_code();
312
- if ( WC()->cart->has_discount( $coupon_code ) && ! in_array( $coupon_code, $valid_coupon_codes ) ) {
313
- $this->log( sprintf( "Removing %s", $coupon_code ) );
314
- WC()->cart->remove_coupon( $coupon_code );
315
- $calc_needed = true;
316
- }
317
- }
318
- return $calc_needed;
319
- }
320
-
321
- private $update_matched_autocoupons_executed = false;
322
 
323
  /**
324
  * Apply matched autocoupons and remove unmatched autocoupons.
325
  * @return void
326
- */
327
- function update_matched_autocoupons() {
328
- if ( $this->update_matched_autocoupons_executed ) {
329
- return;
330
- }
331
- $this->update_matched_autocoupons_executed = true;
332
- $this->log( "()" );
333
-
334
- //2.3.3 Keep track of queued coupons and apply when they validate
335
- $queuer = WJECF()->get_plugin('WJECF_Pro_Coupon_Queueing');
336
- if ( $queuer !== false ) {
337
- $queuer->apply_valid_queued_coupons();
338
- }
339
-
340
- //Get the coupons that should be in the cart
341
- $valid_coupons = $this->get_valid_auto_coupons();
342
- $valid_coupons = $this->individual_use_filter( $valid_coupons );
343
-
344
- $valid_coupon_codes = array();
345
- foreach ( $valid_coupons as $coupon ) {
346
- $valid_coupon_codes[] = WJECF_Wrap( $coupon )->get_code();
347
- }
348
-
349
- $this->log( sprintf( "Auto coupons that should be in cart: %s", implode( ', ', $valid_coupon_codes ) ) );
350
-
351
- $calc_needed = $this->remove_unmatched_autocoupons( $valid_coupon_codes );
352
-
353
- //Add valids
354
- foreach( $valid_coupons as $coupon ) {
355
- $coupon_code = WJECF_Wrap( $coupon )->get_code();
356
- if ( ! WC()->cart->has_discount( $coupon_code ) ) {
357
- $this->log( sprintf( "Applying auto coupon %s", $coupon_code ) );
358
-
359
- $apply_silently = WJECF_Wrap( $coupon )->get_meta( '_wjecf_apply_silently' ) == 'yes';
360
-
361
- if ( $apply_silently ) {
362
- $new_succss_msg = ''; // no message
363
- } else {
364
- $coupon_excerpt = $this->coupon_excerpt($coupon);
365
- $new_succss_msg = sprintf(
366
- __("Discount applied: %s", 'woocommerce-jos-autocoupon'),
367
- __( empty( $coupon_excerpt ) ? $coupon_code : $coupon_excerpt, 'woocommerce-jos-autocoupon')
368
- );
369
- }
370
-
371
- WJECF()->start_overwrite_success_message( $coupon, $new_succss_msg );
372
- WC()->cart->add_discount( $coupon_code ); //Causes calculation and will remove other coupons if it's a individual coupon
373
- WJECF()->stop_overwrite_success_message();
374
-
375
- $calc_needed = false; //Already done by adding the discount
376
-
377
- }
378
- }
379
-
380
- $this->log( 'Coupons in cart: ' . implode( ', ', WC()->cart->applied_coupons ) . ($calc_needed ? ". RECALC" : "") );
381
-
382
- if ( $calc_needed ) {
383
- WC()->cart->calculate_totals();
384
- }
385
-
386
- }
387
-
388
- private function get_valid_auto_coupons( ) {
389
- $valid_coupons = array();
390
- foreach ( $this->get_all_auto_coupons() as $coupon ) {
391
- if ( $this->coupon_can_be_applied( $coupon ) && $this->coupon_has_a_value( $coupon ) ) {
392
- $valid_coupons[] = $coupon;
393
- }
394
- }
395
- return $valid_coupons;
396
- }
397
 
398
  /**
399
  * Test whether the coupon is valid and has a discount > 0
400
  * @return bool
401
  */
402
- function coupon_can_be_applied( $coupon ) {
403
- $wrap_coupon = WJECF_Wrap( $coupon );
404
- $can_be_applied = true;
405
-
406
- //Test validity
407
- if ( ! $coupon->is_valid() ) {
408
- $can_be_applied = false;
409
- }
410
- //Test restricted emails
411
- //See WooCommerce: class-wc-cart.php function check_customer_coupons
412
- else if ( $can_be_applied && is_array( $wrap_coupon->get_email_restrictions() ) && sizeof( $wrap_coupon->get_email_restrictions() ) > 0 ) {
413
- $user_emails = array_map( 'sanitize_email', array_map( 'strtolower', $this->get_user_emails() ) );
414
- $coupon_emails = array_map( 'sanitize_email', array_map( 'strtolower', $wrap_coupon->get_email_restrictions() ) );
415
-
416
- if ( 0 == sizeof( array_intersect( $user_emails, $coupon_emails ) ) ) {
417
- $can_be_applied = false;
418
- }
419
- }
420
- return apply_filters( 'wjecf_coupon_can_be_applied', $can_be_applied, $coupon );
421
-
422
- }
423
-
424
- /**
425
- * Does the coupon have a value? (autocoupon should not be applied if it has no value)
426
- * @param WC_Coupon $coupon The coupon data
427
- * @return bool True if it has a value (discount, free shipping, whatever) otherwise false)
428
- **/
429
- function coupon_has_a_value( $coupon ) {
430
-
431
- $has_a_value = false;
432
-
433
- if ( WJECF_Wrap( $coupon )->get_free_shipping() ) {
434
- $has_a_value = true;
435
- } else {
436
- //Test whether discount > 0
437
- //See WooCommerce: class-wc-cart.php function get_discounted_price
438
- global $woocommerce;
439
- foreach ( $woocommerce->cart->get_cart() as $cart_item ) {
440
- if ( $coupon->is_valid_for_cart() || $coupon->is_valid_for_product( $cart_item['data'], $cart_item ) ) {
441
- $has_a_value = true;
442
- break;
443
- }
444
- }
445
- }
446
-
447
- return apply_filters( 'wjecf_coupon_has_a_value', $has_a_value, $coupon );
448
-
449
- }
450
-
451
-
452
 
453
  /**
454
  * Check wether the coupon is an "Auto coupon".
455
  * @param WC_Coupon $coupon The coupon data
456
  * @return bool true if it is an "Auto coupon"
457
- */
458
- public function is_auto_coupon($coupon) {
459
- return WJECF_Wrap( $coupon )->get_meta( '_wjecf_is_auto_coupon' ) == 'yes';
460
- }
461
-
462
- private function get_coupon_priority($coupon) {
463
- if ( WJECF()->is_pro() ) {
464
- $prio = WJECF_Wrap( $coupon )->get_meta( '_wjecf_coupon_priority' );
465
- if ( ! empty( $prio ) ) {
466
- return intval( $prio );
467
- }
468
- }
469
- return 0;
470
- }
471
-
472
 
473
  /**
474
  * Get the coupon excerpt (description)
475
  * @param WC_Coupon $coupon The coupon data
476
  * @return string The excerpt (translated)
477
- */
478
- private function coupon_excerpt($coupon) {
479
- $my_post = get_post( WJECF_Wrap( $coupon )->get_id() );
480
- return __( $my_post->post_excerpt, 'woocommerce-jos-autocoupon' );
481
- }
482
 
483
  /**
484
  * Get a list of the users' known email addresses
485
  *
486
  */
487
- private function get_user_emails() {
488
- if ( ! is_array($this->_user_emails) ) {
489
- $this->_user_emails = array();
490
- //Email of the logged in user
491
- if ( is_user_logged_in() ) {
492
- $current_user = wp_get_current_user();
493
- $this->_user_emails[] = $current_user->user_email;
494
- }
495
-
496
- if ( isset( $_POST['billing_email'] ) )
497
- $this->_user_emails[] = $_POST['billing_email'];
498
- }
499
- //$this->log( "User emails: " . implode( ",", $this->_user_emails ) );
500
- return $this->_user_emails;
501
- }
502
 
503
  /**
504
  * Append a single or an array of email addresses.
505
  * @param array|string $append_emails The email address(es) to be added
506
  * @return void
507
  */
508
- private function append_user_emails($append_emails) {
509
- //$append_emails must be an array
510
- if ( ! is_array( $append_emails ) ) {
511
- $append_emails = array( $append_emails );
512
- }
513
- $this->_user_emails = array_unique( array_merge( $this->get_user_emails(), $append_emails ) );
514
- //$this->log('Append emails: ' . implode( ',', $append_emails ) );
515
- }
516
-
517
- public function fetch_billing_email( $post_data ) {
518
- //post_data can be an array, or a query=string&like=this
519
- if ( ! is_array( $post_data ) ) {
520
- parse_str( $post_data, $posted );
521
- } else {
522
- $posted = $post_data;
523
- }
524
-
525
- if ( isset ( $posted['billing_email'] ) ) {
526
- $this->append_user_emails( $posted['billing_email'] );
527
- }
528
-
529
- }
530
-
531
- /**
532
- * Return an array of WC_Coupons with coupons that shouldn't cause individual use conflicts
533
- */
534
- private function individual_use_filter( $valid_auto_coupons ) {
535
- $filtered = array();
536
-
537
- //Any individual use non-autocoupons in the cart?
538
- foreach ( WC()->cart->get_applied_coupons() as $coupon_code ) {
539
- $coupon = new WC_Coupon( $coupon_code );
540
- if ( WJECF_Wrap( $coupon )->get_individual_use() && ! $this->is_auto_coupon( $coupon ) ) {
541
- return $filtered; //Don't allow any auto coupon
542
- }
543
- }
544
- foreach ( $valid_auto_coupons as $coupon ) {
545
- if ( ! WJECF_Wrap( $coupon )->get_individual_use() || empty( $filtered ) ) {
546
- $filtered[] = $coupon;
547
- if ( WJECF_Wrap( $coupon )->get_individual_use() ) {
548
- break;
549
- }
550
- }
551
- }
552
- return $filtered;
553
- }
554
-
555
  /**
556
  * Get a list of all auto coupon codes
557
  * @return array All auto coupon codes
558
- */
559
- public function get_all_auto_coupons() {
560
- if ( ! is_array( $this->_autocoupons ) ) {
561
- $this->_autocoupons = array();
562
-
563
- $query_args = array(
564
- 'posts_per_page' => -1,
565
- 'post_type' => 'shop_coupon',
566
- 'post_status' => 'publish',
567
- 'orderby' => array( 'title' => 'ASC' ),
568
- 'meta_query' => array(
569
- array(
570
- 'key' => '_wjecf_is_auto_coupon',
571
- 'compare' => '=',
572
- 'value' => 'yes',
573
- ),
574
- )
575
- );
576
-
577
- $query = new WP_Query($query_args);
578
- foreach ($query->posts as $post) {
579
- $coupon = new WC_Coupon($post->post_title);
580
- if ( $this->is_auto_coupon($coupon) ) {
581
- $this->_autocoupons[ WJECF_Wrap( $coupon )->get_code() ] = $coupon;
582
- }
583
- }
584
-
585
- //Sort by priority
586
- @uasort( $this->_autocoupons , array( $this, 'sort_auto_coupons' ) ); //Ignore error PHP Bug #50688
587
-
588
- $coupon_codes = array();
589
- foreach( $this->_autocoupons as $coupon ) {
590
- $coupon_codes[] = WJECF_Wrap( $coupon )->get_code();
591
- }
592
-
593
- $this->log( "Autocoupons: " . implode(", ", $coupon_codes ) );
594
- }
595
-
596
- return $this->_autocoupons;
597
- }
598
-
599
- /**
600
- * Compare function to sort coupons by priority
601
- * @param type $a
602
- * @param type $b
603
- * @return type
604
- */
605
- private function sort_auto_coupons( $coupon_a, $coupon_b ) {
606
- $prio_a = $this->get_coupon_priority( $coupon_a );
607
- $prio_b = $this->get_coupon_priority( $coupon_b );
608
- $this->log("A: $prio_a B: $prio_b ");
609
- if ( $prio_a == $prio_b ) {
610
- return $a->code < $b->code ? -1 : 1; //By title ASC
611
- } else {
612
- return $prio_a > $prio_b ? -1 : 1; //By prio DESC
613
- }
614
- }
615
  }
4
 
5
  class WJECF_AutoCoupon extends Abstract_WJECF_Plugin {
6
 
7
+ private $_autocoupons = null;
8
+
9
+ private $_user_emails = null;
10
+
11
+ protected $_executed_coupon_by_url = false;
12
+
13
+ public function __construct() {
14
+ $this->set_plugin_data( array(
15
+ 'description' => __( 'Allow coupons to be automatically applied to the cart when restrictions are met or by url.', 'woocommerce-jos-autocoupon' ),
16
+ 'dependencies' => array(),
17
+ 'can_be_disabled' => true
18
+ ) );
19
+ }
20
+
21
+ public function init_hook() {
22
+ if ( ! class_exists('WC_Coupon') ) {
23
+ return;
24
+ }
25
+
26
+ //Frontend hooks - logic
27
+ if ( WJECF_WC()->check_woocommerce_version('2.3.0')) {
28
+ add_action( 'woocommerce_after_calculate_totals', array( &$this, 'update_matched_autocoupons' ) );
29
+ } else {
30
+ //WC Versions prior to 2.3.0 don't have after_calculate_totals hook, this is a fallback
31
+ add_action( 'woocommerce_cart_updated', array( &$this, 'update_matched_autocoupons' ) );
32
+ }
33
+ add_action( 'woocommerce_check_cart_items', array( &$this, 'remove_unmatched_autocoupons' ) , 0, 0 ); //Remove coupon before WC does it and shows a message
34
+ //Last check for coupons with restricted_emails
35
+ add_action( 'woocommerce_checkout_update_order_review', array( &$this, 'fetch_billing_email' ), 10 ); // AJAX One page checkout
36
+
37
+ //Frontend hooks - visualisation
38
+ add_filter('woocommerce_cart_totals_coupon_label', array( &$this, 'coupon_label' ), 10, 2 );
39
+ add_filter('woocommerce_cart_totals_coupon_html', array( &$this, 'coupon_html' ), 10, 2 );
40
+
41
+ //Inhibit redirect to cart when apply_coupon supplied
42
+ add_filter('option_woocommerce_cart_redirect_after_add', array ( &$this, 'option_woocommerce_cart_redirect_after_add') );
43
+
44
+ if ( ! is_ajax() ) {
45
+ //Get cart should not be called before the wp_loaded action nor the add_to_cart_action (class-wc-form-handler)
46
+ add_action( 'wp_loaded', array( &$this, 'coupon_by_url' ), 90 ); //Coupon through url
47
+ }
48
+ }
49
 
50
  /* ADMIN HOOKS */
51
+ public function init_admin_hook() {
52
+ add_action( 'wjecf_woocommerce_coupon_options_extended_features', array( $this, 'admin_coupon_options_extended_features' ), 20, 2 );
53
+ add_action( 'woocommerce_process_shop_coupon_meta', array( $this, 'process_shop_coupon_meta' ), 10, 2 );
54
+
55
+ //Inject columns
56
+ if ( WJECF()->is_pro() ) {
57
+ WJECF()->inject_coupon_column(
58
+ '_wjecf_auto_coupon',
59
+ __( 'Auto coupon', 'woocommerce-jos-autocoupon' ),
60
+ array( $this, 'admin_render_shop_coupon_columns' ), 'coupon_code'
61
+ );
62
+ WJECF()->inject_coupon_column(
63
+ '_wjecf_individual_use',
64
+ __( 'Individual use', 'woocommerce-jos-autocoupon' ),
65
+ array( $this, 'admin_render_shop_coupon_columns' ), 'coupon_code'
66
+ );
67
+ }
68
+
69
+ add_filter( 'views_edit-shop_coupon', array( $this, 'admin_views_edit_coupon' ) );
70
+ add_filter( 'request', array( $this, 'admin_request_query' ) );
71
+ }
72
+
73
+ /**
74
+ * Output a coupon custom column value
75
+ *
76
+ * @param string $column
77
+ * @param WP_Post The coupon post object
78
+ */
79
+ public function admin_render_shop_coupon_columns( $column, $post ) {
80
+ $wrap_coupon = WJECF_Wrap( $post->ID );
81
+
82
+ switch ( $column ) {
83
+ case '_wjecf_auto_coupon' :
84
+ $is_auto_coupon = $wrap_coupon->get_meta( '_wjecf_is_auto_coupon', true ) == 'yes';
85
+ echo $is_auto_coupon ? __( 'Yes', 'woocommerce' ) : __( 'No', 'woocommerce' );
86
+ if ( $is_auto_coupon ) {
87
+ $prio = $wrap_coupon->get_meta( '_wjecf_coupon_priority', true );
88
+ if ( $prio ) echo " (" . intval( $prio ) . ")";
89
+ }
90
+ break;
91
+ case '_wjecf_individual_use' :
92
+ $individual = $wrap_coupon->get_individual_use();
93
+ echo $individual ? __( 'Yes', 'woocommerce' ) : __( 'No', 'woocommerce' );
94
+ break;
95
+ }
96
+ }
97
+
98
+ public function admin_views_edit_coupon( $views ) {
99
+ global $post_type, $wp_query;
100
+
101
+ $class = ( isset( $wp_query->query['meta_key'] ) && $wp_query->query['meta_key'] == '_wjecf_is_auto_coupon' ) ? 'current' : '';
102
+ $query_string = remove_query_arg(array( 'wjecf_is_auto_coupon' ));
103
+ $query_string = add_query_arg( 'wjecf_is_auto_coupon', '1', $query_string );
104
+ $views['wjecf_is_auto_coupon'] = '<a href="' . esc_url( $query_string ) . '" class="' . esc_attr( $class ) . '">' . __( 'Auto coupons', 'woocommerce' ) . '</a>';
105
+
106
+ return $views;
107
+ }
108
+
109
+ /**
110
+ * Filters and sorting handler
111
+ *
112
+ * @param array $vars
113
+ * @return array
114
+ */
115
+ public function admin_request_query( $vars ) {
116
+ global $typenow, $wp_query, $wp_post_statuses;
117
+
118
+ if ( 'shop_coupon' === $typenow ) {
119
+ if ( isset( $_GET['wjecf_is_auto_coupon'] ) ) {
120
+ $vars['meta_key'] = '_wjecf_is_auto_coupon';
121
+ $vars['meta_value'] = $_GET['wjecf_is_auto_coupon'] == '1' ? 'yes' : 'no';
122
+ }
123
+ }
124
+
125
+ return $vars;
126
+ }
127
+
128
+ public function admin_coupon_options_extended_features( $thepostid, $post ) {
129
+
130
+ //=============================
131
+ //Title
132
+ echo "<h3>" . esc_html( __( 'Auto coupon', 'woocommerce-jos-autocoupon' ) ). "</h3>\n";
133
+
134
+
135
+ //=============================
136
+ // Auto coupon checkbox
137
+ woocommerce_wp_checkbox( array(
138
+ 'id' => '_wjecf_is_auto_coupon',
139
+ 'label' => __( 'Auto coupon', 'woocommerce-jos-autocoupon' ),
140
+ 'description' => __( "Automatically add the coupon to the cart if the restrictions are met. Please enter a description when you check this box, the description will be shown in the customer's cart if the coupon is applied.", 'woocommerce-jos-autocoupon' )
141
+ ) );
142
+
143
+ echo '<div class="_wjecf_show_if_autocoupon">';
144
+ if ( WJECF()->is_pro() ) {
145
+ // Maximum quantity of matching products (product/category)
146
+ woocommerce_wp_text_input( array(
147
+ 'id' => '_wjecf_coupon_priority',
148
+ 'label' => __( 'Priority', 'woocommerce-jos-autocoupon' ),
149
+ 'placeholder' => __( 'No priority', 'woocommerce' ),
150
+ 'description' => __( 'When \'individual use\' is checked, auto coupons with a higher value will have priority over other auto coupons.', 'woocommerce-jos-autocoupon' ),
151
+ 'data_type' => 'decimal',
152
+ 'desc_tip' => true
153
+ ) );
154
+ }
155
+
156
+ //=============================
157
+ // Apply without notice
158
+ woocommerce_wp_checkbox( array(
159
+ 'id' => '_wjecf_apply_silently',
160
+ 'label' => __( 'Apply silently', 'woocommerce-jos-autocoupon' ),
161
+ 'description' => __( "Don't display a message when this coupon is automatically applied.", 'woocommerce-jos-autocoupon' ),
162
+ ) );
163
+ echo '</div>';
164
+
165
+ ?>
166
+ <script type="text/javascript">
167
+ //Hide/show when AUTO-COUPON value changes
168
+ function update_wjecf_apply_silently_field( animation ) {
169
+ if ( animation === undefined ) animation = 'slow';
170
+
171
+ if (jQuery("#_wjecf_is_auto_coupon").prop('checked')) {
172
+ jQuery("._wjecf_show_if_autocoupon").show( animation );
173
+ } else {
174
+ jQuery("._wjecf_show_if_autocoupon").hide( animation );
175
+ }
176
+ }
177
+ update_wjecf_apply_silently_field( 0 );
178
+
179
+ jQuery("#_wjecf_is_auto_coupon").click( update_wjecf_apply_silently_field );
180
+ </script>
181
+ <?php
182
+
183
+ }
184
+
185
+ public function process_shop_coupon_meta( $post_id, $post ) {
186
+ $wrap_coupon = WJECF_Wrap( $post_id );
187
+
188
+ $autocoupon = isset( $_POST['_wjecf_is_auto_coupon'] );
189
+ $wrap_coupon->set_meta( '_wjecf_is_auto_coupon', $autocoupon ? 'yes' : 'no' );
190
+ $wrap_coupon->set_meta( '_wjecf_apply_silently', isset( $_POST['_wjecf_apply_silently'] ) ? 'yes' : 'no' );
191
+ if ( WJECF()->is_pro() ) {
192
+ $wrap_coupon->set_meta( '_wjecf_coupon_priority', intval( $_POST['_wjecf_coupon_priority'] ) );
193
+ }
194
+
195
+ $wrap_coupon->save();
196
+ }
197
 
198
  /* FRONTEND HOOKS */
199
 
200
+ /**
201
+ ** Inhibit redirect to cart when apply_coupon supplied
202
+ */
203
+ public function option_woocommerce_cart_redirect_after_add ( $value ) {
204
+ if ( ! $this->_executed_coupon_by_url && isset( $_GET['apply_coupon'] ) ) {
205
+ $value = 'no';
206
+ }
207
+ return $value;
208
+ }
209
+
210
+ /**
211
+ * Add coupon through url
212
+ */
213
+ public function coupon_by_url() {
214
+ $must_redirect = false;
215
+
216
+ //Apply coupon by url
217
+ if ( isset( $_GET['apply_coupon'] ) ) {
218
+ $must_redirect = true;
219
+ $this->_executed_coupon_by_url = true;
220
+ $split = explode( ",", wc_clean( $_GET['apply_coupon'] ) );
221
+ //2.2.2 Make sure a session cookie is set
222
+ if( ! WC()->session->has_session() )
223
+ {
224
+ WC()->session->set_customer_session_cookie( true );
225
+ }
226
+
227
+ $cart = WC()->cart;
228
+ foreach ( $split as $coupon_code ) {
229
+ $coupon = WJECF_WC()->get_coupon( $coupon_code );
230
+ if ( WJECF_WC()->check_woocommerce_version('2.3.0') && ! WJECF_Wrap( $coupon )->exists() ) {
231
+ wc_add_notice( $coupon->get_coupon_error( WC_Coupon::E_WC_COUPON_NOT_EXIST ), 'error' );
232
+ } else {
233
+ $valid = $coupon->is_valid();
234
+ if ( $valid ) {
235
+ $cart->add_discount( $coupon_code );
236
+ }
237
+ }
238
+ }
239
+ }
240
+
241
+ //Redirect to page without autocoupon query args
242
+ if ( $must_redirect ) {
243
+ $requested_url = is_ssl() ? 'https://' : 'http://';
244
+ $requested_url .= $_SERVER['HTTP_HOST'];
245
+ $requested_url .= $_SERVER['REQUEST_URI'];
246
+
247
+ wp_safe_redirect( remove_query_arg( array( 'apply_coupon', 'add-to-cart' ), ( $requested_url ) ) );
248
+ exit;
249
+ }
250
+ }
251
+
252
  /**
253
  * Overwrite the html created by wc_cart_totals_coupon_label() so a descriptive text will be shown for the discount.
254
  * @param string $originaltext The default text created by wc_cart_totals_coupon_label()
255
  * @param WC_Coupon $coupon The coupon data
256
  * @return string The overwritten text
257
+ */
258
+ function coupon_label( $originaltext, $coupon ) {
259
+
260
+ if ( $this->is_auto_coupon($coupon) ) {
261
+
262
+ return $this->coupon_excerpt($coupon); //__($this->autocoupons[$coupon->code], 'woocommerce-jos-autocoupon');
263
+ } else {
264
+ return $originaltext;
265
+ }
266
+ }
267
+
268
  /**
269
  * Overwrite the html created by wc_cart_totals_coupon_html(). This function is required to remove the "Remove" link.
270
  * @param string $originaltext The html created by wc_cart_totals_coupon_html()
271
  * @param WC_Coupon $coupon The coupon data
272
  * @return string The overwritten html
273
  */
274
+ function coupon_html( $originaltext, $coupon ) {
275
+ if ( $this->is_auto_coupon($coupon) ) {
276
+ $value = array();
277
+
278
+ if ( $amount = WC()->cart->get_coupon_discount_amount( WJECF_Wrap( $coupon )->get_code(), WC()->cart->display_cart_ex_tax ) ) {
279
+ $discount_html = '-' . wc_price( $amount );
280
+ } else {
281
+ $discount_html = '';
282
+ }
283
+
284
+ $value[] = apply_filters( 'woocommerce_coupon_discount_amount_html', $discount_html, $coupon );
285
+
286
+ if ( WJECF_Wrap( $coupon )->get_free_shipping() ) {
287
+ $value[] = __( 'Free shipping coupon', 'woocommerce' );
288
+ }
289
+
290
+ return implode(', ', array_filter($value)); //Remove empty array elements
291
+ } else {
292
+ return $originaltext;
293
+ }
294
+ }
295
+
296
+
297
+ function remove_unmatched_autocoupons( $valid_coupon_codes = null ) {
298
+ if ( $valid_coupon_codes === null ) {
299
+ //Get the coupons that should be in the cart
300
+ $valid_coupons = $this->get_valid_auto_coupons();
301
+ $valid_coupons = $this->individual_use_filter( $valid_coupons );
302
+ $valid_coupon_codes = array();
303
+ foreach ( $valid_coupons as $coupon ) {
304
+ $valid_coupon_codes[] = WJECF_Wrap( $coupon )->get_code();
305
+ }
306
+ }
307
+
308
+ //Remove invalids
309
+ $calc_needed = false;
310
+ foreach ( $this->get_all_auto_coupons() as $coupon ) {
311
+ $coupon_code = WJECF_Wrap( $coupon )->get_code();
312
+ if ( WC()->cart->has_discount( $coupon_code ) && ! in_array( $coupon_code, $valid_coupon_codes ) ) {
313
+ $this->log( sprintf( "Removing %s", $coupon_code ) );
314
+ WC()->cart->remove_coupon( $coupon_code );
315
+ $calc_needed = true;
316
+ }
317
+ }
318
+ return $calc_needed;
319
+ }
320
+
321
+ private $update_matched_autocoupons_executed = false;
322
 
323
  /**
324
  * Apply matched autocoupons and remove unmatched autocoupons.
325
  * @return void
326
+ */
327
+ function update_matched_autocoupons() {
328
+ if ( $this->update_matched_autocoupons_executed ) {
329
+ return;
330
+ }
331
+ $this->update_matched_autocoupons_executed = true;
332
+ $this->log( "()" );
333
+
334
+ //2.3.3 Keep track of queued coupons and apply when they validate
335
+ $queuer = WJECF()->get_plugin('WJECF_Pro_Coupon_Queueing');
336
+ if ( $queuer !== false ) {
337
+ $queuer->apply_valid_queued_coupons();
338
+ }
339
+
340
+ //Get the coupons that should be in the cart
341
+ $valid_coupons = $this->get_valid_auto_coupons();
342
+ $valid_coupons = $this->individual_use_filter( $valid_coupons );
343
+
344
+ $valid_coupon_codes = array();
345
+ foreach ( $valid_coupons as $coupon ) {
346
+ $valid_coupon_codes[] = WJECF_Wrap( $coupon )->get_code();
347
+ }
348
+
349
+ $this->log( sprintf( "Auto coupons that should be in cart: %s", implode( ', ', $valid_coupon_codes ) ) );
350
+
351
+ $calc_needed = $this->remove_unmatched_autocoupons( $valid_coupon_codes );
352
+
353
+ //Add valids
354
+ foreach( $valid_coupons as $coupon ) {
355
+ $coupon_code = WJECF_Wrap( $coupon )->get_code();
356
+ if ( ! WC()->cart->has_discount( $coupon_code ) ) {
357
+ $this->log( sprintf( "Applying auto coupon %s", $coupon_code ) );
358
+
359
+ $apply_silently = WJECF_Wrap( $coupon )->get_meta( '_wjecf_apply_silently' ) == 'yes';
360
+
361
+ if ( $apply_silently ) {
362
+ $new_succss_msg = ''; // no message
363
+ } else {
364
+ $coupon_excerpt = $this->coupon_excerpt($coupon);
365
+ $new_succss_msg = sprintf(
366
+ __("Discount applied: %s", 'woocommerce-jos-autocoupon'),
367
+ __( empty( $coupon_excerpt ) ? $coupon_code : $coupon_excerpt, 'woocommerce-jos-autocoupon')
368
+ );
369
+ }
370
+
371
+ WJECF()->start_overwrite_success_message( $coupon, $new_succss_msg );
372
+ WC()->cart->add_discount( $coupon_code ); //Causes calculation and will remove other coupons if it's a individual coupon
373
+ WJECF()->stop_overwrite_success_message();
374
+
375
+ $calc_needed = false; //Already done by adding the discount
376
+
377
+ }
378
+ }
379
+
380
+ $this->log( 'Coupons in cart: ' . implode( ', ', WC()->cart->applied_coupons ) . ($calc_needed ? ". RECALC" : "") );
381
+
382
+ if ( $calc_needed ) {
383
+ WC()->cart->calculate_totals();
384
+ }
385
+
386
+ }
387
+
388
+ private function get_valid_auto_coupons( ) {
389
+ $valid_coupons = array();
390
+ foreach ( $this->get_all_auto_coupons() as $coupon ) {
391
+ if ( $this->coupon_can_be_applied( $coupon ) && $this->coupon_has_a_value( $coupon ) ) {
392
+ $valid_coupons[] = $coupon;
393
+ }
394
+ }
395
+ return $valid_coupons;
396
+ }
397
 
398
  /**
399
  * Test whether the coupon is valid and has a discount > 0
400
  * @return bool
401
  */
402
+ function coupon_can_be_applied( $coupon ) {
403
+ $wrap_coupon = WJECF_Wrap( $coupon );
404
+ $can_be_applied = true;
405
+
406
+ //Test validity
407
+ if ( ! $coupon->is_valid() ) {
408
+ $can_be_applied = false;
409
+ }
410
+ //Test restricted emails
411
+ //See WooCommerce: class-wc-cart.php function check_customer_coupons
412
+ else if ( $can_be_applied && is_array( $wrap_coupon->get_email_restrictions() ) && sizeof( $wrap_coupon->get_email_restrictions() ) > 0 ) {
413
+ $user_emails = array_map( 'sanitize_email', array_map( 'strtolower', $this->get_user_emails() ) );
414
+ $coupon_emails = array_map( 'sanitize_email', array_map( 'strtolower', $wrap_coupon->get_email_restrictions() ) );
415
+
416
+ if ( 0 == sizeof( array_intersect( $user_emails, $coupon_emails ) ) ) {
417
+ $can_be_applied = false;
418
+ }
419
+ }
420
+ return apply_filters( 'wjecf_coupon_can_be_applied', $can_be_applied, $coupon );
421
+
422
+ }
423
+
424
+ /**
425
+ * Does the coupon have a value? (autocoupon should not be applied if it has no value)
426
+ * @param WC_Coupon $coupon The coupon data
427
+ * @return bool True if it has a value (discount, free shipping, whatever) otherwise false)
428
+ **/
429
+ function coupon_has_a_value( $coupon ) {
430
+
431
+ $has_a_value = false;
432
+
433
+ if ( WJECF_Wrap( $coupon )->get_free_shipping() ) {
434
+ $has_a_value = true;
435
+ } else {
436
+ //Test whether discount > 0
437
+ //See WooCommerce: class-wc-cart.php function get_discounted_price
438
+ global $woocommerce;
439
+ foreach ( $woocommerce->cart->get_cart() as $cart_item ) {
440
+ if ( $coupon->is_valid_for_cart() || $coupon->is_valid_for_product( $cart_item['data'], $cart_item ) ) {
441
+ $has_a_value = true;
442
+ break;
443
+ }
444
+ }
445
+ }
446
+
447
+ return apply_filters( 'wjecf_coupon_has_a_value', $has_a_value, $coupon );
448
+
449
+ }
450
+
451
+
452
 
453
  /**
454
  * Check wether the coupon is an "Auto coupon".
455
  * @param WC_Coupon $coupon The coupon data
456
  * @return bool true if it is an "Auto coupon"
457
+ */
458
+ public function is_auto_coupon($coupon) {
459
+ return WJECF_Wrap( $coupon )->get_meta( '_wjecf_is_auto_coupon' ) == 'yes';
460
+ }
461
+
462
+ private function get_coupon_priority($coupon) {
463
+ if ( WJECF()->is_pro() ) {
464
+ $prio = WJECF_Wrap( $coupon )->get_meta( '_wjecf_coupon_priority' );
465
+ if ( ! empty( $prio ) ) {
466
+ return intval( $prio );
467
+ }
468
+ }
469
+ return 0;
470
+ }
471
+
472
 
473
  /**
474
  * Get the coupon excerpt (description)
475
  * @param WC_Coupon $coupon The coupon data
476
  * @return string The excerpt (translated)
477
+ */
478
+ private function coupon_excerpt($coupon) {
479
+ $my_post = get_post( WJECF_Wrap( $coupon )->get_id() );
480
+ return __( $my_post->post_excerpt, 'woocommerce-jos-autocoupon' );
481
+ }
482
 
483
  /**
484
  * Get a list of the users' known email addresses
485
  *
486
  */
487
+ private function get_user_emails() {
488
+ if ( ! is_array($this->_user_emails) ) {
489
+ $this->_user_emails = array();
490
+ //Email of the logged in user
491
+ if ( is_user_logged_in() ) {
492
+ $current_user = wp_get_current_user();
493
+ $this->_user_emails[] = $current_user->user_email;
494
+ }
495
+
496
+ if ( isset( $_POST['billing_email'] ) )
497
+ $this->_user_emails[] = $_POST['billing_email'];
498
+ }
499
+ //$this->log( "User emails: " . implode( ",", $this->_user_emails ) );
500
+ return $this->_user_emails;
501
+ }
502
 
503
  /**
504
  * Append a single or an array of email addresses.
505
  * @param array|string $append_emails The email address(es) to be added
506
  * @return void
507
  */
508
+ private function append_user_emails($append_emails) {
509
+ //$append_emails must be an array
510
+ if ( ! is_array( $append_emails ) ) {
511
+ $append_emails = array( $append_emails );
512
+ }
513
+ $this->_user_emails = array_unique( array_merge( $this->get_user_emails(), $append_emails ) );
514
+ //$this->log('Append emails: ' . implode( ',', $append_emails ) );
515
+ }
516
+
517
+ public function fetch_billing_email( $post_data ) {
518
+ //post_data can be an array, or a query=string&like=this
519
+ if ( ! is_array( $post_data ) ) {
520
+ parse_str( $post_data, $posted );
521
+ } else {
522
+ $posted = $post_data;
523
+ }
524
+
525
+ if ( isset ( $posted['billing_email'] ) ) {
526
+ $this->append_user_emails( $posted['billing_email'] );
527
+ }
528
+
529
+ }
530
+
531
+ /**
532
+ * Return an array of WC_Coupons with coupons that shouldn't cause individual use conflicts
533
+ */
534
+ private function individual_use_filter( $valid_auto_coupons ) {
535
+ $filtered = array();
536
+
537
+ //Any individual use non-autocoupons in the cart?
538
+ foreach ( WC()->cart->get_applied_coupons() as $coupon_code ) {
539
+ $coupon = new WC_Coupon( $coupon_code );
540
+ if ( WJECF_Wrap( $coupon )->get_individual_use() && ! $this->is_auto_coupon( $coupon ) ) {
541
+ return $filtered; //Don't allow any auto coupon
542
+ }
543
+ }
544
+ foreach ( $valid_auto_coupons as $coupon ) {
545
+ if ( ! WJECF_Wrap( $coupon )->get_individual_use() || empty( $filtered ) ) {
546
+ $filtered[] = $coupon;
547
+ if ( WJECF_Wrap( $coupon )->get_individual_use() ) {
548
+ break;
549
+ }
550
+ }
551
+ }
552
+ return $filtered;
553
+ }
554
+
555
  /**
556
  * Get a list of all auto coupon codes
557
  * @return array All auto coupon codes
558
+ */
559
+ public function get_all_auto_coupons() {
560
+ if ( ! is_array( $this->_autocoupons ) ) {
561
+ $this->_autocoupons = array();
562
+
563
+ $query_args = array(
564
+ 'posts_per_page' => -1,
565
+ 'post_type' => 'shop_coupon',
566
+ 'post_status' => 'publish',
567
+ 'orderby' => array( 'title' => 'ASC' ),
568
+ 'meta_query' => array(
569
+ array(
570
+ 'key' => '_wjecf_is_auto_coupon',
571
+ 'compare' => '=',
572
+ 'value' => 'yes',
573
+ ),
574
+ )
575
+ );
576
+
577
+ $query = new WP_Query($query_args);
578
+ foreach ($query->posts as $post) {
579
+ $coupon = new WC_Coupon($post->post_title);
580
+ if ( $this->is_auto_coupon($coupon) ) {
581
+ $this->_autocoupons[ WJECF_Wrap( $coupon )->get_code() ] = $coupon;
582
+ }
583
+ }
584
+
585
+ //Sort by priority
586
+ @uasort( $this->_autocoupons , array( $this, 'sort_auto_coupons' ) ); //Ignore error PHP Bug #50688
587
+
588
+ $coupon_codes = array();
589
+ foreach( $this->_autocoupons as $coupon ) {
590
+ $coupon_codes[] = WJECF_Wrap( $coupon )->get_code();
591
+ }
592
+
593
+ $this->log( "Autocoupons: " . implode(", ", $coupon_codes ) );
594
+ }
595
+
596
+ return $this->_autocoupons;
597
+ }
598
+
599
+ /**
600
+ * Compare function to sort coupons by priority
601
+ * @param type $a
602
+ * @param type $b
603
+ * @return type
604
+ */
605
+ private function sort_auto_coupons( $coupon_a, $coupon_b ) {
606
+ $prio_a = $this->get_coupon_priority( $coupon_a );
607
+ $prio_b = $this->get_coupon_priority( $coupon_b );
608
+ $this->log("A: $prio_a B: $prio_b ");
609
+ if ( $prio_a == $prio_b ) {
610
+ return $a->code < $b->code ? -1 : 1; //By title ASC
611
+ } else {
612
+ return $prio_a > $prio_b ? -1 : 1; //By prio DESC
613
+ }
614
+ }
615
  }
includes/wjecf-controller.php CHANGED
@@ -7,759 +7,759 @@ defined('ABSPATH') or die();
7
  */
8
  class WJECF_Controller {
9
 
10
- // Coupon message codes
11
- //NOTE: I use prefix 79 for this plugin; there's no guarantee that other plugins don't use the same values!
12
- const E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET = 79100;
13
- const E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET = 79101;
14
- const E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET = 79102;
15
- const E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET = 79103;
16
- const E_WC_COUPON_SHIPPING_METHOD_NOT_MET = 79104;
17
- const E_WC_COUPON_PAYMENT_METHOD_NOT_MET = 79105;
18
- const E_WC_COUPON_NOT_FOR_THIS_USER = 79106;
19
-
20
- protected $debug_mode = false;
21
- protected $log = array();
22
- public $options = false;
23
-
24
- //Plugin data
25
- public $plugin_path; // Has trailing slash
26
- public $plugin_url; // Has trailing slash
27
- public $version; // Use for js
28
-
29
- /**
30
- * Singleton Instance
31
- *
32
- * @static
33
- * @return Singleton Instance
34
- */
35
- public static function instance() {
36
- if ( is_null( self::$_instance ) ) {
37
- self::$_instance = new self();
38
- }
39
- return self::$_instance;
40
- }
41
- protected static $_instance = null;
42
-
43
-
44
- public function __construct() {
45
- $this->debug_mode = false && defined( 'WP_DEBUG' ) && WP_DEBUG;
46
- //Paths
47
- $this->plugin_path = plugin_dir_path( dirname(__FILE__) );
48
- $this->plugin_url = plugins_url( '/', dirname( __FILE__ ) );
49
-
50
- $filename = $this->is_pro() ? "woocommerce-jos-autocoupon-pro.php" : "woocommerce-jos-autocoupon.php" ;
51
-
52
- //Version
53
- $default_headers = array(
54
- 'Version' => 'Version',
55
- );
56
- $plugin_data = get_file_data( $this->plugin_path . $filename, $default_headers, 'plugin' );
57
- $this->version = $plugin_data['Version'];
58
- }
59
-
60
- public function start() {
61
- add_action('init', array( $this, 'init_hook' ));
62
- }
63
-
64
- public function init_hook() {
65
- if ( ! class_exists('WC_Coupon') ) {
66
- add_action( 'admin_notices', array( $this, 'admin_notice_woocommerce_not_found' ) );
67
- return;
68
- }
69
-
70
- $this->controller_init();
71
-
72
- /**
73
- * Fires before the WJECF plugins are initialised.
74
- *
75
- * Perfect hook for themes or plugins to load custom WJECF plugins.
76
- *
77
- * @since 2.3.7
78
- **/
79
- do_action( 'wjecf_init_plugins');
80
-
81
- //Start the plugins
82
  foreach ( WJECF()->get_plugins() as $name => $plugin ) {
83
- if ( $plugin->plugin_is_enabled() ) {
84
-
85
- foreach( $plugin->get_plugin_dependencies() as $dependency_name ) {
86
- $dependency = $this->get_plugin( $dependency_name );
87
- if ( ! $dependency || ! $dependency->plugin_is_enabled() ) {
88
- error_log('Unable to initialize ' . $name . ' because it requires ' . $dependency_name );
89
- continue;
90
- }
91
- }
92
-
93
- $plugin->init_hook();
94
- if ( is_admin() ) {
95
- $plugin->init_admin_hook();
96
- }
97
- }
98
- }
99
- }
100
-
101
- public function controller_init() {
102
-
103
- $this->log( "INIT " . ( is_ajax() ? "AJAX" : is_admin() ? "ADMIN" : "FRONTEND" ) . " " . $_SERVER['REQUEST_URI'] );
104
-
105
- $this->init_options();
106
-
107
- //Frontend hooks
108
-
109
- //assert_coupon_is_valid (which raises exception on invalid coupon) can only be used on WC 2.3.0 and up
110
- if ( WJECF_WC()->check_woocommerce_version('2.3.0') ) {
111
- add_filter('woocommerce_coupon_is_valid', array( $this, 'assert_coupon_is_valid' ), 10, 2 );
112
- } else {
113
- add_filter('woocommerce_coupon_is_valid', array( $this, 'coupon_is_valid' ), 10, 2 );
114
- }
115
-
116
- add_filter('woocommerce_coupon_error', array( $this, 'woocommerce_coupon_error' ), 10, 3 );
117
- add_action('woocommerce_coupon_loaded', array( $this, 'woocommerce_coupon_loaded' ), 10, 1);
118
- add_filter('woocommerce_coupon_get_discount_amount', array( $this, 'woocommerce_coupon_get_discount_amount' ), 10, 5);
119
  add_action( 'wp_footer', array( $this, 'render_log' ) ); //Log
120
- }
121
-
122
- protected $plugins = array();
123
-
124
- /**
125
- * Load a WJECF Plugin (class name)
126
- * @param string $class_name The class name of the plugin
127
- * @return bool True if succeeded, otherwise false
128
- */
129
- public function add_plugin( $class_name ) {
130
- if ( isset( $this->plugins[ $class_name ] ) ) {
131
- return false; //Already loaded
132
- }
133
-
134
- if ( ! class_exists( $class_name ) ) {
135
- return false; //Not found
136
- }
137
-
138
- $the_plugin = new $class_name();
139
- foreach( $the_plugin->get_plugin_dependencies() as $dependency ) {
140
- if ( ! class_exists( $dependency ) ) {
141
- $this->log( 'Unknown dependency: ' . $dependency . ' for plugin ' . $class_name );
142
- return false;
143
- }
144
-
145
- if ( isset( $this->plugins[ $class_name ] ) ) {
146
- continue; //dependency is al geladen
147
- }
148
-
149
- $this->add_plugin( $dependency );
150
- }
151
-
152
- //Assert dependencies
153
- try {
154
- $the_plugin->assert_dependencies();
155
- } catch (Exception $ex) {
156
- $msg = sprintf('Failed loading %s: %s', $class_name, $ex->getMessage() );
157
- if ( $wjecf_admin = WJECF()->get_plugin('WJECF_Admin') ) {
158
- $wjecf_admin->enqueue_notice( $msg, 'error' );
159
- } else {
160
- }
161
- error_log("PRINTR".print_r($wjecf_admin, true));
162
- error_log( $msg );
163
- return false;
164
- }
165
-
166
- $this->plugins[ $class_name ] = $the_plugin;
167
- $this->log( 'Loaded plugin: ' . $class_name );
168
-
169
- return true;
170
- }
171
-
172
- public function get_plugins() {
173
- return $this->plugins;
174
- }
175
-
176
- /**
177
- * Retrieves the WJECF Plugin
178
- * @param string $class_name
179
- * @return object|bool The plugin if found, otherwise returns false
180
- */
181
- public function get_plugin( $class_name ) {
182
- if ( isset( $this->plugins[ $class_name ] ) ) {
183
- return $this->plugins[ $class_name ];
184
- } else {
185
- return false;
186
- }
187
- }
188
-
189
- public function init_options() {
190
  $this->options = get_option( 'wjecf_options' );
191
- if (false === $this->options) {
192
- $this->options = array( 'db_version' => 0 );
193
- }
194
- }
195
 
196
  /* FRONTEND HOOKS */
197
 
198
- /**
199
- * Notifies that WooCommerce has not been detected.
200
- * @return void
201
- */
202
  public function admin_notice_woocommerce_not_found() {
203
  $msg = __( 'WooCommerce Extended Coupon Features is disabled because WooCommerce could not be detected.', 'woocommerce-jos-autocoupon' );
204
  echo '<div class="error"><p>' . $msg . '</p></div>';
205
  }
206
 
207
- //2.2.2
208
- public function woocommerce_coupon_loaded ( $coupon ) {
209
- if ( ! is_admin() ) {
210
- //2.2.2 Allow coupon even if minimum spend not reached
211
- if ( WJECF_Wrap( $coupon )->get_meta( '_wjecf_allow_below_minimum_spend' ) == 'yes' ) {
212
- //HACK: Overwrite the minimum amount with 0 so WooCommerce will allow the coupon
213
- $coupon->wjecf_minimum_amount_for_discount = WJECF_Wrap( $coupon )->get_minimum_amount();
214
- $coupon->minimum_amount = 0;
215
- }
216
- }
217
- }
218
-
219
- //2.2.2
220
- public function woocommerce_coupon_get_discount_amount ( $discount, $discounting_amount, $cart_item, $single, $coupon ) {
221
- //2.2.2 No value if minimum spend not reached
222
- if (isset( $coupon->wjecf_minimum_amount_for_discount ) ) {
223
- if ( wc_format_decimal( $coupon->wjecf_minimum_amount_for_discount ) > wc_format_decimal( WC()->cart->subtotal ) ) {
224
- return 0;
225
- };
226
- }
227
- return $discount;
228
- }
229
-
230
- /**
231
- * Overwrite coupon error message, if $err_code is an error code of this plugin
232
- * @param string $err Original error message
233
- * @param int $err_code Error code
234
- * @param WC_Coupon $coupon The coupon
235
- * @return string Overwritten error message
236
- */
237
- public function woocommerce_coupon_error( $err, $err_code, $coupon ) {
238
- switch ( $err_code ) {
239
- case self::E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET:
240
- $min_price = wc_price( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_subtotal' ) );
241
- $err = sprintf( __( 'The minimum subtotal of the matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $min_price );
242
- break;
243
- case self::E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET:
244
- $max_price = wc_price( WJECF_Wrap( $coupon )->get_meta( '_wjecf_max_matching_product_subtotal' ) );
245
- $err = sprintf( __( 'The maximum subtotal of the matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $max_price );
246
- break;
247
- case self::E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET:
248
- $min_matching_product_qty = intval( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_qty' ) );
249
- $err = sprintf( __( 'The minimum quantity of matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $min_matching_product_qty );
250
- break;
251
- case self::E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET:
252
- $max_matching_product_qty = intval( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_qty' ) );
253
- $err = sprintf( __( 'The maximum quantity of matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $max_matching_product_qty );
254
- break;
255
- case self::E_WC_COUPON_SHIPPING_METHOD_NOT_MET:
256
- $err = __( 'The coupon is not valid for the currently selected shipping method.', 'woocommerce-jos-autocoupon' );
257
- break;
258
- case self::E_WC_COUPON_PAYMENT_METHOD_NOT_MET:
259
- $err = __( 'The coupon is not valid for the currently selected payment method.', 'woocommerce-jos-autocoupon' );
260
- break;
261
- case self::E_WC_COUPON_NOT_FOR_THIS_USER:
262
- $err = sprintf( __( 'Sorry, it seems the coupon "%s" is not yours.', 'woocommerce-jos-autocoupon' ), WJECF_Wrap( $coupon )->get_code() );
263
- break;
264
- default:
265
- //Do nothing
266
- break;
267
- }
268
- return $err;
269
- }
270
-
271
- /**
272
- * Extra validation rules for coupons.
273
- * @param bool $valid
274
- * @param WC_Coupon $coupon
275
- * @return bool True if valid; False if not valid.
276
- */
277
- public function coupon_is_valid ( $valid, $coupon ) {
278
- try {
279
- return $this->assert_coupon_is_valid( $valid, $coupon );
280
- } catch ( Exception $e ) {
281
- return false;
282
- }
283
- }
284
-
285
- /**
286
- * Extra validation rules for coupons. Throw an exception when not valid.
287
- * @param bool $valid
288
- * @param WC_Coupon $coupon
289
- * @return bool True if valid; False if already invalid on function call. In any other case an Exception will be thrown.
290
- */
291
- public function assert_coupon_is_valid ( $valid, $coupon ) {
292
-
293
- $wrap_coupon = WJECF_Wrap( $coupon );
294
-
295
- //Not valid? Then it will never validate, so get out of here
296
- if ( ! $valid ) {
297
- return false;
298
- }
299
-
300
- //============================
301
- //Test if ALL products are in the cart (if AND-operator selected instead of the default OR)
302
- $products_and = $wrap_coupon->get_meta( '_wjecf_products_and' ) == 'yes';
303
- if ( $products_and && sizeof( $wrap_coupon->get_product_ids() ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference
304
- //Get array of all cart product and variation ids
305
- $cart_item_ids = array();
306
- $cart = WC()->cart->get_cart();
307
- foreach( $cart as $cart_item_key => $cart_item ) {
308
- $cart_item_ids[] = $cart_item['product_id'];
309
- $cart_item_ids[] = $cart_item['variation_id'];
310
- }
311
  //Filter used by WJECF_WPML hook
312
- $cart_item_ids = apply_filters( 'wjecf_get_product_ids', $cart_item_ids );
313
-
314
- //check if every single product is in the cart
315
- foreach( apply_filters( 'wjecf_get_product_ids', $wrap_coupon->get_product_ids() ) as $product_id ) {
316
- if ( ! in_array( $product_id, $cart_item_ids ) ) {
317
- throw new Exception( WC_Coupon::E_WC_COUPON_NOT_APPLICABLE );
318
- }
319
- }
320
- }
321
-
322
- //============================
323
- //Test if products form ALL categories are in the cart (if AND-operator selected instead of the default OR)
324
- $categories_and = $wrap_coupon->get_meta( '_wjecf_categories_and' ) == 'yes';
325
- if ( $categories_and && sizeof( $wrap_coupon->get_product_categories() ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference
326
- //Get array of all cart product and variation ids
327
- $cart_product_cats = array();
328
- $cart = WC()->cart->get_cart();
329
- foreach( $cart as $cart_item_key => $cart_item ) {
330
- $cart_product_cats = array_merge ( $cart_product_cats, wp_get_post_terms( $cart_item['product_id'], 'product_cat', array( "fields" => "ids" ) ) );
331
- }
332
  //Filter used by WJECF_WPML hook
333
- $cart_product_cats = apply_filters( 'wjecf_get_product_cat_ids', $cart_product_cats );
334
- //check if every single category is in the cart
335
- foreach( apply_filters( 'wjecf_get_product_cat_ids', $wrap_coupon->get_product_categories() ) as $cat_id ) {
336
- if ( ! in_array( $cat_id, $cart_product_cats ) ) {
337
- $this->log( $cat_id . " is not in " . print_r($cart_product_cats, true));
338
- throw new Exception( WC_Coupon::E_WC_COUPON_NOT_APPLICABLE );
339
- }
340
- }
341
- }
342
-
343
- //============================
344
- //Test min/max quantity of matching products
345
- //
346
- //For all items in the cart:
347
- // If coupon contains both a product AND category inclusion filter: the item is counted if it matches either one of them
348
- // If coupon contains either a product OR category exclusion filter: the item will NOT be counted if it matches either one of them
349
- // If sale items are excluded by the coupon: the item will NOT be counted if it is a sale item
350
- // If no filter exist, all items will be counted
351
-
352
- $multiplier = null; //null = not initialized
353
-
354
- //Validate quantity
355
- $min_matching_product_qty = intval( $wrap_coupon->get_meta( '_wjecf_min_matching_product_qty' ) );
356
- $max_matching_product_qty = intval( $wrap_coupon->get_meta( '_wjecf_max_matching_product_qty' ) );
357
- if ( $min_matching_product_qty > 0 || $max_matching_product_qty > 0 ) {
358
- //Count the products
359
- $qty = $this->get_quantity_of_matching_products( $coupon );
360
- if ( $min_matching_product_qty > 0 && $qty < $min_matching_product_qty ) throw new Exception( self::E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET );
361
- if ( $max_matching_product_qty > 0 && $qty > $max_matching_product_qty ) throw new Exception( self::E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET );
362
-
363
- if ( $min_matching_product_qty > 0 ) {
364
- $multiplier = self::min_value( floor( $qty / $min_matching_product_qty ), $multiplier );
365
- }
366
- }
367
-
368
- //Validate subtotal (2.2.2)
369
- $min_matching_product_subtotal = floatval( $wrap_coupon->get_meta( '_wjecf_min_matching_product_subtotal' ) );
370
- $max_matching_product_subtotal = floatval( $wrap_coupon->get_meta( '_wjecf_max_matching_product_subtotal' ) );
371
- if ( $min_matching_product_subtotal > 0 || $max_matching_product_subtotal > 0 ) {
372
- $subtotal = $this->get_subtotal_of_matching_products( $coupon );
373
- if ( $min_matching_product_subtotal > 0 && $subtotal < $min_matching_product_subtotal ) throw new Exception( self::E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET );
374
- if ( $max_matching_product_subtotal > 0 && $subtotal > $max_matching_product_subtotal ) throw new Exception( self::E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET );
375
-
376
- if ( $min_matching_product_subtotal > 0 ) {
377
- $multiplier = self::min_value( floor( $subtotal / $min_matching_product_subtotal ), $multiplier );
378
- }
379
- }
380
-
381
- //============================
382
- //Test restricted shipping methods
383
- $shipping_method_ids = $this->get_coupon_shipping_method_ids( $coupon );
384
- if ( sizeof( $shipping_method_ids ) > 0 ) {
385
- $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
386
- $chosen_shipping = empty( $chosen_shipping_methods ) ? '' : $chosen_shipping_methods[0];
387
- $chosen_shipping = explode( ':', $chosen_shipping); //UPS and USPS stores extra data, seperated by colon
388
- $chosen_shipping = $chosen_shipping[0];
389
-
390
- if ( ! in_array( $chosen_shipping, $shipping_method_ids ) ) {
391
- throw new Exception( self::E_WC_COUPON_SHIPPING_METHOD_NOT_MET );
392
- }
393
- }
394
-
395
- //============================
396
- //Test restricted payment methods
397
- $payment_method_ids = $this->get_coupon_payment_method_ids( $coupon );
398
- if ( sizeof( $payment_method_ids ) > 0 ) {
399
- $chosen_payment_method = isset( WC()->session->chosen_payment_method ) ? WC()->session->chosen_payment_method : array();
400
-
401
- if ( ! in_array( $chosen_payment_method, $payment_method_ids ) ) {
402
- throw new Exception( self::E_WC_COUPON_PAYMENT_METHOD_NOT_MET );
403
- }
404
- }
405
-
406
-
407
- //============================
408
- //Test restricted user ids and roles
409
- //NOTE: If both customer id and role restrictions are provided, the coupon matches if either the id or the role matches
410
- $coupon_customer_ids = $this->get_coupon_customer_ids( $coupon );
411
- $coupon_customer_roles = $this->get_coupon_customer_roles( $coupon );
412
- if ( sizeof( $coupon_customer_ids ) > 0 || sizeof( $coupon_customer_roles ) > 0 ) {
413
- $user = wp_get_current_user();
414
-
415
- //If both fail we invalidate. Otherwise it's ok
416
- if ( ! in_array( $user->ID, $coupon_customer_ids ) && ! array_intersect( $user->roles, $coupon_customer_roles ) ) {
417
- throw new Exception( self::E_WC_COUPON_NOT_FOR_THIS_USER );
418
- }
419
- }
420
-
421
- //============================
422
- //Test excluded user roles
423
- $coupon_excluded_customer_roles = $this->get_coupon_excluded_customer_roles( $coupon );
424
- if ( sizeof( $coupon_excluded_customer_roles ) > 0 ) {
425
- $user = wp_get_current_user();
426
-
427
- //Excluded customer roles
428
- if ( array_intersect( $user->roles, $coupon_excluded_customer_roles ) ) {
429
- throw new Exception( self::E_WC_COUPON_NOT_FOR_THIS_USER );
430
- }
431
- }
432
-
433
- //We use our own filter (instead of woocommerce_coupon_is_valid) for easier compatibility management
434
- //e.g. WC prior to 2.3.0 can't handle Exceptions; while 2.3.0 and above require exceptions
435
- do_action( 'wjecf_assert_coupon_is_valid', $coupon );
436
-
437
- if ( $wrap_coupon->get_minimum_amount() ) {
438
- $multiplier = self::min_value( floor( WC()->cart->subtotal / $wrap_coupon->get_minimum_amount() ), $multiplier );
439
- }
440
-
441
-
442
- $this->coupon_multiplier_values[ $wrap_coupon->get_code() ] = $multiplier;
443
- //error_log("multiplier " . $wrap_coupon->get_code() . " = " . $multiplier );
444
-
445
- return true; // VALID!
446
- }
447
-
448
- /**
449
- * Return the lowest multiplier value
450
- */
451
- private static function min_value( $value, $current_multiplier_value = null ) {
452
- return ( $current_multiplier_value === null || $value < $current_multiplier_value ) ? $value : $current_multiplier_value;
453
- }
454
-
455
- /**
456
- * The amount of times the minimum spend / quantity / subtotal values are reached
457
- * @return int 1 or more if coupon is valid, otherwise 0
458
- */
459
- public function get_coupon_multiplier_value( $coupon ) {
460
- $coupon = WJECF_WC()->get_coupon( $coupon );
461
-
462
- //If coupon validation was not executed, the value is unknown
463
- if ( ! isset( $this->coupon_multiplier_values[ WJECF_Wrap( $coupon )->get_code() ] ) ) {
464
- if ( ! $this->coupon_is_valid( true, $coupon ) ) {
465
- return 0;
466
- }
467
- }
468
- $multiplier = $this->coupon_multiplier_values[ WJECF_Wrap( $coupon )->get_code() ];
469
-
470
- //error_log("get multiplier " . WJECF_Wrap( $coupon )->get_code() . " = " . $multiplier );
471
- return $multiplier;
472
- }
473
-
474
- //Temporary storage
475
- private $coupon_multiplier_values = array();
476
-
477
-
478
- /**
479
- * (API FUNCTION)
480
- * The total amount of the products in the cart that match the coupon restrictions
481
- * since 2.2.2-b3
482
- */
483
- public function get_quantity_of_matching_products( $coupon ) {
484
- $coupon = WJECF_WC()->get_coupon( $coupon );
485
-
486
- $qty = 0;
487
- foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
488
- $_product = $cart_item['data'];
489
- if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) {
490
- $qty += $cart_item['quantity'];
491
- }
492
- }
493
- return $qty;
494
- }
495
-
496
- /**
497
- * (API FUNCTION)
498
- * The total value of the products in the cart that match the coupon restrictions
499
- * since 2.2.2-b3
500
- */
501
- public function get_subtotal_of_matching_products( $coupon ) {
502
- $coupon = WJECF_WC()->get_coupon( $coupon );
503
-
504
- $subtotal = 0;
505
- foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
506
- $_product = $cart_item['data'];
507
- if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) {
508
- $subtotal += $_product->get_price() * $cart_item['quantity'];
509
- }
510
- }
511
- return $subtotal;
512
- }
513
-
514
- /**
515
- * (API FUNCTION)
516
- * Test if coupon is valid for the product
517
- * (this function is used to count the quantity of matching products)
518
- */
519
- public function coupon_is_valid_for_product( $coupon, $product, $values = array() ) {
520
- //Do not count the free products
521
- if ( isset( $values['_wjecf_free_product_coupon'] ) ) {
522
- return false;
523
- }
524
-
525
- if ( ! $coupon->is_type( WJECF_WC()->wc_get_cart_coupon_types() ) ) {
526
- return $coupon->is_valid_for_product( $product, $values );
527
- }
528
-
529
- //$coupon->is_valid_for_product() only works for fixed_product or percent_product discounts
530
- //It's not, so we create a temporary duplicate
531
- $duplicate_coupon = WJECF_WC()->get_coupon( WJECF_Wrap( $coupon )->get_code() );
532
- WJECF_Wrap( $duplicate_coupon )->set_discount_type( 'fixed_product' );
533
- return $duplicate_coupon->is_valid_for_product( $product, $values );
534
- }
535
-
536
-
537
-
538
- // =====================
539
-
540
- /**
541
- * Get array of the selected shipping methods ids.
542
- * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
543
- * @return array Id's of the shipping methods or an empty array.
544
- */
545
- public function get_coupon_shipping_method_ids( $coupon ) {
546
- $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_shipping_methods' );
547
- if ($v == '') {
548
- $v = array();
549
- }
550
-
551
- return $v;
552
- }
553
 
554
  /**
555
  * Get array of the selected payment method ids.
556
  * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
557
  * @return array Id's of the payment methods or an empty array.
558
- */
559
- public function get_coupon_payment_method_ids( $coupon ) {
560
- $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_payment_methods' );
561
- if ($v == '') {
562
- $v = array();
563
- }
564
-
565
- return $v;
566
- }
567
-
568
  /**
569
  * Get array of the selected customer ids.
570
  * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
571
  * @return array Id's of the customers (users) or an empty array.
572
- */
573
- public function get_coupon_customer_ids( $coupon ) {
574
- $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_customer_ids' );
575
-
576
- if ($v == '') {
577
- $v = array();
578
- } else {
579
- $v = array_map( 'intval', explode(",", $v ) );
580
- }
581
-
582
- return $v;
583
- }
584
-
585
  /**
586
  * Get array of the selected customer role ids.
587
  * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
588
  * @return array Id's (string) of the customer roles or an empty array.
589
- */
590
- public function get_coupon_customer_roles( $coupon ) {
591
- $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_customer_roles' );
592
- if ($v == '') {
593
- $v = array();
594
- }
595
-
596
- return $v;
597
- }
598
 
599
  /**
600
  * Get array of the excluded customer role ids.
601
  * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
602
  * @return array Id's (string) of the excluded customer roles or an empty array.
603
- */
604
- public function get_coupon_excluded_customer_roles( $coupon ) {
605
- $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_excluded_customer_roles' );
606
- if ($v == '') {
607
- $v = array();
608
- }
609
-
610
- return $v;
611
- }
612
-
613
- public function is_pro() {
614
- return $this instanceof WJECF_Pro_Controller;
615
- }
616
 
617
  // ===========================================================================
618
  // START - OVERWRITE INFO MESSAGES
619
  // ===========================================================================
620
 
621
- /**
622
- * 2.3.4
623
- * If a 'Coupon applied' message is displayed by WooCommerce, replace it by another message (or no message)
624
- * @param WC_Coupon $coupon The coupon to replace the message for
625
- * @param string $new_message The new message. Set to empty string if no message must be displayed
626
- */
627
- public function start_overwrite_success_message( $coupon, $new_message = '' ) {
628
- $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ] = array( $coupon->get_coupon_message( WC_Coupon::WC_COUPON_SUCCESS ) => $new_message );
629
- add_filter( 'woocommerce_coupon_message', array( $this, 'filter_woocommerce_coupon_message' ), 10, 3 );
630
- }
631
-
632
- /**
633
- * 2.3.4
634
- * Stop overwriting messages
635
- */
636
- public function stop_overwrite_success_message() {
637
- remove_filter( 'woocommerce_coupon_message', array( $this, 'filter_woocommerce_coupon_message' ), 10 );
638
- $this->overwrite_coupon_message = array();
639
- }
640
-
641
- private $overwrite_coupon_message = array(); /* [ 'coupon_code' => [ old_message' => 'new_message' ] ] */
642
-
643
- function filter_woocommerce_coupon_message( $msg, $msg_code, $coupon ) {
644
- if ( isset( $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ][ $msg ] ) ) {
645
- $msg = $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ][ $msg ];
646
- }
647
- return $msg;
648
- }
649
 
650
  // ===========================================================================
651
  // END - OVERWRITE INFO MESSAGES
652
  // ===========================================================================
653
 
654
 
655
- private $_session_data = null;
656
- /**
657
- * Read something from the session
658
- * @param string $key The key for identification
659
- * @param any $default The default value (Default: false)
660
- *
661
- * @return The saved value if found, otherwise the default value
662
- */
663
- public function get_session( $key, $default = false ) {
664
- if ( $this->_session_data == null) {
665
- $this->_session_data = WC()->session->get( '_wjecf_session_data', array() );
666
- }
667
- return isset( $this->_session_data[ $key ] ) ? $this->_session_data[ $key ] : $default;
668
- }
669
-
670
- /**
671
- * Save something in the session
672
- *
673
- * @param string $key The key for identification
674
- * @param anything $value The value to store
675
- */
676
- public function set_session( $key, $value ) {
677
- if ( $this->_session_data == null) {
678
- $this->_session_data = WC()->session->get( '_wjecf_session_data', array() );
679
- }
680
- $this->_session_data[ $key ] = $value;
681
- if ( $value !== null ) {
682
- WC()->session->set( '_wjecf_session_data', $this->_session_data );
683
- } else {
684
-
685
- }
686
- }
687
-
688
-
689
- /**
690
- * Get overwritable template filename
691
- * @param string $template_name
692
- * @return string Template filename
693
- */
694
- public function get_template_filename( $template_name ) {
695
- $template_path = 'woocommerce-auto-added-coupons';
696
-
697
- $plugin_template_path = plugin_dir_path( dirname(__FILE__) ) . 'templates/';
698
-
699
- //Get template overwritten file
700
- $template = locate_template( trailingslashit( $template_path ) . $template_name );
701
-
702
- // Get default template
703
- if ( ! $template ) {
704
- $template = $plugin_template_path . $template_name;
705
- }
706
-
707
- return $template;
708
- }
709
-
710
- /**
711
- * Include a template file, either from this plugins directory or overwritten in the themes directory
712
- * @param type $template_name
713
- * @return type
714
- */
715
- public function include_template( $template_name, $variables = array() ) {
716
- extract( $variables );
717
- include( $this->get_template_filename( $template_name ) );
718
- }
719
-
720
- /**
721
- * Log message for debugging
722
- * @param string $string The message to log
723
- * @param int $skip_backtrace Defaults to 0, amount of items to skip in backtrace to fetch class and method name
724
- */
725
- public function log( $string, $skip_backtrace = 0) {
726
- if ( $this->debug_mode ) {
727
- $nth = 1 + $skip_backtrace;
728
- $bt = debug_backtrace();
729
- $class = $bt[$nth]['class'];
730
- $function = $bt[$nth]['function'];
731
-
732
- $row = array(
733
- 'time' => time(),
734
- 'class' => $class,
735
- 'function' => $function,
736
- 'filter' => current_filter(),
737
- 'message' => $string,
738
- );
739
- $this->log[] = $row;
740
- error_log( $row['filter'] . ' ' . $row['class'] . '::' . $row['function'] . ' ' . $row['message'] );
741
- }
742
- }
743
-
744
- /**
745
- * Output the log as html
746
- */
747
- public function render_log() {
748
- if ( $this->debug_mode && current_user_can( 'manage_options' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) {
749
- echo "<table class='soft79_wjecf_log'>";
750
- foreach( $this->log as $row ) {
751
- $cells = array(
752
- date("H:i:s", $row['time']),
753
- esc_html( $row['filter'] ),
754
- esc_html( $row['class'] . '::' . $row['function'] ),
755
- esc_html( $row['message'] ),
756
- );
757
- echo "<tr><td>" . implode( "</td><td>", $cells ) . "</td></tr>";
758
- }
759
- $colspan = isset( $cells ) ? count( $cells ) : 1;
760
- echo "<tr><td colspan='" . $colspan . "'>Current coupons in cart: " . implode( ", ", WC()->cart->applied_coupons ) . "</td></tr>";
761
- echo "</table>";
762
- }
763
- }
764
 
765
  }
7
  */
8
  class WJECF_Controller {
9
 
10
+ // Coupon message codes
11
+ //NOTE: I use prefix 79 for this plugin; there's no guarantee that other plugins don't use the same values!
12
+ const E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET = 79100;
13
+ const E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET = 79101;
14
+ const E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET = 79102;
15
+ const E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET = 79103;
16
+ const E_WC_COUPON_SHIPPING_METHOD_NOT_MET = 79104;
17
+ const E_WC_COUPON_PAYMENT_METHOD_NOT_MET = 79105;
18
+ const E_WC_COUPON_NOT_FOR_THIS_USER = 79106;
19
+
20
+ protected $debug_mode = false;
21
+ protected $log = array();
22
+ public $options = false;
23
+
24
+ //Plugin data
25
+ public $plugin_path; // Has trailing slash
26
+ public $plugin_url; // Has trailing slash
27
+ public $version; // Use for js
28
+
29
+ /**
30
+ * Singleton Instance
31
+ *
32
+ * @static
33
+ * @return Singleton Instance
34
+ */
35
+ public static function instance() {
36
+ if ( is_null( self::$_instance ) ) {
37
+ self::$_instance = new self();
38
+ }
39
+ return self::$_instance;
40
+ }
41
+ protected static $_instance = null;
42
+
43
+
44
+ public function __construct() {
45
+ $this->debug_mode = false && defined( 'WP_DEBUG' ) && WP_DEBUG;
46
+ //Paths
47
+ $this->plugin_path = plugin_dir_path( dirname(__FILE__) );
48
+ $this->plugin_url = plugins_url( '/', dirname( __FILE__ ) );
49
+
50
+ $filename = $this->is_pro() ? "woocommerce-jos-autocoupon-pro.php" : "woocommerce-jos-autocoupon.php" ;
51
+
52
+ //Version
53
+ $default_headers = array(
54
+ 'Version' => 'Version',
55
+ );
56
+ $plugin_data = get_file_data( $this->plugin_path . $filename, $default_headers, 'plugin' );
57
+ $this->version = $plugin_data['Version'];
58
+ }
59
+
60
+ public function start() {
61
+ add_action('init', array( $this, 'init_hook' ));
62
+ }
63
+
64
+ public function init_hook() {
65
+ if ( ! class_exists('WC_Coupon') ) {
66
+ add_action( 'admin_notices', array( $this, 'admin_notice_woocommerce_not_found' ) );
67
+ return;
68
+ }
69
+
70
+ $this->controller_init();
71
+
72
+ /**
73
+ * Fires before the WJECF plugins are initialised.
74
+ *
75
+ * Perfect hook for themes or plugins to load custom WJECF plugins.
76
+ *
77
+ * @since 2.3.7
78
+ **/
79
+ do_action( 'wjecf_init_plugins');
80
+
81
+ //Start the plugins
82
  foreach ( WJECF()->get_plugins() as $name => $plugin ) {
83
+ if ( $plugin->plugin_is_enabled() ) {
84
+
85
+ foreach( $plugin->get_plugin_dependencies() as $dependency_name ) {
86
+ $dependency = $this->get_plugin( $dependency_name );
87
+ if ( ! $dependency || ! $dependency->plugin_is_enabled() ) {
88
+ error_log('Unable to initialize ' . $name . ' because it requires ' . $dependency_name );
89
+ continue;
90
+ }
91
+ }
92
+
93
+ $plugin->init_hook();
94
+ if ( is_admin() ) {
95
+ $plugin->init_admin_hook();
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ public function controller_init() {
102
+
103
+ $this->log( "INIT " . ( is_ajax() ? "AJAX" : is_admin() ? "ADMIN" : "FRONTEND" ) . " " . $_SERVER['REQUEST_URI'] );
104
+
105
+ $this->init_options();
106
+
107
+ //Frontend hooks
108
+
109
+ //assert_coupon_is_valid (which raises exception on invalid coupon) can only be used on WC 2.3.0 and up
110
+ if ( WJECF_WC()->check_woocommerce_version('2.3.0') ) {
111
+ add_filter('woocommerce_coupon_is_valid', array( $this, 'assert_coupon_is_valid' ), 10, 2 );
112
+ } else {
113
+ add_filter('woocommerce_coupon_is_valid', array( $this, 'coupon_is_valid' ), 10, 2 );
114
+ }
115
+
116
+ add_filter('woocommerce_coupon_error', array( $this, 'woocommerce_coupon_error' ), 10, 3 );
117
+ add_action('woocommerce_coupon_loaded', array( $this, 'woocommerce_coupon_loaded' ), 10, 1);
118
+ add_filter('woocommerce_coupon_get_discount_amount', array( $this, 'woocommerce_coupon_get_discount_amount' ), 10, 5);
119
  add_action( 'wp_footer', array( $this, 'render_log' ) ); //Log
120
+ }
121
+
122
+ protected $plugins = array();
123
+
124
+ /**
125
+ * Load a WJECF Plugin (class name)
126
+ * @param string $class_name The class name of the plugin
127
+ * @return bool True if succeeded, otherwise false
128
+ */
129
+ public function add_plugin( $class_name ) {
130
+ if ( isset( $this->plugins[ $class_name ] ) ) {
131
+ return false; //Already loaded
132
+ }
133
+
134
+ if ( ! class_exists( $class_name ) ) {
135
+ return false; //Not found
136
+ }
137
+
138
+ $the_plugin = new $class_name();
139
+ foreach( $the_plugin->get_plugin_dependencies() as $dependency ) {
140
+ if ( ! class_exists( $dependency ) ) {
141
+ $this->log( 'Unknown dependency: ' . $dependency . ' for plugin ' . $class_name );
142
+ return false;
143
+ }
144
+
145
+ if ( isset( $this->plugins[ $class_name ] ) ) {
146
+ continue; //dependency is al geladen
147
+ }
148
+
149
+ $this->add_plugin( $dependency );
150
+ }
151
+
152
+ //Assert dependencies
153
+ try {
154
+ $the_plugin->assert_dependencies();
155
+ } catch (Exception $ex) {
156
+ $msg = sprintf('Failed loading %s: %s', $class_name, $ex->getMessage() );
157
+ if ( $wjecf_admin = WJECF()->get_plugin('WJECF_Admin') ) {
158
+ $wjecf_admin->enqueue_notice( $msg, 'error' );
159
+ } else {
160
+ }
161
+ error_log("PRINTR".print_r($wjecf_admin, true));
162
+ error_log( $msg );
163
+ return false;
164
+ }
165
+
166
+ $this->plugins[ $class_name ] = $the_plugin;
167
+ $this->log( 'Loaded plugin: ' . $class_name );
168
+
169
+ return true;
170
+ }
171
+
172
+ public function get_plugins() {
173
+ return $this->plugins;
174
+ }
175
+
176
+ /**
177
+ * Retrieves the WJECF Plugin
178
+ * @param string $class_name
179
+ * @return object|bool The plugin if found, otherwise returns false
180
+ */
181
+ public function get_plugin( $class_name ) {
182
+ if ( isset( $this->plugins[ $class_name ] ) ) {
183
+ return $this->plugins[ $class_name ];
184
+ } else {
185
+ return false;
186
+ }
187
+ }
188
+
189
+ public function init_options() {
190
  $this->options = get_option( 'wjecf_options' );
191
+ if (false === $this->options) {
192
+ $this->options = array( 'db_version' => 0 );
193
+ }
194
+ }
195
 
196
  /* FRONTEND HOOKS */
197
 
198
+ /**
199
+ * Notifies that WooCommerce has not been detected.
200
+ * @return void
201
+ */
202
  public function admin_notice_woocommerce_not_found() {
203
  $msg = __( 'WooCommerce Extended Coupon Features is disabled because WooCommerce could not be detected.', 'woocommerce-jos-autocoupon' );
204
  echo '<div class="error"><p>' . $msg . '</p></div>';
205
  }
206
 
207
+ //2.2.2
208
+ public function woocommerce_coupon_loaded ( $coupon ) {
209
+ if ( ! is_admin() ) {
210
+ //2.2.2 Allow coupon even if minimum spend not reached
211
+ if ( WJECF_Wrap( $coupon )->get_meta( '_wjecf_allow_below_minimum_spend' ) == 'yes' ) {
212
+ //HACK: Overwrite the minimum amount with 0 so WooCommerce will allow the coupon
213
+ $coupon->wjecf_minimum_amount_for_discount = WJECF_Wrap( $coupon )->get_minimum_amount();
214
+ $coupon->minimum_amount = 0;
215
+ }
216
+ }
217
+ }
218
+
219
+ //2.2.2
220
+ public function woocommerce_coupon_get_discount_amount ( $discount, $discounting_amount, $cart_item, $single, $coupon ) {
221
+ //2.2.2 No value if minimum spend not reached
222
+ if (isset( $coupon->wjecf_minimum_amount_for_discount ) ) {
223
+ if ( wc_format_decimal( $coupon->wjecf_minimum_amount_for_discount ) > wc_format_decimal( WC()->cart->subtotal ) ) {
224
+ return 0;
225
+ };
226
+ }
227
+ return $discount;
228
+ }
229
+
230
+ /**
231
+ * Overwrite coupon error message, if $err_code is an error code of this plugin
232
+ * @param string $err Original error message
233
+ * @param int $err_code Error code
234
+ * @param WC_Coupon $coupon The coupon
235
+ * @return string Overwritten error message
236
+ */
237
+ public function woocommerce_coupon_error( $err, $err_code, $coupon ) {
238
+ switch ( $err_code ) {
239
+ case self::E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET:
240
+ $min_price = wc_price( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_subtotal' ) );
241
+ $err = sprintf( __( 'The minimum subtotal of the matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $min_price );
242
+ break;
243
+ case self::E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET:
244
+ $max_price = wc_price( WJECF_Wrap( $coupon )->get_meta( '_wjecf_max_matching_product_subtotal' ) );
245
+ $err = sprintf( __( 'The maximum subtotal of the matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $max_price );
246
+ break;
247
+ case self::E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET:
248
+ $min_matching_product_qty = intval( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_qty' ) );
249
+ $err = sprintf( __( 'The minimum quantity of matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $min_matching_product_qty );
250
+ break;
251
+ case self::E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET:
252
+ $max_matching_product_qty = intval( WJECF_Wrap( $coupon )->get_meta( '_wjecf_min_matching_product_qty' ) );
253
+ $err = sprintf( __( 'The maximum quantity of matching products for this coupon is %s.', 'woocommerce-jos-autocoupon' ), $max_matching_product_qty );
254
+ break;
255
+ case self::E_WC_COUPON_SHIPPING_METHOD_NOT_MET:
256
+ $err = __( 'The coupon is not valid for the currently selected shipping method.', 'woocommerce-jos-autocoupon' );
257
+ break;
258
+ case self::E_WC_COUPON_PAYMENT_METHOD_NOT_MET:
259
+ $err = __( 'The coupon is not valid for the currently selected payment method.', 'woocommerce-jos-autocoupon' );
260
+ break;
261
+ case self::E_WC_COUPON_NOT_FOR_THIS_USER:
262
+ $err = sprintf( __( 'Sorry, it seems the coupon "%s" is not yours.', 'woocommerce-jos-autocoupon' ), WJECF_Wrap( $coupon )->get_code() );
263
+ break;
264
+ default:
265
+ //Do nothing
266
+ break;
267
+ }
268
+ return $err;
269
+ }
270
+
271
+ /**
272
+ * Extra validation rules for coupons.
273
+ * @param bool $valid
274
+ * @param WC_Coupon $coupon
275
+ * @return bool True if valid; False if not valid.
276
+ */
277
+ public function coupon_is_valid ( $valid, $coupon ) {
278
+ try {
279
+ return $this->assert_coupon_is_valid( $valid, $coupon );
280
+ } catch ( Exception $e ) {
281
+ return false;
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Extra validation rules for coupons. Throw an exception when not valid.
287
+ * @param bool $valid
288
+ * @param WC_Coupon $coupon
289
+ * @return bool True if valid; False if already invalid on function call. In any other case an Exception will be thrown.
290
+ */
291
+ public function assert_coupon_is_valid ( $valid, $coupon ) {
292
+
293
+ $wrap_coupon = WJECF_Wrap( $coupon );
294
+
295
+ //Not valid? Then it will never validate, so get out of here
296
+ if ( ! $valid ) {
297
+ return false;
298
+ }
299
+
300
+ //============================
301
+ //Test if ALL products are in the cart (if AND-operator selected instead of the default OR)
302
+ $products_and = $wrap_coupon->get_meta( '_wjecf_products_and' ) == 'yes';
303
+ if ( $products_and && sizeof( $wrap_coupon->get_product_ids() ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference
304
+ //Get array of all cart product and variation ids
305
+ $cart_item_ids = array();
306
+ $cart = WC()->cart->get_cart();
307
+ foreach( $cart as $cart_item_key => $cart_item ) {
308
+ $cart_item_ids[] = $cart_item['product_id'];
309
+ $cart_item_ids[] = $cart_item['variation_id'];
310
+ }
311
  //Filter used by WJECF_WPML hook
312
+ $cart_item_ids = apply_filters( 'wjecf_get_product_ids', $cart_item_ids );
313
+
314
+ //check if every single product is in the cart
315
+ foreach( apply_filters( 'wjecf_get_product_ids', $wrap_coupon->get_product_ids() ) as $product_id ) {
316
+ if ( ! in_array( $product_id, $cart_item_ids ) ) {
317
+ throw new Exception( WC_Coupon::E_WC_COUPON_NOT_APPLICABLE );
318
+ }
319
+ }
320
+ }
321
+
322
+ //============================
323
+ //Test if products form ALL categories are in the cart (if AND-operator selected instead of the default OR)
324
+ $categories_and = $wrap_coupon->get_meta( '_wjecf_categories_and' ) == 'yes';
325
+ if ( $categories_and && sizeof( $wrap_coupon->get_product_categories() ) > 1 ) { // We use > 1, because if size == 1, 'AND' makes no difference
326
+ //Get array of all cart product and variation ids
327
+ $cart_product_cats = array();
328
+ $cart = WC()->cart->get_cart();
329
+ foreach( $cart as $cart_item_key => $cart_item ) {
330
+ $cart_product_cats = array_merge ( $cart_product_cats, wp_get_post_terms( $cart_item['product_id'], 'product_cat', array( "fields" => "ids" ) ) );
331
+ }
332
  //Filter used by WJECF_WPML hook
333
+ $cart_product_cats = apply_filters( 'wjecf_get_product_cat_ids', $cart_product_cats );
334
+ //check if every single category is in the cart
335
+ foreach( apply_filters( 'wjecf_get_product_cat_ids', $wrap_coupon->get_product_categories() ) as $cat_id ) {
336
+ if ( ! in_array( $cat_id, $cart_product_cats ) ) {
337
+ $this->log( $cat_id . " is not in " . print_r($cart_product_cats, true));
338
+ throw new Exception( WC_Coupon::E_WC_COUPON_NOT_APPLICABLE );
339
+ }
340
+ }
341
+ }
342
+
343
+ //============================
344
+ //Test min/max quantity of matching products
345
+ //
346
+ //For all items in the cart:
347
+ // If coupon contains both a product AND category inclusion filter: the item is counted if it matches either one of them
348
+ // If coupon contains either a product OR category exclusion filter: the item will NOT be counted if it matches either one of them
349
+ // If sale items are excluded by the coupon: the item will NOT be counted if it is a sale item
350
+ // If no filter exist, all items will be counted
351
+
352
+ $multiplier = null; //null = not initialized
353
+
354
+ //Validate quantity
355
+ $min_matching_product_qty = intval( $wrap_coupon->get_meta( '_wjecf_min_matching_product_qty' ) );
356
+ $max_matching_product_qty = intval( $wrap_coupon->get_meta( '_wjecf_max_matching_product_qty' ) );
357
+ if ( $min_matching_product_qty > 0 || $max_matching_product_qty > 0 ) {
358
+ //Count the products
359
+ $qty = $this->get_quantity_of_matching_products( $coupon );
360
+ if ( $min_matching_product_qty > 0 && $qty < $min_matching_product_qty ) throw new Exception( self::E_WC_COUPON_MIN_MATCHING_QUANTITY_NOT_MET );
361
+ if ( $max_matching_product_qty > 0 && $qty > $max_matching_product_qty ) throw new Exception( self::E_WC_COUPON_MAX_MATCHING_QUANTITY_NOT_MET );
362
+
363
+ if ( $min_matching_product_qty > 0 ) {
364
+ $multiplier = self::min_value( floor( $qty / $min_matching_product_qty ), $multiplier );
365
+ }
366
+ }
367
+
368
+ //Validate subtotal (2.2.2)
369
+ $min_matching_product_subtotal = floatval( $wrap_coupon->get_meta( '_wjecf_min_matching_product_subtotal' ) );
370
+ $max_matching_product_subtotal = floatval( $wrap_coupon->get_meta( '_wjecf_max_matching_product_subtotal' ) );
371
+ if ( $min_matching_product_subtotal > 0 || $max_matching_product_subtotal > 0 ) {
372
+ $subtotal = $this->get_subtotal_of_matching_products( $coupon );
373
+ if ( $min_matching_product_subtotal > 0 && $subtotal < $min_matching_product_subtotal ) throw new Exception( self::E_WC_COUPON_MIN_MATCHING_SUBTOTAL_NOT_MET );
374
+ if ( $max_matching_product_subtotal > 0 && $subtotal > $max_matching_product_subtotal ) throw new Exception( self::E_WC_COUPON_MAX_MATCHING_SUBTOTAL_NOT_MET );
375
+
376
+ if ( $min_matching_product_subtotal > 0 ) {
377
+ $multiplier = self::min_value( floor( $subtotal / $min_matching_product_subtotal ), $multiplier );
378
+ }
379
+ }
380
+
381
+ //============================
382
+ //Test restricted shipping methods
383
+ $shipping_method_ids = $this->get_coupon_shipping_method_ids( $coupon );
384
+ if ( sizeof( $shipping_method_ids ) > 0 ) {
385
+ $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
386
+ $chosen_shipping = empty( $chosen_shipping_methods ) ? '' : $chosen_shipping_methods[0];
387
+ $chosen_shipping = explode( ':', $chosen_shipping); //UPS and USPS stores extra data, seperated by colon
388
+ $chosen_shipping = $chosen_shipping[0];
389
+
390
+ if ( ! in_array( $chosen_shipping, $shipping_method_ids ) ) {
391
+ throw new Exception( self::E_WC_COUPON_SHIPPING_METHOD_NOT_MET );
392
+ }
393
+ }
394
+
395
+ //============================
396
+ //Test restricted payment methods
397
+ $payment_method_ids = $this->get_coupon_payment_method_ids( $coupon );
398
+ if ( sizeof( $payment_method_ids ) > 0 ) {
399
+ $chosen_payment_method = isset( WC()->session->chosen_payment_method ) ? WC()->session->chosen_payment_method : array();
400
+
401
+ if ( ! in_array( $chosen_payment_method, $payment_method_ids ) ) {
402
+ throw new Exception( self::E_WC_COUPON_PAYMENT_METHOD_NOT_MET );
403
+ }
404
+ }
405
+
406
+
407
+ //============================
408
+ //Test restricted user ids and roles
409
+ //NOTE: If both customer id and role restrictions are provided, the coupon matches if either the id or the role matches
410
+ $coupon_customer_ids = $this->get_coupon_customer_ids( $coupon );
411
+ $coupon_customer_roles = $this->get_coupon_customer_roles( $coupon );
412
+ if ( sizeof( $coupon_customer_ids ) > 0 || sizeof( $coupon_customer_roles ) > 0 ) {
413
+ $user = wp_get_current_user();
414
+
415
+ //If both fail we invalidate. Otherwise it's ok
416
+ if ( ! in_array( $user->ID, $coupon_customer_ids ) && ! array_intersect( $user->roles, $coupon_customer_roles ) ) {
417
+ throw new Exception( self::E_WC_COUPON_NOT_FOR_THIS_USER );
418
+ }
419
+ }
420
+
421
+ //============================
422
+ //Test excluded user roles
423
+ $coupon_excluded_customer_roles = $this->get_coupon_excluded_customer_roles( $coupon );
424
+ if ( sizeof( $coupon_excluded_customer_roles ) > 0 ) {
425
+ $user = wp_get_current_user();
426
+
427
+ //Excluded customer roles
428
+ if ( array_intersect( $user->roles, $coupon_excluded_customer_roles ) ) {
429
+ throw new Exception( self::E_WC_COUPON_NOT_FOR_THIS_USER );
430
+ }
431
+ }
432
+
433
+ //We use our own filter (instead of woocommerce_coupon_is_valid) for easier compatibility management
434
+ //e.g. WC prior to 2.3.0 can't handle Exceptions; while 2.3.0 and above require exceptions
435
+ do_action( 'wjecf_assert_coupon_is_valid', $coupon );
436
+
437
+ if ( $wrap_coupon->get_minimum_amount() ) {
438
+ $multiplier = self::min_value( floor( WC()->cart->subtotal / $wrap_coupon->get_minimum_amount() ), $multiplier );
439
+ }
440
+
441
+
442
+ $this->coupon_multiplier_values[ $wrap_coupon->get_code() ] = $multiplier;
443
+ //error_log("multiplier " . $wrap_coupon->get_code() . " = " . $multiplier );
444
+
445
+ return true; // VALID!
446
+ }
447
+
448
+ /**
449
+ * Return the lowest multiplier value
450
+ */
451
+ private static function min_value( $value, $current_multiplier_value = null ) {
452
+ return ( $current_multiplier_value === null || $value < $current_multiplier_value ) ? $value : $current_multiplier_value;
453
+ }
454
+
455
+ /**
456
+ * The amount of times the minimum spend / quantity / subtotal values are reached
457
+ * @return int 1 or more if coupon is valid, otherwise 0
458
+ */
459
+ public function get_coupon_multiplier_value( $coupon ) {
460
+ $coupon = WJECF_WC()->get_coupon( $coupon );
461
+
462
+ //If coupon validation was not executed, the value is unknown
463
+ if ( ! isset( $this->coupon_multiplier_values[ WJECF_Wrap( $coupon )->get_code() ] ) ) {
464
+ if ( ! $this->coupon_is_valid( true, $coupon ) ) {
465
+ return 0;
466
+ }
467
+ }
468
+ $multiplier = $this->coupon_multiplier_values[ WJECF_Wrap( $coupon )->get_code() ];
469
+
470
+ //error_log("get multiplier " . WJECF_Wrap( $coupon )->get_code() . " = " . $multiplier );
471
+ return $multiplier;
472
+ }
473
+
474
+ //Temporary storage
475
+ private $coupon_multiplier_values = array();
476
+
477
+
478
+ /**
479
+ * (API FUNCTION)
480
+ * The total amount of the products in the cart that match the coupon restrictions
481
+ * since 2.2.2-b3
482
+ */
483
+ public function get_quantity_of_matching_products( $coupon ) {
484
+ $coupon = WJECF_WC()->get_coupon( $coupon );
485
+
486
+ $qty = 0;
487
+ foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
488
+ $_product = $cart_item['data'];
489
+ if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) {
490
+ $qty += $cart_item['quantity'];
491
+ }
492
+ }
493
+ return $qty;
494
+ }
495
+
496
+ /**
497
+ * (API FUNCTION)
498
+ * The total value of the products in the cart that match the coupon restrictions
499
+ * since 2.2.2-b3
500
+ */
501
+ public function get_subtotal_of_matching_products( $coupon ) {
502
+ $coupon = WJECF_WC()->get_coupon( $coupon );
503
+
504
+ $subtotal = 0;
505
+ foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
506
+ $_product = $cart_item['data'];
507
+ if ($this->coupon_is_valid_for_product( $coupon, $_product, $cart_item ) ) {
508
+ $subtotal += $_product->get_price() * $cart_item['quantity'];
509
+ }
510
+ }
511
+ return $subtotal;
512
+ }
513
+
514
+ /**
515
+ * (API FUNCTION)
516
+ * Test if coupon is valid for the product
517
+ * (this function is used to count the quantity of matching products)
518
+ */
519
+ public function coupon_is_valid_for_product( $coupon, $product, $values = array() ) {
520
+ //Do not count the free products
521
+ if ( isset( $values['_wjecf_free_product_coupon'] ) ) {
522
+ return false;
523
+ }
524
+
525
+ if ( ! $coupon->is_type( WJECF_WC()->wc_get_cart_coupon_types() ) ) {
526
+ return $coupon->is_valid_for_product( $product, $values );
527
+ }
528
+
529
+ //$coupon->is_valid_for_product() only works for fixed_product or percent_product discounts
530
+ //It's not, so we create a temporary duplicate
531
+ $duplicate_coupon = WJECF_WC()->get_coupon( WJECF_Wrap( $coupon )->get_code() );
532
+ WJECF_Wrap( $duplicate_coupon )->set_discount_type( 'fixed_product' );
533
+ return $duplicate_coupon->is_valid_for_product( $product, $values );
534
+ }
535
+
536
+
537
+
538
+ // =====================
539
+
540
+ /**
541
+ * Get array of the selected shipping methods ids.
542
+ * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
543
+ * @return array Id's of the shipping methods or an empty array.
544
+ */
545
+ public function get_coupon_shipping_method_ids( $coupon ) {
546
+ $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_shipping_methods' );
547
+ if ($v == '') {
548
+ $v = array();
549
+ }
550
+
551
+ return $v;
552
+ }
553
 
554
  /**
555
  * Get array of the selected payment method ids.
556
  * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
557
  * @return array Id's of the payment methods or an empty array.
558
+ */
559
+ public function get_coupon_payment_method_ids( $coupon ) {
560
+ $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_payment_methods' );
561
+ if ($v == '') {
562
+ $v = array();
563
+ }
564
+
565
+ return $v;
566
+ }
567
+
568
  /**
569
  * Get array of the selected customer ids.
570
  * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
571
  * @return array Id's of the customers (users) or an empty array.
572
+ */
573
+ public function get_coupon_customer_ids( $coupon ) {
574
+ $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_customer_ids' );
575
+
576
+ if ($v == '') {
577
+ $v = array();
578
+ } else {
579
+ $v = array_map( 'intval', explode(",", $v ) );
580
+ }
581
+
582
+ return $v;
583
+ }
584
+
585
  /**
586
  * Get array of the selected customer role ids.
587
  * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
588
  * @return array Id's (string) of the customer roles or an empty array.
589
+ */
590
+ public function get_coupon_customer_roles( $coupon ) {
591
+ $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_customer_roles' );
592
+ if ($v == '') {
593
+ $v = array();
594
+ }
595
+
596
+ return $v;
597
+ }
598
 
599
  /**
600
  * Get array of the excluded customer role ids.
601
  * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
602
  * @return array Id's (string) of the excluded customer roles or an empty array.
603
+ */
604
+ public function get_coupon_excluded_customer_roles( $coupon ) {
605
+ $v = WJECF_Wrap( $coupon )->get_meta( '_wjecf_excluded_customer_roles' );
606
+ if ($v == '') {
607
+ $v = array();
608
+ }
609
+
610
+ return $v;
611
+ }
612
+
613
+ public function is_pro() {
614
+ return $this instanceof WJECF_Pro_Controller;
615
+ }
616
 
617
  // ===========================================================================
618
  // START - OVERWRITE INFO MESSAGES
619
  // ===========================================================================
620
 
621
+ /**
622
+ * 2.3.4
623
+ * If a 'Coupon applied' message is displayed by WooCommerce, replace it by another message (or no message)
624
+ * @param WC_Coupon $coupon The coupon to replace the message for
625
+ * @param string $new_message The new message. Set to empty string if no message must be displayed
626
+ */
627
+ public function start_overwrite_success_message( $coupon, $new_message = '' ) {
628
+ $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ] = array( $coupon->get_coupon_message( WC_Coupon::WC_COUPON_SUCCESS ) => $new_message );
629
+ add_filter( 'woocommerce_coupon_message', array( $this, 'filter_woocommerce_coupon_message' ), 10, 3 );
630
+ }
631
+
632
+ /**
633
+ * 2.3.4
634
+ * Stop overwriting messages
635
+ */
636
+ public function stop_overwrite_success_message() {
637
+ remove_filter( 'woocommerce_coupon_message', array( $this, 'filter_woocommerce_coupon_message' ), 10 );
638
+ $this->overwrite_coupon_message = array();
639
+ }
640
+
641
+ private $overwrite_coupon_message = array(); /* [ 'coupon_code' => [ old_message' => 'new_message' ] ] */
642
+
643
+ function filter_woocommerce_coupon_message( $msg, $msg_code, $coupon ) {
644
+ if ( isset( $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ][ $msg ] ) ) {
645
+ $msg = $this->overwrite_coupon_message[ WJECF_Wrap( $coupon )->get_code() ][ $msg ];
646
+ }
647
+ return $msg;
648
+ }
649
 
650
  // ===========================================================================
651
  // END - OVERWRITE INFO MESSAGES
652
  // ===========================================================================
653
 
654
 
655
+ private $_session_data = null;
656
+ /**
657
+ * Read something from the session
658
+ * @param string $key The key for identification
659
+ * @param any $default The default value (Default: false)
660
+ *
661
+ * @return The saved value if found, otherwise the default value
662
+ */
663
+ public function get_session( $key, $default = false ) {
664
+ if ( $this->_session_data == null) {
665
+ $this->_session_data = WC()->session->get( '_wjecf_session_data', array() );
666
+ }
667
+ return isset( $this->_session_data[ $key ] ) ? $this->_session_data[ $key ] : $default;
668
+ }
669
+
670
+ /**
671
+ * Save something in the session
672
+ *
673
+ * @param string $key The key for identification
674
+ * @param anything $value The value to store
675
+ */
676
+ public function set_session( $key, $value ) {
677
+ if ( $this->_session_data == null) {
678
+ $this->_session_data = WC()->session->get( '_wjecf_session_data', array() );
679
+ }
680
+ $this->_session_data[ $key ] = $value;
681
+ if ( $value !== null ) {
682
+ WC()->session->set( '_wjecf_session_data', $this->_session_data );
683
+ } else {
684
+
685
+ }
686
+ }
687
+
688
+
689
+ /**
690
+ * Get overwritable template filename
691
+ * @param string $template_name
692
+ * @return string Template filename
693
+ */
694
+ public function get_template_filename( $template_name ) {
695
+ $template_path = 'woocommerce-auto-added-coupons';
696
+
697
+ $plugin_template_path = plugin_dir_path( dirname(__FILE__) ) . 'templates/';
698
+
699
+ //Get template overwritten file
700
+ $template = locate_template( trailingslashit( $template_path ) . $template_name );
701
+
702
+ // Get default template
703
+ if ( ! $template ) {
704
+ $template = $plugin_template_path . $template_name;
705
+ }
706
+
707
+ return $template;
708
+ }
709
+
710
+ /**
711
+ * Include a template file, either from this plugins directory or overwritten in the themes directory
712
+ * @param type $template_name
713
+ * @return type
714
+ */
715
+ public function include_template( $template_name, $variables = array() ) {
716
+ extract( $variables );
717
+ include( $this->get_template_filename( $template_name ) );
718
+ }
719
+
720
+ /**
721
+ * Log message for debugging
722
+ * @param string $string The message to log
723
+ * @param int $skip_backtrace Defaults to 0, amount of items to skip in backtrace to fetch class and method name
724
+ */
725
+ public function log( $string, $skip_backtrace = 0) {
726
+ if ( $this->debug_mode ) {
727
+ $nth = 1 + $skip_backtrace;
728
+ $bt = debug_backtrace();
729
+ $class = $bt[$nth]['class'];
730
+ $function = $bt[$nth]['function'];
731
+
732
+ $row = array(
733
+ 'time' => time(),
734
+ 'class' => $class,
735
+ 'function' => $function,
736
+ 'filter' => current_filter(),
737
+ 'message' => $string,
738
+ );
739
+ $this->log[] = $row;
740
+ error_log( $row['filter'] . ' ' . $row['class'] . '::' . $row['function'] . ' ' . $row['message'] );
741
+ }
742
+ }
743
+
744
+ /**
745
+ * Output the log as html
746
+ */
747
+ public function render_log() {
748
+ if ( $this->debug_mode && current_user_can( 'manage_options' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) {
749
+ echo "<table class='soft79_wjecf_log'>";
750
+ foreach( $this->log as $row ) {
751
+ $cells = array(
752
+ date("H:i:s", $row['time']),
753
+ esc_html( $row['filter'] ),
754
+ esc_html( $row['class'] . '::' . $row['function'] ),
755
+ esc_html( $row['message'] ),
756
+ );
757
+ echo "<tr><td>" . implode( "</td><td>", $cells ) . "</td></tr>";
758
+ }
759
+ $colspan = isset( $cells ) ? count( $cells ) : 1;
760
+ echo "<tr><td colspan='" . $colspan . "'>Current coupons in cart: " . implode( ", ", WC()->cart->applied_coupons ) . "</td></tr>";
761
+ echo "</table>";
762
+ }
763
+ }
764
 
765
  }
includes/wjecf-wc.php CHANGED
@@ -10,145 +10,145 @@ defined('ABSPATH') or die();
10
  */
11
  class WJECF_WC {
12
 
13
- protected $wrappers = array();
14
-
15
- /**
16
- * Wrap a data object (WC 2.7 introduced WC_Data)
17
- * @param type $object
18
- * @return type
19
- */
20
- public function wrap( $object, $use_pool = true ) {
21
- if ( $use_pool ) {
22
- //Prevent a huge amount of wrappers to be initiated; one wrapper per object instance should do the trick
23
- foreach( $this->wrappers as $wrapper ) {
24
- if ($wrapper->holds( $object ) ) {
25
- //error_log('Reusing wrapper ' . get_class( $object ) );
26
- return $wrapper;
27
- }
28
- }
29
- }
30
-
31
- if ( is_numeric( $object ) ) {
32
- $post_type = get_post_type( $object );
33
- if ( $post_type == 'shop_coupon' ) {
34
- $object = WJECF_WC()->get_coupon( $object );
35
- } elseif ( $post_type == 'product' ) {
36
- $object = new WC_Product( $object );
37
- }
38
- }
39
-
40
-
41
- if ( $object instanceof WC_Coupon ) {
42
- return $this->wrappers[] = new WJECF_Wrap_Coupon( $object );
43
- }
44
-
45
- if ( $object instanceof WC_Product ) {
46
- return $this->wrappers[] = new WJECF_Wrap_Product( $object );
47
- }
48
-
49
- throw new Exception( 'Cannot wrap ' . get_class( $object ) );
50
- }
51
 
52
  /**
53
  * Returns a specific item in the cart.
54
  *
55
  * @param string $cart_item_key Cart item key.
56
  * @return array Item data
57
- */
58
- public function get_cart_item( $cart_item_key ) {
59
- if ( $this->check_woocommerce_version('2.2.9') ) {
60
- return WC()->cart->get_cart_item( $cart_item_key );
61
- }
62
-
63
- return isset( WC()->cart->cart_contents[ $cart_item_key ] ) ? WC()->cart->cart_contents[ $cart_item_key ] : array();
64
- }
65
-
66
- /**
67
- * Get categories of a product (and anchestors)
68
- * @param int $product_id
69
- * @return array product_cat_ids
70
- */
71
- public function wc_get_product_cat_ids( $product_id ) {
72
- //Since WC 2.5.0
73
- if ( function_exists( 'wc_get_product_cat_ids' ) ) {
74
- return wc_get_product_cat_ids( $product_id );
75
- }
76
-
77
- $product_cats = wp_get_post_terms( $product_id, 'product_cat', array( "fields" => "ids" ) );
78
-
79
- foreach ( $product_cats as $product_cat ) {
80
- $product_cats = array_merge( $product_cats, get_ancestors( $product_cat, 'product_cat' ) );
81
- }
82
- return $product_cats;
83
- }
84
-
85
- /**
86
- * Coupon types that apply to individual products. Controls which validation rules will apply.
87
- *
88
- * @since 2.5.0
89
- * @return array
90
- */
91
- public function wc_get_product_coupon_types() {
92
- //Since WC 2.5.0
93
- if ( function_exists( 'wc_get_product_coupon_types' ) ) {
94
- return wc_get_product_coupon_types();
95
- }
96
- return array( 'fixed_product', 'percent_product' );
97
- }
98
-
99
- public function wc_get_cart_coupon_types() {
100
- //Since WC 2.5.0
101
- if ( function_exists( 'wc_get_cart_coupon_types' ) ) {
102
- return wc_get_cart_coupon_types();
103
- }
104
- return array( 'fixed_cart', 'percent' );
105
- }
106
-
107
- /**
108
- * @since 2.4.0 for WC 2.7 compatibility
109
- *
110
- * Get a WC_Coupon object
111
- * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
112
- * @return WC_Coupon The coupon object
113
- */
114
- public function get_coupon( $coupon ) {
115
- if ( is_numeric( $coupon ) ) {
116
- //By id
117
- global $wpdb;
118
- $coupon = $wpdb->get_var( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE id = %s AND post_type = 'shop_coupon' AND post_status = 'publish'", $coupon ) );
119
- }
120
- if ( ! ( $coupon instanceof WC_Coupon ) ) {
121
- //By code
122
- $coupon = new WC_Coupon( $coupon );
123
- }
124
- return $coupon;
125
- }
126
 
127
  //ADMIN
128
 
129
- /**
130
- * Display a WooCommerce help tip
131
- * @param string $tip The tip to display
132
- * @return string
133
- */
134
- public function wc_help_tip( $tip ) {
135
- //Since WC 2.5.0
136
- if ( function_exists( 'wc_help_tip' ) ) {
137
- return wc_help_tip( $tip );
138
- }
139
-
140
- return '<img class="help_tip" style="margin-top: 21px;" data-tip="' . esc_attr( $tip ) . '" src="' . esc_url( WC()->plugin_url() ) . '/assets/images/help.png" height="16" width="16" />';
141
- }
142
-
143
- /**
144
- * Renders a product selection <input>. Will use either select2 v4 (WC3.0+) select2 v3 (WC2.3+) or chosen (< WC2.3)
145
- * @param string $dom_id
146
- * @param string $field_name
147
- * @param array $selected_ids Array of integers
148
- * @param string|null $placeholder
149
- * @return void
150
- */
151
- public function render_admin_product_selector( $dom_id, $field_name, $selected_ids, $placeholder = null ) {
152
  $product_key_values = array();
153
  foreach ( $selected_ids as $product_id ) {
154
  $product = wc_get_product( $product_id );
@@ -157,203 +157,256 @@ class WJECF_WC {
157
  }
158
  }
159
 
160
- if ( $placeholder === null ) $placeholder = __( 'Search for a product…', 'woocommerce' );
161
-
162
- //In WooCommerce version 2.3.0 chosen was replaced by select2
163
- //In WooCommerce version 3.0 select2 v3 was replaced by select2 v4
164
- if ( $this->check_woocommerce_version('3.0') ) {
165
- $this->render_admin_select2_v4_product_selector( $dom_id, $field_name, $product_key_values, $placeholder );
166
- } elseif ( $this->check_woocommerce_version('2.3.0') ) {
167
- $this->render_admin_select2_product_selector( $dom_id, $field_name, $product_key_values, $placeholder );
168
- } else {
169
- $this->render_admin_chosen_product_selector( $dom_id, $field_name, $product_key_values, $placeholder );
170
- }
171
- }
172
-
173
-
174
- /**
175
- * Renders a product selection <input>.
176
- * Chosen (Legacy)
177
- * @param string $dom_id
178
- * @param string $field_name
179
- * @param array $selected_ids Array of integers
180
- * @param string|null $placeholder
181
- */
182
- private function render_admin_chosen_product_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder ) {
183
- // $selected_keys_and_values must be an array of [ id => name ]
184
-
185
- echo '<select id="' . esc_attr( $dom_id ) . '" name="' . esc_attr( $field_name ) . '[]" class="ajax_chosen_select_products_and_variations" multiple="multiple" data-placeholder="' . esc_attr( $placeholder ) . '">';
186
- foreach ( $selected_keys_and_values as $product_id => $product_name ) {
187
- echo '<option value="' . $product_id . '" selected="selected">' . $product_name . '</option>';
188
- }
189
- echo '</select>';
190
- }
191
-
192
- /**
193
- * @since 2.4.1 for WC 3.0 compatibility
194
- *
195
- * Renders a product selection <input>.
196
- * Select2 version 3 (Since WC 2.3.0)
197
- * @param string $dom_id
198
- * @param string $field_name
199
- * @param array $selected_ids Array of integers
200
- * @param string|null $placeholder
201
- */
202
- private function render_admin_select2_product_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder ) {
203
- // $selected_keys_and_values must be an array of [ id => name ]
204
-
205
- $json_encoded = esc_attr( json_encode( $selected_keys_and_values ) );
206
- echo '<input type="hidden" class="wc-product-search" data-multiple="true" style="width: 50%;" name="'
207
- . esc_attr( $field_name ) . '" data-placeholder="'
208
- . esc_attr( $placeholder ) . '" data-action="woocommerce_json_search_products_and_variations" data-selected="'
209
- . $json_encoded . '" value="' . implode( ',', array_keys( $selected_keys_and_values ) ) . '" />';
210
-
211
- }
212
-
213
- /**
214
- * Renders a product selection <input>.
215
- * Select2 version 4 (Since WC 3.0)
216
- * @param string $dom_id
217
- * @param string $field_name
218
- * @param string $selected_keys_and_values
219
- * @param string $placeholder
220
- */
221
- private function render_admin_select2_v4_product_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder ) {
222
- // $selected_keys_and_values must be an array of [ id => name ]
223
-
224
- $json_encoded = esc_attr( json_encode( $selected_keys_and_values ) );
225
-
226
- echo '<select id="'. esc_attr( $dom_id ) .'" class="wc-product-search" name="'
227
- . esc_attr( $field_name ) . '[]" multiple="multiple" style="width: 50%;" data-placeholder="'
228
- . esc_attr( $placeholder ) . '" data-action="woocommerce_json_search_products_and_variations">';
229
-
230
- foreach ( $selected_keys_and_values as $product_id => $product_name ) {
231
- echo '<option value="' . esc_attr( $product_id ) . '"' . selected( true, true, false ) . '>' . wp_kses_post( $product_name ) . '</option>';
232
- }
233
-
234
- echo '</select>';
235
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
 
238
  //VERSION
239
 
240
- /**
241
- * Check whether WooCommerce version is greater or equal than $req_version
242
- * @param string @req_version The version to compare to
243
- * @return bool true if WooCommerce is at least the given version
244
- */
245
- public function check_woocommerce_version( $req_version ) {
246
- return version_compare( $this->get_woocommerce_version(), $req_version, '>=' );
247
- }
248
-
249
- private $wc_version = null;
250
-
251
- /**
252
- * Get the WooCommerce version number
253
- * @return string|bool WC Version number or false if WC not detected
254
- */
255
- public function get_woocommerce_version() {
256
- if ( isset( $this->wc_version ) ) {
257
- return $this->wc_version;
258
- }
259
-
260
- if ( defined( 'WC_VERSION' ) ) {
261
- return $this->wc_version = WC_VERSION;
262
- }
263
-
264
- // If get_plugins() isn't available, require it
265
- if ( ! function_exists( 'get_plugins' ) ) {
266
- require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
267
- }
268
- // Create the plugins folder and file variables
269
- $plugin_folder = get_plugins( '/woocommerce' );
270
- $plugin_file = 'woocommerce.php';
271
-
272
- // If the plugin version number is set, return it
273
- if ( isset( $plugin_folder[$plugin_file]['Version'] ) ) {
274
- return $this->wc_version = $plugin_folder[$plugin_file]['Version'];
275
- }
276
-
277
- return $this->wc_version = false; // Not found
278
- }
279
 
280
  //INSTANCE
281
 
282
- /**
283
- * Singleton Instance
284
- *
285
- * @static
286
- * @return Singleton Instance
287
- */
288
- public static function instance() {
289
- if ( is_null( self::$_instance ) ) {
290
- self::$_instance = new self();
291
- }
292
- return self::$_instance;
293
- }
294
- protected static $_instance = null;
295
  }
296
 
297
 
298
  class WJECF_Wrap {
299
- protected $object = null;
300
- public $use_wc27 = true;
301
-
302
- public function __construct( $object ) {
303
- $this->object = $object;
304
- //error_log('Wrapping ' . get_class( $object ) );
305
- }
306
-
307
- public function get_id() {
308
- //Since WC 2.7
309
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_id' ) ) ) {
310
- return $this->object->get_id();
311
- }
312
- return $this->object->id;
313
- }
314
-
315
- public function holds( $object ) {
316
- return $object === $this->object;
317
- }
318
-
319
- /**
320
- * Get Meta Data by Key.
321
- * @since 2.4.0
322
- * @param string $key
323
- * @param bool $single return first found meta, or all
324
- * @return mixed
325
- */
326
- public function get_meta( $meta_key, $single = true ) {
327
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_meta' ) ) ) {
328
- return $this->get_meta_wc27( $meta_key, $single );
329
- }
330
-
331
- return $this->get_meta_legacy( $meta_key, $single );
332
-
333
- //return get_post_meta( $this->object->id, $meta_key, $single );
334
- //If no value found:
335
- //If $single is true, an empty string is returned.
336
- //If $single is false, an empty array is returned.
337
- }
338
-
339
- protected function get_meta_wc27( $meta_key, $single = true ) {
340
- $values = $this->object->get_meta( $meta_key, $single );
341
- if ($single) {
342
- return $values; //it's just one, dispite the plural in the name!
343
- }
344
-
345
- if ( $values === '' ) {
346
- return array(); //get_meta returns empty string if meta does not exist
347
- }
348
-
349
- return wp_list_pluck( array_values( $values ), 'value' ); //when not using array_values; the index might not start with 0
350
- }
351
-
352
- protected $meta_cache = array();
353
-
354
- protected function get_meta_legacy( $meta_key, $single = true ) {
355
- throw new Exception( sprintf( '%s::get_meta_legacy not implemented', get_class( $this ) ) );
356
- }
357
 
358
  }
359
 
@@ -362,347 +415,347 @@ class WJECF_Wrap {
362
  */
363
  class WJECF_Wrap_Coupon extends WJECF_Wrap {
364
 
365
- public function exists() {
366
- return $this->get_id() > 0;
367
- }
368
-
369
- public function get_code() {
370
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_code' ) ) ) {
371
- return $this->object->get_code();
372
- }
373
-
374
- return $this->object->code;
375
- }
376
-
377
- public function get_amount() {
378
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_amount' ) ) ) {
379
- return $this->object->get_amount();
380
- }
381
-
382
- return $this->object->coupon_amount;
383
- }
384
-
385
- public function get_individual_use() {
386
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_individual_use' ) ) ) {
387
- return $this->object->get_individual_use();
388
- }
389
-
390
- return $this->object->individual_use == 'yes';
391
- }
392
-
393
- public function get_limit_usage_to_x_items() {
394
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_limit_usage_to_x_items' ) ) ) {
395
- return $this->object->get_limit_usage_to_x_items();
396
- }
397
-
398
- return $this->object->limit_usage_to_x_items;
399
- }
400
-
401
- public function set_limit_usage_to_x_items( $limit_usage_to_x_items ) {
402
- if ( $this->use_wc27 && is_callable( array( $this->object, 'set_limit_usage_to_x_items' ) ) ) {
403
- $this->object->set_limit_usage_to_x_items( $limit_usage_to_x_items );
404
- } else {
405
- $this->object->limit_usage_to_x_items = $limit_usage_to_x_items;
406
- }
407
- }
408
-
409
- public function get_discount_type() {
410
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_discount_type' ) ) ) {
411
- return $this->object->get_discount_type();
412
- }
413
-
414
- return $this->object->discount_type;
415
- }
416
-
417
- public function set_discount_type( $discount_type ) {
418
- if ( $this->use_wc27 && is_callable( array( $this->object, 'set_discount_type' ) ) ) {
419
- $this->object->set_discount_type( $discount_type );
420
- } else {
421
- $this->object->discount_type = $discount_type;
422
- $this->object->type = $discount_type;
423
- }
424
- }
425
-
426
-
427
- public function get_email_restrictions() {
428
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_email_restrictions' ) ) ) {
429
- return $this->object->get_email_restrictions();
430
- }
431
-
432
- return $this->object->customer_email;
433
- }
434
-
435
- public function get_product_ids() {
436
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_product_ids' ) ) ) {
437
- return $this->object->get_product_ids();
438
- }
439
-
440
- return $this->object->product_ids;
441
- }
442
-
443
- public function get_free_shipping() {
444
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_free_shipping' ) ) ) {
445
- return $this->object->get_free_shipping();
446
- }
447
-
448
- return $this->object->enable_free_shipping();
449
- }
450
-
451
- public function get_product_categories() {
452
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_product_categories' ) ) ) {
453
- return $this->object->get_product_categories();
454
- }
455
-
456
- return $this->object->product_categories;
457
- }
458
-
459
- public function get_minimum_amount() {
460
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_minimum_amount' ) ) ) {
461
- return $this->object->get_minimum_amount();
462
- }
463
-
464
- return $this->object->minimum_amount;
465
- }
466
-
467
- /**
468
- * Set the product IDs this coupon cannot be used with.
469
- * @since 2.4.2 (For WC3.0)
470
- * @param array $excluded_product_ids
471
- * @throws WC_Data_Exception
472
- */
473
- public function set_excluded_product_ids( $excluded_product_ids ) {
474
- if ( $this->use_wc27 && is_callable( array( $this->object, 'set_excluded_product_ids' ) ) ) {
475
- $this->object->set_excluded_product_ids( $excluded_product_ids );
476
- } else {
477
- //NOTE: Prior to WC2.7 it was called exclude_ instead of excluded_
478
- $this->object->exclude_product_ids = $excluded_product_ids;
479
- }
480
- }
481
-
482
- /**
483
- * Set the product category IDs this coupon cannot be used with.
484
- * @since 2.4.2 (For WC3.0)
485
- * @param array $excluded_product_categories
486
- * @throws WC_Data_Exception
487
- */
488
- public function set_excluded_product_categories( $excluded_product_categories ) {
489
- if ( $this->use_wc27 && is_callable( array( $this->object, 'set_excluded_product_categories' ) ) ) {
490
- $this->object->set_excluded_product_categories( $excluded_product_categories );
491
- } else {
492
- //NOTE: Prior to WC2.7 it was called exclude_ instead of excluded_
493
- $this->object->exclude_product_categories = $excluded_product_categories;
494
- }
495
- }
496
-
497
- /**
498
- * Set if this coupon should excluded sale items or not.
499
- * @since 2.4.2 (For WC3.0)
500
- * @param bool $exclude_sale_items
501
- * @throws WC_Data_Exception
502
- */
503
- public function set_exclude_sale_items( $exclude_sale_items ) {
504
- if ( $this->use_wc27 && is_callable( array( $this->object, 'set_exclude_sale_items' ) ) ) {
505
- $this->object->set_exclude_sale_items( $exclude_sale_items );
506
- } else {
507
- //NOTE: Prior to WC2.7 it was yes/no instead of boolean
508
- $this->object->exclude_sale_items = $exclude_sale_items ? 'yes' : 'no';
509
- }
510
- }
511
-
512
- /**
513
- * Check the type of the coupon
514
- * @param string|array $type The type(s) we want to check for
515
- * @return bool True if the coupon is of the type
516
- */
517
- public function is_type( $type ) {
518
- //Backwards compatibility 2.2.11
519
- if ( method_exists( $this->object, 'is_type' ) ) {
520
- return $this->object->is_type( $type );
521
- }
522
-
523
- return ( $this->object->discount_type == $type || ( is_array( $type ) && in_array( $this->object->discount_type, $type ) ) ) ? true : false;
524
- }
525
-
526
- /**
527
- * Update single meta data item by meta key.
528
- * Call save() afterwards!
529
- * @since 2.4.0
530
- * @param string $key
531
- * @param mixed $value The value; use null to clear
532
- */
533
- public function set_meta( $meta_key, $value ) {
534
- if ( $this->use_wc27 && is_callable( array( $this->object, 'update_meta_data' ) ) ) {
535
- if ( $value === null ) {
536
- $this->object->delete_meta_data( $meta_key );
537
- } else {
538
- $this->object->update_meta_data( $meta_key, $value );
539
- }
540
- return;
541
- }
542
-
543
- $this->maybe_get_custom_fields();
544
- //WJECF()->log('...setting legacy meta ' . $meta_key );
545
- $this->legacy_custom_fields[ $meta_key ] = $value;
546
- $this->legacy_unsaved_keys[] = $meta_key;
547
- }
548
-
549
- /**
550
- * Save the metadata
551
- * @return id of this object
552
- */
553
- public function save() {
554
- //WJECF()->log('Saving ' . $this->get_id() );
555
- if ( $this->use_wc27 && is_callable( array( $this->object, 'save' ) ) ) {
556
- return $this->object->save();
557
- }
558
-
559
- //Save the unsaved...
560
- foreach( $this->legacy_unsaved_keys as $meta_key ) {
561
- //WJECF()->log('...saving legacy meta ' . $meta_key );
562
- $value = $this->legacy_custom_fields[ $meta_key ];
563
- if ( $value === null ) {
564
- delete_post_meta( $this->get_id(), $meta_key );
565
- } else {
566
- update_post_meta( $this->get_id(), $meta_key, $value );
567
- }
568
- }
569
- $this->legacy_unsaved_keys = array();
570
-
571
- return $this->get_id();
572
- }
573
-
574
- protected $legacy_custom_fields = null;
575
- protected $legacy_unsaved_keys = array();
576
-
577
- protected function maybe_get_custom_fields() {
578
- //Read custom fields if not yet done
579
- if ( is_null( $this->legacy_custom_fields ) ) {
580
- $this->legacy_custom_fields = $this->object->coupon_custom_fields;
581
- }
582
- }
583
-
584
- protected function get_meta_legacy( $meta_key, $single = true ) {
585
- //Read custom fields if not yet done
586
- $this->maybe_get_custom_fields();
587
-
588
- if ( isset( $this->legacy_custom_fields[ $meta_key ] ) ) {
589
- $values = $this->legacy_custom_fields[ $meta_key ];
590
- //WP_CLI::log( "LEGACY:" . print_r( $values, true ));
591
- if ($single) {
592
- return maybe_unserialize( reset( $values ) ); //reset yields the first
593
- }
594
- $values = array_map( 'maybe_unserialize', $values );
595
- return $values;
596
- }
597
-
598
- return $single ? '' : array();
599
- }
600
 
601
  }
602
 
603
  class WJECF_Wrap_Product extends WJECF_Wrap {
604
 
605
- public function is_variation() {
606
- return $this->object instanceof WC_Product_Variation;
607
- }
608
-
609
- /**
610
- * Retrieve the id of the product or the variation id if it's a variant.
611
- *
612
- * (2.4.0: Moved from WJECF_Controller to WJECF_WC)
613
- *
614
- * @param WC_Product $product
615
- * @return int|bool The variation or product id. False if not a valid product
616
- */
617
- public function get_product_or_variation_id() {
618
- if ( $this->is_variation() ) {
619
- return $this->get_variation_id();
620
- } elseif ( $this->object instanceof WC_Product ) {
621
- return $this->get_id();
622
- } else {
623
- return false;
624
- }
625
- }
626
-
627
- /**
628
- * Retrieve the id of the parent product if it's a variation; otherwise retrieve this products id
629
- *
630
- * (2.4.0: Moved from WJECF_Controller to WJECF_WC)
631
- *
632
- * @param WC_Product $product
633
- * @return int|bool The product id. False if this product is not a variation
634
- */
635
- public function get_variable_product_id() {
636
- if ( ! $this->is_variation() ) {
637
- return false;
638
- }
639
-
640
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_parent_id' ) ) ) {
641
- return $this->object->get_parent_id();
642
- } else {
643
- return wp_get_post_parent_id( $this->object->variation_id );
644
- }
645
- }
646
-
647
- /**
648
- * Get current variation id
649
- * @return int|bool False if this is not a variation
650
- */
651
- protected function get_variation_id() {
652
- if ( ! $this->is_variation() ) {
653
- return false;
654
- }
655
-
656
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_id' ) ) ) {
657
- //WP_CLI::log( "get_variation_id:WC27 " . get_class( $this->object ) );
658
- return $this->object->get_id();
659
- } elseif ( is_callable( array( $this->object, 'get_variation_id' ) ) ) {
660
- //WP_CLI::log( "get_variation_id:LEGACY " . get_class( $this->object ) );
661
- return $this->object->get_variation_id();
662
- }
663
- //WP_CLI::log( "get_variation_id:VERY OLD " . get_class( $this->object ) );
664
- return $this->object->variation_id;
665
- }
666
-
667
-
668
- public function get_name() {
669
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_name' ) ) ) {
670
- return $this->object->get_name();
671
- } else {
672
- return $this->object->post->post_title;
673
- }
674
- }
675
-
676
- public function get_description() {
677
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_description' ) ) ) {
678
- return $this->object->get_description();
679
- } else {
680
- return $this->object->post->post_content;
681
- }
682
- }
683
-
684
- public function get_short_description() {
685
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_short_description' ) ) ) {
686
- return $this->object->get_short_description();
687
- } else {
688
- return $this->object->post->post_excerpt;
689
- }
690
- }
691
-
692
- public function get_tag_ids() {
693
- if ( $this->use_wc27 && is_callable( array( $this->object, 'get_tag_ids' ) ) ) {
694
- return $this->object->get_tag_ids();
695
- } else {
696
- return $this->legacy_get_term_ids( 'product_tag' );
697
- }
698
- }
699
-
700
- protected function legacy_get_term_ids( $taxonomy ) {
701
- $terms = get_the_terms( $this->get_id(), $taxonomy );
702
- if ( false === $terms || is_wp_error( $terms ) ) {
703
- return array();
704
- }
705
- return wp_list_pluck( $terms, 'term_id' );
706
- }
707
 
708
  }
10
  */
11
  class WJECF_WC {
12
 
13
+ protected $wrappers = array();
14
+
15
+ /**
16
+ * Wrap a data object (WC 2.7 introduced WC_Data)
17
+ * @param type $object
18
+ * @return type
19
+ */
20
+ public function wrap( $object, $use_pool = true ) {
21
+ if ( $use_pool ) {
22
+ //Prevent a huge amount of wrappers to be initiated; one wrapper per object instance should do the trick
23
+ foreach( $this->wrappers as $wrapper ) {
24
+ if ($wrapper->holds( $object ) ) {
25
+ //error_log('Reusing wrapper ' . get_class( $object ) );
26
+ return $wrapper;
27
+ }
28
+ }
29
+ }
30
+
31
+ if ( is_numeric( $object ) ) {
32
+ $post_type = get_post_type( $object );
33
+ if ( $post_type == 'shop_coupon' ) {
34
+ $object = WJECF_WC()->get_coupon( $object );
35
+ } elseif ( $post_type == 'product' ) {
36
+ $object = new WC_Product( $object );
37
+ }
38
+ }
39
+
40
+
41
+ if ( $object instanceof WC_Coupon ) {
42
+ return $this->wrappers[] = new WJECF_Wrap_Coupon( $object );
43
+ }
44
+
45
+ if ( $object instanceof WC_Product ) {
46
+ return $this->wrappers[] = new WJECF_Wrap_Product( $object );
47
+ }
48
+
49
+ throw new Exception( 'Cannot wrap ' . get_class( $object ) );
50
+ }
51
 
52
  /**
53
  * Returns a specific item in the cart.
54
  *
55
  * @param string $cart_item_key Cart item key.
56
  * @return array Item data
57
+ */
58
+ public function get_cart_item( $cart_item_key ) {
59
+ if ( $this->check_woocommerce_version('2.2.9') ) {
60
+ return WC()->cart->get_cart_item( $cart_item_key );
61
+ }
62
+
63
+ return isset( WC()->cart->cart_contents[ $cart_item_key ] ) ? WC()->cart->cart_contents[ $cart_item_key ] : array();
64
+ }
65
+
66
+ /**
67
+ * Get categories of a product (and anchestors)
68
+ * @param int $product_id
69
+ * @return array product_cat_ids
70
+ */
71
+ public function wc_get_product_cat_ids( $product_id ) {
72
+ //Since WC 2.5.0
73
+ if ( function_exists( 'wc_get_product_cat_ids' ) ) {
74
+ return wc_get_product_cat_ids( $product_id );
75
+ }
76
+
77
+ $product_cats = wp_get_post_terms( $product_id, 'product_cat', array( "fields" => "ids" ) );
78
+
79
+ foreach ( $product_cats as $product_cat ) {
80
+ $product_cats = array_merge( $product_cats, get_ancestors( $product_cat, 'product_cat' ) );
81
+ }
82
+ return $product_cats;
83
+ }
84
+
85
+ /**
86
+ * Coupon types that apply to individual products. Controls which validation rules will apply.
87
+ *
88
+ * @since 2.5.0
89
+ * @return array
90
+ */
91
+ public function wc_get_product_coupon_types() {
92
+ //Since WC 2.5.0
93
+ if ( function_exists( 'wc_get_product_coupon_types' ) ) {
94
+ return wc_get_product_coupon_types();
95
+ }
96
+ return array( 'fixed_product', 'percent_product' );
97
+ }
98
+
99
+ public function wc_get_cart_coupon_types() {
100
+ //Since WC 2.5.0
101
+ if ( function_exists( 'wc_get_cart_coupon_types' ) ) {
102
+ return wc_get_cart_coupon_types();
103
+ }
104
+ return array( 'fixed_cart', 'percent' );
105
+ }
106
+
107
+ /**
108
+ * @since 2.4.0 for WC 2.7 compatibility
109
+ *
110
+ * Get a WC_Coupon object
111
+ * @param WC_Coupon|string $coupon The coupon code or a WC_Coupon object
112
+ * @return WC_Coupon The coupon object
113
+ */
114
+ public function get_coupon( $coupon ) {
115
+ if ( is_numeric( $coupon ) ) {
116
+ //By id
117
+ global $wpdb;
118
+ $coupon = $wpdb->get_var( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE id = %s AND post_type = 'shop_coupon' AND post_status = 'publish'", $coupon ) );
119
+ }
120
+ if ( ! ( $coupon instanceof WC_Coupon ) ) {
121
+ //By code
122
+ $coupon = new WC_Coupon( $coupon );
123
+ }
124
+ return $coupon;
125
+ }
126
 
127
  //ADMIN
128
 
129
+ /**
130
+ * Display a WooCommerce help tip
131
+ * @param string $tip The tip to display
132
+ * @return string
133
+ */
134
+ public function wc_help_tip( $tip ) {
135
+ //Since WC 2.5.0
136
+ if ( function_exists( 'wc_help_tip' ) ) {
137
+ return wc_help_tip( $tip );
138
+ }
139
+
140
+ return '<img class="help_tip" style="margin-top: 21px;" data-tip="' . esc_attr( $tip ) . '" src="' . esc_url( WC()->plugin_url() ) . '/assets/images/help.png" height="16" width="16" />';
141
+ }
142
+
143
+ /**
144
+ * Renders a product selection <input>. Will use either select2 v4 (WC3.0+) select2 v3 (WC2.3+) or chosen (< WC2.3)
145
+ * @param string $dom_id
146
+ * @param string $field_name
147
+ * @param array $selected_ids Array of integers
148
+ * @param string|null $placeholder
149
+ * @return void
150
+ */
151
+ public function render_admin_product_selector( $dom_id, $field_name, $selected_ids, $placeholder = null ) {
152
  $product_key_values = array();
153
  foreach ( $selected_ids as $product_id ) {
154
  $product = wc_get_product( $product_id );
157
  }
158
  }
159
 
160
+ if ( $placeholder === null ) $placeholder = __( 'Search for a product…', 'woocommerce' );
161
+
162
+ //In WooCommerce version 2.3.0 chosen was replaced by select2
163
+ //In WooCommerce version 3.0 select2 v3 was replaced by select2 v4
164
+ if ( $this->check_woocommerce_version('3.0') ) {
165
+ $this->render_admin_select2_v4_product_selector( $dom_id, $field_name, $product_key_values, $placeholder );
166
+ } elseif ( $this->check_woocommerce_version('2.3.0') ) {
167
+ $this->render_admin_select2_product_selector( $dom_id, $field_name, $product_key_values, $placeholder );
168
+ } else {
169
+ $this->render_admin_chosen_product_selector( $dom_id, $field_name, $product_key_values, $placeholder );
170
+ }
171
+ }
172
+
173
+
174
+ /**
175
+ * Renders a product selection <input>.
176
+ * Chosen (Legacy)
177
+ * @param string $dom_id
178
+ * @param string $field_name
179
+ * @param array $selected_ids Array of integers
180
+ * @param string|null $placeholder
181
+ */
182
+ private function render_admin_chosen_product_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder ) {
183
+ // $selected_keys_and_values must be an array of [ id => name ]
184
+
185
+ echo '<select id="' . esc_attr( $dom_id ) . '" name="' . esc_attr( $field_name ) . '[]" class="ajax_chosen_select_products_and_variations" multiple="multiple" data-placeholder="' . esc_attr( $placeholder ) . '">';
186
+ foreach ( $selected_keys_and_values as $product_id => $product_name ) {
187
+ echo '<option value="' . $product_id . '" selected="selected">' . $product_name . '</option>';
188
+ }
189
+ echo '</select>';
190
+ }
191
+
192
+ /**
193
+ * @since 2.4.1 for WC 3.0 compatibility
194
+ *
195
+ * Renders a product selection <input>.
196
+ * Select2 version 3 (Since WC 2.3.0)
197
+ * @param string $dom_id
198
+ * @param string $field_name
199
+ * @param array $selected_ids Array of integers
200
+ * @param string|null $placeholder
201
+ */
202
+ private function render_admin_select2_product_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder ) {
203
+ // $selected_keys_and_values must be an array of [ id => name ]
204
+
205
+ $json_encoded = esc_attr( json_encode( $selected_keys_and_values ) );
206
+ echo '<input type="hidden" class="wc-product-search" data-multiple="true" style="width: 50%;" name="'
207
+ . esc_attr( $field_name ) . '" data-placeholder="'
208
+ . esc_attr( $placeholder ) . '" data-action="woocommerce_json_search_products_and_variations" data-selected="'
209
+ . $json_encoded . '" value="' . implode( ',', array_keys( $selected_keys_and_values ) ) . '" />';
210
+
211
+ }
212
+
213
+ /**
214
+ * Renders a product selection <input>.
215
+ * Select2 version 4 (Since WC 3.0)
216
+ * @param string $dom_id
217
+ * @param string $field_name
218
+ * @param string $selected_keys_and_values
219
+ * @param string $placeholder
220
+ */
221
+ private function render_admin_select2_v4_product_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder ) {
222
+ // $selected_keys_and_values must be an array of [ id => name ]
223
+
224
+ $json_encoded = esc_attr( json_encode( $selected_keys_and_values ) );
225
+
226
+ echo '<select id="'. esc_attr( $dom_id ) .'" class="wc-product-search" name="'
227
+ . esc_attr( $field_name ) . '[]" multiple="multiple" style="width: 50%;" data-placeholder="'
228
+ . esc_attr( $placeholder ) . '" data-action="woocommerce_json_search_products_and_variations">';
229
+
230
+ foreach ( $selected_keys_and_values as $product_id => $product_name ) {
231
+ echo '<option value="' . esc_attr( $product_id ) . '"' . selected( true, true, false ) . '>' . wp_kses_post( $product_name ) . '</option>';
232
+ }
233
+
234
+ echo '</select>';
235
+ }
236
+
237
+
238
+ /**
239
+ * Renders a customer selection <input>. Will use either select2 v4 (WC3.0+) or select2 v3 (WC2.3+)
240
+ * @param string $dom_id
241
+ * @param string $field_name
242
+ * @param array $selected_customer_ids Array of integers
243
+ * @param string|null $placeholder
244
+ * @return void
245
+ */
246
+ public function render_admin_customer_selector( $dom_id, $field_name, $selected_customer_ids, $placeholder = null ) {
247
+ $selected_keys_and_values = array();
248
+ foreach ( $selected_customer_ids as $customer_id ) {
249
+ $customer = get_userdata( $customer_id );
250
+ if ( is_object( $customer ) ) {
251
+ $selected_keys_and_values[ $customer_id ] = $customer->display_name . ' (#' . $customer->ID . ' &ndash; ' . sanitize_email( $customer->user_email ) . ')';
252
+ }
253
+ }
254
+ if ( $placeholder === null ) $placeholder = __( 'Any customer', 'woocommerce-jos-autocoupon' );
255
+
256
+ //In WooCommerce version 2.3.0 chosen was replaced by select2
257
+ //In WooCommerce version 3.0 select2 v3 was replaced by select2 v4
258
+ if ( $this->check_woocommerce_version('3.0') ) {
259
+ $this->render_admin_select2_v4_customer_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder );
260
+ } else {
261
+ $this->render_admin_select2_customer_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder );
262
+ }
263
+ }
264
+
265
+ private function render_admin_select2_customer_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder ) {
266
+ // $selected_keys_and_values must be an array of [ id => name ] .e.g. [ 12 => 'John Smith (#12 john.smith@example.com)' ]
267
+
268
+ $json_encoded = esc_attr( json_encode( $selected_keys_and_values ) );
269
+ echo '<input type="hidden" class="wc-customer-search" data-multiple="true" style="width: 50%;" name="'
270
+ . esc_attr( $field_name ) . '" data-placeholder="'
271
+ . esc_attr( $placeholder ) . '" data-action="woocommerce_json_search_customers" data-selected="'
272
+ . $json_encoded . '" value="' . implode( ',', array_keys( $selected_keys_and_values ) ) . '" />';
273
+ }
274
+
275
+ private function render_admin_select2_v4_customer_selector( $dom_id, $field_name, $selected_keys_and_values, $placeholder ) {
276
+ // $selected_keys_and_values must be an array of [ id => name ]
277
+
278
+ $json_encoded = esc_attr( json_encode( $selected_keys_and_values ) );
279
+
280
+ echo '<select id="'. esc_attr( $dom_id ) .'" class="wc-customer-search" name="'
281
+ . esc_attr( $field_name ) . '[]" multiple="multiple" style="width: 50%;" data-placeholder="'
282
+ . esc_attr( $placeholder ) . '" data-action="woocommerce_json_search_customers">';
283
+
284
+ foreach ( $selected_keys_and_values as $key => $value ) {
285
+ echo '<option value="' . esc_attr( $key ) . '"' . selected( true, true, false ) . '>' . wp_kses_post( $value ) . '</option>';
286
+ }
287
+
288
+ echo '</select>';
289
+ }
290
 
291
 
292
  //VERSION
293
 
294
+ /**
295
+ * Check whether WooCommerce version is greater or equal than $req_version
296
+ * @param string @req_version The version to compare to
297
+ * @return bool true if WooCommerce is at least the given version
298
+ */
299
+ public function check_woocommerce_version( $req_version ) {
300
+ return version_compare( $this->get_woocommerce_version(), $req_version, '>=' );
301
+ }
302
+
303
+ private $wc_version = null;
304
+
305
+ /**
306
+ * Get the WooCommerce version number
307
+ * @return string|bool WC Version number or false if WC not detected
308
+ */
309
+ public function get_woocommerce_version() {
310
+ if ( isset( $this->wc_version ) ) {
311
+ return $this->wc_version;
312
+ }
313
+
314
+ if ( defined( 'WC_VERSION' ) ) {
315
+ return $this->wc_version = WC_VERSION;
316
+ }
317
+
318
+ // If get_plugins() isn't available, require it
319
+ if ( ! function_exists( 'get_plugins' ) ) {
320
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
321
+ }
322
+ // Create the plugins folder and file variables
323
+ $plugin_folder = get_plugins( '/woocommerce' );
324
+ $plugin_file = 'woocommerce.php';
325
+
326
+ // If the plugin version number is set, return it
327
+ if ( isset( $plugin_folder[$plugin_file]['Version'] ) ) {
328
+ return $this->wc_version = $plugin_folder[$plugin_file]['Version'];
329
+ }
330
+
331
+ return $this->wc_version = false; // Not found
332
+ }
333
 
334
  //INSTANCE
335
 
336
+ /**
337
+ * Singleton Instance
338
+ *
339
+ * @static
340
+ * @return Singleton Instance
341
+ */
342
+ public static function instance() {
343
+ if ( is_null( self::$_instance ) ) {
344
+ self::$_instance = new self();
345
+ }
346
+ return self::$_instance;
347
+ }
348
+ protected static $_instance = null;
349
  }
350
 
351
 
352
  class WJECF_Wrap {
353
+ protected $object = null;
354
+
355
+ public function __construct( $object ) {
356
+ $this->object = $object;
357
+ //error_log('Wrapping ' . get_class( $object ) );
358
+ }
359
+
360
+ public function get_id() {
361
+ //Since WC 2.7
362
+ if ( is_callable( array( $this->object, 'get_id' ) ) ) {
363
+ return $this->object->get_id();
364
+ }
365
+ return $this->object->id;
366
+ }
367
+
368
+ public function holds( $object ) {
369
+ return $object === $this->object;
370
+ }
371
+
372
+ /**
373
+ * Get Meta Data by Key.
374
+ * @since 2.4.0
375
+ * @param string $key
376
+ * @param bool $single return first found meta, or all
377
+ * @return mixed
378
+ */
379
+ public function get_meta( $meta_key, $single = true ) {
380
+ if ( is_callable( array( $this->object, 'get_meta' ) ) ) {
381
+ return $this->get_meta_wc27( $meta_key, $single );
382
+ }
383
+
384
+ return $this->get_meta_legacy( $meta_key, $single );
385
+
386
+ //return get_post_meta( $this->object->id, $meta_key, $single );
387
+ //If no value found:
388
+ //If $single is true, an empty string is returned.
389
+ //If $single is false, an empty array is returned.
390
+ }
391
+
392
+ protected function get_meta_wc27( $meta_key, $single = true ) {
393
+ $values = $this->object->get_meta( $meta_key, $single );
394
+ if ($single) {
395
+ return $values; //it's just one, dispite the plural in the name!
396
+ }
397
+
398
+ if ( $values === '' ) {
399
+ return array(); //get_meta returns empty string if meta does not exist
400
+ }
401
+
402
+ return wp_list_pluck( array_values( $values ), 'value' ); //when not using array_values; the index might not start with 0
403
+ }
404
+
405
+ protected $meta_cache = array();
406
+
407
+ protected function get_meta_legacy( $meta_key, $single = true ) {
408
+ throw new Exception( sprintf( '%s::get_meta_legacy not implemented', get_class( $this ) ) );
409
+ }
 
410
 
411
  }
412
 
415
  */
416
  class WJECF_Wrap_Coupon extends WJECF_Wrap {
417
 
418
+ public function exists() {
419
+ return $this->get_id() > 0;
420
+ }
421
+
422
+ public function get_code() {
423
+ if ( is_callable( array( $this->object, 'get_code' ) ) ) {
424
+ return $this->object->get_code();
425
+ }
426
+
427
+ return $this->object->code;
428
+ }
429
+
430
+ public function get_amount() {
431
+ if ( is_callable( array( $this->object, 'get_amount' ) ) ) {
432
+ return $this->object->get_amount();
433
+ }
434
+
435
+ return $this->object->coupon_amount;
436
+ }
437
+
438
+ public function get_individual_use() {
439
+ if ( is_callable( array( $this->object, 'get_individual_use' ) ) ) {
440
+ return $this->object->get_individual_use();
441
+ }
442
+
443
+ return $this->object->individual_use == 'yes';
444
+ }
445
+
446
+ public function get_limit_usage_to_x_items() {
447
+ if ( is_callable( array( $this->object, 'get_limit_usage_to_x_items' ) ) ) {
448
+ return $this->object->get_limit_usage_to_x_items();
449
+ }
450
+
451
+ return $this->object->limit_usage_to_x_items;
452
+ }
453
+
454
+ public function set_limit_usage_to_x_items( $limit_usage_to_x_items ) {
455
+ if ( is_callable( array( $this->object, 'set_limit_usage_to_x_items' ) ) ) {
456
+ $this->object->set_limit_usage_to_x_items( $limit_usage_to_x_items );
457
+ } else {
458
+ $this->object->limit_usage_to_x_items = $limit_usage_to_x_items;
459
+ }
460
+ }
461
+
462
+ public function get_discount_type() {
463
+ if ( is_callable( array( $this->object, 'get_discount_type' ) ) ) {
464
+ return $this->object->get_discount_type();
465
+ }
466
+
467
+ return $this->object->discount_type;
468
+ }
469
+
470
+ public function set_discount_type( $discount_type ) {
471
+ if ( is_callable( array( $this->object, 'set_discount_type' ) ) ) {
472
+ $this->object->set_discount_type( $discount_type );
473
+ } else {
474
+ $this->object->discount_type = $discount_type;
475
+ $this->object->type = $discount_type;
476
+ }
477
+ }
478
+
479
+
480
+ public function get_email_restrictions() {
481
+ if ( is_callable( array( $this->object, 'get_email_restrictions' ) ) ) {
482
+ return $this->object->get_email_restrictions();
483
+ }
484
+
485
+ return $this->object->customer_email;
486
+ }
487
+
488
+ public function get_product_ids() {
489
+ if ( is_callable( array( $this->object, 'get_product_ids' ) ) ) {
490
+ return $this->object->get_product_ids();
491
+ }
492
+
493
+ return $this->object->product_ids;
494
+ }
495
+
496
+ public function get_free_shipping() {
497
+ if ( is_callable( array( $this->object, 'get_free_shipping' ) ) ) {
498
+ return $this->object->get_free_shipping();
499
+ }
500
+
501
+ return $this->object->enable_free_shipping();
502
+ }
503
+
504
+ public function get_product_categories() {
505
+ if ( is_callable( array( $this->object, 'get_product_categories' ) ) ) {
506
+ return $this->object->get_product_categories();
507
+ }
508
+
509
+ return $this->object->product_categories;
510
+ }
511
+
512
+ public function get_minimum_amount() {
513
+ if ( is_callable( array( $this->object, 'get_minimum_amount' ) ) ) {
514
+ return $this->object->get_minimum_amount();
515
+ }
516
+
517
+ return $this->object->minimum_amount;
518
+ }
519
+
520
+ /**
521
+ * Set the product IDs this coupon cannot be used with.
522
+ * @since 2.4.2 (For WC3.0)
523
+ * @param array $excluded_product_ids
524
+ * @throws WC_Data_Exception
525
+ */
526
+ public function set_excluded_product_ids( $excluded_product_ids ) {
527
+ if ( is_callable( array( $this->object, 'set_excluded_product_ids' ) ) ) {
528
+ $this->object->set_excluded_product_ids( $excluded_product_ids );
529
+ } else {
530
+ //NOTE: Prior to WC2.7 it was called exclude_ instead of excluded_
531
+ $this->object->exclude_product_ids = $excluded_product_ids;
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Set the product category IDs this coupon cannot be used with.
537
+ * @since 2.4.2 (For WC3.0)
538
+ * @param array $excluded_product_categories
539
+ * @throws WC_Data_Exception
540
+ */
541
+ public function set_excluded_product_categories( $excluded_product_categories ) {
542
+ if ( is_callable( array( $this->object, 'set_excluded_product_categories' ) ) ) {
543
+ $this->object->set_excluded_product_categories( $excluded_product_categories );
544
+ } else {
545
+ //NOTE: Prior to WC2.7 it was called exclude_ instead of excluded_
546
+ $this->object->exclude_product_categories = $excluded_product_categories;
547
+ }
548
+ }
549
+
550
+ /**
551
+ * Set if this coupon should excluded sale items or not.
552
+ * @since 2.4.2 (For WC3.0)
553
+ * @param bool $exclude_sale_items
554
+ * @throws WC_Data_Exception
555
+ */
556
+ public function set_exclude_sale_items( $exclude_sale_items ) {
557
+ if ( is_callable( array( $this->object, 'set_exclude_sale_items' ) ) ) {
558
+ $this->object->set_exclude_sale_items( $exclude_sale_items );
559
+ } else {
560
+ //NOTE: Prior to WC2.7 it was yes/no instead of boolean
561
+ $this->object->exclude_sale_items = $exclude_sale_items ? 'yes' : 'no';
562
+ }
563
+ }
564
+
565
+ /**
566
+ * Check the type of the coupon
567
+ * @param string|array $type The type(s) we want to check for
568
+ * @return bool True if the coupon is of the type
569
+ */
570
+ public function is_type( $type ) {
571
+ //Backwards compatibility 2.2.11
572
+ if ( method_exists( $this->object, 'is_type' ) ) {
573
+ return $this->object->is_type( $type );
574
+ }
575
+
576
+ return ( $this->object->discount_type == $type || ( is_array( $type ) && in_array( $this->object->discount_type, $type ) ) ) ? true : false;
577
+ }
578
+
579
+ /**
580
+ * Update single meta data item by meta key.
581
+ * Call save() afterwards!
582
+ * @since 2.4.0
583
+ * @param string $key
584
+ * @param mixed $value The value; use null to clear
585
+ */
586
+ public function set_meta( $meta_key, $value ) {
587
+ if ( is_callable( array( $this->object, 'update_meta_data' ) ) ) {
588
+ if ( $value === null ) {
589
+ $this->object->delete_meta_data( $meta_key );
590
+ } else {
591
+ $this->object->update_meta_data( $meta_key, $value );
592
+ }
593
+ return;
594
+ }
595
+
596
+ $this->maybe_get_custom_fields();
597
+ //WJECF()->log('...setting legacy meta ' . $meta_key );
598
+ $this->legacy_custom_fields[ $meta_key ] = $value;
599
+ $this->legacy_unsaved_keys[] = $meta_key;
600
+ }
601
+
602
+ /**
603
+ * Save the metadata
604
+ * @return id of this object
605
+ */
606
+ public function save() {
607
+ //WJECF()->log('Saving ' . $this->get_id() );
608
+ if ( is_callable( array( $this->object, 'save' ) ) ) {
609
+ return $this->object->save();
610
+ }
611
+
612
+ //Save the unsaved...
613
+ foreach( $this->legacy_unsaved_keys as $meta_key ) {
614
+ //WJECF()->log('...saving legacy meta ' . $meta_key );
615
+ $value = $this->legacy_custom_fields[ $meta_key ];
616
+ if ( $value === null ) {
617
+ delete_post_meta( $this->get_id(), $meta_key );
618
+ } else {
619
+ update_post_meta( $this->get_id(), $meta_key, $value );
620
+ }
621
+ }
622
+ $this->legacy_unsaved_keys = array();
623
+
624
+ return $this->get_id();
625
+ }
626
+
627
+ protected $legacy_custom_fields = null;
628
+ protected $legacy_unsaved_keys = array();
629
+
630
+ protected function maybe_get_custom_fields() {
631
+ //Read custom fields if not yet done
632
+ if ( is_null( $this->legacy_custom_fields ) ) {
633
+ $this->legacy_custom_fields = $this->object->coupon_custom_fields;
634
+ }
635
+ }
636
+
637
+ protected function get_meta_legacy( $meta_key, $single = true ) {
638
+ //Read custom fields if not yet done
639
+ $this->maybe_get_custom_fields();
640
+
641
+ if ( isset( $this->legacy_custom_fields[ $meta_key ] ) ) {
642
+ $values = $this->legacy_custom_fields[ $meta_key ];
643
+ //WP_CLI::log( "LEGACY:" . print_r( $values, true ));
644
+ if ($single) {
645
+ return maybe_unserialize( reset( $values ) ); //reset yields the first
646
+ }
647
+ $values = array_map( 'maybe_unserialize', $values );
648
+ return $values;
649
+ }
650
+
651
+ return $single ? '' : array();
652
+ }
653
 
654
  }
655
 
656
  class WJECF_Wrap_Product extends WJECF_Wrap {
657
 
658
+ public function is_variation() {
659
+ return $this->object instanceof WC_Product_Variation;
660
+ }
661
+
662
+ /**
663
+ * Retrieve the id of the product or the variation id if it's a variant.
664
+ *
665
+ * (2.4.0: Moved from WJECF_Controller to WJECF_WC)
666
+ *
667
+ * @param WC_Product $product
668
+ * @return int|bool The variation or product id. False if not a valid product
669
+ */
670
+ public function get_product_or_variation_id() {
671
+ if ( $this->is_variation() ) {
672
+ return $this->get_variation_id();
673
+ } elseif ( $this->object instanceof WC_Product ) {
674
+ return $this->get_id();
675
+ } else {
676
+ return false;
677
+ }
678
+ }
679
+
680
+ /**
681
+ * Retrieve the id of the parent product if it's a variation; otherwise retrieve this products id
682
+ *
683
+ * (2.4.0: Moved from WJECF_Controller to WJECF_WC)
684
+ *
685
+ * @param WC_Product $product
686
+ * @return int|bool The product id. False if this product is not a variation
687
+ */
688
+ public function get_variable_product_id() {
689
+ if ( ! $this->is_variation() ) {
690
+ return false;
691
+ }
692
+
693
+ if ( is_callable( array( $this->object, 'get_parent_id' ) ) ) {
694
+ return $this->object->get_parent_id();
695
+ } else {
696
+ return wp_get_post_parent_id( $this->object->variation_id );
697
+ }
698
+ }
699
+
700
+ /**
701
+ * Get current variation id
702
+ * @return int|bool False if this is not a variation
703
+ */
704
+ protected function get_variation_id() {
705
+ if ( ! $this->is_variation() ) {
706
+ return false;
707
+ }
708
+
709
+ if ( is_callable( array( $this->object, 'get_id' ) ) ) {
710
+ //WP_CLI::log( "get_variation_id:WC27 " . get_class( $this->object ) );
711
+ return $this->object->get_id();
712
+ } elseif ( is_callable( array( $this->object, 'get_variation_id' ) ) ) {
713
+ //WP_CLI::log( "get_variation_id:LEGACY " . get_class( $this->object ) );
714
+ return $this->object->get_variation_id();
715
+ }
716
+ //WP_CLI::log( "get_variation_id:VERY OLD " . get_class( $this->object ) );
717
+ return $this->object->variation_id;
718
+ }
719
+
720
+
721
+ public function get_name() {
722
+ if ( is_callable( array( $this->object, 'get_name' ) ) ) {
723
+ return $this->object->get_name();
724
+ } else {
725
+ return $this->object->post->post_title;
726
+ }
727
+ }
728
+
729
+ public function get_description() {
730
+ if ( is_callable( array( $this->object, 'get_description' ) ) ) {
731
+ return $this->object->get_description();
732
+ } else {
733
+ return $this->object->post->post_content;
734
+ }
735
+ }
736
+
737
+ public function get_short_description() {
738
+ if ( is_callable( array( $this->object, 'get_short_description' ) ) ) {
739
+ return $this->object->get_short_description();
740
+ } else {
741
+ return $this->object->post->post_excerpt;
742
+ }
743
+ }
744
+
745
+ public function get_tag_ids() {
746
+ if ( is_callable( array( $this->object, 'get_tag_ids' ) ) ) {
747
+ return $this->object->get_tag_ids();
748
+ } else {
749
+ return $this->legacy_get_term_ids( 'product_tag' );
750
+ }
751
+ }
752
+
753
+ protected function legacy_get_term_ids( $taxonomy ) {
754
+ $terms = get_the_terms( $this->get_id(), $taxonomy );
755
+ if ( false === $terms || is_wp_error( $terms ) ) {
756
+ return array();
757
+ }
758
+ return wp_list_pluck( $terms, 'term_id' );
759
+ }
760
 
761
  }
includes/wjecf-wpml.php CHANGED
@@ -7,53 +7,53 @@ defined('ABSPATH') or die();
7
  */
8
  class WJECF_WPML extends Abstract_WJECF_Plugin {
9
 
10
- public function __construct() {
11
- $this->set_plugin_data( array(
12
- 'description' => __( 'Compatiblity with WPML.', 'woocommerce-jos-autocoupon' ),
13
- 'dependencies' => array(),
14
- 'can_be_disabled' => true
15
- ) );
16
- }
17
-
18
- public function init_hook() {
19
- global $sitepress;
20
- if ( isset( $sitepress ) ) {
21
- //WJECF_Controller hooks
22
- add_filter( 'wjecf_get_product_ids', array( $this, 'filter_get_product_ids' ), 10 );
23
- add_filter( 'wjecf_get_product_cat_ids', array( $this, 'filter_get_product_cat_ids' ), 10 );
24
- }
25
- }
26
 
27
  //HOOKS
28
 
29
- public function filter_get_product_ids( $product_ids ) {
30
- return $this->get_translated_object_ids( $product_ids, 'product' );
31
- }
32
 
33
- public function filter_get_product_cat_ids( $product_cat_ids ) {
34
- return $this->get_translated_object_ids( $product_cat_ids, 'product_cat' );
35
- }
36
 
37
 
38
  //FUNCTIONS
39
 
40
- /**
41
- * Get the ids of all the translations. Otherwise return the original array
42
- *
43
- * @param int|array $product_ids The product_ids to find the translations for
44
- * @return array The product ids of all translations
45
- *
46
- */
47
- public function get_translated_object_ids( $object_ids, $object_type ) {
48
- //Make sure it's an array
49
- if ( ! is_array( $object_ids ) ) {
50
- $object_ids = array( $object_ids );
51
- }
52
 
53
  $translated_object_ids = array();
54
  foreach( $object_ids as $object_id) {
55
- $translated_object_ids[] = apply_filters( 'wpml_object_id', $object_id, $object_type );
56
  }
57
  return $translated_object_ids;
58
- }
59
  }
7
  */
8
  class WJECF_WPML extends Abstract_WJECF_Plugin {
9
 
10
+ public function __construct() {
11
+ $this->set_plugin_data( array(
12
+ 'description' => __( 'Compatiblity with WPML.', 'woocommerce-jos-autocoupon' ),
13
+ 'dependencies' => array(),
14
+ 'can_be_disabled' => true
15
+ ) );
16
+ }
17
+
18
+ public function init_hook() {
19
+ global $sitepress;
20
+ if ( isset( $sitepress ) ) {
21
+ //WJECF_Controller hooks
22
+ add_filter( 'wjecf_get_product_ids', array( $this, 'filter_get_product_ids' ), 10 );
23
+ add_filter( 'wjecf_get_product_cat_ids', array( $this, 'filter_get_product_cat_ids' ), 10 );
24
+ }
25
+ }
26
 
27
  //HOOKS
28
 
29
+ public function filter_get_product_ids( $product_ids ) {
30
+ return $this->get_translated_object_ids( $product_ids, 'product' );
31
+ }
32
 
33
+ public function filter_get_product_cat_ids( $product_cat_ids ) {
34
+ return $this->get_translated_object_ids( $product_cat_ids, 'product_cat' );
35
+ }
36
 
37
 
38
  //FUNCTIONS
39
 
40
+ /**
41
+ * Get the ids of all the translations. Otherwise return the original array
42
+ *
43
+ * @param int|array $product_ids The product_ids to find the translations for
44
+ * @return array The product ids of all translations
45
+ *
46
+ */
47
+ public function get_translated_object_ids( $object_ids, $object_type ) {
48
+ //Make sure it's an array
49
+ if ( ! is_array( $object_ids ) ) {
50
+ $object_ids = array( $object_ids );
51
+ }
52
 
53
  $translated_object_ids = array();
54
  foreach( $object_ids as $object_id) {
55
+ $translated_object_ids[] = apply_filters( 'wpml_object_id', $object_id, $object_type );
56
  }
57
  return $translated_object_ids;
58
+ }
59
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5T9XQ
4
  Tags: woocommerce, coupons, discount
5
  Requires at least: 4.0.0
6
  Tested up to: 4.7.3
7
- Stable tag: 2.4.2.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -106,6 +106,9 @@ Sure! [This](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5T9XQ
106
 
107
  == Changelog ==
108
 
 
 
 
109
  = 2.4.2.1 =
110
  * FIX: WooCommerce < 2.7 compatibility
111
 
4
  Tags: woocommerce, coupons, discount
5
  Requires at least: 4.0.0
6
  Tested up to: 4.7.3
7
+ Stable tag: 2.4.3
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
106
 
107
  == Changelog ==
108
 
109
+ = 2.4.3 =
110
+ * FIX: Customer selector WooCommerce 3.0.0 compatibility
111
+
112
  = 2.4.2.1 =
113
  * FIX: WooCommerce < 2.7 compatibility
114
 
woocommerce-jos-autocoupon.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: WooCommerce Extended Coupon Features
4
  * Plugin URI: http://www.soft79.nl
5
  * Description: Additional functionality for WooCommerce Coupons: Apply certain coupons automatically, allow applying coupons via an url, etc...
6
- * Version: 2.4.2.1
7
  * Author: Soft79
8
  * License: GPL2
9
  */
@@ -25,103 +25,103 @@ defined('ABSPATH') or die();
25
 
26
  if ( ! function_exists( 'wjecf_load_plugin_textdomain' ) ) {
27
 
28
- /**
29
- * Include the file once if it exists.
30
- * @param string $filename
31
- * @return void
32
- */
33
- function wjecf_optional_include( $filename ) {
34
- if ( ! file_exists( dirname( __FILE__ ) . '/' . $filename ) ) {
35
- return false;
36
- }
37
-
38
- include_once( $filename );
39
- return true;
40
- }
41
-
42
- require_once( 'includes/wjecf-wc.php' );
43
- require_once( 'includes/wjecf-controller.php' );
44
- require_once( 'includes/abstract-wjecf-plugin.php' );
45
- require_once( 'includes/admin/wjecf-admin.php' );
46
- require_once( 'includes/admin/wjecf-admin-auto-upgrade.php' );
47
- //Optional
48
- wjecf_optional_include( 'includes/wjecf-autocoupon.php' );
49
- wjecf_optional_include( 'includes/wjecf-wpml.php' );
50
- //PRO
51
- wjecf_optional_include( 'includes/wjecf-pro-controller.php' );
52
- wjecf_optional_include( 'includes/wjecf-pro-free-products.php' );
53
- wjecf_optional_include( 'includes/wjecf-pro-coupon-queueing.php' );
54
- wjecf_optional_include( 'includes/wjecf-pro-product-filter.php' );
55
- wjecf_optional_include( 'includes/wjecf-pro-limit-discount-quantities.php' );
56
- wjecf_optional_include( 'includes/wjecf-pro-api.php' );
57
-
58
- //Translations
59
- add_action( 'plugins_loaded', 'wjecf_load_plugin_textdomain' );
60
- function wjecf_load_plugin_textdomain() {
61
- $locale = apply_filters( 'plugin_locale', get_locale(), 'woocommerce' );
62
-
63
- load_textdomain( 'woocommerce-jos-autocoupon', WP_LANG_DIR . '/woocommerce-jos-autocoupon/woocommerce-jos-autocoupon-' . $locale . '.mo' );
64
- load_plugin_textdomain('woocommerce-jos-autocoupon', false, basename(dirname(__FILE__)) . '/languages/' );
65
-
66
- //WP-cli for debugging
67
- if ( defined( 'WP_CLI' ) && WP_CLI ) {
68
- if ( wjecf_optional_include('includes/WJECF_Debug_CLI.php') ) {
69
- WP_CLI::add_command( 'wjecf', 'WJECF_Debug_CLI' );
70
- }
71
- }
72
-
73
- // Only Initiate the plugin if WooCommerce is active
74
- if ( WJECF_WC::instance()->get_woocommerce_version() == false ) {
75
- add_action( 'admin_notices', 'wjecf_admin_notice' );
76
- function wjecf_admin_notice() {
77
- $msg = __( 'WooCommerce Extended Coupon Features is disabled because WooCommerce could not be detected.', 'woocommerce-jos-autocoupon' );
78
- echo '<div class="error"><p>' . $msg . '</p></div>';
79
- }
80
- } else {
81
-
82
- function WJECF_WC() {
83
- return WJECF_WC::instance();
84
- }
85
-
86
- function WJECF_Wrap( $object ) {
87
- return WJECF_WC::instance()->wrap( $object );
88
- }
89
-
90
- /**
91
- * Get the instance of WJECF
92
- * @return WJECF_Controller|WJECF_Pro_Controller The instance of WJECF
93
- */
94
- function WJECF() {
95
- if ( class_exists( 'WJECF_Pro_Controller' ) ) {
96
- return WJECF_Pro_Controller::instance();
97
- } else {
98
- return WJECF_Controller::instance();
99
- }
100
- }
101
-
102
- /**
103
- * Get the instance of WJECF_Admin
104
- * @return WJECF_Admin The instance of WJECF_Admin
105
- */
106
- function WJECF_ADMIN() {
107
- return WJECF()->get_plugin('WJECF_Admin');
108
- }
109
-
110
- $wjecf_extended_coupon_features = WJECF();
111
-
112
- WJECF()->add_plugin('WJECF_Admin');
113
- WJECF()->add_plugin('WJECF_Admin_Auto_Upgrade');
114
- WJECF()->add_plugin('WJECF_AutoCoupon');
115
- WJECF()->add_plugin('WJECF_WPML');
116
- if ( WJECF()->is_pro() ) {
117
- WJECF()->add_plugin('WJECF_Pro_Free_Products');
118
- WJECF()->add_plugin('WJECF_Pro_Coupon_Queueing');
119
- WJECF()->add_plugin('WJECF_Pro_Product_Filter');
120
- WJECF()->add_plugin('WJECF_Pro_Limit_Discount_Quantities');
121
- }
122
- WJECF()->start();
123
- }
124
- }
125
 
126
  }
127
 
@@ -134,8 +134,8 @@ if ( ! function_exists( 'wjecf_load_plugin_textdomain' ) ) {
134
 
135
  //Update the cart preview when the billing email is changed by the customer
136
  add_filter( 'woocommerce_checkout_fields', function( $checkout_fields ) {
137
- $checkout_fields['billing']['billing_email']['class'][] = 'update_totals_on_change';
138
- return $checkout_fields;
139
  } );
140
 
141
  // */
@@ -147,13 +147,13 @@ add_filter( 'woocommerce_checkout_fields', function( $checkout_fields ) {
147
 
148
  //Update the cart preview when payment method is changed by the customer
149
  add_action( 'woocommerce_review_order_after_submit' , function () {
150
- ?><script type="text/javascript">
151
- jQuery(document).ready(function($){
152
- $(document.body).on('change', 'input[name="payment_method"]', function() {
153
- $('body').trigger('update_checkout');
154
- //$.ajax( $fragment_refresh );
155
- });
156
- });
157
- </script><?php
158
  } );
159
  // */
3
  * Plugin Name: WooCommerce Extended Coupon Features
4
  * Plugin URI: http://www.soft79.nl
5
  * Description: Additional functionality for WooCommerce Coupons: Apply certain coupons automatically, allow applying coupons via an url, etc...
6
+ * Version: 2.4.3
7
  * Author: Soft79
8
  * License: GPL2
9
  */
25
 
26
  if ( ! function_exists( 'wjecf_load_plugin_textdomain' ) ) {
27
 
28
+ /**
29
+ * Include the file once if it exists.
30
+ * @param string $filename
31
+ * @return void
32
+ */
33
+ function wjecf_optional_include( $filename ) {
34
+ if ( ! file_exists( dirname( __FILE__ ) . '/' . $filename ) ) {
35
+ return false;
36
+ }
37
+
38
+ include_once( $filename );
39
+ return true;
40
+ }
41
+
42
+ require_once( 'includes/wjecf-wc.php' );
43
+ require_once( 'includes/wjecf-controller.php' );
44
+ require_once( 'includes/abstract-wjecf-plugin.php' );
45
+ require_once( 'includes/admin/wjecf-admin.php' );
46
+ require_once( 'includes/admin/wjecf-admin-auto-upgrade.php' );
47
+ //Optional
48
+ wjecf_optional_include( 'includes/wjecf-autocoupon.php' );
49
+ wjecf_optional_include( 'includes/wjecf-wpml.php' );
50
+ //PRO
51
+ wjecf_optional_include( 'includes/wjecf-pro-controller.php' );
52
+ wjecf_optional_include( 'includes/wjecf-pro-free-products.php' );
53
+ wjecf_optional_include( 'includes/wjecf-pro-coupon-queueing.php' );
54
+ wjecf_optional_include( 'includes/wjecf-pro-product-filter.php' );
55
+ wjecf_optional_include( 'includes/wjecf-pro-limit-discount-quantities.php' );
56
+ wjecf_optional_include( 'includes/wjecf-pro-api.php' );
57
+
58
+ //Translations
59
+ add_action( 'plugins_loaded', 'wjecf_load_plugin_textdomain' );
60
+ function wjecf_load_plugin_textdomain() {
61
+ $locale = apply_filters( 'plugin_locale', get_locale(), 'woocommerce' );
62
+
63
+ load_textdomain( 'woocommerce-jos-autocoupon', WP_LANG_DIR . '/woocommerce-jos-autocoupon/woocommerce-jos-autocoupon-' . $locale . '.mo' );
64
+ load_plugin_textdomain('woocommerce-jos-autocoupon', false, basename(dirname(__FILE__)) . '/languages/' );
65
+
66
+ // //WP-cli for debugging
67
+ // if ( defined( 'WP_CLI' ) && WP_CLI ) {
68
+ // if ( wjecf_optional_include('includes/WJECF_Debug_CLI.php') ) {
69
+ // WP_CLI::add_command( 'wjecf', 'WJECF_Debug_CLI' );
70
+ // }
71
+ // }
72
+
73
+ // Only Initiate the plugin if WooCommerce is active
74
+ if ( WJECF_WC::instance()->get_woocommerce_version() == false ) {
75
+ add_action( 'admin_notices', 'wjecf_admin_notice' );
76
+ function wjecf_admin_notice() {
77
+ $msg = __( 'WooCommerce Extended Coupon Features is disabled because WooCommerce could not be detected.', 'woocommerce-jos-autocoupon' );
78
+ echo '<div class="error"><p>' . $msg . '</p></div>';
79
+ }
80
+ } else {
81
+
82
+ function WJECF_WC() {
83
+ return WJECF_WC::instance();
84
+ }
85
+
86
+ function WJECF_Wrap( $object ) {
87
+ return WJECF_WC::instance()->wrap( $object );
88
+ }
89
+
90
+ /**
91
+ * Get the instance of WJECF
92
+ * @return WJECF_Controller|WJECF_Pro_Controller The instance of WJECF
93
+ */
94
+ function WJECF() {
95
+ if ( class_exists( 'WJECF_Pro_Controller' ) ) {
96
+ return WJECF_Pro_Controller::instance();
97
+ } else {
98
+ return WJECF_Controller::instance();
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Get the instance of WJECF_Admin
104
+ * @return WJECF_Admin The instance of WJECF_Admin
105
+ */
106
+ function WJECF_ADMIN() {
107
+ return WJECF()->get_plugin('WJECF_Admin');
108
+ }
109
+
110
+ $wjecf_extended_coupon_features = WJECF();
111
+
112
+ WJECF()->add_plugin('WJECF_Admin');
113
+ WJECF()->add_plugin('WJECF_Admin_Auto_Upgrade');
114
+ WJECF()->add_plugin('WJECF_AutoCoupon');
115
+ WJECF()->add_plugin('WJECF_WPML');
116
+ if ( WJECF()->is_pro() ) {
117
+ WJECF()->add_plugin('WJECF_Pro_Free_Products');
118
+ WJECF()->add_plugin('WJECF_Pro_Coupon_Queueing');
119
+ WJECF()->add_plugin('WJECF_Pro_Product_Filter');
120
+ WJECF()->add_plugin('WJECF_Pro_Limit_Discount_Quantities');
121
+ }
122
+ WJECF()->start();
123
+ }
124
+ }
125
 
126
  }
127
 
134
 
135
  //Update the cart preview when the billing email is changed by the customer
136
  add_filter( 'woocommerce_checkout_fields', function( $checkout_fields ) {
137
+ $checkout_fields['billing']['billing_email']['class'][] = 'update_totals_on_change';
138
+ return $checkout_fields;
139
  } );
140
 
141
  // */
147
 
148
  //Update the cart preview when payment method is changed by the customer
149
  add_action( 'woocommerce_review_order_after_submit' , function () {
150
+ ?><script type="text/javascript">
151
+ jQuery(document).ready(function($){
152
+ $(document.body).on('change', 'input[name="payment_method"]', function() {
153
+ $('body').trigger('update_checkout');
154
+ //$.ajax( $fragment_refresh );
155
+ });
156
+ });
157
+ </script><?php
158
  } );
159
  // */