PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / 3.5.4
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent v3.5.4
3.5.4 3.5.3 3.5.2 3.5.1 3.4.9 3.5.0 3.4.8 3.4.7 trunk 2.3.1 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6
← All changes | includes/EDD_SL_Plugin_Updater.php +311 -254 2.3.1 → 3.5.4 View file →
@@ -1,27 +1,29 @@
1 -<?php
1 +<?php // phpcs:ignorefile
2 2
3 3 // Exit if accessed directly
4 -if ( ! defined( 'ABSPATH' ) ) exit;
4 +if ( ! defined( 'ABSPATH' ) ) {
5 + exit;
6 +}
5 7
6 8 /**
7 9 * Allows plugins to use their own update API.
8 10 *
9 11 * @author Easy Digital Downloads
10 - * @version 1.6.19
12 + * @version 1.9.1
11 13 */
12 -class EDD_SL_Plugin_Updater {
14 +class WPSC_EDD_SL_Plugin_Updater {
13 15
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 = '';
16 + private $api_url = '';
17 + private $api_data = array();
18 + private $plugin_file = '';
19 + private $name = '';
20 + private $slug = '';
21 + private $version = '';
22 + private $wp_override = false;
23 + private $beta = false;
24 + private $failed_request_cache_key;
21 25
22 - private $health_check_timeout = 5;
23 -
24 26 /**
25 27 * Class constructor.
26 28 *
27 29 * @uses plugin_basename()
@@ -34,16 +36,17 @@
34 36 public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
35 37
36 38 global $edd_plugin_data;
37 39
38 - $this->api_url = trailingslashit( $_api_url );
39 - $this->api_data = $_api_data;
40 - $this->name = plugin_basename( $_plugin_file );
41 - $this->slug = basename( $_plugin_file, '.php' );
42 - $this->version = $_api_data['version'];
43 - $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
44 - $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
45 - $this->cache_key = 'edd_sl_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
40 + $this->api_url = trailingslashit( $_api_url );
41 + $this->api_data = $_api_data;
42 + $this->plugin_file = $_plugin_file;
43 + $this->name = plugin_basename( $_plugin_file );
44 + $this->slug = basename( $_plugin_file, '.php' );
45 + $this->version = $_api_data['version'];
46 + $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
47 + $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
48 + $this->failed_request_cache_key = 'edd_sl_failed_http_' . md5( $this->api_url );
46 49
47 50 $edd_plugin_data[ $this->slug ] = $this->api_data;
48 51
49 52 /**
@@ -70,10 +73,9 @@
70 73 public function init() {
71 74
72 75 add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
73 76 add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
74 - remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
75 - add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
77 + add_action( 'after_plugin_row', array( $this, 'show_update_notification' ), 10, 2 );
76 78 add_action( 'admin_init', array( $this, 'show_changelog' ) );
77 79
78 80 }
79 81
@@ -94,49 +96,63 @@
94 96
95 97 global $pagenow;
96 98
97 99 if ( ! is_object( $_transient_data ) ) {
98 - $_transient_data = new stdClass;
100 + $_transient_data = new stdClass();
99 101 }
100 102
101 - if ( 'plugins.php' == $pagenow && is_multisite() ) {
103 + if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
102 104 return $_transient_data;
103 105 }
104 106
105 - if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
106 - return $_transient_data;
107 + $current = $this->get_repo_api_data();
108 + if ( false !== $current && is_object( $current ) && isset( $current->new_version ) ) {
109 + if ( version_compare( $this->version, $current->new_version, '<' ) ) {
110 + $_transient_data->response[ $this->name ] = $current;
111 + } else {
112 + // Populating the no_update information is required to support auto-updates in WordPress 5.5.
113 + $_transient_data->no_update[ $this->name ] = $current;
114 + }
107 115 }
116 + $_transient_data->last_checked = time();
117 + $_transient_data->checked[ $this->name ] = $this->version;
108 118
119 + return $_transient_data;
120 + }
121 +
122 + /**
123 + * Get repo API data from store.
124 + * Save to cache.
125 + *
126 + * @return \stdClass
127 + */
128 + public function get_repo_api_data() {
109 129 $version_info = $this->get_cached_version_info();
110 130
111 131 if ( false === $version_info ) {
112 - $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
113 -
114 - $this->set_version_info_cache( $version_info );
115 -
116 - }
117 -
118 - if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
119 -
120 - if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
121 -
122 - $_transient_data->response[ $this->name ] = $version_info;
123 -
124 - // Make sure the plugin property is set to the plugin's name/location. See issue 1463 on Software Licensing's GitHub repo.
125 - $_transient_data->response[ $this->name ]->plugin = $this->name;
126 -
132 + $version_info = $this->api_request(
133 + 'plugin_latest_version',
134 + array(
135 + 'slug' => $this->slug,
136 + 'beta' => $this->beta,
137 + )
138 + );
139 + if ( ! $version_info ) {
140 + return false;
127 141 }
128 142
129 - $_transient_data->last_checked = time();
130 - $_transient_data->checked[ $this->name ] = $this->version;
143 + // This is required for your plugin to support auto-updates in WordPress 5.5.
144 + $version_info->plugin = $this->name;
145 + $version_info->id = $this->name;
131 146
147 + $this->set_version_info_cache( $version_info );
132 148 }
133 149
134 - return $_transient_data;
150 + return $version_info;
135 151 }
136 152
137 153 /**
138 - * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
154 + * Show the update notification on multisite subsites.
139 155 *
140 156 * @param string $file
141 157 * @param array $plugin
142 158 */
@@ -141,121 +157,120 @@
141 157 * @param array $plugin
142 158 */
143 159 public function show_update_notification( $file, $plugin ) {
144 160
145 - if ( is_network_admin() ) {
161 + // Return early if in the network admin, or if this is not a multisite install.
162 + if ( is_network_admin() || ! is_multisite() ) {
146 163 return;
147 164 }
148 165
149 - if( ! current_user_can( 'update_plugins' ) ) {
166 + // Allow single site admins to see that an update is available.
167 + if ( ! current_user_can( 'activate_plugins' ) ) {
150 168 return;
151 169 }
152 170
153 - if( ! is_multisite() ) {
171 + if ( $this->name !== $file ) {
154 172 return;
155 173 }
156 174
157 - if ( $this->name != $file ) {
158 - return;
159 - }
160 -
161 - // Remove our filter on the site transient
162 - remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
163 -
175 + // Do not print any message if update does not exist.
164 176 $update_cache = get_site_transient( 'update_plugins' );
165 177
166 - $update_cache = is_object( $update_cache ) ? $update_cache : new stdClass();
167 -
168 - if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
169 -
170 - $version_info = $this->get_cached_version_info();
171 -
172 - if ( false === $version_info ) {
173 - $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
174 -
175 - // Since we disabled our filter for the transient, we aren't running our object conversion on banners, sections, or icons. Do this now:
176 - if ( isset( $version_info->banners ) && ! is_array( $version_info->banners ) ) {
177 - $version_info->banners = $this->convert_object_to_array( $version_info->banners );
178 - }
179 -
180 - if ( isset( $version_info->sections ) && ! is_array( $version_info->sections ) ) {
181 - $version_info->sections = $this->convert_object_to_array( $version_info->sections );
182 - }
183 -
184 - if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
185 - $version_info->icons = $this->convert_object_to_array( $version_info->icons );
186 - }
187 -
188 - if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
189 - $version_info->icons = $this->convert_object_to_array( $version_info->icons );
190 - }
191 -
192 - if ( isset( $version_info->contributors ) && ! is_array( $version_info->contributors ) ) {
193 - $version_info->contributors = $this->convert_object_to_array( $version_info->contributors );
194 - }
195 -
196 - $this->set_version_info_cache( $version_info );
178 + if ( ! isset( $update_cache->response[ $this->name ] ) ) {
179 + if ( ! is_object( $update_cache ) ) {
180 + $update_cache = new stdClass();
197 181 }
182 + $update_cache->response[ $this->name ] = $this->get_repo_api_data();
183 + }
198 184
199 - if ( ! is_object( $version_info ) ) {
200 - return;
201 - }
185 + // Return early if this plugin isn't in the transient->response or if the site is running the current or newer version of the plugin.
186 + if ( empty( $update_cache->response[ $this->name ] ) || version_compare( $this->version, $update_cache->response[ $this->name ]->new_version, '>=' ) ) {
187 + return;
188 + }
202 189
203 - if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
190 + printf(
191 + '<tr class="plugin-update-tr %3$s" id="%1$s-update" data-slug="%1$s" data-plugin="%2$s">',
192 + $this->slug,
193 + $file,
194 + in_array( $this->name, $this->get_active_plugins(), true ) ? 'active' : 'inactive'
195 + );
204 196
205 - $update_cache->response[ $this->name ] = $version_info;
197 + echo '<td colspan="3" class="plugin-update colspanchange">';
198 + echo '<div class="update-message notice inline notice-warning notice-alt"><p>';
206 199
207 - }
200 + $changelog_link = '';
201 + if ( ! empty( $update_cache->response[ $this->name ]->sections->changelog ) ) {
202 + $changelog_link = add_query_arg(
203 + array(
204 + 'edd_sl_action' => 'view_plugin_changelog',
205 + 'plugin' => urlencode( $this->name ),
206 + 'slug' => urlencode( $this->slug ),
207 + 'TB_iframe' => 'true',
208 + 'width' => 77,
209 + 'height' => 911,
210 + ),
211 + self_admin_url( 'index.php' )
212 + );
213 + }
214 + $update_link = add_query_arg(
215 + array(
216 + 'action' => 'upgrade-plugin',
217 + 'plugin' => urlencode( $this->name ),
218 + ),
219 + self_admin_url( 'update.php' )
220 + );
208 221
209 - $update_cache->last_checked = time();
210 - $update_cache->checked[ $this->name ] = $this->version;
222 + printf(
223 + esc_html( wpsc__( 'There is a new version of %1$s available.', 'easy-digital-downloads' ) ),
224 + esc_html( $plugin['Name'] )
225 + );
211 226
212 - set_site_transient( 'update_plugins', $update_cache );
213 -
227 + if ( ! current_user_can( 'update_plugins' ) ) {
228 + echo ' ';
229 + echo esc_html( wpsc__( 'Contact your network administrator to install the update.', 'easy-digital-downloads' ) );
230 + } elseif ( empty( $update_cache->response[ $this->name ]->package ) && ! empty( $changelog_link ) ) {
231 + echo ' ';
232 + printf(
233 + /* translators: 1. opening anchor tag, do not translate 2. the new plugin version 3. closing anchor tag, do not translate. */
234 + wpsc__( '%1$sView version %2$s details%3$s.', 'easy-digital-downloads' ),
235 + '<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
236 + esc_html( $update_cache->response[ $this->name ]->new_version ),
237 + '</a>'
238 + );
239 + } elseif ( ! empty( $changelog_link ) ) {
240 + echo ' ';
241 + printf(
242 + wpsc__( '%1$sView version %2$s details%3$s or %4$supdate now%5$s.', 'easy-digital-downloads' ),
243 + '<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
244 + esc_html( $update_cache->response[ $this->name ]->new_version ),
245 + '</a>',
246 + '<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
247 + '</a>'
248 + );
214 249 } else {
215 -
216 - $version_info = $update_cache->response[ $this->name ];
217 -
250 + printf(
251 + ' %1$s%2$s%3$s',
252 + '<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
253 + esc_html( wpsc__( 'Update now.', 'easy-digital-downloads' ) ),
254 + '</a>'
255 + );
218 256 }
219 257
220 - // Restore our filter
221 - add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
258 + do_action( "in_plugin_update_message-{$file}", $plugin, $plugin );
222 259
223 - if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
260 + echo '</p></div></td></tr>';
261 + }
224 262
225 - // build a plugin list row, with update notification
226 - $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
227 - # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
228 - echo '<tr class="plugin-update-tr" id="' . esc_attr($this->slug) . '-update" data-slug="' . esc_attr($this->slug) . '" data-plugin="' . esc_attr($this->slug) . '/' . esc_attr($file) . '">';
229 - echo '<td colspan="3" class="plugin-update colspanchange">';
230 - echo '<div class="update-message notice inline notice-warning notice-alt">';
263 + /**
264 + * Gets the plugins active in a multisite network.
265 + *
266 + * @return array
267 + */
268 + private function get_active_plugins() {
269 + $active_plugins = (array) get_option( 'active_plugins' );
270 + $active_network_plugins = (array) get_site_option( 'active_sitewide_plugins' );
231 271
232 - $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' );
233 -
234 - if ( empty( $version_info->download_link ) ) {
235 - printf(
236 - __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'easy-digital-downloads' ),
237 - esc_html( $version_info->name ),
238 - '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
239 - esc_html( $version_info->new_version ),
240 - '</a>'
241 - );
242 - } else {
243 - printf(
244 - __( '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' ),
245 - esc_html( $version_info->name ),
246 - '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
247 - esc_html( $version_info->new_version ),
248 - '</a>',
249 - '<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
250 - '</a>'
251 - );
252 - }
253 -
254 - do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
255 -
256 - echo '</div></td></tr>';
257 - }
272 + return array_merge( $active_plugins, array_keys( $active_network_plugins ) );
258 273 }
259 274
260 275 /**
261 276 * Updates information on the "View version x.x details" page with custom data.
@@ -268,15 +283,15 @@
268 283 * @return object $_data
269 284 */
270 285 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
271 286
272 - if ( $_action != 'plugin_information' ) {
287 + if ( 'plugin_information' !== $_action ) {
273 288
274 289 return $_data;
275 290
276 291 }
277 292
278 - if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
293 + if ( ! isset( $_args->slug ) || ( $_args->slug !== $this->slug ) ) {
279 294
280 295 return $_data;
281 296
282 297 }
@@ -287,15 +302,13 @@
287 302 'fields' => array(
288 303 'banners' => array(),
289 304 'reviews' => false,
290 305 'icons' => array(),
291 - )
306 + ),
292 307 );
293 308
294 - $cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
295 -
296 309 // Get the transient where we store the api request for this plugin for 24 hours
297 - $edd_api_request_transient = $this->get_cached_version_info( $cache_key );
310 + $edd_api_request_transient = $this->get_cached_version_info();
298 311
299 312 //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.
300 313 if ( empty( $edd_api_request_transient ) ) {
301 314
@@ -301,14 +314,13 @@
301 314
302 315 $api_response = $this->api_request( 'plugin_information', $to_send );
303 316
304 317 // Expires in 3 hours
305 - $this->set_version_info_cache( $api_response, $cache_key );
318 + $this->set_version_info_cache( $api_response );
306 319
307 320 if ( false !== $api_response ) {
308 321 $_data = $api_response;
309 322 }
310 -
311 323 } else {
312 324 $_data = $edd_api_request_transient;
313 325 }
314 326
@@ -331,9 +343,9 @@
331 343 if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
332 344 $_data->contributors = $this->convert_object_to_array( $_data->contributors );
333 345 }
334 346
335 - if( ! isset( $_data->plugin ) ) {
347 + if ( ! isset( $_data->plugin ) ) {
336 348 $_data->plugin = $this->name;
337 349 }
338 350
339 351 return $_data;
@@ -351,8 +363,11 @@
351 363 *
352 364 * @return array
353 365 */
354 366 private function convert_object_to_array( $data ) {
367 + if ( ! is_array( $data ) && ! is_object( $data ) ) {
368 + return array();
369 + }
355 370 $new_data = array();
356 371 foreach ( $data as $key => $value ) {
357 372 $new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
358 373 }
@@ -368,11 +383,10 @@
368 383 * @return object $array
369 384 */
370 385 public function http_request_args( $args, $url ) {
371 386
372 - $verify_ssl = $this->verify_ssl();
373 387 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
374 - $args['sslverify'] = $verify_ssl;
388 + $args['sslverify'] = $this->verify_ssl();
375 389 }
376 390 return $args;
377 391
378 392 }
@@ -385,167 +399,190 @@
385 399 * @uses is_wp_error()
386 400 *
387 401 * @param string $_action The requested action.
388 402 * @param array $_data Parameters for the API action.
389 - * @return false|object
403 + * @return false|object|void
390 404 */
391 405 private function api_request( $_action, $_data ) {
406 + $data = array_merge( $this->api_data, $_data );
392 407
393 - global $wp_version, $edd_plugin_url_available;
394 -
395 - $verify_ssl = $this->verify_ssl();
396 -
397 - // Do a quick status check on this domain if we haven't already checked it.
398 - $store_hash = md5( $this->api_url );
399 - if ( ! is_array( $edd_plugin_url_available ) || ! isset( $edd_plugin_url_available[ $store_hash ] ) ) {
400 - $test_url_parts = parse_url( $this->api_url );
401 -
402 - $scheme = ! empty( $test_url_parts['scheme'] ) ? $test_url_parts['scheme'] : 'http';
403 - $host = ! empty( $test_url_parts['host'] ) ? $test_url_parts['host'] : '';
404 - $port = ! empty( $test_url_parts['port'] ) ? ':' . $test_url_parts['port'] : '';
405 -
406 - if ( empty( $host ) ) {
407 - $edd_plugin_url_available[ $store_hash ] = false;
408 - } else {
409 - $test_url = $scheme . '://' . $host . $port;
410 - $response = wp_remote_get( $test_url, array( 'timeout' => $this->health_check_timeout, 'sslverify' => $verify_ssl ) );
411 - $edd_plugin_url_available[ $store_hash ] = is_wp_error( $response ) ? false : true;
412 - }
413 - }
414 -
415 - if ( false === $edd_plugin_url_available[ $store_hash ] ) {
408 + if ( $data['slug'] !== $this->slug ) {
416 409 return;
417 410 }
418 411
419 - $data = array_merge( $this->api_data, $_data );
420 -
421 - if ( $data['slug'] != $this->slug ) {
422 - return;
412 + // Don't allow a plugin to ping itself
413 + if ( trailingslashit( home_url() ) === $this->api_url ) {
414 + return false;
423 415 }
424 416
425 - if( $this->api_url == trailingslashit ( home_url() ) ) {
426 - return false; // Don't allow a plugin to ping itself
417 + if ( $this->request_recently_failed() ) {
418 + return false;
427 419 }
428 420
429 - $api_params = array(
430 - 'edd_action' => 'get_version',
431 - 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
432 - 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
433 - 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
434 - 'version' => isset( $data['version'] ) ? $data['version'] : false,
435 - 'slug' => $data['slug'],
436 - 'author' => $data['author'],
437 - 'url' => home_url(),
438 - 'beta' => ! empty( $data['beta'] ),
439 - );
421 + return $this->get_version_from_remote();
422 + }
440 423
441 - $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
424 + /**
425 + * Determines if a request has recently failed.
426 + *
427 + * @since 1.9.1
428 + *
429 + * @return bool
430 + */
431 + private function request_recently_failed() {
432 + $failed_request_details = get_option( $this->failed_request_cache_key );
442 433
443 - if ( ! is_wp_error( $request ) ) {
444 - $request = json_decode( wp_remote_retrieve_body( $request ) );
434 + // Request has never failed.
435 + if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
436 + return false;
445 437 }
446 438
447 - if ( $request && isset( $request->sections ) ) {
448 - $request->sections = maybe_unserialize( $request->sections );
449 - } else {
450 - $request = false;
451 - }
439 + /*
440 + * Request previously failed, but the timeout has expired.
441 + * This means we're allowed to try again.
442 + */
443 + if ( time() > $failed_request_details ) {
444 + delete_option( $this->failed_request_cache_key );
452 445
453 - if ( $request && isset( $request->banners ) ) {
454 - $request->banners = maybe_unserialize( $request->banners );
446 + return false;
455 447 }
456 448
457 - if ( $request && isset( $request->icons ) ) {
458 - $request->icons = maybe_unserialize( $request->icons );
459 - }
449 + return true;
450 + }
460 451
461 - if( ! empty( $request->sections ) ) {
462 - foreach( $request->sections as $key => $section ) {
463 - $request->$key = (array) $section;
464 - }
465 - }
466 -
467 - return $request;
452 + /**
453 + * Logs a failed HTTP request for this API URL.
454 + * We set a timestamp for 1 hour from now. This prevents future API requests from being
455 + * made to this domain for 1 hour. Once the timestamp is in the past, API requests
456 + * will be allowed again. This way if the site is down for some reason we don't bombard
457 + * it with failed API requests.
458 + *
459 + * @see WPSC_EDD_SL_Plugin_Updater::request_recently_failed
460 + *
461 + * @since 1.9.1
462 + */
463 + private function log_failed_request() {
464 + update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
468 465 }
469 466
467 + /**
468 + * If available, show the changelog for sites in a multisite install.
469 + */
470 470 public function show_changelog() {
471 471
472 - global $edd_plugin_data;
472 + if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' !== $_REQUEST['edd_sl_action'] ) {
473 + return;
474 + }
473 475
474 - if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
476 + if ( empty( $_REQUEST['plugin'] ) ) {
475 477 return;
476 478 }
477 479
478 - if( empty( $_REQUEST['plugin'] ) ) {
480 + if ( empty( $_REQUEST['slug'] ) || $this->slug !== $_REQUEST['slug'] ) {
479 481 return;
480 482 }
481 483
482 - if( empty( $_REQUEST['slug'] ) ) {
483 - return;
484 + if ( ! current_user_can( 'update_plugins' ) ) {
485 + wp_die( esc_html( wpsc__( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ) ), esc_html( wpsc__( 'Error', 'easy-digital-downloads' ) ), array( 'response' => 403 ) );
484 486 }
485 487
486 - if( ! current_user_can( 'update_plugins' ) ) {
487 - wp_die( __( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
488 + $version_info = $this->get_repo_api_data();
489 + if ( isset( $version_info->sections ) ) {
490 + $sections = $this->convert_object_to_array( $version_info->sections );
491 + if ( ! empty( $sections['changelog'] ) ) {
492 + echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $sections['changelog'] ) . '</div>';
493 + }
488 494 }
489 495
490 - $data = $edd_plugin_data[ $_REQUEST['slug'] ];
491 - $beta = ! empty( $data['beta'] ) ? true : false;
492 - $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
493 - $version_info = $this->get_cached_version_info( $cache_key );
496 + exit;
497 + }
494 498
495 - if( false === $version_info ) {
499 + /**
500 + * Gets the current version information from the remote site.
501 + *
502 + * @return array|false
503 + */
504 + private function get_version_from_remote() {
505 + $api_params = array(
506 + 'edd_action' => 'get_version',
507 + 'license' => ! empty( $this->api_data['license'] ) ? $this->api_data['license'] : '',
508 + 'item_name' => isset( $this->api_data['item_name'] ) ? $this->api_data['item_name'] : false,
509 + 'item_id' => isset( $this->api_data['item_id'] ) ? $this->api_data['item_id'] : false,
510 + 'version' => isset( $this->api_data['version'] ) ? $this->api_data['version'] : false,
511 + 'slug' => $this->slug,
512 + 'author' => $this->api_data['author'],
513 + 'url' => home_url(),
514 + 'beta' => $this->beta,
515 + 'php_version' => phpversion(),
516 + 'wp_version' => get_bloginfo( 'version' ),
517 + );
496 518
497 - $api_params = array(
498 - 'edd_action' => 'get_version',
499 - 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
500 - 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
501 - 'slug' => $_REQUEST['slug'],
502 - 'author' => $data['author'],
503 - 'url' => home_url(),
504 - 'beta' => ! empty( $data['beta'] )
505 - );
519 + /**
520 + * Filters the parameters sent in the API request.
521 + *
522 + * @param array $api_params The array of data sent in the request.
523 + * @param array $this->api_data The array of data set up in the class constructor.
524 + * @param string $this->plugin_file The full path and filename of the file.
525 + */
526 + $api_params = apply_filters( 'edd_sl_plugin_updater_api_params', $api_params, $this->api_data, $this->plugin_file );
506 527
507 - $verify_ssl = $this->verify_ssl();
508 - $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
528 + $request = wp_remote_post(
529 + $this->api_url,
530 + array(
531 + 'timeout' => 15,
532 + 'sslverify' => $this->verify_ssl(),
533 + 'body' => $api_params,
534 + )
535 + );
509 536
510 - if ( ! is_wp_error( $request ) ) {
511 - $version_info = json_decode( wp_remote_retrieve_body( $request ) );
512 - }
537 + if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
538 + $this->log_failed_request();
513 539
540 + return false;
541 + }
514 542
515 - if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
516 - $version_info->sections = maybe_unserialize( $version_info->sections );
517 - } else {
518 - $version_info = false;
519 - }
543 + $request = json_decode( wp_remote_retrieve_body( $request ) );
520 544
521 - if( ! empty( $version_info ) ) {
522 - foreach( $version_info->sections as $key => $section ) {
523 - $version_info->$key = (array) $section;
524 - }
525 - }
545 + if ( $request && isset( $request->sections ) ) {
546 + $request->sections = maybe_unserialize( $request->sections );
547 + } else {
548 + $request = false;
549 + }
526 550
527 - $this->set_version_info_cache( $version_info, $cache_key );
551 + if ( $request && isset( $request->banners ) ) {
552 + $request->banners = maybe_unserialize( $request->banners );
553 + }
528 554
555 + if ( $request && isset( $request->icons ) ) {
556 + $request->icons = maybe_unserialize( $request->icons );
529 557 }
530 558
531 - if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
532 - echo '<div style="background:#fff;padding:10px;">' . esc_attr($version_info->sections['changelog']) . '</div>';
559 + if ( ! empty( $request->sections ) ) {
560 + foreach ( $request->sections as $key => $section ) {
561 + $request->$key = (array) $section;
562 + }
533 563 }
534 564
535 - exit;
565 + return $request;
536 566 }
537 567
568 + /**
569 + * Get the version info from the cache, if it exists.
570 + *
571 + * @param string $cache_key
572 + * @return object
573 + */
538 574 public function get_cached_version_info( $cache_key = '' ) {
539 575
540 - if( empty( $cache_key ) ) {
541 - $cache_key = $this->cache_key;
576 + if ( empty( $cache_key ) ) {
577 + $cache_key = $this->get_cache_key();
542 578 }
543 579
544 580 $cache = get_option( $cache_key );
545 581
546 - if( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
547 - return false; // Cache is expired
582 + // Cache is expired
583 + if ( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
584 + return false;
548 585 }
549 586
550 587 // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
551 588 $cache['value'] = json_decode( $cache['value'] );
@@ -556,21 +593,29 @@
556 593 return $cache['value'];
557 594
558 595 }
559 596
597 + /**
598 + * Adds the plugin version information to the database.
599 + *
600 + * @param string $value
601 + * @param string $cache_key
602 + */
560 603 public function set_version_info_cache( $value = '', $cache_key = '' ) {
561 604
562 - if( empty( $cache_key ) ) {
563 - $cache_key = $this->cache_key;
605 + if ( empty( $cache_key ) ) {
606 + $cache_key = $this->get_cache_key();
564 607 }
565 608
566 609 $data = array(
567 610 'timeout' => strtotime( '+3 hours', time() ),
568 - 'value' => json_encode( $value )
611 + 'value' => wp_json_encode( $value ),
569 612 );
570 613
571 614 update_option( $cache_key, $data, 'no' );
572 615
616 + // Delete the duplicate option
617 + delete_option( 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) ) );
573 618 }
574 619
575 620 /**
576 621 * Returns if the SSL of the store should be verified.
@@ -579,7 +624,19 @@
579 624 * @return bool
580 625 */
581 626 private function verify_ssl() {
582 627 return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
628 + }
629 +
630 + /**
631 + * Gets the unique key (option name) for a plugin.
632 + *
633 + * @since 1.9.0
634 + * @return string
635 + */
636 + private function get_cache_key() {
637 + $string = $this->slug . $this->api_data['license'] . $this->beta;
638 +
639 + return 'edd_sl_' . md5( serialize( $string ) );
583 640 }
584 641
585 642 }