PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.25
Advanced Ads – Ad Manager & AdSense v2.0.25
2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / includes / admin / class-edd-updater.php
advanced-ads / includes / admin Last commit date
ads 1 week ago groups 1 week ago metaboxes 1 year ago pages 1 day ago placements 1 day ago class-action-links.php 1 week ago class-addon-box.php 1 day ago class-addon-updater.php 1 day ago class-admin-menu.php 1 day ago class-admin-notices.php 1 year ago class-ajax.php 1 day ago class-app.php 1 day ago class-assets.php 1 day ago class-authors.php 1 year ago class-edd-updater.php 1 day ago class-license-admin-post.php 1 day ago class-list-filters.php 1 week ago class-marketing.php 1 year ago class-metabox-ad-settings.php 1 year ago class-metabox-ad.php 1 year ago class-misc.php 1 week ago class-page-quick-edit.php 1 day ago class-plugin-auto-update.php 1 day ago class-plugin-installer.php 1 day ago class-post-list.php 1 year ago class-post-types.php 1 week ago class-screen-options.php 3 months ago class-settings.php 1 day ago class-shortcode-creator.php 1 year ago class-system-info.php 1 year ago class-tinymce.php 2 years ago class-translation-promo.php 1 year ago class-upgrades.php 1 year ago class-version-control.php 3 months ago class-welcome.php 1 year ago class-wordpress-dashboard.php 1 year ago index.php 2 years ago
class-edd-updater.php
808 lines
1 <?php // phpcs:ignoreFile
2 /**
3 * Allows plugins to use their own update API.
4 *
5 * @package AdvancedAds
6 * @author Advanced Ads <info@wpadvancedads.com>
7 * @since 1.50.0
8 */
9
10 namespace AdvancedAds\Admin;
11
12 use AdvancedAds\License\License_Utils;
13
14 defined( 'ABSPATH' ) || exit;
15
16 /**
17 * Class Updater
18 */
19 class EDD_Updater {
20
21 /**
22 * The URL pointing to the custom API endpoint.
23 *
24 * @var string
25 */
26 private $api_url = '';
27
28 /**
29 * Optional data to send with API calls.
30 *
31 * @var array
32 */
33 private $api_data = [];
34
35 /**
36 * Path to the plugin file.
37 *
38 * @var string
39 */
40 private $plugin_file = '';
41
42 /**
43 * The plugin basename.
44 *
45 * @var string
46 */
47 private $name = '';
48
49 /**
50 * The plugin slug.
51 *
52 * @var string
53 */
54 private $slug = '';
55
56 /**
57 * The plugin version.
58 *
59 * @var string
60 */
61 private $version = '';
62
63 /**
64 * Whether to override the WordPress.org update check.
65 *
66 * @var bool
67 */
68 private $wp_override = false;
69
70 /**
71 * Whether to check for beta versions.
72 *
73 * @var bool
74 */
75 private $beta = false;
76
77 /**
78 * The cache key for failed requests.
79 *
80 * @var string
81 */
82 private $failed_request_cache_key;
83
84 /**
85 * Class constructor.
86 *
87 * @uses plugin_basename()
88 * @uses hook()
89 *
90 * @param string $_api_url The URL pointing to the custom API endpoint.
91 * @param string $_plugin_file Path to the plugin file.
92 * @param array $_api_data Optional data to send with API calls.
93 */
94 public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
95 global $edd_plugin_data;
96
97 $this->api_url = trailingslashit( $_api_url );
98 $this->api_data = $_api_data;
99 $this->plugin_file = $_plugin_file;
100 $this->name = plugin_basename( $_plugin_file );
101 $this->slug = basename( dirname( $_plugin_file ) );
102 $this->version = $_api_data['version'];
103 $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
104 $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
105 $this->failed_request_cache_key = 'edd_sl_failed_http_' . md5( $this->api_url );
106
107 $edd_plugin_data[ $this->slug ] = $this->api_data;
108
109 /**
110 * Fires after the $edd_plugin_data is setup.
111 *
112 * @since x.x.x
113 *
114 * @param array $edd_plugin_data Array of EDD SL plugin data.
115 */
116 do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
117
118 // Set up hooks.
119 $this->init();
120 }
121
122 /**
123 * Set up WordPress filters to hook into WP's update process.
124 *
125 * @uses add_filter()
126 *
127 * @return void
128 */
129 public function init() {
130 add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_update' ] );
131 add_filter( 'plugins_api', [ $this, 'plugins_api_filter' ], 10, 3 );
132 add_filter( 'http_request_args', [ $this, 'http_request_args' ], 10, 2 );
133 add_action( 'after_plugin_row', [ $this, 'show_update_notification' ], 10, 2 );
134 add_action( 'admin_init', [ $this, 'show_changelog' ] );
135 }
136
137 /**
138 * Check for Updates at the defined API endpoint and modify the update array.
139 *
140 * This function dives into the update API just when WordPress creates its update array,
141 * then adds a custom API call and injects the custom plugin data retrieved from the API.
142 * It is reassembled from parts of the native WordPress plugin update code.
143 * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
144 *
145 * @uses api_request()
146 *
147 * @param array $_transient_data Update array build by WordPress.
148 * @return array Modified update array with custom plugin data.
149 */
150 public function check_update( $_transient_data ) {
151
152 if ( ! is_object( $_transient_data ) ) {
153 $_transient_data = new \stdClass();
154 }
155
156 if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
157 $existing = $_transient_data->response[ $this->name ];
158 if ( ! empty( $existing->package ) ) {
159 return $_transient_data;
160 }
161 }
162
163 $current = $this->get_update_transient_data();
164 if ( false !== $current && is_object( $current ) && isset( $current->new_version ) ) {
165 if ( version_compare( $this->version, $current->new_version, '<' ) ) {
166 $_transient_data->response[ $this->name ] = $current;
167 } else {
168 // Populating the no_update information is required to support auto-updates in WordPress 5.5.
169 $_transient_data->no_update[ $this->name ] = $current;
170 }
171 }
172 $_transient_data->last_checked = time();
173 $_transient_data->checked[ $this->name ] = $this->version;
174
175 return $_transient_data;
176 }
177
178 /**
179 * Get repo API data from store.
180 * Save to cache.
181 *
182 * @return \stdClass
183 */
184 public function get_repo_api_data() {
185 $version_info = $this->get_cached_version_info();
186
187 if ( false === $version_info ) {
188 $version_info = $this->api_request(
189 'plugin_latest_version',
190 [
191 'slug' => $this->slug,
192 'beta' => $this->beta,
193 ]
194 );
195 if ( ! $version_info ) {
196 return false;
197 }
198
199 // This is required for your plugin to support auto-updates in WordPress 5.5.
200 $version_info->plugin = $this->name;
201 $version_info->id = $this->name;
202 $version_info->tested = $this->get_tested_version( $version_info );
203 if ( ! isset( $version_info->requires ) ) {
204 $version_info->requires = '';
205 }
206 if ( ! isset( $version_info->requires_php ) ) {
207 $version_info->requires_php = '';
208 }
209
210 $this->set_version_info_cache( $version_info );
211 }
212
213 return $version_info;
214 }
215
216 /**
217 * Gets a limited set of data from the API response.
218 * This is used for the update_plugins transient.
219 *
220 * @since 3.8.12
221 * @return \stdClass|false
222 */
223 private function get_update_transient_data() {
224 $version_info = $this->get_repo_api_data();
225
226 if ( ! $version_info ) {
227 return false;
228 }
229
230 $limited_data = new \stdClass();
231 $limited_data->slug = $this->slug;
232 $limited_data->plugin = $this->name;
233 $limited_data->url = $version_info->url ?? '';
234 $limited_data->package = $this->resolve_package_url( $version_info );
235 $limited_data->icons = $this->convert_object_to_array( $version_info->icons );
236 $limited_data->banners = $this->convert_object_to_array( $version_info->banners );
237 $limited_data->new_version = $version_info->new_version;
238 $limited_data->tested = $version_info->tested ?? '6.7';
239 $limited_data->requires = $version_info->requires ?? '5.7';
240 $limited_data->requires_php = $version_info->requires_php ?? '7.4';
241
242 return $limited_data;
243 }
244
245 /**
246 * Gets the plugin's tested version.
247 *
248 * @since 1.9.2
249 * @param object $version_info The version info object.
250 * @return null|string
251 */
252 private function get_tested_version( $version_info ) {
253
254 // There is no tested version.
255 if ( empty( $version_info->tested ) ) {
256 return null;
257 }
258
259 // Strip off extra version data so the result is x.y or x.y.z.
260 list( $current_wp_version ) = explode( '-', get_bloginfo( 'version' ) );
261
262 // The tested version is greater than or equal to the current WP version, no need to do anything.
263 if ( version_compare( $version_info->tested, $current_wp_version, '>=' ) ) {
264 return $version_info->tested;
265 }
266 $current_version_parts = explode( '.', $current_wp_version );
267 $tested_parts = explode( '.', $version_info->tested );
268
269 // The current WordPress version is x.y.z, so update the tested version to match it.
270 if ( isset( $current_version_parts[2] ) && $current_version_parts[0] === $tested_parts[0] && $current_version_parts[1] === $tested_parts[1] ) {
271 $tested_parts[2] = $current_version_parts[2];
272 }
273
274 return implode( '.', $tested_parts );
275 }
276
277 /**
278 * Show the update notification on multisite subsites.
279 *
280 * @param string $file The plugin file.
281 * @param array $plugin The plugin data.
282 */
283 public function show_update_notification( $file, $plugin ) {
284
285 // Return early if in the network admin, or if this is not a multisite install.
286 if ( is_network_admin() || ! is_multisite() ) {
287 return;
288 }
289
290 // Allow single site admins to see that an update is available.
291 if ( ! current_user_can( 'activate_plugins' ) ) {
292 return;
293 }
294
295 if ( $this->name !== $file ) {
296 return;
297 }
298
299 // Do not print any message if update does not exist.
300 $update_cache = get_site_transient( 'update_plugins' );
301
302 if ( ! isset( $update_cache->response[ $this->name ] ) ) {
303 if ( ! is_object( $update_cache ) ) {
304 $update_cache = new \stdClass();
305 }
306 $update_cache->response[ $this->name ] = $this->get_repo_api_data();
307 }
308
309 // 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.
310 if ( empty( $update_cache->response[ $this->name ] ) || version_compare( $this->version, $update_cache->response[ $this->name ]->new_version, '>=' ) ) {
311 return;
312 }
313
314 printf(
315 '<tr class="plugin-update-tr %3$s" id="%1$s-update" data-slug="%1$s" data-plugin="%2$s">',
316 $this->slug, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
317 $file, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
318 in_array( $this->name, $this->get_active_plugins(), true ) ? 'active' : 'inactive'
319 );
320
321 echo '<td colspan="3" class="plugin-update colspanchange">';
322 echo '<div class="update-message notice inline notice-warning notice-alt"><p>';
323
324 $changelog_link = '';
325 if ( ! empty( $update_cache->response[ $this->name ]->sections->changelog ) ) {
326 $changelog_link = add_query_arg(
327 [
328 'edd_sl_action' => 'view_plugin_changelog',
329 'plugin' => rawurlencode( $this->name ),
330 'slug' => rawurlencode( $this->slug ),
331 'TB_iframe' => 'true',
332 'width' => 77,
333 'height' => 911,
334 ],
335 self_admin_url( 'index.php' )
336 );
337 }
338 $update_link = add_query_arg(
339 [
340 'action' => 'upgrade-plugin',
341 'plugin' => rawurlencode( $this->name ),
342 ],
343 self_admin_url( 'update.php' )
344 );
345
346 printf(
347 /* translators: the plugin name. */
348 esc_html__( 'There is a new version of %1$s available.', 'advanced-ads' ),
349 esc_html( $plugin['Name'] )
350 );
351
352 if ( ! current_user_can( 'update_plugins' ) ) {
353 echo ' ';
354 esc_html_e( 'Contact your network administrator to install the update.', 'advanced-ads' );
355 } elseif ( empty( $update_cache->response[ $this->name ]->package ) && ! empty( $changelog_link ) ) {
356 echo ' ';
357 printf(
358 /* translators: 1: opening anchor tag, do not translate 2. the new plugin version 3. closing anchor tag, do not translate. */
359 __( '%1$sView version %2$s details%3$s.', 'advanced-ads' ), // phpcs:ignore
360 '<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
361 esc_html( $update_cache->response[ $this->name ]->new_version ),
362 '</a>'
363 );
364 } elseif ( ! empty( $changelog_link ) ) {
365 echo ' ';
366 printf(
367 /* translators: 1: opening anchor tag, do not translate 2. the new plugin version 3. closing anchor tag, do not translate 4. opening anchor tag, do not translate 5. closing anchor tag, do not translate. */
368 __( '%1$sView version %2$s details%3$s or %4$supdate now%5$s.', 'advanced-ads' ), // phpcs:ignore
369 '<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
370 esc_html( $update_cache->response[ $this->name ]->new_version ),
371 '</a>',
372 '<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
373 '</a>'
374 );
375 } else {
376 printf(
377 ' %1$s%2$s%3$s',
378 '<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
379 esc_html__( 'Update now.', 'advanced-ads' ),
380 '</a>'
381 );
382 }
383
384 do_action( "in_plugin_update_message-{$file}", $plugin, $plugin );
385
386 echo '</p></div></td></tr>';
387 }
388
389 /**
390 * Gets the plugins active in a multisite network.
391 *
392 * @return array
393 */
394 private function get_active_plugins() {
395 $active_plugins = (array) get_option( 'active_plugins' );
396 $active_network_plugins = (array) get_site_option( 'active_sitewide_plugins' );
397
398 return array_merge( $active_plugins, array_keys( $active_network_plugins ) );
399 }
400
401 /**
402 * Updates information on the "View version x.x details" page with custom data.
403 *
404 * @uses api_request()
405 *
406 * @param mixed $_data The data originally requested.
407 * @param string $_action The type of information being requested.
408 * @param object $_args Plugin API arguments.
409 * @return object $_data
410 */
411 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
412
413 if ( 'plugin_information' !== $_action ) {
414 return $_data;
415 }
416
417 if ( ! isset( $_args->slug ) || ( $_args->slug !== $this->slug ) ) {
418 return $_data;
419 }
420
421 $to_send = [
422 'slug' => $this->slug,
423 'is_ssl' => is_ssl(),
424 'fields' => [
425 'banners' => [],
426 'reviews' => false,
427 'icons' => [],
428 ],
429 ];
430
431 // Get the transient where we store the api request for this plugin for 24 hours.
432 $edd_api_request_transient = $this->get_cached_version_info();
433
434 // 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.
435 if ( empty( $edd_api_request_transient ) ) {
436
437 $api_response = $this->api_request( 'plugin_information', $to_send );
438
439 // Expires in 3 hours.
440 $this->set_version_info_cache( $api_response );
441
442 if ( false !== $api_response ) {
443 $_data = $api_response;
444 }
445 } else {
446 $_data = $edd_api_request_transient;
447 }
448
449 // Safeguard: If the API failed or the transient returned false, ensure $_data is an object before proceeding.
450 if ( ! is_object( $_data ) ) {
451 return $_data;
452 }
453
454 // Convert sections into an associative array, since we're getting an object, but Core expects an array.
455 if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
456 $_data->sections = $this->convert_object_to_array( $_data->sections );
457 }
458
459 // Convert banners into an associative array, since we're getting an object, but Core expects an array.
460 if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
461 $_data->banners = $this->convert_object_to_array( $_data->banners );
462 }
463
464 // Convert icons into an associative array, since we're getting an object, but Core expects an array.
465 if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
466 $_data->icons = $this->convert_object_to_array( $_data->icons );
467 }
468
469 // Convert contributors into an associative array, since we're getting an object, but Core expects an array.
470 if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
471 $_data->contributors = $this->convert_object_to_array( $_data->contributors );
472 }
473
474 if ( ! isset( $_data->plugin ) ) {
475 $_data->plugin = $this->name;
476 }
477
478 if ( ! isset( $_data->version ) && ! empty( $_data->new_version ) ) {
479 $_data->version = $_data->new_version;
480 }
481
482 return $_data;
483 }
484
485 /**
486 * Convert some objects to arrays when injecting data into the update API
487 *
488 * Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
489 * decoding, they are objects. This method allows us to pass in the object and return an associative array.
490 *
491 * @since 3.6.5
492 *
493 * @param \stdClass $data The object to convert to an array.
494 *
495 * @return array
496 */
497 private function convert_object_to_array( $data ) {
498 if ( ! is_array( $data ) && ! is_object( $data ) ) {
499 return [];
500 }
501 $new_data = [];
502 foreach ( $data as $key => $value ) {
503 $new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
504 }
505
506 return $new_data;
507 }
508
509 /**
510 * Disable SSL verification in order to prevent download update failures
511 *
512 * @param array $args Array of HTTP request arguments.
513 * @param string $url The request URL.
514 * @return object $array
515 */
516 public function http_request_args( $args, $url ) {
517
518 if ( is_string( $url ) && (
519 str_contains( $url, 'edd_action=package_download' )
520 || str_contains( $url, '/edd-sl/package_download/' )
521 ) ) {
522 $args['sslverify'] = $this->verify_ssl();
523 }
524
525 return $args;
526 }
527
528 /**
529 * Resolve package download URL from shop get_version payload.
530 *
531 * @param object $version_info Remote version payload.
532 * @return string
533 */
534 private function resolve_package_url( $version_info ): string {
535 $package = is_object( $version_info ) ? trim( (string) ( $version_info->package ?? '' ) ) : '';
536 if ( '' !== $package ) {
537 return $package;
538 }
539
540 if ( is_object( $version_info ) ) {
541 return trim( (string) ( $version_info->download_link ?? '' ) );
542 }
543
544 return '';
545 }
546
547 /**
548 * Calls the API and, if successfull, returns the object delivered by the API.
549 *
550 * @uses get_bloginfo()
551 * @uses wp_remote_post()
552 * @uses is_wp_error()
553 *
554 * @param string $_action The requested action.
555 * @param array $_data Parameters for the API action.
556 * @return false|object|void
557 */
558 private function api_request( $_action, $_data ) {
559 $data = array_merge( $this->api_data, $_data );
560
561 if ( $data['slug'] !== $this->slug ) {
562 return;
563 }
564
565 // Don't allow a plugin to ping itself.
566 if ( trailingslashit( home_url() ) === $this->api_url ) {
567 return false;
568 }
569
570 if ( $this->request_recently_failed() ) {
571 return false;
572 }
573
574 return $this->get_version_from_remote();
575 }
576
577 /**
578 * Determines if a request has recently failed.
579 *
580 * @since 1.9.1
581 *
582 * @return bool
583 */
584 private function request_recently_failed() {
585 $failed_request_details = get_option( $this->failed_request_cache_key );
586
587 // Request has never failed.
588 if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
589 return false;
590 }
591
592 /*
593 * Request previously failed, but the timeout has expired.
594 * This means we're allowed to try again.
595 */
596 if ( time() > $failed_request_details ) {
597 delete_option( $this->failed_request_cache_key );
598
599 return false;
600 }
601
602 return true;
603 }
604
605 /**
606 * Logs a failed HTTP request for this API URL.
607 * We set a timestamp for 1 hour from now. This prevents future API requests from being
608 * made to this domain for 1 hour. Once the timestamp is in the past, API requests
609 * will be allowed again. This way if the site is down for some reason we don't bombard
610 * it with failed API requests.
611 *
612 * @see EDD_SL_Plugin_Updater::request_recently_failed
613 *
614 * @since 1.9.1
615 */
616 private function log_failed_request() {
617 update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
618 }
619
620 /**
621 * If available, show the changelog for sites in a multisite install.
622 */
623 public function show_changelog() {
624
625 if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' !== $_REQUEST['edd_sl_action'] ) {
626 return;
627 }
628
629 if ( empty( $_REQUEST['plugin'] ) ) {
630 return;
631 }
632
633 if ( empty( $_REQUEST['slug'] ) || $this->slug !== $_REQUEST['slug'] ) {
634 return;
635 }
636
637 if ( ! current_user_can( 'update_plugins' ) ) {
638 wp_die( esc_html__( 'You do not have permission to install plugin updates', 'advanced-ads' ), esc_html__( 'Error', 'advanced-ads' ), [ 'response' => 403 ] );
639 }
640
641 $version_info = $this->get_repo_api_data();
642 if ( isset( $version_info->sections ) ) {
643 $sections = $this->convert_object_to_array( $version_info->sections );
644 if ( ! empty( $sections['changelog'] ) ) {
645 echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $sections['changelog'] ) . '</div>';
646 }
647 }
648
649 exit;
650 }
651
652 /**
653 * Gets the current version information from the remote site.
654 *
655 * @return array|false
656 */
657 private function get_version_from_remote() {
658 $api_params = [
659 'edd_action' => 'get_version',
660 'license' => ! empty( $this->api_data['license'] ) ? $this->api_data['license'] : '',
661 'item_name' => isset( $this->api_data['item_name'] ) ? $this->api_data['item_name'] : false,
662 'item_id' => isset( $this->api_data['item_id'] ) ? $this->api_data['item_id'] : false,
663 'version' => isset( $this->api_data['version'] ) ? $this->api_data['version'] : false,
664 'slug' => $this->slug,
665 'author' => $this->api_data['author'],
666 'url' => home_url(),
667 'beta' => $this->beta,
668 'php_version' => phpversion(),
669 'wp_version' => get_bloginfo( 'version' ),
670 ];
671
672 /**
673 * Filters the parameters sent in the API request.
674 *
675 * @param array $api_params The array of data sent in the request.
676 * @param array $this->api_data The array of data set up in the class constructor.
677 * @param string $this->plugin_file The full path and filename of the file.
678 */
679 $api_params = apply_filters( 'edd_sl_plugin_updater_api_params', $api_params, $this->api_data, $this->plugin_file );
680
681 $request = wp_remote_post(
682 $this->api_url,
683 [
684 'timeout' => 15,
685 'sslverify' => $this->verify_ssl(),
686 'body' => $api_params,
687 ]
688 );
689
690 if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
691 $this->log_failed_request();
692
693 return false;
694 }
695
696 $body = json_decode( wp_remote_retrieve_body( $request ) );
697
698 if ( ! $body || ! is_object( $body ) ) {
699 $this->log_failed_request();
700
701 return false;
702 }
703
704 if ( isset( $body->sections ) ) {
705 $body->sections = maybe_unserialize( $body->sections );
706 }
707
708 if ( '' === $this->resolve_package_url( $body ) ) {
709 $this->log_failed_request();
710
711 return false;
712 }
713
714 $body->package = $this->resolve_package_url( $body );
715
716 if ( $body && isset( $body->banners ) ) {
717 $body->banners = maybe_unserialize( $body->banners );
718 }
719
720 if ( $body && isset( $body->icons ) ) {
721 $body->icons = maybe_unserialize( $body->icons );
722 }
723
724 if ( ! empty( $body->sections ) ) {
725 foreach ( $body->sections as $key => $section ) {
726 $body->$key = (array) $section;
727 }
728 }
729
730 return $body;
731 }
732
733 /**
734 * Get the version info from the cache, if it exists.
735 *
736 * @param string $cache_key The cache key to use.
737 * @return object
738 */
739 public function get_cached_version_info( $cache_key = '' ) {
740
741 if ( empty( $cache_key ) ) {
742 $cache_key = $this->get_cache_key();
743 }
744
745 $cache = get_option( $cache_key );
746
747 // Cache is expired.
748 if ( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
749 return false;
750 }
751
752 // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
753 $cache['value'] = json_decode( $cache['value'] );
754 if ( ! empty( $cache['value']->icons ) ) {
755 $cache['value']->icons = (array) $cache['value']->icons;
756 }
757
758 return $cache['value'];
759 }
760
761 /**
762 * Adds the plugin version information to the database.
763 *
764 * @param string $value The value to store.
765 * @param string $cache_key The cache key to use.
766 */
767 public function set_version_info_cache( $value = '', $cache_key = '' ) {
768
769 if ( empty( $cache_key ) ) {
770 $cache_key = $this->get_cache_key();
771 }
772
773 $data = [
774 'timeout' => strtotime( '+12 hours', time() ),
775 'value' => wp_json_encode( $value ),
776 ];
777
778 update_option( $cache_key, $data, 'no' );
779
780 // Delete the duplicate option.
781 delete_option( 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) ) );
782 }
783
784 /**
785 * Returns if the SSL of the store should be verified.
786 *
787 * @since 1.6.13
788 * @return bool
789 */
790 private function verify_ssl() {
791 $default = License_Utils::should_verify_ssl_for_url( $this->api_url );
792
793 return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', $default, $this );
794 }
795
796 /**
797 * Gets the unique key (option name) for a plugin.
798 *
799 * @since 1.9.0
800 * @return string
801 */
802 private function get_cache_key() {
803 $string = $this->slug . $this->api_data['license'] . $this->beta;
804
805 return 'advads_edd_sl_' . md5( serialize( $string ) );
806 }
807 }
808