Subscribe2 - Version 10.33

Version Description

(4th June, 2020) =

  • Bump tested upto version 4.4
  • Minimum PHP version set to 5.4

See complete changelog.

Download this release

Release Info

Developer tareq1988
Plugin Icon 128x128 Subscribe2
Version 10.33
Comparing to
See all releases

Code changes from version 10.32 to 10.33

.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ config.codekit
2
+ .idea
3
+ .codekit-cache
4
+ /vendor/
5
+ node_modules
6
+ zip
7
+ .DS_Store
8
+ nbproject
9
+ debug.log
10
+ npm-debug.log
11
+ .netbeans.xml
12
+ .vscode
13
+ build/*
14
+ !build/index.js
ChangeLog.txt → changelog.txt RENAMED
@@ -1,3 +1,8 @@
1
= 10.32 =
2
3
* Bump minimum required WordPress to 4.4
1
+ = 10.33 (4th June, 2020) =
2
+
3
+ * Bump tested upto version 4.4
4
+ * Minimum PHP version set to 5.4
5
+
6
= 10.32 =
7
8
* Bump minimum required WordPress to 4.4
classes/class-mo-admin-notice.php CHANGED
@@ -46,11 +46,11 @@ if ( ! class_exists( 'MO_Admin_Notice' ) ) {
46
);
47
$this->notice_css();
48
$install_url = wp_nonce_url(
49
- admin_url( 'update.php?action=install-plugin&plugin=mailoptin' ),
50
'install-plugin_mailoptin'
51
);
52
53
- $activate_url = wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=mailoptin%2Fmailoptin.php' ), 'activate-plugin_mailoptin/mailoptin.php' );
54
?>
55
<div class="mo-admin-notice notice notice-success">
56
<div class="mo-notice-first-half">
@@ -58,7 +58,7 @@ if ( ! class_exists( 'MO_Admin_Notice' ) ) {
58
<?php
59
printf(
60
// Translators: Mail Optin admin notice
61
- __( 'Free optin form plugin that will %1$sincrease your email list subscribers%2$s and keep them engaged with %1$sautomated and schedule newsletters%2$s.', 'subscribe2' ),
62
'<span class="mo-stylize"><strong>',
63
'</strong></span>'
64
);
@@ -69,16 +69,16 @@ if ( ! class_exists( 'MO_Admin_Notice' ) ) {
69
<div class="mo-notice-other-half">
70
<?php if ( ! $this->is_plugin_installed() ) : ?>
71
<a class="button button-primary button-hero" id="mo-install-mailoptin-plugin" href="<?php echo $install_url; ?>">
72
- <?php _e( 'Install MailOptin Now for Free!', 'subscribe2' ); ?>
73
</a>
74
<?php endif; ?>
75
<?php if ( $this->is_plugin_installed() && ! $this->is_plugin_active() ) : ?>
76
<a class="button button-primary button-hero" id="mo-activate-mailoptin-plugin" href="<?php echo $activate_url; ?>">
77
- <?php _e( 'Activate MailOptin Now!', 'subscribe2' ); ?>
78
</a>
79
<?php endif; ?>
80
<div class="mo-notice-learn-more">
81
- <a target="_blank" href="https://mailoptin.io">Learn more</a>
82
</div>
83
</div>
84
<a href="<?php echo $dismiss_url; ?>">
@@ -107,11 +107,11 @@ if ( ! class_exists( 'MO_Admin_Notice' ) ) {
107
public function is_plugin_installed() {
108
$installed_plugins = get_plugins();
109
110
- return isset( $installed_plugins['mailoptin/mailoptin.php'] );
111
}
112
113
public function is_plugin_active() {
114
- return is_plugin_active( 'mailoptin/mailoptin.php' );
115
}
116
117
public function notice_css() {
46
);
47
$this->notice_css();
48
$install_url = wp_nonce_url(
49
+ admin_url( 'plugin-install.php?s=wemail&tab=search&type=term' ),
50
'install-plugin_mailoptin'
51
);
52
53
+ $activate_url = wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=wemail%2Fwemail.php' ), 'activate-plugin_wemail/wemail.php' );
54
?>
55
<div class="mo-admin-notice notice notice-success">
56
<div class="mo-notice-first-half">
58
<?php
59
printf(
60
// Translators: Mail Optin admin notice
61
+ __( 'Free email newsletter plugin that will %1$sincrease your email list subscribers%2$s and keep them engaged with %1$sautomated and schedule newsletters%2$s.', 'subscribe2' ),
62
'<span class="mo-stylize"><strong>',
63
'</strong></span>'
64
);
69
<div class="mo-notice-other-half">
70
<?php if ( ! $this->is_plugin_installed() ) : ?>
71
<a class="button button-primary button-hero" id="mo-install-mailoptin-plugin" href="<?php echo $install_url; ?>">
72
+ <?php _e( 'Install weMail Now for Free!', 'subscribe2' ); ?>
73
</a>
74
<?php endif; ?>
75
<?php if ( $this->is_plugin_installed() && ! $this->is_plugin_active() ) : ?>
76
<a class="button button-primary button-hero" id="mo-activate-mailoptin-plugin" href="<?php echo $activate_url; ?>">
77
+ <?php _e( 'Activate weMail Now!', 'subscribe2' ); ?>
78
</a>
79
<?php endif; ?>
80
<div class="mo-notice-learn-more">
81
+ <a target="_blank" href="https://getwemail.io">Learn more</a>
82
</div>
83
</div>
84
<a href="<?php echo $dismiss_url; ?>">
107
public function is_plugin_installed() {
108
$installed_plugins = get_plugins();
109
110
+ return isset( $installed_plugins['wemail/wemail.php'] );
111
}
112
113
public function is_plugin_active() {
114
+ return is_plugin_active( 'wemail/wemail.php' );
115
}
116
117
public function notice_css() {
include/appsero/composer.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "appsero/client",
3
+ "description": "Appsero Client",
4
+ "keywords": ["analytics", "wordpress", "plugin", "theme"],
5
+ "type": "library",
6
+ "license": "MIT",
7
+ "authors": [
8
+ {
9
+ "name": "Tareq Hasan",
10
+ "email": "tareq@appsero.com"
11
+ }
12
+ ],
13
+ "autoload": {
14
+ "psr-4": {
15
+ "Appsero\\": "src/"
16
+ }
17
+ },
18
+ "require": {
19
+ "php": ">=5.3"
20
+ }
21
+ }
include/appsero/readme.md ADDED
@@ -0,0 +1,206 @@
1
+ # Appsero - Client
2
+
3
+ - [Installation](#installation)
4
+ - [Insights](#insights)
5
+ - [Dynamic Usage](#dynamic-usage)
6
+
7
+
8
+ ## Installation
9
+
10
+ You can install AppSero Client in two ways, via composer and manually.
11
+
12
+ ### 1. Composer Installation
13
+
14
+ Add dependency in your project (theme/plugin):
15
+
16
+ ```
17
+ composer require appsero/client
18
+ ```
19
+
20
+ Now add `autoload.php` in your file if you haven't done already.
21
+
22
+ ```php
23
+ require __DIR__ . '/vendor/autoload.php';
24
+ ```
25
+
26
+ ### 2. Manual Installation
27
+
28
+ Clone the repository in your project.
29
+
30
+ ```
31
+ cd /path/to/your/project/folder
32
+ git clone https://github.com/AppSero/client.git appsero
33
+ ```
34
+
35
+ Now include the dependencies in your plugin/theme.
36
+
37
+ ```php
38
+ require __DIR__ . '/appsero/src/Client.php';
39
+ ```
40
+
41
+ ## Insights
42
+
43
+ AppSero can be used in both themes and plugins.
44
+
45
+ The `Appsero\Client` class has *three* parameters:
46
+
47
+ ```php
48
+ $client = new Appsero\Client( $hash, $name, $file );
49
+ ```
50
+
51
+ - **hash** (*string*, *required*) - The unique identifier for a plugin or theme.
52
+ - **name** (*string*, *required*) - The name of the plugin or theme.
53
+ - **file** (*string*, *required*) - The **main file** path of the plugin. For theme, path to `functions.php`
54
+
55
+ ### Usage Example
56
+
57
+ Please refer to the **installation** step before start using the class.
58
+
59
+ You can obtain the **hash** for your plugin for the [Appsero Dashboard](https://dashboard.appsero.com). The 3rd parameter **must** have to be the main file of the plugin.
60
+
61
+ ```php
62
+ /**
63
+ * Initialize the tracker
64
+ *
65
+ * @return void
66
+ */
67
+ function appsero_init_tracker_appsero_test() {
68
+
69
+ if ( ! class_exists( 'Appsero\Client' ) ) {
70
+ require_once __DIR__ . '/appsero/src/Client.php';
71
+ }
72
+
73
+ $client = new Appsero\Client( 'a4a8da5b-b419-4656-98e9-4a42e9044891', 'Akismet', __FILE__ );
74
+
75
+ // Active insights
76
+ $client->insights()->init();
77
+
78
+ // Active automatic updater
79
+ $client->updater();
80
+
81
+ // Active license page and checker
82
+ $args = array(
83
+ 'type' => 'options',
84
+ 'menu_title' => 'Akismet',
85
+ 'page_title' => 'Akismet License Settings',
86
+ 'menu_slug' => 'akismet_settings',
87
+ );
88
+ $client->license()->add_settings_page( $args );
89
+ }
90
+
91
+ appsero_init_tracker_appsero_test();
92
+ ```
93
+
94
+ Make sure you call this function directly, never use any action hook to call this function.
95
+
96
+ > For plugins example code that needs to be used on your main plugin file.
97
+ > For themes example code that needs to be used on your themes `functions.php` file.
98
+
99
+ ## More Usage
100
+
101
+ Sometimes you wouldn't want to show the notice, or want to customize the notice message. You can do that as well.
102
+
103
+ ```php
104
+ $client = new Appsero\Client( 'a4a8da5b-b419-4656-98e9-4a42e9044892', 'Twenty Twelve', __FILE__ );
105
+ ```
106
+
107
+ #### 1. Hiding the notice
108
+
109
+ ```php
110
+ $client->insights()
111
+ ->hide_notice()
112
+ ->init();
113
+ ```
114
+
115
+ #### 2. Customizing the notice message
116
+
117
+ ```php
118
+ $client->insights()
119
+ ->notice( 'My Custom Notice Message' )
120
+ ->init();
121
+ ```
122
+
123
+ #### 3. Adding extra data
124
+
125
+ You can add extra metadata from your theme or plugin. In that case, the **keys** has to be whitelisted from the Appsero dashboard.
126
+ `add_extra` method also support callback as parameter, If you need database call then callback is best for you.
127
+
128
+ ```php
129
+ $metadata = array(
130
+ 'key' => 'value',
131
+ 'another' => 'another_value'
132
+ );
133
+ $client->insights()
134
+ ->add_extra( $metadata )
135
+ ->init();
136
+ ```
137
+
138
+ or
139
+
140
+ ```php
141
+ $metadata = function () {
142
+ return array(
143
+ 'key' => 'value',
144
+ 'another' => 'another_value'
145
+ );
146
+ };
147
+ $client->insights()
148
+ ->add_extra( $metadata )
149
+ ->init();
150
+ ```
151
+
152
+ ---
153
+
154
+ ### Check License Validity
155
+
156
+ Check your plugin/theme is using with valid license or not, First create a global variable of `License` object then use it anywhere in your code.
157
+ If you are using it outside of same function make sure you global the variable before using the condition.
158
+
159
+ ```php
160
+ $client = new Appsero\Client( 'a4a8da5b-b419-4656-98e9-4a42e9044892', 'Twenty Twelve', __FILE__ );
161
+
162
+ $args = array(
163
+ 'type' => 'submenu',
164
+ 'menu_title' => 'Twenty Twelve License',
165
+ 'page_title' => 'Twenty Twelve License Settings',
166
+ 'menu_slug' => 'twenty_twelve_settings',
167
+ 'parent_slug' => 'themes.php',
168
+ );
169
+
170
+ global $twenty_twelve_license;
171
+ $twenty_twelve_license = $client->license();
172
+ $twenty_twelve_license->add_settings_page( $args );
173
+
174
+ if ( $twenty_twelve_license->is_valid() ) {
175
+ // Your special code here
176
+ }
177
+
178
+ Or check by pricing plan title
179
+
180
+ if ( $twenty_twelve_license->is_valid_by( 'title', 'Business' ) ) {
181
+ // Your special code here
182
+ }
183
+ ```
184
+
185
+ ### Use your own license form
186
+
187
+ You can easily manage license by creating a form using HTTP request. Call `license_form_submit` method from License object.
188
+
189
+ ```php
190
+ global $twenty_twelve_license; // License object
191
+ $twenty_twelve_license->license_form_submit([
192
+ '_nonce' => wp_create_nonce( 'Twenty Twelve' ), // create a nonce with name
193
+ '_action' => 'active', // active, deactive
194
+ 'license_key' => 'random-license-key', // no need to provide if you want to deactive
195
+ ]);
196
+ if ( ! $twenty_twelve_license->error ) {
197
+ // license activated
198
+ $twenty_twelve_license->success; // Success message is here
199
+ } else {
200
+ $twenty_twelve_license->error; // has error message here
201
+ }
202
+ ```
203
+
204
+ ## Credits
205
+
206
+ Created and maintained by [Appsero](https://appsero.com).
include/appsero/src/Client.php ADDED
@@ -0,0 +1,233 @@
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.11';
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
+ /**
211
+ * Check if the current server is localhost
212
+ *
213
+ * @return boolean
214
+ */
215
+ public function is_local_server() {
216
+ return in_array( $_SERVER['REMOTE_ADDR'], array( '127.0.0.1', '::1' ) );
217
+ }
218
+
219
+ /**
220
+ * Translate function _e()
221
+ */
222
+ public function _etrans( $text ) {
223
+ call_user_func( '_e', $text, $this->textdomain );
224
+ }
225
+
226
+ /**
227
+ * Translate function __()
228
+ */
229
+ public function __trans( $text ) {
230
+ return call_user_func( '__', $text, $this->textdomain );
231
+ }
232
+
233
+ }
include/appsero/src/Insights.php ADDED
@@ -0,0 +1,972 @@
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
+ // skip on AJAX Requests
169
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
170
+ return;
171
+ }
172
+
173
+ if ( ! $this->tracking_allowed() && ! $override ) {
174
+ return;
175
+ }
176
+
177
+ // Send a maximum of once per week
178
+ $last_send = $this->get_last_send();
179
+
180
+ if ( $last_send && $last_send > strtotime( '-1 week' ) ) {
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
+ /**
190
+ * Get the tracking data points
191
+ *
192
+ * @return array
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;
210
+ $last_name = $admin_user->last_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
+ /**
240
+ * If a child class wants to send extra data
241
+ *
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
+ /**
257
+ * Explain the user which data we collect
258
+ *
259
+ * @return string
260
+ */
261
+ protected function data_we_collect() {
262
+ $data = array(
263
+ 'Server environment details (php, mysql, server, WordPress versions)',
264
+ 'Number of users in your site',
265
+ 'Site language',
266
+ 'Number of active and inactive plugins',
267
+ 'Site name and url',
268
+ 'Your name and email address',
269
+ );
270
+
271
+ return $data;
272
+ }
273
+
274
+ /**
275
+ * Check if the user has opted into tracking
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
+ }
284
+
285
+ /**
286
+ * Get the last time a tracking was sent
287
+ *
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
+ /**
295
+ * Check if the notice has been dismissed or enabled
296
+ *
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;
304
+ }
305
+
306
+ return false;
307
+ }
308
+
309
+ /**
310
+ * Check if the current server is localhost
311
+ *
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 );
320
+ }
321
+
322
+ /**
323
+ * Schedule the event weekly
324
+ *
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
+ /**
336
+ * Clear any scheduled hook
337
+ *
338
+ * @return void
339
+ */
340
+ private function clear_schedule_event() {
341
+ wp_clear_scheduled_hook( $this->client->slug . '_tracker_send_event' );
342
+ }
343
+
344
+ /**
345
+ * Display the admin notice to users that have not opted-in or out
346
+ *
347
+ * @return void
348
+ */
349
+ public function admin_notice() {
350
+
351
+ if ( $this->notice_dismissed() ) {
352
+ return;
353
+ }
354
+
355
+ if ( $this->tracking_allowed() ) {
356
+ return;
357
+ }
358
+
359
+ if ( ! current_user_can( 'manage_options' ) ) {
360
+ return;
361
+ }
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( $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 );
370
+ } else {
371
+ $notice = $this->notice;
372
+ }
373
+
374
+ $policy_url = 'https://' . 'appsero.com/privacy-policy/';
375
+
376
+ $notice .= ' (<a class="' . $this->client->slug . '-insights-data-we-collect" href="#">' . $this->client->__trans( 'what we collect' ) . '</a>)';
377
+ $notice .= '<p class="description" style="display:none;">' . implode( ', ', $this->data_we_collect() ) . '. No sensitive data is tracked. ';
378
+ $notice .= 'We are using Appsero to collect your data. <a href="' . $policy_url . '">Learn more</a> about how Appsero collects and handle your data.</p>';
379
+
380
+ echo '<div class="updated"><p>';
381
+ echo $notice;
382
+ echo '</p><p class="submit">';
383
+ echo '&nbsp;<a href="' . esc_url( $optin_url ) . '" class="button-primary button-large">' . $this->client->__trans( 'Allow' ) . '</a>';
384
+ echo '&nbsp;<a href="' . esc_url( $optout_url ) . '" class="button-secondary button-large">' . $this->client->__trans( 'No thanks' ) . '</a>';
385
+ echo '</p></div>';
386
+
387
+ echo "<script type='text/javascript'>jQuery('." . $this->client->slug . "-insights-data-we-collect').on('click', function(e) {
388
+ e.preventDefault();
389
+ jQuery(this).parents('.updated').find('p.description').slideToggle('fast');
390
+ });
391
+ </script>
392
+ ";
393
+ }
394
+ }
395
+
396
+ /**
397
+ * handle the optin/optout
398
+ *
399
+ * @return void
400
+ */
401
+ public function handle_optin_optout() {
402
+
403
+ if ( isset( $_GET[ $this->client->slug . '_tracker_optin' ] ) && $_GET[ $this->client->slug . '_tracker_optin' ] == 'true' ) {
404
+ $this->optin();
405
+
406
+ wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optin' ) );
407
+ exit;
408
+ }
409
+
410
+ if ( isset( $_GET[ $this->client->slug . '_tracker_optout' ] ) && $_GET[ $this->client->slug . '_tracker_optout' ] == 'true' ) {
411
+ $this->optout();
412
+
413
+ wp_redirect( remove_query_arg( $this->client->slug . '_tracker_optout' ) );
414
+ exit;
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Tracking optin
420
+ *
421
+ * @return void
422
+ */
423
+ public function optin() {
424
+ update_option( $this->client->slug . '_allow_tracking', 'yes' );
425
+ update_option( $this->client->slug . '_tracking_notice', 'hide' );
426
+
427
+ $this->clear_schedule_event();
428
+ $this->schedule_event();
429
+ $this->send_tracking_data();
430
+ }
431
+
432
+ /**
433
+ * Optout from tracking
434
+ *
435
+ * @return void
436
+ */
437
+ public function optout() {
438
+ update_option( $this->client->slug . '_allow_tracking', 'no' );
439
+ update_option( $this->client->slug . '_tracking_notice', 'hide' );
440
+
441
+ $this->clear_schedule_event();
442
+ }
443
+
444
+ /**
445
+ * Get the number of post counts
446
+ *
447
+ * @param string $post_type
448
+ *
449
+ * @return integer
450
+ */
451
+ public function get_post_count( $post_type ) {
452
+ global $wpdb;
453
+
454
+ return (int) $wpdb->get_var( "SELECT count(ID) FROM $wpdb->posts WHERE post_type = '$post_type' and post_status = 'publish'");
455
+ }
456
+
457
+ /**
458
+ * Get server related info.
459
+ *
460
+ * @return array
461
+ */
462
+ private static function get_server_info() {
463
+ global $wpdb;
464
+
465
+ $server_data = array();
466
+
467
+ if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) {
468
+ $server_data['software'] = $_SERVER['SERVER_SOFTWARE'];
469
+ }
470
+
471
+ if ( function_exists( 'phpversion' ) ) {
472
+ $server_data['php_version'] = phpversion();
473
+ }
474
+
475
+ $server_data['mysql_version'] = $wpdb->db_version();
476
+
477
+ $server_data['php_max_upload_size'] = size_format( wp_max_upload_size() );
478
+ $server_data['php_default_timezone'] = date_default_timezone_get();
479
+ $server_data['php_soap'] = class_exists( 'SoapClient' ) ? 'Yes' : 'No';
480
+ $server_data['php_fsockopen'] = function_exists( 'fsockopen' ) ? 'Yes' : 'No';
481
+ $server_data['php_curl'] = function_exists( 'curl_init' ) ? 'Yes' : 'No';
482
+
483
+ return $server_data;
484
+ }
485
+
486
+ /**
487
+ * Get WordPress related data.
488
+ *
489
+ * @return array
490
+ */
491
+ private function get_wp_info() {
492
+ $wp_data = array();
493
+
494
+ $wp_data['memory_limit'] = WP_MEMORY_LIMIT;
495
+ $wp_data['debug_mode'] = ( defined('WP_DEBUG') && WP_DEBUG ) ? 'Yes' : 'No';
496
+ $wp_data['locale'] = get_locale();
497
+ $wp_data['version'] = get_bloginfo( 'version' );
498
+ $wp_data['multisite'] = is_multisite() ? 'Yes' : 'No';
499
+
500
+ return $wp_data;
501
+ }
502
+
503
+ /**
504
+ * Get the list of active and inactive plugins
505
+ *
506
+ * @return array
507
+ */
508
+ private function get_all_plugins() {
509
+ // Ensure get_plugins function is loaded
510
+ if ( ! function_exists( 'get_plugins' ) ) {
511
+ include ABSPATH . '/wp-admin/includes/plugin.php';
512
+ }
513
+
514
+ $plugins = get_plugins();
515
+ $active_plugins_keys = get_option( 'active_plugins', array() );
516
+ $active_plugins = array();
517
+
518
+ foreach ( $plugins as $k => $v ) {
519
+ // Take care of formatting the data how we want it.
520
+ $formatted = array();
521
+ $formatted['name'] = strip_tags( $v['Name'] );
522
+
523
+ if ( isset( $v['Version'] ) ) {
524
+ $formatted['version'] = strip_tags( $v['Version'] );
525
+ }
526
+
527
+ if ( isset( $v['Author'] ) ) {
528
+ $formatted['author'] = strip_tags( $v['Author'] );
529
+ }
530
+
531
+ if ( isset( $v['Network'] ) ) {
532
+ $formatted['network'] = strip_tags( $v['Network'] );
533
+ }
534
+
535
+ if ( isset( $v['PluginURI'] ) ) {
536
+ $formatted['plugin_uri'] = strip_tags( $v['PluginURI'] );
537
+ }
538
+
539
+ if ( in_array( $k, $active_plugins_keys ) ) {
540
+ // Remove active plugins from list so we can show active and inactive separately
541
+ unset( $plugins[$k] );
542
+ $active_plugins[$k] = $formatted;
543
+ } else {
544
+ $plugins[$k] = $formatted;
545
+ }
546
+ }
547
+
548
+ return array( 'active_plugins' => $active_plugins, 'inactive_plugins' => $plugins );
549
+ }
550
+
551
+ /**
552
+ * Get user totals based on user role.
553
+ *
554
+ * @return array
555
+ */
556
+ public function get_user_counts() {
557
+ $user_count = array();
558
+ $user_count_data = count_users();
559
+ $user_count['total'] = $user_count_data['total_users'];
560
+
561
+ // Get user count based on user role
562
+ foreach ( $user_count_data['avail_roles'] as $role => $count ) {
563
+ $user_count[ $role ] = $count;
564
+ }
565
+
566
+ return $user_count;
567
+ }
568
+
569
+ /**
570
+ * Add weekly cron schedule
571
+ *
572
+ * @param array $schedules
573
+ *
574
+ * @return array
575
+ */
576
+ public function add_weekly_schedule( $schedules ) {
577
+
578
+ $schedules['weekly'] = array(
579
+ 'interval' => DAY_IN_SECONDS * 7,
580
+ 'display' => 'Once Weekly',
581
+ );
582
+
583
+ return $schedules;
584
+ }
585
+
586
+ /**
587
+ * Plugin activation hook
588
+ *
589
+ * @return void
590
+ */
591
+ public function activate_plugin() {
592
+ $allowed = get_option( $this->client->slug . '_allow_tracking', 'no' );
593
+
594
+ // if it wasn't allowed before, do nothing
595
+ if ( 'yes' !== $allowed ) {
596
+ return;
597
+ }
598
+
599
+ // re-schedule and delete the last sent time so we could force send again
600
+ $hook_name = $this->client->slug . '_tracker_send_event';
601
+ if ( ! wp_next_scheduled( $hook_name ) ) {
602
+ wp_schedule_event( time(), 'weekly', $hook_name );
603
+ }
604
+
605
+ delete_option( $this->client->slug . '_tracking_last_send' );
606
+
607
+ $this->send_tracking_data( true );
608
+ }
609
+
610
+ /**
611
+ * Clear our options upon deactivation
612
+ *
613
+ * @return void
614
+ */
615
+ public function deactivation_cleanup() {
616
+ $this->clear_schedule_event();
617
+
618
+ if ( 'theme' == $this->client->type ) {
619
+ delete_option( $this->client->slug . '_tracking_last_send' );
620
+ delete_option( $this->client->slug . '_allow_tracking' );
621
+ }
622
+
623
+ delete_option( $this->client->slug . '_tracking_notice' );
624
+ }
625
+
626
+ /**
627
+ * Hook into action links and modify the deactivate link
628
+ *
629
+ * @param array $links
630
+ *
631
+ * @return array
632
+ */
633
+ public function plugin_action_links( $links ) {
634
+
635
+ if ( array_key_exists( 'deactivate', $links ) ) {
636
+ $links['deactivate'] = str_replace( '<a', '<a class="' . $this->client->slug . '-deactivate-link"', $links['deactivate'] );
637
+ }
638
+
639
+ return $links;
640
+ }
641
+
642
+ /**
643
+ * Plugin uninstall reasons
644
+ *
645
+ * @return array
646
+ */
647
+ private function get_uninstall_reasons() {
648
+ $reasons = array(
649
+ array(
650
+ 'id' => 'could-not-understand',
651
+ 'text' => "I couldn't understand how to make it work",
652
+ 'type' => 'textarea',
653
+ 'placeholder' => 'Would you like us to assist you?'
654
+ ),
655
+ array(
656
+ 'id' => 'found-better-plugin',
657
+ 'text' => 'I found a better plugin',
658
+ 'type' => 'text',
659
+ 'placeholder' => 'Which plugin?'
660
+ ),
661
+ array(
662
+ 'id' => 'not-have-that-feature',
663
+ 'text' => 'The plugin is great, but I need specific feature that you don\'t support',
664
+ 'type' => 'textarea',
665
+ 'placeholder' => 'Could you tell us more about that feature?'
666
+ ),
667
+ array(
668
+ 'id' => 'is-not-working',
669
+ 'text' => 'The plugin is not working',
670
+ 'type' => 'textarea',
671
+ 'placeholder' => 'Could you tell us a bit more whats not working?'
672
+ ),
673
+ array(
674
+ 'id' => 'looking-for-other',
675
+ 'text' => "It's not what I was looking for",
676
+ 'type' => '',
677
+ 'placeholder' => ''
678
+ ),
679
+ array(
680
+ 'id' => 'did-not-work-as-expected',
681
+ 'text' => "The plugin didn't work as expected",
682
+ 'type' => 'textarea',
683
+ 'placeholder' => 'What did you expect?'
684
+ ),
685
+ array(
686
+ 'id' => 'other',
687
+ 'text' => 'Other',
688
+ 'type' => 'textarea',
689
+ 'placeholder' => 'Could you tell us a bit more?'
690
+ ),
691
+ );
692
+
693
+ return $reasons;
694
+ }
695
+
696
+ /**
697
+ * Plugin deactivation uninstall reason submission
698
+ *
699
+ * @return void
700
+ */
701
+ public function uninstall_reason_submission() {
702
+
703
+ if ( ! isset( $_POST['reason_id'] ) ) {
704
+ wp_send_json_error();
705
+ }
706
+
707
+ $current_user = wp_get_current_user();
708
+
709
+ $data = array(
710
+ 'hash' => $this->client->hash,
711
+ 'reason_id' => sanitize_text_field( $_POST['reason_id'] ),
712
+ 'reason_info' => isset( $_REQUEST['reason_info'] ) ? trim( stripslashes( $_REQUEST['reason_info'] ) ) : '',
713
+ 'site' => $this->get_site_name(),
714
+ 'url' => esc_url( home_url() ),
715
+ 'admin_email' => get_option( 'admin_email' ),
716
+ 'user_email' => $current_user->user_email,
717
+ 'first_name' => $current_user->first_name,
718
+ 'last_name' => $current_user->last_name,
719
+ 'server' => $this->get_server_info(),
720
+ 'wp' => $this->get_wp_info(),
721
+ 'ip_address' => $this->get_user_ip_address(),
722
+ 'theme' => get_stylesheet(),
723
+ 'version' => $this->client->project_version,
724
+ );
725
+
726
+ // Add metadata
727
+ if ( $extra = $this->get_extra_data() ) {
728
+ $data['extra'] = $extra;
729
+ }
730
+
731
+ $this->client->send_request( $data, 'deactivate' );
732
+
733
+ wp_send_json_success();
734
+ }
735
+
736
+ /**
737
+ * Handle the plugin deactivation feedback
738
+ *
739
+ * @return void
740
+ */
741
+ public function deactivate_scripts() {
742
+ global $pagenow;
743
+
744
+ if ( 'plugins.php' != $pagenow ) {
745
+ return;
746
+ }
747
+
748
+ $reasons = $this->get_uninstall_reasons();
749
+ ?>
750
+
751
+ <div class="wd-dr-modal" id="<?php echo $this->client->slug; ?>-wd-dr-modal">
752
+ <div class="wd-dr-modal-wrap">
753
+ <div class="wd-dr-modal-header">
754
+ <h3><?php $this->client->_etrans( 'If you have a moment, please let us know why you are deactivating:' ); ?></h3>
755
+ </div>
756
+
757
+ <div class="wd-dr-modal-body">
758
+ <ul class="reasons">
759
+ <?php foreach ($reasons as $reason) { ?>
760
+ <li data-type="<?php echo esc_attr( $reason['type'] ); ?>" data-placeholder="<?php echo esc_attr( $reason['placeholder'] ); ?>">
761
+ <label><input type="radio" name="selected-reason" value="<?php echo $reason['id']; ?>"> <?php echo $reason['text']; ?></label>
762
+ </li>
763
+ <?php } ?>
764
+ </ul>
765
+ <p class="wd-dr-modal-reasons-bottom">
766
+ We share your data with <a href="<?php echo 'https://appsero.com'; ?>">Appsero</a> to troubleshoot problems &amp; make product improvements.
767
+ <a href="<?php echo 'https://appsero.com/privacy-policy'; ?>">Learn more</a> about how Appsero handles your data.
768
+ </p>
769
+ </div>
770
+
771
+ <div class="wd-dr-modal-footer">
772
+ <a href="#" class="dont-bother-me"><?php $this->client->_etrans( "I rather wouldn't say" ); ?></a>
773
+ <button class="button-secondary"><?php $this->client->_etrans( 'Submit & Deactivate' ); ?></button>
774
+ <button class="button-primary"><?php $this->client->_etrans( 'Cancel' ); ?></button>
775
+ </div>
776
+ </div>
777
+ </div>
778
+
779
+ <style type="text/css">
780
+ .wd-dr-modal {
781
+ position: fixed;
782
+ z-index: 99999;
783
+ top: 0;
784
+ right: 0;
785
+ bottom: 0;
786
+ left: 0;
787
+ background: rgba(0,0,0,0.5);
788
+ display: none;
789
+ }
790
+
791
+ .wd-dr-modal.modal-active {
792
+ display: block;
793
+ }
794
+
795
+ .wd-dr-modal-wrap {
796
+ width: 475px;
797
+ position: relative;
798
+ margin: 10% auto;
799
+ background: #fff;
800
+ }
801
+
802
+ .wd-dr-modal-header {
803
+ border-bottom: 1px solid #eee;
804
+ padding: 8px 20px;
805
+ }
806
+
807
+ .wd-dr-modal-header h3 {
808
+ line-height: 150%;
809
+ margin: 0;
810
+ }
811
+
812
+ .wd-dr-modal-body {
813
+ padding: 5px 20px 20px 20px;
814
+ }
815
+
816
+ .wd-dr-modal-body .reason-input {
817
+ margin-top: 5px;
818
+ margin-left: 20px;
819
+ }
820
+ .wd-dr-modal-footer {
821
+ border-top: 1px solid #eee;
822
+ padding: 12px 20px;
823
+ text-align: right;
824
+ }
825
+ .wd-dr-modal-reasons-bottom {
826
+ margin: 15px 0 0 0;
827
+ }
828
+ </style>
829
+
830
+ <script type="text/javascript">
831
+ (function($) {
832
+ $(function() {
833
+ var modal = $( '#<?php echo $this->client->slug; ?>-wd-dr-modal' );
834
+ var deactivateLink = '';
835
+
836
+ $( '#the-list' ).on('click', 'a.<?php echo $this->client->slug; ?>-deactivate-link', function(e) {
837
+ e.preventDefault();
838
+
839
+ modal.addClass('modal-active');
840
+ deactivateLink = $(this).attr('href');
841
+ modal.find('a.dont-bother-me').attr('href', deactivateLink).css('float', 'left');
842
+ });
843
+
844
+ modal.on('click', 'button.button-primary', function(e) {
845
+ e.preventDefault();
846
+
847
+ modal.removeClass('modal-active');
848
+ });
849
+
850
+ modal.on('click', 'input[type="radio"]', function () {
851
+ var parent = $(this).parents('li:first');
852
+
853
+ modal.find('.reason-input').remove();
854
+
855
+ var inputType = parent.data('type'),
856
+ inputPlaceholder = parent.data('placeholder'),
857
+ reasonInputHtml = '<div class="reason-input">' + ( ( 'text' === inputType ) ? '<input type="text" size="40" />' : '<textarea rows="5" cols="45"></textarea>' ) + '</div>';
858
+
859
+ if ( inputType !== '' ) {
860
+ parent.append( $(reasonInputHtml) );
861
+ parent.find('input, textarea').attr('placeholder', inputPlaceholder).focus();
862
+ }
863
+ });
864
+
865
+ modal.on('click', 'button.button-secondary', function(e) {
866
+ e.preventDefault();
867
+
868
+ var button = $(this);
869
+
870
+ if ( button.hasClass('disabled') ) {
871
+ return;
872
+ }
873
+
874
+ var $radio = $( 'input[type="radio"]:checked', modal );
875
+
876
+ var $selected_reason = $radio.parents('li:first'),
877
+ $input = $selected_reason.find('textarea, input[type="text"]');
878
+
879
+ $.ajax({
880
+ url: ajaxurl,
881
+ type: 'POST',
882
+ data: {
883
+ action: '<?php echo $this->client->slug; ?>_submit-uninstall-reason',
884
+ reason_id: ( 0 === $radio.length ) ? 'none' : $radio.val(),
885
+ reason_info: ( 0 !== $input.length ) ? $input.val().trim() : ''
886
+ },
887
+ beforeSend: function() {
888
+ button.addClass('disabled');
889
+ button.text('Processing...');
890
+ },
891
+ complete: function() {
892
+ window.location.href = deactivateLink;
893
+ }
894
+ });
895
+ });
896
+ });
897
+ }(jQuery));
898
+ </script>
899
+
900
+ <?php
901
+ }
902
+
903
+ /**
904
+ * Run after theme deactivated
905
+ * @param string $new_name
906
+ * @param object $new_theme
907
+ * @param object $old_theme
908
+ * @return void
909
+ */
910
+ public function theme_deactivated( $new_name, $new_theme, $old_theme ) {
911
+ // Make sure this is appsero theme
912
+ if ( $old_theme->get_template() == $this->client->slug ) {
913
+ $current_user = wp_get_current_user();
914
+
915
+ $data = array(
916
+ 'hash' => $this->client->hash,
917
+ 'reason_id' => 'none',
918
+ 'reason_info' => '',
919
+ 'site' => $this->get_site_name(),
920
+ 'url' => esc_url( home_url() ),
921
+ 'admin_email' => get_option( 'admin_email' ),
922
+ 'user_email' => $current_user->user_email,
923
+ 'first_name' => $current_user->first_name,
924
+ 'last_name' => $current_user->last_name,
925
+ 'server' => $this->get_server_info(),
926
+ 'wp' => $this->get_wp_info(),
927
+ 'ip_address' => $this->get_user_ip_address(),
928
+ 'theme' => get_stylesheet(),
929
+ 'version' => $this->client->project_version,
930
+ );
931
+
932
+ $this->client->send_request( $data, 'deactivate' );
933
+ }
934
+ }
935
+
936
+ /**
937
+ * Get user IP Address
938
+ */
939
+ private function get_user_ip_address() {
940
+ $response = wp_remote_get( 'https://icanhazip.com/' );
941
+
942
+ if ( is_wp_error( $response ) ) {
943
+ return '';
944
+ }
945
+
946
+ $ip = trim( wp_remote_retrieve_body( $response ) );
947
+
948
+ if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
949
+ return '';
950
+ }
951
+
952
+ return $ip;
953
+ }
954
+
955
+ /**
956
+ * Get site name
957
+ */
958
+ private function get_site_name() {
959
+ $site_name = get_bloginfo( 'name' );
960
+
961
+ if ( empty( $site_name ) ) {
962
+ $site_name = get_bloginfo( 'description' );
963
+ $site_name = wp_trim_words( $site_name, 3, '' );
964
+ }
965
+
966
+ if ( empty( $site_name ) ) {
967
+ $site_name = esc_url( home_url() );
968
+ }
969
+
970
+ return $site_name;
971
+ }
972
+ }
include/appsero/src/License.php ADDED
@@ -0,0 +1,705 @@
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
+ 'is_local' => $this->client->is_local_server(),
125
+ );
126
+
127
+ $response = $this->client->send_request( $params, $route, true );
128
+
129
+ if ( is_wp_error( $response ) ) {
130
+ return array(
131
+ 'success' => false,
132
+ 'error' => $response->get_error_message()
133
+ );
134
+ }
135
+
136
+ $response = json_decode( wp_remote_retrieve_body( $response ), true );
137
+
138
+ if ( empty( $response ) || isset( $response['exception'] )) {
139
+ return array(
140
+ 'success' => false,
141
+ 'error' => 'Unknown error occurred, Please try again.'
142
+ );
143
+ }
144
+
145
+ if ( isset( $response['errors'] ) && isset( $response['errors']['license_key'] ) ) {
146
+ $response = array(
147
+ 'success' => false,
148
+ 'error' => $response['errors']['license_key'][0]
149
+ );
150
+ }
151
+
152
+ return $response;
153
+ }
154
+
155
+ /**
156
+ * Add settings page for license
157
+ *
158
+ * @param array $args
159
+ *
160
+ * @return void
161
+ */
162
+ public function add_settings_page( $args = array() ) {
163
+ $defaults = array(
164
+ 'type' => 'menu', // Can be: menu, options, submenu
165
+ 'page_title' => 'Manage License',
166
+ 'menu_title' => 'Manage License',
167
+ 'capability' => 'manage_options',
168
+ 'menu_slug' => $this->client->slug . '-manage-license',
169
+ 'icon_url' => '',
170
+ 'position' => null,
171
+ 'parent_slug' => '',
172
+ );
173
+
174
+ $this->menu_args = wp_parse_args( $args, $defaults );
175
+
176
+ add_action( 'admin_menu', array( $this, 'admin_menu' ), 99 );
177
+ }
178
+
179
+ /**
180
+ * Admin Menu hook
181
+ *
182
+ * @return void
183
+ */
184
+ public function admin_menu() {
185
+ switch ( $this->menu_args['type'] ) {
186
+ case 'menu':
187
+ $this->create_menu_page();
188
+ break;
189
+
190
+ case 'submenu':
191
+ $this->create_submenu_page();
192
+ break;
193
+
194
+ case 'options':
195
+ $this->create_options_page();
196
+ break;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * License menu output
202
+ */
203
+ public function menu_output() {
204
+
205
+ if ( isset( $_POST['submit'] ) ) {
206
+ $this->license_form_submit( $_POST );
207
+ }
208
+
209
+ $license = get_option( $this->option_key, null );
210
+ $action = ( $license && isset( $license['status'] ) && 'activate' == $license['status'] ) ? 'deactive' : 'active';
211
+ $this->licenses_style();
212
+ ?>
213
+
214
+ <div class="wrap appsero-license-settings-wrapper">
215
+ <h1>License Settings</h1>
216
+
217
+ <?php
218
+ $this->show_license_page_notices();
219
+ do_action( 'before_appsero_license_section' );
220
+ ?>
221
+
222
+ <div class="appsero-license-settings appsero-license-section">
223
+ <?php $this->show_license_page_card_header(); ?>
224
+
225
+ <div class="appsero-license-details">
226
+ <p>Activate <strong><?php echo $this->client->name; ?></strong> by your license key to get professional support and automatic update from your WordPress dashboard.</p>
227
+ <form method="post" action="<?php $this->formActionUrl(); ?>" novalidate="novalidate" spellcheck="false">
228
+ <input type="hidden" name="_action" value="<?php echo $action; ?>">
229
+ <input type="hidden" name="_nonce" value="<?php echo wp_create_nonce( $this->client->name ); ?>">
230
+ <div class="license-input-fields">
231
+ <div class="license-input-key">
232
+ <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">
233
+ <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"/>
234
+ </svg>
235
+ <input type="text" value="<?php echo $this->get_input_license_value( $action, $license ); ?>"
236
+ placeholder="Enter your license key to activate" name="license_key"
237
+ <?php echo ( 'deactive' == $action ) ? 'readonly="readonly"' : ''; ?>
238
+ />
239
+ </div>
240
+ <button type="submit" name="submit" class="<?php echo 'deactive' == $action ? 'deactive-button' : ''; ?>">
241
+ <?php echo $action == 'active' ? 'Activate License' : 'Deactivate License' ; ?>
242
+ </button>
243
+ </div>
244
+ </form>
245
+
246
+ <?php
247
+ if ( 'deactive' == $action && isset( $license['remaining'] ) ) {
248
+ $this->show_active_license_info( $license );
249
+ }
250
+ ?>
251
+ </div>
252
+ </div> <!-- /.appsero-license-settings -->
253
+
254
+ <?php do_action( 'after_appsero_license_section' ); ?>
255
+ </div>
256
+ <?php
257
+ }
258
+
259
+ /**
260
+ * License form submit
261
+ */
262
+ public function license_form_submit( $form ) {
263
+ if ( ! isset( $form['_nonce'], $form['_action'] ) ) {
264
+ $this->error = "Please add all information";
265
+ return;
266
+ }
267
+
268
+ if ( ! wp_verify_nonce( $form['_nonce'], $this->client->name ) ) {
269
+ $this->error = "You don't have permission to manage license.";
270
+ return;
271
+ }
272
+
273
+ switch ( $form['_action'] ) {
274
+ case 'active':
275
+ $this->active_client_license( $form );
276
+ break;
277
+
278
+ case 'deactive':
279
+ $this->deactive_client_license( $form );
280
+ break;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Check license status on schedule
286
+ */
287
+ public function check_license_status() {
288
+ $license = get_option( $this->option_key, null );
289
+
290
+ if ( isset( $license['key'] ) && ! empty( $license['key'] ) ) {
291
+ $response = $this->check( $license['key'] );
292
+
293
+ if ( isset( $response['success'] ) && $response['success'] ) {
294
+ $license['status'] = 'activate';
295
+ $license['remaining'] = $response['remaining'];
296
+ $license['activation_limit'] = $response['activation_limit'];
297
+ $license['expiry_days'] = $response['expiry_days'];
298
+ $license['title'] = $response['title'];
299
+ $license['source_id'] = $response['source_identifier'];
300
+ $license['recurring'] = $response['recurring'];
301
+ } else {
302
+ $license['status'] = 'deactivate';
303
+ $license['expiry_days'] = 0;
304
+ }
305
+
306
+ update_option( $this->option_key, $license, false );
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Check this is a valid license
312
+ */
313
+ public function is_valid() {
314
+ if ( null !== $this->is_valid_licnese ) {
315
+ return $this->is_valid_licnese;
316
+ }
317
+
318
+ $license = get_option( $this->option_key, null );
319
+ if ( ! empty( $license['key'] ) && isset( $license['status'] ) && $license['status'] == 'activate' ) {
320
+ $this->is_valid_licnese = true;
321
+ } else {
322
+ $this->is_valid_licnese = false;
323
+ }
324
+
325
+ return $this->is_valid_licnese;
326
+ }
327
+
328
+ /**
329
+ * Check this is a valid license
330
+ */
331
+ public function is_valid_by( $option, $value ) {
332
+ $license = get_option( $this->option_key, null );
333
+
334
+ if ( ! empty( $license['key'] ) && isset( $license['status'] ) && $license['status'] == 'activate' ) {
335
+ if ( isset( $license[ $option ] ) && $license[ $option ] == $value ) {
336
+ return true;
337
+ }
338
+ }
339
+
340
+ return false;
341
+ }
342
+
343
+ /**
344
+ * Styles for licenses page
345
+ */
346
+ private function licenses_style() {
347
+ ?>
348
+ <style type="text/css">
349
+ .appsero-license-section {
350
+ width: 100%;
351
+ max-width: 1100px;
352
+ min-height: 1px;
353
+ box-sizing: border-box;
354
+ }