Completed
Push — develop ( ee5f9e...c0b481 )
by Zack
23:53 queued 03:55
created

EDD_SL_Plugin_Updater::show_update_notification()   F

Complexity

Conditions 24
Paths 472

Size

Total Lines 116

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 24
nc 472
nop 2
dl 0
loc 116
rs 0.5866
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 13 and the first side effect is on line 5.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
namespace GV;
3
4
// Exit if accessed directly
5
if ( ! defined( 'ABSPATH' ) ) exit;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
6
7
/**
8
 * Allows plugins to use their own update API.
9
 *
10
 * @author Easy Digital Downloads
11
 * @version 1.6.19
12
 */
13
class EDD_SL_Plugin_Updater {
14
15
	private $api_url     = '';
16
	private $api_data    = array();
17
	private $name        = '';
18
	private $slug        = '';
19
	private $version     = '';
20
	private $wp_override = false;
21
	private $cache_key   = '';
22
23
	private $health_check_timeout = 5;
24
25
	/**
26
	 * Class constructor.
27
	 *
28
	 * @uses plugin_basename()
29
	 * @uses hook()
30
	 *
31
	 * @param string  $_api_url     The URL pointing to the custom API endpoint.
32
	 * @param string  $_plugin_file Path to the plugin file.
33
	 * @param array   $_api_data    Optional data to send with API calls.
34
	 */
35
	public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
36
37
		global $edd_plugin_data;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
38
39
		$this->api_url     = trailingslashit( $_api_url );
40
		$this->api_data    = $_api_data;
41
		$this->name        = plugin_basename( $_plugin_file );
42
		$this->slug        = basename( $_plugin_file, '.php' );
43
		$this->version     = $_api_data['version'];
44
		$this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
45
		$this->beta        = ! empty( $this->api_data['beta'] ) ? true : false;
0 ignored issues
show
Bug introduced by
The property beta does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
46
		$this->cache_key   = 'edd_sl_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
47
48
		$edd_plugin_data[ $this->slug ] = $this->api_data;
49
50
		/**
51
		 * Fires after the $edd_plugin_data is setup.
52
		 *
53
		 * @since x.x.x
54
		 *
55
		 * @param array $edd_plugin_data Array of EDD SL plugin data.
56
		 */
57
		do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
58
59
		// Set up hooks.
60
		$this->init();
61
62
	}
63
64
	/**
65
	 * Set up WordPress filters to hook into WP's update process.
66
	 *
67
	 * @uses add_filter()
68
	 *
69
	 * @return void
70
	 */
71
	public function init() {
72
73
		add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
74
		add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
75
		remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
76
		add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
77
		add_action( 'admin_init', array( $this, 'show_changelog' ) );
78
79
	}
80
81
	/**
82
	 * Check for Updates at the defined API endpoint and modify the update array.
83
	 *
84
	 * This function dives into the update API just when WordPress creates its update array,
85
	 * then adds a custom API call and injects the custom plugin data retrieved from the API.
86
	 * It is reassembled from parts of the native WordPress plugin update code.
87
	 * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
88
	 *
89
	 * @uses api_request()
90
	 *
91
	 * @param array   $_transient_data Update array build by WordPress.
92
	 * @return array Modified update array with custom plugin data.
93
	 */
94
	public function check_update( $_transient_data ) {
95
96
		global $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
97
98
		if ( ! is_object( $_transient_data ) ) {
99
			$_transient_data = new \stdClass;
100
		}
101
102
		if ( 'plugins.php' == $pagenow && is_multisite() ) {
103
			return $_transient_data;
104
		}
105
106
		if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
107
			return $_transient_data;
108
		}
109
110
		$version_info = $this->get_cached_version_info();
111
112
		if ( false === $version_info ) {
113
			$version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
114
115
			$this->set_version_info_cache( $version_info );
116
117
		}
118
119
		if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
120
121
			if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
122
123
				$_transient_data->response[ $this->name ] = $version_info;
124
125
				// Make sure the plugin property is set to the plugin's name/location. See issue 1463 on Software Licensing's GitHub repo.
126
				$_transient_data->response[ $this->name ]->plugin = $this->name;
127
128
			}
129
130
			$_transient_data->last_checked           = time();
131
			$_transient_data->checked[ $this->name ] = $this->version;
132
133
		}
134
135
		return $_transient_data;
136
	}
137
138
	/**
139
	 * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
140
	 *
141
	 * @param string  $file
142
	 * @param array   $plugin
143
	 */
144
	public function show_update_notification( $file, $plugin ) {
145
146
		if ( is_network_admin() ) {
147
			return;
148
		}
149
150
		if( ! current_user_can( 'update_plugins' ) ) {
151
			return;
152
		}
153
154
		if( ! is_multisite() ) {
155
			return;
156
		}
157
158
		if ( $this->name != $file ) {
159
			return;
160
		}
161
162
		// Remove our filter on the site transient
163
		remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
164
165
		$update_cache = get_site_transient( 'update_plugins' );
166
167
		$update_cache = is_object( $update_cache ) ? $update_cache : new \stdClass();
168
169
		if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
170
171
			$version_info = $this->get_cached_version_info();
172
173
			if ( false === $version_info ) {
174
				$version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
175
176
				// Since we disabled our filter for the transient, we aren't running our object conversion on banners, sections, or icons. Do this now:
177
				if ( isset( $version_info->banners ) && ! is_array( $version_info->banners ) ) {
178
					$version_info->banners = $this->convert_object_to_array( $version_info->banners );
179
				}
180
181
				if ( isset( $version_info->sections ) && ! is_array( $version_info->sections ) ) {
182
					$version_info->sections = $this->convert_object_to_array( $version_info->sections );
183
				}
184
185
				if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
186
					$version_info->icons = $this->convert_object_to_array( $version_info->icons );
187
				}
188
189
				if ( isset( $version_info->icons ) && ! is_array( $version_info->icons ) ) {
190
					$version_info->icons = $this->convert_object_to_array( $version_info->icons );
191
				}
192
193
				if ( isset( $version_info->contributors ) && ! is_array( $version_info->contributors ) ) {
194
					$version_info->contributors = $this->convert_object_to_array( $version_info->contributors );
195
				}
196
197
				$this->set_version_info_cache( $version_info );
198
			}
199
200
			if ( ! is_object( $version_info ) ) {
201
				return;
202
			}
203
204
			if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
205
206
				$update_cache->response[ $this->name ] = $version_info;
207
208
			}
209
210
			$update_cache->last_checked = time();
211
			$update_cache->checked[ $this->name ] = $this->version;
212
213
			set_site_transient( 'update_plugins', $update_cache );
214
215
		} else {
216
217
			$version_info = $update_cache->response[ $this->name ];
218
219
		}
220
221
		// Restore our filter
222
		add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
223
224
		if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
225
226
			// build a plugin list row, with update notification
227
			$wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
228
			# <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
229
			echo '<tr class="plugin-update-tr" id="' . $this->slug . '-update" data-slug="' . $this->slug . '" data-plugin="' . $this->slug . '/' . $file . '">';
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$this'
Loading history...
introduced by
Expected next thing to be a escaping function, not '$file'
Loading history...
230
			echo '<td colspan="3" class="plugin-update colspanchange">';
231
			echo '<div class="update-message notice inline notice-warning notice-alt">';
232
233
			$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' );
234
235
			if ( empty( $version_info->download_link ) ) {
236
				printf(
237
					__( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'gravityview' ),
238
					esc_html( $version_info->name ),
239
					'<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
240
					esc_html( $version_info->new_version ),
241
					'</a>'
242
				);
243
			} else {
244
				printf(
245
					__( 'There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'gravityview' ),
246
					esc_html( $version_info->name ),
247
					'<a target="_blank" class="thickbox" href="' . esc_url( $changelog_link ) . '">',
248
					esc_html( $version_info->new_version ),
249
					'</a>',
250
					'<a href="' . esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) ) .'">',
251
					'</a>'
252
				);
253
			}
254
255
			do_action( "in_plugin_update_message-{$file}", $plugin, $version_info );
256
257
			echo '</div></td></tr>';
258
		}
259
	}
260
261
	/**
262
	 * Updates information on the "View version x.x details" page with custom data.
263
	 *
264
	 * @uses api_request()
265
	 *
266
	 * @param mixed   $_data
267
	 * @param string  $_action
268
	 * @param object  $_args
269
	 * @return object $_data
270
	 */
271
	public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
272
273
		if ( $_action != 'plugin_information' ) {
0 ignored issues
show
introduced by
Found "!= '". Use Yoda Condition checks, you must
Loading history...
274
275
			return $_data;
276
277
		}
278
279
		if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
280
281
			return $_data;
282
283
		}
284
285
		$to_send = array(
286
			'slug'   => $this->slug,
287
			'is_ssl' => is_ssl(),
288
			'fields' => array(
289
				'banners' => array(),
290
				'reviews' => false,
291
				'icons'   => array(),
292
			)
293
		);
294
295
		$cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
296
297
		// Get the transient where we store the api request for this plugin for 24 hours
298
		$edd_api_request_transient = $this->get_cached_version_info( $cache_key );
299
300
		//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.
301
		if ( empty( $edd_api_request_transient ) ) {
302
303
			$api_response = $this->api_request( 'plugin_information', $to_send );
304
305
			// Expires in 3 hours
306
			$this->set_version_info_cache( $api_response, $cache_key );
307
308
			if ( false !== $api_response ) {
309
				$_data = $api_response;
310
			}
311
312
		} else {
313
			$_data = $edd_api_request_transient;
314
		}
315
316
		// Convert sections into an associative array, since we're getting an object, but Core expects an array.
317
		if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
318
			$_data->sections = $this->convert_object_to_array( $_data->sections );
319
		}
320
321
		// Convert banners into an associative array, since we're getting an object, but Core expects an array.
322
		if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
323
			$_data->banners = $this->convert_object_to_array( $_data->banners );
324
		}
325
326
		// Convert icons into an associative array, since we're getting an object, but Core expects an array.
327
		if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
328
			$_data->icons = $this->convert_object_to_array( $_data->icons );
329
		}
330
331
		// Convert contributors into an associative array, since we're getting an object, but Core expects an array.
332
		if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
333
			$_data->contributors = $this->convert_object_to_array( $_data->contributors );
334
		}
335
336
		if( ! isset( $_data->plugin ) ) {
337
			$_data->plugin = $this->name;
338
		}
339
340
		return $_data;
341
	}
342
343
	/**
344
	 * Convert some objects to arrays when injecting data into the update API
345
	 *
346
	 * Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
347
	 * decoding, they are objects. This method allows us to pass in the object and return an associative array.
348
	 *
349
	 * @since 3.6.5
350
	 *
351
	 * @param \stdClass $data
352
	 *
353
	 * @return array
354
	 */
355
	private function convert_object_to_array( $data ) {
356
		$new_data = array();
357
		foreach ( $data as $key => $value ) {
0 ignored issues
show
Bug introduced by
The expression $data of type object<stdClass> is not traversable.
Loading history...
358
			$new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
359
		}
360
361
		return $new_data;
362
	}
363
364
	/**
365
	 * Disable SSL verification in order to prevent download update failures
366
	 *
367
	 * @param array   $args
368
	 * @param string  $url
369
	 * @return object $array
370
	 */
371
	public function http_request_args( $args, $url ) {
372
373
		$verify_ssl = $this->verify_ssl();
374
		if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
0 ignored issues
show
introduced by
Found "!== false". Use Yoda Condition checks, you must
Loading history...
375
			$args['sslverify'] = $verify_ssl;
376
		}
377
		return $args;
378
379
	}
380
381
	/**
382
	 * Calls the API and, if successful, returns the object delivered by the API.
383
	 *
384
	 * @uses get_bloginfo()
385
	 * @uses wp_remote_post()
386
	 * @uses is_wp_error()
387
	 *
388
	 * @param string  $_action The requested action.
389
	 * @param array   $_data   Parameters for the API action.
390
	 * @return false|object
391
	 */
392
	private function api_request( $_action, $_data ) {
0 ignored issues
show
Unused Code introduced by
The parameter $_action is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
393
394
		global $wp_version, $edd_plugin_url_available;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
395
396
		$verify_ssl = $this->verify_ssl();
397
398
		// Do a quick status check on this domain if we haven't already checked it.
399
		$store_hash = md5( $this->api_url );
400
		if ( ! is_array( $edd_plugin_url_available ) || ! isset( $edd_plugin_url_available[ $store_hash ] ) ) {
401
			$test_url_parts = parse_url( $this->api_url );
402
403
			$scheme = ! empty( $test_url_parts['scheme'] ) ? $test_url_parts['scheme']     : 'http';
404
			$host   = ! empty( $test_url_parts['host'] )   ? $test_url_parts['host']       : '';
405
			$port   = ! empty( $test_url_parts['port'] )   ? ':' . $test_url_parts['port'] : '';
406
407
			if ( empty( $host ) ) {
408
				$edd_plugin_url_available[ $store_hash ] = false;
409
			} else {
410
				$test_url = $scheme . '://' . $host . $port;
411
				$response = wp_remote_get( $test_url, array( 'timeout' => $this->health_check_timeout, 'sslverify' => $verify_ssl ) );
0 ignored issues
show
introduced by
wp_remote_get is highly discouraged, please use vip_safe_wp_remote_get() instead.
Loading history...
412
				$edd_plugin_url_available[ $store_hash ] = is_wp_error( $response ) ? false : true;
413
			}
414
		}
415
416
		if ( false === $edd_plugin_url_available[ $store_hash ] ) {
417
			return;
418
		}
419
420
		$data = array_merge( $this->api_data, $_data );
421
422
		if ( $data['slug'] != $this->slug ) {
423
			return;
424
		}
425
426
		if( $this->api_url == trailingslashit ( home_url() ) ) {
0 ignored issues
show
Coding Style introduced by
Space before opening parenthesis of function call prohibited
Loading history...
427
			return false; // Don't allow a plugin to ping itself
428
		}
429
430
		$api_params = array(
431
			'edd_action' => 'get_version',
432
			'license'    => ! empty( $data['license'] ) ? $data['license'] : '',
433
			'item_name'  => isset( $data['item_name'] ) ? $data['item_name'] : false,
434
			'item_id'    => isset( $data['item_id'] ) ? $data['item_id'] : false,
435
			'version'    => isset( $data['version'] ) ? $data['version'] : false,
436
			'slug'       => $data['slug'],
437
			'author'     => $data['author'],
438
			'url'        => home_url(),
439
			'beta'       => ! empty( $data['beta'] ),
440
		);
441
442
		$request    = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
443
444
		if ( ! is_wp_error( $request ) ) {
445
			$request = json_decode( wp_remote_retrieve_body( $request ) );
446
		}
447
448
		if ( $request && isset( $request->sections ) ) {
449
			$request->sections = maybe_unserialize( $request->sections );
450
		} else {
451
			$request = false;
452
		}
453
454
		if ( $request && isset( $request->banners ) ) {
455
			$request->banners = maybe_unserialize( $request->banners );
456
		}
457
458
		if ( $request && isset( $request->icons ) ) {
459
			$request->icons = maybe_unserialize( $request->icons );
460
		}
461
462
		if( ! empty( $request->sections ) ) {
463
			foreach( $request->sections as $key => $section ) {
464
				$request->$key = (array) $section;
465
			}
466
		}
467
468
		return $request;
469
	}
470
471
	public function show_changelog() {
472
473
		global $edd_plugin_data;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
474
475
		if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
476
			return;
477
		}
478
479
		if( empty( $_REQUEST['plugin'] ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
480
			return;
481
		}
482
483
		if( empty( $_REQUEST['slug'] ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
484
			return;
485
		}
486
487
		if( ! current_user_can( 'update_plugins' ) ) {
488
			wp_die( __( 'You do not have permission to install plugin updates', 'gravityview' ), __( 'Error', 'gravityview' ), array( 'response' => 403 ) );
489
		}
490
491
		$data         = $edd_plugin_data[ $_REQUEST['slug'] ];
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
492
		$beta         = ! empty( $data['beta'] ) ? true : false;
493
		$cache_key    = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
494
		$version_info = $this->get_cached_version_info( $cache_key );
495
496
		if( false === $version_info ) {
497
498
			$api_params = array(
499
				'edd_action' => 'get_version',
500
				'item_name'  => isset( $data['item_name'] ) ? $data['item_name'] : false,
501
				'item_id'    => isset( $data['item_id'] ) ? $data['item_id'] : false,
502
				'slug'       => $_REQUEST['slug'],
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_REQUEST
Loading history...
503
				'author'     => $data['author'],
504
				'url'        => home_url(),
505
				'beta'       => ! empty( $data['beta'] )
506
			);
507
508
			$verify_ssl = $this->verify_ssl();
509
			$request    = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params ) );
510
511
			if ( ! is_wp_error( $request ) ) {
512
				$version_info = json_decode( wp_remote_retrieve_body( $request ) );
513
			}
514
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
515
516
			if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
517
				$version_info->sections = maybe_unserialize( $version_info->sections );
518
			} else {
519
				$version_info = false;
520
			}
521
522
			if( ! empty( $version_info ) ) {
523
				foreach( $version_info->sections as $key => $section ) {
524
					$version_info->$key = (array) $section;
525
				}
526
			}
527
528
			$this->set_version_info_cache( $version_info, $cache_key );
529
530
		}
531
532
		if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
533
			echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$version_info'
Loading history...
534
		}
535
536
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method show_changelog() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
537
	}
538
539
	public function get_cached_version_info( $cache_key = '' ) {
540
541
		if( empty( $cache_key ) ) {
542
			$cache_key = $this->cache_key;
543
		}
544
545
		$cache = get_option( $cache_key );
546
547
		if( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
548
			return false; // Cache is expired
549
		}
550
551
		// We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
552
		$cache['value'] = json_decode( $cache['value'] );
553
		if ( ! empty( $cache['value']->icons ) ) {
554
			$cache['value']->icons = (array) $cache['value']->icons;
555
		}
556
557
		return $cache['value'];
558
559
	}
560
561
	public function set_version_info_cache( $value = '', $cache_key = '' ) {
562
563
		if( empty( $cache_key ) ) {
564
			$cache_key = $this->cache_key;
565
		}
566
567
		$data = array(
568
			'timeout' => strtotime( '+3 hours', time() ),
569
			'value'   => json_encode( $value )
570
		);
571
572
		update_option( $cache_key, $data, 'no' );
573
574
	}
575
576
	/**
577
	 * Returns if the SSL of the store should be verified.
578
	 *
579
	 * @since  1.6.13
580
	 * @return bool
581
	 */
582
	private function verify_ssl() {
583
		return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
584
	}
585
586
}
587