CTX Feed – WooCommerce Product Feed Manager Plugin - Version 3.2.12

Version Description

(2020-01-14) = * Service library update * Add Support Form Added

Download this release

Release Info

Developer wahid0003
Plugin Icon 128x128 CTX Feed – WooCommerce Product Feed Manager Plugin
Version 3.2.12
Comparing to
See all releases

Code changes from version 3.2.11 to 3.2.12

README.txt CHANGED
@@ -5,7 +5,7 @@ Tags:woocommerce,google product feed,facebook product feed,woocommerce product f
5
  Requires at least: 3.6
6
  Tested Up To: 5.4-alpha-46743
7
  Requires PHP: 5.6
8
- Stable tag: 3.2.11
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -305,6 +305,10 @@ Using pro version:
305
 
306
  == Changelog ==
307
 
 
 
 
 
308
  = 3.2.11 (2020-01-08) =
309
  * Fix logging class not found error.
310
  * Update Feed Save Message
5
  Requires at least: 3.6
6
  Tested Up To: 5.4-alpha-46743
7
  Requires PHP: 5.6
8
+ Stable tag: 3.2.12
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
305
 
306
  == Changelog ==
307
 
308
+ = 3.2.12 (2020-01-14) =
309
+ * Service library update
310
+ * Add Support Form Added
311
+
312
  = 3.2.11 (2020-01-08) =
313
  * Fix logging class not found error.
314
  * Update Feed Save Message
admin/partials/woo-feed-config.php CHANGED
@@ -82,7 +82,7 @@ if ( ! $batch_limit || $batch_limit <= 0 ) {
82
  <td>
83
  <label for="opt_in">
84
  <input type="checkbox" name="opt_in" id="opt_in"
85
- value="on" <?php checked( WooFeedTracker::getInstance()->is_tracking_allowed(),
86
  true ); ?>>
87
  <?php _e( 'Allow WooFeed To Collect Debug Info.', 'woo-feed' ); ?>
88
  </label>
@@ -95,7 +95,7 @@ if ( ! $batch_limit || $batch_limit <= 0 ) {
95
  </p>
96
  <ul class="tracker_collection_list" style="display: none;">
97
  <li><?php echo implode( '</li><li>',
98
- WooFeedTracker::getInstance()->get_data_collection_description() ); ?></li>
99
  </ul>
100
  </td>
101
  </tr>
82
  <td>
83
  <label for="opt_in">
84
  <input type="checkbox" name="opt_in" id="opt_in"
85
+ value="on" <?php checked( WooFeedWebAppickAPI::getInstance()->is_tracking_allowed(),
86
  true ); ?>>
87
  <?php _e( 'Allow WooFeed To Collect Debug Info.', 'woo-feed' ); ?>
88
  </label>
95
  </p>
96
  <ul class="tracker_collection_list" style="display: none;">
97
  <li><?php echo implode( '</li><li>',
98
+ WooFeedWebAppickAPI::getInstance()->get_data_collection_description() ); ?></li>
99
  </ul>
100
  </td>
101
  </tr>
includes/classes/class-woo-feed-tracker.php DELETED
@@ -1,162 +0,0 @@
1
- <?php
2
- /**
3
- * WooCommerce Product Feed Plugin Uses Tracker
4
- * Uses Webappick Insights for tracking
5
- * @since 3.1.41
6
- * @version 1.0.2
7
- */
8
- if( ! defined( 'ABSPATH' ) ) die();
9
-
10
- if( ! class_exists( 'WooFeedTracker' ) ) {
11
- /**
12
- * Class WooFeedTracker
13
- */
14
- final class WooFeedTracker {
15
-
16
- /**
17
- * Singleton instance
18
- * @var WooFeedTracker
19
- */
20
- protected static $instance;
21
-
22
- /**
23
- * @var WebAppick\AppServices\Insights
24
- */
25
- protected $insights = null;
26
-
27
- /**
28
- * Promotions Class Instance
29
- * @var \WebAppick\AppServices\Promotions
30
- */
31
- public $promotion;
32
-
33
- /**
34
- * @var WebAppick\AppServices\Updater
35
- */
36
- protected $updater = null;
37
-
38
- /**
39
- * @var WebAppick\AppServices\License
40
- */
41
- protected $license = null;
42
-
43
- /**
44
- * Initialize
45
- * @return WooFeedTracker
46
- */
47
- public static function getInstance() {
48
- if( is_null( self::$instance ) ) self::$instance = new self();
49
- return self::$instance;
50
- }
51
-
52
- /**
53
- * Class constructor
54
- *
55
- * @return void
56
- * @since 1.0.0
57
- *
58
- */
59
- private function __construct() {
60
- add_action( 'init', [ $this, '__init' ] );
61
- }
62
-
63
- /**
64
- * Cloning is forbidden.
65
- * @since 1.0.2
66
- */
67
- public function __clone() {
68
- _doing_it_wrong( __FUNCTION__, __( 'Cloning is forbidden.', 'woo-feed' ), '1.0.2' );
69
- }
70
-
71
- public function __init() {
72
- if ( ! class_exists( 'WebAppick\AppServices\Client' ) ) {
73
- /** @noinspection PhpIncludeInspection */
74
- require_once WOO_FEED_LIBS_PATH . 'WebAppick/AppServices/Client.php';
75
- }
76
-
77
- $client = new WebAppick\AppServices\Client( '4e68acba-cbdc-476b-b4bf-eab176ac6a16', 'WooCommerce Product Feed', WOO_FEED_FREE_FILE );
78
-
79
- $this->insights = $client->insights();
80
-
81
- // $this->license = $client->license();
82
- // $this->updater = $client->updater();
83
- // $this->license->add_settings_page( [ 'type' => 'submenu', 'parent_slug' => 'webappick-manage-feeds', ] );
84
- $result = [];
85
- // global $wpdb;
86
- // $query = $wpdb->prepare("SELECT * FROM $wpdb->options WHERE option_name LIKE %s;", "wf_feed_%");
87
- // $result = $wpdb->get_results($query, 'ARRAY_A');
88
- $catCount = wp_count_terms( 'product_cat', ['hide_empty'=> false, 'parent' => 0] );
89
- if( is_wp_error( $catCount ) ) $catCount = 0;
90
- /**
91
- * @TODO count products by type
92
- * @see wc_get_product_types();
93
- */
94
- $this->insights->add_extra( [
95
- 'products' => $this->insights->get_post_count( 'product' ),
96
- 'variations' => $this->insights->get_post_count( 'product_variation' ),
97
- 'batch_limit' => get_option( 'woo_feed_per_batch' ),
98
- 'feed_configs' => json_encode( $result ),
99
-
100
- 'product_cat_num' => $catCount,
101
- ] );
102
-
103
- // Promo offers
104
- $this->promotion = $client->promotions();
105
- $this->promotion->set_source( 'https://api.bitbucket.org/2.0/snippets/woofeed/RLbyop/files/woo-feed-notice.json' )->init();
106
-
107
- add_filter( $client->getSlug() . '_what_tracked', [ $this, 'data_we_collect' ], 10, 1 );
108
-
109
- $this->insights->init_plugin();
110
- }
111
-
112
- /**
113
- * Set Data Collection description for the tracker
114
- * @param $data
115
- *
116
- * @return array
117
- */
118
- public function data_we_collect( $data ) {
119
- $data = array_merge( $data, [
120
- esc_html__( 'Number of products in your site.', 'woo-feed' ),
121
- esc_html__( 'Number of product categories in your site.', 'woo-feed' ),
122
- esc_html__( 'Feed Configuration.', 'woo-feed' ),
123
- esc_html__( 'Site name, language and url.', 'woo-feed' ),
124
- esc_html__( 'Number of active and inactive plugins.', 'woo-feed' ),
125
- esc_html__( 'Your name and email address.', 'woo-feed' )
126
- ] );
127
- return $data;
128
- }
129
-
130
- public function get_data_collection_description() {
131
- return $this->insights->get_data_collection_description();
132
- }
133
-
134
- /**
135
- * Update Tracker OptIn
136
- *
137
- * @param bool $override optional. ignore last send datetime settings if true.
138
- * @see Insights::send_tracking_data()
139
- * @return void
140
- */
141
- public function trackerOptIn( $override = false ) {
142
- $this->insights->optIn( true );
143
- }
144
-
145
- /**
146
- * Update Tracker OptOut
147
- * @return void
148
- */
149
- public function trackerOptOut() {
150
- $this->insights->optOut();
151
- }
152
-
153
- /**
154
- * Check if tracking is enable
155
- * @return bool
156
- */
157
- public function is_tracking_allowed() {
158
- return $this->insights->is_tracking_allowed();
159
- }
160
- }
161
- }
162
- // End of file class-woo-feed-tracker.php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class-woo-feed-webappick-api.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WooCommerce Product Feed Plugin Uses Tracker
4
+ * Uses Webappick Insights for tracking
5
+ * @since 3.1.41
6
+ * @version 1.0.2
7
+ */
8
+ if( ! defined( 'ABSPATH' ) ) die();
9
+
10
+ if( ! class_exists( 'WooFeedWebAppickAPI' ) ) {
11
+ /**
12
+ * Class WooFeedWebAppickAPI
13
+ */
14
+ final class WooFeedWebAppickAPI {
15
+
16
+ /**
17
+ * Singleton instance
18
+ * @var WooFeedWebAppickAPI
19
+ */
20
+ protected static $instance;
21
+
22
+ /**
23
+ * @var WebAppick\AppServices\Client
24
+ */
25
+ protected $client = null;
26
+
27
+ /**
28
+ * @var WebAppick\AppServices\Insights
29
+ */
30
+ protected $insights = null;
31
+
32
+ /**
33
+ * Promotions Class Instance
34
+ * @var WebAppick\AppServices\Promotions
35
+ */
36
+ public $promotion = null;
37
+
38
+ /**
39
+ * Plugin License Manager
40
+ * @var WebAppick\AppServices\License
41
+ */
42
+ protected $license = null;
43
+
44
+ /**
45
+ * Plugin Updater
46
+ * @var WebAppick\AppServices\Updater
47
+ */
48
+ protected $updater = null;
49
+
50
+ /**
51
+ * Initialize
52
+ * @return WooFeedWebAppickAPI
53
+ */
54
+ public static function getInstance() {
55
+ if( is_null( self::$instance ) ) self::$instance = new self();
56
+ return self::$instance;
57
+ }
58
+
59
+ /**
60
+ * Class constructor
61
+ *
62
+ * @return void
63
+ * @since 1.0.0
64
+ *
65
+ */
66
+ private function __construct() {
67
+ add_action( 'init', [ $this, '__init' ] );
68
+ }
69
+
70
+ /**
71
+ * Cloning is forbidden.
72
+ * @since 1.0.2
73
+ */
74
+ public function __clone() {
75
+ _doing_it_wrong( __FUNCTION__, __( 'Cloning is forbidden.', 'woo-feed' ), '1.0.2' );
76
+ }
77
+
78
+ public function __init() {
79
+ if ( ! class_exists( 'WebAppick\AppServices\Client' ) ) {
80
+ /** @noinspection PhpIncludeInspection */
81
+ require_once WOO_FEED_LIBS_PATH . 'WebAppick/AppServices/Client.php';
82
+ }
83
+ // Load Client
84
+ $this->client = new WebAppick\AppServices\Client( '4e68acba-cbdc-476b-b4bf-eab176ac6a16', 'WooCommerce Product Feed', WOO_FEED_FREE_FILE );
85
+ // Load
86
+ $this->insights = $this->client->insights(); // Plugin Insights
87
+ $this->promotion = $this->client->promotions(); // Promo offers
88
+
89
+ // Setup
90
+ $this->promotion->set_source( 'https://api.bitbucket.org/2.0/snippets/woofeed/RLbyop/files/woo-feed-notice.json' );
91
+
92
+ // Initialize
93
+ $this->insightInit();
94
+ $this->promotion->init();
95
+ }
96
+
97
+ /**
98
+ * Initialize Insights
99
+ * @return void
100
+ */
101
+ private function insightInit() {
102
+ global $wpdb;
103
+ $result = $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->options WHERE option_name LIKE %s;", "wf_feed_%"), 'ARRAY_A' );
104
+ if( ! is_array( $result ) ) $result = [];
105
+ $catCount = wp_count_terms( 'product_cat', ['hide_empty'=> false, 'parent' => 0] );
106
+ if( is_wp_error( $catCount ) ) $catCount = 0;
107
+ /**
108
+ * @TODO count products by type
109
+ * @see wc_get_product_types();
110
+ */
111
+ $this->insights->add_extra( [
112
+ 'products' => $this->insights->get_post_count( 'product' ),
113
+ 'variations' => $this->insights->get_post_count( 'product_variation' ),
114
+ 'batch_limit' => get_option( 'woo_feed_per_batch' ),
115
+ 'feed_configs' => json_encode( $result ),
116
+
117
+ 'product_cat_num' => $catCount,
118
+ ] );
119
+ add_filter( $this->client->getSlug() . '_what_tracked', [ $this, 'data_we_collect' ], 10, 1 );
120
+ $this->insights
121
+ ->setTicketRecipient( 'support@webappick.com' )
122
+ ->setSupportResponse( $this->supportResponse() )
123
+ ->setTicketTemplate( $this->supportTicketTemplate() )
124
+ ->setSupportErrorResponse( $this->supportErrorResponse() )
125
+ ->setSupportURL( 'https://webappick.com/support/' )
126
+ ->init();
127
+ }
128
+
129
+ /**
130
+ * Generate Support Ticket Email Template
131
+ * @return string
132
+ */
133
+ private function supportTicketTemplate() {
134
+ // dynamic variable format __INPUT_NAME__
135
+ $licenseData = '';
136
+ if ( $license = get_option( 'woocommerce_product_feed_pro_data', false ) ) {
137
+ $licenseData = sprintf( '<br>API Key : %s<br>API Email : %s', $license['api_key'], $license['activation_email'] );
138
+ }
139
+ /** @noinspection HtmlUnknownTarget */
140
+ $template = '<div style="margin: 10px auto;"><p>Website : <a href="__WEBSITE__">__WEBSITE__</a><br>Plugin : %s (v.%s)%s</p></div>';
141
+ $template = sprintf( $template, $this->client->getName(), $this->client->getProjectVersion(), $licenseData );
142
+ $template .= '<div style="margin: 10px auto;"><hr></div>';
143
+ $template .= '<div style="margin: 10px auto;"><h3>__SUBJECT__</h3></div>';
144
+ $template .= '<div style="margin: 10px auto;">__MESSAGE__</div>';
145
+ $template .= '<div style="margin: 10px auto;"><hr></div>';
146
+ $template .= sprintf(
147
+ '<div style="margin: 50px auto 10px auto;"><p style="font-size: 12px;color: #009688">%s</p></div>',
148
+ 'Message Processed With WebAppick Service Library (v.' . $this->client->getClientVersion() . ')'
149
+ );
150
+ return $template;
151
+ }
152
+
153
+ /**
154
+ * Generate Support Ticket Ajax Response
155
+ * @return string
156
+ */
157
+ private function supportResponse() {
158
+ $response = '';
159
+ $response .= sprintf( '<h3>%s</h3>', esc_html__( 'Thank you -- Support Ticket Submitted.', 'webappick' ) );
160
+ $ticketSubmitted = esc_html__( 'Your ticket has been successfully submitted.', 'webappick' );
161
+ $twenty4Hours = sprintf( '<strong>%s</strong>', esc_html__( '24 hours', 'webappick' ) );
162
+ $notification = sprintf( esc_html__( 'You will receive an email notification from "support@webappick.com" in your inbox within %s.', 'webappick' ), $twenty4Hours );
163
+ $followUp = esc_html__( 'Please Follow the email and WebAppick Support Team will get back with you shortly.', 'webappick' );
164
+ $response .= sprintf( '<p>%s %s %s</p>', $ticketSubmitted, $notification, $followUp );
165
+ $docLink = sprintf( '<a class="button button-primary" href="https://webappick.helpscoutdocs.com/" target="_blank"><span class="dashicons dashicons-media-document" aria-hidden="true"></span> %s</a>', esc_html__( 'Documentation', 'webappick' ) );
166
+ $vidLink = sprintf( '<a class="button button-primary" href="http://bit.ly/2u6giNz" target="_blank"><span class="dashicons dashicons-video-alt3" aria-hidden="true"></span> %s</a>', esc_html__( 'Video Tutorials', 'webappick' ) );
167
+ $response .= sprintf( '<p>%s %s</p>', $docLink, $vidLink );
168
+ $response .= '<br><br><br>';
169
+ $toc = sprintf( '<a href="https://webappick.com/terms-and-conditions/" target="_blank">%s</a>', esc_html__( 'Terms & Conditions', 'webappick' ) );
170
+ $pp = sprintf( '<a href="https://webappick.com/privacy-policy/" target="_blank">%s</a>', esc_html__( 'Privacy Policy', 'webappick' ) );
171
+ $policy = sprintf( esc_html__( 'Please read our %s and %s', 'webappick' ), $toc, $pp );
172
+ $response .= sprintf( '<p style="font-size: 12px;">%s</p>', $policy );
173
+ return $response;
174
+ }
175
+
176
+ /**
177
+ * Set Error Response Message For Support Ticket Request
178
+ * @return string
179
+ */
180
+ private function supportErrorResponse() {
181
+ return sprintf(
182
+ '<div class="mui-error"><p>%s</p><p>%s</p><br><br><p style="font-size: 12px;">%s</p></div>',
183
+ esc_html__( 'Something Went Wrong. Please Try The Support Ticket Form On Our Website.', 'webappick' ),
184
+ sprintf( '<a class="button button-primary" href="https://webappick.com/support/" target="_blank">%s</a>', esc_html__( 'Get Support', 'webappick' ) ),
185
+ esc_html__( 'Support Ticket form will open in new tab in 5 seconds.', 'webappick' )
186
+ );
187
+ }
188
+
189
+ /**
190
+ * Set Data Collection description for the tracker
191
+ * @param $data
192
+ *
193
+ * @return array
194
+ */
195
+ public function data_we_collect( $data ) {
196
+ $data = array_merge( $data, [
197
+ esc_html__( 'Number of products in your site.', 'woo-feed' ),
198
+ esc_html__( 'Number of product categories in your site.', 'woo-feed' ),
199
+ esc_html__( 'Feed Configuration.', 'woo-feed' ),
200
+ esc_html__( 'Site name, language and url.', 'woo-feed' ),
201
+ esc_html__( 'Number of active and inactive plugins.', 'woo-feed' ),
202
+ esc_html__( 'Your name and email address.', 'woo-feed' )
203
+ ] );
204
+ return $data;
205
+ }
206
+
207
+ public function get_data_collection_description() {
208
+ return $this->insights->get_data_collection_description();
209
+ }
210
+
211
+ /**
212
+ * Update Tracker OptIn
213
+ *
214
+ * @param bool $override optional. ignore last send datetime settings if true.
215
+ * @see Insights::send_tracking_data()
216
+ * @return void
217
+ */
218
+ public function trackerOptIn( $override = false ) {
219
+ $this->insights->optIn( true );
220
+ }
221
+
222
+ /**
223
+ * Update Tracker OptOut
224
+ * @return void
225
+ */
226
+ public function trackerOptOut() {
227
+ $this->insights->optOut();
228
+ }
229
+
230
+ /**
231
+ * Check if tracking is enable
232
+ * @return bool
233
+ */
234
+ public function is_tracking_allowed() {
235
+ return $this->insights->is_tracking_allowed();
236
+ }
237
+ }
238
+ }
239
+ // End of file class-woo-feed-webappick-api.php
libs/WebAppick/AppServices/Client.php CHANGED
@@ -82,18 +82,26 @@ class Client {
82
  */
83
  protected $type;
84
 
 
 
 
 
 
 
 
85
  /**
86
  * Initialize the class
87
  *
88
- * @param string $hash hash of the Plugin/Theme
89
- * @param string $name readable name of the Plugin/Theme
90
- * @param string $file main Plugin/Theme file path
 
91
  */
92
- public function __construct( $hash, $name, $file ) {
93
  $this->hash = $hash;
94
  $this->name = $name;
95
  $this->file = $file;
96
-
97
  $this->set_basename_and_slug();
98
  }
99
 
@@ -120,14 +128,14 @@ class Client {
120
 
121
  /**
122
  * Initialize Plugin/Theme updater
123
- *
124
  * @return Updater
125
  */
126
- public function updater() {
127
  if ( ! class_exists( __NAMESPACE__ . '\Updater') ) {
128
  require_once __DIR__ . '/Updater.php';
129
  }
130
- return new Updater( $this );
131
  }
132
 
133
  /**
@@ -304,7 +312,7 @@ class Client {
304
  * @return string
305
  */
306
  public function getApi() {
307
- return $this->api;
308
  }
309
 
310
  /**
@@ -331,6 +339,14 @@ class Client {
331
  return $this->name;
332
  }
333
 
 
 
 
 
 
 
 
 
334
  /**
335
  * Get Plugin/Theme file
336
  * @return string
82
  */
83
  protected $type;
84
 
85
+ /**
86
+ * Store Product (unique) id for current Product
87
+ * Required by WooCommerce API Manager > 2.0
88
+ * @var bool|int
89
+ */
90
+ protected $ProjectId;
91
+
92
  /**
93
  * Initialize the class
94
  *
95
+ * @param string $hash hash of the Plugin/Theme
96
+ * @param string $name readable name of the Plugin/Theme
97
+ * @param string $file main Plugin/Theme file path
98
+ * @param string $ProjectId Store Product id for pro product
99
  */
100
+ public function __construct( $hash, $name, $file, $ProjectId = null ) {
101
  $this->hash = $hash;
102
  $this->name = $name;
103
  $this->file = $file;
104
+ $this->ProjectId = ! empty( $ProjectId ) ? (int) $ProjectId : false;
105
  $this->set_basename_and_slug();
106
  }
107
 
128
 
129
  /**
130
  * Initialize Plugin/Theme updater
131
+ * @param License $license
132
  * @return Updater
133
  */
134
+ public function updater( License $license ) {
135
  if ( ! class_exists( __NAMESPACE__ . '\Updater') ) {
136
  require_once __DIR__ . '/Updater.php';
137
  }
138
+ return new Updater( $this, $license );
139
  }
140
 
141
  /**
312
  * @return string
313
  */
314
  public function getApi() {
315
+ return $this->API_EndPoint;
316
  }
317
 
318
  /**
339
  return $this->name;
340
  }
341
 
342
+ /**
343
+ * Store Product ID
344
+ * @return bool|int
345
+ */
346
+ public function getProjectId() {
347
+ return $this->ProjectId;
348
+ }
349
+
350
  /**
351
  * Get Plugin/Theme file
352
  * @return string
libs/WebAppick/AppServices/Insights.php CHANGED
@@ -3,10 +3,12 @@ namespace WebAppick\AppServices;
3
  use mysql_xdevapi\Exception;
4
  use WP_Theme;
5
  use WP_User;
6
- if( ! defined( 'ABSPATH' ) ) die();
7
  /**
8
  * WebAppick Insights
9
  *
 
 
10
  * This is a tracker class to track plugin usage based on if the customer has opted in.
11
  * No personal information is being tracked by this class, only general settings, active plugins, environment details
12
  * and admin email.
@@ -41,6 +43,33 @@ class Insights {
41
  */
42
  protected $client;
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  /**
45
  * Initialize the class
46
  *
@@ -84,7 +113,7 @@ class Insights {
84
  /**
85
  * Set custom notice text
86
  *
87
- * @param string $text
88
  *
89
  * @return Insights
90
  */
@@ -102,9 +131,10 @@ class Insights {
102
  public function init() {
103
  if ( $this->client->getType() == 'plugin' ) {
104
  $this->init_plugin();
105
- } else if ( $this->client->getType() == 'theme' ) {
106
  $this->init_theme();
107
  }
 
108
  }
109
 
110
  /**
@@ -112,11 +142,11 @@ class Insights {
112
  *
113
  * @return void
114
  */
115
- public function init_theme() {
116
  $this->init_common();
117
 
118
- add_action( 'switch_theme', array( $this, 'deactivation_cleanup' ) );
119
- add_action( 'switch_theme', array( $this, 'theme_deactivated' ), 12, 3 );
120
  }
121
 
122
  /**
@@ -124,17 +154,17 @@ class Insights {
124
  *
125
  * @return void
126
  */
127
- public function init_plugin() {
128
  // plugin deactivate popup
129
  if ( ! $this->__is_local_server() ) {
130
- add_action( 'plugin_action_links_' . $this->client->getBasename(), array( $this, 'plugin_action_links' ) );
131
- add_action( 'admin_footer', array( $this, 'deactivate_scripts' ) );
132
  }
133
 
134
  $this->init_common();
135
 
136
- register_activation_hook( $this->client->getFile(), array( $this, 'activate_plugin' ) );
137
- register_deactivation_hook( $this->client->getFile(), array( $this, 'deactivation_cleanup' ) );
138
  }
139
 
140
  /**
@@ -145,45 +175,54 @@ class Insights {
145
  protected function init_common() {
146
  if ( $this->show_notice ) {
147
  // tracking notice
148
- add_action( 'admin_notices', array( $this, 'admin_notice' ) );
149
  }
150
- add_action( 'admin_init', array( $this, 'handle_optIn_optOut' ) );
151
- add_action( 'removable_query_args', array( $this, 'add_removable_query_args' ), 10, 1 );
152
  // uninstall reason
153
- add_action( 'wp_ajax_' . $this->client->getSlug() . '_submit-uninstall-reason', array( $this, 'uninstall_reason_submission' ) );
 
154
  // cron events
155
- add_filter( 'cron_schedules', array( $this, 'add_weekly_schedule' ) );
156
- add_action( $this->client->getSlug() . '_tracker_send_event', array( $this, 'send_tracking_data' ) );
157
- // add_action( 'admin_init', array( $this, 'send_tracking_data' ) ); // test
158
  }
159
 
160
  /**
161
  * Send tracking data to WebAppick server
162
  *
163
- * @param boolean $override
164
  *
165
  * @return void
166
  */
167
  public function send_tracking_data( $override = false ) {
168
  // skip on AJAX Requests
169
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) return;
170
- if ( ! $this->is_tracking_allowed() && ! $override ) return;
 
 
 
 
171
  // Send a maximum of once per week
172
  $last_send = $this->__get_last_send();
173
  /**
174
  * Tracking interval
175
- * @param string $interval A valid date/time string
 
 
176
  * @see strtotime()
177
  * @link https://www.php.net/manual/en/function.strtotime.php
178
  */
179
  $trackingInterval = apply_filters( $this->client->getSlug() . '_tracking_interval', '-1 week' );
180
  try {
181
  $intervalCheck = strtotime( $trackingInterval );
182
- } catch( Exception $e ) {
183
  // fallback to default 1 week if filter returned unusable data
184
  $intervalCheck = strtotime( '-1 week' );
185
  }
186
- if ( $last_send && $last_send > $intervalCheck && ! $override ) return;
 
 
187
  $this->client->send_request( $this->get_tracking_data(), 'track' );
188
  update_option( $this->client->getSlug() . '_tracking_last_send', time(), false );
189
  }
@@ -194,12 +233,12 @@ class Insights {
194
  * @return array
195
  */
196
  protected function get_tracking_data() {
197
- $all_plugins = $this->__get_all_plugins();
198
- $admin_user = $this->__get_admin();
199
  $admin_emails = [ get_option( 'admin_email' ), $admin_user->user_email ];
200
  $admin_emails = array_filter( $admin_emails );
201
  $admin_emails = array_unique( $admin_emails );
202
- $data = array(
203
  'version' => $this->client->getProjectVersion(),
204
  'url' => esc_url( home_url() ),
205
  'site' => $this->__get_site_name(),
@@ -215,11 +254,12 @@ class Insights {
215
  'inactive_plugins' => $all_plugins['inactive_plugins'],
216
  'ip_address' => $this->__get_user_ip_address(),
217
  'theme' => get_stylesheet(),
218
- );
219
  // for child classes
220
  if ( $extra = $this->get_extra_data() ) {
221
  $data['extra'] = $extra;
222
  }
 
223
  return apply_filters( $this->client->getSlug() . '_tracker_data', $data );
224
  }
225
 
@@ -242,6 +282,7 @@ class Insights {
242
  esc_html__( 'Server environment details (php, mysql, server, WordPress versions).', 'webappick' ),
243
  ];
244
  $data = apply_filters( $this->client->getSlug() . '_what_tracked', $data );
 
245
  return $data;
246
  }
247
 
@@ -255,13 +296,14 @@ class Insights {
255
  * @return WP_User
256
  */
257
  private function __get_admin() {
258
- $admins = get_users( array(
259
  'role' => 'administrator',
260
  'orderby' => 'ID',
261
  'order' => 'ASC',
262
  'number' => 1,
263
  'paged' => 1,
264
- ) );
 
265
  return ( is_array( $admins ) && ! empty( $admins ) ) ? $admins[0] : new WP_User();
266
  }
267
 
@@ -290,7 +332,10 @@ class Insights {
290
  */
291
  private function __notice_dismissed() {
292
  $hide_notice = get_option( $this->client->getSlug() . '_tracking_notice', 'no' );
293
- if ( 'hide' == $hide_notice ) return true;
 
 
 
294
  return false;
295
  }
296
 
@@ -300,7 +345,7 @@ class Insights {
300
  * @return boolean
301
  */
302
  private function __is_local_server() {
303
- return apply_filters( 'WebAppick_is_local', in_array( $_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1' ) ) );
304
  }
305
 
306
  /**
@@ -330,27 +375,41 @@ class Insights {
330
  * @return void
331
  */
332
  public function admin_notice() {
333
- if ( $this->__notice_dismissed() ) return;
 
 
334
 
335
- if ( $this->is_tracking_allowed() ) return;
336
- if ( ! current_user_can( 'manage_options' ) ) return;
 
 
 
 
337
 
338
  // don't show tracking if a local server
339
  if ( ! $this->__is_local_server() ) {
340
 
341
  if ( empty( $this->notice ) ) {
342
- $notice = sprintf( apply_filters( $this->client->getSlug() . '_tracking_default_notice_message', esc_html__( 'Want to help make %1$s even more awesome? Allow %1$s to collect non-sensitive diagnostic data and usage information.', 'webappick' ) ), '<strong>'.$this->client->getName().'</strong>' );
 
 
 
343
  } else {
344
  $notice = $this->notice;
345
  }
346
 
347
- $notice .= ' (<a class="' . $this->client->getSlug() . '-insights-data-we-collect" href="#">' . esc_html__( 'what we collect', 'webappick' ) . '</a>)';
348
- $notice .= '<p class="description" style="display:none;">' . implode( ', ', $this->data_we_collect() ) . '. '. esc_html__( 'No sensitive data is tracked.', 'webappick' ) .'</p>';
 
 
 
349
  echo '<div class="updated"><p>';
350
  echo $notice;
351
  echo '</p><p class="submit">';
352
- echo '&nbsp;<a href="' . esc_url( $this->get_opt_out_url() ) . '" class="button button-secondary">' . esc_html__( 'No thanks', 'webappick' ) . '</a>';
353
- echo '&nbsp;<a href="' . esc_url( $this->get_opt_in_url() ) . '" class="button button-primary">' . esc_html__( 'Allow', 'webappick' ) . '</a>';
 
 
354
  echo '</p></div>';
355
  echo "<script type='text/javascript'>jQuery('." . $this->client->getSlug() . "-insights-data-we-collect').on('click', function(e) {
356
  e.preventDefault();
@@ -382,7 +441,7 @@ class Insights {
382
  */
383
  public function handle_optIn_optOut() {
384
 
385
- if( isset( $_GET[ $this->client->getSlug() . '_tracker_optIn' ] ) && $_GET[ $this->client->getSlug() . '_tracker_optIn' ] == 'true' ) {
386
  $this->optIn();
387
  wp_redirect( remove_query_arg( $this->client->getSlug() . '_tracker_optIn' ) );
388
  exit;
@@ -396,19 +455,23 @@ class Insights {
396
 
397
  /**
398
  * Add query vars to removable query args array
 
399
  * @param array $removable_query_args
 
400
  * @return array
401
  */
402
  public function add_removable_query_args( $removable_query_args ) {
403
- return array_merge( $removable_query_args, array($this->client->getSlug() . '_tracker_optIn', $this->client->getSlug() . '_tracker_optOut' ) );
 
404
  }
405
 
406
  /**
407
  * Tracking optIn
408
  *
409
- * @param bool $override optional. set send tracking data override setting, ignore last send datetime setting if true.
410
- * @see Insights::send_tracking_data()
411
  * @return void
 
412
  */
413
  public function optIn( $override = false ) {
414
  update_option( $this->client->getSlug() . '_allow_tracking', 'yes', false );
@@ -432,13 +495,15 @@ class Insights {
432
  /**
433
  * Get the number of post counts
434
  *
435
- * @param string $post_type
436
  *
437
  * @return integer
438
  */
439
  public function get_post_count( $post_type ) {
440
  global $wpdb;
441
- return (int) $wpdb->get_var( $wpdb->prepare( "SELECT count(ID) FROM $wpdb->posts WHERE post_type = %s and post_status = 'publish'", $post_type ) );
 
 
442
  }
443
 
444
  /**
@@ -448,19 +513,20 @@ class Insights {
448
  */
449
  private function __get_server_info() {
450
  global $wpdb;
451
- $server_data = array(
452
- 'software' => ( isset( $_SERVER['SERVER_SOFTWARE'] ) && ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) ? $_SERVER['SERVER_SOFTWARE'] : 'N/A',
453
- 'php_version' => ( function_exists( 'phpversion' ) ) ? phpversion() : 'N/A',
454
- 'mysql_version' => $wpdb->db_version(),
455
- 'php_execution_time' => @ini_get('max_execution_time'),
456
- 'php_max_upload_size' => size_format( wp_max_upload_size() ),
457
- 'php_default_timezone' => date_default_timezone_get(),
458
- 'php_soap' => class_exists( 'SoapClient' ) ? 'Yes' : 'No',
459
- 'php_fsockopen' => function_exists( 'fsockopen' ) ? 'Yes' : 'No',
460
- 'php_curl' => function_exists( 'curl_init' ) ? 'Yes' : 'No',
461
- 'php_ftp' => function_exists( 'ftp_connect' ) ? 'Yes' : 'No',
462
- 'php_sftp' => function_exists( 'ssh2_connect' ) ? 'Yes' : 'No',
463
- );
 
464
  return $server_data;
465
  }
466
 
@@ -470,13 +536,14 @@ class Insights {
470
  * @return array
471
  */
472
  private function __get_wp_info() {
473
- $wp_data = array(
474
- 'memory_limit' => WP_MEMORY_LIMIT,
475
- 'debug_mode' => ( defined('WP_DEBUG') && WP_DEBUG ) ? 'Yes' : 'No',
476
- 'locale' => get_locale(),
477
- 'version' => get_bloginfo( 'version' ),
478
- 'multisite' => is_multisite() ? 'Yes' : 'No',
479
- );
 
480
  return $wp_data;
481
  }
482
 
@@ -485,29 +552,30 @@ class Insights {
485
  * @return array
486
  */
487
  private function __get_all_plugins() {
488
- if( ! function_exists( 'get_plugins' ) ) {
489
  include ABSPATH . '/wp-admin/includes/plugin.php';
490
  }
491
  $plugins = get_plugins();
492
- $active_plugins = array();
493
- $active_plugins_keys = get_option( 'active_plugins', array() );
494
  foreach ( $plugins as $k => $v ) {
495
  // Take care of formatting the data how we want it.
496
- $formatted = array(
497
- 'name' => strip_tags( $v['Name'] ),
498
- 'version' => isset( $v['Version'] ) ? strip_tags( $v['Version'] ) : 'N/A',
499
- 'author' => isset( $v['Author'] ) ? strip_tags( $v['Author'] ) : 'N/A',
500
- 'network' => isset( $v['Network'] ) ? strip_tags( $v['Network'] ) : 'N/A',
501
- 'plugin_uri' => isset( $v['PluginURI'] ) ? strip_tags( $v['PluginURI'] ) : 'N/A',
502
- );
503
  if ( in_array( $k, $active_plugins_keys ) ) {
504
- unset( $plugins[$k] ); // Remove active plugins from list so we can show active and inactive separately
505
- $active_plugins[$k] = $formatted;
506
  } else {
507
- $plugins[$k] = $formatted;
508
  }
509
  }
510
- return array( 'active_plugins' => $active_plugins, 'inactive_plugins' => $plugins );
 
511
  }
512
 
513
  /**
@@ -516,28 +584,30 @@ class Insights {
516
  * @return array
517
  */
518
  public function __get_user_counts() {
519
- $user_count = array();
520
  $user_count_data = count_users();
521
  $user_count['total'] = $user_count_data['total_users'];
522
  // Get user count based on user role
523
  foreach ( $user_count_data['avail_roles'] as $role => $count ) {
524
  $user_count[ $role ] = $count;
525
  }
 
526
  return $user_count;
527
  }
528
 
529
  /**
530
  * Add weekly cron schedule
531
  *
532
- * @param array $schedules
533
  *
534
  * @return array
535
  */
536
  public function add_weekly_schedule( $schedules ) {
537
- $schedules['weekly'] = array(
538
  'interval' => DAY_IN_SECONDS * 7,
539
- 'display' => __( 'Once Weekly', 'webappick' )
540
- );
 
541
  return $schedules;
542
  }
543
 
@@ -549,7 +619,9 @@ class Insights {
549
  public function activate_plugin() {
550
  $allowed = get_option( $this->client->getSlug() . '_allow_tracking', 'no' );
551
  // if it wasn't allowed before, do nothing
552
- if ( 'yes' !== $allowed ) return;
 
 
553
  // re-schedule and delete the last sent time so we could force send again
554
  wp_schedule_event( time(), 'weekly', $this->client->getSlug() . '_tracker_send_event' );
555
  // wp_schedule_event( time(), 'daily', $this->client->getSlug() . '_license_check_event' );
@@ -575,14 +647,16 @@ class Insights {
575
  /**
576
  * Hook into action links and modify the deactivate link
577
  *
578
- * @param array $links
579
  *
580
  * @return array
581
  */
582
  public function plugin_action_links( $links ) {
583
 
584
  if ( array_key_exists( 'deactivate', $links ) ) {
585
- $links['deactivate'] = str_replace( '<a', '<a class="' . $this->client->getSlug() . '-deactivate-link"', $links['deactivate'] );
 
 
586
  }
587
 
588
  return $links;
@@ -594,90 +668,184 @@ class Insights {
594
  */
595
  private function __get_uninstall_reasons() {
596
 
597
- $reasons = array(
598
-
599
- array(
600
  'id' => 'could-not-understand',
601
  'text' => esc_html__( 'I couldn\'t understand how to make it work', 'webappick' ),
602
  'type' => 'textarea',
603
  'placeholder' => esc_html__( 'Would you like us to assist you?', 'webappick' ),
604
- ),
605
- array(
606
  'id' => 'found-better-plugin',
607
  'text' => esc_html__( 'I found a better plugin', 'webappick' ),
608
  'type' => 'text',
609
  'placeholder' => esc_html__( 'Which plugin?', 'webappick' ),
610
- ),
611
- array(
612
  'id' => 'not-have-that-feature',
613
- 'text' => esc_html__( 'The plugin is great, but I need specific feature that you don\'t support', 'webappick' ),
 
614
  'type' => 'textarea',
615
  'placeholder' => esc_html__( 'Could you tell us more about that feature?', 'webappick' ),
616
- ),
617
- array(
618
  'id' => 'is-not-working',
619
  'text' => esc_html__( 'The plugin is not working', 'webappick' ),
620
  'type' => 'textarea',
621
  'placeholder' => esc_html__( 'Could you tell us a bit more whats not working?', 'webappick' ),
622
- ),
623
- array(
624
  'id' => 'looking-for-other',
625
  'text' => esc_html__( 'It\'s not what I was looking for', 'webappick' ),
626
  'type' => '',
627
  'placeholder' => '',
628
- ),
629
- array(
630
  'id' => 'did-not-work-as-expected',
631
  'text' => esc_html__( 'The plugin didn\'t work as expected', 'webappick' ),
632
  'type' => 'textarea',
633
  'placeholder' => esc_html__( 'What did you expect?', 'webappick' ),
634
- ),
635
- array(
 
 
 
 
 
 
636
  'id' => 'other',
637
  'text' => esc_html__( 'Other', 'webappick' ),
638
  'type' => 'textarea',
639
  'placeholder' => esc_html__( 'Could you tell us a bit more?', 'webappick' ),
640
- ),
641
- );
642
-
 
 
 
 
 
643
  return $reasons;
644
  }
645
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
  /**
647
  * Plugin deactivation uninstall reason submission
648
  *
649
  * @return void
650
  */
651
  public function uninstall_reason_submission() {
652
-
653
  // if ( ! isset( $_POST['reason_id'] ) ) wp_send_json_error();
654
 
655
  $current_user = wp_get_current_user();
656
  global $wpdb;
657
  // @TODO remove deprecated data after server update
658
- $data = array(
659
  'hash' => $this->client->getHash(),
660
- 'reason_id' => isset( $_REQUEST['reason_id'] ) && ! empty( $_REQUEST['reason_id'] ) ? sanitize_text_field( $_REQUEST['reason_id'] ) : '', // WPCS: CSRF ok, Input var ok.
661
- 'reason_info' => isset( $_REQUEST['reason_info'] ) ? trim( sanitize_textarea_field( $_REQUEST['reason_info'] ) ) : '', // WPCS: CSRF ok, Input var ok.
 
 
662
  'plugin' => $this->client->getName(),
663
  'site' => $this->__get_site_name(),
664
  'url' => esc_url( home_url() ),
665
  'admin_email' => get_option( 'admin_email' ),
666
  'user_email' => $current_user->user_email,
667
- 'user_name' => $current_user->display_name, // deprecated
 
668
  'first_name' => ( ! empty( $current_user->first_name ) ) ? $current_user->first_name : $current_user->display_name,
669
  'last_name' => $current_user->last_name,
670
  'server' => $this->__get_server_info(),
671
- 'software' => $_SERVER['SERVER_SOFTWARE'], // deprecated, using $data['server'] for wp info
672
- 'php_version' => phpversion(), // deprecated, using $data['server'] for wp info
673
- 'mysql_version' => $wpdb->db_version(), // deprecated, using $data['server'] for wp info
 
 
 
674
  'wp' => $this->__get_wp_info(),
675
- 'wp_version' => get_bloginfo( 'version' ), // deprecated, using $data['wp'] for wp info
676
- 'locale' => get_locale(), // deprecated, using $data['wp'] for wp info
677
- 'multisite' => is_multisite() ? 'Yes' : 'No', // deprecated, using $data['wp'] for wp info
 
 
 
678
  'ip_address' => $this->__get_user_ip_address(),
679
  'version' => $this->client->getProjectVersion(),
680
- );
681
  // Add extra data
682
  if ( $extra = $this->get_extra_data() ) {
683
  $data['extra'] = $extra;
@@ -686,6 +854,52 @@ class Insights {
686
  wp_send_json_success();
687
  }
688
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
  /**
690
  * Handle the plugin deactivation feedback
691
  *
@@ -693,101 +907,381 @@ class Insights {
693
  */
694
  public function deactivate_scripts() {
695
  global $pagenow;
696
- if ( 'plugins.php' !== $pagenow ) return;
697
- $reasons = $this->__get_uninstall_reasons();
 
 
 
 
 
698
  ?>
699
- <div class="wapk-dr-modal" id="<?php echo $this->client->getSlug(); ?>-wapk-dr-modal">
700
- <div class="wapk-dr-modal-wrap">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
701
  <div class="wapk-dr-modal-header">
702
- <h3><?php _e( 'If you have a moment, please let us know why you are deactivating:', 'domain' ); ?></h3>
 
 
 
 
 
 
703
  </div>
704
  <div class="wapk-dr-modal-body">
705
  <ul class="reasons">
706
- <?php foreach ($reasons as $reason) { ?>
707
- <li data-type="<?php echo esc_attr( $reason['type'] ); ?>" data-placeholder="<?php echo esc_attr( $reason['placeholder'] ); ?>">
708
- <label><input type="radio" name="selected-reason" value="<?php echo $reason['id']; ?>"> <?php echo $reason['text']; ?></label>
709
- </li>
710
- <?php } ?>
711
  </ul>
 
 
 
 
 
 
 
 
 
 
 
712
  </div>
713
  <div class="wapk-dr-modal-footer">
714
- <a href="#" class="dont-bother-me"><?php _e( 'I rather wouldn\'t say', 'domain' ); ?></a>
715
- <button class="button-secondary"><?php _e( 'Submit & Deactivate', 'domain' ); ?></button>
716
- <button class="button-primary"><?php _e( 'Cancel', 'domain' ); ?></button>
717
  </div>
718
  </div>
719
  </div>
720
- <!--suppress CssUnusedSymbol -->
721
  <style type="text/css">
722
- .wapk-dr-modal { position: fixed; z-index: 99999; top: 0; right: 0; bottom: 0; left: 0; background: rgba(0,0,0,0.5); display: none; }
 
723
  .wapk-dr-modal.modal-active { display: block; }
 
724
  .wapk-dr-modal-wrap { width: 475px; position: relative; margin: 10% auto; background: #fff; }
725
- .wapk-dr-modal-header { border-bottom: 1px solid #eee; padding: 8px 20px; }
 
 
 
 
 
 
 
726
  .wapk-dr-modal-header h3 { line-height: 150%; margin: 0; }
727
- .wapk-dr-modal-body { padding: 5px 20px 20px 20px; }
 
 
728
  .wapk-dr-modal-body .reason-input { margin-top: 5px; margin-left: 20px; }
729
- .wapk-dr-modal-footer { border-top: 1px solid #eee; padding: 12px 20px; text-align: right; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
  </style>
731
  <!--suppress ES6ConvertVarToLetConst, JSUnresolvedVariable -->
732
  <script type="text/javascript">
733
- (function($) {
734
- $(function() {
735
  /**
736
  * Ajax Helper For Submitting Deactivation Reasons
737
  * @param {Object} data
738
  * @param {*|jQuery|HTMLElement} buttonElem
739
- * @param {String} redirectTo
740
  * @returns {*|jQuery}
741
  * @private
742
  */
743
- function _ajax( data, buttonElem, redirectTo ) {
744
- if ( buttonElem.hasClass('disabled') ) return;
745
- return $.ajax( {
 
746
  url: ajaxurl,
747
  type: 'POST',
748
- data: $.fn.extend( {}, data, { action: '<?php echo $this->client->getSlug(); ?>_submit-uninstall-reason' } ),
749
- beforeSend: function() {
750
  buttonElem.addClass("disabled");
751
- buttonElem.text('Processing...');
752
  },
753
- complete: function() {
754
- window.location.href = redirectTo;
 
 
 
 
755
  }
756
- } );
757
  }
758
- var modal = $( '#<?php echo $this->client->getSlug(); ?>-wapk-dr-modal' ), deactivateLink = '';
759
- $( '#the-list' ).on('click', 'a.<?php echo $this->client->getSlug(); ?>-deactivate-link', function(e) {
760
- e.preventDefault();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
761
  modal.addClass('modal-active');
762
  deactivateLink = $(this).attr('href');
763
  modal.find('a.dont-bother-me').attr('href', deactivateLink).css('float', 'left');
764
  });
765
- modal.on('click', 'button.button-primary', function(e) {
766
- e.preventDefault();
767
- modal.removeClass('modal-active');
768
- }).on('click', 'input[type="radio"]', function () {
769
- modal.find('.reason-input').remove();
770
- var parent = $(this).parents('li:first'),
771
- inputType = parent.data('type'),
772
- inputPlaceholder = parent.data('placeholder'),
773
- reasonInputHtml = '<div class="reason-input">' + ( ( 'text' === inputType ) ? '<input type="text" size="40" />' : '<textarea rows="5" cols="45"></textarea>' ) + '</div>';
774
- if ( inputType !== '' ) {
775
- parent.append( $(reasonInputHtml) );
776
- parent.find('input, textarea').attr('placeholder', inputPlaceholder).focus();
777
- }
778
- }).on('click', '.dont-bother-me', function(e) {
779
- e.preventDefault();
780
- _ajax( { reason_id: 'no-comment', reason_info: 'I rather wouldn\'t say' }, $(this), deactivateLink );
781
- }).on('click', 'button.button-secondary', function(e) {
782
- e.preventDefault();
783
- var $radio = $( 'input[type="radio"]:checked', modal ),
784
- $selected_reason = $radio.parents('li:first'),
785
- $input = $selected_reason.find('textarea, input[type="text"]');
786
- _ajax( {
787
- reason_id: ( 0 === $radio.length ) ? 'none' : $radio.val(),
788
- reason_info: ( 0 !== $input.length ) ? $input.val().trim() : ''
789
- }, $(this), deactivateLink );
790
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
  });
792
  }(jQuery));
793
  </script>
@@ -796,26 +1290,28 @@ class Insights {
796
 
797
  /**
798
  * Run after theme deactivated
799
- * @param string $new_name
800
- * @param WP_Theme $new_theme
801
- * @param WP_Theme $old_theme
 
 
802
  * @return void
803
  */
804
  public function theme_deactivated( $new_name, $new_theme, $old_theme ) {
805
  // Make sure this is WebAppick theme
806
- if( $old_theme->get_template() == $this->client->getSlug() ) {
807
  $current_user = wp_get_current_user();
808
  /** @noinspection PhpUndefinedFieldInspection */
809
- $data = array(
810
  'hash' => $this->client->getHash(),
811
  'reason_id' => 'none',
812
  'reason_info' => json_encode( [
813
  'new_theme' => [
814
- 'name' => $new_name,
815
- 'version' => $new_theme->version,
816
  'parent_theme' => $new_name->parent_theme,
817
- 'author' => $new_name->parent_theme,
818
- ]
819
  ] ),
820
  'site' => $this->__get_site_name(),
821
  'url' => esc_url( home_url() ),
@@ -827,7 +1323,7 @@ class Insights {
827
  'wp' => $this->__get_wp_info(),
828
  'ip_address' => $this->__get_user_ip_address(),
829
  'version' => $this->client->getProjectVersion(),
830
- );
831
  $this->client->send_request( $data, 'reason' );
832
  }
833
  }
@@ -838,9 +1334,14 @@ class Insights {
838
  */
839
  private function __get_user_ip_address() {
840
  $response = wp_remote_get( 'https://icanhazip.com/' );
841
- if( is_wp_error( $response ) ) return '';
 
 
842
  $ip = trim( wp_remote_retrieve_body( $response ) );
843
- if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) return '';
 
 
 
844
  return $ip;
845
  }
846
 
@@ -854,8 +1355,11 @@ class Insights {
854
  $site_name = get_bloginfo( 'description' );
855
  $site_name = wp_trim_words( $site_name, 3, '' );
856
  }
857
- if ( empty( $site_name ) ) $site_name = get_bloginfo( 'url' );
 
 
 
858
  return $site_name;
859
  }
860
  }
861
- // End of file Insights.php
3
  use mysql_xdevapi\Exception;
4
  use WP_Theme;
5
  use WP_User;
6
+ if ( ! defined( 'ABSPATH' ) ) die();
7
  /**
8
  * WebAppick Insights
9
  *
10
+ * @version 1.0.1
11
+ *
12
  * This is a tracker class to track plugin usage based on if the customer has opted in.
13
  * No personal information is being tracked by this class, only general settings, active plugins, environment details
14
  * and admin email.
43
  */
44
  protected $client;
45
 
46
+ /**
47
+ * Flag for checking if the init method is already called.
48
+ * @var bool
49
+ */
50
+ private $didInit = false;
51
+
52
+ /**
53
+ * Email Message Template For sending Support Ticket
54
+ * @var string
55
+ */
56
+ protected $ticketTemplate = '';
57
+
58
+ /**
59
+ * Ticket Email Recipient
60
+ * @var string
61
+ */
62
+ protected $ticketRecipient = '';
63
+
64
+ /**
65
+ * Response to show after support ticket submitted.
66
+ * @var string
67
+ */
68
+ protected $supportResponse = '';
69
+
70
+ protected $supportErrorResponse = '';
71
+ protected $supportURL = '';
72
+
73
  /**
74
  * Initialize the class
75
  *
113
  /**
114
  * Set custom notice text
115
  *
116
+ * @param string $text
117
  *
118
  * @return Insights
119
  */
131
  public function init() {
132
  if ( $this->client->getType() == 'plugin' ) {
133
  $this->init_plugin();
134
+ } elseif ( $this->client->getType() == 'theme' ) {
135
  $this->init_theme();
136
  }
137
+ $this->didInit = true;
138
  }
139
 
140
  /**
142
  *
143
  * @return void
144
  */
145
+ private function init_theme() {
146
  $this->init_common();
147
 
148
+ add_action( 'switch_theme', [ $this, 'deactivation_cleanup' ] );
149
+ add_action( 'switch_theme', [ $this, 'theme_deactivated' ], 12, 3 );
150
  }
151
 
152
  /**
154
  *
155
  * @return void
156
  */
157
+ private function init_plugin() {
158
  // plugin deactivate popup
159
  if ( ! $this->__is_local_server() ) {
160
+ add_action( 'plugin_action_links_' . $this->client->getBasename(), [ $this, 'plugin_action_links' ] );
161
+ add_action( 'admin_footer', [ $this, 'deactivate_scripts' ] );
162
  }
163
 
164
  $this->init_common();
165
 
166
+ register_activation_hook( $this->client->getFile(), [ $this, 'activate_plugin' ] );
167
+ register_deactivation_hook( $this->client->getFile(), [ $this, 'deactivation_cleanup' ] );
168
  }
169
 
170
  /**
175
  protected function init_common() {
176
  if ( $this->show_notice ) {
177
  // tracking notice
178
+ add_action( 'admin_notices', [ $this, 'admin_notice' ] );
179
  }
180
+ add_action( 'admin_init', [ $this, 'handle_optIn_optOut' ] );
181
+ add_action( 'removable_query_args', [ $this, 'add_removable_query_args' ], 10, 1 );
182
  // uninstall reason
183
+ add_action( 'wp_ajax_' . $this->client->getSlug() . '_submit-uninstall-reason', [ $this, 'uninstall_reason_submission' ] );
184
+ add_action( 'wp_ajax_' . $this->client->getSlug() . '_submit-support-ticket', [ $this, 'support_ticket_submission' ] );
185
  // cron events
186
+ add_filter( 'cron_schedules', [ $this, 'add_weekly_schedule' ] );
187
+ add_action( $this->client->getSlug() . '_tracker_send_event', [ $this, 'send_tracking_data' ] );
188
+ //add_action( 'admin_init', [ $this, 'send_tracking_data' ] ); // test
189
  }
190
 
191
  /**
192
  * Send tracking data to WebAppick server
193
  *
194
+ * @param boolean $override
195
  *
196
  * @return void
197
  */
198
  public function send_tracking_data( $override = false ) {
199
  // skip on AJAX Requests
200
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
201
+ return;
202
+ }
203
+ if ( ! $this->is_tracking_allowed() && ! $override ) {
204
+ return;
205
+ }
206
  // Send a maximum of once per week
207
  $last_send = $this->__get_last_send();
208
  /**
209
  * Tracking interval
210
+ *
211
+ * @param string $interval A valid date/time string
212
+ *
213
  * @see strtotime()
214
  * @link https://www.php.net/manual/en/function.strtotime.php
215
  */
216
  $trackingInterval = apply_filters( $this->client->getSlug() . '_tracking_interval', '-1 week' );
217
  try {
218
  $intervalCheck = strtotime( $trackingInterval );
219
+ } catch ( Exception $e ) {
220
  // fallback to default 1 week if filter returned unusable data
221
  $intervalCheck = strtotime( '-1 week' );
222
  }
223
+ if ( $last_send && $last_send > $intervalCheck && ! $override ) {
224
+ return;
225
+ }
226
  $this->client->send_request( $this->get_tracking_data(), 'track' );
227
  update_option( $this->client->getSlug() . '_tracking_last_send', time(), false );
228
  }
233
  * @return array
234
  */
235
  protected function get_tracking_data() {
236
+ $all_plugins = $this->__get_all_plugins();
237
+ $admin_user = $this->__get_admin();
238
  $admin_emails = [ get_option( 'admin_email' ), $admin_user->user_email ];
239
  $admin_emails = array_filter( $admin_emails );
240
  $admin_emails = array_unique( $admin_emails );
241
+ $data = [
242
  'version' => $this->client->getProjectVersion(),
243
  'url' => esc_url( home_url() ),
244
  'site' => $this->__get_site_name(),
254
  'inactive_plugins' => $all_plugins['inactive_plugins'],
255
  'ip_address' => $this->__get_user_ip_address(),
256
  'theme' => get_stylesheet(),
257
+ ];
258
  // for child classes
259
  if ( $extra = $this->get_extra_data() ) {
260
  $data['extra'] = $extra;
261
  }
262
+
263
  return apply_filters( $this->client->getSlug() . '_tracker_data', $data );
264
  }
265
 
282
  esc_html__( 'Server environment details (php, mysql, server, WordPress versions).', 'webappick' ),
283
  ];
284
  $data = apply_filters( $this->client->getSlug() . '_what_tracked', $data );
285
+
286
  return $data;
287
  }
288
 
296
  * @return WP_User
297
  */
298
  private function __get_admin() {
299
+ $admins = get_users( [
300
  'role' => 'administrator',
301
  'orderby' => 'ID',
302
  'order' => 'ASC',
303
  'number' => 1,
304
  'paged' => 1,
305
+ ] );
306
+
307
  return ( is_array( $admins ) && ! empty( $admins ) ) ? $admins[0] : new WP_User();
308
  }
309
 
332
  */
333
  private function __notice_dismissed() {
334
  $hide_notice = get_option( $this->client->getSlug() . '_tracking_notice', 'no' );
335
+ if ( 'hide' == $hide_notice ) {
336
+ return true;
337
+ }
338
+
339
  return false;
340
  }
341
 
345
  * @return boolean
346
  */
347
  private function __is_local_server() {
348
+ return apply_filters( 'WebAppick_is_local', in_array( $_SERVER['REMOTE_ADDR'], [ '127.0.0.1', '::1' ] ) );
349
  }
350
 
351
  /**
375
  * @return void
376
  */
377
  public function admin_notice() {
378
+ if ( $this->__notice_dismissed() ) {
379
+ return;
380
+ }
381
 
382
+ if ( $this->is_tracking_allowed() ) {
383
+ return;
384
+ }
385
+ if ( ! current_user_can( 'manage_options' ) ) {
386
+ return;
387
+ }
388
 
389
  // don't show tracking if a local server
390
  if ( ! $this->__is_local_server() ) {
391
 
392
  if ( empty( $this->notice ) ) {
393
+ $notice = sprintf( apply_filters( $this->client->getSlug() . '_tracking_default_notice_message',
394
+ esc_html__( 'Want to help make %1$s even more awesome? Allow %1$s to collect non-sensitive diagnostic data and usage information.',
395
+ 'webappick' ) ),
396
+ '<strong>' . $this->client->getName() . '</strong>' );
397
  } else {
398
  $notice = $this->notice;
399
  }
400
 
401
+ $notice .= ' (<a class="' . $this->client->getSlug() . '-insights-data-we-collect" href="#">' . esc_html__( 'what we collect',
402
+ 'webappick' ) . '</a>)';
403
+ $notice .= '<p class="description" style="display:none;">' . implode( ', ',
404
+ $this->data_we_collect() ) . '. ' . esc_html__( 'No sensitive data is tracked.',
405
+ 'webappick' ) . '</p>';
406
  echo '<div class="updated"><p>';
407
  echo $notice;
408
  echo '</p><p class="submit">';
409
+ echo '&nbsp;<a href="' . esc_url( $this->get_opt_out_url() ) . '" class="button button-secondary">' . esc_html__( 'No thanks',
410
+ 'webappick' ) . '</a>';
411
+ echo '&nbsp;<a href="' . esc_url( $this->get_opt_in_url() ) . '" class="button button-primary">' . esc_html__( 'Allow',
412
+ 'webappick' ) . '</a>';
413
  echo '</p></div>';
414
  echo "<script type='text/javascript'>jQuery('." . $this->client->getSlug() . "-insights-data-we-collect').on('click', function(e) {
415
  e.preventDefault();
441
  */
442
  public function handle_optIn_optOut() {
443
 
444
+ if ( isset( $_GET[ $this->client->getSlug() . '_tracker_optIn' ] ) && $_GET[ $this->client->getSlug() . '_tracker_optIn' ] == 'true' ) {
445
  $this->optIn();
446
  wp_redirect( remove_query_arg( $this->client->getSlug() . '_tracker_optIn' ) );
447
  exit;
455
 
456
  /**
457
  * Add query vars to removable query args array
458
+ *
459
  * @param array $removable_query_args
460
+ *
461
  * @return array
462
  */
463
  public function add_removable_query_args( $removable_query_args ) {
464
+ return array_merge( $removable_query_args,
465
+ [ $this->client->getSlug() . '_tracker_optIn', $this->client->getSlug() . '_tracker_optOut' ] );
466
  }
467
 
468
  /**
469
  * Tracking optIn
470
  *
471
+ * @param bool $override optional. set send tracking data override setting, ignore last send datetime setting if true.
472
+ *
473
  * @return void
474
+ * @see Insights::send_tracking_data()
475
  */
476
  public function optIn( $override = false ) {
477
  update_option( $this->client->getSlug() . '_allow_tracking', 'yes', false );
495
  /**
496
  * Get the number of post counts
497
  *
498
+ * @param string $post_type
499
  *
500
  * @return integer
501
  */
502
  public function get_post_count( $post_type ) {
503
  global $wpdb;
504
+
505
+ return (int) $wpdb->get_var( $wpdb->prepare( "SELECT count(ID) FROM $wpdb->posts WHERE post_type = %s and post_status = 'publish'",
506
+ $post_type ) );
507
  }
508
 
509
  /**
513
  */
514
  private function __get_server_info() {
515
  global $wpdb;
516
+ $server_data = [
517
+ 'software' => ( isset( $_SERVER['SERVER_SOFTWARE'] ) && ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) ? $_SERVER['SERVER_SOFTWARE'] : 'N/A',
518
+ 'php_version' => ( function_exists( 'phpversion' ) ) ? phpversion() : 'N/A',
519
+ 'mysql_version' => $wpdb->db_version(),
520
+ 'php_execution_time' => @ini_get( 'max_execution_time' ),
521
+ 'php_max_upload_size' => size_format( wp_max_upload_size() ),
522
+ 'php_default_timezone' => date_default_timezone_get(),
523
+ 'php_soap' => class_exists( 'SoapClient' ) ? 'Yes' : 'No',
524
+ 'php_fsockopen' => function_exists( 'fsockopen' ) ? 'Yes' : 'No',
525
+ 'php_curl' => function_exists( 'curl_init' ) ? 'Yes' : 'No',
526
+ 'php_ftp' => function_exists( 'ftp_connect' ) ? 'Yes' : 'No',
527
+ 'php_sftp' => function_exists( 'ssh2_connect' ) ? 'Yes' : 'No',
528
+ ];
529
+
530
  return $server_data;
531
  }
532
 
536
  * @return array
537
  */
538
  private function __get_wp_info() {
539
+ $wp_data = [
540
+ 'memory_limit' => WP_MEMORY_LIMIT,
541
+ 'debug_mode' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? 'Yes' : 'No',
542
+ 'locale' => get_locale(),
543
+ 'version' => get_bloginfo( 'version' ),
544
+ 'multisite' => is_multisite() ? 'Yes' : 'No',
545
+ ];
546
+
547
  return $wp_data;
548
  }
549
 
552
  * @return array
553
  */
554
  private function __get_all_plugins() {
555
+ if ( ! function_exists( 'get_plugins' ) ) {
556
  include ABSPATH . '/wp-admin/includes/plugin.php';
557
  }
558
  $plugins = get_plugins();
559
+ $active_plugins = [];
560
+ $active_plugins_keys = get_option( 'active_plugins', [] );
561
  foreach ( $plugins as $k => $v ) {
562
  // Take care of formatting the data how we want it.
563
+ $formatted = [
564
+ 'name' => strip_tags( $v['Name'] ),
565
+ 'version' => isset( $v['Version'] ) ? strip_tags( $v['Version'] ) : 'N/A',
566
+ 'author' => isset( $v['Author'] ) ? strip_tags( $v['Author'] ) : 'N/A',
567
+ 'network' => isset( $v['Network'] ) ? strip_tags( $v['Network'] ) : 'N/A',
568
+ 'plugin_uri' => isset( $v['PluginURI'] ) ? strip_tags( $v['PluginURI'] ) : 'N/A',
569
+ ];
570
  if ( in_array( $k, $active_plugins_keys ) ) {
571
+ unset( $plugins[ $k ] ); // Remove active plugins from list so we can show active and inactive separately
572
+ $active_plugins[ $k ] = $formatted;
573
  } else {
574
+ $plugins[ $k ] = $formatted;
575
  }
576
  }
577
+
578
+ return [ 'active_plugins' => $active_plugins, 'inactive_plugins' => $plugins ];
579
  }
580
 
581
  /**
584
  * @return array
585
  */
586
  public function __get_user_counts() {
587
+ $user_count = [];
588
  $user_count_data = count_users();
589
  $user_count['total'] = $user_count_data['total_users'];
590
  // Get user count based on user role
591
  foreach ( $user_count_data['avail_roles'] as $role => $count ) {
592
  $user_count[ $role ] = $count;
593
  }
594
+
595
  return $user_count;
596
  }
597
 
598
  /**
599
  * Add weekly cron schedule
600
  *
601
+ * @param array $schedules
602
  *
603
  * @return array
604
  */
605
  public function add_weekly_schedule( $schedules ) {
606
+ $schedules['weekly'] = [
607
  'interval' => DAY_IN_SECONDS * 7,
608
+ 'display' => __( 'Once Weekly', 'webappick' ),
609
+ ];
610
+
611
  return $schedules;
612
  }
613
 
619
  public function activate_plugin() {
620
  $allowed = get_option( $this->client->getSlug() . '_allow_tracking', 'no' );
621
  // if it wasn't allowed before, do nothing
622
+ if ( 'yes' !== $allowed ) {
623
+ return;
624
+ }
625
  // re-schedule and delete the last sent time so we could force send again
626
  wp_schedule_event( time(), 'weekly', $this->client->getSlug() . '_tracker_send_event' );
627
  // wp_schedule_event( time(), 'daily', $this->client->getSlug() . '_license_check_event' );
647
  /**
648
  * Hook into action links and modify the deactivate link
649
  *
650
+ * @param array $links
651
  *
652
  * @return array
653
  */
654
  public function plugin_action_links( $links ) {
655
 
656
  if ( array_key_exists( 'deactivate', $links ) ) {
657
+ $links['deactivate'] = str_replace( '<a',
658
+ '<a class="' . $this->client->getSlug() . '-deactivate-link"',
659
+ $links['deactivate'] );
660
  }
661
 
662
  return $links;
668
  */
669
  private function __get_uninstall_reasons() {
670
 
671
+ $reasons = [
672
+ [
 
673
  'id' => 'could-not-understand',
674
  'text' => esc_html__( 'I couldn\'t understand how to make it work', 'webappick' ),
675
  'type' => 'textarea',
676
  'placeholder' => esc_html__( 'Would you like us to assist you?', 'webappick' ),
677
+ ],
678
+ [
679
  'id' => 'found-better-plugin',
680
  'text' => esc_html__( 'I found a better plugin', 'webappick' ),
681
  'type' => 'text',
682
  'placeholder' => esc_html__( 'Which plugin?', 'webappick' ),
683
+ ],
684
+ [
685
  'id' => 'not-have-that-feature',
686
+ 'text' => esc_html__( 'The plugin is great, but I need specific feature that you don\'t support',
687
+ 'webappick' ),
688
  'type' => 'textarea',
689
  'placeholder' => esc_html__( 'Could you tell us more about that feature?', 'webappick' ),
690
+ ],
691
+ [
692
  'id' => 'is-not-working',
693
  'text' => esc_html__( 'The plugin is not working', 'webappick' ),
694
  'type' => 'textarea',
695
  'placeholder' => esc_html__( 'Could you tell us a bit more whats not working?', 'webappick' ),
696
+ ],
697
+ [
698
  'id' => 'looking-for-other',
699
  'text' => esc_html__( 'It\'s not what I was looking for', 'webappick' ),
700
  'type' => '',
701
  'placeholder' => '',
702
+ ],
703
+ [
704
  'id' => 'did-not-work-as-expected',
705
  'text' => esc_html__( 'The plugin didn\'t work as expected', 'webappick' ),
706
  'type' => 'textarea',
707
  'placeholder' => esc_html__( 'What did you expect?', 'webappick' ),
708
+ ],
709
+ [
710
+ 'id' => 'debugging',
711
+ 'text' => esc_html__( 'Temporary deactivation for debugging', 'webappick' ),
712
+ 'type' => '',
713
+ 'placeholder' => '',
714
+ ],
715
+ [
716
  'id' => 'other',
717
  'text' => esc_html__( 'Other', 'webappick' ),
718
  'type' => 'textarea',
719
  'placeholder' => esc_html__( 'Could you tell us a bit more?', 'webappick' ),
720
+ ],
721
+ ];
722
+ $extra = apply_filters( $this->client->getSlug() . '_extra_uninstall_reasons', [], $reasons );
723
+ if( is_array( $extra ) && ! empty( $extra ) ) {
724
+ // extract the last (other) reason and add after extras
725
+ $other = array_pop( $reasons );
726
+ $reasons = array_merge( $reasons, $extra, [ $other ] );
727
+ }
728
  return $reasons;
729
  }
730
 
731
+ /**
732
+ * Set Support Ticket Template For sending the email query.
733
+ * @param string $ticketTemplate
734
+ *
735
+ * @return Insights
736
+ */
737
+ public function setTicketTemplate( $ticketTemplate ) {
738
+ if( $this->didInit ) {
739
+ _doing_it_wrong( __METHOD__, sprintf( '<code>%s</code> Should be called before Insights::init()', __METHOD__ ), '1.0.1' );
740
+ return $this;
741
+ }
742
+ $this->ticketTemplate = $ticketTemplate;
743
+ return $this;
744
+ }
745
+
746
+ /**
747
+ * Set Ajax Success Response for Support Ticket Submission
748
+ * @param string $supportResponse
749
+ *
750
+ * @return Insights
751
+ */
752
+ public function setSupportResponse( $supportResponse ) {
753
+ if( $this->didInit ) {
754
+ _doing_it_wrong( __METHOD__, sprintf( '<code>%s</code> Should be called before Insights::init()', __METHOD__ ), '1.0.1' );
755
+ return $this;
756
+ }
757
+ $this->supportResponse = $supportResponse;
758
+ return $this;
759
+ }
760
+
761
+ /**
762
+ * @param string $supportErrorResponse
763
+ * @return Insights
764
+ */
765
+ public function setSupportErrorResponse( $supportErrorResponse ) {
766
+ if( $this->didInit ) {
767
+ _doing_it_wrong( __METHOD__, sprintf( '<code>%s</code> Should be called before Insights::init()', __METHOD__ ), '1.0.1' );
768
+ return $this;
769
+ }
770
+ $this->supportErrorResponse = $supportErrorResponse;
771
+ return $this;
772
+ }
773
+
774
+ /**
775
+ * @param string $supportURL
776
+ * @return Insights
777
+ */
778
+ public function setSupportURL( $supportURL ) {
779
+ if( $this->didInit ) {
780
+ _doing_it_wrong( __METHOD__, sprintf( '<code>%s</code> Should be called before Insights::init()', __METHOD__ ), '1.0.1' );
781
+ return $this;
782
+ }
783
+ $this->supportURL = esc_url_raw( $supportURL, [ 'http', 'https' ] );
784
+ return $this;
785
+ }
786
+
787
+ /**
788
+ * Set Ticket Recipient Email
789
+ * @param string $ticketRecipient
790
+ * @return Insights
791
+ */
792
+ public function setTicketRecipient( $ticketRecipient ) {
793
+ if( $this->didInit ) {
794
+ _doing_it_wrong( __METHOD__, sprintf( '<code>%s</code> Should be called before Insights::init()', __METHOD__ ), '1.0.1' );
795
+ return $this;
796
+ }
797
+ if( ! is_email( $ticketRecipient ) ) {
798
+ _doing_it_wrong( __METHOD__, sprintf( '<code>%s</code> Should be called before Insights::init()', __METHOD__ ), '1.0.1' );
799
+ return $this;
800
+ }
801
+ $this->ticketRecipient = sanitize_email( $ticketRecipient );
802
+ return $this;
803
+ }
804
+
805
  /**
806
  * Plugin deactivation uninstall reason submission
807
  *
808
  * @return void
809
  */
810
  public function uninstall_reason_submission() {
811
+
812
  // if ( ! isset( $_POST['reason_id'] ) ) wp_send_json_error();
813
 
814
  $current_user = wp_get_current_user();
815
  global $wpdb;
816
  // @TODO remove deprecated data after server update
817
+ $data = [
818
  'hash' => $this->client->getHash(),
819
+ 'reason_id' => isset( $_REQUEST['reason_id'] ) && ! empty( $_REQUEST['reason_id'] ) ? sanitize_text_field( $_REQUEST['reason_id'] ) : '',
820
+ // WPCS: CSRF ok, Input var ok.
821
+ 'reason_info' => isset( $_REQUEST['reason_info'] ) ? trim( sanitize_textarea_field( $_REQUEST['reason_info'] ) ) : '',
822
+ // WPCS: CSRF ok, Input var ok.
823
  'plugin' => $this->client->getName(),
824
  'site' => $this->__get_site_name(),
825
  'url' => esc_url( home_url() ),
826
  'admin_email' => get_option( 'admin_email' ),
827
  'user_email' => $current_user->user_email,
828
+ 'user_name' => $current_user->display_name,
829
+ // deprecated
830
  'first_name' => ( ! empty( $current_user->first_name ) ) ? $current_user->first_name : $current_user->display_name,
831
  'last_name' => $current_user->last_name,
832
  'server' => $this->__get_server_info(),
833
+ 'software' => $_SERVER['SERVER_SOFTWARE'],
834
+ // deprecated, using $data['server'] for wp info
835
+ 'php_version' => phpversion(),
836
+ // deprecated, using $data['server'] for wp info
837
+ 'mysql_version' => $wpdb->db_version(),
838
+ // deprecated, using $data['server'] for wp info
839
  'wp' => $this->__get_wp_info(),
840
+ 'wp_version' => get_bloginfo( 'version' ),
841
+ // deprecated, using $data['wp'] for wp info
842
+ 'locale' => get_locale(),
843
+ // deprecated, using $data['wp'] for wp info
844
+ 'multisite' => is_multisite() ? 'Yes' : 'No',
845
+ // deprecated, using $data['wp'] for wp info
846
  'ip_address' => $this->__get_user_ip_address(),
847
  'version' => $this->client->getProjectVersion(),
848
+ ];
849
  // Add extra data
850
  if ( $extra = $this->get_extra_data() ) {
851
  $data['extra'] = $extra;
854
  wp_send_json_success();
855
  }
856
 
857
+ public function support_ticket_submission() {
858
+ if( empty( $this->supportResponse ) || empty( $this->ticketTemplate ) || empty( $this->ticketRecipient ) || empty( $this->supportErrorResponse ) || empty( $this->supportURL ) ) {
859
+ wp_send_json_error( sprintf( '<p class="mui-error">%s<br>%s</p>', esc_html__( 'Something Went Wrong.', 'webappick' ), esc_html__( 'Please try again after sometime.', 'webappick' ) ) );
860
+ wp_die(-1 );
861
+ }
862
+ if (
863
+ isset( $_REQUEST['name'], $_REQUEST['email'], $_REQUEST['subject'], $_REQUEST['website'], $_REQUEST['message'] ) &&
864
+ (
865
+ ! empty( sanitize_text_field( $_REQUEST['name'] ) ) &&
866
+ ! empty( sanitize_email( $_REQUEST['email'] ) ) &&
867
+ ! empty( sanitize_text_field( $_REQUEST['subject'] ) ) &&
868
+ ! empty( sanitize_text_field( $_REQUEST['website'] ) ) &&
869
+ ! empty( sanitize_text_field( $_REQUEST['message'] ) )
870
+ )
871
+ ) {
872
+ $headers = [
873
+ 'Content-Type: text/html; charset=UTF-8',
874
+ sprintf( 'From: %s <%s>',
875
+ sanitize_text_field( $_REQUEST['name'] ),
876
+ sanitize_email( $_REQUEST['email'] ) ),
877
+ sprintf( 'Reply-To: %s <%s>',
878
+ sanitize_text_field( $_REQUEST['name'] ),
879
+ sanitize_text_field( $_REQUEST['email'] ) ),
880
+ ];
881
+
882
+ foreach ( $_REQUEST as $k => $v ) {
883
+ $sanitizer = 'sanitize_text_field';
884
+ if( $k == 'email' ) $sanitizer = 'sanitize_email';
885
+ if( $k == 'website' ) $sanitizer = 'esc_url';
886
+ $k = '__' . strtoupper( $k ) . '__';
887
+ $this->ticketTemplate = str_replace( [ $k ], [ call_user_func_array( $sanitizer, [$v] ) ], $this->ticketTemplate );
888
+ $this->supportResponse = str_replace( [ $k ], [ call_user_func_array( $sanitizer, [$v] ) ], $this->supportResponse );
889
+ $this->supportErrorResponse = str_replace( [ $k ], [ call_user_func_array( $sanitizer, [$v] ) ], $this->supportErrorResponse );
890
+ }
891
+
892
+ if ( wp_mail( $this->ticketRecipient, sanitize_text_field( $_REQUEST['subject'] ), sprintf( '<div>%s</div>', $this->ticketTemplate ), $headers ) ) {
893
+ wp_send_json_success( $this->supportResponse );
894
+ } else {
895
+ wp_send_json_error( $this->supportErrorResponse );
896
+ }
897
+ } else {
898
+ wp_send_json_error( sprintf( '<p class="mui-error">%s</p>', esc_html__( 'Missing Required Fields.', 'webappick' ) ) );
899
+ }
900
+ die();
901
+ }
902
+
903
  /**
904
  * Handle the plugin deactivation feedback
905
  *
907
  */
908
  public function deactivate_scripts() {
909
  global $pagenow;
910
+ if ( 'plugins.php' !== $pagenow ) {
911
+ return;
912
+ }
913
+ $reasons = $this->__get_uninstall_reasons();
914
+ $admin_user = $this->__get_admin();
915
+ $displayName = ( ! empty( $admin_user->first_name ) && ! empty( $admin_user->last_name ) ) ? $admin_user->first_name . ' ' . $admin_user->last_name : $admin_user->display_name;
916
+ $showSupportTicket = ( ! empty( $this->ticketTemplate ) && ! empty( $this->supportResponse ) && ! empty( $this->ticketRecipient ) && ! empty( $this->supportErrorResponse ) && ! empty( $this->supportURL) );
917
  ?>
918
+ <div class="wapk-dr-modal" id="<?php echo $this->client->getSlug(); ?>-wapk-dr-modal" aria-label="<?php printf( esc_attr__( '&ldquo;%s&rdquo; Uninstall Confirmation', 'webappick' ), $this->client->getName() ); ?>" role="dialog" aria-modal="true">
919
+ <?php if( $showSupportTicket ) { ?>
920
+ <div class="wapk-dr-modal-wrap support" style="display: none;">
921
+ <div class="wapk-dr-modal-header">
922
+ <h3><?php esc_html_e( 'Submit Support Ticket.', 'webappick' ); ?></h3>
923
+ <a href="javascript:void 0;" class="wapk-dr-modal-close" aria-label="<?php esc_attr_e( 'Close', 'webappick' ); ?>">
924
+ <!--suppress HtmlUnknownAttribute -->
925
+ <svg class="" focusable="false" viewBox="0 0 24 24" aria-hidden="true" role="presentation">
926
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
927
+ </svg>
928
+ </a>
929
+ </div>
930
+ <div class="wapk-dr-modal-body">
931
+ <div class="wapk-row mui col-2 col-left">
932
+ <label for="wapk-support-name" class="<?php echo ! empty( $displayName ) ? 'shrink' : ''; ?>"><?php esc_html_e( 'Name', 'webappick' ); ?></label>
933
+ <div class="wapk-form-control">
934
+ <input type="text" name="name" id="wapk-support-name" value="<?php echo esc_attr( $displayName ); ?>" required>
935
+ </div>
936
+ </div>
937
+ <div class="wapk-row mui col-2 col-right">
938
+ <label for="wapk-support-email" class="shrink"><?php esc_html_e( 'Email', 'webappick' ); ?></label>
939
+ <div class="wapk-form-control">
940
+ <input type="email" name="email" id="wapk-support-email" value="<?php echo esc_attr( $admin_user->user_email ); ?>" required>
941
+ </div>
942
+ </div>
943
+ <div class="clear"></div>
944
+ <div class="wapk-row mui col-2 col-left">
945
+ <label for="wapk-support-subject"><?php esc_html_e( 'Subject', 'webappick' ); ?></label>
946
+ <div class="wapk-form-control">
947
+ <input type="text" name="subject" id="wapk-support-subject" required>
948
+ </div>
949
+ </div>
950
+ <div class="wapk-row mui col-2 col-right">
951
+ <label for="wapk-support-website" class="shrink"><?php esc_html_e( 'Website', 'webappick' ); ?></label>
952
+ <div class="wapk-form-control">
953
+ <input type="url" name="website" id="wapk-support-website" value="<?php echo esc_url( site_url() ); ?>" required>
954
+ </div>
955
+ </div>
956
+ <div class="clear"></div>
957
+ <div class="wapk-row mui">
958
+ <label for="wapk-support-message"><?php esc_html_e( 'Message', 'webappick' ); ?></label>
959
+ <div class="wapk-form-control">
960
+ <textarea id="wapk-support-message" name="message" rows="11" required></textarea>
961
+ </div>
962
+ </div>
963
+ <div class="response">
964
+ <div class="wrapper"></div>
965
+ </div>
966
+ </div>
967
+ <div class="wapk-dr-modal-footer">
968
+ <button class="button button-primary send-ticket"><?php esc_html_e( 'Send Message', 'webappick' ); ?></button>
969
+ <button class="button button-secondary close-ticket"><?php esc_html_e( 'Cancel', 'webappick' ); ?></button>
970
+ </div>
971
+ </div>
972
+ <?php } ?>
973
+ <div class="wapk-dr-modal-wrap reason">
974
  <div class="wapk-dr-modal-header">
975
+ <h3><?php esc_html_e( 'If you have a moment, please let us know why you are deactivating:', 'webappick' ); ?></h3>
976
+ <a href="javascript:void 0;" class="wapk-dr-modal-close" aria-label="<?php esc_attr_e( 'Close', 'webappick' ); ?>">
977
+ <!--suppress HtmlUnknownAttribute -->
978
+ <svg class="" focusable="false" viewBox="0 0 24 24" aria-hidden="true" role="presentation">
979
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
980
+ </svg>
981
+ </a>
982
  </div>
983
  <div class="wapk-dr-modal-body">
984
  <ul class="reasons">
985
+ <?php foreach ( $reasons as $reason ) { ?>
986
+ <li data-type="<?php echo esc_attr( $reason['type'] ); ?>" data-placeholder="<?php echo esc_attr( $reason['placeholder'] ); ?>">
987
+ <label><input type="radio" name="selected-reason" value="<?php echo esc_attr( $reason['id'] ); ?>"> <?php echo esc_html( $reason['text'] ); ?></label>
988
+ </li>
989
+ <?php } ?>
990
  </ul>
991
+ <div class="response" style="<?php echo ( $showSupportTicket ) ? 'display: block;' : ''; ?>">
992
+ <div class="wrapper">
993
+ <?php if( $showSupportTicket ) { ?>
994
+ <p><?php esc_html_e( 'In trouble? Please submit a support request.', 'webappick' ); ?></p>
995
+ <p>
996
+ <a href="#" class="button button-secondary not-interested"><?php esc_html_e( 'Not Interested', 'webappick' ); ?></a>
997
+ <button class="button button-primary open-ticket-form"><?php esc_html_e( 'Open Support Ticket', 'webappick' ); ?></button>
998
+ </p>
999
+ <?php } ?>
1000
+ </div>
1001
+ </div>
1002
  </div>
1003
  <div class="wapk-dr-modal-footer">
1004
+ <a href="#" class="button button-link dont-bother-me disabled"><?php esc_html_e( 'I rather wouldn\'t say', 'webappick' ); ?></a>
1005
+ <button class="button button-secondary deactivate disabled"><?php esc_html_e( 'Submit & Deactivate', 'webappick' ); ?></button>
1006
+ <button class="button button-primary modal-close disabled"><?php esc_html_e( 'Cancel', 'webappick' ); ?></button>
1007
  </div>
1008
  </div>
1009
  </div>
1010
+ <!--suppress CssUnusedSymbol, CssInvalidPseudoSelector, CssFloatPxLength -->
1011
  <style type="text/css">
1012
+ .wapk-dr-modal, .wapk-dr-modal * { box-sizing: border-box; }
1013
+ .wapk-dr-modal { position: fixed; z-index: 99999; top: 0; right: 0; bottom: 0; left: 0; background: rgba(0, 0, 0, 0.5); display: none; }
1014
  .wapk-dr-modal.modal-active { display: block; }
1015
+ .wapk-dr-modal strong, .wapk-dr-modal b { font-weight: bold; }
1016
  .wapk-dr-modal-wrap { width: 475px; position: relative; margin: 10% auto; background: #fff; }
1017
+ .wapk-dr-modal-wrap { position: absolute; display: block; top: 0; left: 0; right: 0; /*bottom: 0;*/ z-index: 99; }
1018
+ .wapk-dr-modal-wrap.support { z-index: 999; margin: 6% auto; }
1019
+ .wapk-dr-modal-wrap .response { position: absolute; display: none; background: rgba(0, 150, 136, 0.68); top: 0; left: 0; right: 0; bottom: 0; overflow: hidden; }
1020
+ .wapk-dr-modal .response.show { display: block; }
1021
+ .wapk-dr-modal-wrap .response .wrapper { display: flex; align-items: center; justify-content: center; width: calc(100% - 40px); height: calc(100% - 40px); flex-flow: column; padding: 40px 40px; margin: 20px 20px; text-align: center; background: #FFF; }
1022
+ .wapk-dr-modal .reason .response .wrapper { width: calc(100% - 80px); height: calc(100% - 80px); flex-flow: column; padding: 40px 40px; margin: 40px; }
1023
+ .wapk-dr-modal .button .dashicons { margin: 4px 0; }
1024
+ .wapk-dr-modal-header { border-bottom: 1px solid #eee; padding: 8px 40px 8px 20px; position: relative; display: block; width: 100%; float: left; }
1025
  .wapk-dr-modal-header h3 { line-height: 150%; margin: 0; }
1026
+ .wapk-dr-modal-close { position: absolute; top: 50%; right: 10px; transform: translateY(-50%); width: 20px; height: 20px; line-height: 0; }
1027
+ .wapk-dr-modal-close svg { font-size: 20px; display: inline-block; width: 1em; height: 1em; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
1028
+ .wapk-dr-modal-body { padding: 5px 20px 20px 20px; position: relative; display: block; width: 100%; float: left; box-sizing: border-box; }
1029
  .wapk-dr-modal-body .reason-input { margin-top: 5px; margin-left: 20px; }
1030
+ .wapk-dr-modal-footer { border-top: 1px solid #eee; padding: 12px 20px; text-align: right; position: relative; display: block; width: 100%; float: left; }
1031
+ .wapk-dr-modal-footer a, .wapk-dr-modal-footer button { vertical-align: middle; }
1032
+ .support .wapk-dr-modal-footer { text-align: left; }
1033
+ .support .wapk-dr-modal-footer button { float: right; margin-left: 10px; }
1034
+ .wapk-dr-modal .wapk-row { position: relative; width: 100%; display: block; box-sizing: border-box; float: left; margin: 4px auto; }
1035
+ .mui { border: 0; margin: 0; display: inline-flex; padding: 0; min-width: 0; flex-direction: column; vertical-align: top; }
1036
+ .wapk-dr-modal .wapk-row.col-2 { width: calc(50% - 16px); }
1037
+ .wapk-dr-modal .wapk-row.col-3 { width: calc(calc(100% / 3) - 16px); }
1038
+ .wapk-dr-modal .wapk-row.col-left { margin-right: 8px; }
1039
+ .wapk-dr-modal .wapk-row.col-center { margin-left: 8px; margin-right: 8px; }
1040
+ .wapk-dr-modal .wapk-row.col-right { margin-left: 8px; }
1041
+ .wapk-dr-modal .mui .wapk-form-control { cursor: text; display: inline-flex; position: relative; font-size: 1rem; box-sizing: border-box; align-items: center; line-height: 1.1875em; width: 100%; }
1042
+ .wapk-dr-modal .mui label { color: rgba(0, 0, 0, 0.54); padding: 0; font-size: 1rem; font-weight: 400; line-height: 1; letter-spacing: 0.00938em; display: block; transform-origin: top left; top: 0; left: 0; position: absolute; transform: translate(0, 24px) scale(1); transition: color 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms, transform 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms; }
1043
+ .wapk-dr-modal .mui label.focused { color: #007cba; }
1044
+ p:not(.helper-text).mui-error, div:not(.helper-text).mui-error, .wapk-dr-modal .mui label.mui-error { color: #f44336; }
1045
+ p:not(.helper-text).mui-error, div:not(.helper-text).mui-error { padding: 5px 10px; border: 1px solid #f44336; font-weight: bold; }
1046
+ .wapk-dr-modal .mui label.shrink { transform: translate(0, 1.5px) scale(0.75); transform-origin: top left; }
1047
+ .wapk-dr-modal .mui label + .wapk-form-control { margin-top: 16px; }
1048
+ .wapk-dr-modal .mui .wapk-form-control:before { left: 0; right: 0; bottom: 0; content: "\00a0"; position: absolute; transition: border-bottom-color 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; border-bottom: 1px solid rgba(0, 0, 0, 0.42); pointer-events: none; }
1049
+ .wapk-dr-modal .mui .wapk-form-control:hover:not(.disabled):before { border-bottom: 2px solid rgba(0, 0, 0, 0.87); }
1050
+ .wapk-dr-modal .mui .wapk-form-control:after { left: 0; right: 0; bottom: 0; content: ""; position: absolute; transform: scaleX(0); transition: transform 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms; border-bottom: 2px solid #007cba; pointer-events: none; }
1051
+ .wapk-dr-modal .mui .wapk-form-control.focused:after { transform: scaleX(1); }
1052
+ .wapk-dr-modal .mui .wapk-form-control.mui-error:after { transform: scaleX(1); border-bottom-color: #f44336; }
1053
+ .wapk-dr-modal .mui .wapk-form-control input,
1054
+ .wapk-dr-modal .mui .wapk-form-control textarea { font: inherit; color: currentColor; width: 100%; border: 0; height: 1.1875em; min-height: auto; margin: 0; display: block; padding: 6px 0 7px; min-width: 0; background: none; box-sizing: content-box; -webkit-tap-highlight-color: transparent; }
1055
+ .wapk-dr-modal .mui .wapk-form-control input { animation-name: mui-keyframes-auto-fill-cancel; }
1056
+ .wapk-dr-modal .mui .wapk-form-control input:-moz-autofill,
1057
+ .wapk-dr-modal .mui .wapk-form-control input:-webkit-autofill { animation-name: mui-keyframes-auto-fill; animation-duration: 5000s; }
1058
+ @-webkit-keyframes mui-keyframes-auto-fill {}
1059
+ @-webkit-keyframes mui-keyframes-auto-fill-cancel {}
1060
+ .wapk-dr-modal .mui .wapk-form-control textarea { height: auto; resize: none; padding: 0; }
1061
+ .wapk-dr-modal .mui .wapk-form-control input::-webkit-search-decoration,
1062
+ .wapk-dr-modal .mui .wapk-form-control textarea::-webkit-search-decoration { -webkit-appearance: none; }
1063
+ .wapk-dr-modal .mui .wapk-form-control input::-webkit-input-placeholder,
1064
+ .wapk-dr-modal .mui .wapk-form-control textarea::-webkit-input-placeholder { color: currentColor; opacity: 0.42; transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; }
1065
+ .wapk-dr-modal .mui .wapk-form-control input::-moz-placeholder,
1066
+ .wapk-dr-modal .mui .wapk-form-control textarea::-moz-placeholder { color: currentColor; opacity: 0.42; transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; }
1067
+ .wapk-dr-modal .mui .wapk-form-control input:-ms-input-placeholder,
1068
+ .wapk-dr-modal .mui .wapk-form-control textarea:-ms-input-placeholder { color: currentColor; opacity: 0.42; transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; }
1069
+ .wapk-dr-modal .mui .wapk-form-control input::-ms-input-placeholder,
1070
+ .wapk-dr-modal .mui .wapk-form-control textarea::-ms-input-placeholder { color: currentColor; opacity: 0.42; transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; }
1071
+ .wapk-dr-modal .mui .wapk-form-control input:focus,
1072
+ .wapk-dr-modal .mui .wapk-form-control input:invalid,
1073
+ .wapk-dr-modal .mui .wapk-form-control textarea:focus,
1074
+ .wapk-dr-modal .mui .wapk-form-control textarea:invalid { outline: 0; box-shadow: none; }
1075
+ .wapk-dr-modal .mui .helper-text { color: rgba(0, 0, 0, 0.54); margin: 8px 0 0 0; font-size: 0.75rem; min-height: 1em; text-align: left; font-weight: 400; line-height: 1em; letter-spacing: 0.03333em; }
1076
+ .wapk-dr-modal .mui .helper-text.contained { margin: 8px 14px 0; }
1077
+ .wapk-dr-modal .mui .helper-text.mui-error { color: #f44336; }
1078
+ .wapk-dr-modal .button.disabled, .wapk-dr-modal button.disabled { cursor: not-allowed !important; }
1079
+ /*.wapk-dr-modal .wapk-row input, .wapk-dr-modal .wapk-row textarea { width: calc( 100% - 10px ); margin: 0 5px; display: block; vertical-align: middle; box-sizing: border-box; float: left; }*/
1080
  </style>
1081
  <!--suppress ES6ConvertVarToLetConst, JSUnresolvedVariable -->
1082
  <script type="text/javascript">
1083
+ (function ($) {
1084
+ $(function () {
1085
  /**
1086
  * Ajax Helper For Submitting Deactivation Reasons
1087
  * @param {Object} data
1088
  * @param {*|jQuery|HTMLElement} buttonElem
1089
+ * @param {String|Function} cb
1090
  * @returns {*|jQuery}
1091
  * @private
1092
  */
1093
+ function _ajax(data, buttonElem, cb) {
1094
+ if (buttonElem.hasClass('disabled')) return;
1095
+ buttonElem.attr('data-label', buttonElem.text());
1096
+ return $.ajax({
1097
  url: ajaxurl,
1098
  type: 'POST',
1099
+ data: $.fn.extend({}, {action: '<?php echo $this->client->getSlug(); ?>_submit-uninstall-reason'}, data), // add default action if action is empty.
1100
+ beforeSend: function () {
1101
  buttonElem.addClass("disabled");
1102
+ buttonElem.text('<?php esc_html_e( 'Processing...', 'webappick' ); ?>');
1103
  },
1104
+ complete: function (event, xhr, options) {
1105
+ if ('string' === typeof cb) {
1106
+ window.location.href = cb;
1107
+ } else if ('function' === typeof cb) {
1108
+ cb({event: event, xhr: xhr, options: options});
1109
+ }
1110
  }
1111
+ });
1112
  }
1113
+ // Variables
1114
+ var modal = $('#<?php echo $this->client->getSlug(); ?>-wapk-dr-modal'),
1115
+ deactivateLink = '',
1116
+ reason = modal.find('.reason'),
1117
+ support = modal.find('.support'),
1118
+ supportResponse = support.find('.response'),
1119
+ reasonResponse = reason.find('.response'),
1120
+ mui = $('.mui input, .mui textarea, .mui select'),
1121
+ // sendButton = modal.find('.send-ticket'),
1122
+ validMessage = false,
1123
+ preventDefault = function ( e ) { e && e.preventDefault() },
1124
+ responseButtons = modal.find('.reason .wapk-dr-modal-footer .button'),
1125
+ supportURL = '<?php echo esc_url( $this->supportURL ); ?>',
1126
+ closeModal = function (e) {
1127
+ preventDefault(e);
1128
+ var buttons = modal.find('.button');
1129
+ modal.removeClass('modal-active');
1130
+ // modal.find('.wapk-dr-modal-wrap').show();
1131
+ supportResponse.hide().find('.wrapper').html('');
1132
+ reasonResponse.show();
1133
+ support.hide();
1134
+ // enable buttons and restore original labels
1135
+ buttons.removeClass('disabled');
1136
+ responseButtons.addClass('disabled');
1137
+ buttons.each(function () {
1138
+ var self = $(this), label = self.attr('data-label');
1139
+ if (label) self.text(label);
1140
+ });
1141
+ modal.find('input[type="radio"]').prop('checked', false );
1142
+ $('.reason-input').remove();
1143
+ },
1144
+ checkMessageValidity = function (e) {
1145
+ // e.target.checkValidity();
1146
+ var target = e && e.target ? e.target : this;
1147
+ var self = $(this), currentMui = self.closest('.mui'),
1148
+ label = currentMui.find('label'),
1149
+ control = currentMui.find('.wapk-form-control');
1150
+ if (target.checkValidity()) {
1151
+ if (label.hasClass('mui-error')) label.removeClass('mui-error');
1152
+ if (control.hasClass('mui-error')) control.removeClass('mui-error');
1153
+ currentMui.find('p.helper-text').hide().remove();
1154
+ validMessage = true;
1155
+ }
1156
+ },
1157
+ resetTicketForm = function( clearValues, clearAll ) {
1158
+ $('p.helper-text.mui-error').remove();
1159
+ $('.mui-error').removeClass('mui-error');
1160
+ if( clearValues ) {
1161
+ if( clearAll ) mui.val('');
1162
+ else modal.find('#wapk-support-message,#wapk-support-subject').val('');
1163
+ }
1164
+ };
1165
+ // The MUI
1166
+ {
1167
+ // any input el except radio, checkbox and select
1168
+ mui
1169
+ .not('select')
1170
+ .not('[type="checkbox"]')
1171
+ .not('[type="radio"]')
1172
+ .on('focus', function () {
1173
+ var self = $(this), currentMui = self.closest('.mui'),
1174
+ label = currentMui.find('label'),
1175
+ control = currentMui.find('.wapk-form-control');
1176
+ control.addClass('focused');
1177
+ label.addClass('focused');
1178
+ label.addClass('shrink');
1179
+ })
1180
+ .on('blur', function () {
1181
+ var self = $(this), currentMui = self.closest('.mui'),
1182
+ label = currentMui.find('label'),
1183
+ control = currentMui.find('.wapk-form-control');
1184
+ control.removeClass('focused');
1185
+ label.removeClass('focused');
1186
+ if (self.val() === '') {
1187
+ label.removeClass('shrink');
1188
+ }
1189
+ });
1190
+ // any input el in mui
1191
+ mui
1192
+ .on('blur', checkMessageValidity)
1193
+ .on('invalid', function (e) {
1194
+ preventDefault(e);
1195
+ var self = $(this), currentMui = self.closest('.mui'),
1196
+ label = currentMui.find('label'),
1197
+ control = currentMui.find('.wapk-form-control');
1198
+ currentMui.find('p.helper-text').remove();
1199
+ validMessage = false;
1200
+ if (!label.hasClass('mui-error')) label.addClass('mui-error');
1201
+ if (!control.hasClass('mui-error')) control.addClass('mui-error');
1202
+ control.after('<p class="helper-text mui-error">' + e.target.validationMessage + '</p>');
1203
+ });
1204
+ }
1205
+ // the clicker
1206
+ $('#the-list').on('click', 'a.<?php echo $this->client->getSlug(); ?>-deactivate-link', function (e) {
1207
+ preventDefault(e);
1208
  modal.addClass('modal-active');
1209
  deactivateLink = $(this).attr('href');
1210
  modal.find('a.dont-bother-me').attr('href', deactivateLink).css('float', 'left');
1211
  });
1212
+ // The Modal
1213
+ modal
1214
+ .on( 'click', '.not-interested', function(e) {
1215
+ preventDefault(e);
1216
+ $( this ).closest('.response').slideUp( function() {
1217
+ responseButtons.removeClass('disabled');
1218
+ } );
1219
+ } )
1220
+ .on( 'click', '.open-ticket-form', function(e){
1221
+ preventDefault(e);
1222
+ support.slideDown();
1223
+ resetTicketForm( true );
1224
+ } )
1225
+ .on( 'click', '.close-ticket', function (e) {
1226
+ preventDefault(e);
1227
+ support.slideUp();
1228
+ //if( ! reasonResponse.is(':visible') ) responseButtons.removeClass('disabled');
1229
+ } )
1230
+ .on( 'click', '.modal-close, .wapk-dr-modal-close', closeModal )
1231
+ .on( 'click', 'input[type="radio"]', function () {
1232
+ modal.find('.reason-input').remove();
1233
+ var parent = $(this).parents('li:first'),
1234
+ inputType = parent.data('type'),
1235
+ inputPlaceholder = parent.data('placeholder'),
1236
+ reasonInputHtml = '<div class="reason-input">' + (('text' === inputType) ? '<input type="text" size="40" />' : '<textarea rows="5" cols="45"></textarea>') + '</div>';
1237
+ if (inputType !== '') {
1238
+ parent.append($(reasonInputHtml));
1239
+ parent.find('input, textarea').attr('placeholder', inputPlaceholder).focus();
1240
+ }
1241
+ } )
1242
+ .on( 'click', '.dont-bother-me', function (e) {
1243
+ preventDefault(e);
1244
+ _ajax({
1245
+ reason_id: 'no-comment',
1246
+ reason_info: 'I rather wouldn\'t say'
1247
+ }, $(this), deactivateLink);
1248
+ } )
1249
+ .on( 'click', '.deactivate', function (e) {
1250
+ preventDefault(e);
1251
+ var $radio = $('input[type="radio"]:checked', modal),
1252
+ $selected_reason = $radio.parents('li:first'),
1253
+ $input = $selected_reason.find('textarea, input[type="text"]');
1254
+ _ajax({
1255
+ reason_id: (0 === $radio.length) ? 'none' : $radio.val(),
1256
+ reason_info: (0 !== $input.length) ? $input.val().trim() : ''
1257
+ }, $(this), deactivateLink);
1258
+ } )
1259
+ .on( 'click', '.send-ticket', function (e) {
1260
+ preventDefault(e);
1261
+ mui.each(checkMessageValidity);
1262
+ if (!validMessage) return;
1263
+ var buttonElem = $(this),
1264
+ __BTN_TEXT__ = buttonElem.text(),
1265
+ data = {action: '<?php echo $this->client->getSlug(); ?>_submit-support-ticket',};
1266
+ mui.each( function () { data[$(this).attr('name')] = $(this).val() } );
1267
+ _ajax(data, $(this), function (jqXhr) {
1268
+ var response = jqXhr.event.responseJSON;
1269
+ if ( response.hasOwnProperty('data') ) {
1270
+ supportResponse.find('.wrapper').html(response.data);
1271
+ supportResponse.show();
1272
+ }
1273
+ if( response.success ) {
1274
+ modal.find('#wapk-support-message,#wapk-support-subject').val('');
1275
+ } else {
1276
+ setTimeout( function() {
1277
+ window.open( supportURL, '_blank' );
1278
+ // supportResponse.slideUp();
1279
+ buttonElem.hasClass('disabled') && buttonElem.removeClass('disabled');
1280
+ }, 5000 );
1281
+ }
1282
+ buttonElem.text(__BTN_TEXT__);
1283
+ });
1284
+ });
1285
  });
1286
  }(jQuery));
1287
  </script>
1290
 
1291
  /**
1292
  * Run after theme deactivated
1293
+ *
1294
+ * @param string $new_name
1295
+ * @param WP_Theme $new_theme
1296
+ * @param WP_Theme $old_theme
1297
+ *
1298
  * @return void
1299
  */
1300
  public function theme_deactivated( $new_name, $new_theme, $old_theme ) {
1301
  // Make sure this is WebAppick theme
1302
+ if ( $old_theme->get_template() == $this->client->getSlug() ) {
1303
  $current_user = wp_get_current_user();
1304
  /** @noinspection PhpUndefinedFieldInspection */
1305
+ $data = [
1306
  'hash' => $this->client->getHash(),
1307
  'reason_id' => 'none',
1308
  'reason_info' => json_encode( [
1309
  'new_theme' => [
1310
+ 'name' => $new_name,
1311
+ 'version' => $new_theme->version,
1312
  'parent_theme' => $new_name->parent_theme,
1313
+ 'author' => $new_name->parent_theme,
1314
+ ],
1315
  ] ),
1316
  'site' => $this->__get_site_name(),
1317
  'url' => esc_url( home_url() ),
1323
  'wp' => $this->__get_wp_info(),
1324
  'ip_address' => $this->__get_user_ip_address(),
1325
  'version' => $this->client->getProjectVersion(),
1326
+ ];
1327
  $this->client->send_request( $data, 'reason' );
1328
  }
1329
  }
1334
  */
1335
  private function __get_user_ip_address() {
1336
  $response = wp_remote_get( 'https://icanhazip.com/' );
1337
+ if ( is_wp_error( $response ) ) {
1338
+ return '';
1339
+ }
1340
  $ip = trim( wp_remote_retrieve_body( $response ) );
1341
+ if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
1342
+ return '';
1343
+ }
1344
+
1345
  return $ip;
1346
  }
1347
 
1355
  $site_name = get_bloginfo( 'description' );
1356
  $site_name = wp_trim_words( $site_name, 3, '' );
1357
  }
1358
+ if ( empty( $site_name ) ) {
1359
+ $site_name = get_bloginfo( 'url' );
1360
+ }
1361
+
1362
  return $site_name;
1363
  }
1364
  }
1365
+ // End of file Insights.php
libs/WebAppick/AppServices/License.php CHANGED
@@ -15,6 +15,12 @@ class License {
15
  */
16
  protected $client;
17
 
 
 
 
 
 
 
18
  /**
19
  * Arguments of create menu
20
  *
@@ -56,9 +62,24 @@ class License {
56
  * @var boolean
57
  */
58
  private $is_valid_license = null;
59
-
 
 
 
60
  protected $license;
61
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  /**
63
  * Initialize the class
64
  *
@@ -70,73 +91,184 @@ class License {
70
  $this->option_key = 'WebAppick_' . md5( $this->client->getSlug() ) . '_manage_license';
71
 
72
  $this->schedule_hook = $this->client->getSlug() . '_license_check_event';
73
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  // Run hook to check license status daily
75
  add_action( $this->schedule_hook, array( $this, 'check_license_status' ) );
76
-
77
- // Active/Deactivate corn schedule
78
- $this->run_schedule();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
 
81
  /**
82
  * Check license
83
- * @param string $license_key
84
  * @return array
85
  */
86
- public function check( $license_key ) {
87
- $route = 'public/license/' . $this->client->getHash() . '/check';
88
-
89
- return $this->send_request( $license_key, $route );
 
 
 
 
 
 
 
 
 
 
90
  }
91
 
92
  /**
93
  * Active a license
94
- * @param $license_key
95
  * @return array
96
  */
97
- public function activate( $license_key ) {
98
- $route = 'public/license/' . $this->client->getHash() . '/activate';
99
-
100
- return $this->send_request( $license_key, $route );
101
  }
102
 
103
  /**
104
- * Deactivate a license
105
- * @param string $license_key
106
  * @return array
107
  */
108
- public function deactivate( $license_key ) {
109
- $route = 'public/license/' . $this->client->getHash() . '/deactivate';
110
-
111
- return $this->send_request( $license_key, $route );
112
  }
113
 
114
  /**
115
  * Send common request
116
  *
117
- * @param $license_key
118
- * @param $route
119
  *
120
  * @return array
121
  */
122
- protected function send_request( $license_key, $route ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  $params = [
124
- 'license_key' => $license_key,
125
- 'url' => esc_url( home_url() ),
 
 
 
 
 
126
  ];
127
- $response = $this->client->send_request( $params, $route, true );
 
 
128
  if ( ! is_wp_error( $response ) ) {
129
- $response = json_decode( wp_remote_retrieve_body( $response ), true );
130
- if ( empty( $response ) || isset( $response['exception'] )) {
 
 
 
 
 
 
 
131
  return [
132
  'success' => false,
133
  'error' => esc_html__( 'Unknown error occurred, Please try again.', 'webappick' ),
134
  ];
135
  }
136
- if ( isset( $response['errors'] ) && isset( $response['errors']['license_key'] ) ) {
137
  $response = [
138
  'success' => false,
139
- 'error' => $response['errors']['license_key'][0],
140
  ];
141
  }
142
  return $response;
@@ -148,6 +280,31 @@ class License {
148
  }
149
  }
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  /**
152
  * Add settings page for license
153
  *
@@ -156,10 +313,14 @@ class License {
156
  * @return void
157
  */
158
  public function add_settings_page( $args = array() ) {
 
 
 
 
159
  $defaults = [
160
  'type' => 'menu', // Can be: menu, options, submenu
161
- 'page_title' => esc_html__( 'Manage License', 'webappick' ),
162
- 'menu_title' => esc_html__( 'Manage License', 'webappick' ),
163
  'capability' => 'manage_options',
164
  'menu_slug' => $this->client->getSlug() . '-manage-license',
165
  'icon_url' => '',
@@ -194,24 +355,23 @@ class License {
194
  * License menu output
195
  */
196
  public function menu_output() {
197
- if ( isset( $_POST['submit'] ) ) $this->license_page_form( $_POST );
198
  $this->licenses_style();
199
- $license = $this->getLicense();
200
  ?>
201
- <div class="wrap wapk-license-settings-wrapper">
202
- <h1><?php esc_html_e( 'License Settings', 'webappick' ); ?></h1>
203
- <?php
204
- $this->show_license_page_notices();
205
- do_action( 'before_webappick_license_section' );
206
- ?>
207
  <div class="webappick-license-settings webappick-license-section">
208
  <?php $this->show_license_page_card_header(); ?>
209
  <div class="webappick-license-details">
 
210
  <p><?php
211
  printf( esc_html__( 'Active %s by your license key to get professional support and automatic update from your WordPress dashboard.', 'webappick' ), '<strong>'.$this->client->getName().'</strong>' );
212
  ?></p>
 
213
  <form method="post" action="<?php $this->formActionUrl(); ?>" novalidate="novalidate" spellcheck="false" autocomplete="off">
214
- <input type="hidden" name="_action" value="<?php echo $license['status']; ?>">
215
  <div class="license-input-fields">
216
  <div class="license-input-key">
217
  <svg enable-background="new 0 0 512 512" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
@@ -219,23 +379,20 @@ class License {
219
  </svg>
220
  <label for="license_key" class="screen-reader-text"><?php esc_html_e( 'License Key', 'webappick' ); ?></label>
221
  <input class="regular-text" id="license_key" type="text"
222
- value="<?php echo $this->get_input_license_value( $license['status'], $license ); ?>"
223
  placeholder="<?php esc_attr_e( 'Enter your license key to activate', 'webappick' ); ?>"
224
- name="license_key"<?php readonly( ( 'deactivate' == $license['status'] ), true, true ) ?>>
225
  </div>
226
- <button type="submit" name="submit" class="<?php printf( esc_attr( '%s-button' ), $license['status'] );?>"><?php
227
- $license['status'] == 'active' ? esc_html_e( 'Activate License', 'webappick' ) : esc_html_e( 'Deactivate License', 'webappick' ) ;
228
  ?></button>
 
229
  </div>
230
  </form>
231
- <?php
232
- if ( 'deactivate' == $license['status'] && isset( $license['remaining'] ) ) {
233
- $this->show_active_license_info( $license );
234
- }
235
- ?>
236
  </div>
237
  </div> <!-- /.webappick-license-settings -->
238
- <?php do_action( 'after_webappick_license_section' ); ?>
239
  </div>
240
  <?php
241
  }
@@ -244,10 +401,10 @@ class License {
244
  * License form submit
245
  * @param array $postData $_POST Data
246
  */
247
- private function license_page_form( $postData ) {
248
  switch ( $postData['_action'] ) {
249
- case 'active':
250
- $this->active_client_license( $postData );
251
  break;
252
  case 'deactivate':
253
  $this->deactivate_client_license();
@@ -263,12 +420,29 @@ class License {
263
  public function check_license_status() {
264
  $license = $this->getLicense();
265
  if ( $license ) {
266
- $response = $this->check( $license['key'] );
267
- if ( isset( $response['success'] ) && $response['success'] ) {
268
- $license['status'] = 'activate';
 
 
 
 
 
 
 
269
  } else {
270
- $license['status'] = 'deactivate';
 
 
 
 
 
 
 
271
  }
 
 
 
272
  $this->setLicense( $license );
273
  }
274
  }
@@ -280,9 +454,9 @@ class License {
280
  if ( null !== $this->is_valid_license ) {
281
  return $this->is_valid_license;
282
  }
283
-
284
- $license = $this->getLicense();
285
- if ( $license && $license['status'] == 'activate' ) {
286
  $this->is_valid_license = true;
287
  } else {
288
  $this->is_valid_license = false;
@@ -291,6 +465,43 @@ class License {
291
  return $this->is_valid_license;
292
  }
293
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  /**
295
  * Styles for licenses page
296
  */
@@ -298,166 +509,73 @@ class License {
298
  ?>
299
  <!--suppress CssUnusedSymbol -->
300
  <style>
301
- .wapk-license-section {
302
- width: 100%;
303
- max-width: 1100px;
304
- min-height: 1px;
305
- box-sizing: border-box;
306
- }
307
- .wapk-license-settings {
308
- background-color: #fff;
309
- box-shadow: 0 3px 10px rgba(16, 16, 16, 0.05);
310
- }
311
- .wapk-license-settings * {
312
- box-sizing: border-box;
313
- }
314
- .wapk-license-title {
315
- background-color: #F8FAFB;
316
- border-bottom: 2px solid #EAEAEA;
317
- display: flex;
318
- align-items: center;
319
- padding: 10px 20px;
320
- }
321
- .wapk-license-title svg {
322
- width: 30px;
323
- height: 30px;
324
- fill: #0082BF;
325
- }
326
- .wapk-license-title span {
327
- font-size: 17px;
328
- color: #444444;
329
- margin-left: 10px;
330
- }
331
- .wapk-license-details {
332
- padding: 20px;
333
- }
334
- .wapk-license-details p {
335
- font-size: 15px;
336
- margin: 0 0 20px 0;
337
- }
338
- .license-input-key {
339
- position: relative;
340
- flex: 0 0 72%;
341
- max-width: 72%;
342
- }
343
- .license-input-key input {
344
- background-color: #F9F9F9;
345
- padding: 10px 15px 10px 48px;
346
- border: 1px solid #E8E5E5;
347
- border-radius: 3px;
348
- height: 45px;
349
- font-size: 16px;
350
- color: #71777D;
351
- width: 100%;
352
- box-shadow: 0 0 0 transparent;
353
- }
354
- .license-input-key input:focus {
355
- outline: 0 none;
356
- border: 1px solid #E8E5E5;
357
- box-shadow: 0 0 0 transparent;
358
- }
359
- .license-input-key svg {
360
- width: 22px;
361
- height: 22px;
362
- fill: #0082BF;
363
- position: absolute;
364
- left: 14px;
365
- top: 13px;
366
- }
367
- .license-input-fields {
368
- display: flex;
369
- justify-content: space-between;
370
- margin-bottom: 30px;
371
- max-width: 850px;
372
- width: 100%;
373
- }
374
- .license-input-fields button {
375
- color: #fff;
376
- font-size: 17px;
377
- padding: 8px;
378
- height: 46px;
379
- background-color: #0082BF;
380
- border-radius: 3px;
381
- cursor: pointer;
382
- flex: 0 0 25%;
383
- max-width: 25%;
384
- border: 1px solid #0082BF;
385
- }
386
- .license-input-fields button.deactivate-button {
387
- background-color: #E40055;
388
- border-color: #E40055;
389
- }
390
- .license-input-fields button:focus {
391
- outline: 0 none;
392
- }
393
- .active-license-info {
394
- display: flex;
395
- }
396
- .single-license-info {
397
- min-width: 220px;
398
- flex: 0 0 30%;
399
- }
400
- .single-license-info h3 {
401
- font-size: 18px;
402
- margin: 0 0 12px 0;
403
- }
404
- .single-license-info p {
405
- margin: 0;
406
- color: #00C000;
407
- }
408
- .single-license-info p.occupied {
409
- color: #E40055;
410
- }
411
  </style>
412
  <?php
413
  }
414
 
415
  /**
416
  * Show active license information
417
- * @param array $license
418
  * @return void
419
  */
420
- private function show_active_license_info( $license ) {
 
 
 
 
 
421
  ?>
422
  <div class="active-license-info">
423
  <div class="single-license-info">
424
- <h3><?php esc_html_e( 'Activation Remaining', 'webappick' ); ?></h3>
425
- <?php if ( empty( $license['activation_limit'] ) ): ?>
426
- <p><?php esc_html_e( 'Unlimited', 'webappick' ); ?></p>
427
- <?php else: ?>
428
- <p class="<?php echo $license['remaining'] ? '' : 'occupied'; ?>"><?php
429
- printf( esc_html__( '%1$d out of %2$d', 'webappick' ), $license['remaining'], $license['activation_limit'] );
430
- ?></p>
431
- <?php endif; ?>
432
  </div>
 
433
  <div class="single-license-info">
434
- <h3><?php esc_html_e( 'Expires in,', 'webappick' ); ?></h3>
435
- <p class="<?php echo $license['expiry_days'] > 10 ? '' : 'occupied'; ?>"><?php printf( _n( '%d day', '%d days', $license['expiry_days'], 'webappick' ), $license['expiry_days'] ); ?></p>
436
  </div>
437
- </div>
438
- <?php
439
- }
440
-
441
- /**
442
- * Show license settings page notices
443
- * @return void
444
- */
445
- private function show_license_page_notices() {
446
- if ( ! empty( $this->error ) ) :
447
- ?>
448
- <div class="notice notice-error is-dismissible wapk-license-section">
449
- <p><?php echo $this->error; ?></p>
450
  </div>
451
- <?php
452
- endif;
453
- if ( ! empty( $this->success ) ) :
454
- ?>
455
- <div class="notice notice-success is-dismissible wapk-license-section">
456
- <p><?php echo $this->success; ?></p>
 
 
 
 
 
 
457
  </div>
 
458
  <?php
459
- endif;
460
- echo '<br />';
461
  }
462
 
463
  /**
@@ -466,7 +584,7 @@ class License {
466
  */
467
  private function show_license_page_card_header() {
468
  ?>
469
- <div class="wapk-license-title">
470
  <svg enable-background="new 0 0 299.995 299.995" version="1.1" viewBox="0 0 300 300" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
471
  <path d="m150 161.48c-8.613 0-15.598 6.982-15.598 15.598 0 5.776 3.149 10.807 7.817 13.505v17.341h15.562v-17.341c4.668-2.697 7.817-7.729 7.817-13.505 0-8.616-6.984-15.598-15.598-15.598z"/>
472
  <path d="m150 85.849c-13.111 0-23.775 10.665-23.775 23.775v25.319h47.548v-25.319c-1e-3 -13.108-10.665-23.775-23.773-23.775z"/>
@@ -482,44 +600,64 @@ class License {
482
  * @param array $postData $_POST Data
483
  * @return void
484
  */
485
- private function active_client_license( $postData ) {
486
  if ( empty( $postData['license_key'] ) ) {
487
  $this->error = esc_html__('The license key field is required.', 'webappick' );
488
  return;
489
  }
490
-
491
- $license_key = sanitize_text_field( $postData['license_key'] );
492
- $response = $this->activate( $license_key );
493
-
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  if ( ! $response['success'] ) {
495
  $this->error = $response['error'] ? $response['error'] : esc_html__( 'Unknown error occurred.', 'webappick' );
496
  return;
497
  }
498
-
499
- $this->setLicense( [
500
- 'key' => $license_key,
501
- 'status' => 'activate',
502
- 'remaining' => $response['remaining'],
503
- 'activation_limit' => $response['activation_limit'],
504
- 'expiry_days' => $response['expiry_days'],
505
- ] );
506
- $this->success = esc_html__( 'License activated successfully.', 'webappick' );
 
 
 
 
 
 
507
  }
508
 
509
  /**
510
  * deactivate client license
511
  */
512
  private function deactivate_client_license() {
513
-
514
- $license = $this->getLicense();
515
- if ( ! $license ) {
516
  $this->error = esc_html__( 'License key not found.', 'webappick' );
517
- return;
518
- }
519
- $response = $this->deactivate( $license['key'] );
520
- if ( ! $response['success'] ) {
521
- $this->error = $response['error'] ? $response['error'] : esc_html__( 'Unknown error occurred.', 'webappick' );
522
- return;
 
 
 
523
  }
524
  $this->setLicense();
525
  $this->success = esc_html__( 'License deactivated successfully.', 'webappick' );
@@ -530,7 +668,7 @@ class License {
530
  */
531
  private function add_menu_page() {
532
  add_menu_page(
533
- $this->menu_args['page_title'], $this->menu_args['menu_title'],
534
  $this->menu_args['capability'], $this->menu_args['menu_slug'],
535
  array( $this, 'menu_output' ),
536
  $this->menu_args['icon_url'], $this->menu_args['position']
@@ -542,8 +680,8 @@ class License {
542
  */
543
  private function add_submenu_page() {
544
  add_submenu_page(
545
- $this->menu_args['parent_slug'], $this->menu_args['page_title'],
546
- $this->menu_args['menu_title'], $this->menu_args['capability'],
547
  $this->menu_args['menu_slug'], array( $this, 'menu_output' )
548
  );
549
  }
@@ -553,7 +691,7 @@ class License {
553
  */
554
  private function add_options_page() {
555
  add_options_page(
556
- $this->menu_args['page_title'], $this->menu_args['menu_title'],
557
  $this->menu_args['capability'], $this->menu_args['menu_slug'],
558
  array( $this, 'menu_output' )
559
  );
@@ -577,22 +715,42 @@ class License {
577
  }
578
 
579
  /**
580
- * Enable/Disable schedule
 
581
  */
582
- private function run_schedule() {
583
  switch ( $this->client->getType() ) {
584
  case 'plugin':
585
  register_activation_hook( $this->client->getFile(), array( $this, 'schedule_cron_event' ) );
586
  register_deactivation_hook( $this->client->getFile(), array( $this, 'clear_scheduler' ) );
 
587
  break;
588
 
589
  case 'theme':
590
- add_action( 'after_switch_theme', array( $this, 'schedule_cron_event' ) );
591
- add_action( 'switch_theme', array( $this, 'clear_scheduler' ) );
 
592
  break;
593
  }
594
  }
595
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
  /**
597
  * Form action URL
598
  */
@@ -610,19 +768,23 @@ class License {
610
  * @return string
611
  */
612
  private function get_input_license_value( $action, $license ) {
613
- if ( 'deactivate' != $action ) return '';
614
- $key_length = strlen( $license['key'] );
615
- return str_pad( substr( $license['key'], 0, $key_length / 2 ), $key_length, '*' );
 
616
  }
617
 
618
  /**
619
  * get Plugin/Theme License
620
- * @return bool|array
621
  */
622
  private function getLicense() {
623
  if( $this->license !== null ) return $this->license;
624
- $license = get_option( $this->option_key, null );
625
- $this->license = isset( $license['key'], $license['status'] ) && ! empty( $license['key'] && ! empty( $license['status'] ) ) ? $license : false;
 
 
 
626
  return $this->license;
627
  }
628
 
@@ -641,20 +803,42 @@ class License {
641
  */
642
  private function setLicense( $license = [] ) {
643
  $defaults = [
644
- 'key' => '',
645
- 'status' => 'deactivate',
646
- 'remaining' => 0,
647
- 'activation_limit' => 0,
648
- 'expiry_days' => 0,
 
 
 
649
  ];
650
- $this->license = wp_parse_args( $license, $defaults );
 
651
  // sanitize data
652
- $this->license['status'] = strtolower( $license['status'] ) === 'activate' ? 'activate' : 'deactivate';
653
- $this->license['remaining'] = absint( $license['remaining'] );
654
- $this->license['activation_limit'] = absint( $license['activation_limit'] );
655
- $this->license['expiry_days'] = absint( $license['expiry_days'] );
 
 
 
 
656
  // update in db
657
  return update_option( $this->option_key, $this->license, false );
658
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
659
  }
660
  // End of file License.php
15
  */
16
  protected $client;
17
 
18
+ /**
19
+ * Flag for checking if the init method is already called.
20
+ * @var bool
21
+ */
22
+ private $didInit = false;
23
+
24
  /**
25
  * Arguments of create menu
26
  *
62
  * @var boolean
63
  */
64
  private $is_valid_license = null;
65
+ /**
66
+ * The license data
67
+ * @var array
68
+ */
69
  protected $license;
70
 
71
+ /**
72
+ * Current User Permission for managing License
73
+ * @var bool
74
+ */
75
+ protected $currentUserCanManage = false;
76
+
77
+ /**
78
+ * Is Current Page is the license manage page
79
+ * @var bool
80
+ */
81
+ protected $isLicensePage = false;
82
+
83
  /**
84
  * Initialize the class
85
  *
91
  $this->option_key = 'WebAppick_' . md5( $this->client->getSlug() ) . '_manage_license';
92
 
93
  $this->schedule_hook = $this->client->getSlug() . '_license_check_event';
94
+ }
95
+
96
+ /**
97
+ * Initialize License
98
+ *
99
+ * @return void
100
+ */
101
+ public function init() {
102
+ // load the license
103
+ $this->getLicense();
104
+ // handle license page form before anything
105
+ if ( isset( $_POST['submit'], $_POST['_action'] ) ) {
106
+ $this->handle_license_page_form( $_POST );
107
+ }
108
+ // check the validity and save the state
109
+ $this->is_valid();
110
  // Run hook to check license status daily
111
  add_action( $this->schedule_hook, array( $this, 'check_license_status' ) );
112
+ $this->currentUserCanManage = current_user_can( $this->menu_args['capability'] );
113
+ $this->isLicensePage = isset( $_GET['page'] ) && $_GET['page'] === $this->menu_args['menu_slug'];
114
+ add_action( 'plugin_action_links_' . $this->client->getBasename(), [ $this, 'plugin_action_links' ] );
115
+ add_action( 'admin_notices', array( $this, '__admin_notices' ), 10 );
116
+ // Activation/Deactivation hooks
117
+ $this->activation_deactivation();
118
+ $this->didInit = true;
119
+ }
120
+
121
+ public function __admin_notices() {
122
+ if ( ! $this->currentUserCanManage ) return;
123
+ if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL === true ) {
124
+ $host = parse_url( $this->__getLicenceAPI(), PHP_URL_HOST );
125
+ if ( ! defined( 'WP_ACCESSIBLE_HOSTS' ) || ( defined( 'WP_ACCESSIBLE_HOSTS' ) && false === stristr( WP_ACCESSIBLE_HOSTS, $host ) ) ) {
126
+ ?><div class="notice notice-error">
127
+ <p><?php printf( __( '<b>Warning!</b> You\'re blocking external requests which means you won\'t be able to get %s updates. Please add %s to %s.', 'webappick' ), $this->client->getName(), '<strong>' . $host . '</strong>', '<code>WP_ACCESSIBLE_HOSTS</code>' ); ?></p>
128
+ </div><?php
129
+ }
130
+ }
131
+ if ( ! $this->isLicensePage && ! $this->is_valid() ) { ?>
132
+ <div class="notice notice-error">
133
+ <p><?php printf( __( 'The <strong>%s</strong> API Key has not been activated, so the %s is inactive! %sClick here%s to activate <strong>%s</strong>.', 'webappick' ), esc_attr( $this->client->getName() ), esc_attr( $this->client->getType() ), '<a href="' . esc_url( admin_url( 'admin.php?page=' . $this->menu_args['menu_slug'] ) ) . '">', '</a>', esc_attr( $this->client->getName() ) ); ?></p>
134
+ </div>
135
+ <?php }
136
+ if ( ! empty( $this->error ) ) {
137
+ ?>
138
+ <div class="notice notice-error is-dismissible">
139
+ <p><?php echo $this->error; ?></p>
140
+ </div>
141
+ <?php
142
+ }
143
+ if ( ! empty( $this->success ) ) {
144
+ ?>
145
+ <div class="notice notice-success is-dismissible">
146
+ <p><?php echo $this->success; ?></p>
147
+ </div>
148
+ <?php
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Setup plugin action link to the license page
154
+ * @param array $links
155
+ * @return array
156
+ */
157
+ public function plugin_action_links( $links ) {
158
+ if( ! empty( $this->menu_args['menu_slug'] ) && ! empty( $this->menu_args['menu_title'] ) ) {
159
+ /** @noinspection HtmlUnknownTarget */
160
+ $links[] = sprintf( '<a href="%s">%s</a>', esc_url( admin_url( 'admin.php?page=' . $this->menu_args['menu_slug'] ) ), esc_html( $this->menu_args['menu_title'] ) );
161
+ }
162
+ return $links;
163
  }
164
 
165
  /**
166
  * Check license
 
167
  * @return array
168
  */
169
+ public function check() {
170
+ return $this->send_request( 'status', $this->license );
171
+ }
172
+
173
+ /**
174
+ * Check Plugin Update
175
+ * @return array
176
+ */
177
+ public function check_update() {
178
+ return $this->send_request( 'update', $this->license );
179
+ }
180
+
181
+ public function get_information() {
182
+ return $this->send_request( 'information', $this->license );
183
  }
184
 
185
  /**
186
  * Active a license
187
+ * @param array $license
188
  * @return array
189
  */
190
+ public function activate( $license ) {
191
+ return $this->send_request( 'activate', $license );
 
 
192
  }
193
 
194
  /**
195
+ * Deactivate current license
 
196
  * @return array
197
  */
198
+ public function deactivate() {
199
+ return $this->send_request( 'deactivate', $this->license );
 
 
200
  }
201
 
202
  /**
203
  * Send common request
204
  *
205
+ * @param string $action request action
206
+ * @param array $license license data
207
  *
208
  * @return array
209
  */
210
+ protected function send_request( $action, $license = [] ) {
211
+ // WC-AM Valid Actions and response data types
212
+ $actions = [
213
+ 'activate' => 'json',
214
+ 'deactivate' => 'json',
215
+ 'status' => 'json',
216
+ 'information' => 'json',
217
+ 'update' => 'json',
218
+ 'plugininformation' => 'serialize', // serialize option doesn't provide success status
219
+ 'pluginupdatecheck' => 'serialize',
220
+ ];
221
+ if( ! in_array( $action, array_keys( $actions ) ) ) {
222
+ return [
223
+ 'success' => false,
224
+ 'error' => esc_html__( 'Invalid Request Action.', 'webappick' ),
225
+ ];
226
+ }
227
+ // parse license data
228
+ $license = wp_parse_args( $license, $this->getLicense() );
229
+ if( empty( $license['key'] ) || empty( $license['instance'] ) ) {
230
+ return [
231
+ 'success' => false,
232
+ 'error' => esc_html__( 'Invalid/Empty License Data.', 'webappick' ),
233
+ ];
234
+ }
235
+ if( empty( $this->client->getProjectId() ) && empty( $this->client->getName() ) ) {
236
+ return [
237
+ 'success' => false,
238
+ 'error' => esc_html__( 'A valid project name/id is required.', 'webappick' ),
239
+ ];
240
+ }
241
  $params = [
242
+ 'object' => str_ireplace( array( 'http://', 'https://' ), '', home_url() ),
243
+ 'api_key' => $license['key'],
244
+ 'version' => $this->client->getProjectVersion(),
245
+ 'instance' => $license['instance'],
246
+ 'product_id' => $this->client->getName(),//$this->client->getProjectId(),
247
+ 'plugin_name' => $this->client->getBasename(),
248
+ 'wc_am_action' => $action,
249
  ];
250
+ $this->setAPI_URL();
251
+ $response = $this->client->send_request( $params, '', true );
252
+ $this->restoreAPI_URL();
253
  if ( ! is_wp_error( $response ) ) {
254
+ $response = wp_remote_retrieve_body( $response );
255
+ if( $actions[$action] == 'json' ) {
256
+ $response = json_decode( $response, true );
257
+ } else {
258
+ $response = maybe_unserialize( $response );
259
+ //@TODO check wc-am error ..
260
+ return $response;
261
+ }
262
+ if ( empty( $response ) || ! isset( $response['success'] ) ) {
263
  return [
264
  'success' => false,
265
  'error' => esc_html__( 'Unknown error occurred, Please try again.', 'webappick' ),
266
  ];
267
  }
268
+ if ( ! $response['success'] ) {
269
  $response = [
270
  'success' => false,
271
+ 'error' => isset( $response['error'] ) ? sanitize_text_field( $response['error'] ) : esc_html__( 'Unknown error occurred in API server.', 'webappick' ),
272
  ];
273
  }
274
  return $response;
280
  }
281
  }
282
 
283
+ /**
284
+ * License API URL
285
+ * @return string
286
+ */
287
+ public function __getLicenceAPI() {
288
+ return 'https://webappick.com/?wc-api=wc-am-api';
289
+ }
290
+
291
+ /**
292
+ * Filter api url for licensing api
293
+ * @return void
294
+ */
295
+ private function setAPI_URL() {
296
+ add_filter( $this->client->getSlug() . '_WebAppick_API_URL', [ $this, '__getLicenceAPI' ], 10 );
297
+ }
298
+
299
+ /**
300
+ * Remove filter for changing wpi url
301
+ * @see License::setAPI_URL()
302
+ * @return void
303
+ */
304
+ private function restoreAPI_URL() {
305
+ remove_filter( $this->client->getSlug() . '_WebAppick_API_URL', [ $this, '__getLicenceAPI' ], 10 );
306
+ }
307
+
308
  /**
309
  * Add settings page for license
310
  *
313
  * @return void
314
  */
315
  public function add_settings_page( $args = array() ) {
316
+ if( $this->didInit ) {
317
+ _doing_it_wrong( __METHOD__, sprintf( '<code>%s</code> Should be called before License::init()', __METHOD__ ), '1.0.1' );
318
+ return;
319
+ }
320
  $defaults = [
321
  'type' => 'menu', // Can be: menu, options, submenu
322
+ 'page_title' => __( 'Manage License', 'webappick' ),
323
+ 'menu_title' => __( 'Manage License', 'webappick' ),
324
  'capability' => 'manage_options',
325
  'menu_slug' => $this->client->getSlug() . '-manage-license',
326
  'icon_url' => '',
355
  * License menu output
356
  */
357
  public function menu_output() {
 
358
  $this->licenses_style();
359
+ $action = ( isset( $this->license['status'] ) && 'active' == $this->license['status'] ) ? 'deactivate' : 'activate';
360
  ?>
361
+ <div class="wrap webappick-license-settings-wrapper">
362
+ <h1 class="wp-heading-inline"><?php esc_html_e( 'License Settings', 'webappick' ); ?></h1>
363
+ <hr class="wp-header-end">
364
+ <?php do_action( 'before_webappick_' . $this->client->getSlug() . '_license_section' ); ?>
 
 
365
  <div class="webappick-license-settings webappick-license-section">
366
  <?php $this->show_license_page_card_header(); ?>
367
  <div class="webappick-license-details">
368
+ <?php if( $action == 'activate' ) { ?>
369
  <p><?php
370
  printf( esc_html__( 'Active %s by your license key to get professional support and automatic update from your WordPress dashboard.', 'webappick' ), '<strong>'.$this->client->getName().'</strong>' );
371
  ?></p>
372
+ <?php } ?>
373
  <form method="post" action="<?php $this->formActionUrl(); ?>" novalidate="novalidate" spellcheck="false" autocomplete="off">
374
+ <input type="hidden" name="_action" value="<?php echo esc_attr( $action ); ?>">
375
  <div class="license-input-fields">
376
  <div class="license-input-key">
377
  <svg enable-background="new 0 0 512 512" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
379
  </svg>
380
  <label for="license_key" class="screen-reader-text"><?php esc_html_e( 'License Key', 'webappick' ); ?></label>
381
  <input class="regular-text" id="license_key" type="text"
382
+ value="<?php echo $this->get_input_license_value( $action, $this->license ); ?>"
383
  placeholder="<?php esc_attr_e( 'Enter your license key to activate', 'webappick' ); ?>"
384
+ name="license_key"<?php readonly( ( 'deactivate' == $action ), true, true ) ?> autocomplete="off">
385
  </div>
386
+ <button type="submit" name="submit" class="<?php printf( esc_attr( '%s-button' ), $action );?>"><?php
387
+ $action == 'activate' ? esc_html_e( 'Activate License', 'webappick' ) : esc_html_e( 'Deactivate License', 'webappick' ) ;
388
  ?></button>
389
+ <a href="http://webappick.com/my-account/api-keys/" class="button button-primary button-hero" style="margin-left: 20px;font-size: 17px;line-height: 2.5;" target="_blank"><?php esc_html_e( 'Manage License', 'webappick' ); ?></a>
390
  </div>
391
  </form>
392
+ <?php $this->show_active_license_info(); ?>
 
 
 
 
393
  </div>
394
  </div> <!-- /.webappick-license-settings -->
395
+ <?php do_action( 'after_webappick_' . $this->client->getSlug() . '_license_section' ); ?>
396
  </div>
397
  <?php
398
  }
401
  * License form submit
402
  * @param array $postData $_POST Data
403
  */
404
+ private function handle_license_page_form( $postData ) {
405
  switch ( $postData['_action'] ) {
406
+ case 'activate':
407
+ $this->activate_client_license( $postData );
408
  break;
409
  case 'deactivate':
410
  $this->deactivate_client_license();
420
  public function check_license_status() {
421
  $license = $this->getLicense();
422
  if ( $license ) {
423
+ $response = $this->check();
424
+ if ( isset( $response['success'], $response['status_check'] ) && $response['success'] ) {
425
+ $license = [
426
+ 'status' => $response['status_check'] == 'active' ? 'active' : 'inactive',
427
+ 'remaining' => isset( $response['data'], $response['data']['activations_remaining'] ) ? $response['data']['activations_remaining'] : 0,
428
+ 'activations' => isset( $response['data'], $response['data']['total_activations'] ) ? $response['data']['total_activations'] : 0,
429
+ 'limit' => isset( $response['data'], $response['data']['total_activations_purchased'] ) ? $response['data']['total_activations_purchased'] : 0,
430
+ 'unlimited' => isset( $response['data'], $response['data']['unlimited_activations'] ) ? $response['data']['unlimited_activations'] : false,
431
+ 'expiry_date' => 0, // wc-am doesn't sent remaining date
432
+ ];
433
  } else {
434
+ $license = [
435
+ 'status' => 'inactive',
436
+ 'remaining' => 0,
437
+ 'activations' => 0,
438
+ 'limit' => 0,
439
+ 'unlimited' => false,
440
+ 'expiry_date' => 0, // wc-am doesn't sent remaining date
441
+ ];
442
  }
443
+
444
+ // Don't reset the key.
445
+ // keep it, if the user renew subscription update the status and reactivate the plugin.
446
  $this->setLicense( $license );
447
  }
448
  }
454
  if ( null !== $this->is_valid_license ) {
455
  return $this->is_valid_license;
456
  }
457
+ // load the license if already not loaded
458
+ $this->getLicense();
459
+ if ( isset( $this->license['status'] ) && $this->license['status'] == 'active' ) {
460
  $this->is_valid_license = true;
461
  } else {
462
  $this->is_valid_license = false;
465
  return $this->is_valid_license;
466
  }
467
 
468
+ /**
469
+ * Read WooCommerce API Manager Data, Convert to new license format and save in db
470
+ * @return bool
471
+ */
472
+ public function migrate_license_from_wc_am() {
473
+ // if( false === get_option( $this->option_key . '_wc_am_migrated', false ) ) return false;
474
+ // is already migrated
475
+ // api manager data prefix
476
+ $wcAmPrefix = str_ireplace( array( ' ', '_', '&', '?' ), '_', strtolower( $this->client->getName() ) );
477
+ /*// WC AM data structure
478
+ [
479
+ '_data' => [ 'api_key', 'activation_email', ], // api key & email
480
+ '_product_id' => '', // product title or name
481
+ '_instance' => '', // instance key unique id
482
+ '_activated' => '', // activation status => Activated|Deactivated
483
+ '_deactivate_checkbox' => '', // deactivation check box state > On|Off
484
+ ];*/
485
+ $license = [
486
+ 'key' => '',
487
+ 'status' => 'deactivate', // activate
488
+ 'instance' => '', // max len 190
489
+ ];
490
+ // get key
491
+ $data = get_option( $wcAmPrefix . '_data', false );
492
+ if( $data && isset( $data['api_key'] ) ) $license['key'] = $data['api_key'];
493
+ // instance id
494
+ $data = get_option( $wcAmPrefix . '_instance', false );
495
+ if( $data ) $license['instance'] = $data;
496
+ // activation status
497
+ $data = get_option( $wcAmPrefix . '_activated', false );
498
+ if( $data ) $license['status'] = strtolower( $data ) === 'activated' ? 'active' : 'inactive'; // Deactivated
499
+ $this->setLicense( $license );
500
+ $this->check_license_status();
501
+ update_option( $this->option_key . '_wc_am_migrated', 1, false );
502
+ return true;
503
+ }
504
+
505
  /**
506
  * Styles for licenses page
507
  */
509
  ?>
510
  <!--suppress CssUnusedSymbol -->
511
  <style>
512
+ .webappick-license-settings *{-webkit-box-sizing:border-box;box-sizing:border-box}
513
+ .webappick-license-settings{margin-top:20px;background-color:#fff;-webkit-box-shadow:0 3px 10px rgba(16,16,16,.05);box-shadow:0 3px 10px rgba(16,16,16,.05)}
514
+ .webappick-license-section{width:100%;min-height:1px;-webkit-box-sizing:border-box;box-sizing:border-box}
515
+ .webappick-license-title{background-color:#f8fafb;border-bottom:2px solid #eaeaea;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:10px 20px}
516
+ .webappick-license-title svg{width:30px;height:30px;fill:#0082bf}
517
+ .webappick-license-title span{font-size:17px;color:#444;margin-left:10px}
518
+ .webappick-license-details{padding:20px}
519
+ .webappick-license-details p{font-size:15px;margin:0 0 20px 0}
520
+ .license-input-key{position:relative;-webkit-box-flex:0;-ms-flex:0 0 72%;flex:0 0 72%;max-width:72%}
521
+ .license-input-key input{background-color:#f9f9f9;padding:10px 15px 10px 48px;border:1px solid #e8e5e5;border-radius:3px;height:45px;font-size:16px;color:#71777d;width:100%;-webkit-box-shadow:0 0 0 transparent;box-shadow:0 0 0 transparent}
522
+ .license-input-key input:focus{outline:0 none;border:1px solid #e8e5e5;-webkit-box-shadow:0 0 0 transparent;box-shadow:0 0 0 transparent}
523
+ .license-input-key svg{width:22px;height:22px;fill:#0082bf;position:absolute;left:14px;top:13px}
524
+ .license-input-fields{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin:20px 0;max-width:850px;width:100%}
525
+ .license-input-fields button{margin-left:20px;color:#fff;font-size:17px;padding:8px;height:46px;background-color:#0082bf;border-radius:3px;cursor:pointer;-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%;border:1px solid #0082bf}
526
+ .license-input-fields button.deactivate-button{background-color:#e40055;border-color:#e40055}
527
+ .license-input-fields button:focus{outline:0 none}
528
+ .active-license-info{display:-webkit-box;display:-ms-flexbox;display:flex}
529
+ .single-license-info{margin-right:40px}
530
+ .single-license-info:last-child{margin-right:0}
531
+ .single-license-info h3{font-size:15px;margin:0 0 12px 0;display:inline-block}
532
+ .single-license-info p{margin:0 0 0 5px;font-size:15px;font-weight:500;display:inline-block}
533
+ .single-license-info p.active{color:#047167}
534
+ .single-license-info p.inactive{color:#e40055}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  </style>
536
  <?php
537
  }
538
 
539
  /**
540
  * Show active license information
 
541
  * @return void
542
  */
543
+ private function show_active_license_info() {
544
+ $status = ( isset( $this->license['status'] ) && $this->license['status'] === 'active' ) ? 'active' : 'inactive';
545
+ $limit = isset( $this->license['limit'] ) ? $this->license['limit'] : 0;
546
+ $activations = isset( $this->license['activations'] ) ? $this->license['activations'] : 0;
547
+ $remaining = isset( $this->license['remaining'] ) ? $this->license['remaining'] : 0;
548
+ $unlimited = isset( $this->license['unlimited'] ) ? $this->license['unlimited'] : false;
549
  ?>
550
  <div class="active-license-info">
551
  <div class="single-license-info">
552
+ <h3><?php esc_html_e( 'Status:', 'webappick' ); ?></h3>
553
+ <p class="<?php echo esc_attr( $status ); ?>"><?php $status == 'active' ? esc_html_e( 'Active', 'webappick' ) : esc_html_e( 'Inactive', 'webappick' ); ?></p>
 
 
 
 
 
 
554
  </div>
555
+ <?php if ( false !== $unlimited ) { ?>
556
  <div class="single-license-info">
557
+ <h3><?php esc_html_e( 'Activation Limit:', 'webappick' ); ?></h3>
558
+ <p class="active"><?php esc_html_e( 'Unlimited', 'webappick' ); ?></p>
559
  </div>
560
+ <div class="single-license-info">
561
+ <h3><?php esc_html_e( 'Total Activation:', 'webappick' ); ?></h3>
562
+ <p class="active"><?php echo $activations; ?></p>
 
 
 
 
 
 
 
 
 
 
563
  </div>
564
+ <?php } else { ?>
565
+ <div class="single-license-info">
566
+ <h3><?php esc_html_e( 'Activation Remaining:', 'webappick' ); ?></h3>
567
+ <p class="<?php echo $remaining ? 'active' : 'inactive'; ?>"><?php
568
+ if( $status == 'active' ) printf( esc_html__( '%1$d out of %2$d', 'webappick' ), $remaining, $limit );
569
+ else esc_html_e( 'N/A', 'webappick' );
570
+ ?></p>
571
+ </div>
572
+ <?php } ?>
573
+ <div class="single-license-info">
574
+ <h3><?php esc_html_e( 'Automatic Update:', 'webappick' ); ?></h3>
575
+ <p class="<?php echo $status; ?>"><?php $status == 'active' ? esc_html_e( 'Enabled', 'webappick' ) : esc_html_e( 'Disabled', 'webappick' ); ?></p>
576
  </div>
577
+ </div>
578
  <?php
 
 
579
  }
580
 
581
  /**
584
  */
585
  private function show_license_page_card_header() {
586
  ?>
587
+ <div class="webappick-license-title">
588
  <svg enable-background="new 0 0 299.995 299.995" version="1.1" viewBox="0 0 300 300" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
589
  <path d="m150 161.48c-8.613 0-15.598 6.982-15.598 15.598 0 5.776 3.149 10.807 7.817 13.505v17.341h15.562v-17.341c4.668-2.697 7.817-7.729 7.817-13.505 0-8.616-6.984-15.598-15.598-15.598z"/>
590
  <path d="m150 85.849c-13.111 0-23.775 10.665-23.775 23.775v25.319h47.548v-25.319c-1e-3 -13.108-10.665-23.775-23.773-23.775z"/>
600
  * @param array $postData $_POST Data
601
  * @return void
602
  */
603
+ private function activate_client_license( $postData ) {
604
  if ( empty( $postData['license_key'] ) ) {
605
  $this->error = esc_html__('The license key field is required.', 'webappick' );
606
  return;
607
  }
608
+ $key = sanitize_text_field( $postData['license_key'] );
609
+ $license = (array) $this->getLicense();
610
+ // check if it's a change request
611
+ $updateKey = ( isset( $this->license['key'] ) && $key === $this->license['key'] ) ? true : false;
612
+ if( $updateKey ) {
613
+ $deactivate = $this->deactivate(); // deactivate first.
614
+ if ( ! $deactivate['success'] ) {
615
+ $check = $this->check(); // check api status
616
+ if( $check['success'] && $check['status_check'] != 'inactive' ) {
617
+ $this->error = $deactivate['error'] ? $deactivate['error'] : esc_html__( 'Unknown error occurred.', 'webappick' );
618
+ return;
619
+ }
620
+ }
621
+ }
622
+ $license['key'] = $key;
623
+ if( empty( $license['instance'] ) ) $license['instance'] = $this->generateInstanceId();
624
+ $response = $this->activate( $license );
625
  if ( ! $response['success'] ) {
626
  $this->error = $response['error'] ? $response['error'] : esc_html__( 'Unknown error occurred.', 'webappick' );
627
  return;
628
  }
629
+ // Don't reset the key.
630
+ // keep it, if the user renew subscription update the status and reactivate the plugin.
631
+ $this->setLicense( array_merge( $license, [
632
+ 'status' => isset( $response['activated'] ) && $response['activated'] ? 'active' : 'inactive',
633
+ 'remaining' => isset( $response['data'], $response['data']['activations_remaining'] ) ? $response['data']['activations_remaining'] : 0,
634
+ 'activations' => isset( $response['data'], $response['data']['total_activations'] ) ? $response['data']['total_activations'] : 0,
635
+ 'limit' => isset( $response['data'], $response['data']['total_activations_purchased'] ) ? $response['data']['total_activations_purchased'] : 0,
636
+ 'unlimited' => isset( $response['data'], $response['data']['unlimited_activations'] ) ? $response['data']['unlimited_activations'] : false,
637
+ 'expiry_date' => 0, // wc-am doesn't sent remaining date
638
+ ] ) );
639
+ if( ! $updateKey ) {
640
+ $this->success = esc_html__( 'License activated successfully.', 'webappick' );
641
+ } else {
642
+ $this->success = esc_html__( 'License Updated successfully.', 'webappick' );
643
+ }
644
  }
645
 
646
  /**
647
  * deactivate client license
648
  */
649
  private function deactivate_client_license() {
650
+ if ( ! isset( $this->license['key'] ) || empty( $this->license['key'] ) ) {
 
 
651
  $this->error = esc_html__( 'License key not found.', 'webappick' );
652
+ } else {
653
+ $response = $this->deactivate();
654
+ if ( ! $response['success'] ) {
655
+ // check api status
656
+ $check = $this->check();
657
+ if( $check['success'] && $check['status_check'] != 'inactive' ) {
658
+ $this->error = $response['error'] ? $response['error'] : esc_html__( 'Unknown error occurred.', 'webappick' );
659
+ }
660
+ }
661
  }
662
  $this->setLicense();
663
  $this->success = esc_html__( 'License deactivated successfully.', 'webappick' );
668
  */
669
  private function add_menu_page() {
670
  add_menu_page(
671
+ esc_html( $this->menu_args['page_title'] ), esc_html( $this->menu_args['menu_title'] ),
672
  $this->menu_args['capability'], $this->menu_args['menu_slug'],
673
  array( $this, 'menu_output' ),
674
  $this->menu_args['icon_url'], $this->menu_args['position']
680
  */
681
  private function add_submenu_page() {
682
  add_submenu_page(
683
+ $this->menu_args['parent_slug'], esc_html( $this->menu_args['page_title'] ),
684
+ esc_html( $this->menu_args['menu_title'] ), $this->menu_args['capability'],
685
  $this->menu_args['menu_slug'], array( $this, 'menu_output' )
686
  );
687
  }
691
  */
692
  private function add_options_page() {
693
  add_options_page(
694
+ esc_html( $this->menu_args['page_title'] ), esc_html( $this->menu_args['menu_title'] ),
695
  $this->menu_args['capability'], $this->menu_args['menu_slug'],
696
  array( $this, 'menu_output' )
697
  );
715
  }
716
 
717
  /**
718
+ * Register Activation And Deactivation Hooks
719
+ * @return void
720
  */
721
+ private function activation_deactivation() {
722
  switch ( $this->client->getType() ) {
723
  case 'plugin':
724
  register_activation_hook( $this->client->getFile(), array( $this, 'schedule_cron_event' ) );
725
  register_deactivation_hook( $this->client->getFile(), array( $this, 'clear_scheduler' ) );
726
+ add_action( 'activated_plugin', array( $this, 'redirect_after_activation' ), 999, 2 );
727
  break;
728
 
729
  case 'theme':
730
+ add_action( 'switch_theme', array( $this, 'clear_scheduler' ), 10 );
731
+ add_action( 'after_switch_theme', array( $this, 'schedule_cron_event' ), 10 );
732
+ add_action( 'after_switch_theme', array( $this, 'redirect_to_license_page' ), 999, 2 );
733
  break;
734
  }
735
  }
736
 
737
+ /**
738
+ * Redirect to the license activation page after plugin/theme is activated.
739
+ * @TODO make option for the plugin/theme (which is using this lib) can alter this method with their custom function.
740
+ * @param string $param1 Plugin: base file|Theme: old theme name
741
+ * @param bool|WP_Theme $param2 Plugin: network wide activation status|Theme: WP_Theme instance of the old theme
742
+ * @return void
743
+ */
744
+ public function redirect_to_license_page( $param1, $param2 ) {
745
+ $canRedirect = false;
746
+ if( $this->client->getType() == 'plugin' ) $canRedirect = ( $param1 == $this->client->getBasename() );
747
+ if( $this->client->getType() == 'theme' ) $canRedirect = ( ! get_option( 'theme_switched_via_customizer' ) );
748
+ if( $canRedirect ) {
749
+ wp_redirect( admin_url( 'admin.php?page=' . $this->menu_args['menu_slug'] ) );
750
+ die();
751
+ }
752
+ }
753
+
754
  /**
755
  * Form action URL
756
  */
768
  * @return string
769
  */
770
  private function get_input_license_value( $action, $license ) {
771
+ //if ( 'deactivate' != $action ) return '';
772
+ //$key_length = strlen( $license['key'] );
773
+ //return str_pad( substr( $license['key'], 0, $key_length / 2 ), $key_length, '*' );
774
+ return isset( $license['key'] ) ? $license['key'] : '';
775
  }
776
 
777
  /**
778
  * get Plugin/Theme License
779
+ * @return array
780
  */
781
  private function getLicense() {
782
  if( $this->license !== null ) return $this->license;
783
+ $this->license = get_option( $this->option_key, false );
784
+ // initialize blank inactive license data.
785
+ if( false === $this->license ) {
786
+ $this->setLicense();
787
+ }
788
  return $this->license;
789
  }
790
 
803
  */
804
  private function setLicense( $license = [] ) {
805
  $defaults = [
806
+ 'key' => '', // license key
807
+ 'status' => 'inactive', // current status
808
+ 'instance' => '', // instance unique id
809
+ 'remaining' => 0, // remaining activation
810
+ 'activations' => 0, // total activation
811
+ 'limit' => 0, // activation limit
812
+ 'unlimited' => false, // is unlimited activation
813
+ 'expiry_date' => 0, // expires set this to a unix timestamp
814
  ];
815
+ // parse
816
+ $license = wp_parse_args( $license, $defaults );
817
  // sanitize data
818
+ $this->license['key'] = sanitize_text_field( $license['key'] );
819
+ $this->license['status'] = strtolower( $license['status'] ) === 'active' ? 'active' : 'inactive';
820
+ $this->license['instance'] = sanitize_text_field( $license['instance'] );
821
+ $this->license['remaining'] = absint( $license['remaining'] );
822
+ $this->license['activations'] = absint( $license['activations'] );
823
+ $this->license['limit'] = absint( $license['limit'] );
824
+ $this->license['unlimited'] = (bool) $license['unlimited'];
825
+ $this->license['expiry_date'] = absint( $license['expiry_date'] );
826
  // update in db
827
  return update_option( $this->option_key, $this->license, false );
828
  }
829
+
830
+ /**
831
+ * Generate a random Instance ID
832
+ * @return string
833
+ */
834
+ private function generateInstanceId() {
835
+ $id = false;
836
+ if( function_exists( 'wp_generate_password' ) ) {
837
+ $id = wp_generate_password( 12, false );
838
+ if( 12 !== strlen( $id ) ) $id = false;
839
+ }
840
+ if( ! $id ) $id = md5( uniqid( rand( 100,100000 ), true ) );
841
+ return $id;
842
+ }
843
  }
844
  // End of file License.php
libs/WebAppick/AppServices/Promotions.php CHANGED
@@ -218,7 +218,8 @@ class Promotions {
218
  /**
219
  * Check if promotion is active by date.
220
  * must have start and end property
221
- * @param stdClass $promo {
 
222
  * @type string $content string. required
223
  * @type string $start valid timestamp. required
224
  * @type string $end valid timestamp. required
@@ -232,7 +233,8 @@ class Promotions {
232
 
233
  /**
234
  * Check if promo is hidden by current user
235
- * @param stdClass $promo {
 
236
  * @type string $hash valid unique hash for a promo
237
  * }
238
  *
@@ -256,7 +258,8 @@ class Promotions {
256
  * @return void
257
  */
258
  public function __get_promo_scripts() {
259
- ?><script>
 
260
  (function($){
261
  $('body').on('click', '.wapk-promo .notice-dismiss', function (e) {
262
  e.preventDefault();
@@ -276,7 +279,8 @@ class Promotions {
276
  * @return void
277
  */
278
  public function __get_promo_styles() {
279
- ?><style>
 
280
  .wapk-promo { border: none; padding: 15px 0; }
281
  .wapk-promo-wrap { display: flex; justify-content: center; align-items: center; text-align: center; color: inherit; max-width: 1820px; margin: 0 auto; }
282
  .wapk-promo-wrap.no-column{ display: block; }
@@ -327,6 +331,7 @@ class Promotions {
327
  }
328
 
329
  /**
 
330
  * Clear Hidden Promotion preference for User
331
  * @return bool
332
  */
218
  /**
219
  * Check if promotion is active by date.
220
  * must have start and end property
221
+ * @param object $promo {
222
+ * Single Promo Object
223
  * @type string $content string. required
224
  * @type string $start valid timestamp. required
225
  * @type string $end valid timestamp. required
233
 
234
  /**
235
  * Check if promo is hidden by current user
236
+ * @param object $promo {
237
+ * Single Promo Object
238
  * @type string $hash valid unique hash for a promo
239
  * }
240
  *
258
  * @return void
259
  */
260
  public function __get_promo_scripts() {
261
+ ?><!--suppress ES6ConvertVarToLetConst -->
262
+ <script>
263
  (function($){
264
  $('body').on('click', '.wapk-promo .notice-dismiss', function (e) {
265
  e.preventDefault();
279
  * @return void
280
  */
281
  public function __get_promo_styles() {
282
+ ?><!--suppress CssUnusedSymbol -->
283
+ <style>
284
  .wapk-promo { border: none; padding: 15px 0; }
285
  .wapk-promo-wrap { display: flex; justify-content: center; align-items: center; text-align: center; color: inherit; max-width: 1820px; margin: 0 auto; }
286
  .wapk-promo-wrap.no-column{ display: block; }
331
  }
332
 
333
  /**
334
+ * @noinspection PhpUnused
335
  * Clear Hidden Promotion preference for User
336
  * @return bool
337
  */
libs/WebAppick/AppServices/Updater.php CHANGED
@@ -16,6 +16,19 @@ class Updater {
16
  */
17
  protected $client;
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  /**
20
  * Cache Key for current App
21
  * @var string
@@ -25,17 +38,28 @@ class Updater {
25
  /**
26
  * Initialize the class
27
  *
28
- * @param Client
 
29
  */
30
- public function __construct( Client $client ) {
31
  $this->client = $client;
 
32
  $this->cache_key = 'WebAppick_' . md5( $this->client->getSlug() ) . '_version_info';
 
 
 
 
 
 
 
 
33
  // Run hooks.
34
  if ( $this->client->getType() == 'plugin' ) {
35
  $this->run_plugin_hooks();
36
  } elseif ( $this->client->getType() == 'theme' ) {
37
  $this->run_theme_hooks();
38
  }
 
39
  }
40
 
41
  /**
@@ -43,9 +67,10 @@ class Updater {
43
  *
44
  * @return void
45
  */
46
- public function run_plugin_hooks() {
47
  add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_plugin_update' ) );
48
  add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
 
49
  }
50
 
51
  /**
@@ -53,8 +78,9 @@ class Updater {
53
  *
54
  * @return void
55
  */
56
- public function run_theme_hooks() {
57
  add_filter( 'pre_set_site_transient_update_themes', array( $this, 'check_theme_update' ) );
 
58
  }
59
 
60
  /**
@@ -64,20 +90,24 @@ class Updater {
64
  */
65
  public function check_plugin_update( $transient_data ) {
66
  global $pagenow;
 
 
 
 
67
  if ( ! is_object( $transient_data ) ) $transient_data = new stdClass;
68
  if ( 'plugins.php' == $pagenow && is_multisite() ) return $transient_data;
69
  if ( ! empty( $transient_data->response ) && ! empty( $transient_data->response[ $this->client->getBasename() ] ) ) {
70
  return $transient_data;
71
  }
72
- $version_info = $this->get_cached_version_info();
73
- if ( false === $version_info ) {
74
- $version_info = $this->get_project_latest_version();
75
- $this->set_cached_version_info( $version_info );
76
  }
77
- if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
78
- if ( version_compare( $this->client->getProjectVersion(), $version_info->new_version, '<' ) ) {
79
- unset( $version_info->sections );
80
- $transient_data->response[ $this->client->getBasename() ] = $version_info;
81
  }
82
  $transient_data->last_checked = time();
83
  $transient_data->checked[ $this->client->getBasename() ] = $this->client->getProjectVersion();
@@ -108,33 +138,61 @@ class Updater {
108
  set_transient( $this->cache_key, $value, 3 * HOUR_IN_SECONDS );
109
  }
110
 
 
 
 
 
 
 
 
 
111
  /**
112
  * Get plugin info from WebAppick
113
  * @return bool|array
114
  */
115
- private function get_project_latest_version() {
116
- $license_option_key = 'WebAppick_' . md5( $this->client->getSlug() ) . '_manage_license';
117
- $license = get_option( $license_option_key, null );
118
- $params = [
119
- 'version' => $this->client->getProjectVersion(),
120
- 'name' => $this->client->getName(),
121
- 'slug' => $this->client->getSlug(),
122
- 'basename' => $this->client->getBasename(),
123
- 'license_key' => ! empty( $license ) && isset( $license['key'] ) ? $license['key'] : '',
124
- ];
125
- $response = $this->client->send_request( $params, 'update/' . $this->client->getHash() . '/check', true );
126
- if ( ! is_wp_error( $response ) ) {
127
- $response = wp_remote_retrieve_body( $response );
128
- $response = json_decode( $response );
129
- if ( isset( $response->slug ) ) {
130
- return $this->__children_to_array( json_decode( $response ), [ 'icons', 'banners', 'sections' ] );
131
- } else {
132
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  }
134
- } else {
135
- return false;
 
 
 
 
 
136
  }
137
-
138
  }
139
 
140
  /**
@@ -149,12 +207,12 @@ class Updater {
149
  public function plugins_api_filter( $data, $action = '', $args = null ) {
150
  if ( $action != 'plugin_information' ) return $data;
151
  if ( ! isset( $args->slug ) || ( $args->slug != $this->client->getSlug() ) ) return $data;
152
- $version_info = $this->get_cached_version_info();
153
- if ( false === $version_info ) {
154
- $version_info = $this->get_project_latest_version();
155
- $this->set_cached_version_info( $version_info );
156
  }
157
- return $version_info;
158
  }
159
 
160
  /**
@@ -169,14 +227,14 @@ class Updater {
169
  if ( ! empty( $transient_data->response ) && ! empty( $transient_data->response[ $this->client->getSlug() ] ) ) {
170
  return $transient_data;
171
  }
172
- $version_info = $this->get_cached_version_info();
173
- if ( false === $version_info ) {
174
- $version_info = $this->get_project_latest_version();
175
- $this->set_cached_version_info( $version_info );
176
  }
177
- if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
178
- if ( version_compare( $this->client->getProjectVersion(), $version_info->new_version, '<' ) ) {
179
- $transient_data->response[ $this->client->getSlug() ] = (array) $version_info;
180
  }
181
  $transient_data->last_checked = time();
182
  $transient_data->checked[ $this->client->getSlug() ] = $this->client->getProjectVersion();
16
  */
17
  protected $client;
18
 
19
+ /**
20
+ * Flag for checking if the init method is already called.
21
+ * @var bool
22
+ */
23
+ private $didInit = false;
24
+
25
+ /**
26
+ * WebAppick\AppServices\License
27
+ *
28
+ * @var License
29
+ */
30
+ protected $license;
31
+
32
  /**
33
  * Cache Key for current App
34
  * @var string
38
  /**
39
  * Initialize the class
40
  *
41
+ * @param Client $client
42
+ * @param License $license
43
  */
44
+ public function __construct( Client $client, License $license ) {
45
  $this->client = $client;
46
+ $this->license = $license;
47
  $this->cache_key = 'WebAppick_' . md5( $this->client->getSlug() ) . '_version_info';
48
+ }
49
+
50
+ /**
51
+ * Initialize Updater
52
+ *
53
+ * @return void
54
+ */
55
+ public function init() {
56
  // Run hooks.
57
  if ( $this->client->getType() == 'plugin' ) {
58
  $this->run_plugin_hooks();
59
  } elseif ( $this->client->getType() == 'theme' ) {
60
  $this->run_theme_hooks();
61
  }
62
+ $this->didInit = true;
63
  }
64
 
65
  /**
67
  *
68
  * @return void
69
  */
70
+ private function run_plugin_hooks() {
71
  add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_plugin_update' ) );
72
  add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
73
+ register_deactivation_hook( $this->client->getFile(), array( $this, 'delete_cached_version_info' ) );
74
  }
75
 
76
  /**
78
  *
79
  * @return void
80
  */
81
+ private function run_theme_hooks() {
82
  add_filter( 'pre_set_site_transient_update_themes', array( $this, 'check_theme_update' ) );
83
+ add_action( 'switch_theme', array( $this, 'delete_cached_version_info' ) );
84
  }
85
 
86
  /**
90
  */
91
  public function check_plugin_update( $transient_data ) {
92
  global $pagenow;
93
+ //delete_site_transient( 'update_plugins' );
94
+ if ( ! isset( $transient_data->checked ) || isset( $transient_data->checked ) && empty( $transient_data->checked ) ) {
95
+ return $transient_data;
96
+ }
97
  if ( ! is_object( $transient_data ) ) $transient_data = new stdClass;
98
  if ( 'plugins.php' == $pagenow && is_multisite() ) return $transient_data;
99
  if ( ! empty( $transient_data->response ) && ! empty( $transient_data->response[ $this->client->getBasename() ] ) ) {
100
  return $transient_data;
101
  }
102
+ $project_info = $this->get_cached_version_info();
103
+ if ( false === $project_info ) {
104
+ $project_info = $this->get_information();
105
+ $this->set_cached_version_info( $project_info );
106
  }
107
+ if ( false !== $project_info && is_object( $project_info ) && isset( $project_info->new_version ) ) {
108
+ if ( version_compare( $this->client->getProjectVersion(), $project_info->new_version, '<' ) ) {
109
+ unset( $project_info->sections );
110
+ $transient_data->response[ $this->client->getBasename() ] = $project_info;
111
  }
112
  $transient_data->last_checked = time();
113
  $transient_data->checked[ $this->client->getBasename() ] = $this->client->getProjectVersion();
138
  set_transient( $this->cache_key, $value, 3 * HOUR_IN_SECONDS );
139
  }
140
 
141
+ /**
142
+ * Delete cached version info
143
+ * @return bool
144
+ */
145
+ public function delete_cached_version_info() {
146
+ return delete_transient( $this->cache_key );
147
+ }
148
+
149
  /**
150
  * Get plugin info from WebAppick
151
  * @return bool|array
152
  */
153
+ private function get_information() {
154
+ $response = $this->license->check_update();
155
+ // $fields = [
156
+ // 'id',
157
+ // 'name',
158
+ // 'author',
159
+ // 'author_profile',
160
+ // 'slug',
161
+ // 'plugin',
162
+ // 'new_version',
163
+ // 'url',
164
+ // 'package',
165
+ // 'icons',
166
+ // 'banners',
167
+ // 'banner_rtl',
168
+ // 'upgrade_notice',
169
+ // 'requires',
170
+ // 'requires_php',
171
+ // 'tested',
172
+ // 'compatibility',
173
+ // 'contributors',
174
+ // 'ratings',
175
+ // 'num_ratings',
176
+ // 'last_updated',
177
+ // 'homepage',
178
+ // 'author_block_count',
179
+ // 'author_block_rating',
180
+ // ];
181
+ if( isset( $response['success'] ) && $response['success'] ) {
182
+ $data = $response['data']['package'];
183
+ $response = $this->license->get_information();
184
+ if( isset( $response['success'] ) && $response['success'] ) {
185
+ $data = array_merge( $data, $response['data']['info'] );
186
  }
187
+ if( isset( $data['product_id'] ) ) unset( $data['product_id'] );
188
+ /**
189
+ * Filter API Response Data
190
+ * @param array $data
191
+ */
192
+ $data = apply_filters( 'WebAppick_' . $this->client->getSlug() . '_plugin_api_info', $data );
193
+ return $this->__children_to_array( (object) $data, [ 'icons', 'banners', 'sections', 'compatibility', 'ratings', 'contributors', 'screenshots', 'tags' ] );
194
  }
195
+ return false;
196
  }
197
 
198
  /**
207
  public function plugins_api_filter( $data, $action = '', $args = null ) {
208
  if ( $action != 'plugin_information' ) return $data;
209
  if ( ! isset( $args->slug ) || ( $args->slug != $this->client->getSlug() ) ) return $data;
210
+ $project_info = $this->get_cached_version_info();
211
+ if ( false === $project_info ) {
212
+ $project_info = $this->get_information();
213
+ $this->set_cached_version_info( $project_info );
214
  }
215
+ return $project_info;
216
  }
217
 
218
  /**
227
  if ( ! empty( $transient_data->response ) && ! empty( $transient_data->response[ $this->client->getSlug() ] ) ) {
228
  return $transient_data;
229
  }
230
+ $project_info = $this->get_cached_version_info();
231
+ if ( false === $project_info ) {
232
+ $project_info = $this->get_information();
233
+ $this->set_cached_version_info( $project_info );
234
  }
235
+ if ( false !== $project_info && is_object( $project_info ) && isset( $project_info->new_version ) ) {
236
+ if ( version_compare( $this->client->getProjectVersion(), $project_info->new_version, '<' ) ) {
237
+ $transient_data->response[ $this->client->getSlug() ] = (array) $project_info;
238
  }
239
  $transient_data->last_checked = time();
240
  $transient_data->checked[ $this->client->getSlug() ] = $this->client->getProjectVersion();
woo-feed.php CHANGED
@@ -15,7 +15,7 @@
15
  * Plugin Name: WooCommerce Product Feed
16
  * Plugin URI: https://webappick.com/
17
  * Description: This plugin generate WooCommerce product feed for Shopping Engines like Google Shopping,Facebook Product Feed,eBay,Amazon,Idealo and many more..
18
- * Version: 3.2.11
19
  * Author: WebAppick
20
  * Author URI: https://webappick.com/
21
  * License: GPL v2
@@ -41,7 +41,7 @@ if ( ! defined( 'WOO_FEED_FREE_VERSION' ) ) {
41
  * Plugin Version
42
  * @var string
43
  */
44
- define( 'WOO_FEED_FREE_VERSION', '3.2.11' );
45
  }
46
  if ( ! defined( 'WOO_FEED_FREE_FILE') ) {
47
  /**
@@ -129,7 +129,7 @@ require_once WOO_FEED_FREE_PATH . 'includes/class-woo-feed-installer.php';
129
  /**
130
  * Load Uses Tracker
131
  */
132
- require_once WOO_FEED_FREE_PATH . 'includes/classes/class-woo-feed-tracker.php';
133
 
134
  if ( ! class_exists( 'Woo_Feed' ) ) {
135
  /**
@@ -158,7 +158,7 @@ if ( ! function_exists( 'run_woo_feed' ) ) {
158
  */
159
  add_action( 'plugins_loaded', [ $plugin, 'run' ], PHP_INT_MAX );
160
  add_action( 'admin_notices', 'wooFeed_Admin_Notices' );
161
- WooFeedTracker::getInstance();
162
  }
163
  run_woo_feed();
164
  }
@@ -844,9 +844,9 @@ if ( ! function_exists( 'woo_feed_config_feed' ) ) {
844
  delete_option( 'woo_feed_enable_error_debugging' );
845
  }
846
  if( isset( $_POST['opt_in'] ) ) {
847
- WooFeedTracker::getInstance()->trackerOptIn();
848
  } else {
849
- WooFeedTracker::getInstance()->trackerOptOut();
850
  }
851
  }
852
 
15
  * Plugin Name: WooCommerce Product Feed
16
  * Plugin URI: https://webappick.com/
17
  * Description: This plugin generate WooCommerce product feed for Shopping Engines like Google Shopping,Facebook Product Feed,eBay,Amazon,Idealo and many more..
18
+ * Version: 3.2.12
19
  * Author: WebAppick
20
  * Author URI: https://webappick.com/
21
  * License: GPL v2
41
  * Plugin Version
42
  * @var string
43
  */
44
+ define( 'WOO_FEED_FREE_VERSION', '3.2.12' );
45
  }
46
  if ( ! defined( 'WOO_FEED_FREE_FILE') ) {
47
  /**
129
  /**
130
  * Load Uses Tracker
131
  */
132
+ require_once WOO_FEED_FREE_PATH . 'includes/classes/class-woo-feed-webappick-api.php';
133
 
134
  if ( ! class_exists( 'Woo_Feed' ) ) {
135
  /**
158
  */
159
  add_action( 'plugins_loaded', [ $plugin, 'run' ], PHP_INT_MAX );
160
  add_action( 'admin_notices', 'wooFeed_Admin_Notices' );
161
+ WooFeedWebAppickAPI::getInstance();
162
  }
163
  run_woo_feed();
164
  }
844
  delete_option( 'woo_feed_enable_error_debugging' );
845
  }
846
  if( isset( $_POST['opt_in'] ) ) {
847
+ WooFeedWebAppickAPI::getInstance()->trackerOptIn();
848
  } else {
849
+ WooFeedWebAppickAPI::getInstance()->trackerOptOut();
850
  }
851
  }
852