WP VR – 360 Panorama and virtual tour creator for WordPress - Version 3.8.0

Version Description

  • Load box design changed

=

Download this release

Release Info

Developer rextheme
Plugin Icon 128x128 WP VR – 360 Panorama and virtual tour creator for WordPress
Version 3.8.0
Comparing to
See all releases

Code changes from version 3.7.0 to 3.8.0

README.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://rextheme.com/wp-vr-360-panorama-and-virtual-tour-creator-fo
4
  Tags: virtual tour, real estate tour, panorama, panorama viewer, virtual tour, 360 panorama, interactive tour
5
  Requires at least: 4.0
6
  Tested up to: 5.3
7
- Stable tag: 3.7.0
8
  Requires PHP: 5.6
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -123,6 +123,15 @@ For more control over creating a virtual tour, learn about the [Premium version]
123
 
124
  **[Documentation](https://rextheme.com/docs/wp-vr/)**
125
 
 
 
 
 
 
 
 
 
 
126
  == Frequently Asked Questions ==
127
  = 1. Why should I use WPVR? =
128
  You can easily create a Virtual Tour for your place using WPVR. You simply need to provide a 360 degree panoramic photo, and this plugin will create a virtual tour which visitors can navigate easily. You can further add hotspots to include information or add more scenes to navigate to.
@@ -308,5 +317,8 @@ Simply add "/plugins/wpvr" to exclusion field (or use the location where you sto
308
  = 3.7.0 =
309
  * Partial Panorama for pro version
310
 
 
 
 
311
  == Upgrade Notice ==
312
  Please do update the WP VR to the latest version. Each update makes it sure your plugin is supporting all tour features.  
4
  Tags: virtual tour, real estate tour, panorama, panorama viewer, virtual tour, 360 panorama, interactive tour
5
  Requires at least: 4.0
6
  Tested up to: 5.3
7
+ Stable tag: 3.8.0
8
  Requires PHP: 5.6
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
123
 
124
  **[Documentation](https://rextheme.com/docs/wp-vr/)**
125
 
126
+ ## Privacy Policy
127
+ WP VR uses [Appsero](https://appsero.com) SDK to collect some telemetry data upon user's confirmation. This helps us to troubleshoot problems faster & make product improvements.
128
+
129
+ Appsero SDK **does not gather any data by default.** The SDK only starts gathering basic telemetry data **when a user allows it via the admin notice**. We collect the data to ensure great user experience for all our users.
130
+
131
+ Integrating Appsero SDK **DOES NOT IMMEDIATELY** start gathering data, **without confirmation from users in any case.**
132
+
133
+ Learn more how [Appsero collects and uses this data](https://appsero.com/privacy-policy/).
134
+
135
  == Frequently Asked Questions ==
136
  = 1. Why should I use WPVR? =
137
  You can easily create a Virtual Tour for your place using WPVR. You simply need to provide a 360 degree panoramic photo, and this plugin will create a virtual tour which visitors can navigate easily. You can further add hotspots to include information or add more scenes to navigate to.
317
  = 3.7.0 =
318
  * Partial Panorama for pro version
319
 
320
+ = 3.8.0 =
321
+ * Load box design changed
322
+
323
  == Upgrade Notice ==
324
  Please do update the WP VR to the latest version. Each update makes it sure your plugin is supporting all tour features.  
appsero/src/Client.php ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Appsero;
3
+
4
+ /**
5
+ * Appsero Client
6
+ *
7
+ * This class is necessary to set project data
8
+ */
9
+ class Client {
10
+
11
+ /**
12
+ * The client version
13
+ *
14
+ * @var string
15
+ */
16
+ public $version = '1.1.9';
17
+
18
+ /**
19
+ * Hash identifier of the plugin
20
+ *
21
+ * @var string
22
+ */
23
+ public $hash;
24
+
25
+ /**
26
+ * Name of the plugin
27
+ *
28
+ * @var string
29
+ */
30
+ public $name;
31
+
32
+ /**
33
+ * The plugin/theme file path
34
+ * @example .../wp-content/plugins/test-slug/test-slug.php
35
+ *
36
+ * @var string
37
+ */
38
+ public $file;
39
+
40
+ /**
41
+ * Main plugin file
42
+ * @example test-slug/test-slug.php
43
+ *
44
+ * @var string
45
+ */
46
+ public $basename;
47
+
48
+ /**
49
+ * Slug of the plugin
50
+ * @example test-slug
51
+ *
52
+ * @var string
53
+ */
54
+ public $slug;
55
+
56
+ /**
57
+ * The project version
58
+ *
59
+ * @var string
60
+ */
61
+ public $project_version;
62
+
63
+ /**
64
+ * The project type
65
+ *
66
+ * @var string
67
+ */
68
+ public $type;
69
+
70
+ /**
71
+ * textdomain
72
+ *
73
+ * @var string
74
+ */
75
+ public $textdomain;
76
+
77
+ /**
78
+ * Initialize the class
79
+ *
80
+ * @param string $hash hash of the plugin
81
+ * @param string $name readable name of the plugin
82
+ * @param string $file main plugin file path
83
+ */
84
+ public function __construct( $hash, $name, $file ) {
85
+ $this->hash = $hash;
86
+ $this->name = $name;
87
+ $this->file = $file;
88
+
89
+ $this->set_basename_and_slug();
90
+ }
91
+
92
+ /**
93
+ * Initialize insights class
94
+ *
95
+ * @return Appsero\Insights
96
+ */
97
+ public function insights() {
98
+
99
+ if ( ! class_exists( __NAMESPACE__ . '\Insights') ) {
100
+ require_once __DIR__ . '/Insights.php';
101
+ }
102
+
103
+ return new Insights( $this );
104
+ }
105
+
106
+ /**
107
+ * Initialize plugin/theme updater
108
+ *
109
+ * @return Appsero\Updater
110
+ */
111
+ public function updater() {
112
+
113
+ if ( ! class_exists( __NAMESPACE__ . '\Updater') ) {
114
+ require_once __DIR__ . '/Updater.php';
115
+ }
116
+
117
+ return new Updater( $this );
118
+ }
119
+
120
+ /**
121
+ * Initialize license checker
122
+ *
123
+ * @return Appsero\License
124
+ */
125
+ public function license() {
126
+
127
+ if ( ! class_exists( __NAMESPACE__ . '\License') ) {
128
+ require_once __DIR__ . '/License.php';
129
+ }
130
+
131
+ return new License( $this );
132
+ }
133
+
134
+ /**
135
+ * API Endpoint
136
+ *
137
+ * @return string
138
+ */
139
+ public function endpoint() {
140
+ $endpoint = apply_filters( 'appsero_endpoint', 'https://api.appsero.com' );
141
+
142
+ return trailingslashit( $endpoint );
143
+ }
144
+
145
+ /**
146
+ * Set project basename, slug and version
147
+ *
148
+ * @return void
149
+ */
150
+ protected function set_basename_and_slug() {
151
+
152
+ if ( strpos( $this->file, WP_CONTENT_DIR . '/themes/' ) === false ) {
153
+
154
+ $this->basename = plugin_basename( $this->file );
155
+
156
+ list( $this->slug, $mainfile) = explode( '/', $this->basename );
157
+
158
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
159
+
160
+ $plugin_data = get_plugin_data( $this->file );
161
+
162
+ $this->project_version = $plugin_data['Version'];
163
+ $this->type = 'plugin';
164
+ $this->textdomain = $this->slug;
165
+
166
+ } else {
167
+
168
+ $this->basename = str_replace( WP_CONTENT_DIR . '/themes/', '', $this->file );
169
+
170
+ list( $this->slug, $mainfile) = explode( '/', $this->basename );
171
+
172
+ $theme = wp_get_theme( $this->slug );
173
+
174
+ $this->project_version = $theme->version;
175
+ $this->type = 'theme';
176
+
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Send request to remote endpoint
182
+ *
183
+ * @param array $params
184
+ * @param string $route
185
+ *
186
+ * @return array|WP_Error Array of results including HTTP headers or WP_Error if the request failed.
187
+ */
188
+ public function send_request( $params, $route, $blocking = false ) {
189
+ $url = $this->endpoint() . $route;
190
+
191
+ $headers = array(
192
+ 'user-agent' => 'Appsero/' . md5( esc_url( home_url() ) ) . ';',
193
+ 'Accept' => 'application/json',
194
+ );
195
+
196
+ $response = wp_remote_post( $url, array(
197
+ 'method' => 'POST',
198
+ 'timeout' => 30,
199
+ 'redirection' => 5,
200
+ 'httpversion' => '1.0',
201
+ 'blocking' => $blocking,
202
+ 'headers' => $headers,
203
+ 'body' => array_merge( $params, array( 'client' => $this->version ) ),
204
+ 'cookies' => array()
205
+ ) );
206
+
207
+ return $response;
208
+ }
209
+
210
+ }
wpvrinsights.php → appsero/src/Insights.php RENAMED
@@ -1,56 +1,14 @@
1
  <?php
2
- namespace AppSero;
3
 
4
  /**
5
- * AppSero Insights
6
  *
7
  * This is a tracker class to track plugin usage based on if the customer has opted in.
8
  * No personal information is being tracked by this class, only general settings, active plugins, environment details
9
  * and admin email.
10
  */
11
- class WPVRInsights {
12
-
13
- /**
14
- * The client version
15
- *
16
- * @var string
17
- */
18
- public $version = '1.0';
19
-
20
- /**
21
- * Hash identifier of the plugin
22
- *
23
- * @var string
24
- */
25
- public $hash;
26
-
27
- /**
28
- * Slug of the plugin
29
- *
30
- * @var string
31
- */
32
- public $slug;
33
-
34
- /**
35
- * Name of the plugin
36
- *
37
- * @var string
38
- */
39
- public $name;
40
-
41
- /**
42
- * Main plugin file
43
- *
44
- * @var string
45
- */
46
- public $basename;
47
-
48
- /**
49
- * The plugin/theme file path
50
- *
51
- * @var string
52
- */
53
- public $file;
54
 
55
  /**
56
  * The notice text
@@ -74,23 +32,26 @@ class WPVRInsights {
74
  protected $extra_data = array();
75
 
76
  /**
77
- * URL to the API endpoint
78
  *
79
- * @var string
80
  */
81
- protected $endpoint = 'https://api.appsero.com';
82
 
83
  /**
84
  * Initialize the class
85
  *
86
- * @param string $slug slug of the plugin
87
- * @param string $name readable name of the plugin
88
- * @param string $file main plugin file path
89
  */
90
- public function __construct( $hash, $name, $file ) {
91
- $this->hash = $hash;
92
- $this->name = $name;
93
- $this->file = $file;
 
 
 
 
 
94
  }
95
 
96
  /**
@@ -130,18 +91,29 @@ class WPVRInsights {
130
  return $this;
131
  }
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  /**
134
  * Initialize theme hooks
135
  *
136
  * @return void
137
  */
138
  public function init_theme() {
139
- $this->basename = str_replace( WP_CONTENT_DIR . '/themes/', '', $this->file );
140
- list( $this->slug, $mainfile) = explode( '/', $this->basename );
141
-
142
  $this->init_common();
143
 
144
  add_action( 'switch_theme', array( $this, 'deactivation_cleanup' ) );
 
145
  }
146
 
147
  /**
@@ -150,19 +122,16 @@ class WPVRInsights {
150
  * @return void
151
  */
152
  public function init_plugin() {
153
- $this->basename = plugin_basename( $this->file );
154
-
155
- list( $this->slug, $mainfile) = explode( '/', $this->basename );
156
-
157
  // plugin deactivate popup
158
  if ( ! $this->is_local_server() ) {
159
- add_action( 'plugin_action_links_' . $this->basename, array( $this, 'plugin_action_links' ) );
160
  add_action( 'admin_footer', array( $this, 'deactivate_scripts' ) );
161
  }
162
 
163
  $this->init_common();
164
 
165
- register_deactivation_hook( $this->file, array( $this, 'deactivation_cleanup' ) );
 
166
  }
167
 
168
  /**
@@ -180,23 +149,14 @@ class WPVRInsights {
180
  add_action( 'admin_init', array( $this, 'handle_optin_optout' ) );
181
 
182
  // uninstall reason
183
- add_action( 'wp_ajax_' . $this->slug . '_submit-uninstall-reason', array( $this, 'uninstall_reason_submission' ) );
184
 
185
  // cron events
186
- add_action( 'cron_schedules', array( $this, 'add_weekly_schedule' ) );
187
- add_action( $this->slug . '_tracker_send_event', array( $this, 'send_tracking_data' ) );
188
  // add_action( 'admin_init', array( $this, 'send_tracking_data' ) ); // test
189
  }
190
 
191
- /**
192
- * API Endpoint
193
- *
194
- * @return string
195
- */
196
- public function endpoint() {
197
- return apply_filters( 'appsero_endpoint', $this->endpoint );
198
- }
199
-
200
  /**
201
  * Send tracking data to AppSero server
202
  *
@@ -221,34 +181,9 @@ class WPVRInsights {
221
  return;
222
  }
223
 
224
- $this->send_request( $this->get_tracking_data(), 'track' );
225
-
226
- update_option( $this->slug . '_tracking_last_send', time() );
227
- }
228
-
229
- /**
230
- * Send request to remote endpoint
231
- *
232
- * @param array $params
233
- * @param string $route
234
- *
235
- * @return void
236
- */
237
- private function send_request( $params, $route ) {
238
- $url = trailingslashit( $this->endpoint() ) . $route;
239
-
240
- $response = wp_remote_post( $url, array(
241
- 'method' => 'POST',
242
- 'timeout' => 30,
243
- 'redirection' => 5,
244
- 'httpversion' => '1.0',
245
- 'blocking' => false,
246
- 'headers' => array( 'user-agent' => 'AppSero/' . md5( esc_url( home_url() ) ) . ';' ),
247
- 'body' => array_merge( $params, array( 'client' => $this->version ) ),
248
- 'cookies' => array()
249
- ) );
250
 
251
- return $response;
252
  }
253
 
254
  /**
@@ -258,8 +193,17 @@ class WPVRInsights {
258
  */
259
  protected function get_tracking_data() {
260
  $all_plugins = $this->get_all_plugins();
261
- $admin_user = get_user_by( 'id', 1 );
262
- $first_name = $last_name = '';
 
 
 
 
 
 
 
 
 
263
 
264
  if ( $admin_user ) {
265
  $first_name = $admin_user->first_name ? $admin_user->first_name : $admin_user->display_name;
@@ -267,25 +211,29 @@ class WPVRInsights {
267
  }
268
 
269
  $data = array(
270
- 'url' => home_url(),
271
- 'site' => get_bloginfo( 'name' ),
 
272
  'admin_email' => get_option( 'admin_email' ),
273
  'first_name' => $first_name,
274
  'last_name' => $last_name,
275
- 'hash' => $this->hash,
276
  'server' => $this->get_server_info(),
277
  'wp' => $this->get_wp_info(),
278
  'users' => $this->get_user_counts(),
279
  'active_plugins' => count( $all_plugins['active_plugins'] ),
280
  'inactive_plugins' => count( $all_plugins['inactive_plugins'] ),
 
 
 
281
  );
282
 
283
- // for child classes
284
  if ( $extra = $this->get_extra_data() ) {
285
  $data['extra'] = $extra;
286
  }
287
 
288
- return apply_filters( $this->slug . '_tracker_data', $data );
289
  }
290
 
291
  /**
@@ -294,7 +242,15 @@ class WPVRInsights {
294
  * @return mixed
295
  */
296
  protected function get_extra_data() {
297
- return $this->extra_data;
 
 
 
 
 
 
 
 
298
  }
299
 
300
  /**
@@ -320,8 +276,8 @@ class WPVRInsights {
320
  *
321
  * @return bool
322
  */
323
- private function tracking_allowed() {
324
- $allow_tracking = get_option( $this->slug . '_allow_tracking', 'no' );
325
 
326
  return $allow_tracking == 'yes';
327
  }
@@ -332,7 +288,7 @@ class WPVRInsights {
332
  * @return false|string
333
  */
334
  private function get_last_send() {
335
- return get_option( $this->slug . '_tracking_last_send', false );
336
  }
337
 
338
  /**
@@ -341,7 +297,7 @@ class WPVRInsights {
341
  * @return boolean
342
  */
343
  private function notice_dismissed() {
344
- $hide_notice = get_option( $this->slug . '_tracking_notice', 'no' );
345
 
346
  if ( 'hide' == $hide_notice ) {
347
  return true;
@@ -356,6 +312,8 @@ class WPVRInsights {
356
  * @return boolean
357
  */
358
  private function is_local_server() {
 
 
359
  $is_local = in_array( $_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1' ) );
360
 
361
  return apply_filters( 'appsero_is_local', $is_local );
@@ -367,7 +325,11 @@ class WPVRInsights {
367
  * @return void
368
  */
369
  private function schedule_event() {
370
- wp_schedule_event( time(), 'weekly', $this->slug . '_tracker_send_event' );
 
 
 
 
371
  }
372
 
373
  /**
@@ -376,7 +338,7 @@ class WPVRInsights {
376
  * @return void
377
  */
378
  private function clear_schedule_event() {
379
- wp_clear_scheduled_hook( $this->slug . '_tracker_send_event' );
380
  }
381
 
382
  /**
@@ -400,26 +362,27 @@ class WPVRInsights {
400
 
401
  // don't show tracking if a local server
402
  if ( ! $this->is_local_server() ) {
403
- $optin_url = add_query_arg( $this->slug . '_tracker_optin', 'true' );
404
- $optout_url = add_query_arg( $this->slug . '_tracker_optout', 'true' );
405
 
406
  if ( empty( $this->notice ) ) {
407
- $notice = sprintf( __( 'Want to help make <strong>%1$s</strong> even more awesome? Allow %1$s to collect non-sensitive diagnostic data and usage information.', 'textdomain' ), $this->name );
408
  } else {
409
  $notice = $this->notice;
410
  }
411
 
412
- $notice .= ' (<a class="' . $this->slug . '-insights-data-we-collect" href="#">' . __( 'what we collect', 'textdomain' ) . '</a>)';
413
- $notice .= '<p class="description" style="display:none;">' . implode( ', ', $this->data_we_collect() ) . '. No sensitive data is tracked.</p>';
 
414
 
415
  echo '<div class="updated"><p>';
416
  echo $notice;
417
  echo '</p><p class="submit">';
418
- echo '&nbsp;<a href="' . esc_url( $optin_url ) . '" class="button-primary button-large">' . __( 'Allow', 'textdomain' ) . '</a>';
419
- echo '&nbsp;<a href="' . esc_url( $optout_url ) . '" class="button-secondary button-large">' . __( 'No thanks', 'textdomain' ) . '</a>';
420
  echo '</p></div>';
421
 
422
- echo "<script type='text/javascript'>jQuery('." . $this->slug . "-insights-data-we-collect').on('click', function(e) {
423
  e.preventDefault();
424
  jQuery(this).parents('.updated').find('p.description').slideToggle('fast');
425
  });
@@ -435,17 +398,17 @@ class WPVRInsights {
435
  */
436
  public function handle_optin_optout() {
437
 
438
- if ( isset( $_GET[ $this->slug . '_tracker_optin' ] ) && $_GET[ $this->slug . '_tracker_optin' ] == 'true' ) {
439
  $this->optin();
440
 
441
- wp_redirect( remove_query_arg( $this->slug . '_tracker_optin' ) );
442
  exit;
443
  }
444
 
445
- if ( isset( $_GET[ $this->slug . '_tracker_optout' ] ) && $_GET[ $this->slug . '_tracker_optout' ] == 'true' ) {
446
  $this->optout();
447
 
448
- wp_redirect( remove_query_arg( $this->slug . '_tracker_optout' ) );
449
  exit;
450
  }
451
  }
@@ -456,8 +419,8 @@ class WPVRInsights {
456
  * @return void
457
  */
458
  public function optin() {
459
- update_option( $this->slug . '_allow_tracking', 'yes' );
460
- update_option( $this->slug . '_tracking_notice', 'hide' );
461
 
462
  $this->clear_schedule_event();
463
  $this->schedule_event();
@@ -470,8 +433,8 @@ class WPVRInsights {
470
  * @return void
471
  */
472
  public function optout() {
473
- update_option( $this->slug . '_allow_tracking', 'no' );
474
- update_option( $this->slug . '_tracking_notice', 'hide' );
475
 
476
  $this->clear_schedule_event();
477
  }
@@ -612,12 +575,36 @@ class WPVRInsights {
612
 
613
  $schedules['weekly'] = array(
614
  'interval' => DAY_IN_SECONDS * 7,
615
- 'display' => __( 'Once Weekly', 'textdomain' )
616
  );
617
 
618
  return $schedules;
619
  }
620
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  /**
622
  * Clear our options upon deactivation
623
  *
@@ -626,9 +613,12 @@ class WPVRInsights {
626
  public function deactivation_cleanup() {
627
  $this->clear_schedule_event();
628
 
629
- delete_option( $this->slug . '_allow_tracking' );
630
- delete_option( $this->slug . '_tracking_notice' );
631
- delete_option( $this->slug . '_tracking_last_send' );
 
 
 
632
  }
633
 
634
  /**
@@ -641,7 +631,7 @@ class WPVRInsights {
641
  public function plugin_action_links( $links ) {
642
 
643
  if ( array_key_exists( 'deactivate', $links ) ) {
644
- $links['deactivate'] = str_replace( '<a', '<a class="' . $this->slug . '-deactivate-link"', $links['deactivate'] );
645
  }
646
 
647
  return $links;
@@ -710,20 +700,28 @@ class WPVRInsights {
710
  $current_user = wp_get_current_user();
711
 
712
  $data = array(
713
- 'hash' => $this->hash,
714
  'reason_id' => sanitize_text_field( $_POST['reason_id'] ),
715
  'reason_info' => isset( $_REQUEST['reason_info'] ) ? trim( stripslashes( $_REQUEST['reason_info'] ) ) : '',
716
- 'site' => get_bloginfo( 'name' ),
717
- 'url' => home_url(),
718
  'admin_email' => get_option( 'admin_email' ),
719
  'user_email' => $current_user->user_email,
720
  'first_name' => $current_user->first_name,
721
  'last_name' => $current_user->last_name,
722
  'server' => $this->get_server_info(),
723
  'wp' => $this->get_wp_info(),
 
 
 
724
  );
725
 
726
- $this->send_request( $data, 'deactivate' );
 
 
 
 
 
727
 
728
  wp_send_json_success();
729
  }
@@ -743,10 +741,10 @@ class WPVRInsights {
743
  $reasons = $this->get_uninstall_reasons();
744
  ?>
745
 
746
- <div class="wd-dr-modal" id="<?php echo $this->slug; ?>-wd-dr-modal">
747
  <div class="wd-dr-modal-wrap">
748
  <div class="wd-dr-modal-header">
749
- <h3><?php _e( 'If you have a moment, please let us know why you are deactivating:', 'domain' ); ?></h3>
750
  </div>
751
 
752
  <div class="wd-dr-modal-body">
@@ -757,12 +755,13 @@ class WPVRInsights {
757
  </li>
758
  <?php } ?>
759
  </ul>
 
760
  </div>
761
 
762
  <div class="wd-dr-modal-footer">
763
- <a href="#" class="dont-bother-me"><?php _e( 'I rather wouldn\'t say', 'domain' ); ?></a>
764
- <button class="button-secondary"><?php _e( 'Submit & Deactivate', 'domain' ); ?></button>
765
- <button class="button-primary"><?php _e( 'Cancel', 'domain' ); ?></button>
766
  </div>
767
  </div>
768
  </div>
@@ -813,15 +812,18 @@ class WPVRInsights {
813
  padding: 12px 20px;
814
  text-align: right;
815
  }
 
 
 
816
  </style>
817
 
818
  <script type="text/javascript">
819
  (function($) {
820
  $(function() {
821
- var modal = $( '#<?php echo $this->slug; ?>-wd-dr-modal' );
822
  var deactivateLink = '';
823
 
824
- $( '#the-list' ).on('click', 'a.<?php echo $this->slug; ?>-deactivate-link', function(e) {
825
  e.preventDefault();
826
 
827
  modal.addClass('modal-active');
@@ -868,7 +870,7 @@ class WPVRInsights {
868
  url: ajaxurl,
869
  type: 'POST',
870
  data: {
871
- action: '<?php echo $this->slug; ?>_submit-uninstall-reason',
872
  reason_id: ( 0 === $radio.length ) ? 'none' : $radio.val(),
873
  reason_info: ( 0 !== $input.length ) ? $input.val().trim() : ''
874
  },
@@ -887,4 +889,74 @@ class WPVRInsights {
887
 
888
  <?php
889
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
890
  }
1
  <?php
2
+ namespace Appsero;
3
 
4
  /**
5
+ * Appsero Insights
6
  *
7
  * This is a tracker class to track plugin usage based on if the customer has opted in.
8
  * No personal information is being tracked by this class, only general settings, active plugins, environment details
9
  * and admin email.
10
  */
11
+ class Insights {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  /**
14
  * The notice text
32
  protected $extra_data = array();
33
 
34
  /**
35
+ * AppSero\Client
36
  *
37
+ * @var object
38
  */
39
+ protected $client;
40
 
41
  /**
42
  * Initialize the class
43
  *
44
+ * @param AppSero\Client
 
 
45
  */
46
+ public function __construct( $client, $name = null, $file = null ) {
47
+
48
+ if ( is_string( $client ) && ! empty( $name ) && ! empty( $file ) ) {
49
+ $client = new Client( $client, $name, $file );
50
+ }
51
+
52
+ if ( is_object( $client ) && is_a( $client, 'Appsero\Client' ) ) {
53
+ $this->client = $client;
54
+ }
55
  }
56
 
57
  /**
91
  return $this;
92
  }
93
 
94
+ /**
95
+ * Initialize insights
96
+ *
97
+ * @return void
98
+ */
99
+ public function init() {
100
+ if ( $this->client->type == 'plugin' ) {
101
+ $this->init_plugin();
102
+ } else if ( $this->client->type == 'theme' ) {
103
+ $this->init_theme();
104
+ }
105
+ }
106
+
107
  /**
108
  * Initialize theme hooks
109
  *
110
  * @return void
111
  */
112
  public function init_theme() {
 
 
 
113
  $this->init_common();
114
 
115
  add_action( 'switch_theme', array( $this, 'deactivation_cleanup' ) );
116
+ add_action( 'switch_theme', array( $this, 'theme_deactivated' ), 12, 3 );
117
  }
118
 
119
  /**
122
  * @return void
123
  */
124
  public function init_plugin() {
 
 
 
 
125
  // plugin deactivate popup
126
  if ( ! $this->is_local_server() ) {
127
+ add_filter( 'plugin_action_links_' . $this->client->basename, array( $this, 'plugin_action_links' ) );
128
  add_action( 'admin_footer', array( $this, 'deactivate_scripts' ) );
129
  }
130
 
131
  $this->init_common();
132
 
133
+ register_activation_hook( $this->client->file, array( $this, 'activate_plugin' ) );
134
+ register_deactivation_hook( $this->client->file, array( $this, 'deactivation_cleanup' ) );
135
  }
136
 
137
  /**
149
  add_action( 'admin_init', array( $this, 'handle_optin_optout' ) );
150
 
151
  // uninstall reason
152
+ add_action( 'wp_ajax_' . $this->client->slug . '_submit-uninstall-reason', array( $this, 'uninstall_reason_submission' ) );
153
 
154
  // cron events
155
+ add_filter( 'cron_schedules', array( $this, 'add_weekly_schedule' ) );
156
+ add_action( $this->client->slug . '_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 AppSero server
162
  *
181
  return;
182
  }
183
 
184
+ $response = $this->client->send_request( $this->get_tracking_data(), 'track' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
+ update_option( $this->client->slug . '_tracking_last_send', time() );
187
  }
188
 
189
  /**
193
  */
194
  protected function get_tracking_data() {
195
  $all_plugins = $this->get_all_plugins();
196
+
197
+ $users = get_users( array(
198
+ 'role' => 'administrator',
199
+ 'orderby' => 'ID',
200
+ 'order' => 'ASC',
201
+ 'number' => 1,
202
+ 'paged' => 1,
203
+ ) );
204
+
205
+ $admin_user = ( is_array( $users ) && ! empty( $users ) ) ? $users[0] : false;
206
+ $first_name = $last_name = '';
207
 
208
  if ( $admin_user ) {
209
  $first_name = $admin_user->first_name ? $admin_user->first_name : $admin_user->display_name;
211
  }
212
 
213
  $data = array(
214
+ 'version' => $this->client->project_version,
215
+ 'url' => esc_url( home_url() ),
216
+ 'site' => $this->get_site_name(),
217
  'admin_email' => get_option( 'admin_email' ),
218
  'first_name' => $first_name,
219
  'last_name' => $last_name,
220
+ 'hash' => $this->client->hash,
221
  'server' => $this->get_server_info(),
222
  'wp' => $this->get_wp_info(),
223
  'users' => $this->get_user_counts(),
224
  'active_plugins' => count( $all_plugins['active_plugins'] ),
225
  'inactive_plugins' => count( $all_plugins['inactive_plugins'] ),
226
+ 'ip_address' => $this->get_user_ip_address(),
227
+ 'theme' => get_stylesheet(),
228
+ 'version' => $this->client->project_version,
229
  );
230
 
231
+ // Add metadata
232
  if ( $extra = $this->get_extra_data() ) {
233
  $data['extra'] = $extra;
234
  }
235
 
236
+ return apply_filters( $this->client->slug . '_tracker_data', $data );
237
  }
238
 
239
  /**
242
  * @return mixed
243
  */
244
  protected function get_extra_data() {
245
+ if ( is_callable( $this->extra_data ) ) {
246
+ return call_user_func( $this->extra_data );
247
+ }
248
+
249
+ if ( is_array( $this->extra_data ) ) {
250
+ return $this->extra_data;
251
+ }
252
+
253
+ return array();
254
  }
255
 
256
  /**
276
  *
277
  * @return bool
278
  */
279
+ public function tracking_allowed() {
280
+ $allow_tracking = get_option( $this->client->slug . '_allow_tracking', 'no' );
281
 
282
  return $allow_tracking == 'yes';
283
  }
288
  * @return false|string
289
  */
290
  private function get_last_send() {
291
+ return get_option( $this->client->slug . '_tracking_last_send', false );
292
  }
293
 
294
  /**
297
  * @return boolean
298
  */
299
  private function notice_dismissed() {
300
+ $hide_notice = get_option( $this->client->slug . '_tracking_notice', null );
301
 
302
  if ( 'hide' == $hide_notice ) {
303
  return true;
312
  * @return boolean
313
  */
314
  private function is_local_server() {
315
+ return false;
316
+
317
  $is_local = in_array( $_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1' ) );
318
 
319
  return apply_filters( 'appsero_is_local', $is_local );
325
  * @return void
326
  */
327
  private function schedule_event() {
328
+ $hook_name = $this->client->slug . '_tracker_send_event';
329
+
330
+ if ( ! wp_next_scheduled( $hook_name ) ) {
331
+ wp_schedule_event( time(), 'weekly', $hook_name );
332
+ }
333
  }
334
 
335
  /**
338
  * @return void
339
  */
340
  private function clear_schedule_event() {
341
+ wp_clear_scheduled_hook( $this->client->slug . '_tracker_send_event' );
342
  }
343
 
344
  /**
362
 
363
  // don't show tracking if a local server
364
  if ( ! $this->is_local_server() ) {
365
+ $optin_url = add_query_arg( $this->client->slug . '_tracker_optin', 'true' );
366
+ $optout_url = add_query_arg( $this->client->slug . '_tracker_optout', 'true' );
367
 
368
  if ( empty( $this->notice ) ) {
369
+ $notice = sprintf( __( 'Want to help make <strong>%1$s</strong> even more awesome? Allow %1$s to collect non-sensitive diagnostic data and usage information.', $this->client->textdomain ), $this->client->name );
370
  } else {
371
  $notice = $this->notice;
372
  }
373
 
374
+ $notice .= ' (<a class="' . $this->client->slug . '-insights-data-we-collect" href="#">' . __( 'what we collect', $this->client->textdomain ) . '</a>)';
375
+ $notice .= '<p class="description" style="display:none;">' . implode( ', ', $this->data_we_collect() ) . '. No sensitive data is tracked. ';
376
+ $notice .= 'We are using Appsero to collect your data. <a href="https://appsero.com/privacy-policy/">Learn more</a> about how Appsero collects and handle your data.</p>';
377
 
378
  echo '<div class="updated"><p>';
379
  echo $notice;
380
  echo '</p><p class="submit">';
381
+ echo '&nbsp;<a href="' . esc_url( $optin_url ) . '" class="button-primary button-large">' . __( 'Allow', $this->client->textdomain ) . '</a>';
382
+ echo '&nbsp;<a href="' . esc_url( $optout_url ) . '" class="button-secondary button-large">' . __( 'No thanks', $this->client->textdomain ) . '</a>';
383
  echo '</p></div>';
384
 
385
+ echo "<script type='text/javascript'>jQuery('." . $this->client->slug . "-insights-data-we-collect').on('click', function(e) {
386
  e.preventDefault();
387
  jQuery(this).parents('.updated').find('p.description').slideToggle('fast');
388
  });
398
  */
399
  public function handle_optin_optout() {
400
 
401
+ if ( isset( $_GET[ $this->client->slug . '_tracker_optin' ] ) && $_GET[ $this->client->slug . '_tracker_optin' ] == 'true' ) {
402
  $this->optin();
403
 
404
+ wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optin' ) );
405
  exit;
406
  }
407
 
408
+ if ( isset( $_GET[ $this->client->slug . '_tracker_optout' ] ) && $_GET[ $this->client->slug . '_tracker_optout' ] == 'true' ) {
409
  $this->optout();
410
 
411
+ wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optout' ) );
412
  exit;
413
  }
414
  }
419
  * @return void
420
  */
421
  public function optin() {
422
+ update_option( $this->client->slug . '_allow_tracking', 'yes' );
423
+ update_option( $this->client->slug . '_tracking_notice', 'hide' );
424
 
425
  $this->clear_schedule_event();
426
  $this->schedule_event();
433
  * @return void
434
  */
435
  public function optout() {
436
+ update_option( $this->client->slug . '_allow_tracking', 'no' );
437
+ update_option( $this->client->slug . '_tracking_notice', 'hide' );
438
 
439
  $this->clear_schedule_event();
440
  }
575
 
576
  $schedules['weekly'] = array(
577
  'interval' => DAY_IN_SECONDS * 7,
578
+ 'display' => 'Once Weekly',
579
  );
580
 
581
  return $schedules;
582
  }
583
 
584
+ /**
585
+ * Plugin activation hook
586
+ *
587
+ * @return void
588
+ */
589
+ public function activate_plugin() {
590
+ $allowed = get_option( $this->client->slug . '_allow_tracking', 'no' );
591
+
592
+ // if it wasn't allowed before, do nothing
593
+ if ( 'yes' !== $allowed ) {
594
+ return;
595
+ }
596
+
597
+ // re-schedule and delete the last sent time so we could force send again
598
+ $hook_name = $this->client->slug . '_tracker_send_event';
599
+ if ( ! wp_next_scheduled( $hook_name ) ) {
600
+ wp_schedule_event( time(), 'weekly', $hook_name );
601
+ }
602
+
603
+ delete_option( $this->client->slug . '_tracking_last_send' );
604
+
605
+ $this->send_tracking_data( true );
606
+ }
607
+
608
  /**
609
  * Clear our options upon deactivation
610
  *
613
  public function deactivation_cleanup() {
614
  $this->clear_schedule_event();
615
 
616
+ if ( 'theme' == $this->client->type ) {
617
+ delete_option( $this->client->slug . '_tracking_last_send' );
618
+ delete_option( $this->client->slug . '_allow_tracking' );
619
+ }
620
+
621
+ delete_option( $this->client->slug . '_tracking_notice' );
622
  }
623
 
624
  /**
631
  public function plugin_action_links( $links ) {
632
 
633
  if ( array_key_exists( 'deactivate', $links ) ) {
634
+ $links['deactivate'] = str_replace( '<a', '<a class="' . $this->client->slug . '-deactivate-link"', $links['deactivate'] );
635
  }
636
 
637
  return $links;
700
  $current_user = wp_get_current_user();
701
 
702
  $data = array(
703
+ 'hash' => $this->client->hash,
704
  'reason_id' => sanitize_text_field( $_POST['reason_id'] ),
705
  'reason_info' => isset( $_REQUEST['reason_info'] ) ? trim( stripslashes( $_REQUEST['reason_info'] ) ) : '',
706
+ 'site' => $this->get_site_name(),
707
+ 'url' => esc_url( home_url() ),
708
  'admin_email' => get_option( 'admin_email' ),
709
  'user_email' => $current_user->user_email,
710
  'first_name' => $current_user->first_name,
711
  'last_name' => $current_user->last_name,
712
  'server' => $this->get_server_info(),
713
  'wp' => $this->get_wp_info(),
714
+ 'ip_address' => $this->get_user_ip_address(),
715
+ 'theme' => get_stylesheet(),
716
+ 'version' => $this->client->project_version,
717
  );
718
 
719
+ // Add metadata
720
+ if ( $extra = $this->get_extra_data() ) {
721
+ $data['extra'] = $extra;
722
+ }
723
+
724
+ $this->client->send_request( $data, 'deactivate' );
725
 
726
  wp_send_json_success();
727
  }
741
  $reasons = $this->get_uninstall_reasons();
742
  ?>
743
 
744
+ <div class="wd-dr-modal" id="<?php echo $this->client->slug; ?>-wd-dr-modal">
745
  <div class="wd-dr-modal-wrap">
746
  <div class="wd-dr-modal-header">
747
+ <h3><?php _e( 'If you have a moment, please let us know why you are deactivating:', $this->client->textdomain ); ?></h3>
748
  </div>
749
 
750
  <div class="wd-dr-modal-body">
755
  </li>
756
  <?php } ?>
757
  </ul>
758
+ <p class="wd-dr-modal-reasons-bottom">We share your data with <a href="https://appsero.com/">Appsero</a> to troubleshoot problems &amp; make product improvements. <a href="https://appsero.com/privacy-policy/">Learn more</a> about how Appsero handles your data.</p>
759
  </div>
760
 
761
  <div class="wd-dr-modal-footer">
762
+ <a href="#" class="dont-bother-me"><?php _e( 'I rather wouldn\'t say', $this->client->textdomain ); ?></a>
763
+ <button class="button-secondary"><?php _e( 'Submit & Deactivate', $this->client->textdomain ); ?></button>
764
+ <button class="button-primary"><?php _e( 'Cancel', $this->client->textdomain ); ?></button>
765
  </div>
766
  </div>
767
  </div>
812
  padding: 12px 20px;
813
  text-align: right;
814
  }
815
+ .wd-dr-modal-reasons-bottom {
816
+ margin: 15px 0 0 0;
817
+ }
818
  </style>
819
 
820
  <script type="text/javascript">
821
  (function($) {
822
  $(function() {
823
+ var modal = $( '#<?php echo $this->client->slug; ?>-wd-dr-modal' );
824
  var deactivateLink = '';
825
 
826
+ $( '#the-list' ).on('click', 'a.<?php echo $this->client->slug; ?>-deactivate-link', function(e) {
827
  e.preventDefault();
828
 
829
  modal.addClass('modal-active');
870
  url: ajaxurl,
871
  type: 'POST',
872
  data: {
873
+ action: '<?php echo $this->client->slug; ?>_submit-uninstall-reason',
874
  reason_id: ( 0 === $radio.length ) ? 'none' : $radio.val(),
875
  reason_info: ( 0 !== $input.length ) ? $input.val().trim() : ''
876
  },
889
 
890
  <?php
891
  }
892
+
893
+ /**
894
+ * Run after theme deactivated
895
+ * @param string $new_name
896
+ * @param object $new_theme
897
+ * @param object $old_theme
898
+ * @return void
899
+ */
900
+ public function theme_deactivated( $new_name, $new_theme, $old_theme ) {
901
+ // Make sure this is appsero theme
902
+ if ( $old_theme->get_template() == $this->client->slug ) {
903
+ $current_user = wp_get_current_user();
904
+
905
+ $data = array(
906
+ 'hash' => $this->client->hash,
907
+ 'reason_id' => 'none',
908
+ 'reason_info' => '',
909
+ 'site' => $this->get_site_name(),
910
+ 'url' => esc_url( home_url() ),
911
+ 'admin_email' => get_option( 'admin_email' ),
912
+ 'user_email' => $current_user->user_email,
913
+ 'first_name' => $current_user->first_name,
914
+ 'last_name' => $current_user->last_name,
915
+ 'server' => $this->get_server_info(),
916
+ 'wp' => $this->get_wp_info(),
917
+ 'ip_address' => $this->get_user_ip_address(),
918
+ 'theme' => get_stylesheet(),
919
+ 'version' => $this->client->project_version,
920
+ );
921
+
922
+ $this->client->send_request( $data, 'deactivate' );
923
+ }
924
+ }
925
+
926
+ /**
927
+ * Get user IP Address
928
+ */
929
+ private function get_user_ip_address() {
930
+ $response = wp_remote_get( 'https://icanhazip.com/' );
931
+
932
+ if ( is_wp_error( $response ) ) {
933
+ return '';
934
+ }
935
+
936
+ $ip = trim( wp_remote_retrieve_body( $response ) );
937
+
938
+ if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
939
+ return '';
940
+ }
941
+
942
+ return $ip;
943
+ }
944
+
945
+ /**
946
+ * Get site name
947
+ */
948
+ private function get_site_name() {
949
+ $site_name = get_bloginfo( 'name' );
950
+
951
+ if ( empty( $site_name ) ) {
952
+ $site_name = get_bloginfo( 'description' );
953
+ $site_name = wp_trim_words( $site_name, 3, '' );
954
+ }
955
+
956
+ if ( empty( $site_name ) ) {
957
+ $site_name = get_bloginfo( 'url' );
958
+ }
959
+
960
+ return $site_name;
961
+ }
962
  }
appsero/src/License.php ADDED
@@ -0,0 +1,701 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Appsero;
3
+
4
+ /**
5
+ * Appsero License Checker
6
+ *
7
+ * This class will check, active and deactive license
8
+ */
9
+ class License {
10
+
11
+ /**
12
+ * AppSero\Client
13
+ *
14
+ * @var object
15
+ */
16
+ protected $client;
17
+
18
+ /**
19
+ * Arguments of create menu
20
+ *
21
+ * @var array
22
+ */
23
+ protected $menu_args;
24
+
25
+ /**
26
+ * `option_name` of `wp_options` table
27
+ *
28
+ * @var string
29
+ */
30
+ protected $option_key;
31
+
32
+ /**
33
+ * Error message of HTTP request
34
+ *
35
+ * @var string
36
+ */
37
+ public $error;
38
+
39
+ /**
40
+ * Success message on form submit
41
+ *
42
+ * @var string
43
+ */
44
+ public $success;
45
+
46
+ /**
47
+ * Corn schedule hook name
48
+ *
49
+ * @var string
50
+ */
51
+ protected $schedule_hook;
52
+
53
+ /**
54
+ * Set value for valid licnese
55
+ *
56
+ * @var boolean
57
+ */
58
+ private $is_valid_licnese = null;
59
+
60
+ /**
61
+ * Initialize the class
62
+ *
63
+ * @param Appsero\Client
64
+ */
65
+ public function __construct( Client $client ) {
66
+ $this->client = $client;
67
+
68
+ $this->option_key = 'appsero_' . md5( $this->client->slug ) . '_manage_license';
69
+
70
+ $this->schedule_hook = $this->client->slug . '_license_check_event';
71
+
72
+ // Run hook to check license status daily
73
+ add_action( $this->schedule_hook, array( $this, 'check_license_status' ) );
74
+
75
+ // Active/Deactive corn schedule
76
+ $this->run_schedule();
77
+ }
78
+
79
+ /**
80
+ * Check license
81
+ *
82
+ * @return boolean
83
+ */
84
+ public function check( $license_key ) {
85
+ $route = 'public/license/' . $this->client->hash . '/check';
86
+
87
+ return $this->send_request( $license_key, $route );
88
+ }
89
+
90
+ /**
91
+ * Active a license
92
+ *
93
+ * @return boolean
94
+ */
95
+ public function activate( $license_key ) {
96
+ $route = 'public/license/' . $this->client->hash . '/activate';
97
+
98
+ return $this->send_request( $license_key, $route );
99
+ }
100
+
101
+ /**
102
+ * Deactivate a license
103
+ *
104
+ * @return boolean
105
+ */
106
+ public function deactivate( $license_key ) {
107
+ $route = 'public/license/' . $this->client->hash . '/deactivate';
108
+
109
+ return $this->send_request( $license_key, $route );
110
+ }
111
+
112
+ /**
113
+ * Send common request
114
+ *
115
+ * @param $license_key
116
+ * @param $route
117
+ *
118
+ * @return array
119
+ */
120
+ protected function send_request( $license_key, $route ) {
121
+ $params = array(
122
+ 'license_key' => $license_key,
123
+ 'url' => esc_url( home_url() ),
124
+ );
125
+
126
+ $response = $this->client->send_request( $params, $route, true );
127
+
128
+ if ( is_wp_error( $response ) ) {
129
+ return array(
130
+ 'success' => false,
131
+ 'error' => $response->get_error_message()
132
+ );
133
+ }
134
+
135
+ $response = json_decode( wp_remote_retrieve_body( $response ), true );
136
+
137
+ if ( empty( $response ) || isset( $response['exception'] )) {
138
+ return array(
139
+ 'success' => false,
140
+ 'error' => 'Unknown error occurred, Please try again.'
141
+ );
142
+ }
143
+
144
+ if ( isset( $response['errors'] ) && isset( $response['errors']['license_key'] ) ) {
145
+ $response = array(
146
+ 'success' => false,
147
+ 'error' => $response['errors']['license_key'][0]
148
+ );
149
+ }
150
+
151
+ return $response;
152
+ }
153
+
154
+ /**
155
+ * Add settings page for license
156
+ *
157
+ * @param array $args
158
+ *
159
+ * @return void
160
+ */
161
+ public function add_settings_page( $args = array() ) {
162
+ $defaults = array(
163
+ 'type' => 'menu', // Can be: menu, options, submenu
164
+ 'page_title' => 'Manage License',
165
+ 'menu_title' => 'Manage License',
166
+ 'capability' => 'manage_options',
167
+ 'menu_slug' => $this->client->slug . '-manage-license',
168
+ 'icon_url' => '',
169
+ 'position' => null,
170
+ 'parent_slug' => '',
171
+ );
172
+
173
+ $this->menu_args = wp_parse_args( $args, $defaults );
174
+
175
+ add_action( 'admin_menu', array( $this, 'admin_menu' ), 99 );
176
+ }
177
+
178
+ /**
179
+ * Admin Menu hook
180
+ *
181
+ * @return void
182
+ */
183
+ public function admin_menu() {
184
+ switch ( $this->menu_args['type'] ) {
185
+ case 'menu':
186
+ $this->add_menu_page();
187
+ break;
188
+
189
+ case 'submenu':
190
+ $this->add_submenu_page();
191
+ break;
192
+
193
+ case 'options':
194
+ $this->add_options_page();
195
+ break;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * License menu output
201
+ */
202
+ public function menu_output() {
203
+
204
+ if ( isset( $_POST['submit'] ) ) {
205
+ $this->license_form_submit( $_POST );
206
+ }
207
+
208
+ $license = get_option( $this->option_key, null );
209
+ $action = ( $license && isset( $license['status'] ) && 'activate' == $license['status'] ) ? 'deactive' : 'active';
210
+ $this->licenses_style();
211
+ ?>
212
+
213
+ <div class="wrap appsero-license-settings-wrapper">
214
+ <h1>License Settings</h1>
215
+
216
+ <?php
217
+ $this->show_license_page_notices();
218
+ do_action( 'before_appsero_license_section' );
219
+ ?>
220
+
221
+ <div class="appsero-license-settings appsero-license-section">
222
+ <?php $this->show_license_page_card_header(); ?>
223
+
224
+ <div class="appsero-license-details">
225
+ <p>Active <strong><?php echo $this->client->name; ?></strong> by your license key to get professional support and automatic update from your WordPress dashboard.</p>
226
+ <form method="post" action="<?php $this->formActionUrl(); ?>" novalidate="novalidate" spellcheck="false">
227
+ <input type="hidden" name="_action" value="<?php echo $action; ?>">
228
+ <input type="hidden" name="_nonce" value="<?php echo wp_create_nonce( $this->client->name ); ?>">
229
+ <div class="license-input-fields">
230
+ <div class="license-input-key">
231
+ <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">
232
+ <path d="m463.75 48.251c-64.336-64.336-169.01-64.335-233.35 1e-3 -43.945 43.945-59.209 108.71-40.181 167.46l-185.82 185.82c-2.813 2.813-4.395 6.621-4.395 10.606v84.858c0 8.291 6.709 15 15 15h84.858c3.984 0 7.793-1.582 10.605-4.395l21.211-21.226c3.237-3.237 4.819-7.778 4.292-12.334l-2.637-22.793 31.582-2.974c7.178-0.674 12.847-6.343 13.521-13.521l2.974-31.582 22.793 2.651c4.233 0.571 8.496-0.85 11.704-3.691 3.193-2.856 5.024-6.929 5.024-11.206v-27.929h27.422c3.984 0 7.793-1.582 10.605-4.395l38.467-37.958c58.74 19.043 122.38 4.929 166.33-39.046 64.336-64.335 64.336-169.01 0-233.35zm-42.435 106.07c-17.549 17.549-46.084 17.549-63.633 0s-17.549-46.084 0-63.633 46.084-17.549 63.633 0 17.548 46.084 0 63.633z"/>
233
+ </svg>
234
+ <input type="text" value="<?php echo $this->get_input_license_value( $action, $license ); ?>"
235
+ placeholder="Enter your license key to activate" name="license_key"
236
+ <?php echo ( 'deactive' == $action ) ? 'readonly="readonly"' : ''; ?>
237
+ />
238
+ </div>
239
+ <button type="submit" name="submit" class="<?php echo 'deactive' == $action ? 'deactive-button' : ''; ?>">
240
+ <?php echo $action == 'active' ? 'Activate License' : 'Deactivate License' ; ?>
241
+ </button>
242
+ </div>
243
+ </form>
244
+
245
+ <?php
246
+ if ( 'deactive' == $action && isset( $license['remaining'] ) ) {
247
+ $this->show_active_license_info( $license );
248
+ }
249
+ ?>
250
+ </div>
251
+ </div> <!-- /.appsero-license-settings -->
252
+
253
+ <?php do_action( 'after_appsero_license_section' ); ?>
254
+ </div>
255
+ <?php
256
+ }
257
+
258
+ /**
259
+ * License form submit
260
+ */
261
+ public function license_form_submit( $form ) {
262
+ if ( ! isset( $form['_nonce'], $form['_action'] ) ) {
263
+ $this->error = "Please add all information";
264
+ return;
265
+ }
266
+
267
+ if ( ! wp_verify_nonce( $form['_nonce'], $this->client->name ) ) {
268
+ $this->error = "You don't have permission to manage license.";
269
+ return;
270
+ }
271
+
272
+ switch ( $form['_action'] ) {
273
+ case 'active':
274
+ $this->active_client_license( $form );
275
+ break;
276
+
277
+ case 'deactive':
278
+ $this->deactive_client_license( $form );
279
+ break;
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Check license status on schedule
285
+ */
286
+ public function check_license_status() {
287
+ $license = get_option( $this->option_key, null );
288
+
289
+ if ( isset( $license['key'] ) && ! empty( $license['key'] ) ) {
290
+ $response = $this->check( $license['key'] );
291
+
292
+ if ( isset( $response['success'] ) && $response['success'] ) {
293
+ $license['status'] = 'activate';
294
+ $license['remaining'] = $response['remaining'];
295
+ $license['activation_limit'] = $response['activation_limit'];
296
+ $license['expiry_days'] = $response['expiry_days'];
297
+ $license['title'] = $response['title'];
298
+ $license['source_id'] = $response['source_identifier'];
299
+ $license['recurring'] = $response['recurring'];
300
+ } else {
301
+ $license['status'] = 'deactivate';
302
+ $license['expiry_days'] = 0;
303
+ }
304
+
305
+ update_option( $this->option_key, $license, false );
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Check this is a valid license
311
+ */
312
+ public function is_valid() {
313
+ if ( null !== $this->is_valid_licnese ) {
314
+ return $this->is_valid_licnese;
315
+ }
316
+
317
+ $license = get_option( $this->option_key, null );
318
+ if ( ! empty( $license['key'] ) && isset( $license['status'] ) && $license['status'] == 'activate' ) {
319
+ $this->is_valid_licnese = true;
320
+ } else {
321
+ $this->is_valid_licnese = false;
322
+ }
323
+
324
+ return $this->is_valid_licnese;
325
+ }
326
+
327
+ /**
328
+ * Check this is a valid license
329
+ */
330
+ public function is_valid_by( $option, $value ) {
331
+ $license = get_option( $this->option_key, null );
332
+
333
+ if ( ! empty( $license['key'] ) && isset( $license['status'] ) && $license['status'] == 'activate' ) {
334
+ if ( isset( $license[ $option ] ) && $license[ $option ] == $value ) {
335
+ return true;
336
+ }
337
+ }
338
+
339
+ return false;
340
+ }
341
+
342
+ /**
343
+ * Styles for licenses page
344
+ */
345
+ private function licenses_style() {
346
+ ?>
347
+ <style type="text/css">
348
+ .appsero-license-section {
349
+ width: 100%;
350
+ max-width: 1100px;
351
+ min-height: 1px;
352
+ box-sizing: border-box;
353
+ }
354
+ .appsero-license-settings {
355
+ background-color: #fff;
356
+ box-shadow: 0px 3px 10px rgba(16, 16, 16, 0.05);
357
+ }
358
+ .appsero-license-settings * {
359
+ box-sizing: border-box;
360
+ }
361
+ .appsero-license-title {
362
+ background-color: #F8FAFB;
363
+ border-bottom: 2px solid #EAEAEA;
364
+ display: flex;
365
+ align-items: center;
366
+ padding: 10px 20px;
367
+ }
368
+ .appsero-license-title svg {
369
+ width: 30px;
370
+ height: 30px;
371
+ fill: #0082BF;
372
+ }
373
+ .appsero-license-title span {
374
+ font-size: 17px;
375
+ color: #444444;
376
+ margin-left: 10px;
377
+ }
378
+ .appsero-license-details {
379
+ padding: 20px;
380
+ }
381
+ .appsero-license-details p {
382
+ font-size: 15px;
383
+ margin: 0 0 20px 0;
384
+ }
385
+ .license-input-key {
386
+ position: relative;
387
+ flex: 0 0 72%;
388
+ max-width: 72%;
389
+ }
390
+ .license-input-key input {
391
+ background-color: #F9F9F9;
392
+ padding: 10px 15px 10px 48px;
393
+ border: 1px solid #E8E5E5;
394
+ border-radius: 3px;
395
+ height: 45px;
396
+ font-size: 16px;
397
+ color: #71777D;
398
+ width: 100%;
399
+ box-shadow: 0 0 0 transparent;
400
+ }
401
+ .license-input-key input:focus {
402
+ outline: 0 none;
403
+ border: 1px solid #E8E5E5;
404
+ box-shadow: 0 0 0 transparent;
405
+ }
406
+ .license-input-key svg {
407
+ width: 22px;
408
+ height: 22px;
409
+ fill: #0082BF;
410
+ position: absolute;
411
+ left: 14px;
412
+ top: 13px;
413
+ }
414
+ .license-input-fields {
415
+ display: flex;
416
+ justify-content: space-between;
417
+ margin-bottom: 30px;
418
+ max-width: 850px;
419
+ width: 100%;
420
+ }
421
+ .license-input-fields button {
422
+ color: #fff;
423
+ font-size: 17px;
424
+ padding: 8px;
425
+ height: 46px;
426
+ background-color: #0082BF;
427
+ border-radius: 3px;
428
+ cursor: pointer;
429
+ flex: 0 0 25%;
430
+ max-width: 25%;
431
+ border: 1px solid #0082BF;
432
+ }
433
+ .license-input-fields button.deactive-button {
434
+ background-color: #E40055;
435
+ border-color: #E40055;
436
+ }
437
+ .license-input-fields button:focus {
438
+ outline: 0 none;
439
+ }
440