PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.26
Advanced Ads – Ad Manager & AdSense v2.0.26
2.0.26 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 3 weeks ago groups 3 weeks ago metaboxes 3 weeks ago pages 3 weeks ago placements 3 weeks ago class-action-links.php 3 weeks ago class-addon-box.php 3 weeks ago class-addon-updater.php 3 weeks ago class-admin-menu.php 3 weeks ago class-admin-notices.php 1 year ago class-ajax.php 3 weeks ago class-app.php 1 month ago class-assets.php 3 weeks ago class-authors.php 3 weeks ago class-edd-updater.php 3 weeks ago class-license-admin-post.php 3 weeks ago class-list-filters.php 3 weeks ago class-marketing.php 3 weeks ago class-metabox-ad-settings.php 1 year ago class-metabox-ad.php 3 weeks ago class-misc.php 3 weeks ago class-page-quick-edit.php 1 month ago class-plugin-auto-update.php 1 month ago class-plugin-installer.php 3 weeks ago class-post-list.php 3 weeks ago class-post-types.php 3 weeks ago class-screen-options.php 3 weeks ago class-settings.php 3 weeks ago class-shortcode-creator.php 3 weeks ago class-system-info.php 1 year ago class-tinymce.php 2 years ago class-translation-promo.php 3 weeks ago class-upgrades.php 3 weeks ago class-version-control.php 3 weeks ago class-welcome.php 1 year ago class-wordpress-dashboard.php 1 year ago index.php 2 years ago
class-edd-updater.php
806 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<string, mixed>
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 $_plugin_file Path to the plugin file.
91 * @param array<string, mixed> $_api_data Optional data to send with API calls.
92 */
93 public function __construct( $_plugin_file, $_api_data = null ) {
94 global $edd_plugin_data;
95
96 $this->api_url = defined( 'AA_SHOP_URL' ) ? esc_url( AA_SHOP_URL . '/license-api/' ) : $this->api_url;
97 $this->api_data = $_api_data;
98 $this->plugin_file = $_plugin_file;
99 $this->name = plugin_basename( $_plugin_file );
100 $this->slug = basename( dirname( $_plugin_file ) );
101 $this->version = $_api_data['version'];
102 $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
103 $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
104 $this->failed_request_cache_key = 'edd_sl_failed_http_' . md5( $this->api_url );
105
106 $edd_plugin_data[ $this->slug ] = $this->api_data;
107
108 /**
109 * Fires after the $edd_plugin_data is setup.
110 *
111 * @since x.x.x
112 *
113 * @param array $edd_plugin_data Array of EDD SL plugin data.
114 */
115 do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
116
117 // Set up hooks.
118 $this->init();
119 }
120
121 /**
122 * Set up WordPress filters to hook into WP's update process.
123 *
124 * @uses add_filter()
125 *
126 * @return void
127 */
128 public function init() {
129 add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_update' ] );
130 add_filter( 'plugins_api', [ $this, 'plugins_api_filter' ], 10, 3 );
131 add_filter( 'http_request_args', [ $this, 'http_request_args' ], 10, 2 );
132 add_action( 'after_plugin_row', [ $this, 'show_update_notification' ], 10, 2 );
133 add_action( 'admin_init', [ $this, 'show_changelog' ] );
134 }
135
136 /**
137 * Check for Updates at the defined API endpoint and modify the update array.
138 *
139 * This function dives into the update API just when WordPress creates its update array,
140 * then adds a custom API call and injects the custom plugin data retrieved from the API.
141 * It is reassembled from parts of the native WordPress plugin update code.
142 * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
143 *
144 * @uses api_request()
145 *
146 * @param array<string, mixed>|false $_transient_data Update array build by WordPress.
147 * @return array<string, mixed>|false Modified update array with custom plugin data.
148 */
149 public function check_update( $_transient_data ) {
150
151 if ( ! is_object( $_transient_data ) ) {
152 $_transient_data = new \stdClass();
153 }
154
155 if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
156 $existing = $_transient_data->response[ $this->name ];
157 if ( ! empty( $existing->package ) ) {
158 return $_transient_data;
159 }
160 }
161
162 $current = $this->get_update_transient_data();
163 if ( false !== $current && is_object( $current ) && isset( $current->new_version ) ) {
164 if ( version_compare( $this->version, $current->new_version, '<' ) ) {
165 $_transient_data->response[ $this->name ] = $current;
166 } else {
167 // Populating the no_update information is required to support auto-updates in WordPress 5.5.
168 $_transient_data->no_update[ $this->name ] = $current;
169 }
170 }
171 $_transient_data->last_checked = time();
172 $_transient_data->checked[ $this->name ] = $this->version;
173
174 return $_transient_data;
175 }
176
177 /**
178 * Get repo API data from store.
179 * Save to cache.
180 *
181 * @return \stdClass
182 */
183 public function get_repo_api_data() {
184 $version_info = $this->get_cached_version_info();
185
186 if ( false === $version_info ) {
187 $version_info = $this->api_request(
188 'plugin_latest_version',
189 [
190 'slug' => $this->slug,
191 'beta' => $this->beta,
192 ]
193 );
194 if ( ! $version_info ) {
195 return false;
196 }
197
198 // This is required for your plugin to support auto-updates in WordPress 5.5.
199 $version_info->plugin = $this->name;
200 $version_info->id = $this->name;
201 $version_info->tested = $this->get_tested_version( $version_info );
202 if ( ! isset( $version_info->requires ) ) {
203 $version_info->requires = '';
204 }
205 if ( ! isset( $version_info->requires_php ) ) {
206 $version_info->requires_php = '';
207 }
208
209 $this->set_version_info_cache( $version_info );
210 }
211
212 return $version_info;
213 }
214
215 /**
216 * Gets a limited set of data from the API response.
217 * This is used for the update_plugins transient.
218 *
219 * @since 3.8.12
220 * @return \stdClass|false
221 */
222 private function get_update_transient_data() {
223 $version_info = $this->get_repo_api_data();
224
225 if ( ! $version_info ) {
226 return false;
227 }
228
229 $limited_data = new \stdClass();
230 $limited_data->slug = $this->slug;
231 $limited_data->plugin = $this->name;
232 $limited_data->url = $version_info->url ?? '';
233 $limited_data->package = $this->resolve_package_url( $version_info );
234 $limited_data->icons = $this->convert_object_to_array( $version_info->icons );
235 $limited_data->banners = $this->convert_object_to_array( $version_info->banners );
236 $limited_data->new_version = $version_info->new_version;
237 $limited_data->tested = $version_info->tested ?? '6.7';
238 $limited_data->requires = $version_info->requires ?? '5.7';
239 $limited_data->requires_php = $version_info->requires_php ?? '7.4';
240
241 return $limited_data;
242 }
243
244 /**
245 * Gets the plugin's tested version.
246 *
247 * @since 1.9.2
248 * @param object $version_info The version info object.
249 * @return null|string
250 */
251 private function get_tested_version( $version_info ) {
252
253 // There is no tested version.
254 if ( empty( $version_info->tested ) ) {
255 return null;
256 }
257
258 // Strip off extra version data so the result is x.y or x.y.z.
259 list( $current_wp_version ) = explode( '-', get_bloginfo( 'version' ) );
260
261 // The tested version is greater than or equal to the current WP version, no need to do anything.
262 if ( version_compare( $version_info->tested, $current_wp_version, '>=' ) ) {
263 return $version_info->tested;
264 }
265 $current_version_parts = explode( '.', $current_wp_version );
266 $tested_parts = explode( '.', $version_info->tested );
267
268 // The current WordPress version is x.y.z, so update the tested version to match it.
269 if ( isset( $current_version_parts[2] ) && $current_version_parts[0] === $tested_parts[0] && $current_version_parts[1] === $tested_parts[1] ) {
270 $tested_parts[2] = $current_version_parts[2];
271 }
272
273 return implode( '.', $tested_parts );
274 }
275
276 /**
277 * Show the update notification on multisite subsites.
278 *
279 * @param string $file The plugin file.
280 * @param array<string, mixed> $plugin The plugin data.
281 */
282 public function show_update_notification( $file, $plugin ) {
283
284 // Return early if in the network admin, or if this is not a multisite install.
285 if ( is_network_admin() || ! is_multisite() ) {
286 return;
287 }
288
289 // Allow single site admins to see that an update is available.
290 if ( ! current_user_can( 'activate_plugins' ) ) {
291 return;
292 }
293
294 if ( $this->name !== $file ) {
295 return;
296 }
297
298 // Do not print any message if update does not exist.
299 $update_cache = get_site_transient( 'update_plugins' );
300
301 if ( ! isset( $update_cache->response[ $this->name ] ) ) {
302 if ( ! is_object( $update_cache ) ) {
303 $update_cache = new \stdClass();
304 }
305 $update_cache->response[ $this->name ] = $this->get_repo_api_data();
306 }
307
308 // 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.
309 if ( empty( $update_cache->response[ $this->name ] ) || version_compare( $this->version, $update_cache->response[ $this->name ]->new_version, '>=' ) ) {
310 return;
311 }
312
313 printf(
314 '<tr class="plugin-update-tr %3$s" id="%1$s-update" data-slug="%1$s" data-plugin="%2$s">',
315 $this->slug, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
316 $file, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
317 in_array( $this->name, $this->get_active_plugins(), true ) ? 'active' : 'inactive'
318 );
319
320 echo '<td colspan="3" class="plugin-update colspanchange">';
321 echo '<div class="update-message notice inline notice-warning notice-alt"><p>';
322
323 $changelog_link = '';
324 if ( ! empty( $update_cache->response[ $this->name ]->sections->changelog ) ) {
325 $changelog_link = add_query_arg(
326 [
327 'edd_sl_action' => 'view_plugin_changelog',
328 'plugin' => rawurlencode( $this->name ),
329 'slug' => rawurlencode( $this->slug ),
330 'TB_iframe' => 'true',
331 'width' => 77,
332 'height' => 911,
333 ],
334 self_admin_url( 'index.php' )
335 );
336 }
337 $update_link = add_query_arg(
338 [
339 'action' => 'upgrade-plugin',
340 'plugin' => rawurlencode( $this->name ),
341 ],
342 self_admin_url( 'update.php' )
343 );
344
345 printf(
346 /* translators: the plugin name. */
347 esc_html__( 'There is a new version of %1$s available.', 'advanced-ads' ),
348 esc_html( $plugin['Name'] )
349 );
350
351 if ( ! current_user_can( 'update_plugins' ) ) {
352 echo ' ';
353 esc_html_e( 'Contact your network administrator to install the update.', 'advanced-ads' );
354 } elseif ( empty( $update_cache->response[ $this->name ]->package ) && ! empty( $changelog_link ) ) {
355 echo ' ';
356 printf(
357 /* translators: 1: opening anchor tag, do not translate 2. the new plugin version 3. closing anchor tag, do not translate. */
358 __( '%1$sView version %2$s details%3$s.', 'advanced-ads' ), // phpcs:ignore
359 '<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
360 esc_html( $update_cache->response[ $this->name ]->new_version ),
361 '</a>'
362 );
363 } elseif ( ! empty( $changelog_link ) ) {
364 echo ' ';
365 printf(
366 /* 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. */
367 __( '%1$sView version %2$s details%3$s or %4$supdate now%5$s.', 'advanced-ads' ), // phpcs:ignore
368 '<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
369 esc_html( $update_cache->response[ $this->name ]->new_version ),
370 '</a>',
371 '<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
372 '</a>'
373 );
374 } else {
375 printf(
376 ' %1$s%2$s%3$s',
377 '<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
378 esc_html__( 'Update now.', 'advanced-ads' ),
379 '</a>'
380 );
381 }
382
383 do_action( "in_plugin_update_message-{$file}", $plugin, $plugin );
384
385 echo '</p></div></td></tr>';
386 }
387
388 /**
389 * Gets the plugins active in a multisite network.
390 *
391 * @return array<int, string>
392 */
393 private function get_active_plugins() {
394 $active_plugins = (array) get_option( 'active_plugins' );
395 $active_network_plugins = (array) get_site_option( 'active_sitewide_plugins' );
396
397 return array_merge( $active_plugins, array_keys( $active_network_plugins ) );
398 }
399
400 /**
401 * Updates information on the "View version x.x details" page with custom data.
402 *
403 * @uses api_request()
404 *
405 * @param mixed $_data The data originally requested.
406 * @param string $_action The type of information being requested.
407 * @param object $_args Plugin API arguments.
408 * @return object $_data
409 */
410 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
411
412 if ( 'plugin_information' !== $_action ) {
413 return $_data;
414 }
415
416 if ( ! isset( $_args->slug ) || ( $_args->slug !== $this->slug ) ) {
417 return $_data;
418 }
419
420 $to_send = [
421 'slug' => $this->slug,
422 'is_ssl' => is_ssl(),
423 'fields' => [
424 'banners' => [],
425 'reviews' => false,
426 'icons' => [],
427 ],
428 ];
429
430 // Get the transient where we store the api request for this plugin for 24 hours.
431 $edd_api_request_transient = $this->get_cached_version_info();
432
433 // 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.
434 if ( empty( $edd_api_request_transient ) ) {
435
436 $api_response = $this->api_request( 'plugin_information', $to_send );
437
438 // Expires in 3 hours.
439 $this->set_version_info_cache( $api_response );
440
441 if ( false !== $api_response ) {
442 $_data = $api_response;
443 }
444 } else {
445 $_data = $edd_api_request_transient;
446 }
447
448 // Safeguard: If the API failed or the transient returned false, ensure $_data is an object before proceeding.
449 if ( ! is_object( $_data ) ) {
450 return $_data;
451 }
452
453 // Convert sections into an associative array, since we're getting an object, but Core expects an array.
454 if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
455 $_data->sections = $this->convert_object_to_array( $_data->sections );
456 }
457
458 // Convert banners into an associative array, since we're getting an object, but Core expects an array.
459 if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
460 $_data->banners = $this->convert_object_to_array( $_data->banners );
461 }
462
463 // Convert icons into an associative array, since we're getting an object, but Core expects an array.
464 if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
465 $_data->icons = $this->convert_object_to_array( $_data->icons );
466 }
467
468 // Convert contributors into an associative array, since we're getting an object, but Core expects an array.
469 if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
470 $_data->contributors = $this->convert_object_to_array( $_data->contributors );
471 }
472
473 if ( ! isset( $_data->plugin ) ) {
474 $_data->plugin = $this->name;
475 }
476
477 if ( ! isset( $_data->version ) && ! empty( $_data->new_version ) ) {
478 $_data->version = $_data->new_version;
479 }
480
481 return $_data;
482 }
483
484 /**
485 * Convert some objects to arrays when injecting data into the update API
486 *
487 * Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
488 * decoding, they are objects. This method allows us to pass in the object and return an associative array.
489 *
490 * @since 3.6.5
491 *
492 * @param \stdClass $data The object to convert to an array.
493 *
494 * @return array<string, mixed>
495 */
496 private function convert_object_to_array( $data ) {
497 if ( ! is_array( $data ) && ! is_object( $data ) ) {
498 return [];
499 }
500 $new_data = [];
501 foreach ( $data as $key => $value ) {
502 $new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
503 }
504
505 return $new_data;
506 }
507
508 /**
509 * Disable SSL verification in order to prevent download update failures
510 *
511 * @param array<string, mixed> $args Array of HTTP request arguments.
512 * @param string $url The request URL.
513 * @return object $array
514 */
515 public function http_request_args( $args, $url ) {
516
517 if ( is_string( $url ) && (
518 str_contains( $url, 'edd_action=package_download' )
519 || str_contains( $url, '/edd-sl/package_download/' )
520 ) ) {
521 $args['sslverify'] = $this->verify_ssl();
522 }
523
524 return $args;
525 }
526
527 /**
528 * Resolve package download URL from shop get_version payload.
529 *
530 * @param object $version_info Remote version payload.
531 * @return string
532 */
533 private function resolve_package_url( $version_info ): string {
534 $package = is_object( $version_info ) ? trim( (string) ( $version_info->package ?? '' ) ) : '';
535 if ( '' !== $package ) {
536 return $package;
537 }
538
539 if ( is_object( $version_info ) ) {
540 return trim( (string) ( $version_info->download_link ?? '' ) );
541 }
542
543 return '';
544 }
545
546 /**
547 * Calls the API and, if successfull, returns the object delivered by the API.
548 *
549 * @uses get_bloginfo()
550 * @uses wp_remote_post()
551 * @uses is_wp_error()
552 *
553 * @param string $_action The requested action.
554 * @param array<string, mixed> $_data Parameters for the API action.
555 * @return false|object|void
556 */
557 private function api_request( $_action, $_data ) {
558 $data = array_merge( $this->api_data, $_data );
559
560 if ( $data['slug'] !== $this->slug ) {
561 return;
562 }
563
564 // Don't allow a plugin to ping itself.
565 if ( trailingslashit( home_url() ) === $this->api_url ) {
566 return false;
567 }
568
569 if ( $this->request_recently_failed() ) {
570 return false;
571 }
572
573 return $this->get_version_from_remote();
574 }
575
576 /**
577 * Determines if a request has recently failed.
578 *
579 * @since 1.9.1
580 *
581 * @return bool
582 */
583 private function request_recently_failed() {
584 $failed_request_details = get_option( $this->failed_request_cache_key );
585
586 // Request has never failed.
587 if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
588 return false;
589 }
590
591 /*
592 * Request previously failed, but the timeout has expired.
593 * This means we're allowed to try again.
594 */
595 if ( time() > $failed_request_details ) {
596 delete_option( $this->failed_request_cache_key );
597
598 return false;
599 }
600
601 return true;
602 }
603
604 /**
605 * Logs a failed HTTP request for this API URL.
606 * We set a timestamp for 1 hour from now. This prevents future API requests from being
607 * made to this domain for 1 hour. Once the timestamp is in the past, API requests
608 * will be allowed again. This way if the site is down for some reason we don't bombard
609 * it with failed API requests.
610 *
611 * @see EDD_SL_Plugin_Updater::request_recently_failed
612 *
613 * @since 1.9.1
614 */
615 private function log_failed_request() {
616 update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
617 }
618
619 /**
620 * If available, show the changelog for sites in a multisite install.
621 */
622 public function show_changelog() {
623
624 if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' !== $_REQUEST['edd_sl_action'] ) {
625 return;
626 }
627
628 if ( empty( $_REQUEST['plugin'] ) ) {
629 return;
630 }
631
632 if ( empty( $_REQUEST['slug'] ) || $this->slug !== $_REQUEST['slug'] ) {
633 return;
634 }
635
636 if ( ! current_user_can( 'update_plugins' ) ) {
637 wp_die( esc_html__( 'You do not have permission to install plugin updates', 'advanced-ads' ), esc_html__( 'Error', 'advanced-ads' ), [ 'response' => 403 ] );
638 }
639
640 $version_info = $this->get_repo_api_data();
641 if ( isset( $version_info->sections ) ) {
642 $sections = $this->convert_object_to_array( $version_info->sections );
643 if ( ! empty( $sections['changelog'] ) ) {
644 echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $sections['changelog'] ) . '</div>';
645 }
646 }
647
648 exit;
649 }
650
651 /**
652 * Gets the current version information from the remote site.
653 *
654 * @return array<string, mixed>|false
655 */
656 private function get_version_from_remote() {
657 $api_params = [
658 'edd_action' => 'get_version',
659 'license' => ! empty( $this->api_data['license'] ) ? $this->api_data['license'] : '',
660 'item_name' => isset( $this->api_data['item_name'] ) ? $this->api_data['item_name'] : false,
661 'item_id' => isset( $this->api_data['item_id'] ) ? $this->api_data['item_id'] : false,
662 'version' => isset( $this->api_data['version'] ) ? $this->api_data['version'] : false,
663 'slug' => $this->slug,
664 'author' => $this->api_data['author'],
665 'url' => home_url(),
666 'beta' => $this->beta,
667 'php_version' => phpversion(),
668 'wp_version' => get_bloginfo( 'version' ),
669 ];
670
671 /**
672 * Filters the parameters sent in the API request.
673 *
674 * @param array $api_params The array of data sent in the request.
675 * @param array $this->api_data The array of data set up in the class constructor.
676 * @param string $this->plugin_file The full path and filename of the file.
677 */
678 $api_params = apply_filters( 'edd_sl_plugin_updater_api_params', $api_params, $this->api_data, $this->plugin_file );
679 $request = wp_remote_post(
680 $this->api_url,
681 [
682 'timeout' => 15,
683 'sslverify' => $this->verify_ssl(),
684 'body' => $api_params,
685 ]
686 );
687
688 if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
689 $this->log_failed_request();
690
691 return false;
692 }
693
694 $body = json_decode( wp_remote_retrieve_body( $request ) );
695
696 if ( ! $body || ! is_object( $body ) ) {
697 $this->log_failed_request();
698
699 return false;
700 }
701
702 if ( isset( $body->sections ) ) {
703 $body->sections = maybe_unserialize( $body->sections );
704 }
705
706 if ( '' === $this->resolve_package_url( $body ) ) {
707 $this->log_failed_request();
708
709 return false;
710 }
711
712 $body->package = $this->resolve_package_url( $body );
713
714 if ( $body && isset( $body->banners ) ) {
715 $body->banners = maybe_unserialize( $body->banners );
716 }
717
718 if ( $body && isset( $body->icons ) ) {
719 $body->icons = maybe_unserialize( $body->icons );
720 }
721
722 if ( ! empty( $body->sections ) ) {
723 foreach ( $body->sections as $key => $section ) {
724 $body->$key = (array) $section;
725 }
726 }
727
728 return $body;
729 }
730
731 /**
732 * Get the version info from the cache, if it exists.
733 *
734 * @param string $cache_key The cache key to use.
735 * @return object
736 */
737 public function get_cached_version_info( $cache_key = '' ) {
738
739 if ( empty( $cache_key ) ) {
740 $cache_key = $this->get_cache_key();
741 }
742
743 $cache = get_option( $cache_key );
744
745 // Cache is expired.
746 if ( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
747 return false;
748 }
749
750 // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
751 $cache['value'] = json_decode( $cache['value'] );
752 if ( ! empty( $cache['value']->icons ) ) {
753 $cache['value']->icons = (array) $cache['value']->icons;
754 }
755
756 return $cache['value'];
757 }
758
759 /**
760 * Adds the plugin version information to the database.
761 *
762 * @param string $value The value to store.
763 * @param string $cache_key The cache key to use.
764 */
765 public function set_version_info_cache( $value = '', $cache_key = '' ) {
766
767 if ( empty( $cache_key ) ) {
768 $cache_key = $this->get_cache_key();
769 }
770
771 $data = [
772 'timeout' => strtotime( '+12 hours', time() ),
773 'value' => wp_json_encode( $value ),
774 ];
775
776 update_option( $cache_key, $data, 'no' );
777
778 // Delete the duplicate option.
779 delete_option( 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) ) );
780 }
781
782 /**
783 * Returns if the SSL of the store should be verified.
784 *
785 * @since 1.6.13
786 * @return bool
787 */
788 private function verify_ssl() {
789 $default = License_Utils::should_verify_ssl_for_url( $this->api_url );
790
791 return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', $default, $this );
792 }
793
794 /**
795 * Gets the unique key (option name) for a plugin.
796 *
797 * @since 1.9.0
798 * @return string
799 */
800 private function get_cache_key() {
801 $string = $this->slug . $this->api_data['license'] . $this->beta;
802
803 return 'advads_edd_sl_' . md5( serialize( $string ) );
804 }
805 }
806