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