PluginProbe ʕ •ᴥ•ʔ
Folders – Unlimited Folders to Organize Media Library Folder, Pages, Posts, File Manager / 2.0.2
Folders – Unlimited Folders to Organize Media Library Folder, Pages, Posts, File Manager v2.0.2
3.1.9 3.1.8 3.1.7 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 2.9.8 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 trunk 1.3.7 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.5 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.6.7 2.6.8 2.6.9 2.7 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9 2.9.1 2.9.2
folders / includes / plugin.updates.php
folders / includes Last commit date
folders.class.php 7 years ago form.class.php 7 years ago plugin.updates.php 7 years ago tree.class.php 7 years ago
plugin.updates.php
565 lines
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.17
11 */
12 class Folder_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 private $health_check_timeout = 5;
23
24 /**
25 * Class constructor.
26 *
27 * @uses plugin_basename()
28 * @uses hook()
29 *
30 * @param string $_api_url The URL pointing to the custom API endpoint.
31 * @param string $_plugin_file Path to the plugin file.
32 * @param array $_api_data Optional data to send with API calls.
33 */
34 public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
35
36 global $edd_plugin_data;
37
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 ) );
46
47 $edd_plugin_data[ $this->slug ] = $this->api_data;
48
49 /**
50 * Fires after the $edd_plugin_data is setup.
51 *
52 * @since x.x.x
53 *
54 * @param array $edd_plugin_data Array of EDD SL plugin data.
55 */
56 do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
57
58 // Set up hooks.
59 $this->init();
60
61 }
62
63 /**
64 * Set up WordPress filters to hook into WP's update process.
65 *
66 * @uses add_filter()
67 *
68 * @return void
69 */
70 public function init() {
71
72 add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
73 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 );
76 add_action( 'admin_init', array( $this, 'show_changelog' ) );
77
78 }
79
80 /**
81 * Check for Updates at the defined API endpoint and modify the update array.
82 *
83 * This function dives into the update API just when WordPress creates its update array,
84 * then adds a custom API call and injects the custom plugin data retrieved from the API.
85 * It is reassembled from parts of the native WordPress plugin update code.
86 * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
87 *
88 * @uses api_request()
89 *
90 * @param array $_transient_data Update array build by WordPress.
91 * @return array Modified update array with custom plugin data.
92 */
93 public function check_update( $_transient_data ) {
94
95 global $pagenow;
96
97 if ( ! is_object( $_transient_data ) ) {
98 $_transient_data = new stdClass;
99 }
100
101 if ( 'plugins.php' == $pagenow && is_multisite() ) {
102 return $_transient_data;
103 }
104
105 if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
106 return $_transient_data;
107 }
108
109 $version_info = $this->get_cached_version_info();
110
111 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 }
125
126 $_transient_data->last_checked = time();
127 $_transient_data->checked[ $this->name ] = $this->version;
128
129 }
130
131 return $_transient_data;
132 }
133
134 /**
135 * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
136 *
137 * @param string $file
138 * @param array $plugin
139 */
140 public function show_update_notification( $file, $plugin ) {
141
142 if ( is_network_admin() ) {
143 return;
144 }
145
146 if( ! current_user_can( 'update_plugins' ) ) {
147 return;
148 }
149
150 if( ! is_multisite() ) {
151 return;
152 }
153
154 if ( $this->name != $file ) {
155 return;
156 }
157
158 // Remove our filter on the site transient
159 remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
160
161 $update_cache = get_site_transient( 'update_plugins' );
162
163 $update_cache = is_object( $update_cache ) ? $update_cache : new stdClass();
164
165 if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
166
167 $version_info = $this->get_cached_version_info();
168
169 if ( false === $version_info ) {
170 $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
171
172 // Since we disabled our filter for the transient, we aren't running our object conversion on banners, sections, or icons. Do this now:
173 if ( isset( $version_info->banners ) && ! is_array( $version_info->banners ) ) {
174 $version_info->banners = $this->convert_object_to_array( $version_info->banners );
175 }
176
177 if ( isset( $version_info->sections ) && ! is_array( $version_info->sections ) ) {
178 $version_info->sections = $this->convert_object_to_array( $version_info->sections );
179 }
180
181 if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
182 $version_info->icons = $this->convert_object_to_array( $version_info->icons );
183 }
184
185 $this->set_version_info_cache( $version_info );
186 }
187
188 if ( ! is_object( $version_info ) ) {
189 return;
190 }
191
192 if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
193
194 $update_cache->response[ $this->name ] = $version_info;
195
196 }
197
198 $update_cache->last_checked = time();
199 $update_cache->checked[ $this->name ] = $this->version;
200
201 set_site_transient( 'update_plugins', $update_cache );
202
203 } else {
204
205 $version_info = $update_cache->response[ $this->name ];
206
207 }
208
209 // Restore our filter
210 add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
211
212 if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
213
214 // build a plugin list row, with update notification
215 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
216 # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
217 echo '<tr class="plugin-update-tr" id="' . $this->slug . '-update" data-slug="' . $this->slug . '" data-plugin="' . $this->slug . '/' . $file . '">';
218 echo '<td colspan="3" class="plugin-update colspanchange">';
219 echo '<div class="update-message notice inline notice-warning notice-alt">';
220
221 $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' );
222
223 if ( empty( $version_info->download_link ) ) {
224 printf(
225 __( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'easy-digital-downloads' ),
226 esc_html( $version_info->name ),
227 '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
228 esc_html( $version_info->new_version ),
229 '</a>'
230 );
231 } else {
232 printf(
233 __( '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' ),
234 esc_html( $version_info->name ),
235 '<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
236 esc_html( $version_info->new_version ),
237 '</a>',
238 '<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
239 '</a>'
240 );
241 }
242
243 do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
244
245 echo '</div></td></tr>';
246 }
247 }
248
249 /**
250 * Updates information on the "View version x.x details" page with custom data.
251 *
252 * @uses api_request()
253 *
254 * @param mixed $_data
255 * @param string $_action
256 * @param object $_args
257 * @return object $_data
258 */
259 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
260
261 if ( $_action != 'plugin_information' ) {
262
263 return $_data;
264
265 }
266
267 if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
268
269 return $_data;
270
271 }
272
273 $to_send = array(
274 'slug' => $this->slug,
275 'is_ssl' => is_ssl(),
276 'fields' => array(
277 'banners' => array(),
278 'reviews' => false,
279 'icons' => array(),
280 )
281 );
282
283 $cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
284
285 // Get the transient where we store the api request for this plugin for 24 hours
286 $edd_api_request_transient = $this->get_cached_version_info( $cache_key );
287
288 //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.
289 if ( empty( $edd_api_request_transient ) ) {
290
291 $api_response = $this->api_request( 'plugin_information', $to_send );
292
293 // Expires in 3 hours
294 $this->set_version_info_cache( $api_response, $cache_key );
295
296 if ( false !== $api_response ) {
297 $_data = $api_response;
298 }
299
300 } else {
301 $_data = $edd_api_request_transient;
302 }
303
304 // Convert sections into an associative array, since we're getting an object, but Core expects an array.
305 if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
306 $_data->sections = $this->convert_object_to_array( $_data->sections );
307 }
308
309 // Convert banners into an associative array, since we're getting an object, but Core expects an array.
310 if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
311 $_data->banners = $this->convert_object_to_array( $_data->banners );
312 }
313
314 // Convert icons into an associative array, since we're getting an object, but Core expects an array.
315 if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
316 $_data->icons = $this->convert_object_to_array( $_data->icons );
317 }
318
319 return $_data;
320 }
321
322 /**
323 * Convert some objects to arrays when injecting data into the update API
324 *
325 * Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
326 * decoding, they are objects. This method allows us to pass in the object and return an associative array.
327 *
328 * @since 3.6.5
329 *
330 * @param stdClass $data
331 *
332 * @return array
333 */
334 private function convert_object_to_array( $data ) {
335 $new_data = array();
336 foreach ( $data as $key => $value ) {
337 $new_data[ $key ] = $value;
338 }
339
340 return $new_data;
341 }
342
343 /**
344 * Disable SSL verification in order to prevent download update failures
345 *
346 * @param array $args
347 * @param string $url
348 * @return object $array
349 */
350 public function http_request_args( $args, $url ) {
351
352 $verify_ssl = $this->verify_ssl();
353 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
354 $args['sslverify'] = $verify_ssl;
355 }
356 return $args;
357
358 }
359
360 /**
361 * Calls the API and, if successfull, returns the object delivered by the API.
362 *
363 * @uses get_bloginfo()
364 * @uses wp_remote_post()
365 * @uses is_wp_error()
366 *
367 * @param string $_action The requested action.
368 * @param array $_data Parameters for the API action.
369 * @return false|object
370 */
371 private function api_request( $_action, $_data ) {
372
373 global $wp_version, $edd_plugin_url_available;
374
375 // Do a quick status check on this domain if we haven't already checked it.
376 $store_hash = md5( $this->api_url );
377 if ( ! is_array( $edd_plugin_url_available ) || ! isset( $edd_plugin_url_available[ $store_hash ] ) ) {
378 $test_url_parts = parse_url( $this->api_url );
379
380 $scheme = ! empty( $test_url_parts['scheme'] ) ? $test_url_parts['scheme'] : 'http';
381 $host = ! empty( $test_url_parts['host'] ) ? $test_url_parts['host'] : '';
382 $port = ! empty( $test_url_parts['port'] ) ? ':' . $test_url_parts['port'] : '';
383
384 if ( empty( $host ) ) {
385 $edd_plugin_url_available[ $store_hash ] = false;
386 } else {
387 $test_url = $scheme . '://' . $host . $port;
388 $response = wp_remote_get( $test_url, array( 'timeout' => $this->health_check_timeout, 'sslverify' => true ) );
389 $edd_plugin_url_available[ $store_hash ] = is_wp_error( $response ) ? false : true;
390 }
391 }
392
393 if ( false === $edd_plugin_url_available[ $store_hash ] ) {
394 return;
395 }
396
397 $data = array_merge( $this->api_data, $_data );
398
399 if ( $data['slug'] != $this->slug ) {
400 return;
401 }
402
403 if( $this->api_url == trailingslashit ( home_url() ) ) {
404 return false; // Don't allow a plugin to ping itself
405 }
406
407 $api_params = array(
408 'edd_action' => 'get_version',
409 'license' => ! empty( $data['license'] ) ? $data['license'] : '',
410 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
411 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
412 'version' => isset( $data['version'] ) ? $data['version'] : false,
413 'slug' => $data['slug'],
414 'author' => $data['author'],
415 'url' => home_url(),
416 'beta' => ! empty( $data['beta'] ),
417 );
418
419 $verify_ssl = $this->verify_ssl();
420 $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
421
422 if ( ! is_wp_error( $request ) ) {
423 $request = json_decode( wp_remote_retrieve_body( $request ) );
424 }
425
426 if ( $request && isset( $request->sections ) ) {
427 $request->sections = maybe_unserialize( $request->sections );
428 } else {
429 $request = false;
430 }
431
432 if ( $request && isset( $request->banners ) ) {
433 $request->banners = maybe_unserialize( $request->banners );
434 }
435
436 if ( $request && isset( $request->icons ) ) {
437 $request->icons = maybe_unserialize( $request->icons );
438 }
439
440 if( ! empty( $request->sections ) ) {
441 foreach( $request->sections as $key => $section ) {
442 $request->$key = (array) $section;
443 }
444 }
445
446 return $request;
447 }
448
449 public function show_changelog() {
450
451 global $edd_plugin_data;
452
453 if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
454 return;
455 }
456
457 if( empty( $_REQUEST['plugin'] ) ) {
458 return;
459 }
460
461 if( empty( $_REQUEST['slug'] ) ) {
462 return;
463 }
464
465 if( ! current_user_can( 'update_plugins' ) ) {
466 wp_die( __( 'You do not have permission to install plugin updates', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
467 }
468
469 $data = $edd_plugin_data[ $_REQUEST['slug'] ];
470 $beta = ! empty( $data['beta'] ) ? true : false;
471 $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
472 $version_info = $this->get_cached_version_info( $cache_key );
473
474 if( false === $version_info ) {
475
476 $api_params = array(
477 'edd_action' => 'get_version',
478 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
479 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
480 'slug' => $_REQUEST['slug'],
481 'author' => $data['author'],
482 'url' => home_url(),
483 'beta' => ! empty( $data['beta'] )
484 );
485
486 $verify_ssl = $this->verify_ssl();
487 $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
488
489 if ( ! is_wp_error( $request ) ) {
490 $version_info = json_decode( wp_remote_retrieve_body( $request ) );
491 }
492
493
494 if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
495 $version_info->sections = maybe_unserialize( $version_info->sections );
496 } else {
497 $version_info = false;
498 }
499
500 if( ! empty( $version_info ) ) {
501 foreach( $version_info->sections as $key => $section ) {
502 $version_info->$key = (array) $section;
503 }
504 }
505
506 $this->set_version_info_cache( $version_info, $cache_key );
507
508 }
509
510 if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
511 echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
512 }
513
514 exit;
515 }
516
517 public function get_cached_version_info( $cache_key = '' ) {
518
519 if( empty( $cache_key ) ) {
520 $cache_key = $this->cache_key;
521 }
522
523 $cache = get_option( $cache_key );
524
525 if( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
526 return false; // Cache is expired
527 }
528
529 // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
530 $cache['value'] = json_decode( $cache['value'] );
531 if ( ! empty( $cache['value']->icons ) ) {
532 $cache['value']->icons = (array) $cache['value']->icons;
533 }
534
535 return $cache['value'];
536
537 }
538
539 public function set_version_info_cache( $value = '', $cache_key = '' ) {
540
541 if( empty( $cache_key ) ) {
542 $cache_key = $this->cache_key;
543 }
544
545 $data = array(
546 'timeout' => strtotime( '+3 hours', time() ),
547 'value' => json_encode( $value )
548 );
549
550 update_option( $cache_key, $data, 'no' );
551
552 }
553
554 /**
555 * Returns if the SSL of the store should be verified.
556 *
557 * @since 1.6.13
558 * @return bool
559 */
560 private function verify_ssl() {
561 return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
562 }
563
564 }
565