WooCommerce Cart Abandonment Recovery - Version 1.2.7

Version Description

Download this release

Release Info

Developer sandesh055
Plugin Icon 128x128 WooCommerce Cart Abandonment Recovery
Version 1.2.7
Comparing to
See all releases

Code changes from version 1.2.6 to 1.2.7

admin/bsf-analytics/assets/css/minified/style-rtl.min.css ADDED
@@ -0,0 +1 @@
 
1
+ #bsf-optin-notice{padding:1px 12px;border-right-color:#007cba}#bsf-optin-notice .notice-container{padding-top:10px;padding-bottom:12px}#bsf-optin-notice .notice-content{margin:0}#bsf-optin-notice .notice-heading{padding:0 0 12px 20px}#bsf-optin-notice .button-primary{margin-left:5px}
admin/bsf-analytics/assets/css/minified/style.min.css ADDED
@@ -0,0 +1 @@
 
1
+ #bsf-optin-notice{padding:1px 12px;border-left-color:#007cba}#bsf-optin-notice .notice-container{padding-top:10px;padding-bottom:12px}#bsf-optin-notice .notice-content{margin:0}#bsf-optin-notice .notice-heading{padding:0 20px 12px 0}#bsf-optin-notice .button-primary{margin-right:5px}
admin/bsf-analytics/assets/css/unminified/style-rtl.css ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #bsf-optin-notice {
2
+ padding: 1px 12px;
3
+ border-right-color: #007cba;
4
+ }
5
+
6
+ #bsf-optin-notice .notice-container {
7
+ padding-top: 10px;
8
+ padding-bottom: 12px;
9
+ }
10
+
11
+ #bsf-optin-notice .notice-content {
12
+ margin: 0;
13
+ }
14
+
15
+ #bsf-optin-notice .notice-heading {
16
+ padding: 0 0 12px 20px;
17
+ }
18
+
19
+ #bsf-optin-notice .button-primary {
20
+ margin-left: 5px;
21
+ }
admin/bsf-analytics/assets/css/unminified/style.css ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #bsf-optin-notice {
2
+ padding: 1px 12px;
3
+ border-left-color: #007cba;
4
+ }
5
+
6
+ #bsf-optin-notice .notice-container {
7
+ padding-top: 10px;
8
+ padding-bottom: 12px;
9
+ }
10
+
11
+ #bsf-optin-notice .notice-content {
12
+ margin: 0;
13
+ }
14
+
15
+ #bsf-optin-notice .notice-heading {
16
+ padding: 0 20px 12px 0;
17
+ }
18
+
19
+ #bsf-optin-notice .button-primary {
20
+ margin-right: 5px;
21
+ }
admin/bsf-analytics/bin/block-commits-with-merge-conflict.sh ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+
3
+ ## pre-commit script to prevent merge markers from being committed.
4
+
5
+ changed=$(git diff --cached --name-only)
6
+
7
+ if [[ -z "$changed" ]]
8
+ then
9
+ exit 0
10
+ fi
11
+
12
+ echo $changed | xargs egrep '[><]{7}' -H -I --line-number
13
+
14
+ ## If the egrep command has any hits - echo a warning and exit with non-zero status.
15
+ if [ $? == 0 ]
16
+ then
17
+ echo "\n\nWARNING: You have merge markers in the above files, lines. Fix them before committing.\n\n"
18
+ exit 1
19
+ fi
admin/bsf-analytics/class-bsf-analytics-stats.php ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BSF analytics stat class file.
4
+ *
5
+ * @package bsf-analytics
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly.
10
+ }
11
+
12
+ if ( ! class_exists( 'BSF_Analytics_Stats' ) ) {
13
+ /**
14
+ * BSF analytics stat class.
15
+ */
16
+ class BSF_Analytics_Stats {
17
+
18
+ /**
19
+ * Active plugins.
20
+ *
21
+ * Holds the sites active plugins list.
22
+ *
23
+ * @var array
24
+ */
25
+ private $plugins;
26
+
27
+ /**
28
+ * Instance of BSF_Analytics_Stats.
29
+ *
30
+ * Holds only the first object of class.
31
+ *
32
+ * @var object
33
+ */
34
+ private static $instance = null;
35
+
36
+ /**
37
+ * Create only once instance of a class.
38
+ *
39
+ * @return object
40
+ * @since 1.0.0
41
+ */
42
+ public static function instance() {
43
+ if ( null === self::$instance ) {
44
+ self::$instance = new self();
45
+ }
46
+
47
+ return self::$instance;
48
+ }
49
+
50
+ /**
51
+ * Get stats.
52
+ *
53
+ * @return array stats data.
54
+ * @since 1.0.0
55
+ */
56
+ public function get_stats() {
57
+ return apply_filters( 'bsf_core_stats', $this->get_default_stats() );
58
+ }
59
+
60
+ /**
61
+ * Retrieve stats for site.
62
+ *
63
+ * @return array stats data.
64
+ * @since 1.0.0
65
+ */
66
+ private function get_default_stats() {
67
+ return array(
68
+ 'graupi_version' => defined( 'BSF_UPDATER_VERSION' ) ? BSF_UPDATER_VERSION : false,
69
+ 'domain_name' => get_site_url(),
70
+ 'php_os' => PHP_OS,
71
+ 'server_software' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? filter_var( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ), FILTER_SANITIZE_STRING ) : '',
72
+ 'mysql_version' => $this->get_mysql_version(),
73
+ 'php_version' => $this->get_php_version(),
74
+ 'php_max_input_vars' => ini_get( 'max_input_vars' ), // phpcs:ignore:PHPCompatibility.IniDirectives.NewIniDirectives.max_input_varsFound
75
+ 'php_post_max_size' => ini_get( 'post_max_size' ),
76
+ 'php_max_execution_time' => ini_get( 'max_execution_time' ),
77
+ 'php_memory_limit' => ini_get( 'memory_limit' ),
78
+ 'zip_installed' => extension_loaded( 'zip' ),
79
+ 'imagick_availabile' => extension_loaded( 'imagick' ),
80
+ 'xmlreader_exists' => class_exists( 'XMLReader' ),
81
+ 'gd_available' => extension_loaded( 'gd' ),
82
+ 'curl_version' => $this->get_curl_version(),
83
+ 'curl_ssl_version' => $this->get_curl_ssl_version(),
84
+ 'is_writable' => $this->is_content_writable(),
85
+
86
+ 'wp_version' => get_bloginfo( 'version' ),
87
+ 'user_count' => $this->get_user_count(),
88
+ 'site_language' => get_locale(),
89
+ 'timezone' => wp_timezone_string(),
90
+ 'is_ssl' => is_ssl(),
91
+ 'is_multisite' => is_multisite(),
92
+ 'network_url' => network_site_url(),
93
+ 'external_object_cache' => (bool) wp_using_ext_object_cache(),
94
+ 'wp_debug' => WP_DEBUG,
95
+ 'wp_debug_display' => WP_DEBUG_DISPLAY,
96
+ 'script_debug' => SCRIPT_DEBUG,
97
+
98
+ 'active_plugins' => $this->get_active_plugins(),
99
+
100
+ 'active_theme' => get_template(),
101
+ 'active_stylesheet' => get_stylesheet(),
102
+ );
103
+ }
104
+
105
+ /**
106
+ * Get installed PHP version.
107
+ *
108
+ * @return float PHP version.
109
+ * @since 1.0.0
110
+ */
111
+ private function get_php_version() {
112
+ if ( defined( 'PHP_MAJOR_VERSION' ) && defined( 'PHP_MINOR_VERSION' ) && defined( 'PHP_RELEASE_VERSION' ) ) { // phpcs:ignore
113
+ return PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;
114
+ }
115
+
116
+ return phpversion();
117
+ }
118
+
119
+ /**
120
+ * User count on site.
121
+ *
122
+ * @return int User count.
123
+ * @since 1.0.0
124
+ */
125
+ private function get_user_count() {
126
+ if ( is_multisite() ) {
127
+ $user_count = get_user_count();
128
+ } else {
129
+ $count = count_users();
130
+ $user_count = $count['total_users'];
131
+ }
132
+
133
+ return $user_count;
134
+ }
135
+
136
+ /**
137
+ * Get active plugin's data.
138
+ *
139
+ * @return array active plugin's list.
140
+ * @since 1.0.0
141
+ */
142
+ private function get_active_plugins() {
143
+ if ( ! $this->plugins ) {
144
+ // Ensure get_plugin_data function is loaded.
145
+ if ( ! function_exists( 'get_plugin_data' ) ) {
146
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
147
+ }
148
+
149
+ $plugins = wp_get_active_and_valid_plugins();
150
+ $plugins = array_map( 'get_plugin_data', $plugins );
151
+ $this->plugins = array_map( array( $this, 'format_plugin' ), $plugins );
152
+ }
153
+
154
+ return $this->plugins;
155
+ }
156
+
157
+ /**
158
+ * Format plugin data.
159
+ *
160
+ * @param string $plugin plugin.
161
+ * @return array formatted plugin data.
162
+ * @since 1.0.0
163
+ */
164
+ public function format_plugin( $plugin ) {
165
+ return array(
166
+ 'name' => html_entity_decode( $plugin['Name'], ENT_COMPAT, 'UTF-8' ),
167
+ 'url' => $plugin['PluginURI'],
168
+ 'version' => $plugin['Version'],
169
+ 'slug' => $plugin['TextDomain'],
170
+ 'author_name' => html_entity_decode( wp_strip_all_tags( $plugin['Author'] ), ENT_COMPAT, 'UTF-8' ),
171
+ 'author_url' => $plugin['AuthorURI'],
172
+ );
173
+ }
174
+
175
+ /**
176
+ * Curl SSL version.
177
+ *
178
+ * @return float SSL version.
179
+ * @since 1.0.0
180
+ */
181
+ private function get_curl_ssl_version() {
182
+ $curl = array();
183
+ if ( function_exists( 'curl_version' ) ) {
184
+ $curl = curl_version(); // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_version
185
+ }
186
+
187
+ return isset( $curl['ssl_version'] ) ? $curl['ssl_version'] : false;
188
+ }
189
+
190
+ /**
191
+ * Get cURL version.
192
+ *
193
+ * @return float cURL version.
194
+ * @since 1.0.0
195
+ */
196
+ private function get_curl_version() {
197
+ if ( function_exists( 'curl_version' ) ) {
198
+ $curl = curl_version(); // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_version
199
+ }
200
+
201
+ return isset( $curl['version'] ) ? $curl['version'] : false;
202
+ }
203
+
204
+ /**
205
+ * Get MySQL version.
206
+ *
207
+ * @return float MySQL version.
208
+ * @since 1.0.0
209
+ */
210
+ private function get_mysql_version() {
211
+ global $wpdb;
212
+ return $wpdb->db_version();
213
+ }
214
+
215
+ /**
216
+ * Check if content directory is writable.
217
+ *
218
+ * @return bool
219
+ * @since 1.0.0
220
+ */
221
+ private function is_content_writable() {
222
+ $upload_dir = wp_upload_dir();
223
+ return wp_is_writable( $upload_dir['basedir'] );
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Polyfill for sites using WP version less than 5.3
230
+ */
231
+ if ( ! function_exists( 'wp_timezone_string' ) ) {
232
+ /**
233
+ * Get timezone string.
234
+ *
235
+ * @return string timezone string.
236
+ * @since 1.0.0
237
+ */
238
+ function wp_timezone_string() {
239
+ $timezone_string = get_option( 'timezone_string' );
240
+
241
+ if ( $timezone_string ) {
242
+ return $timezone_string;
243
+ }
244
+
245
+ $offset = (float) get_option( 'gmt_offset' );
246
+ $hours = (int) $offset;
247
+ $minutes = ( $offset - $hours );
248
+
249
+ $sign = ( $offset < 0 ) ? '-' : '+';
250
+ $abs_hour = abs( $hours );
251
+ $abs_mins = abs( $minutes * 60 );
252
+ $tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
253
+
254
+ return $tz_offset;
255
+ }
256
+ }
admin/bsf-analytics/class-bsf-analytics.php ADDED
@@ -0,0 +1,528 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BSF analytics class file.
4
+ *
5
+ * @version 1.0.0
6
+ *
7
+ * @package bsf-analytics
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit; // Exit if accessed directly.
12
+ }
13
+
14
+ if ( ! class_exists( 'BSF_Analytics' ) ) {
15
+
16
+ /**
17
+ * BSF analytics
18
+ */
19
+ class BSF_Analytics {
20
+
21
+ /**
22
+ * Member Variable
23
+ *
24
+ * @var string Usage tracking document URL
25
+ */
26
+ private $usage_doc_link = 'https://store.brainstormforce.com/usage-tracking/?utm_source=wp_dashboard&utm_medium=general_settings&utm_campaign=usage_tracking';
27
+
28
+ /**
29
+ * Setup actions, load files.
30
+ *
31
+ * @since 1.0.0
32
+ */
33
+ public function __construct() {
34
+
35
+ define( 'BSF_ANALYTICS_FILE', __FILE__ );
36
+ define( 'BSF_ANALYTICS_VERSION', '1.0.2' );
37
+ define( 'BSF_ANALYTICS_PATH', dirname( __FILE__ ) );
38
+ define( 'BSF_ANALYTICS_URI', $this->bsf_analytics_url() );
39
+
40
+ add_action( 'admin_init', array( $this, 'handle_optin_optout' ) );
41
+ add_action( 'cron_schedules', array( $this, 'every_two_days_schedule' ) );
42
+ add_action( 'admin_notices', array( $this, 'option_notice' ) );
43
+ add_action( 'astra_notice_before_markup_bsf-optin-notice', array( $this, 'enqueue_assets' ) );
44
+
45
+ add_action( 'init', array( $this, 'schedule_unschedule_event' ) );
46
+
47
+ if ( ! has_action( 'bsf_analytics_send', array( $this, 'send' ) ) ) {
48
+ add_action( 'bsf_analytics_send', array( $this, 'send' ) );
49
+ }
50
+
51
+ add_action( 'admin_init', array( $this, 'register_usage_tracking_setting' ) );
52
+
53
+ add_action( 'update_option_bsf_analytics_optin', array( $this, 'update_analytics_option_callback' ), 10, 3 );
54
+ add_action( 'add_option_bsf_analytics_optin', array( $this, 'add_analytics_option_callback' ), 10, 2 );
55
+
56
+ $this->includes();
57
+ }
58
+
59
+ /**
60
+ * BSF Analytics URL
61
+ *
62
+ * @return String URL of bsf-analytics directory.
63
+ * @since 1.0.0
64
+ */
65
+ public function bsf_analytics_url() {
66
+
67
+ $path = wp_normalize_path( BSF_ANALYTICS_PATH );
68
+ $theme_dir = wp_normalize_path( get_template_directory() );
69
+
70
+ if ( strpos( $path, $theme_dir ) !== false ) {
71
+ return rtrim( get_template_directory_uri() . '/admin/bsf-analytics/', '/' );
72
+ } else {
73
+ return rtrim( plugin_dir_url( BSF_ANALYTICS_FILE ), '/' );
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Get API URL for sending analytics.
79
+ *
80
+ * @return string API URL.
81
+ * @since 1.0.0
82
+ */
83
+ private function get_api_url() {
84
+ return defined( 'BSF_API_URL' ) ? BSF_API_URL : 'https://support.brainstormforce.com/';
85
+ }
86
+
87
+ /**
88
+ * Enqueue Scripts.
89
+ *
90
+ * @since 1.0.0
91
+ * @return void
92
+ */
93
+ public function enqueue_assets() {
94
+
95
+ /**
96
+ * Load unminified if SCRIPT_DEBUG is true.
97
+ *
98
+ * Directory and Extensions.
99
+ */
100
+ $dir_name = ( SCRIPT_DEBUG ) ? 'unminified' : 'minified';
101
+ $file_rtl = ( is_rtl() ) ? '-rtl' : '';
102
+ $css_ext = ( SCRIPT_DEBUG ) ? '.css' : '.min.css';
103
+
104
+ $css_uri = BSF_ANALYTICS_URI . '/assets/css/' . $dir_name . '/style' . $file_rtl . $css_ext;
105
+
106
+ wp_enqueue_style( 'bsf-analytics-admin-style', $css_uri, false, BSF_ANALYTICS_VERSION, 'all' );
107
+ }
108
+
109
+ /**
110
+ * Send analytics API call.
111
+ *
112
+ * @since 1.0.0
113
+ */
114
+ public function send() {
115
+ wp_remote_post(
116
+ $this->get_api_url() . 'wp-json/bsf-core/v1/analytics/',
117
+ array(
118
+ 'body' => BSF_Analytics_Stats::instance()->get_stats(),
119
+ 'timeout' => 5,
120
+ 'blocking' => false,
121
+ )
122
+ );
123
+ }
124
+
125
+ /**
126
+ * Check if usage tracking is enabled.
127
+ *
128
+ * @return bool
129
+ * @since 1.0.0
130
+ */
131
+ public function is_tracking_enabled() {
132
+ $is_enabled = get_site_option( 'bsf_analytics_optin' ) === 'yes' ? true : false;
133
+ $is_enabled = $this->is_white_label_enabled() ? false : $is_enabled;
134
+
135
+ return apply_filters( 'bsf_tracking_enabled', $is_enabled );
136
+ }
137
+
138
+ /**
139
+ * Check if WHITE label is enabled for BSF products.
140
+ *
141
+ * @return bool
142
+ * @since 1.0.0
143
+ */
144
+ public function is_white_label_enabled() {
145
+
146
+ $options = apply_filters( 'bsf_white_label_options', array() );
147
+ $is_enabled = false;
148
+
149
+ if ( is_array( $options ) ) {
150
+ foreach ( $options as $option ) {
151
+ if ( true === $option ) {
152
+ $is_enabled = true;
153
+ break;
154
+ }
155
+ }
156
+ }
157
+
158
+ return $is_enabled;
159
+ }
160
+
161
+ /**
162
+ * Display admin notice for usage tracking.
163
+ *
164
+ * @since 1.0.0
165
+ */
166
+ public function option_notice() {
167
+
168
+ if ( ! current_user_can( 'manage_options' ) ) {
169
+ return;
170
+ }
171
+
172
+ // Don't display the notice if tracking is disabled or White Label is enabled for any of our plugins.
173
+ if ( false !== get_site_option( 'bsf_analytics_optin', false ) || $this->is_white_label_enabled() ) {
174
+ return;
175
+ }
176
+
177
+ // Show tracker consent notice after 24 hours from installed time.
178
+ if ( strtotime( '+24 hours', $this->get_analytics_install_time() ) > time() ) {
179
+ return;
180
+ }
181
+
182
+ /* translators: %s product name */
183
+ $notice_string = __( 'Want to help make <strong>%1s</strong> even more awesome? Allow us to collect non-sensitive diagnostic data and usage information. ', 'woo-cart-abandonment-recovery' );
184
+
185
+ if ( is_multisite() ) {
186
+ $notice_string .= __( 'This will be applicable for all sites from the network.', 'woo-cart-abandonment-recovery' );
187
+ }
188
+
189
+ $language_dir = is_rtl() ? 'rtl' : 'ltr';
190
+
191
+ Astra_Notices::add_notice(
192
+ array(
193
+ 'id' => 'bsf-optin-notice',
194
+ 'type' => '',
195
+ 'message' => sprintf(
196
+ '<div class="notice-content">
197
+ <div class="notice-heading">
198
+ %1$s
199
+ </div>
200
+ <div class="astra-notices-container">
201
+ <a href="%2$s" class="astra-notices button-primary">
202
+ %3$s
203
+ </a>
204
+ <a href="%4$s" data-repeat-notice-after="%5$s" class="astra-notices button-secondary">
205
+ %6$s
206
+ </a>
207
+ </div>
208
+ </div>',
209
+ /* translators: %s usage doc link */
210
+ sprintf( $notice_string . '<span dir="%2s"><a href="%3s" target="_blank" rel="noreferrer noopener">%4s</a><span>', esc_html( $this->get_product_name() ), $language_dir, esc_url( $this->usage_doc_link ), __( ' Know More.', 'woo-cart-abandonment-recovery' ) ),
211
+ add_query_arg(
212
+ array(
213
+ 'bsf_analytics_optin' => 'yes',
214
+ 'bsf_analytics_nonce' => wp_create_nonce( 'bsf_analytics_optin' ),
215
+ )
216
+ ),
217
+ __( 'Yes! Allow it', 'woo-cart-abandonment-recovery' ),
218
+ add_query_arg(
219
+ array(
220
+ 'bsf_analytics_optin' => 'no',
221
+ 'bsf_analytics_nonce' => wp_create_nonce( 'bsf_analytics_optin' ),
222
+ )
223
+ ),
224
+ MONTH_IN_SECONDS,
225
+ __( 'No Thanks', 'woo-cart-abandonment-recovery' )
226
+ ),
227
+ 'show_if' => true,
228
+ 'repeat-notice-after' => false,
229
+ 'priority' => 18,
230
+ 'display-with-other-notices' => true,
231
+ )
232
+ );
233
+ }
234
+
235
+ /**
236
+ * Process usage tracking opt out.
237
+ *
238
+ * @since 1.0.0
239
+ */
240
+ public function handle_optin_optout() {
241
+ if ( ! isset( $_GET['bsf_analytics_nonce'] ) ) {
242
+ return;
243
+ }
244
+
245
+ if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['bsf_analytics_nonce'] ) ), 'bsf_analytics_optin' ) ) {
246
+ return;
247
+ }
248
+
249
+ $optin_status = isset( $_GET['bsf_analytics_optin'] ) ? sanitize_text_field( wp_unslash( $_GET['bsf_analytics_optin'] ) ) : '';
250
+
251
+ if ( 'yes' === $optin_status ) {
252
+ $this->optin();
253
+ } elseif ( 'no' === $optin_status ) {
254
+ $this->optout();
255
+ }
256
+
257
+ wp_safe_redirect(
258
+ remove_query_arg(
259
+ array(
260
+ 'bsf_analytics_optin',
261
+ 'bsf_analytics_nonce',
262
+ )
263
+ )
264
+ );
265
+ }
266
+
267
+ /**
268
+ * Opt in to usage tracking.
269
+ *
270
+ * @since 1.0.0
271
+ */
272
+ private function optin() {
273
+ update_site_option( 'bsf_analytics_optin', 'yes' );
274
+ }
275
+
276
+ /**
277
+ * Opt out to usage tracking.
278
+ *
279
+ * @since 1.0.0
280
+ */
281
+ private function optout() {
282
+ update_site_option( 'bsf_analytics_optin', 'no' );
283
+ }
284
+
285
+ /**
286
+ * Add two days event schedule variables.
287
+ *
288
+ * @param array $schedules scheduled array data.
289
+ * @since 1.0.0
290
+ */
291
+ public function every_two_days_schedule( $schedules ) {
292
+ $schedules['every_two_days'] = array(
293
+ 'interval' => 2 * DAY_IN_SECONDS,
294
+ 'display' => __( 'Every two days', 'woo-cart-abandonment-recovery' ),
295
+ );
296
+
297
+ return $schedules;
298
+ }
299
+
300
+ /**
301
+ * Schedule usage tracking event.
302
+ *
303
+ * @since 1.0.0
304
+ */
305
+ private function schedule_event() {
306
+ if ( ! wp_next_scheduled( 'bsf_analytics_send' ) && $this->is_tracking_enabled() ) {
307
+ wp_schedule_event( time(), 'every_two_days', 'bsf_analytics_send' );
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Unschedule usage tracking event.
313
+ *
314
+ * @since 1.0.0
315
+ */
316
+ private function unschedule_event() {
317
+ wp_clear_scheduled_hook( 'bsf_analytics_send' );
318
+ }
319
+
320
+ /**
321
+ * Load analytics stat class.
322
+ *
323
+ * @since 1.0.0
324
+ */
325
+ private function includes() {
326
+ require_once __DIR__ . '/class-bsf-analytics-stats.php';
327
+ }
328
+
329
+ /**
330
+ * Register usage tracking option in General settings page.
331
+ *
332
+ * @since 1.0.0
333
+ */
334
+ public function register_usage_tracking_setting() {
335
+
336
+ if ( ! apply_filters( 'bsf_tracking_enabled', true ) || $this->is_white_label_enabled() ) {
337
+ return;
338
+ }
339
+
340
+ register_setting(
341
+ 'general', // Options group.
342
+ 'bsf_analytics_optin', // Option name/database.
343
+ array( 'sanitize_callback' => array( $this, 'sanitize_option' ) ) // sanitize callback function.
344
+ );
345
+
346
+ add_settings_field(
347
+ 'bsf-analytics-optin', // Field ID.
348
+ __( 'Usage Tracking', 'woo-cart-abandonment-recovery' ), // Field title.
349
+ array( $this, 'render_settings_field_html' ), // Field callback function.
350
+ 'general' // Settings page slug.
351
+ );
352
+ }
353
+
354
+ /**
355
+ * Sanitize Callback Function
356
+ *
357
+ * @param bool $input Option value.
358
+ * @since 1.0.0
359
+ */
360
+ public function sanitize_option( $input ) {
361
+
362
+ if ( ! $input || 'no' === $input ) {
363
+ return 'no';
364
+ }
365
+
366
+ return 'yes';
367
+ }
368
+
369
+ /**
370
+ * Print settings field HTML.
371
+ *
372
+ * @since 1.0.0
373
+ */
374
+ public function render_settings_field_html() {
375
+ ?>
376
+ <fieldset>
377
+ <label for="bsf-analytics-optin">
378
+ <input id="bsf-analytics-optin" type="checkbox" value="1" name="bsf_analytics_optin" <?php checked( get_site_option( 'bsf_analytics_optin', 'no' ), 'yes' ); ?>>
379
+ <?php
380
+ esc_html_e( 'Allow Brainstorm Force products to track non-sensitive usage tracking data.', 'woo-cart-abandonment-recovery' );
381
+
382
+ if ( is_multisite() ) {
383
+ esc_html_e( ' This will be applicable for all sites from the network.', 'woo-cart-abandonment-recovery' );
384
+ }
385
+ ?>
386
+ </label>
387
+ <?php
388
+ echo wp_kses_post( sprintf( '<a href="%1s" target="_blank" rel="noreferrer noopener">%2s</a>', esc_url( $this->usage_doc_link ), __( 'Learn More.', 'woo-cart-abandonment-recovery' ) ) );
389
+ ?>
390
+ </fieldset>
391
+ <?php
392
+ }
393
+
394
+ /**
395
+ * Get current product name.
396
+ *
397
+ * @return string $plugin_data['Name] Name of plugin.
398
+ * @since 1.0.0
399
+ */
400
+ private function get_product_name() {
401
+
402
+ $base = wp_normalize_path( dirname( __FILE__ ) );
403
+ $theme_dir = wp_normalize_path( get_template_directory() );
404
+
405
+ if ( false !== strpos( $base, $theme_dir ) ) {
406
+ $theme = wp_get_theme( get_template() );
407
+ return $theme->get( 'Name' );
408
+ }
409
+
410
+ $base = plugin_basename( __FILE__ );
411
+
412
+ $exploded_path = explode( '/', $base, 2 );
413
+ $plugin_slug = $exploded_path[0];
414
+
415
+ return $this->get_plugin_name( $plugin_slug );
416
+ }
417
+
418
+ /**
419
+ * Get plugin name by plugin slug.
420
+ *
421
+ * @param string $plugin_slug Plugin slug.
422
+ * @return string $plugin_info['Name'] Plugin name.
423
+ */
424
+ private function get_plugin_name( $plugin_slug ) {
425
+
426
+ $plugins = get_option( 'active_plugins' );
427
+
428
+ if ( ! function_exists( 'get_plugin_data' ) ) {
429
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
430
+ }
431
+
432
+ foreach ( $plugins as $plugin_file ) {
433
+
434
+ $plugin_folder = explode( "/", $plugin_file );
435
+ $plugin_folder = $plugin_folder[0];
436
+ if ( $plugin_folder === $plugin_slug ) {
437
+ $plugin_path = WP_PLUGIN_DIR . '/' . $plugin_file;
438
+ $plugin_data = get_plugin_data( $plugin_path );
439
+ return $plugin_data['Name'];
440
+ }
441
+ }
442
+ }
443
+
444
+ /**
445
+ * Set analytics installed time in option.
446
+ *
447
+ * @return string $time analytics installed time.
448
+ * @since 1.0.0
449
+ */
450
+ private function get_analytics_install_time() {
451
+
452
+ $time = get_site_option( 'bsf_analytics_installed_time' );
453
+
454
+ if ( ! $time ) {
455
+ $time = time();
456
+ update_site_option( 'bsf_analytics_installed_time', time() );
457
+ }
458
+
459
+ return $time;
460
+ }
461
+
462
+ /**
463
+ * Schedule/unschedule cron event on updation of option.
464
+ *
465
+ * @param string $old_value old value of option.
466
+ * @param string $value value of option.
467
+ * @param string $option Option name.
468
+ * @since 1.0.0
469
+ */
470
+ public function update_analytics_option_callback( $old_value, $value, $option ) {
471
+ $this->add_option_to_network( $value );
472
+ }
473
+
474
+ /**
475
+ * Analytics option add callback.
476
+ *
477
+ * @param string $option Option name.
478
+ * @param string $value value of option.
479
+ * @since 1.0.0
480
+ */
481
+ public function add_analytics_option_callback( $option, $value ) {
482
+ $this->add_option_to_network( $value );
483
+ }
484
+
485
+ /**
486
+ * Schedule or unschedule event based on analytics option value.
487
+ *
488
+ * @since 1.0.0
489
+ */
490
+ public function schedule_unschedule_event() {
491
+
492
+ if ( true === $this->is_white_label_enabled() ) {
493
+ $this->unschedule_event();
494
+ return;
495
+ }
496
+
497
+ $analytics_option = get_site_option( 'bsf_analytics_optin' );
498
+
499
+ if ( 'no' === $analytics_option ) {
500
+ $this->unschedule_event();
501
+ } elseif ( 'yes' === $analytics_option ) {
502
+ $this->schedule_event();
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Save analytics option to network.
508
+ *
509
+ * @param string $value value of option.
510
+ * @since 1.0.0
511
+ */
512
+ public function add_option_to_network( $value ) {
513
+
514
+ // If action coming from general settings page.
515
+ if ( isset( $_POST['option_page'] ) && 'general' === $_POST['option_page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
516
+
517
+ if ( get_site_option( 'bsf_analytics_optin' ) ) {
518
+ update_site_option( 'bsf_analytics_optin', $value );
519
+ } else {
520
+ add_site_option( 'bsf_analytics_optin', $value );
521
+ }
522
+ }
523
+ }
524
+ }
525
+
526
+ new BSF_Analytics();
527
+
528
+ }
changelog.txt CHANGED
@@ -1,3 +1,7 @@
 
 
 
 
1
  Version 1.2.6 - Thursday, 21st May 2020
2
  - New: Added option to send the email to admin after successfully cart recovery of the abandoned order.
3
  - Fix: Email rescheduling was considering the cart abandoned time rather than the current time.
1
+ Version 1.2.7 - Tuesday, 16th June 2020
2
+ - New: Users can now share [non-personal usage data] to help us test and develop better products.
3
+ ( https://store.brainstormforce.com/usage-tracking/?utm_source=wp_repo&utm_medium=changelog&utm_campaign=usage_tracking )
4
+
5
  Version 1.2.6 - Thursday, 21st May 2020
6
  - New: Added option to send the email to admin after successfully cart recovery of the abandoned order.
7
  - Fix: Email rescheduling was considering the cart abandoned time rather than the current time.
classes/class-cartflows-ca-loader.php CHANGED
@@ -78,7 +78,7 @@ if ( ! class_exists( 'CARTFLOWS_CA_Loader' ) ) {
78
  define( 'CARTFLOWS_CA_BASE', plugin_basename( CARTFLOWS_CA_FILE ) );
79
  define( 'CARTFLOWS_CA_DIR', plugin_dir_path( CARTFLOWS_CA_FILE ) );
80
  define( 'CARTFLOWS_CA_URL', plugins_url( '/', CARTFLOWS_CA_FILE ) );
81
- define( 'CARTFLOWS_CA_VER', '1.2.6' );
82
  define( 'CARTFLOWS_CA_SLUG', 'cartflows_ca' );
83
 
84
  define( 'CARTFLOWS_CA_CART_ABANDONMENT_TABLE', 'cartflows_ca_cart_abandonment' );
@@ -210,6 +210,12 @@ if ( ! class_exists( 'CARTFLOWS_CA_Loader' ) ) {
210
  require_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-update.php';
211
 
212
  include_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-settings.php';
 
 
 
 
 
 
213
  }
214
 
215
  /**
78
  define( 'CARTFLOWS_CA_BASE', plugin_basename( CARTFLOWS_CA_FILE ) );
79
  define( 'CARTFLOWS_CA_DIR', plugin_dir_path( CARTFLOWS_CA_FILE ) );
80
  define( 'CARTFLOWS_CA_URL', plugins_url( '/', CARTFLOWS_CA_FILE ) );
81
+ define( 'CARTFLOWS_CA_VER', '1.2.7' );
82
  define( 'CARTFLOWS_CA_SLUG', 'cartflows_ca' );
83
 
84
  define( 'CARTFLOWS_CA_CART_ABANDONMENT_TABLE', 'cartflows_ca_cart_abandonment' );
210
  require_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-update.php';
211
 
212
  include_once CARTFLOWS_CA_DIR . 'classes/class-cartflows-ca-settings.php';
213
+
214
+ if ( is_admin() ) {
215
+ require_once CARTFLOWS_CA_DIR . 'lib/notices/class-astra-notices.php';
216
+ }
217
+
218
+ require_once CARTFLOWS_CA_DIR . 'admin/bsf-analytics/class-bsf-analytics.php';
219
  }
220
 
221
  /**
languages/woo-cart-abandonment-recovery.pot CHANGED
@@ -2,10 +2,10 @@
2
  # This file is distributed under the same license as the WooCommerce Cart Abandonment Recovery package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: WooCommerce Cart Abandonment Recovery 1.2.6\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://wordpress.org/support/plugin/woo-cart-abandonment-recovery\n"
8
- "POT-Creation-Date: 2020-05-21 10:08:45+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
@@ -25,6 +25,51 @@ msgstr ""
25
  "X-Textdomain-Support: yes\n"
26
  "X-Generator: grunt-wp-i18n 1.0.3\n"
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  #: classes/class-cartflows-ca-loader.php:135
29
  #. translators: %s: html tags
30
  msgid ""
@@ -231,6 +276,10 @@ msgstr ""
231
  msgid "Invalid email \"Reply\" address field"
232
  msgstr ""
233
 
 
 
 
 
234
  #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:81
235
  msgid "View"
236
  msgstr ""
@@ -297,10 +346,6 @@ msgid ""
297
  "plugin from test mail."
298
  msgstr ""
299
 
300
- #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:536
301
- msgid "No Thanks"
302
- msgstr ""
303
-
304
  #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:537
305
  msgid "You won't receive further emails from us, thank you!"
306
  msgstr ""
2
  # This file is distributed under the same license as the WooCommerce Cart Abandonment Recovery package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: WooCommerce Cart Abandonment Recovery 1.2.7\n"
6
  "Report-Msgid-Bugs-To: "
7
  "https://wordpress.org/support/plugin/woo-cart-abandonment-recovery\n"
8
+ "POT-Creation-Date: 2020-06-16 08:57:12+00:00\n"
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=utf-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
25
  "X-Textdomain-Support: yes\n"
26
  "X-Generator: grunt-wp-i18n 1.0.3\n"
27
 
28
+ #: admin/bsf-analytics/class-bsf-analytics.php:183
29
+ #. translators: %s product name
30
+ msgid ""
31
+ "Want to help make <strong>%1s</strong> even more awesome? Allow us to "
32
+ "collect non-sensitive diagnostic data and usage information. "
33
+ msgstr ""
34
+
35
+ #: admin/bsf-analytics/class-bsf-analytics.php:186
36
+ msgid "This will be applicable for all sites from the network."
37
+ msgstr ""
38
+
39
+ #: admin/bsf-analytics/class-bsf-analytics.php:210
40
+ #. translators: %s usage doc link
41
+ msgid " Know More."
42
+ msgstr ""
43
+
44
+ #: admin/bsf-analytics/class-bsf-analytics.php:217
45
+ msgid "Yes! Allow it"
46
+ msgstr ""
47
+
48
+ #: admin/bsf-analytics/class-bsf-analytics.php:225
49
+ #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:536
50
+ msgid "No Thanks"
51
+ msgstr ""
52
+
53
+ #: admin/bsf-analytics/class-bsf-analytics.php:294
54
+ msgid "Every two days"
55
+ msgstr ""
56
+
57
+ #: admin/bsf-analytics/class-bsf-analytics.php:348
58
+ msgid "Usage Tracking"
59
+ msgstr ""
60
+
61
+ #: admin/bsf-analytics/class-bsf-analytics.php:380
62
+ msgid "Allow Brainstorm Force products to track non-sensitive usage tracking data."
63
+ msgstr ""
64
+
65
+ #: admin/bsf-analytics/class-bsf-analytics.php:383
66
+ msgid " This will be applicable for all sites from the network."
67
+ msgstr ""
68
+
69
+ #: admin/bsf-analytics/class-bsf-analytics.php:388
70
+ msgid "Learn More."
71
+ msgstr ""
72
+
73
  #: classes/class-cartflows-ca-loader.php:135
74
  #. translators: %s: html tags
75
  msgid ""
276
  msgid "Invalid email \"Reply\" address field"
277
  msgstr ""
278
 
279
+ #: lib/notices/class-astra-notices.php:120
280
+ msgid "WordPress Nonce not validated."
281
+ msgstr ""
282
+
283
  #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment-table.php:81
284
  msgid "View"
285
  msgstr ""
346
  "plugin from test mail."
347
  msgstr ""
348
 
 
 
 
 
349
  #: modules/cart-abandonment/class-cartflows-ca-cart-abandonment.php:537
350
  msgid "You won't receive further emails from us, thank you!"
351
  msgstr ""
lib/notices/class-astra-notices.php ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Astra Sites Notices
4
+ *
5
+ * Closing notice on click on `astra-notice-close` class.
6
+ *
7
+ * If notice has the data attribute `data-repeat-notice-after="%2$s"` then notice close for that SPECIFIC TIME.
8
+ * If notice has NO data attribute `data-repeat-notice-after="%2$s"` then notice close for the CURRENT USER FOREVER.
9
+ *
10
+ * > Create custom close notice link in the notice markup. E.g.
11
+ * `<a href="#" data-repeat-notice-after="<?php echo MONTH_IN_SECONDS; ?>" class="astra-notice-close">`
12
+ * It close the notice for 30 days.
13
+ *
14
+ * @package Astra Sites
15
+ * @since 1.4.0
16
+ */
17
+
18
+ if ( ! defined( 'ABSPATH' ) ) {
19
+ exit; // Exit if accessed directly.
20
+ }
21
+
22
+ if ( ! class_exists( 'Astra_Notices' ) ) :
23
+
24
+ /**
25
+ * Astra_Notices
26
+ *
27
+ * @since 1.4.0
28
+ */
29
+ class Astra_Notices {
30
+
31
+ /**
32
+ * Notices
33
+ *
34
+ * @access private
35
+ * @var array Notices.
36
+ * @since 1.4.0
37
+ */
38
+ private static $version = '1.1.5';
39
+
40
+ /**
41
+ * Notices
42
+ *
43
+ * @access private
44
+ * @var array Notices.
45
+ * @since 1.4.0
46
+ */
47
+ private static $notices = array();
48
+
49
+ /**
50
+ * Instance
51
+ *
52
+ * @access private
53
+ * @var object Class object.
54
+ * @since 1.4.0
55
+ */
56
+ private static $instance;
57
+
58
+ /**
59
+ * Initiator
60
+ *
61
+ * @since 1.4.0
62
+ * @return object initialized object of class.
63
+ */
64
+ public static function get_instance() {
65
+ if ( ! isset( self::$instance ) ) {
66
+ self::$instance = new self();
67
+ }
68
+ return self::$instance;
69
+ }
70
+
71
+ /**
72
+ * Constructor
73
+ *
74
+ * @since 1.4.0
75
+ */
76
+ public function __construct() {
77
+ add_action( 'admin_notices', array( $this, 'show_notices' ), 30 );
78
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
79
+ add_action( 'wp_ajax_astra-notice-dismiss', array( $this, 'dismiss_notice' ) );
80
+ add_filter( 'wp_kses_allowed_html', array( $this, 'add_data_attributes' ), 10, 2 );
81
+ }
82
+
83
+ /**
84
+ * Filters and Returns a list of allowed tags and attributes for a given context.
85
+ *
86
+ * @param Array $allowedposttags Array of allowed tags.
87
+ * @param String $context Context type (explicit).
88
+ * @since 1.4.0
89
+ * @return Array
90
+ */
91
+ public function add_data_attributes( $allowedposttags, $context ) {
92
+ $allowedposttags['a']['data-repeat-notice-after'] = true;
93
+
94
+ return $allowedposttags;
95
+ }
96
+
97
+ /**
98
+ * Add Notice.
99
+ *
100
+ * @since 1.4.0
101
+ * @param array $args Notice arguments.
102
+ * @return void
103
+ */
104
+ public static function add_notice( $args = array() ) {
105
+ self::$notices[] = $args;
106
+ }
107
+
108
+ /**
109
+ * Dismiss Notice.
110
+ *
111
+ * @since 1.4.0
112
+ * @return void
113
+ */
114
+ public function dismiss_notice() {
115
+ $notice_id = ( isset( $_POST['notice_id'] ) ) ? sanitize_key( $_POST['notice_id'] ) : '';
116
+ $repeat_notice_after = ( isset( $_POST['repeat_notice_after'] ) ) ? absint( $_POST['repeat_notice_after'] ) : '';
117
+ $nonce = ( isset( $_POST['nonce'] ) ) ? sanitize_key( $_POST['nonce'] ) : '';
118
+
119
+ if ( false === wp_verify_nonce( $nonce, 'astra-notices' ) ) {
120
+ wp_send_json_error( esc_html_e( 'WordPress Nonce not validated.', 'woo-cart-abandonment-recovery' ) );
121
+ }
122
+
123
+ // Valid inputs?
124
+ if ( ! empty( $notice_id ) ) {
125
+
126
+ if ( ! empty( $repeat_notice_after ) ) {
127
+ set_transient( $notice_id, true, $repeat_notice_after );
128
+ } else {
129
+ update_user_meta( get_current_user_id(), $notice_id, 'notice-dismissed' );
130
+ }
131
+
132
+ wp_send_json_success();
133
+ }
134
+
135
+ wp_send_json_error();
136
+ }
137
+
138
+ /**
139
+ * Enqueue Scripts.
140
+ *
141
+ * @since 1.4.0
142
+ * @return void
143
+ */
144
+ public function enqueue_scripts() {
145
+ wp_register_script( 'astra-notices', self::_get_uri() . 'notices.js', array( 'jquery' ), self::$version, true );
146
+ wp_localize_script(
147
+ 'astra-notices',
148
+ 'astraNotices',
149
+ array(
150
+ '_notice_nonce' => wp_create_nonce( 'astra-notices' ),
151
+ )
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Rating priority sort
157
+ *
158
+ * @since 1.5.2
159
+ * @param array $array1 array one.
160
+ * @param array $array2 array two.
161
+ * @return array
162
+ */
163
+ public function sort_notices( $array1, $array2 ) {
164
+ if ( ! isset( $array1['priority'] ) ) {
165
+ $array1['priority'] = 10;
166
+ }
167
+ if ( ! isset( $array2['priority'] ) ) {
168
+ $array2['priority'] = 10;
169
+ }
170
+
171
+ return $array1['priority'] - $array2['priority'];
172
+ }
173
+
174
+ /**
175
+ * Notice Types
176
+ *
177
+ * @since 1.4.0
178
+ * @return void
179
+ */
180
+ public function show_notices() {
181
+
182
+ $defaults = array(
183
+ 'id' => '', // Optional, Notice ID. If empty it set `astra-notices-id-<$array-index>`.
184
+ 'type' => 'info', // Optional, Notice type. Default `info`. Expected [info, warning, notice, error].
185
+ 'message' => '', // Optional, Message.
186
+ 'show_if' => true, // Optional, Show notice on custom condition. E.g. 'show_if' => if( is_admin() ) ? true, false, .
187
+ 'repeat-notice-after' => '', // Optional, Dismiss-able notice time. It'll auto show after given time.
188
+ 'display-notice-after' => false, // Optional, Dismiss-able notice time. It'll auto show after given time.
189
+ 'class' => '', // Optional, Additional notice wrapper class.
190
+ 'priority' => 10, // Priority of the notice.
191
+ 'display-with-other-notices' => true, // Should the notice be displayed if other notices are being displayed from Astra_Notices.
192
+ 'is_dismissible' => true,
193
+ );
194
+
195
+ // Count for the notices that are rendered.
196
+ $notices_displayed = 0;
197
+
198
+ // sort the array with priority.
199
+ usort( self::$notices, array( $this, 'sort_notices' ) );
200
+
201
+ foreach ( self::$notices as $key => $notice ) {
202
+
203
+ $notice = wp_parse_args( $notice, $defaults );
204
+
205
+ $notice['id'] = self::get_notice_id( $notice, $key );
206
+
207
+ $notice['classes'] = self::get_wrap_classes( $notice );
208
+
209
+ // Notices visible after transient expire.
210
+ if ( isset( $notice['show_if'] ) && true === $notice['show_if'] ) {
211
+
212
+ // don't display the notice if it is not supposed to be displayed with other notices.
213
+ if ( 0 !== $notices_displayed && false === $notice['display-with-other-notices'] ) {
214
+ continue;
215
+ }
216
+
217
+ if ( self::is_expired( $notice ) ) {
218
+
219
+ self::markup( $notice );
220
+ ++$notices_displayed;
221
+ }
222
+ }
223
+ }
224
+
225
+ }
226
+
227
+ /**
228
+ * Markup Notice.
229
+ *
230
+ * @since 1.4.0
231
+ * @param array $notice Notice markup.
232
+ * @return void
233
+ */
234
+ public static function markup( $notice = array() ) {
235
+
236
+ wp_enqueue_script( 'astra-notices' );
237
+
238
+ do_action( 'astra_notice_before_markup' );
239
+
240
+ do_action( "astra_notice_before_markup_{$notice['id']}" );
241
+
242
+ ?>
243
+ <div id="<?php echo esc_attr( $notice['id'] ); ?>" class="<?php echo esc_attr( $notice['classes'] ); ?>" data-repeat-notice-after="<?php echo esc_attr( $notice['repeat-notice-after'] ); ?>">
244
+ <div class="notice-container">
245
+ <?php do_action( "astra_notice_inside_markup_{$notice['id']}" ); ?>
246
+ <?php echo wp_kses_post( $notice['message'] ); ?>
247
+ </div>
248
+ </div>
249
+ <?php
250
+
251
+ do_action( "astra_notice_after_markup_{$notice['id']}" );
252
+
253
+ do_action( 'astra_notice_after_markup' );
254
+
255
+ }
256
+
257
+ /**
258
+ * Notice classes.
259
+ *
260
+ * @since 1.4.0
261
+ *
262
+ * @param array $notice Notice arguments.
263
+ * @return array Notice wrapper classes.
264
+ */
265
+ private static function get_wrap_classes( $notice ) {
266
+ $classes = array( 'astra-notice', 'notice' );
267
+
268
+ if ( $notice['is_dismissible'] ) {
269
+ $classes[] = 'is-dismissible';
270
+ }
271
+
272
+ $classes[] = $notice['class'];
273
+ if ( isset( $notice['type'] ) && '' !== $notice['type'] ) {
274
+ $classes[] = 'notice-' . $notice['type'];
275
+ }
276
+
277
+ return esc_attr( implode( ' ', $classes ) );
278
+ }
279
+
280
+ /**
281
+ * Get Notice ID.
282
+ *
283
+ * @since 1.4.0
284
+ *
285
+ * @param array $notice Notice arguments.
286
+ * @param int $key Notice array index.
287
+ * @return string Notice id.
288
+ */
289
+ private static function get_notice_id( $notice, $key ) {
290
+ if ( isset( $notice['id'] ) && ! empty( $notice['id'] ) ) {
291
+ return $notice['id'];
292
+ }
293
+
294
+ return 'astra-notices-id-' . $key;
295
+ }
296
+
297
+ /**
298
+ * Is notice expired?
299
+ *
300
+ * @since 1.4.0
301
+ *
302
+ * @param array $notice Notice arguments.
303
+ * @return boolean
304
+ */
305
+ private static function is_expired( $notice ) {
306
+ $transient_status = get_transient( $notice['id'] );
307
+
308
+ if ( false === $transient_status ) {
309
+
310
+ if ( isset( $notice['display-notice-after'] ) && false !== $notice['display-notice-after'] ) {
311
+
312
+ if ( 'delayed-notice' !== get_user_meta( get_current_user_id(), $notice['id'], true ) &&
313
+ 'notice-dismissed' !== get_user_meta( get_current_user_id(), $notice['id'], true ) ) {
314
+ set_transient( $notice['id'], 'delayed-notice', $notice['display-notice-after'] );
315
+ update_user_meta( get_current_user_id(), $notice['id'], 'delayed-notice' );
316
+
317
+ return false;
318
+ }
319
+ }
320
+
321
+ // Check the user meta status if current notice is dismissed or delay completed.
322
+ $meta_status = get_user_meta( get_current_user_id(), $notice['id'], true );
323
+
324
+ if ( empty( $meta_status ) || 'delayed-notice' === $meta_status ) {
325
+ return true;
326
+ }
327
+ }
328
+
329
+ return false;
330
+ }
331
+
332
+ /**
333
+ * Get URI
334
+ *
335
+ * @return mixed URL.
336
+ */
337
+ public static function _get_uri() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
338
+ $path = wp_normalize_path( dirname( __FILE__ ) );
339
+ $theme_dir = wp_normalize_path( get_template_directory() );
340
+ $plugin_dir = wp_normalize_path( WP_PLUGIN_DIR );
341
+
342
+ if ( strpos( $path, $theme_dir ) !== false ) {
343
+ return trailingslashit( get_template_directory_uri() . str_replace( $theme_dir, '', $path ) );
344
+ } elseif ( strpos( $path, $plugin_dir ) !== false ) {
345
+ return plugin_dir_url( __FILE__ );
346
+ } elseif ( strpos( $path, dirname( plugin_basename( __FILE__ ) ) ) !== false ) {
347
+ return plugin_dir_url( __FILE__ );
348
+ }
349
+
350
+ return; // phpcs:ignore Squiz.PHP.NonExecutableCode.ReturnNotRequired
351
+ }
352
+
353
+ }
354
+
355
+ /**
356
+ * Kicking this off by calling 'get_instance()' method
357
+ */
358
+ Astra_Notices::get_instance();
359
+
360
+ endif;
lib/notices/notices.js ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Customizer controls toggles
3
+ *
4
+ * @package Astra
5
+ */
6
+
7
+ ( function( $ ) {
8
+
9
+ /**
10
+ * Helper class for the main Customizer interface.
11
+ *
12
+ * @since 1.0.0
13
+ * @class ASTCustomizer
14
+ */
15
+ AstraNotices = {
16
+
17
+ /**
18
+ * Initializes our custom logic for the Customizer.
19
+ *
20
+ * @since 1.0.0
21
+ * @method init
22
+ */
23
+ init: function()
24
+ {
25
+ this._bind();
26
+ },
27
+
28
+ /**
29
+ * Binds events for the Astra Portfolio.
30
+ *
31
+ * @since 1.0.0
32
+ * @access private
33
+ * @method _bind
34
+ */
35
+ _bind: function()
36
+ {
37
+ $( document ).on('click', '.astra-notice-close', AstraNotices._dismissNoticeNew );
38
+ $( document ).on('click', '.astra-notice .notice-dismiss', AstraNotices._dismissNotice );
39
+ },
40
+
41
+ _dismissNotice: function( event ) {
42
+ event.preventDefault();
43
+
44
+ var repeat_notice_after = $( this ).parents('.astra-notice').data( 'repeat-notice-after' ) || '';
45
+ var notice_id = $( this ).parents('.astra-notice').attr( 'id' ) || '';
46
+
47
+ AstraNotices._ajax( notice_id, repeat_notice_after );
48
+ },
49
+
50
+ _dismissNoticeNew: function( event ) {
51
+ event.preventDefault();
52
+
53
+ var repeat_notice_after = $( this ).attr( 'data-repeat-notice-after' ) || '';
54
+ var notice_id = $( this ).parents('.astra-notice').attr( 'id' ) || '';
55
+
56
+ var $el = $( this ).parents('.astra-notice');
57
+ $el.fadeTo( 100, 0, function() {
58
+ $el.slideUp( 100, function() {
59
+ $el.remove();
60
+ });
61
+ });
62
+
63
+ AstraNotices._ajax( notice_id, repeat_notice_after );
64
+
65
+ var link = $( this ).attr( 'href' ) || '';
66
+ var target = $( this ).attr( 'target' ) || '';
67
+ if( '' !== link && '_blank' === target ) {
68
+ window.open(link , '_blank');
69
+ }
70
+ },
71
+
72
+ _ajax: function( notice_id, repeat_notice_after ) {
73
+
74
+ if( '' === notice_id ) {
75
+ return;
76
+ }
77
+
78
+ $.ajax({
79
+ url: ajaxurl,
80
+ type: 'POST',
81
+ data: {
82
+ action : 'astra-notice-dismiss',
83
+ nonce : astraNotices._notice_nonce,
84
+ notice_id : notice_id,
85
+ repeat_notice_after : parseInt( repeat_notice_after ),
86
+ },
87
+ });
88
+
89
+ }
90
+ };
91
+
92
+ $( function() {
93
+ AstraNotices.init();
94
+ } );
95
+ } )( jQuery );
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: brainstormforce, wpcrafter
3
  Donate link: https://www.paypal.me/BrainstormForce
4
  Tags: woocommerce, cart abandonment, cart recovery
5
  Requires at least: 4.4
6
- Tested up to: 5.4
7
- Stable tag: 1.2.6
8
  Requires PHP: 5.6
9
  License: GPLv2 or later
10
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -129,6 +129,10 @@ Here are few thoughts behind making it available for free:
129
 
130
  == Changelog ==
131
 
 
 
 
 
132
  = Version 1.2.6 - Thursday, 21st May 2020 =
133
  * New: Added option to send the email to admin after successfully cart recovery of the abandoned order.
134
  * Fix: Email rescheduling was considering the cart abandoned time rather than the current time.
3
  Donate link: https://www.paypal.me/BrainstormForce
4
  Tags: woocommerce, cart abandonment, cart recovery
5
  Requires at least: 4.4
6
+ Tested up to: 5.4.2
7
+ Stable tag: 1.2.7
8
  Requires PHP: 5.6
9
  License: GPLv2 or later
10
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
129
 
130
  == Changelog ==
131
 
132
+ = Version 1.2.7 - Tuesday, 16th June 2020 =
133
+ * New: Users can now share [non-personal usage data] to help us test and develop better products.
134
+ ( https://store.brainstormforce.com/usage-tracking/?utm_source=wp_repo&utm_medium=changelog&utm_campaign=usage_tracking )
135
+
136
  = Version 1.2.6 - Thursday, 21st May 2020 =
137
  * New: Added option to send the email to admin after successfully cart recovery of the abandoned order.
138
  * Fix: Email rescheduling was considering the cart abandoned time rather than the current time.
woo-cart-abandonment-recovery.php CHANGED
@@ -3,12 +3,12 @@
3
  * Plugin Name: WooCommerce Cart Abandonment Recovery
4
  * Plugin URI: https://cartflows.com/
5
  * Description: Recover your lost revenue. Capture email address of users on the checkout page and send follow up emails if they don't complete the purchase.
6
- * Version: 1.2.6
7
  * Author: CartFlows Inc
8
  * Author URI: https://cartflows.com/
9
  * Text Domain: woo-cart-abandonment-recovery
10
  * WC requires at least: 3.0
11
- * WC tested up to: 4.1.1
12
  *
13
  * @package Woocommerce-Cart-Abandonment-Recovery
14
  */
3
  * Plugin Name: WooCommerce Cart Abandonment Recovery
4
  * Plugin URI: https://cartflows.com/
5
  * Description: Recover your lost revenue. Capture email address of users on the checkout page and send follow up emails if they don't complete the purchase.
6
+ * Version: 1.2.7
7
  * Author: CartFlows Inc
8
  * Author URI: https://cartflows.com/
9
  * Text Domain: woo-cart-abandonment-recovery
10
  * WC requires at least: 3.0
11
+ * WC tested up to: 4.2.0
12
  *
13
  * @package Woocommerce-Cart-Abandonment-Recovery
14
  */