WP Google Map - Version 1.7.7

Version Description

  • Security improvement
  • Appsero SDK implement for prompt support to users
Download this release

Release Info

Developer milonfci
Plugin Icon 128x128 WP Google Map
Version 1.7.7
Comparing to
See all releases

Code changes from version 1.7.6 to 1.7.7

appsero/src/Client.php ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.2.0';
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
+ * The Object of Insights Class
79
+ *
80
+ * @var object
81
+ */
82
+ private $insights;
83
+
84
+ /**
85
+ * The Object of Updater Class
86
+ *
87
+ * @var object
88
+ */
89
+ private $updater;
90
+
91
+ /**
92
+ * The Object of License Class
93
+ *
94
+ * @var object
95
+ */
96
+ private $license;
97
+
98
+ /**
99
+ * Initialize the class
100
+ *
101
+ * @param string $hash hash of the plugin
102
+ * @param string $name readable name of the plugin
103
+ * @param string $file main plugin file path
104
+ */
105
+ public function __construct( $hash, $name, $file ) {
106
+ $this->hash = $hash;
107
+ $this->name = $name;
108
+ $this->file = $file;
109
+
110
+ $this->set_basename_and_slug();
111
+ }
112
+
113
+ /**
114
+ * Initialize insights class
115
+ *
116
+ * @return Appsero\Insights
117
+ */
118
+ public function insights() {
119
+
120
+ if ( ! class_exists( __NAMESPACE__ . '\Insights') ) {
121
+ require_once __DIR__ . '/Insights.php';
122
+ }
123
+
124
+ // if already instantiated, return the cached one
125
+ if ( $this->insights ) {
126
+ return $this->insights;
127
+ }
128
+
129
+ $this->insights = new Insights( $this );
130
+
131
+ return $this->insights;
132
+ }
133
+
134
+ /**
135
+ * Initialize plugin/theme updater
136
+ *
137
+ * @return Appsero\Updater
138
+ */
139
+ public function updater() {
140
+
141
+ if ( ! class_exists( __NAMESPACE__ . '\Updater') ) {
142
+ require_once __DIR__ . '/Updater.php';
143
+ }
144
+
145
+ // if already instantiated, return the cached one
146
+ if ( $this->updater ) {
147
+ return $this->updater;
148
+ }
149
+
150
+ $this->updater = new Updater( $this );
151
+
152
+ return $this->updater;
153
+ }
154
+
155
+ /**
156
+ * Initialize license checker
157
+ *
158
+ * @return Appsero\License
159
+ */
160
+ public function license() {
161
+
162
+ if ( ! class_exists( __NAMESPACE__ . '\License') ) {
163
+ require_once __DIR__ . '/License.php';
164
+ }
165
+
166
+ // if already instantiated, return the cached one
167
+ if ( $this->license ) {
168
+ return $this->license;
169
+ }
170
+
171
+ $this->license = new License( $this );
172
+
173
+ return $this->license;
174
+ }
175
+
176
+ /**
177
+ * API Endpoint
178
+ *
179
+ * @return string
180
+ */
181
+ public function endpoint() {
182
+ $endpoint = apply_filters( 'appsero_endpoint', 'https://api.appsero.com' );
183
+
184
+ return trailingslashit( $endpoint );
185
+ }
186
+
187
+ /**
188
+ * Set project basename, slug and version
189
+ *
190
+ * @return void
191
+ */
192
+ protected function set_basename_and_slug() {
193
+
194
+ if ( strpos( $this->file, WP_CONTENT_DIR . '/themes/' ) === false ) {
195
+ $this->basename = plugin_basename( $this->file );
196
+
197
+ list( $this->slug, $mainfile) = explode( '/', $this->basename );
198
+
199
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
200
+
201
+ $plugin_data = get_plugin_data( $this->file );
202
+
203
+ $this->project_version = $plugin_data['Version'];
204
+ $this->type = 'plugin';
205
+ } else {
206
+ $this->basename = str_replace( WP_CONTENT_DIR . '/themes/', '', $this->file );
207
+
208
+ list( $this->slug, $mainfile) = explode( '/', $this->basename );
209
+
210
+ $theme = wp_get_theme( $this->slug );
211
+
212
+ $this->project_version = $theme->version;
213
+ $this->type = 'theme';
214
+ }
215
+
216
+ $this->textdomain = $this->slug;
217
+ }
218
+
219
+ /**
220
+ * Send request to remote endpoint
221
+ *
222
+ * @param array $params
223
+ * @param string $route
224
+ *
225
+ * @return array|WP_Error Array of results including HTTP headers or WP_Error if the request failed.
226
+ */
227
+ public function send_request( $params, $route, $blocking = false ) {
228
+ $url = $this->endpoint() . $route;
229
+
230
+ $headers = array(
231
+ 'user-agent' => 'Appsero/' . md5( esc_url( home_url() ) ) . ';',
232
+ 'Accept' => 'application/json',
233
+ );
234
+
235
+ $response = wp_remote_post( $url, array(
236
+ 'method' => 'POST',
237
+ 'timeout' => 30,
238
+ 'redirection' => 5,
239
+ 'httpversion' => '1.0',
240
+ 'blocking' => $blocking,
241
+ 'headers' => $headers,
242
+ 'body' => array_merge( $params, array( 'client' => $this->version ) ),
243
+ 'cookies' => array()
244
+ ) );
245
+
246
+ return $response;
247
+ }
248
+
249
+ /**
250
+ * Check if the current server is localhost
251
+ *
252
+ * @return boolean
253
+ */
254
+ public function is_local_server() {
255
+ $is_local = in_array( $_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1' ) );
256
+
257
+ return apply_filters( 'appsero_is_local', $is_local );
258
+ }
259
+
260
+ /**
261
+ * Translate function _e()
262
+ */
263
+ public function _etrans( $text ) {
264
+ call_user_func( '_e', $text, $this->textdomain );
265
+ }
266
+
267
+ /**
268
+ * Translate function __()
269
+ */
270
+ public function __trans( $text ) {
271
+ return call_user_func( '__', $text, $this->textdomain );
272
+ }
273
+
274
+ /**
275
+ * Set project textdomain
276
+ */
277
+ public function set_textdomain( $textdomain ) {
278
+ $this->textdomain = $textdomain;
279
+ }
280
+ }
appsero/src/Insights.php ADDED
@@ -0,0 +1,1123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
15
+ *
16
+ * @var string
17
+ */
18
+ public $notice;
19
+
20
+ /**
21
+ * Wheather to the notice or not
22
+ *
23
+ * @var boolean
24
+ */
25
+ protected $show_notice = true;
26
+
27
+ /**
28
+ * If extra data needs to be sent
29
+ *
30
+ * @var array
31
+ */
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
+ /**
58
+ * Don't show the notice
59
+ *
60
+ * @return \self
61
+ */
62
+ public function hide_notice() {
63
+ $this->show_notice = false;
64
+
65
+ return $this;
66
+ }
67
+
68
+ /**
69
+ * Add extra data if needed
70
+ *
71
+ * @param array $data
72
+ *
73
+ * @return \self
74
+ */
75
+ public function add_extra( $data = array() ) {
76
+ $this->extra_data = $data;
77
+
78
+ return $this;
79
+ }
80
+
81
+ /**
82
+ * Set custom notice text
83
+ *
84
+ * @param string $text
85
+ *
86
+ * @return \self
87
+ */
88
+ public function notice( $text ) {
89
+ $this->notice = $text;
90
+
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
+ /**
120
+ * Initialize plugin hooks
121
+ *
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
+ /**
138
+ * Initialize common hooks
139
+ *
140
+ * @return void
141
+ */
142
+ protected function init_common() {
143
+
144
+ if ( $this->show_notice ) {
145
+ // tracking notice
146
+ add_action( 'admin_notices', array( $this, 'admin_notice' ) );
147
+ }
148
+
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
+ *
163
+ * @param boolean $override
164
+ *
165
+ * @return void
166
+ */
167
+ public function send_tracking_data( $override = false ) {
168
+ if ( ! $this->tracking_allowed() && ! $override ) {
169
+ return;
170
+ }
171
+
172
+ // Send a maximum of once per week
173
+ $last_send = $this->get_last_send();
174
+
175
+ if ( $last_send && $last_send > strtotime( '-1 week' ) ) {
176
+ return;
177
+ }
178
+
179
+ $tracking_data = $this->get_tracking_data();
180
+
181
+ $response = $this->client->send_request( $tracking_data, 'track' );
182
+
183
+ update_option( $this->client->slug . '_tracking_last_send', time() );
184
+ }
185
+
186
+ /**
187
+ * Get the tracking data points
188
+ *
189
+ * @return array
190
+ */
191
+ protected function get_tracking_data() {
192
+ $all_plugins = $this->get_all_plugins();
193
+
194
+ $users = get_users( array(
195
+ 'role' => 'administrator',
196
+ 'orderby' => 'ID',
197
+ 'order' => 'ASC',
198
+ 'number' => 1,
199
+ 'paged' => 1,
200
+ ) );
201
+
202
+ $admin_user = ( is_array( $users ) && ! empty( $users ) ) ? $users[0] : false;
203
+ $first_name = $last_name = '';
204
+
205
+ if ( $admin_user ) {
206
+ $first_name = $admin_user->first_name ? $admin_user->first_name : $admin_user->display_name;
207
+ $last_name = $admin_user->last_name;
208
+ }
209
+
210
+ $data = array(
211
+ 'url' => esc_url( home_url() ),
212
+ 'site' => $this->get_site_name(),
213
+ 'admin_email' => get_option( 'admin_email' ),
214
+ 'first_name' => $first_name,
215
+ 'last_name' => $last_name,
216
+ 'hash' => $this->client->hash,
217
+ 'server' => $this->get_server_info(),
218
+ 'wp' => $this->get_wp_info(),
219
+ 'users' => $this->get_user_counts(),
220
+ 'active_plugins' => count( $all_plugins['active_plugins'] ),
221
+ 'inactive_plugins' => count( $all_plugins['inactive_plugins'] ),
222
+ 'ip_address' => $this->get_user_ip_address(),
223
+ 'project_version' => $this->client->project_version,
224
+ 'tracking_skipped' => false,
225
+ );
226
+
227
+ // Add metadata
228
+ if ( $extra = $this->get_extra_data() ) {
229
+ $data['extra'] = $extra;
230
+ }
231
+
232
+ // Check this has previously skipped tracking
233
+ $skipped = get_option( $this->client->slug . '_tracking_skipped' );
234
+
235
+ if ( $skipped === 'yes' ) {
236
+ delete_option( $this->client->slug . '_tracking_skipped' );
237
+
238
+ $data['tracking_skipped'] = true;
239
+ }
240
+
241
+ return apply_filters( $this->client->slug . '_tracker_data', $data );
242
+ }
243
+
244
+ /**
245
+ * If a child class wants to send extra data
246
+ *
247
+ * @return mixed
248
+ */
249
+ protected function get_extra_data() {
250
+ if ( is_callable( $this->extra_data ) ) {
251
+ return call_user_func( $this->extra_data );
252
+ }
253
+
254
+ if ( is_array( $this->extra_data ) ) {
255
+ return $this->extra_data;
256
+ }
257
+
258
+ return array();
259
+ }
260
+
261
+ /**
262
+ * Explain the user which data we collect
263
+ *
264
+ * @return array
265
+ */
266
+ protected function data_we_collect() {
267
+ $data = array(
268
+ 'Server environment details (php, mysql, server, WordPress versions)',
269
+ 'Number of users in your site',
270
+ 'Site language',
271
+ 'Number of active and inactive plugins',
272
+ 'Site name and url',
273
+ 'Your name and email address',
274
+ );
275
+
276
+ return $data;
277
+ }
278
+
279
+ /**
280
+ * Check if the user has opted into tracking
281
+ *
282
+ * @return bool
283
+ */
284
+ public function tracking_allowed() {
285
+ $allow_tracking = get_option( $this->client->slug . '_allow_tracking', 'no' );
286
+
287
+ return $allow_tracking == 'yes';
288
+ }
289
+
290
+ /**
291
+ * Get the last time a tracking was sent
292
+ *
293
+ * @return false|string
294
+ */
295
+ private function get_last_send() {
296
+ return get_option( $this->client->slug . '_tracking_last_send', false );
297
+ }
298
+
299
+ /**
300
+ * Check if the notice has been dismissed or enabled
301
+ *
302
+ * @return boolean
303
+ */
304
+ public function notice_dismissed() {
305
+ $hide_notice = get_option( $this->client->slug . '_tracking_notice', null );
306
+
307
+ if ( 'hide' == $hide_notice ) {
308
+ return true;
309
+ }
310
+
311
+ return false;
312
+ }
313
+
314
+ /**
315
+ * Check if the current server is localhost
316
+ *
317
+ * @return boolean
318
+ */
319
+ private function is_local_server() {
320
+ return false;
321
+
322
+ $is_local = in_array( $_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1' ) );
323
+
324
+ return apply_filters( 'appsero_is_local', $is_local );
325
+ }
326
+
327
+ /**
328
+ * Schedule the event weekly
329
+ *
330
+ * @return void
331
+ */
332
+ private function schedule_event() {
333
+ $hook_name = $this->client->slug . '_tracker_send_event';
334
+
335
+ if ( ! wp_next_scheduled( $hook_name ) ) {
336
+ wp_schedule_event( time(), 'weekly', $hook_name );
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Clear any scheduled hook
342
+ *
343
+ * @return void
344
+ */
345
+ private function clear_schedule_event() {
346
+ wp_clear_scheduled_hook( $this->client->slug . '_tracker_send_event' );
347
+ }
348
+
349
+ /**
350
+ * Display the admin notice to users that have not opted-in or out
351
+ *
352
+ * @return void
353
+ */
354
+ public function admin_notice() {
355
+
356
+ if ( $this->notice_dismissed() ) {
357
+ return;
358
+ }
359
+
360
+ if ( $this->tracking_allowed() ) {
361
+ return;
362
+ }
363
+
364
+ if ( ! current_user_can( 'manage_options' ) ) {
365
+ return;
366
+ }
367
+
368
+ // don't show tracking if a local server
369
+ if ( $this->is_local_server() ) {
370
+ return;
371
+ }
372
+
373
+ $optin_url = add_query_arg( $this->client->slug . '_tracker_optin', 'true' );
374
+ $optout_url = add_query_arg( $this->client->slug . '_tracker_optout', 'true' );
375
+
376
+ if ( empty( $this->notice ) ) {
377
+ $notice = sprintf( $this->client->__trans( '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->name );
378
+ } else {
379
+ $notice = $this->notice;
380
+ }
381
+
382
+ $policy_url = 'https://' . 'appsero.com/privacy-policy/';
383
+
384
+ $notice .= ' (<a class="' . $this->client->slug . '-insights-data-we-collect" href="#">' . $this->client->__trans( 'what we collect' ) . '</a>)';
385
+ $notice .= '<p class="description" style="display:none;">' . implode( ', ', $this->data_we_collect() ) . '. No sensitive data is tracked. ';
386
+ $notice .= 'We are using Appsero to collect your data. <a href="' . $policy_url . '" target="_blank">Learn more</a> about how Appsero collects and handle your data.</p>';
387
+
388
+ echo '<div class="updated"><p>';
389
+ echo $notice;
390
+ echo '</p><p class="submit">';
391
+ echo '&nbsp;<a href="' . esc_url( $optin_url ) . '" class="button-primary button-large">' . $this->client->__trans( 'Allow' ) . '</a>';
392
+ echo '&nbsp;<a href="' . esc_url( $optout_url ) . '" class="button-secondary button-large">' . $this->client->__trans( 'No thanks' ) . '</a>';
393
+ echo '</p></div>';
394
+
395
+ echo "<script type='text/javascript'>jQuery('." . $this->client->slug . "-insights-data-we-collect').on('click', function(e) {
396
+ e.preventDefault();
397
+ jQuery(this).parents('.updated').find('p.description').slideToggle('fast');
398
+ });
399
+ </script>
400
+ ";
401
+ }
402
+
403
+ /**
404
+ * handle the optin/optout
405
+ *
406
+ * @return void
407
+ */
408
+ public function handle_optin_optout() {
409
+
410
+ if ( isset( $_GET[ $this->client->slug . '_tracker_optin' ] ) && $_GET[ $this->client->slug . '_tracker_optin' ] == 'true' ) {
411
+ $this->optin();
412
+
413
+ wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optin' ) );
414
+ exit;
415
+ }
416
+
417
+ if ( isset( $_GET[ $this->client->slug . '_tracker_optout' ] ) && $_GET[ $this->client->slug . '_tracker_optout' ] == 'true' ) {
418
+ $this->optout();
419
+
420
+ wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optout' ) );
421
+ exit;
422
+ }
423
+ }
424
+
425
+ /**
426
+ * Tracking optin
427
+ *
428
+ * @return void
429
+ */
430
+ public function optin() {
431
+ update_option( $this->client->slug . '_allow_tracking', 'yes' );
432
+ update_option( $this->client->slug . '_tracking_notice', 'hide' );
433
+
434
+ $this->clear_schedule_event();
435
+ $this->schedule_event();
436
+ $this->send_tracking_data();
437
+ }
438
+
439
+ /**
440
+ * Optout from tracking
441
+ *
442
+ * @return void
443
+ */
444
+ public function optout() {
445
+ update_option( $this->client->slug . '_allow_tracking', 'no' );
446
+ update_option( $this->client->slug . '_tracking_notice', 'hide' );
447
+
448
+ $this->send_tracking_skipped_request();
449
+
450
+ $this->clear_schedule_event();
451
+ }
452
+
453
+ /**
454
+ * Get the number of post counts
455
+ *
456
+ * @param string $post_type
457
+ *
458
+ * @return integer
459
+ */
460
+ public function get_post_count( $post_type ) {
461
+ global $wpdb;
462
+
463
+ return (int) $wpdb->get_var( "SELECT count(ID) FROM $wpdb->posts WHERE post_type = '$post_type' and post_status = 'publish'");
464
+ }
465
+
466
+ /**
467
+ * Get server related info.
468
+ *
469
+ * @return array
470
+ */
471
+ private static function get_server_info() {
472
+ global $wpdb;
473
+
474
+ $server_data = array();
475
+
476
+ if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) {
477
+ $server_data['software'] = $_SERVER['SERVER_SOFTWARE'];
478
+ }
479
+
480
+ if ( function_exists( 'phpversion' ) ) {
481
+ $server_data['php_version'] = phpversion();
482
+ }
483
+
484
+ $server_data['mysql_version'] = $wpdb->db_version();
485
+
486
+ $server_data['php_max_upload_size'] = size_format( wp_max_upload_size() );
487
+ $server_data['php_default_timezone'] = date_default_timezone_get();
488
+ $server_data['php_soap'] = class_exists( 'SoapClient' ) ? 'Yes' : 'No';
489
+ $server_data['php_fsockopen'] = function_exists( 'fsockopen' ) ? 'Yes' : 'No';
490
+ $server_data['php_curl'] = function_exists( 'curl_init' ) ? 'Yes' : 'No';
491
+
492
+ return $server_data;
493
+ }
494
+
495
+ /**
496
+ * Get WordPress related data.
497
+ *
498
+ * @return array
499
+ */
500
+ private function get_wp_info() {
501
+ $wp_data = array();
502
+
503
+ $wp_data['memory_limit'] = WP_MEMORY_LIMIT;
504
+ $wp_data['debug_mode'] = ( defined('WP_DEBUG') && WP_DEBUG ) ? 'Yes' : 'No';
505
+ $wp_data['locale'] = get_locale();
506
+ $wp_data['version'] = get_bloginfo( 'version' );
507
+ $wp_data['multisite'] = is_multisite() ? 'Yes' : 'No';
508
+ $wp_data['theme_slug'] = get_stylesheet();
509
+
510
+ $theme = wp_get_theme( $wp_data['theme_slug'] );
511
+
512
+ $wp_data['theme_name'] = $theme->get( 'Name' );
513
+ $wp_data['theme_version'] = $theme->get( 'Version' );
514
+ $wp_data['theme_uri'] = $theme->get( 'ThemeURI' );
515
+ $wp_data['theme_author'] = $theme->get( 'Author' );
516
+
517
+ return $wp_data;
518
+ }
519
+
520
+ /**
521
+ * Get the list of active and inactive plugins
522
+ *
523
+ * @return array
524
+ */
525
+ private function get_all_plugins() {
526
+ // Ensure get_plugins function is loaded
527
+ if ( ! function_exists( 'get_plugins' ) ) {
528
+ include ABSPATH . '/wp-admin/includes/plugin.php';
529
+ }
530
+
531
+ $plugins = get_plugins();
532
+ $active_plugins_keys = get_option( 'active_plugins', array() );
533
+ $active_plugins = array();
534
+
535
+ foreach ( $plugins as $k => $v ) {
536
+ // Take care of formatting the data how we want it.
537
+ $formatted = array();
538
+ $formatted['name'] = strip_tags( $v['Name'] );
539
+
540
+ if ( isset( $v['Version'] ) ) {
541
+ $formatted['version'] = strip_tags( $v['Version'] );
542
+ }
543
+
544
+ if ( isset( $v['Author'] ) ) {
545
+ $formatted['author'] = strip_tags( $v['Author'] );
546
+ }
547
+
548
+ if ( isset( $v['Network'] ) ) {
549
+ $formatted['network'] = strip_tags( $v['Network'] );
550
+ }
551
+
552
+ if ( isset( $v['PluginURI'] ) ) {
553
+ $formatted['plugin_uri'] = strip_tags( $v['PluginURI'] );
554
+ }
555
+
556
+ if ( in_array( $k, $active_plugins_keys ) ) {
557
+ // Remove active plugins from list so we can show active and inactive separately
558
+ unset( $plugins[$k] );
559
+ $active_plugins[$k] = $formatted;
560
+ } else {
561
+ $plugins[$k] = $formatted;
562
+ }
563
+ }
564
+
565
+ return array( 'active_plugins' => $active_plugins, 'inactive_plugins' => $plugins );
566
+ }
567
+
568
+ /**
569
+ * Get user totals based on user role.
570
+ *
571
+ * @return array
572
+ */
573
+ public function get_user_counts() {
574
+ $user_count = array();
575
+ $user_count_data = count_users();
576
+ $user_count['total'] = $user_count_data['total_users'];
577
+
578
+ // Get user count based on user role
579
+ foreach ( $user_count_data['avail_roles'] as $role => $count ) {
580
+ if ( ! $count ) {
581
+ continue;
582
+ }
583
+
584
+ $user_count[ $role ] = $count;
585
+ }
586
+
587
+ return $user_count;
588
+ }
589
+
590
+ /**
591
+ * Add weekly cron schedule
592
+ *
593
+ * @param array $schedules
594
+ *
595
+ * @return array
596
+ */
597
+ public function add_weekly_schedule( $schedules ) {
598
+
599
+ $schedules['weekly'] = array(
600
+ 'interval' => DAY_IN_SECONDS * 7,
601
+ 'display' => 'Once Weekly',
602
+ );
603
+
604
+ return $schedules;
605
+ }
606
+
607
+ /**
608
+ * Plugin activation hook
609
+ *
610
+ * @return void
611
+ */
612
+ public function activate_plugin() {
613
+ $allowed = get_option( $this->client->slug . '_allow_tracking', 'no' );
614
+
615
+ // if it wasn't allowed before, do nothing
616
+ if ( 'yes' !== $allowed ) {
617
+ return;
618
+ }
619
+
620
+ // re-schedule and delete the last sent time so we could force send again
621
+ $hook_name = $this->client->slug . '_tracker_send_event';
622
+ if ( ! wp_next_scheduled( $hook_name ) ) {
623
+ wp_schedule_event( time(), 'weekly', $hook_name );
624
+ }
625
+
626
+ delete_option( $this->client->slug . '_tracking_last_send' );
627
+
628
+ $this->send_tracking_data( true );
629
+ }
630
+
631
+ /**
632
+ * Clear our options upon deactivation
633
+ *
634
+ * @return void
635
+ */
636
+ public function deactivation_cleanup() {
637
+ $this->clear_schedule_event();
638
+
639
+ if ( 'theme' == $this->client->type ) {
640
+ delete_option( $this->client->slug . '_tracking_last_send' );
641
+ delete_option( $this->client->slug . '_allow_tracking' );
642
+ }
643
+
644
+ delete_option( $this->client->slug . '_tracking_notice' );
645
+ }
646
+
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', '<a class="' . $this->client->slug . '-deactivate-link"', $links['deactivate'] );
658
+ }
659
+
660
+ return $links;
661
+ }
662
+
663
+ /**
664
+ * Plugin uninstall reasons
665
+ *
666
+ * @return array
667
+ */
668
+ private function get_uninstall_reasons() {
669
+ $reasons = array(
670
+ array(
671
+ 'id' => 'could-not-understand',
672
+ 'text' => $this->client->__trans( "Couldn't understand" ),
673
+ 'placeholder' => $this->client->__trans( 'Would you like us to assist you?' ),
674
+ 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#3B86FF"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 10.6 23 9.6 22.9 8.8 22.7L8.8 22.6C9.3 22.5 9.7 22.3 10 21.9 10.3 21.6 10.4 21.3 10.4 20.9 10.8 21 11.1 21 11.5 21 16.7 21 21 16.7 21 11.5 21 6.3 16.7 2 11.5 2 6.3 2 2 6.3 2 11.5 2 13 2.3 14.3 2.9 15.6 2.7 16 2.4 16.3 2.2 16.8L2.1 17.1 2.1 17.3C2 17.5 2 17.7 2 18 0.7 16.1 0 13.9 0 11.5 0 5.1 5.1 0 11.5 0ZM6 13.6C6 13.7 6.1 13.8 6.1 13.9 6.3 14.5 6.2 15.7 6.1 16.4 6.1 16.6 6 16.9 6 17.1 6 17.1 6.1 17.1 6.1 17.1 7.1 16.9 8.2 16 9.3 15.5 9.8 15.2 10.4 15 10.9 15 11.2 15 11.4 15 11.6 15.2 11.9 15.4 12.1 16 11.6 16.4 11.5 16.5 11.3 16.6 11.1 16.7 10.5 17 9.9 17.4 9.3 17.7 9 17.9 9 18.1 9.1 18.5 9.2 18.9 9.3 19.4 9.3 19.8 9.4 20.3 9.3 20.8 9 21.2 8.8 21.5 8.5 21.6 8.1 21.7 7.9 21.8 7.6 21.9 7.3 21.9L6.5 22C6.3 22 6 21.9 5.8 21.9 5 21.8 4.4 21.5 3.9 20.9 3.3 20.4 3.1 19.6 3 18.8L3 18.5C3 18.2 3 17.9 3.1 17.7L3.1 17.6C3.2 17.1 3.5 16.7 3.7 16.3 4 15.9 4.2 15.4 4.3 15 4.4 14.6 4.4 14.5 4.6 14.2 4.6 13.9 4.7 13.7 4.9 13.6 5.2 13.2 5.7 13.2 6 13.6ZM11.7 11.2C13.1 11.2 14.3 11.7 15.2 12.9 15.3 13 15.4 13.1 15.4 13.2 15.4 13.4 15.3 13.8 15.2 13.8 15 13.9 14.9 13.8 14.8 13.7 14.6 13.5 14.4 13.2 14.1 13.1 13.5 12.6 12.8 12.3 12 12.2 10.7 12.1 9.5 12.3 8.4 12.8 8.3 12.8 8.2 12.8 8.1 12.8 7.9 12.8 7.8 12.4 7.8 12.2 7.7 12.1 7.8 11.9 8 11.8 8.4 11.7 8.8 11.5 9.2 11.4 10 11.2 10.9 11.1 11.7 11.2ZM16.3 5.9C17.3 5.9 18 6.6 18 7.6 18 8.5 17.3 9.3 16.3 9.3 15.4 9.3 14.7 8.5 14.7 7.6 14.7 6.6 15.4 5.9 16.3 5.9ZM8.3 5C9.2 5 9.9 5.8 9.9 6.7 9.9 7.7 9.2 8.4 8.2 8.4 7.3 8.4 6.6 7.7 6.6 6.7 6.6 5.8 7.3 5 8.3 5Z"/></g></g></svg>'
675
+ ),
676
+ array(
677
+ 'id' => 'found-better-plugin',
678
+ 'text' => $this->client->__trans( 'Found a better plugin' ),
679
+ 'placeholder' => $this->client->__trans( 'Which plugin?' ),
680
+ 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#3B86FF"><path d="M17.1 14L22.4 19.3C23.2 20.2 23.2 21.5 22.4 22.4 21.5 23.2 20.2 23.2 19.3 22.4L19.3 22.4 14 17.1C15.3 16.3 16.3 15.3 17.1 14L17.1 14ZM8.6 0C13.4 0 17.3 3.9 17.3 8.6 17.3 13.4 13.4 17.2 8.6 17.2 3.9 17.2 0 13.4 0 8.6 0 3.9 3.9 0 8.6 0ZM8.6 2.2C5.1 2.2 2.2 5.1 2.2 8.6 2.2 12.2 5.1 15.1 8.6 15.1 12.2 15.1 15.1 12.2 15.1 8.6 15.1 5.1 12.2 2.2 8.6 2.2ZM8.6 3.6L8.6 5C6.6 5 5 6.6 5 8.6L5 8.6 3.6 8.6C3.6 5.9 5.9 3.6 8.6 3.6L8.6 3.6Z"/></g></g></svg>',
681
+ ),
682
+ array(
683
+ 'id' => 'not-have-that-feature',
684
+ 'text' => $this->client->__trans( "Missing a specific feature" ),
685
+ 'placeholder' => $this->client->__trans( 'Could you tell us more about that feature?' ),
686
+ 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="17" viewBox="0 0 24 17"><g fill="none"><g fill="#3B86FF"><path d="M19.4 0C19.7 0.6 19.8 1.3 19.8 2 19.8 3.2 19.4 4.4 18.5 5.3 17.6 6.2 16.5 6.7 15.2 6.7 15.2 6.7 15.2 6.7 15.2 6.7 14 6.7 12.9 6.2 12 5.3 11.2 4.4 10.7 3.3 10.7 2 10.7 1.3 10.8 0.6 11.1 0L7.6 0 7 0 6.5 0 6.5 5.7C6.3 5.6 5.9 5.3 5.6 5.1 5 4.6 4.3 4.3 3.5 4.3 3.5 4.3 3.5 4.3 3.4 4.3 1.6 4.4 0 5.9 0 7.9 0 8.6 0.2 9.2 0.5 9.7 1.1 10.8 2.2 11.5 3.5 11.5 4.3 11.5 5 11.2 5.6 10.8 6 10.5 6.3 10.3 6.5 10.2L6.5 10.2 6.5 17 6.5 17 7 17 7.6 17 22.5 17C23.3 17 24 16.3 24 15.5L24 0 19.4 0Z"/></g></g></svg>',
687
+ ),
688
+ array(
689
+ 'id' => 'is-not-working',
690
+ 'text' => $this->client->__trans( 'Not working' ),
691
+ 'placeholder' => $this->client->__trans( 'Could you tell us a bit more whats not working?' ),
692
+ 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#3B86FF"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 5.1 23 0 17.9 0 11.5 0 5.1 5.1 0 11.5 0ZM11.8 14.4C11.2 14.4 10.7 14.8 10.7 15.4 10.7 16 11.2 16.4 11.8 16.4 12.4 16.4 12.8 16 12.8 15.4 12.8 14.8 12.4 14.4 11.8 14.4ZM12 7C10.1 7 9.1 8.1 9 9.6L10.5 9.6C10.5 8.8 11.1 8.3 11.9 8.3 12.7 8.3 13.2 8.8 13.2 9.5 13.2 10.1 13 10.4 12.2 10.9 11.3 11.4 10.9 12 11 12.9L11 13.4 12.5 13.4 12.5 13C12.5 12.4 12.7 12.1 13.5 11.6 14.4 11.1 14.9 10.4 14.9 9.4 14.9 8 13.7 7 12 7Z"/></g></g></svg>',
693
+ ),
694
+ array(
695
+ 'id' => 'looking-for-other',
696
+ 'text' => $this->client->__trans( "Not what I was looking" ),
697
+ 'placeholder' => $this->client->__trans( 'Could you tell us a bit more?' ),
698
+ 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="17" viewBox="0 0 24 17"><g fill="none"><g fill="#3B86FF"><path d="M23.5 9C23.5 9 23.5 8.9 23.5 8.9 23.5 8.9 23.5 8.9 23.5 8.9 23.4 8.6 23.2 8.3 23 8 22.2 6.5 20.6 3.7 19.8 2.6 18.8 1.3 17.7 0 16.1 0 15.7 0 15.3 0.1 14.9 0.2 13.8 0.6 12.6 1.2 12.3 2.7L11.7 2.7C11.4 1.2 10.2 0.6 9.1 0.2 8.7 0.1 8.3 0 7.9 0 6.3 0 5.2 1.3 4.2 2.6 3.4 3.7 1.8 6.5 1 8 0.8 8.3 0.6 8.6 0.5 8.9 0.5 8.9 0.5 8.9 0.5 8.9 0.5 8.9 0.5 9 0.5 9 0.2 9.7 0 10.5 0 11.3 0 14.4 2.5 17 5.5 17 7.3 17 8.8 16.1 9.8 14.8L14.2 14.8C15.2 16.1 16.7 17 18.5 17 21.5 17 24 14.4 24 11.3 24 10.5 23.8 9.7 23.5 9ZM5.5 15C3.6 15 2 13.2 2 11 2 8.8 3.6 7 5.5 7 7.4 7 9 8.8 9 11 9 13.2 7.4 15 5.5 15ZM18.5 15C16.6 15 15 13.2 15 11 15 8.8 16.6 7 18.5 7 20.4 7 22 8.8 22 11 22 13.2 20.4 15 18.5 15Z"/></g></g></svg>',
699
+ ),
700
+ array(
701
+ 'id' => 'did-not-work-as-expected',
702
+ 'text' => $this->client->__trans( "Didn't work as expected" ),
703
+ 'placeholder' => $this->client->__trans( 'What did you expect?' ),
704
+ 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="23" height="23" viewBox="0 0 23 23"><g fill="none"><g fill="#3B86FF"><path d="M11.5 0C17.9 0 23 5.1 23 11.5 23 17.9 17.9 23 11.5 23 5.1 23 0 17.9 0 11.5 0 5.1 5.1 0 11.5 0ZM11.5 2C6.3 2 2 6.3 2 11.5 2 16.7 6.3 21 11.5 21 16.7 21 21 16.7 21 11.5 21 6.3 16.7 2 11.5 2ZM12.5 12.9L12.7 5 10.2 5 10.5 12.9 12.5 12.9ZM11.5 17.4C12.4 17.4 13 16.8 13 15.9 13 15 12.4 14.4 11.5 14.4 10.6 14.4 10 15 10 15.9 10 16.8 10.6 17.4 11.5 17.4Z"/></g></g></svg>',
705
+ ),
706
+ array(
707
+ 'id' => 'other',
708
+ 'text' => $this->client->__trans( 'Others' ),
709
+ 'placeholder' => $this->client->__trans( 'Could you tell us a bit more?' ),
710
+ 'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="23" viewBox="0 0 24 6"><g fill="none"><g fill="#3B86FF"><path d="M3 0C4.7 0 6 1.3 6 3 6 4.7 4.7 6 3 6 1.3 6 0 4.7 0 3 0 1.3 1.3 0 3 0ZM12 0C13.7 0 15 1.3 15 3 15 4.7 13.7 6 12 6 10.3 6 9 4.7 9 3 9 1.3 10.3 0 12 0ZM21 0C22.7 0 24 1.3 24 3 24 4.7 22.7 6 21 6 19.3 6 18 4.7 18 3 18 1.3 19.3 0 21 0Z"/></g></g></svg>',
711
+ ),
712
+ );
713
+
714
+ return $reasons;
715
+ }
716
+
717
+ /**
718
+ * Plugin deactivation uninstall reason submission
719
+ *
720
+ * @return void
721
+ */
722
+ public function uninstall_reason_submission() {
723
+
724
+ if ( ! isset( $_POST['reason_id'] ) ) {
725
+ wp_send_json_error();
726
+ }
727
+
728
+ $data = $this->get_tracking_data();
729
+ $data['reason_id'] = sanitize_text_field( $_POST['reason_id'] );
730
+ $data['reason_info'] = isset( $_REQUEST['reason_info'] ) ? trim( stripslashes( $_REQUEST['reason_info'] ) ) : '';
731
+
732
+ $this->client->send_request( $data, 'deactivate' );
733
+
734
+ wp_send_json_success();
735
+ }
736
+
737
+ /**
738
+ * Handle the plugin deactivation feedback
739
+ *
740
+ * @return void
741
+ */
742
+ public function deactivate_scripts() {
743
+ global $pagenow;
744
+
745
+ if ( 'plugins.php' != $pagenow ) {
746
+ return;
747
+ }
748
+
749
+ $this->deactivation_modal_styles();
750
+ $reasons = $this->get_uninstall_reasons();
751
+ $custom_reasons = apply_filters( 'appsero_custom_deactivation_reasons', array() );
752
+ ?>
753
+
754
+ <div class="wd-dr-modal" id="<?php echo $this->client->slug; ?>-wd-dr-modal">
755
+ <div class="wd-dr-modal-wrap">
756
+ <div class="wd-dr-modal-header">
757
+ <h3><?php $this->client->_etrans( 'Goodbyes are always hard. If you have a moment, please let us know how we can improve.' ); ?></h3>
758
+ </div>
759
+
760
+ <div class="wd-dr-modal-body">
761
+ <ul class="wd-de-reasons">
762
+ <?php foreach ( $reasons as $reason ) { ?>
763
+ <li data-placeholder="<?php echo esc_attr( $reason['placeholder'] ); ?>">
764
+ <label>
765
+ <input type="radio" name="selected-reason" value="<?php echo $reason['id']; ?>">
766
+ <div class="wd-de-reason-icon"><?php echo $reason['icon']; ?></div>
767
+ <div class="wd-de-reason-text"><?php echo $reason['text']; ?></div>
768
+ </label>
769
+ </li>
770
+ <?php } ?>
771
+ </ul>
772
+ <?php if ( $custom_reasons && is_array( $custom_reasons ) ) : ?>
773
+ <ul class="wd-de-reasons wd-de-others-reasons">
774
+ <?php foreach ( $custom_reasons as $reason ) { ?>
775
+ <li data-placeholder="<?php echo esc_attr( $reason['placeholder'] ); ?>" data-customreason="true">
776
+ <label>
777
+ <input type="radio" name="selected-reason" value="<?php echo $reason['id']; ?>">
778
+ <div class="wd-de-reason-icon"><?php echo $reason['icon']; ?></div>
779
+ <div class="wd-de-reason-text"><?php echo $reason['text']; ?></div>
780
+ </label>
781
+ </li>
782
+ <?php } ?>
783
+ </ul>
784
+ <?php endif; ?>
785
+ <div class="wd-dr-modal-reason-input"><textarea></textarea></div>
786
+ <p class="wd-dr-modal-reasons-bottom">
787
+ <?php
788
+ echo sprintf(
789
+ $this->client->__trans( 'We share your data with <a href="%1$s" target="_blank">Appsero</a> to troubleshoot problems &amp; make product improvements. <a href="%2$s" target="_blank">Learn more</a> about how Appsero handles your data.'),
790
+ esc_url( 'https://appsero.com/' ),
791
+ esc_url( 'https://appsero.com/privacy-policy' )
792
+ );
793
+ ?>
794
+ </p>
795
+ </div>
796
+
797
+ <div class="wd-dr-modal-footer">
798
+ <a href="#" class="dont-bother-me wd-dr-button-secondary"><?php $this->client->_etrans( "Skip & Deactivate" ); ?></a>
799
+ <button class="wd-dr-button-secondary wd-dr-cancel-modal"><?php $this->client->_etrans( 'Cancel' ); ?></button>
800
+ <button class="wd-dr-submit-modal"><?php $this->client->_etrans( 'Submit & Deactivate' ); ?></button>
801
+ </div>
802
+ </div>
803
+ </div>
804
+
805
+ <script type="text/javascript">
806
+ (function($) {
807
+ $(function() {
808
+ var modal = $( '#<?php echo $this->client->slug; ?>-wd-dr-modal' );
809
+ var deactivateLink = '';
810
+
811
+ // Open modal
812
+ $( '#the-list' ).on('click', 'a.<?php echo $this->client->slug; ?>-deactivate-link', function(e) {
813
+ e.preventDefault();
814
+
815
+ modal.addClass('modal-active');
816
+ deactivateLink = $(this).attr('href');
817
+ modal.find('a.dont-bother-me').attr('href', deactivateLink).css('float', 'left');
818
+ });
819
+
820
+ // Close modal; Cancel
821
+ modal.on('click', 'button.wd-dr-cancel-modal', function(e) {
822
+ e.preventDefault();
823
+ modal.removeClass('modal-active');
824
+ });
825
+
826
+ // Reason change
827
+ modal.on('click', 'input[type="radio"]', function () {
828
+ var parent = $(this).parents('li');
829
+ var isCustomReason = parent.data('customreason');
830
+ var inputValue = $(this).val();
831
+
832
+ if ( isCustomReason ) {
833
+ $('ul.wd-de-reasons.wd-de-others-reasons li').removeClass('wd-de-reason-selected');
834
+ } else {
835
+ $('ul.wd-de-reasons li').removeClass('wd-de-reason-selected');
836
+
837
+ if ( "other" != inputValue ) {
838
+ $('ul.wd-de-reasons.wd-de-others-reasons').css('display', 'none');
839
+ }
840
+ }
841
+
842
+ // Show if has custom reasons
843
+ if ( "other" == inputValue ) {
844
+ $('ul.wd-de-reasons.wd-de-others-reasons').css('display', 'flex');
845
+ }
846
+
847
+ parent.addClass('wd-de-reason-selected');
848
+ $('.wd-dr-modal-reason-input').show();
849
+
850
+ $('.wd-dr-modal-reason-input textarea').attr('placeholder', parent.data('placeholder')).focus();
851
+ });
852
+
853
+ // Submit response
854
+ modal.on('click', 'button.wd-dr-submit-modal', function(e) {
855
+ e.preventDefault();
856
+
857
+ var button = $(this);
858
+
859
+ if ( button.hasClass('disabled') ) {
860
+ return;
861
+ }
862
+
863
+ var $radio = $( 'input[type="radio"]:checked', modal );
864
+ var $input = $('.wd-dr-modal-reason-input textarea');
865
+
866
+ $.ajax({
867
+ url: ajaxurl,
868
+ type: 'POST',
869
+ data: {
870
+ action: '<?php echo $this->client->slug; ?>_submit-uninstall-reason',
871
+ reason_id: ( 0 === $radio.length ) ? 'none' : $radio.val(),
872
+ reason_info: ( 0 !== $input.length ) ? $input.val().trim() : ''
873
+ },
874
+ beforeSend: function() {
875
+ button.addClass('disabled');
876
+ button.text('Processing...');
877
+ },
878
+ complete: function() {
879
+ window.location.href = deactivateLink;
880
+ }
881
+ });
882
+ });
883
+ });
884
+ }(jQuery));
885
+ </script>
886
+
887
+ <?php
888
+ }
889
+
890
+ /**
891
+ * Run after theme deactivated
892
+ * @param string $new_name
893
+ * @param object $new_theme
894
+ * @param object $old_theme
895
+ * @return void
896
+ */
897
+ public function theme_deactivated( $new_name, $new_theme, $old_theme ) {
898
+ // Make sure this is appsero theme
899
+ if ( $old_theme->get_template() == $this->client->slug ) {
900
+ $this->client->send_request( $this->get_tracking_data(), 'deactivate' );
901
+ }
902
+ }
903
+
904
+ /**
905
+ * Get user IP Address
906
+ */
907
+ private function get_user_ip_address() {
908
+ $response = wp_remote_get( 'https://icanhazip.com/' );
909
+
910
+ if ( is_wp_error( $response ) ) {
911
+ return '';
912
+ }
913
+
914
+ $ip = trim( wp_remote_retrieve_body( $response ) );
915
+
916
+ if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
917
+ return '';
918
+ }
919
+
920
+ return $ip;
921
+ }
922
+
923
+ /**
924
+ * Get site name
925
+ */
926
+ private function get_site_name() {
927
+ $site_name = get_bloginfo( 'name' );
928
+
929
+ if ( empty( $site_name ) ) {
930
+ $site_name = get_bloginfo( 'description' );
931
+ $site_name = wp_trim_words( $site_name, 3, '' );
932
+ }
933
+
934
+ if ( empty( $site_name ) ) {
935
+ $site_name = esc_url( home_url() );
936
+ }
937
+
938
+ return $site_name;
939
+ }
940
+
941
+ /**
942
+ * Send request to appsero if user skip to send tracking data
943
+ */
944
+ private function send_tracking_skipped_request() {
945
+ $skipped = get_option( $this->client->slug . '_tracking_skipped' );
946
+
947
+ $data = array(
948
+ 'hash' => $this->client->hash,
949
+ 'previously_skipped' => false,
950
+ );
951
+
952
+ if ( $skipped === 'yes' ) {
953
+ $data['previously_skipped'] = true;
954
+ } else {
955
+ update_option( $this->client->slug . '_tracking_skipped', 'yes' );
956
+ }
957
+
958
+ $this->client->send_request( $data, 'tracking-skipped' );
959
+ }
960
+
961
+ /**
962
+ * Deactivation modal styles
963
+ */
964
+ private function deactivation_modal_styles() {
965
+ ?>
966
+ <style type="text/css">
967
+ .wd-dr-modal {
968
+ position: fixed;
969
+ z-index: 99999;
970
+ top: 0;
971
+ right: 0;
972
+ bottom: 0;
973
+ left: 0;
974
+ background: rgba(0,0,0,0.5);
975
+ display: none;
976
+ box-sizing: border-box;
977
+ overflow: scroll;
978
+ }
979
+ .wd-dr-modal * {
980
+ box-sizing: border-box;
981
+ }
982
+ .wd-dr-modal.modal-active {
983
+ display: block;
984
+ }
985
+ .wd-dr-modal-wrap {
986
+ max-width: 870px;
987
+ width: 100%;
988
+ position: relative;
989
+ margin: 10% auto;
990
+ background: #fff;
991
+ }
992
+ .wd-dr-modal-header {
993
+ border-bottom: 1px solid #E8E8E8;
994
+ padding: 20px 20px 18px 20px;
995
+ }
996
+ .wd-dr-modal-header h3 {
997
+ line-height: 1.8;
998
+ margin: 0;
999
+ color: #4A5568;
1000
+ }
1001
+ .wd-dr-modal-body {
1002
+ padding: 5px 20px 20px 20px;
1003
+ }
1004
+ .wd-dr-modal-body .reason-input {
1005
+ margin-top: 5px;
1006
+ margin-left: 20px;
1007
+ }
1008
+ .wd-dr-modal-footer {
1009
+ border-top: 1px solid #E8E8E8;
1010
+ padding: 20px;
1011
+ text-align: right;
1012
+ }
1013
+ .wd-dr-modal-reasons-bottom {
1014
+ margin: 0;
1015
+ }
1016
+ ul.wd-de-reasons {
1017
+ display: flex;
1018
+ margin: 0 -5px 0 -5px;
1019
+ padding: 15px 0 20px 0;
1020
+ }
1021
+ ul.wd-de-reasons.wd-de-others-reasons {
1022
+ padding-top: 0;
1023
+ display: none;
1024
+ }
1025
+ ul.wd-de-reasons li {
1026
+ padding: 0 5px;
1027
+ margin: 0;
1028
+ width: 14.26%;
1029
+ }
1030
+ ul.wd-de-reasons label {
1031
+ position: relative;
1032
+ border: 1px solid #E8E8E8;
1033
+ border-radius: 4px;
1034
+ display: block;
1035
+ text-align: center;
1036
+ height: 100%;
1037
+ padding: 15px 3px 8px 3px;
1038
+ }
1039
+ ul.wd-de-reasons label:after {
1040
+ width: 0;
1041
+ height: 0;
1042
+ border-left: 8px solid transparent;
1043
+ border-right: 8px solid transparent;
1044
+ border-top: 10px solid #3B86FF;
1045
+ position: absolute;
1046
+ left: 50%;
1047
+ top: 100%;
1048
+ margin-left: -8px;
1049
+ }
1050
+ ul.wd-de-reasons label input[type="radio"] {
1051
+ position: absolute;
1052
+ left: 0;
1053
+ right: 0;
1054
+ visibility: hidden;
1055
+ }
1056
+ .wd-de-reason-text {
1057
+ color: #4A5568;
1058
+ font-size: 13px;
1059
+ }
1060
+ .wd-de-reason-icon {
1061
+ margin-bottom: 7px;
1062
+ }
1063
+ ul.wd-de-reasons li.wd-de-reason-selected label {
1064
+ background-color: #3B86FF;
1065
+ border-color: #3B86FF;
1066
+ }
1067
+ li.wd-de-reason-selected .wd-de-reason-icon svg,
1068
+ li.wd-de-reason-selected .wd-de-reason-icon svg g {
1069
+ fill: #fff;
1070
+ }
1071
+ li.wd-de-reason-selected .wd-de-reason-text {
1072
+ color: #fff;
1073
+ }
1074
+ ul.wd-de-reasons li.wd-de-reason-selected label:after {
1075
+ content: "";
1076
+ }
1077
+ .wd-dr-modal-reason-input {
1078
+ margin-bottom: 15px;
1079
+ display: none;
1080
+ }
1081
+ .wd-dr-modal-reason-input textarea {
1082
+ background: #FAFAFA;
1083
+ border: 1px solid #287EB8;
1084
+ border-radius: 4px;
1085
+ width: 100%;
1086
+ height: 100px;
1087
+ color: #524242;
1088
+ font-size: 13px;
1089
+ line-height: 1.4;
1090
+ padding: 11px 15px;
1091
+ resize: none;
1092
+ }
1093
+ .wd-dr-modal-reason-input textarea:focus {
1094
+ outline: 0 none;
1095
+ box-shadow: 0 0 0;
1096
+ }
1097
+ .wd-dr-button-secondary, .wd-dr-button-secondary:hover {
1098
+ border: 1px solid #EBEBEB;
1099
+ border-radius: 3px;
1100
+ font-size: 13px;
1101
+ line-height: 1.5;
1102
+ color: #718096;
1103
+ padding: 5px 12px;
1104
+ cursor: pointer;
1105
+ background-color: transparent;
1106
+ text-decoration: none;
1107
+ }
1108
+ .wd-dr-submit-modal, .wd-dr-submit-modal:hover {
1109
+ border: 1px solid #3B86FF;
1110
+ background-color: #3B86FF;
1111
+ border-radius: 3px;
1112
+ font-size: 13px;
1113
+ line-height: 1.5;
1114
+ color: #fff;
1115
+ padding: 5px 12px;
1116
+ cursor: pointer;
1117
+ margin-left: 4px;
1118
+ }
1119
+ </style>
1120
+ <?php
1121
+ }
1122
+
1123
+ }
appsero/src/License.php ADDED
@@ -0,0 +1,809 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Appsero;
4
+
5
+ /**
6
+ * Appsero License Checker
7
+ *
8
+ * This class will check, active and deactive license
9
+ */
10
+ class License {
11
+
12
+ /**
13
+ * AppSero\Client
14
+ *
15
+ * @var object
16
+ */
17
+ protected $client;
18
+
19
+ /**
20
+ * Arguments of create menu
21
+ *
22
+ * @var array
23
+ */
24
+ protected $menu_args;
25
+
26
+ /**
27
+ * `option_name` of `wp_options` table
28
+ *
29
+ * @var string
30
+ */
31
+ protected $option_key;
32
+
33
+ /**
34
+ * Error message of HTTP request
35
+ *
36
+ * @var string
37
+ */
38
+ public $error;
39
+
40
+ /**
41
+ * Success message on form submit
42
+ *
43
+ * @var string
44
+ */
45
+ public $success;
46
+
47
+ /**
48
+ * Corn schedule hook name
49
+ *
50
+ * @var string
51
+ */
52
+ protected $schedule_hook;
53
+
54
+ /**
55
+ * Set value for valid licnese
56
+ *
57
+ * @var bool
58
+ */
59
+ private $is_valid_licnese = null;
60
+
61
+ /**
62
+ * Initialize the class
63
+ *
64
+ * @param Appsero\Client
65
+ */
66
+ public function __construct( Client $client ) {
67
+ $this->client = $client;
68
+
69
+ $this->option_key = 'appsero_' . md5( $this->client->slug ) . '_manage_license';
70
+
71
+ $this->schedule_hook = $this->client->slug . '_license_check_event';
72
+
73
+ // Creating WP Ajax Endpoint to refresh license remotely
74
+ add_action( "wp_ajax_appsero_refresh_license_" . $this->client->hash, array( $this, 'refresh_license_api' ) );
75
+
76
+ // Run hook to check license status daily
77
+ add_action( $this->schedule_hook, array( $this, 'check_license_status' ) );
78
+
79
+ // Active/Deactive corn schedule
80
+ $this->run_schedule();
81
+ }
82
+
83
+ /**
84
+ * Set the license option key.
85
+ *
86
+ * If someone wants to override the default generated key.
87
+ *
88
+ * @param string $key
89
+ *
90
+ * @since 1.3.0
91
+ *
92
+ * @return License
93
+ */
94
+ public function set_option_key( $key ) {
95
+ $this->option_key = $key;
96
+
97
+ return $this;
98
+ }
99
+
100
+ /**
101
+ * Get the license key
102
+ *
103
+ * @since 1.3.0
104
+ *
105
+ * @return string|null
106
+ */
107
+ public function get_license() {
108
+ return get_option( $this->option_key, null );
109
+ }
110
+
111
+ /**
112
+ * Check license
113
+ *
114
+ * @return bool
115
+ */
116
+ public function check( $license_key ) {
117
+ $route = 'public/license/' . $this->client->hash . '/check';
118
+
119
+ return $this->send_request( $license_key, $route );
120
+ }
121
+
122
+ /**
123
+ * Active a license
124
+ *
125
+ * @return bool
126
+ */
127
+ public function activate( $license_key ) {
128
+ $route = 'public/license/' . $this->client->hash . '/activate';
129
+
130
+ return $this->send_request( $license_key, $route );
131
+ }
132
+
133
+ /**
134
+ * Deactivate a license
135
+ *
136
+ * @return bool
137
+ */
138
+ public function deactivate( $license_key ) {
139
+ $route = 'public/license/' . $this->client->hash . '/deactivate';
140
+
141
+ return $this->send_request( $license_key, $route );
142
+ }
143
+
144
+ /**
145
+ * Send common request
146
+ *
147
+ * @param $license_key
148
+ * @param $route
149
+ *
150
+ * @return array
151
+ */
152
+ protected function send_request( $license_key, $route ) {
153
+ $params = array(
154
+ 'license_key' => $license_key,
155
+ 'url' => esc_url( home_url() ),
156
+ 'is_local' => $this->client->is_local_server(),
157
+ );
158
+
159
+ $response = $this->client->send_request( $params, $route, true );
160
+
161
+ if ( is_wp_error( $response ) ) {
162
+ return array(
163
+ 'success' => false,
164
+ 'error' => $response->get_error_message()
165
+ );
166
+ }
167
+
168
+ $response = json_decode( wp_remote_retrieve_body( $response ), true );
169
+
170
+ if ( empty( $response ) || isset( $response['exception'] )) {
171
+ return array(
172
+ 'success' => false,
173
+ 'error' => $this->client->__trans( 'Unknown error occurred, Please try again.' ),
174
+ );
175
+ }
176
+
177
+ if ( isset( $response['errors'] ) && isset( $response['errors']['license_key'] ) ) {
178
+ $response = array(
179
+ 'success' => false,
180
+ 'error' => $response['errors']['license_key'][0]
181
+ );
182
+ }
183
+
184
+ return $response;
185
+ }
186
+
187
+ /**
188
+ * License Refresh Endpoint
189
+ */
190
+ public function refresh_license_api() {
191
+ $this->check_license_status();
192
+
193
+ return wp_send_json(
194
+ array(
195
+ 'message' => 'License refreshed successfully.'
196
+ ),
197
+ 200
198
+ );
199
+ }
200
+
201
+ /**
202
+ * Add settings page for license
203
+ *
204
+ * @param array $args
205
+ *
206
+ * @return void
207
+ */
208
+ public function add_settings_page( $args = array() ) {
209
+ $defaults = array(
210
+ 'type' => 'menu', // Can be: menu, options, submenu
211
+ 'page_title' => 'Manage License',
212
+ 'menu_title' => 'Manage License',
213
+ 'capability' => 'manage_options',
214
+ 'menu_slug' => $this->client->slug . '-manage-license',
215
+ 'icon_url' => '',
216
+ 'position' => null,
217
+ 'parent_slug' => '',
218
+ );
219
+
220
+ $this->menu_args = wp_parse_args( $args, $defaults );
221
+
222
+ add_action( 'admin_menu', array( $this, 'admin_menu' ), 99 );
223
+ }
224
+
225
+ /**
226
+ * Admin Menu hook
227
+ *
228
+ * @return void
229
+ */
230
+ public function admin_menu() {
231
+ switch ( $this->menu_args['type'] ) {
232
+ case 'menu':
233
+ $this->create_menu_page();
234
+ break;
235
+
236
+ case 'submenu':
237
+ $this->create_submenu_page();
238
+ break;
239
+
240
+ case 'options':
241
+ $this->create_options_page();
242
+ break;
243
+ }
244
+ }
245
+
246
+ /**
247
+ * License menu output
248
+ */
249
+ public function menu_output() {
250
+ if ( isset( $_POST['submit'] ) ) {
251
+ $this->license_form_submit( $_POST );
252
+ }
253
+
254
+ $license = $this->get_license();
255
+ $action = ( $license && isset( $license['status'] ) && 'activate' == $license['status'] ) ? 'deactive' : 'active';
256
+ $this->licenses_style();
257
+ ?>
258
+
259
+ <div class="wrap appsero-license-settings-wrapper">
260
+ <h1>License Settings</h1>
261
+
262
+ <?php
263
+ $this->show_license_page_notices();
264
+ do_action( 'before_appsero_license_section' );
265
+ ?>
266
+
267
+ <div class="appsero-license-settings appsero-license-section">
268
+ <?php $this->show_license_page_card_header( $license ); ?>
269
+
270
+ <div class="appsero-license-details">
271
+ <p>
272
+ <?php printf( $this->client->__trans( 'Activate <strong>%s</strong> by your license key to get professional support and automatic update from your WordPress dashboard.' ), $this->client->name ); ?>
273
+ </p>
274
+ <form method="post" action="<?php $this->form_action_url(); ?>" novalidate="novalidate" spellcheck="false">
275
+ <input type="hidden" name="_action" value="<?php echo $action; ?>">
276
+ <input type="hidden" name="_nonce" value="<?php echo wp_create_nonce( $this->client->name ); ?>">
277
+ <div class="license-input-fields">
278
+ <div class="license-input-key">
279
+ <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">
280
+ <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"/>
281
+ </svg>
282
+ <input type="text" value="<?php echo $this->get_input_license_value( $action, $license ); ?>"
283
+ placeholder="<?php echo esc_attr( $this->client->__trans( 'Enter your license key to activate' ) ); ?>" name="license_key"
284
+ <?php echo ( 'deactive' == $action ) ? 'readonly="readonly"' : ''; ?>
285
+ />
286
+ </div>
287
+ <button type="submit" name="submit" class="<?php echo 'deactive' == $action ? 'deactive-button' : ''; ?>">
288
+ <?php echo $action == 'active' ? $this->client->__trans( 'Activate License' ) : $this->client->__trans( 'Deactivate License' ); ?>
289
+ </button>
290
+ </div>
291
+ </form>
292
+
293
+ <?php
294
+ if ( 'deactive' == $action && isset( $license['remaining'] ) ) {
295
+ $this->show_active_license_info( $license );
296
+ } ?>
297
+ </div>
298
+ </div> <!-- /.appsero-license-settings -->
299
+
300
+ <?php do_action( 'after_appsero_license_section' ); ?>
301
+ </div>
302
+ <?php
303
+ }
304
+
305
+ /**
306
+ * License form submit
307
+ */
308
+ public function license_form_submit( $form ) {
309
+ if ( ! isset( $form['_nonce'], $form['_action'] ) ) {
310
+ $this->error = $this->client->__trans( 'Please add all information' );
311
+
312
+ return;
313
+ }
314
+
315
+ if ( ! wp_verify_nonce( $form['_nonce'], $this->client->name ) ) {
316
+ $this->error = $this->client->__trans( "You don't have permission to manage license." );
317
+
318
+ return;
319
+ }
320
+
321
+ switch ( $form['_action'] ) {
322
+ case 'active':
323
+ $this->active_client_license( $form );
324
+ break;
325
+
326
+ case 'deactive':
327
+ $this->deactive_client_license( $form );
328
+ break;
329
+
330
+ case 'refresh':
331
+ $this->refresh_client_license( $form );
332
+ break;
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Check license status on schedule
338
+ */
339
+ public function check_license_status() {
340
+ $license = $this->get_license();
341
+
342
+ if ( isset( $license['key'] ) && ! empty( $license['key'] ) ) {
343
+ $response = $this->check( $license['key'] );
344
+
345
+ if ( isset( $response['success'] ) && $response['success'] ) {
346
+ $license['status'] = 'activate';
347
+ $license['remaining'] = $response['remaining'];
348
+ $license['activation_limit'] = $response['activation_limit'];
349
+ $license['expiry_days'] = $response['expiry_days'];
350
+ $license['title'] = $response['title'];
351
+ $license['source_id'] = $response['source_identifier'];
352
+ $license['recurring'] = $response['recurring'];
353
+ } else {
354
+ $license['status'] = 'deactivate';
355
+ $license['expiry_days'] = 0;
356
+ }
357
+
358
+ update_option( $this->option_key, $license, false );
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Check this is a valid license
364
+ */
365
+ public function is_valid() {
366
+ if ( null !== $this->is_valid_licnese ) {
367
+ return $this->is_valid_licnese;
368
+ }
369
+
370
+ $license = $this->get_license();
371
+
372
+ if ( ! empty( $license['key'] ) && isset( $license['status'] ) && $license['status'] == 'activate' ) {
373
+ $this->is_valid_licnese = true;
374
+ } else {
375
+ $this->is_valid_licnese = false;
376
+ }
377
+
378
+ return $this->is_valid_licnese;
379
+ }
380
+
381
+ /**
382
+ * Check this is a valid license
383
+ */
384
+ public function is_valid_by( $option, $value ) {
385
+ $license = $this->get_license();
386
+
387
+ if ( ! empty( $license['key'] ) && isset( $license['status'] ) && $license['status'] == 'activate' ) {
388
+ if ( isset( $license[ $option ] ) && $license[ $option ] == $value ) {
389
+ return true;
390
+ }
391
+ }
392
+
393
+ return false;
394
+ }
395
+
396
+ /**
397
+ * Styles for licenses page
398
+ */
399
+ private function licenses_style() {
400
+ ?>
401
+ <style type="text/css">
402
+ .appsero-license-section {
403
+ width: 100%;
404
+ max-width: 1100px;
405
+ min-height: 1px;
406
+ box-sizing: border-box;
407
+ }
408
+ .appsero-license-settings {
409
+ background-color: #fff;
410
+ box-shadow: 0px 3px 10px rgba(16, 16, 16, 0.05);
411
+ }
412
+ .appsero-license-settings * {
413
+ box-sizing: border-box;
414
+ }
415
+ .appsero-license-title {
416
+ background-color: #F8FAFB;
417
+ border-bottom: 2px solid #EAEAEA;
418
+ display: flex;
419
+ align-items: center;
420
+ padding: 10px 20px;
421
+ }
422
+ .appsero-license-title svg {
423
+ width: 30px;
424
+ height: 30px;
425
+ fill: #0082BF;
426
+ }
427
+ .appsero-license-title span {
428
+ font-size: 17px;
429
+ color: #444444;
430
+ margin-left: 10px;
431
+ }
432
+ .appsero-license-details {
433
+ padding: 20px;
434
+ }
435
+ .appsero-license-details p {
436
+ font-size: 15px;
437
+ margin: 0 0 20px 0;
438
+ }
439
+ .license-input-key {
440
+ position: relative;
441
+ flex: 0 0 72%;
442
+ max-width: 72%;
443
+ }
444
+ .license-input-key input {
445
+ background-color: #F9F9F9;
446
+ padding: 10px 15px 10px 48px;
447
+ border: 1px solid #E8E5E5;
448
+ border-radius: 3px;
449
+ height: 45px;
450
+ font-size: 16px;
451
+ color: #71777D;
452
+ width: 100%;
453
+ box-shadow: 0 0 0 transparent;
454
+ }
455
+ .license-input-key input:focus {
456
+ outline: 0 none;
457
+ border: 1px solid #E8E5E5;
458
+ box-shadow: 0 0 0 transparent;
459
+ }
460
+ .license-input-key svg {
461
+ width: 22px;
462
+ height: 22px;
463
+ fill: #0082BF;
464
+ position: absolute;
465
+ left: 14px;
466
+ top: 13px;
467
+ }
468
+ .license-input-fields {
469
+ display: flex;
470
+ justify-content: space-between;
471
+ margin-bottom: 30px;
472
+ max-width: 850px;
473
+ width: 100%;
474
+ }
475
+ .license-input-fields button {
476
+ color: #fff;
477
+ font-size: 17px;
478
+ padding: 8px;
479
+ height: 46px;
480
+ background-color: #0082BF;
481
+ border-radius: 3px;
482
+ cursor: pointer;
483
+ flex: 0 0 25%;
484
+ max-width: 25%;
485
+ border: 1px solid #0082BF;
486
+ }
487
+ .license-input-fields button.deactive-button {
488
+ background-color: #E40055;
489
+ border-color: #E40055;
490
+ }
491
+ .license-input-fields button:focus {
492
+ outline: 0 none;
493
+ }
494
+ .active-license-info {
495
+ display: flex;
496
+ }
497
+ .single-license-info {
498
+ min-width: 220px;
499
+ flex: 0 0 30%;
500
+ }
501
+ .single-license-info h3 {
502
+ font-size: 18px;
503
+ margin: 0 0 12px 0;
504
+ }
505
+ .single-license-info p {
506
+ margin: 0;
507
+ color: #00C000;
508
+ }
509
+ .single-license-info p.occupied {
510
+ color: #E40055;
511
+ }
512
+ .appsero-license-right-form {
513
+ margin-left: auto;
514
+ }
515
+ .appsero-license-refresh-button {
516
+ padding: 6px 10px 4px 10px;
517
+ border: 1px solid #0082BF;
518
+ border-radius: 3px;
519
+ margin-left: auto;
520
+ background-color: #0082BF;
521
+ color: #fff;
522
+ cursor: pointer;
523
+ }
524
+ .appsero-license-refresh-button .dashicons {
525
+ color: #fff;
526
+ margin-left: 0;
527
+ }
528
+ </style>
529
+ <?php
530
+ }
531
+
532
+ /**
533
+ * Show active license information
534
+ */
535
+ private function show_active_license_info( $license ) {
536
+ ?>
537
+ <div class="active-license-info">
538
+ <div class="single-license-info">
539
+ <h3><?php $this->client->_etrans( 'Activations Remaining' ); ?></h3>
540
+ <?php if ( empty( $license['activation_limit'] ) ) { ?>
541
+ <p><?php $this->client->_etrans( 'Unlimited' ); ?></p>
542
+ <?php } else { ?>
543
+ <p class="<?php echo $license['remaining'] ? '' : 'occupied'; ?>">
544
+ <?php printf( $this->client->__trans( '%1$d out of %2$d' ), $license['remaining'], $license['activation_limit'] ); ?>
545
+ </p>
546
+ <?php } ?>
547
+ </div>
548
+ <div class="single-license-info">
549
+ <h3><?php $this->client->_etrans( 'Expires in' ); ?></h3>
550
+ <?php
551
+ if ( false !== $license['expiry_days'] ) {
552
+ $occupied = $license['expiry_days'] > 21 ? '' : 'occupied';
553
+ echo '<p class="' . $occupied . '">' . $license['expiry_days'] . ' days</p>';
554
+ } else {
555
+ echo '<p>' . $this->client->__trans( 'Never' ) . '</p>';
556
+ } ?>
557
+ </div>
558
+ </div>
559
+ <?php
560
+ }
561
+
562
+ /**
563
+ * Show license settings page notices
564
+ */
565
+ private function show_license_page_notices() {
566
+ if ( ! empty( $this->error ) ) {
567
+ ?>
568
+ <div class="notice notice-error is-dismissible appsero-license-section">
569
+ <p><?php echo $this->error; ?></p>
570
+ </div>
571
+ <?php
572
+ }
573
+
574
+ if ( ! empty( $this->success ) ) {
575
+ ?>
576
+ <div class="notice notice-success is-dismissible appsero-license-section">
577
+ <p><?php echo $this->success; ?></p>
578
+ </div>
579
+ <?php
580
+ }
581
+ echo '<br />';
582
+ }
583
+
584
+ /**
585
+ * Card header
586
+ */
587
+ private function show_license_page_card_header( $license ) {
588
+ ?>
589
+ <div class="appsero-license-title">
590
+ <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">
591
+ <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"/>
592
+ <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"/>
593
+ <path d="m150 1e-3c-82.839 0-150 67.158-150 150 0 82.837 67.156 150 150 150s150-67.161 150-150c0-82.839-67.161-150-150-150zm46.09 227.12h-92.173c-9.734 0-17.626-7.892-17.626-17.629v-56.919c0-8.491 6.007-15.582 14.003-17.25v-25.697c0-27.409 22.3-49.711 49.711-49.711 27.409 0 49.709 22.3 49.709 49.711v25.697c7.993 1.673 14 8.759 14 17.25v56.919h2e-3c0 9.736-7.892 17.629-17.626 17.629z"/>
594
+ </svg>
595
+ <span><?php echo $this->client->__trans( 'Activate License' ); ?></span>
596
+
597
+ <?php if ( $license && $license['key'] ) : ?>
598
+ <form method="post" class="appsero-license-right-form" action="<?php $this->form_action_url(); ?>" novalidate="novalidate" spellcheck="false">
599
+ <input type="hidden" name="_action" value="refresh">
600
+ <input type="hidden" name="_nonce" value="<?php echo wp_create_nonce( $this->client->name ); ?>">
601
+ <button type="submit" name="submit" class="appsero-license-refresh-button">
602
+ <span class="dashicons dashicons-update"></span>
603
+ <?php echo $this->client->__trans( 'Refresh License' ); ?>
604
+ </button>
605
+ </form>
606
+ <?php endif; ?>
607
+
608
+ </div>
609
+ <?php
610
+ }
611
+
612
+ /**
613
+ * Active client license
614
+ */
615
+ private function active_client_license( $form ) {
616
+ if ( empty( $form['license_key'] ) ) {
617
+ $this->error = $this->client->__trans( 'The license key field is required.' );
618
+
619
+ return;
620
+ }
621
+
622
+ $license_key = sanitize_text_field( $form['license_key'] );
623
+ $response = $this->activate( $license_key );
624
+
625
+ if ( ! $response['success'] ) {
626
+ $this->error = $response['error'] ? $response['error'] : $this->client->__trans( 'Unknown error occurred.' );
627
+
628
+ return;
629
+ }
630
+
631
+ $data = array(
632
+ 'key' => $license_key,
633
+ 'status' => 'activate',
634
+ 'remaining' => $response['remaining'],
635
+ 'activation_limit' => $response['activation_limit'],
636
+ 'expiry_days' => $response['expiry_days'],
637
+ 'title' => $response['title'],
638
+ 'source_id' => $response['source_identifier'],
639
+ 'recurring' => $response['recurring'],
640
+ );
641
+
642
+ update_option( $this->option_key, $data, false );
643
+
644
+ $this->success = $this->client->__trans( 'License activated successfully.' );
645
+ }
646
+
647
+ /**
648
+ * Deactive client license
649
+ */
650
+ private function deactive_client_license( $form ) {
651
+ $license = $this->get_license();
652
+
653
+ if ( empty( $license['key'] ) ) {
654
+ $this->error = $this->client->__trans( 'License key not found.' );
655
+
656
+ return;
657
+ }
658
+
659
+ $response = $this->deactivate( $license['key'] );
660
+
661
+ $data = array(
662
+ 'key' => '',
663
+ 'status' => 'deactivate',
664
+ );
665
+
666
+ update_option( $this->option_key, $data, false );
667
+
668
+ if ( ! $response['success'] ) {
669
+ $this->error = $response['error'] ? $response['error'] : $this->client->__trans( 'Unknown error occurred.' );
670
+
671
+ return;
672
+ }
673
+
674
+ $this->success = $this->client->__trans( 'License deactivated successfully.' );
675
+ }
676
+
677
+ /**
678
+ * Refresh Client License
679
+ */
680
+ private function refresh_client_license( $form = null ) {
681
+ $license = $this->get_license();
682
+
683
+ if( !$license || ! isset( $license['key'] ) || empty( $license['key'] ) ) {
684
+ $this->error = $this->client->__trans( "License key not found" );
685
+ return;
686
+ }
687
+
688
+ $this->check_license_status();
689
+
690
+ $this->success = $this->client->__trans( 'License refreshed successfully.' );
691
+ }
692
+
693
+ /**
694
+ * Add license menu page
695
+ */
696
+ private function create_menu_page() {
697
+ call_user_func(
698
+ 'add_' . 'menu' . '_page',
699
+ $this->menu_args['page_title'],
700
+ $this->menu_args['menu_title'],
701
+ $this->menu_args['capability'],
702
+ $this->menu_args['menu_slug'],
703
+ array( $this, 'menu_output' ),
704
+ $this->menu_args['icon_url'],
705
+ $this->menu_args['position']
706
+ );
707
+ }
708
+
709
+ /**
710
+ * Add submenu page
711
+ */
712
+ private function create_submenu_page() {
713
+ call_user_func(
714
+ 'add_' . 'submenu' . '_page',
715
+ $this->menu_args['parent_slug'],
716
+ $this->menu_args['page_title'],
717
+ $this->menu_args['menu_title'],
718
+ $this->menu_args['capability'],
719
+ $this->menu_args['menu_slug'],
720
+ array( $this, 'menu_output' ),
721
+ $this->menu_args['position']
722
+ );
723
+ }
724
+
725
+ /**
726
+ * Add submenu page
727
+ */
728
+ private function create_options_page() {
729
+ call_user_func(
730
+ 'add_' . 'options' . '_page',
731
+ $this->menu_args['page_title'],
732
+ $this->menu_args['menu_title'],
733
+ $this->menu_args['capability'],
734
+ $this->menu_args['menu_slug'],
735
+ array( $this, 'menu_output' ),
736
+ $this->menu_args['position']
737
+ );
738
+ }
739
+
740
+ /**
741
+ * Schedule daily sicense checker event
742
+ */
743
+ public function schedule_cron_event() {
744
+ if ( ! wp_next_scheduled( $this->schedule_hook ) ) {
745
+ wp_schedule_event( time(), 'daily', $this->schedule_hook );
746
+
747
+ wp_schedule_single_event( time() + 20, $this->schedule_hook );
748
+ }
749
+ }
750
+
751
+ /**
752
+ * Clear any scheduled hook
753
+ */
754
+ public function clear_scheduler() {
755
+ wp_clear_scheduled_hook( $this->schedule_hook );
756
+ }
757
+
758
+ /**
759
+ * Enable/Disable schedule
760
+ */
761
+ private function run_schedule() {
762
+ switch ( $this->client->type ) {
763
+ case 'plugin':
764
+ register_activation_hook( $this->client->file, array( $this, 'schedule_cron_event' ) );
765
+ register_deactivation_hook( $this->client->file, array( $this, 'clear_scheduler' ) );
766
+ break;
767
+
768
+ case 'theme':
769
+ add_action( 'after_switch_theme', array( $this, 'schedule_cron_event' ) );
770
+ add_action( 'switch_theme', array( $this, 'clear_scheduler' ) );
771
+ break;
772
+ }
773
+ }
774
+
775
+ /**
776
+ * Form action URL
777
+ */
778
+ private function form_action_url() {
779
+ $url = add_query_arg(
780
+ $_GET,
781
+ admin_url( basename( $_SERVER['SCRIPT_NAME'] ) )
782
+ );
783
+
784
+ echo apply_filters( 'appsero_client_license_form_action', $url );
785
+ }
786
+
787
+ /**
788
+ * Get input license key
789
+ *
790
+ * @param $action
791
+ *
792
+ * @return $license
793
+ */
794
+ private function get_input_license_value( $action, $license ) {
795
+ if ( 'active' == $action ) {
796
+ return isset( $license['key'] ) ? $license['key'] : '';
797
+ }
798
+
799
+ if ( 'deactive' == $action ) {
800
+ $key_length = strlen( $license['key'] );
801
+
802
+ return str_pad(
803
+ substr( $license['key'], 0, $key_length / 2 ), $key_length, '*'
804
+ );
805
+ }
806
+
807
+ return '';
808
+ }
809
+ }
appsero/src/Updater.php ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Appsero;
3
+
4
+ /**
5
+ * Appsero Updater
6
+ *
7
+ * This class will show new updates project
8
+ */
9
+ class Updater {
10
+
11
+ /**
12
+ * Appsero\Client
13
+ *
14
+ * @var object
15
+ */
16
+ protected $client;
17
+
18
+ /**
19
+ * Initialize the class
20
+ *
21
+ * @param Appsero\Client
22
+ */
23
+ public function __construct( Client $client ) {
24
+
25
+ $this->client = $client;
26
+ $this->cache_key = 'appsero_' . md5( $this->client->slug ) . '_version_info';
27
+
28
+ // Run hooks.
29
+ if ( $this->client->type == 'plugin' ) {
30
+ $this->run_plugin_hooks();
31
+ } elseif ( $this->client->type == 'theme' ) {
32
+ $this->run_theme_hooks();
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Set up WordPress filter to hooks to get update.
38
+ *
39
+ * @return void
40
+ */
41
+ public function run_plugin_hooks() {
42
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_plugin_update' ) );
43
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
44
+ }
45
+
46
+ /**
47
+ * Set up WordPress filter to hooks to get update.
48
+ *
49
+ * @return void
50
+ */
51
+ public function run_theme_hooks() {
52
+ add_filter( 'pre_set_site_transient_update_themes', array( $this, 'check_theme_update' ) );
53
+ }
54
+
55
+ /**
56
+ * Check for Update for this specific project
57
+ */
58
+ public function check_plugin_update( $transient_data ) {
59
+ global $pagenow;
60
+
61
+ if ( ! is_object( $transient_data ) ) {
62
+ $transient_data = new \stdClass;
63
+ }
64
+
65
+ if ( 'plugins.php' == $pagenow && is_multisite() ) {
66
+ return $transient_data;
67
+ }
68
+
69
+ if ( ! empty( $transient_data->response ) && ! empty( $transient_data->response[ $this->client->basename ] ) ) {
70
+ return $transient_data;
71
+ }
72
+
73
+ $version_info = $this->get_version_info();
74
+
75
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
76
+
77
+ unset( $version_info->sections );
78
+
79
+ // If new version available then set to `response`
80
+ if ( version_compare( $this->client->project_version, $version_info->new_version, '<' ) ) {
81
+ $transient_data->response[ $this->client->basename ] = $version_info;
82
+ } else {
83
+ // If new version is not available then set to `no_update`
84
+ $transient_data->no_update[ $this->client->basename ] = $version_info;
85
+ }
86
+
87
+ $transient_data->last_checked = time();
88
+ $transient_data->checked[ $this->client->basename ] = $this->client->project_version;
89
+ }
90
+
91
+ return $transient_data;
92
+ }
93
+
94
+ /**
95
+ * Get version info from database
96
+ *
97
+ * @return Object or Boolean
98
+ */
99
+ private function get_cached_version_info() {
100
+ global $pagenow;
101
+
102
+ // If updater page then fetch from API now
103
+ if ( 'update-core.php' == $pagenow ) {
104
+ return false; // Force to fetch data
105
+ }
106
+
107
+ $value = get_transient( $this->cache_key );
108
+
109
+ if( ! $value && ! isset( $value->name ) ) {
110
+ return false; // Cache is expired
111
+ }
112
+
113
+ // We need to turn the icons into an array
114
+ if ( isset( $value->icons ) ) {
115
+ $value->icons = (array) $value->icons;
116
+ }
117
+
118
+ // We need to turn the banners into an array
119
+ if ( isset( $value->banners ) ) {
120
+ $value->banners = (array) $value->banners;
121
+ }
122
+
123
+ if ( isset( $value->sections ) ) {
124
+ $value->sections = (array) $value->sections;
125
+ }
126
+
127
+ return $value;
128
+ }
129
+
130
+ /**
131
+ * Set version info to database
132
+ */
133
+ private function set_cached_version_info( $value ) {
134
+ if ( ! $value ) {
135
+ return;
136
+ }
137
+
138
+ set_transient( $this->cache_key, $value, 3 * HOUR_IN_SECONDS );
139
+ }
140
+
141
+ /**
142
+ * Get plugin info from Appsero
143
+ */
144
+ private function get_project_latest_version() {
145
+
146
+ $license = $this->client->license()->get_license();
147
+
148
+ $params = array(
149
+ 'version' => $this->client->project_version,
150
+ 'name' => $this->client->name,
151
+ 'slug' => $this->client->slug,
152
+ 'basename' => $this->client->basename,
153
+ 'license_key' => ! empty( $license ) && isset( $license['key'] ) ? $license['key'] : '',
154
+ );
155
+
156
+ $route = 'update/' . $this->client->hash . '/check';
157
+
158
+ $response = $this->client->send_request( $params, $route, true );
159
+
160
+ if ( is_wp_error( $response ) ) {
161
+ return false;
162
+ }
163
+
164
+ $response = json_decode( wp_remote_retrieve_body( $response ) );
165
+
166
+ if ( ! isset( $response->slug ) ) {
167
+ return false;
168
+ }
169
+
170
+ if ( isset( $response->icons ) ) {
171
+ $response->icons = (array) $response->icons;
172
+ }
173
+
174
+ if ( isset( $response->banners ) ) {
175
+ $response->banners = (array) $response->banners;
176
+ }
177
+
178
+ if ( isset( $response->sections ) ) {
179
+ $response->sections = (array) $response->sections;
180
+ }
181
+
182
+ return $response;
183
+ }
184
+
185
+ /**
186
+ * Updates information on the "View version x.x details" page with custom data.
187
+ *
188
+ * @param mixed $data
189
+ * @param string $action
190
+ * @param object $args
191
+ *
192
+ * @return object $data
193
+ */
194
+ public function plugins_api_filter( $data, $action = '', $args = null ) {
195
+
196
+ if ( $action != 'plugin_information' ) {
197
+ return $data;
198
+ }
199
+
200
+ if ( ! isset( $args->slug ) || ( $args->slug != $this->client->slug ) ) {
201
+ return $data;
202
+ }
203
+
204
+ return $this->get_version_info();
205
+ }
206
+
207
+ /**
208
+ * Check theme upate
209
+ */
210
+ public function check_theme_update( $transient_data ) {
211
+ global $pagenow;
212
+
213
+ if ( ! is_object( $transient_data ) ) {
214
+ $transient_data = new \stdClass;
215
+ }
216
+
217
+ if ( 'themes.php' == $pagenow && is_multisite() ) {
218
+ return $transient_data;
219
+ }
220
+
221
+ if ( ! empty( $transient_data->response ) && ! empty( $transient_data->response[ $this->client->slug ] ) ) {
222
+ return $transient_data;
223
+ }
224
+
225
+ $version_info = $this->get_version_info();
226
+
227
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
228
+
229
+ // If new version available then set to `response`
230
+ if ( version_compare( $this->client->project_version, $version_info->new_version, '<' ) ) {
231
+ $transient_data->response[ $this->client->slug ] = (array) $version_info;
232
+ } else {
233
+ // If new version is not available then set to `no_update`
234
+ $transient_data->no_update[ $this->client->slug ] = (array) $version_info;
235
+ }
236
+
237
+ $transient_data->last_checked = time();
238
+ $transient_data->checked[ $this->client->slug ] = $this->client->project_version;
239
+ }
240
+
241
+ return $transient_data;
242
+ }
243
+
244
+ /**
245
+ * Get version information
246
+ */
247
+ private function get_version_info() {
248
+ $version_info = $this->get_cached_version_info();
249
+
250
+ if ( false === $version_info ) {
251
+ $version_info = $this->get_project_latest_version();
252
+ $this->set_cached_version_info( $version_info );
253
+ }
254
+
255
+ return $version_info;
256
+ }
257
+
258
+ }
includes/shortcodes.php CHANGED
@@ -36,7 +36,7 @@ if (!function_exists('srm_gmap_embed_shortcode')) {
36
  ob_start();
37
  if ($wpgmap_latlng != '') {
38
  if (isset($wpgmap_show_heading) && $wpgmap_show_heading == 1) {
39
- echo "<h1 class='srm_gmap_heading_$count $wpgmap_heading_class'>" . $wpgmap_title . "</h1>";
40
  }
41
  ?>
42
  <script type="text/javascript">
@@ -118,7 +118,7 @@ if (!function_exists('srm_gmap_embed_shortcode')) {
118
  </script>
119
 
120
  <div id="srm_gmp_embed_<?php echo $count; ?>"
121
- style="width:<?php echo $wpgmap_map_width . ' !important'; ?>;height:<?php echo $wpgmap_map_height; ?> !important; ">
122
 
123
  </div>
124
  <?php
36
  ob_start();
37
  if ($wpgmap_latlng != '') {
38
  if (isset($wpgmap_show_heading) && $wpgmap_show_heading == 1) {
39
+ echo "<h1 class='srm_gmap_heading_$count ".esc_attr($wpgmap_heading_class)."'>" . esc_html(strip_tags($wpgmap_title)) . "</h1>";
40
  }
41
  ?>
42
  <script type="text/javascript">
118
  </script>
119
 
120
  <div id="srm_gmp_embed_<?php echo $count; ?>"
121
+ style="width:<?php echo esc_attr($wpgmap_map_width) . ' !important'; ?>;height:<?php echo esc_attr($wpgmap_map_height); ?> !important; ">
122
 
123
  </div>
124
  <?php
includes/traits/MapCRUD.php CHANGED
@@ -13,7 +13,7 @@ trait MapCRUD
13
  $error = '';
14
  // Getting ajax fileds value
15
  $meta_data = array(
16
- 'wpgmap_title' => sanitize_text_field(esc_html($_POST['map_data']['wpgmap_title'])),
17
  'wpgmap_heading_class' => sanitize_text_field(esc_html($_POST['map_data']['wpgmap_heading_class'])),
18
  'wpgmap_show_heading' => sanitize_text_field(esc_html($_POST['map_data']['wpgmap_show_heading'])),
19
  // current marker lat lng
13
  $error = '';
14
  // Getting ajax fileds value
15
  $meta_data = array(
16
+ 'wpgmap_title' => sanitize_text_field(esc_html(strip_tags($_POST['map_data']['wpgmap_title']))),
17
  'wpgmap_heading_class' => sanitize_text_field(esc_html($_POST['map_data']['wpgmap_heading_class'])),
18
  'wpgmap_show_heading' => sanitize_text_field(esc_html($_POST['map_data']['wpgmap_show_heading'])),
19
  // current marker lat lng
readme.txt CHANGED
@@ -3,9 +3,9 @@ Contributors: milonfci
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZBERRKARGNEYA
4
  Tags: google map, map, maps, google maps, map markers, google map plugin, google map embed, google maps plugin, wp google map, map plugin, map embed, best google maps, store locator, map direction, map widget, street view
5
  Requires at least: 2.9
6
- Tested up to: 5.7
7
- Version: 1.7.6
8
- Stable tag: 1.7.6
9
  Requires PHP: 5.3
10
  Text Domain: gmap-embed
11
  License: GPLv2 or later
@@ -185,6 +185,15 @@ Go to **Appearance** =-> **Widget** then you will see a widget named "WP Google
185
  = How do I get a Google Map API key? =
186
  See the [Video](https://www.youtube.com/watch?v=o90H34eacHg) ,hope you will get idea.
187
 
 
 
 
 
 
 
 
 
 
188
  == Screenshots ==
189
 
190
  1. Google Map menu in the left sidebar
@@ -198,8 +207,12 @@ See the [Video](https://www.youtube.com/watch?v=o90H34eacHg) ,hope you will get
198
 
199
  == Changelog ==
200
 
 
 
 
 
201
  = 1.7.6 =
202
- Google Map Info Window auto focus bug fixed cause by google map last update
203
 
204
  = 1.7.5 =
205
  * media_buttons_context deprecated issue resolving
@@ -451,4 +464,4 @@ New version released with new helpful video
451
 
452
 
453
  = 1.0.0 =
454
- * Initial version of WP Google Map Plugin.
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZBERRKARGNEYA
4
  Tags: google map, map, maps, google maps, map markers, google map plugin, google map embed, google maps plugin, wp google map, map plugin, map embed, best google maps, store locator, map direction, map widget, street view
5
  Requires at least: 2.9
6
+ Tested up to: 5.8
7
+ Version: 1.7.7
8
+ Stable tag: 1.7.7
9
  Requires PHP: 5.3
10
  Text Domain: gmap-embed
11
  License: GPLv2 or later
185
  = How do I get a Google Map API key? =
186
  See the [Video](https://www.youtube.com/watch?v=o90H34eacHg) ,hope you will get idea.
187
 
188
+ ## Privacy Policy
189
+ Maps Plugin using Google Maps for WordPress - WP Google Map 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.
190
+
191
+ 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 a great user experience for all our users.
192
+
193
+ Integrating Appsero SDK **DOES NOT IMMEDIATELY** start gathering data, **without confirmation from users in any case.**
194
+
195
+ Learn more about how [Appsero collects and uses this data](https://appsero.com/privacy-policy/).
196
+
197
  == Screenshots ==
198
 
199
  1. Google Map menu in the left sidebar
207
 
208
  == Changelog ==
209
 
210
+ = 1.7.7 =
211
+ * Security improvement
212
+ * Appsero SDK implement for prompt support to users
213
+
214
  = 1.7.6 =
215
+ * Google Map Info Window auto focus bug fixed cause by google map last update
216
 
217
  = 1.7.5 =
218
  * media_buttons_context deprecated issue resolving
464
 
465
 
466
  = 1.0.0 =
467
+ * Initial version of WP Google Map Plugin.
srm_gmap_embed.php CHANGED
@@ -7,7 +7,7 @@
7
  Text Domain: gmap-embed
8
  Domain Path: /languages
9
  Author URI: https://www.srmilon.info?utm_source=wp-plugins&utm_campaign=author-uri&utm_medium=wp-dash
10
- Version: 1.7.6
11
  */
12
 
13
  if (!defined('ABSPATH')) {
@@ -73,4 +73,25 @@ require_once plugin_dir_path(__FILE__) . '/includes/shortcodes.php';
73
  if (isset($pagenow) and ($pagenow == 'post.php' || $pagenow == 'post-new.php')) {
74
  require_once plugin_dir_path(__FILE__) . '/includes/wpgmap_popup_content.php';
75
  }
76
- load_plugin_textdomain('gmap-embed', false, dirname(plugin_basename(__FILE__)) . '/languages');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  Text Domain: gmap-embed
8
  Domain Path: /languages
9
  Author URI: https://www.srmilon.info?utm_source=wp-plugins&utm_campaign=author-uri&utm_medium=wp-dash
10
+ Version: 1.7.7
11
  */
12
 
13
  if (!defined('ABSPATH')) {
73
  if (isset($pagenow) and ($pagenow == 'post.php' || $pagenow == 'post-new.php')) {
74
  require_once plugin_dir_path(__FILE__) . '/includes/wpgmap_popup_content.php';
75
  }
76
+ load_plugin_textdomain('gmap-embed', false, dirname(plugin_basename(__FILE__)) . '/languages');
77
+
78
+ /**
79
+ * Initialize the appsero plugin
80
+ *
81
+ * @return void
82
+ */
83
+ function appsero_init_gmap_embed()
84
+ {
85
+
86
+ if (!class_exists('Appsero\Client')) {
87
+ require_once __DIR__ . '/appsero/src/Client.php';
88
+ }
89
+
90
+ $client = new Appsero\Client('8aa8c415-a0e1-41a2-9f05-1b385c09e90b', 'WP Google Map', __FILE__);
91
+
92
+ // Active insights
93
+ $client->insights()->init();
94
+
95
+ }
96
+
97
+ appsero_init_gmap_embed();