Better Click To Tweet - Version 5.7.0

Version Description

  • feature moved license management for premium add-ons (get them at https://www.betterclicktotweet.com today!) to the core plugin. Free plugin users will not notice a difference here at all. Premium users: check your email!
  • enhancement several updates to code comments for clarity.
  • enhancement the callback URL (from tweet back to website) is now filterable.
  • not much in terms of user-focused enhancements in this release, but a ton happened "under the hood."
Download this release

Release Info

Developer ben.meredith@gmail.com
Plugin Icon 128x128 Better Click To Tweet
Version 5.7.0
Comparing to
See all releases

Code changes from version 5.6.5 to 5.7.0

better-click-to-tweet.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Plugin Name: Better Click To Tweet
4
  * Description: Add Click to Tweet boxes simply and elegantly to your posts or pages. All the features of a premium plugin, for FREE!
5
- * Version: 5.6.5
6
  * Author: Ben Meredith
7
  * Author URI: https://www.betterclicktotweet.com
8
  * Plugin URI: https://wordpress.org/plugins/better-click-to-tweet/
@@ -17,6 +17,11 @@ include 'bctt_options.php';
17
  include 'bctt-i18n.php';
18
  include 'admin-nags.php';
19
 
 
 
 
 
 
20
  /*
21
  * Strips the html, shortens the text (after checking for mb_internal_encoding compatibility)
22
  * and adds an ellipsis if the text has been shortened
@@ -141,11 +146,11 @@ function bctt_shortcode( $atts ) {
141
 
142
  if ( get_option( 'bctt-short-url' ) != false ) {
143
 
144
- $bcttURL = wp_get_shortlink();
145
 
146
  } else {
147
 
148
- $bcttURL = get_permalink();
149
 
150
  }
151
 
@@ -180,7 +185,7 @@ function bctt_shortcode( $atts ) {
180
  $bctt_button_span_class = apply_filters( 'bctt_button_span_class', 'bctt-ctt-btn' );
181
 
182
 
183
- $href = add_query_arg( array(
184
  'url' => $bcttURL,
185
  'text' => rawurlencode( html_entity_decode( $short ) ),
186
  'via' => $via,
2
  /**
3
  * Plugin Name: Better Click To Tweet
4
  * Description: Add Click to Tweet boxes simply and elegantly to your posts or pages. All the features of a premium plugin, for FREE!
5
+ * Version: 5.7.0
6
  * Author: Ben Meredith
7
  * Author URI: https://www.betterclicktotweet.com
8
  * Plugin URI: https://wordpress.org/plugins/better-click-to-tweet/
17
  include 'bctt-i18n.php';
18
  include 'admin-nags.php';
19
 
20
+ // @since 5.7.0
21
+ include 'includes/updater/bctt-updater.php';
22
+ include 'includes/updater/license-page.php';
23
+ include 'includes/misc-functions.php';
24
+
25
  /*
26
  * Strips the html, shortens the text (after checking for mb_internal_encoding compatibility)
27
  * and adds an ellipsis if the text has been shortened
146
 
147
  if ( get_option( 'bctt-short-url' ) != false ) {
148
 
149
+ $bcttURL = apply_filters( 'bctturl', wp_get_shortlink());
150
 
151
  } else {
152
 
153
+ $bcttURL = apply_filters( 'bctturl', get_permalink());
154
 
155
  }
156
 
185
  $bctt_button_span_class = apply_filters( 'bctt_button_span_class', 'bctt-ctt-btn' );
186
 
187
 
188
+ $href = add_query_arg( array(
189
  'url' => $bcttURL,
190
  'text' => rawurlencode( html_entity_decode( $short ) ),
191
  'via' => $via,
includes/misc-functions.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Get plugin info including status, type, and license validation.
4
+ *
5
+ * This is an enhanced version of get_plugins() that returns the status
6
+ * (`active` or `inactive`) of all plugins, type of plugin (`add-on` or `other`
7
+ * and license validation for Better Click To Tweet add-ons (`true` or `false`). Does not include
8
+ * MU plugins. This function is borrowed from the Give Plugin.
9
+ *
10
+ * @since 5.6
11
+ *
12
+ * @return array Plugin info plus status, and type if
13
+ * available.
14
+ */
15
+
16
+ function bctt_get_active_addons() {
17
+ $plugins = get_plugins();
18
+ $active_plugin_paths = (array) get_option( 'active_plugins', array() );
19
+
20
+ if ( is_multisite() ) {
21
+ $network_activated_plugin_paths = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
22
+ $active_plugin_paths = array_merge( $active_plugin_paths, $network_activated_plugin_paths );
23
+ }
24
+
25
+ foreach ( $plugins as $plugin_path => $plugin_data ) {
26
+ // Is plugin active?
27
+ if ( in_array( $plugin_path, $active_plugin_paths ) ) {
28
+ $plugins[ $plugin_path ]['Status'] = 'active';
29
+ } else {
30
+ $plugins[ $plugin_path ]['Status'] = 'inactive';
31
+ }
32
+
33
+ $dirname = strtolower( dirname( $plugin_path ) );
34
+
35
+ // Is plugin a Better Click To Tweet add-on by Better Click To Tweet?
36
+ if ( strstr( $dirname, 'better-click' ) && strstr( $plugin_data['AuthorURI'], 'betterclicktotweet.com' ) ) {
37
+ // Plugin is a Better Click To Tweet add-on.
38
+ $plugins[ $plugin_path ]['Type'] = 'add-on';
39
+
40
+ } else {
41
+ // Plugin is not a Better Click To Tweet add-on.
42
+ $plugins[ $plugin_path ]['Type'] = 'other';
43
+ }
44
+
45
+ }
46
+
47
+ foreach ( $plugins as $key => $plugin_data ) {
48
+ if ( 'active' != $plugin_data['Status'] || 'add-on' != $plugin_data['Type'] ) {
49
+ unset( $plugins[ $key ] );
50
+ }
51
+ if ( 'Better Click To Tweet' === $plugin_data['Name'] ) {
52
+ unset( $plugins[ $key ] );
53
+ }
54
+
55
+ }
56
+
57
+ return $plugins;
58
+ }
59
+
60
+ function bctt_addon_shortname( $addonname ) {
61
+ $shortname = trim( str_replace( 'Better Click To Tweet ', '', $addonname ) );
62
+
63
+ return $shortname;
64
+ }
65
+
66
+ function bctt_addon_slug( $shortname ) {
67
+
68
+ $slug = str_replace( ' ', '_', strtolower( $shortname ) );
69
+
70
+ return $slug;
71
+
72
+ }
includes/updater/BCTT_SL_Plugin_Updater.php ADDED
@@ -0,0 +1,500 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Allows plugins to use their own update API.
8
+ *
9
+ * @author Easy Digital Downloads
10
+ * @version 1.6.16
11
+ */
12
+ class BCTT_SL_Plugin_Updater {
13
+
14
+ private $api_url = '';
15
+ private $api_data = array();
16
+ private $name = '';
17
+ private $slug = '';
18
+ private $version = '';
19
+ private $wp_override = false;
20
+ private $cache_key = '';
21
+
22
+ /**
23
+ * Class constructor.
24
+ *
25
+ * @uses plugin_basename()
26
+ * @uses hook()
27
+ *
28
+ * @param string $_api_url The URL pointing to the custom API endpoint.
29
+ * @param string $_plugin_file Path to the plugin file.
30
+ * @param array $_api_data Optional data to send with API calls.
31
+ */
32
+ public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
33
+
34
+ global $edd_plugin_data;
35
+
36
+ $this->api_url = trailingslashit( $_api_url );
37
+ $this->api_data = $_api_data;
38
+ $this->name = plugin_basename( $_plugin_file );
39
+ $this->slug = basename( $_plugin_file, '.php' );
40
+ $this->version = $_api_data['version'];
41
+ $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
42
+ $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
43
+ $this->cache_key = 'edd_sl_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
44
+
45
+ $edd_plugin_data[ $this->slug ] = $this->api_data;
46
+
47
+ /**
48
+ * Fires after the $edd_plugin_data is setup.
49
+ *
50
+ * @since x.x.x
51
+ *
52
+ * @param array $edd_plugin_data Array of EDD SL plugin data.
53
+ */
54
+ do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
55
+
56
+ // Set up hooks.
57
+ $this->init();
58
+
59
+ }
60
+
61
+ /**
62
+ * Set up WordPress filters to hook into WP's update process.
63
+ *
64
+ * @uses add_filter()
65
+ *
66
+ * @return void
67
+ */
68
+ public function init() {
69
+
70
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
71
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
72
+ remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
73
+ add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
74
+ add_action( 'admin_init', array( $this, 'show_changelog' ) );
75
+
76
+ }
77
+
78
+ /**
79
+ * Check for Updates at the defined API endpoint and modify the update array.
80
+ *
81
+ * This function dives into the update API just when WordPress creates its update array,
82
+ * then adds a custom API call and injects the custom plugin data retrieved from the API.
83
+ * It is reassembled from parts of the native WordPress plugin update code.
84
+ * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
85
+ *
86
+ * @uses api_request()
87
+ *
88
+ * @param array $_transient_data Update array build by WordPress.
89
+ * @return array Modified update array with custom plugin data.
90
+ */
91
+ public function check_update( $_transient_data ) {
92
+
93
+ global $pagenow;
94
+
95
+ if ( ! is_object( $_transient_data ) ) {
96
+ $_transient_data = new stdClass;
97
+ }
98
+
99
+ if ( 'plugins.php' == $pagenow && is_multisite() ) {
100
+ return $_transient_data;
101
+ }
102
+
103
+ if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
104
+ return $_transient_data;
105
+ }
106
+
107
+ $version_info = $this->get_cached_version_info();
108
+
109
+ if ( false === $version_info ) {
110
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
111
+
112
+ $this->set_version_info_cache( $version_info );
113
+
114
+ }
115
+
116
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
117
+
118
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
119
+
120
+ $_transient_data->response[ $this->name ] = $version_info;
121
+
122
+ }
123
+
124
+ $_transient_data->last_checked = time();
125
+ $_transient_data->checked[ $this->name ] = $this->version;
126
+
127
+ }
128
+
129
+ return $_transient_data;
130
+ }
131
+
132
+ /**
133
+ * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
134
+ *
135
+ * @param string $file
136
+ * @param array $plugin
137
+ */
138
+ public function show_update_notification( $file, $plugin ) {
139
+
140
+ if ( is_network_admin() ) {
141
+ return;
142
+ }
143
+
144
+ if( ! current_user_can( 'update_plugins' ) ) {
145
+ return;
146
+ }
147
+
148
+ if( ! is_multisite() ) {
149
+ return;
150
+ }
151
+
152
+ if ( $this->name != $file ) {
153
+ return;
154
+ }
155
+
156
+ // Remove our filter on the site transient
157
+ remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
158
+
159
+ $update_cache = get_site_transient( 'update_plugins' );
160
+
161
+ $update_cache = is_object( $update_cache ) ? $update_cache : new stdClass();
162
+
163
+ if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
164
+
165
+ $version_info = $this->get_cached_version_info();
166
+
167
+ if ( false === $version_info ) {
168
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
169
+
170
+ $this->set_version_info_cache( $version_info );
171
+ }
172
+
173
+ if ( ! is_object( $version_info ) ) {
174
+ return;
175
+ }
176
+
177
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
178
+
179
+ $update_cache->response[ $this->name ] = $version_info;
180
+
181
+ }
182
+
183
+ $update_cache->last_checked = time();
184
+ $update_cache->checked[ $this->name ] = $this->version;
185
+
186
+ set_site_transient( 'update_plugins', $update_cache );
187
+
188
+ } else {
189
+
190
+ $version_info = $update_cache->response[ $this->name ];
191
+
192
+ }
193
+
194
+ // Restore our filter
195
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
196
+
197
+ if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
198
+
199
+ // build a plugin list row, with update notification
200
+ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
201
+ # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
202
+ echo '<tr class="plugin-update-tr" id="' . $this->slug . '-update" data-slug="' . $this->slug . '" data-plugin="' . $this->slug . '/' . $file . '">';
203
+ echo '<td colspan="3" class="plugin-update colspanchange">';
204
+ echo '<div class="update-message notice inline notice-warning notice-alt">';
205
+
206
+ $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
207
+
208
+ if ( empty( $version_info->download_link ) ) {
209
+ printf(
210
+ __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'easy-digital-downloads' ),
211
+ esc_html( $version_info->name ),
212
+ '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
213
+ esc_html( $version_info->new_version ),
214
+ '</a>'
215
+ );
216
+ } else {
217
+ printf(
218
+ __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'easy-digital-downloads' ),
219
+ esc_html( $version_info->name ),
220
+ '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
221
+ esc_html( $version_info->new_version ),
222
+ '</a>',
223
+ '<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
224
+ '</a>'
225
+ );
226
+ }
227
+
228
+ do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
229
+
230
+ echo '</div></td></tr>';
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Updates information on the "View version x.x details" page with custom data.
236
+ *
237
+ * @uses api_request()
238
+ *
239
+ * @param mixed $_data
240
+ * @param string $_action
241
+ * @param object $_args
242
+ * @return object $_data
243
+ */
244
+ public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
245
+
246
+ if ( $_action != 'plugin_information' ) {
247
+
248
+ return $_data;
249
+
250
+ }
251
+
252
+ if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
253
+
254
+ return $_data;
255
+
256
+ }
257
+
258
+ $to_send = array(
259
+ 'slug' => $this->slug,
260
+ 'is_ssl' => is_ssl(),
261
+ 'fields' => array(
262
+ 'banners' => array(),
263
+ 'reviews' => false
264
+ )
265
+ );
266
+
267
+ $cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
268
+
269
+ // Get the transient where we store the api request for this plugin for 24 hours
270
+ $edd_api_request_transient = $this->get_cached_version_info( $cache_key );
271
+
272
+ //If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
273
+ if ( empty( $edd_api_request_transient ) ) {
274
+
275
+ $api_response = $this->api_request( 'plugin_information', $to_send );
276
+
277
+ // Expires in 3 hours
278
+ $this->set_version_info_cache( $api_response, $cache_key );
279
+
280
+ if ( false !== $api_response ) {
281
+ $_data = $api_response;
282
+ }
283
+
284
+ } else {
285
+ $_data = $edd_api_request_transient;
286
+ }
287
+
288
+ // Convert sections into an associative array, since we're getting an object, but Core expects an array.
289
+ if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
290
+ $new_sections = array();
291
+ foreach ( $_data->sections as $key => $value ) {
292
+ $new_sections[ $key ] = $value;
293
+ }
294
+
295
+ $_data->sections = $new_sections;
296
+ }
297
+
298
+ // Convert banners into an associative array, since we're getting an object, but Core expects an array.
299
+ if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
300
+ $new_banners = array();
301
+ foreach ( $_data->banners as $key => $value ) {
302
+ $new_banners[ $key ] = $value;
303
+ }
304
+
305
+ $_data->banners = $new_banners;
306
+ }
307
+
308
+ return $_data;
309
+ }
310
+
311
+ /**
312
+ * Disable SSL verification in order to prevent download update failures
313
+ *
314
+ * @param array $args
315
+ * @param string $url
316
+ * @return object $array
317
+ */
318
+ public function http_request_args( $args, $url ) {
319
+
320
+ $verify_ssl = $this->verify_ssl();
321
+ if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
322
+ $args['sslverify'] = $verify_ssl;
323
+ }
324
+ return $args;
325
+
326
+ }
327
+
328
+ /**
329
+ * Calls the API and, if successfull, returns the object delivered by the API.
330
+ *
331
+ * @uses get_bloginfo()
332
+ * @uses wp_remote_post()
333
+ * @uses is_wp_error()
334
+ *
335
+ * @param string $_action The requested action.
336
+ * @param array $_data Parameters for the API action.
337
+ * @return false|object
338
+ */
339
+ private function api_request( $_action, $_data ) {
340
+
341
+ global $wp_version;
342
+
343
+ $data = array_merge( $this->api_data, $_data );
344
+
345
+ if ( $data['slug'] != $this->slug ) {
346
+ return;
347
+ }
348
+
349
+ if( $this->api_url == trailingslashit (home_url() ) ) {
350
+ return false; // Don't allow a plugin to ping itself
351
+ }
352
+
353
+ $api_params = array(
354
+ 'edd_action' => 'get_version',
355
+ 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
356
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
357
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
358
+ 'version' => isset( $data['version'] ) ? $data['version'] : false,
359
+ 'slug' => $data['slug'],
360
+ 'author' => $data['author'],
361
+ 'url' => home_url(),
362
+ 'beta' => ! empty( $data['beta'] ),
363
+ );
364
+
365
+ $verify_ssl = $this->verify_ssl();
366
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
367
+
368
+ if ( ! is_wp_error( $request ) ) {
369
+ $request = json_decode( wp_remote_retrieve_body( $request ) );
370
+ }
371
+
372
+ if ( $request && isset( $request->sections ) ) {
373
+ $request->sections = maybe_unserialize( $request->sections );
374
+ } else {
375
+ $request = false;
376
+ }
377
+
378
+ if ( $request && isset( $request->banners ) ) {
379
+ $request->banners = maybe_unserialize( $request->banners );
380
+ }
381
+
382
+ if( ! empty( $request->sections ) ) {
383
+ foreach( $request->sections as $key => $section ) {
384
+ $request->$key = (array) $section;
385
+ }
386
+ }
387
+
388
+ return $request;
389
+ }
390
+
391
+ public function show_changelog() {
392
+
393
+ global $edd_plugin_data;
394
+
395
+ if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
396
+ return;
397
+ }
398
+
399
+ if( empty( $_REQUEST['plugin'] ) ) {
400
+ return;
401
+ }
402
+
403
+ if( empty( $_REQUEST['slug'] ) ) {
404
+ return;
405
+ }
406
+
407
+ if( ! current_user_can( 'update_plugins' ) ) {
408
+ wp_die( __( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
409
+ }
410
+
411
+ $data = $edd_plugin_data[ $_REQUEST['slug'] ];
412
+ $beta = ! empty( $data['beta'] ) ? true : false;
413
+ $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
414
+ $version_info = $this->get_cached_version_info( $cache_key );
415
+
416
+ if( false === $version_info ) {
417
+
418
+ $api_params = array(
419
+ 'edd_action' => 'get_version',
420
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
421
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
422
+ 'slug' => $_REQUEST['slug'],
423
+ 'author' => $data['author'],
424
+ 'url' => home_url(),
425
+ 'beta' => ! empty( $data['beta'] )
426
+ );
427
+
428
+ $verify_ssl = $this->verify_ssl();
429
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
430
+
431
+ if ( ! is_wp_error( $request ) ) {
432
+ $version_info = json_decode( wp_remote_retrieve_body( $request ) );
433
+ }
434
+
435
+
436
+ if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
437
+ $version_info->sections = maybe_unserialize( $version_info->sections );
438
+ } else {
439
+ $version_info = false;
440
+ }
441
+
442
+ if( ! empty( $version_info ) ) {
443
+ foreach( $version_info->sections as $key => $section ) {
444
+ $version_info->$key = (array) $section;
445
+ }
446
+ }
447
+
448
+ $this->set_version_info_cache( $version_info, $cache_key );
449
+
450
+ }
451
+
452
+ if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
453
+ echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
454
+ }
455
+
456
+ exit;
457
+ }
458
+
459
+ public function get_cached_version_info( $cache_key = '' ) {
460
+
461
+ if( empty( $cache_key ) ) {
462
+ $cache_key = $this->cache_key;
463
+ }
464
+
465
+ $cache = get_option( $cache_key );
466
+
467
+ if( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
468
+ return false; // Cache is expired
469
+ }
470
+
471
+ return json_decode( $cache['value'] );
472
+
473
+ }
474
+
475
+ public function set_version_info_cache( $value = '', $cache_key = '' ) {
476
+
477
+ if( empty( $cache_key ) ) {
478
+ $cache_key = $this->cache_key;
479
+ }
480
+
481
+ $data = array(
482
+ 'timeout' => strtotime( '+3 hours', time() ),
483
+ 'value' => json_encode( $value )
484
+ );
485
+
486
+ update_option( $cache_key, $data, 'no' );
487
+
488
+ }
489
+
490
+ /**
491
+ * Returns if the SSL of the store should be verified.
492
+ *
493
+ * @since 1.6.13
494
+ * @return bool
495
+ */
496
+ private function verify_ssl() {
497
+ return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
498
+ }
499
+
500
+ }
includes/updater/bctt-updater.php ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * License Handler Class
4
+ *
5
+ * This is the base class for adding licenses for Premium add-ons. Each add-on extends this class
6
+ *
7
+ * @since 5.7.0
8
+ *
9
+ */
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit;
12
+ }
13
+ if ( ! class_exists( 'BCTT_License' ) ):
14
+
15
+ class BCTT_License {
16
+ /**
17
+ * File
18
+ *
19
+ * @access private
20
+ * @since 1.0
21
+ *
22
+ * @var string
23
+ */
24
+ private $file;
25
+
26
+ /**
27
+ * License
28
+ *
29
+ * @access private
30
+ * @since 1.0
31
+ *
32
+ * @var string
33
+ */
34
+ private $license;
35
+
36
+ /**
37
+ * Item name
38
+ *
39
+ * @access private
40
+ * @since 1.0
41
+ *
42
+ * @var string
43
+ */
44
+ private $item_name;
45
+
46
+ /**
47
+ * License Information object.
48
+ *
49
+ * @access private
50
+ * @since 1.7
51
+ *
52
+ * @var object
53
+ */
54
+ private $license_data;
55
+
56
+ /**
57
+ * Item shortname
58
+ *
59
+ * @access private
60
+ * @since 1.0
61
+ *
62
+ * @var string
63
+ */
64
+ private $item_shortname;
65
+
66
+ /**
67
+ * Version
68
+ *
69
+ * @access private
70
+ * @since 1.0
71
+ *
72
+ * @var string
73
+ */
74
+ private $version;
75
+
76
+ /**
77
+ * Author
78
+ *
79
+ * @access private
80
+ * @since 1.0
81
+ *
82
+ * @var string
83
+ */
84
+ private $author;
85
+
86
+ /**
87
+ * array of licensed addons
88
+ *
89
+ * @since 2.1.4
90
+ * @access private
91
+ *
92
+ * @var array
93
+ */
94
+ private static $licensed_addons = array();
95
+
96
+ /**
97
+ * API URL
98
+ *
99
+ * @access private
100
+ * @since 1.0
101
+ *
102
+ * @var string
103
+ */
104
+ private $api_url = 'https://www.betterclicktotweet.com/';
105
+
106
+
107
+ public function __construct( $_file, $_item_name, $_version, $_author, $_optname = null, $_api_url = null ) {
108
+
109
+
110
+ $this->file = $_file;
111
+ $this->item_name = $_item_name;
112
+ $this->item_shortname = 'bctt_' . bctt_addon_slug( bctt_addon_shortname( $this->item_name ) );
113
+ $this->item_slug = preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->item_shortname ) ) );
114
+ $this->version = $_version;
115
+ $bctt_license = get_option( $this->item_shortname . '_license' );
116
+ $this->license = isset( $bctt_license ) ? trim( $bctt_license ) : '';
117
+ $this->author = $_author;
118
+ $this->api_url = is_null( $_api_url ) ? $this->api_url : $_api_url;
119
+
120
+
121
+ $this->includes();
122
+ $this->hooks();
123
+ $this->bctt_updater();
124
+ }
125
+
126
+ private function hooks() {
127
+ add_action( 'admin_init', array( $this, 'bctt_updater' ), 0 );
128
+ add_action( 'admin_init', array( $this, 'activate_license' ), 10 );
129
+ add_action( 'admin_init', array( $this, 'deactivate_license' ), 11 );
130
+
131
+ }
132
+
133
+
134
+ public function bctt_updater() {
135
+
136
+ // retrieve our license key from the DB
137
+ $license_key = trim( get_option( $this->license ) );
138
+
139
+ // setup the updater
140
+ $edd_updater = new BCTT_SL_Plugin_Updater(
141
+ $this->api_url,
142
+ $this->file,
143
+ array(
144
+ 'version' => $this->version, // current version number
145
+ 'license' => $this->license, // license key (used get_option above to retrieve from DB)
146
+ 'item_name' => $this->item_name, // name of this plugin
147
+ 'author' => $this->author, // author of this plugin
148
+ )
149
+ );
150
+
151
+ }
152
+
153
+ public static function get_short_name( $plugin_name ) {
154
+ $plugin_name = trim( str_replace( 'Better Click To Tweet ', '', $plugin_name ) );
155
+ $plugin_name = 'bctt_' . preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $plugin_name ) ) );
156
+
157
+ return $plugin_name;
158
+ }
159
+
160
+ private function includes() {
161
+
162
+ if ( ! class_exists( 'BCTT_SL_Plugin_Updater' ) ) {
163
+ require_once 'BCTT_SL_Plugin_Updater.php';
164
+ }
165
+ }
166
+
167
+ public function activate_license() {
168
+
169
+ // listen for our activate button to be clicked
170
+ if ( isset( $_POST[ $this->item_shortname . '_license_activate'] ) ) {
171
+
172
+ // run a quick security check
173
+ if ( ! isset( $_REQUEST[ $this->item_shortname . '_license_nonce'] ) || ! wp_verify_nonce( $_REQUEST[ $this->item_shortname . '_license_nonce'], $this->item_shortname . '_license_nonce' ) ) {
174
+
175
+ return;
176
+
177
+ }
178
+ if ( empty( $_POST["{$this->item_shortname}_license"] ) ) {
179
+ $this->unset_license();
180
+
181
+ return;
182
+ }
183
+
184
+ // Do not simultaneously activate add-ons if the user want to deactivate a specific add-on.
185
+ if ( $this->is_deactivating_license() ) {
186
+ return;
187
+ }
188
+
189
+ // Get license key.
190
+ $this->license = sanitize_text_field( $_POST[ $this->item_shortname . '_license' ] );
191
+
192
+ // Make the call to the API and make sure there are no api errors.
193
+ $license_data = $this->get_license_info( 'activate_license' );
194
+ // var_dump($license_data );
195
+ if ( $license_data->success !== true ) {
196
+ add_settings_error('bctt-license', esc_attr( 'settings_updated' ), __( 'License failed to activate. Please double-check it and enter again.', 'better-click-to-tweet' ) );
197
+ return;
198
+ }
199
+
200
+
201
+ // Tell WordPress to check for updates
202
+ set_site_transient( 'update_plugins', null );
203
+
204
+ //update the license key option.
205
+ update_option( "{$this->item_shortname}_license", $this->license );
206
+ update_option( "{$this->item_shortname}_license_active", 'valid' );
207
+
208
+
209
+ }
210
+ }
211
+
212
+ public function get_license_info( $edd_action = '', $response_in_array = false ) {
213
+
214
+ if ( empty( $edd_action ) ) {
215
+ return false;
216
+ }
217
+
218
+ // Data to send to the API.
219
+ $api_params = array(
220
+ 'edd_action' => $edd_action, // never change from "edd_" to "bctt_"!
221
+ 'license' => $this->license,
222
+ 'item_name' => urlencode( $this->item_name ),
223
+ 'url' => home_url(),
224
+ );
225
+
226
+ // Call the API.
227
+ $response = wp_remote_post(
228
+ $this->api_url,
229
+ array(
230
+ 'timeout' => 15,
231
+ 'sslverify' => false,
232
+ 'body' => $api_params,
233
+ )
234
+ );
235
+
236
+ // Make sure there are no errors.
237
+ if ( is_wp_error( $response ) ) {
238
+ return false;
239
+ }
240
+
241
+ return json_decode( wp_remote_retrieve_body( $response ), $response_in_array );
242
+ }
243
+
244
+ private function unset_license() {
245
+
246
+ if ( ! ( $license_data = $this->get_license_info( 'deactivate_license' ) ) ) {
247
+ return;
248
+ }
249
+ // Remove license from database.
250
+ delete_option( "{$this->item_shortname}_license_active" );
251
+ delete_option( "{$this->item_shortname}_license" );
252
+ unset( $_POST["{$this->item_shortname}_license"] );
253
+
254
+ // Unset license param.
255
+ $this->license = '';
256
+ }
257
+
258
+ private function is_deactivating_license() {
259
+ $status = false;
260
+
261
+ foreach ( $_POST as $key => $value ) {
262
+ if ( false !== strpos( $key, 'license_deactivate' ) ) {
263
+ $status = true;
264
+ break;
265
+ }
266
+ }
267
+
268
+ return $status;
269
+ }
270
+
271
+ public function deactivate_license() {
272
+
273
+ // Run on deactivate button press.
274
+ if ( isset( $_POST[ $this->item_shortname . '_license_deactivate'] ) ) {
275
+ $this->unset_license();
276
+ }
277
+ }
278
+
279
+ }
280
+ endif;
includes/updater/license-page.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file adds in a license page, if any premium add-ons are installed and active.
5
+ * If no premium add-ons are active, this file doesn't do anything.
6
+ *
7
+ * @since 5.7.0
8
+ */
9
+
10
+
11
+ // Backward compatibility for the previous version of the Premium Styles add-on (version 1.3 and earlier)
12
+ // removes the submenu page added by those versions of the add-on.
13
+ function maybe_remove_premium_styles_license_page() {
14
+ if ( function_exists( 'bcttps_settings_page') ) {
15
+
16
+ remove_action( 'admin_menu', 'bcttps_settings_page', 30 );
17
+
18
+ }
19
+ }
20
+
21
+ add_action( 'init', 'maybe_remove_premium_styles_license_page', 40 );
22
+
23
+ function bctt_license_menu() {
24
+ $addons = bctt_get_active_addons();
25
+
26
+
27
+
28
+ if ( ! empty( $addons ) ) {
29
+
30
+ add_submenu_page(
31
+ 'better-click-to-tweet',
32
+ 'Add-on Licenses',
33
+ 'Add-on Licenses',
34
+ 'manage_options',
35
+ 'bctt-licenses',
36
+ 'bctt_license_page' );
37
+ }
38
+
39
+ return;
40
+ }
41
+
42
+ add_action( 'admin_menu', 'bctt_license_menu', 40 );
43
+
44
+ /*
45
+ * The output of the submenu page
46
+ */
47
+
48
+ function bctt_license_page() {
49
+ ?>
50
+ <div class="wrap">
51
+ <h2><?php _e( 'Activate Your Addons', 'better-click-to-tweet' ); ?></h2>
52
+ <p><?php _e('An active license is required for updates (including bug fixes and security updates) as well as support.', 'better-click-to-tweet' );?></p>
53
+
54
+ <form method="post" action="admin.php?page=bctt-licenses">
55
+ <?php settings_errors('bctt-license');
56
+
57
+ $active_plugins = bctt_get_active_addons(); ?>
58
+
59
+ <table class="form-table">
60
+ <?php settings_fields( 'bctt_license' );
61
+
62
+ foreach ( $active_plugins
63
+
64
+ as $addons ) {
65
+ $shortname = bctt_addon_shortname( $addons['Name'] );
66
+ $license_key = 'bctt_' . bctt_addon_slug( $shortname ) . '_license';
67
+ $key = get_option( $license_key );
68
+ $status = get_option( $license_key . '_active' );
69
+ //var_dump( $status );
70
+ ?>
71
+ <tbody>
72
+ <tr valign="top">
73
+
74
+ <th scope="row" valign="top">
75
+ <?php echo $shortname; ?>
76
+ </th>
77
+ <td>
78
+ <input id="<?php echo $license_key ?>" name="<?php echo $license_key ?>" type="text"
79
+ class="regular-text"
80
+ value="<?php echo $key; ?>"
81
+ placeholder="<?php _e( 'Enter your license key', 'better-click-to-tweet' ); ?>"/>
82
+ </td>
83
+ </tr>
84
+ <?php if ( false == $status || 'valid' == $status) { ?>
85
+ <tr valign="top">
86
+ <th scope="row" valign="top">
87
+ <?php //empty column for spacing ?>
88
+ </th>
89
+ <td style="padding-top:0;">
90
+ <?php if ( $status == 'valid' ) { ?>
91
+ <div style="color:green; margin-bottom:1em;"><?php _e( 'License active!' ); ?></div>
92
+ <?php wp_nonce_field( $license_key . '_nonce', $license_key . '_nonce' ); ?>
93
+ <input type="submit" class="button-secondary" name="<?php echo $license_key ?>_deactivate"
94
+ value="<?php _e( 'Deactivate License', 'better-click-to-tweet' ); ?>"/>
95
+ <?php } else {
96
+ wp_nonce_field( $license_key . '_nonce', $license_key . '_nonce' ); ?>
97
+ <input type="submit" class="button-secondary" name="<?php echo $license_key ?>_activate"
98
+ value="<?php _e( 'Activate License', 'better-click-to-tweet' ); ?>"/>
99
+ <?php }
100
+ } ?>
101
+ </td>
102
+ </tr>
103
+ <?php } ?>
104
+ </tbody>
105
+ </table>
106
+
107
+ </form>
108
+ <?php
109
+ }
110
+
111
+
112
+ function bctt_register_license_option() {
113
+
114
+ $active_plugins = bctt_get_active_addons();
115
+ $args = array(
116
+ 'type' => 'string',
117
+ 'sanitize_callback' => 'sanitize_text_field',
118
+ 'default' => NULL,
119
+ );
120
+ foreach ( $active_plugins as $addons ) {
121
+ $shortname = bctt_addon_slug( bctt_addon_shortname( $addons['Name'] ) );
122
+ $longoption = 'bctt_' . $shortname . '_license';
123
+ if ( ! get_option( $longoption ) ) {
124
+ // creates our settings in the options table
125
+ register_setting( 'bctt_license', $longoption, $args );
126
+ }
127
+ }
128
+ }
129
+
130
+ add_action( 'admin_init', 'bctt_register_license_option' );
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.wpsteward.com/donations/plugin-support/
4
  Tags: click to tweet, twitter, tweet,
5
  Requires at least: 3.8
6
  Tested up to: 5.0
7
- Stable tag: 5.6.5
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -97,6 +97,12 @@ Donations: http://benlikes.us/donate
97
 
98
  == Changelog ==
99
 
 
 
 
 
 
 
100
  = 5.6.5 =
101
  * fix — added a parameter to the mailing list signup in the admin to help determine where some suspicious signups are originating from.
102
  * enhancement — gave the readme file a makeover so that the page on the plugin directory is more focused and provides greater value.
4
  Tags: click to tweet, twitter, tweet,
5
  Requires at least: 3.8
6
  Tested up to: 5.0
7
+ Stable tag: 5.7.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
97
 
98
  == Changelog ==
99
 
100
+ = 5.7.0 =
101
+ * feature — moved license management for premium add-ons (get them at https://www.betterclicktotweet.com today!) to the core plugin. Free plugin users will not notice a difference here at all. Premium users: check your email!
102
+ * enhancement — several updates to code comments for clarity.
103
+ * enhancement — the callback URL (from tweet back to website) is now filterable.
104
+ * not much in terms of user-focused enhancements in this release, but a ton happened "under the hood."
105
+
106
  = 5.6.5 =
107
  * fix — added a parameter to the mailing list signup in the admin to help determine where some suspicious signups are originating from.
108
  * enhancement — gave the readme file a makeover so that the page on the plugin directory is more focused and provides greater value.