| @@ -36,8 +36,10 @@ | ||
| 36 | 36 | private $_translation_updates; |
| 37 | 37 | |
| 38 | 38 | private static $_upgrade_basename = null; |
| 39 | 39 | |
| 40 | + const UPDATES_CHECK_CACHE_EXPIRATION = ( WP_FS__TIME_24_HOURS_IN_SEC / 24 ); | |
| 41 | + | |
| 40 | 42 | #-------------------------------------------------------------------------------- |
| 41 | 43 | #region Singleton |
| 42 | 44 | #-------------------------------------------------------------------------------- |
| 43 | 45 | |
| @@ -103,8 +105,10 @@ | ||
| 103 | 105 | ), 11, 2 ); |
| 104 | 106 | |
| 105 | 107 | if ( ! $this->_fs->has_any_active_valid_license() ) { |
| 106 | 108 | add_action( 'admin_head', array( &$this, 'catch_plugin_information_dialog_contents' ) ); |
| 109 | + } else { | |
| 110 | + add_action( 'admin_footer', array( &$this, '_add_fs_allow_updater_and_dialog_request_param' ) ); | |
| 107 | 111 | } |
| 108 | 112 | |
| 109 | 113 | if ( ! WP_FS__IS_PRODUCTION_MODE ) { |
| 110 | 114 | add_filter( 'http_request_host_is_external', array( |
| @@ -128,15 +132,48 @@ | ||
| 128 | 132 | } |
| 129 | 133 | |
| 130 | 134 | /** |
| 131 | 135 | * @author Leo Fajardo (@leorw) |
| 136 | + * @since 2.7.4 | |
| 137 | + */ | |
| 138 | + function _add_fs_allow_updater_and_dialog_request_param() { | |
| 139 | + if ( ! $this->is_plugin_information_dialog_for_plugin() ) { | |
| 140 | + return; | |
| 141 | + } | |
| 142 | + ?> | |
| 143 | + <script type="text/javascript"> | |
| 144 | + if ( typeof jQuery !== 'undefined' ) { | |
| 145 | + jQuery( document ).on( 'wp-plugin-updating', function( event, args ) { | |
| 146 | + if ( typeof args === 'object' && args.slug && typeof args.slug === 'string' ) { | |
| 147 | + if ( <?php echo json_encode( $this->_fs->get_slug() ) ?> === args.slug ) { | |
| 148 | + args.fs_allow_updater_and_dialog = true; | |
| 149 | + } | |
| 150 | + } | |
| 151 | + } ); | |
| 152 | + } | |
| 153 | + </script> | |
| 154 | + <?php | |
| 155 | + } | |
| 156 | + | |
| 157 | + /** | |
| 158 | + * @author Leo Fajardo (@leorw) | |
| 159 | + * @since 2.7.4 | |
| 160 | + * | |
| 161 | + * @return bool | |
| 162 | + */ | |
| 163 | + private function is_plugin_information_dialog_for_plugin() { | |
| 164 | + return ( | |
| 165 | + 'plugin-information' === fs_request_get( 'tab', false ) && | |
| 166 | + $this->_fs->get_slug() === fs_request_get_raw( 'plugin', false ) | |
| 167 | + ); | |
| 168 | + } | |
| 169 | + | |
| 170 | + /** | |
| 171 | + * @author Leo Fajardo (@leorw) | |
| 132 | 172 | * @since 2.1.4 |
| 133 | 173 | */ |
| 134 | 174 | function catch_plugin_information_dialog_contents() { |
| 135 | - if ( | |
| 136 | - 'plugin-information' !== fs_request_get( 'tab', false ) || | |
| 137 | - $this->_fs->get_slug() !== fs_request_get( 'plugin', false ) | |
| 138 | - ) { | |
| 175 | + if ( ! $this->is_plugin_information_dialog_for_plugin() ) { | |
| 139 | 176 | return; |
| 140 | 177 | } |
| 141 | 178 | |
| 142 | 179 | add_action( 'admin_footer', array( &$this, 'edit_and_echo_plugin_information_dialog_contents' ), 0, 1 ); |
| @@ -152,9 +189,9 @@ | ||
| 152 | 189 | */ |
| 153 | 190 | function edit_and_echo_plugin_information_dialog_contents( $hook_suffix ) { |
| 154 | 191 | if ( |
| 155 | 192 | 'plugin-information' !== fs_request_get( 'tab', false ) || |
| 156 | - $this->_fs->get_slug() !== fs_request_get( 'plugin', false ) | |
| 193 | + $this->_fs->get_slug() !== fs_request_get_raw( 'plugin', false ) | |
| 157 | 194 | ) { |
| 158 | 195 | return; |
| 159 | 196 | } |
| 160 | 197 | |
| @@ -165,17 +202,21 @@ | ||
| 165 | 202 | null; |
| 166 | 203 | |
| 167 | 204 | $contents = ob_get_clean(); |
| 168 | 205 | |
| 169 | - $update_button_id_attribute_pos = strpos( $contents, 'id="plugin_update_from_iframe"' ); | |
| 206 | + $install_or_update_button_id_attribute_pos = strpos( $contents, 'id="plugin_install_from_iframe"' ); | |
| 170 | 207 | |
| 171 | - if ( false !== $update_button_id_attribute_pos ) { | |
| 172 | - $update_button_start_pos = strrpos( | |
| 173 | - substr( $contents, 0, $update_button_id_attribute_pos ), | |
| 208 | + if ( false === $install_or_update_button_id_attribute_pos ) { | |
| 209 | + $install_or_update_button_id_attribute_pos = strpos( $contents, 'id="plugin_update_from_iframe"' ); | |
| 210 | + } | |
| 211 | + | |
| 212 | + if ( false !== $install_or_update_button_id_attribute_pos ) { | |
| 213 | + $install_or_update_button_start_pos = strrpos( | |
| 214 | + substr( $contents, 0, $install_or_update_button_id_attribute_pos ), | |
| 174 | 215 | '<a' |
| 175 | 216 | ); |
| 176 | 217 | |
| 177 | - $update_button_end_pos = ( strpos( $contents, '</a>', $update_button_id_attribute_pos ) + strlen( '</a>' ) ); | |
| 218 | + $install_or_update_button_end_pos = ( strpos( $contents, '</a>', $install_or_update_button_id_attribute_pos ) + strlen( '</a>' ) ); | |
| 178 | 219 | |
| 179 | 220 | /** |
| 180 | 221 | * The part of the contents without the update button. |
| 181 | 222 | * |
| @@ -181,11 +222,11 @@ | ||
| 181 | 222 | * |
| 182 | 223 | * @author Leo Fajardo (@leorw) |
| 183 | 224 | * @since 2.2.5 |
| 184 | 225 | */ |
| 185 | - $modified_contents = substr( $contents, 0, $update_button_start_pos ); | |
| 226 | + $modified_contents = substr( $contents, 0, $install_or_update_button_start_pos ); | |
| 186 | 227 | |
| 187 | - $update_button = substr( $contents, $update_button_start_pos, ( $update_button_end_pos - $update_button_start_pos ) ); | |
| 228 | + $install_or_update_button = substr( $contents, $install_or_update_button_start_pos, ( $install_or_update_button_end_pos - $install_or_update_button_start_pos ) ); | |
| 188 | 229 | |
| 189 | 230 | /** |
| 190 | 231 | * Replace the plugin information dialog's "Install Update Now" button's text and URL. If there's a license, |
| 191 | 232 | * the text will be "Renew license" and will link to the checkout page with the license's billing cycle |
| @@ -190,13 +231,13 @@ | ||
| 190 | 231 | * Replace the plugin information dialog's "Install Update Now" button's text and URL. If there's a license, |
| 191 | 232 | * the text will be "Renew license" and will link to the checkout page with the license's billing cycle |
| 192 | 233 | * and quota. If there's no license, the text will be "Buy license" and will link to the pricing page. |
| 193 | 234 | */ |
| 194 | - $update_button = preg_replace( | |
| 195 | - '/(\<a.+)(id="plugin_update_from_iframe")(.+href=")([^\s]+)(".*\>)(.+)(\<\/a>)/is', | |
| 235 | + $install_or_update_button = preg_replace( | |
| 236 | + '/(\<a.+)(id="plugin_(install|update)_from_iframe")(.+href=")([^\s]+)(".*\>)(.+)(\<\/a>)/is', | |
| 196 | 237 | is_object( $license ) ? |
| 197 | 238 | sprintf( |
| 198 | - '$1$3%s$5%s$7', | |
| 239 | + '$1$4%s$6%s$8', | |
| 199 | 240 | $this->_fs->checkout_url( |
| 200 | 241 | is_object( $subscription ) ? |
| 201 | 242 | ( 1 == $subscription->billing_cycle ? WP_FS__PERIOD_MONTHLY : WP_FS__PERIOD_ANNUALLY ) : |
| 202 | 243 | WP_FS__PERIOD_LIFETIME, |
| @@ -205,13 +246,13 @@ | ||
| 205 | 246 | ), |
| 206 | 247 | fs_text_inline( 'Renew license', 'renew-license', $this->_fs->get_slug() ) |
| 207 | 248 | ) : |
| 208 | 249 | sprintf( |
| 209 | - '$1$3%s$5%s$7', | |
| 250 | + '$1$4%s$6%s$8', | |
| 210 | 251 | $this->_fs->pricing_url(), |
| 211 | 252 | fs_text_inline( 'Buy license', 'buy-license', $this->_fs->get_slug() ) |
| 212 | 253 | ), |
| 213 | - $update_button | |
| 254 | + $install_or_update_button | |
| 214 | 255 | ); |
| 215 | 256 | |
| 216 | 257 | /** |
| 217 | 258 | * Append the modified button. |
| @@ -218,9 +259,9 @@ | ||
| 218 | 259 | * |
| 219 | 260 | * @author Leo Fajardo (@leorw) |
| 220 | 261 | * @since 2.2.5 |
| 221 | 262 | */ |
| 222 | - $modified_contents .= $update_button; | |
| 263 | + $modified_contents .= $install_or_update_button; | |
| 223 | 264 | |
| 224 | 265 | /** |
| 225 | 266 | * Append the remaining part of the contents after the update button. |
| 226 | 267 | * |
| @@ -226,9 +267,9 @@ | ||
| 226 | 267 | * |
| 227 | 268 | * @author Leo Fajardo (@leorw) |
| 228 | 269 | * @since 2.2.5 |
| 229 | 270 | */ |
| 230 | - $modified_contents .= substr( $contents, $update_button_end_pos ); | |
| 271 | + $modified_contents .= substr( $contents, $install_or_update_button_end_pos ); | |
| 231 | 272 | |
| 232 | 273 | $contents = $modified_contents; |
| 233 | 274 | } |
| 234 | 275 | |
| @@ -239,9 +280,13 @@ | ||
| 239 | 280 | * @author Vova Feldman (@svovaf) |
| 240 | 281 | * @since 2.0.0 |
| 241 | 282 | */ |
| 242 | 283 | private function add_transient_filters() { |
| 243 | - if ( $this->_fs->is_premium() && ! $this->_fs->is_tracking_allowed() ) { | |
| 284 | + if ( | |
| 285 | + $this->_fs->is_premium() && | |
| 286 | + $this->_fs->is_registered() && | |
| 287 | + ! FS_Permission_Manager::instance( $this->_fs )->is_essentials_tracking_allowed() | |
| 288 | + ) { | |
| 244 | 289 | $this->_logger->log( 'Opted out sites cannot receive automatic software updates.' ); |
| 245 | 290 | |
| 246 | 291 | return; |
| 247 | 292 | } |
| @@ -412,9 +457,9 @@ | ||
| 412 | 457 | } |
| 413 | 458 | |
| 414 | 459 | $themes_update = get_site_transient( 'update_themes' ); |
| 415 | 460 | if ( ! isset( $themes_update->response[ $theme_basename ] ) || |
| 416 | - empty( $themes_update->response[ $theme_basename ]['package'] ) | |
| 461 | + empty( $themes_update->response[ $theme_basename ]['package'] ) | |
| 417 | 462 | ) { |
| 418 | 463 | return $prepared_themes; |
| 419 | 464 | } |
| 420 | 465 | |
| @@ -494,38 +539,40 @@ | ||
| 494 | 539 | ) { |
| 495 | 540 | return $transient_data; |
| 496 | 541 | } |
| 497 | 542 | |
| 543 | + // Alias. | |
| 544 | + $basename = $this->_fs->premium_plugin_basename(); | |
| 545 | + | |
| 498 | 546 | global $wp_current_filter; |
| 499 | 547 | |
| 500 | - $current_plugin_version = $this->_fs->get_plugin_version(); | |
| 501 | - | |
| 502 | - if ( ! empty( $wp_current_filter ) && 'upgrader_process_complete' === $wp_current_filter[0] ) { | |
| 503 | - if ( | |
| 504 | - is_null( $this->_update_details ) || | |
| 505 | - ( is_object( $this->_update_details ) && $this->_update_details->new_version !== $current_plugin_version ) | |
| 506 | - ) { | |
| 548 | + /** | |
| 549 | + * During bulk updates, avoid re-injecting update data for the plugin itself once it has already been updated. | |
| 550 | + * | |
| 551 | + * If the custom package is re-added to the transient after the plugin update, WordPress may detect the package again and incorrectly report "The plugin is at the latest version" for a pending update, since the custom package version matches the currently installed version. | |
| 552 | + * | |
| 553 | + * Behavior differs depending on how the bulk update is triggered. Please refer to the inline comments for each flow below for details. | |
| 554 | + */ | |
| 555 | + if ( | |
| 556 | + ! empty( $wp_current_filter ) && ( | |
| 507 | 557 | /** |
| 508 | - * After an update, clear the stored update details and reparse the plugin's main file in order to get | |
| 509 | - * the updated version's information and prevent the previous update information from showing up on the | |
| 510 | - * updates page. | |
| 511 | - * | |
| 512 | - * @author Leo Fajardo (@leorw) | |
| 513 | - * @since 2.3.1 | |
| 558 | + * update-core.php and other upgrader pages: | |
| 559 | + * The `upgrader_process_complete` action fires only once after all updates have finished. In this case, it is the current action (`$wp_current_filter[0]`), while `self::$_upgrade_basename` may contain any plugin basename. | |
| 514 | 560 | */ |
| 515 | - $this->_update_details = null; | |
| 516 | - $current_plugin_version = $this->_fs->get_plugin_version( true ); | |
| 517 | - } | |
| 561 | + 'upgrader_process_complete' === $wp_current_filter[0] || | |
| 562 | + /** | |
| 563 | + * AJAX bulk updates (e.g., from the Plugins page): | |
| 564 | + * The `upgrader_process_complete` action fires multiple times — once for each plugin after it finishes updating. In this flow, it is not the current action (`$wp_current_filter[0]`) because it is triggered from another action. Instead, we compare `self::$_upgrade_basename` with the basename of the plugin currently being updated, since the `upgrader_process_complete` action runs separately for each plugin. | |
| 565 | + */ | |
| 566 | + ( in_array( 'upgrader_process_complete', $wp_current_filter ) && self::$_upgrade_basename === $basename ) | |
| 567 | + ) | |
| 568 | + ) { | |
| 569 | + return $transient_data; | |
| 518 | 570 | } |
| 519 | 571 | |
| 520 | 572 | if ( ! isset( $this->_update_details ) ) { |
| 521 | 573 | // Get plugin's newest update. |
| 522 | - $new_version = $this->_fs->get_update( | |
| 523 | - false, | |
| 524 | - fs_request_get_bool( 'force-check' ), | |
| 525 | - WP_FS__TIME_24_HOURS_IN_SEC / 24, | |
| 526 | - $current_plugin_version | |
| 527 | - ); | |
| 574 | + $new_version = $this->_fs->get_update( false, fs_request_get_bool( 'force-check' ) ); | |
| 528 | 575 | |
| 529 | 576 | $this->_update_details = false; |
| 530 | 577 | |
| 531 | 578 | if ( is_object( $new_version ) && $this->is_new_version_premium( $new_version ) ) { |
| @@ -541,11 +588,8 @@ | ||
| 541 | 588 | $this->_update_details = $this->get_update_details( $new_version ); |
| 542 | 589 | } |
| 543 | 590 | } |
| 544 | 591 | |
| 545 | - // Alias. | |
| 546 | - $basename = $this->_fs->premium_plugin_basename(); | |
| 547 | - | |
| 548 | 592 | if ( is_object( $this->_update_details ) ) { |
| 549 | 593 | if ( isset( $transient_data->no_update ) ) { |
| 550 | 594 | unset( $transient_data->no_update[ $basename ] ); |
| 551 | 595 | } |
| @@ -605,17 +649,15 @@ | ||
| 605 | 649 | } |
| 606 | 650 | |
| 607 | 651 | $slug = $this->_fs->get_slug(); |
| 608 | 652 | |
| 609 | - if ( $this->_fs->is_org_repo_compliant() && $this->_fs->is_freemium() ) { | |
| 653 | + if ( $this->can_fetch_data_from_wp_org() ) { | |
| 610 | 654 | if ( ! isset( $this->_translation_updates ) ) { |
| 611 | 655 | $this->_translation_updates = array(); |
| 612 | 656 | |
| 613 | - if ( current_user_can( 'update_languages' ) ) { | |
| 614 | - $translation_updates = $this->fetch_wp_org_module_translation_updates( $module_type, $slug ); | |
| 615 | - if ( ! empty( $translation_updates ) ) { | |
| 616 | - $this->_translation_updates = $translation_updates; | |
| 617 | - } | |
| 657 | + $translation_updates = $this->fetch_wp_org_module_translation_updates( $module_type, $slug ); | |
| 658 | + if ( ! empty( $translation_updates ) ) { | |
| 659 | + $this->_translation_updates = $translation_updates; | |
| 618 | 660 | } |
| 619 | 661 | } |
| 620 | 662 | |
| 621 | 663 | if ( ! empty( $this->_translation_updates ) ) { |
| @@ -633,9 +675,9 @@ | ||
| 633 | 675 | |
| 634 | 676 | foreach ( $this->_translation_updates as $translation_update ) { |
| 635 | 677 | $lang = $translation_update['language']; |
| 636 | 678 | if ( ! isset( $current_plugin_translation_updates_map[ $lang ] ) || |
| 637 | - version_compare( $translation_update['version'], $current_plugin_translation_updates_map[ $lang ]['version'], '>' ) | |
| 679 | + version_compare( $translation_update['version'], $current_plugin_translation_updates_map[ $lang ]['version'], '>' ) | |
| 638 | 680 | ) { |
| 639 | 681 | $current_plugin_translation_updates_map[ $lang ] = $translation_update; |
| 640 | 682 | } |
| 641 | 683 | } |
| @@ -647,9 +689,9 @@ | ||
| 647 | 689 | return $transient_data; |
| 648 | 690 | } |
| 649 | 691 | |
| 650 | 692 | /** |
| 651 | - * Get module's required data for the updates mechanism. | |
| 693 | + * Get module's required data for the updates' mechanism. | |
| 652 | 694 | * |
| 653 | 695 | * @author Vova Feldman (@svovaf) |
| 654 | 696 | * @since 2.0.0 |
| 655 | 697 | * |
| @@ -657,15 +699,16 @@ | ||
| 657 | 699 | * |
| 658 | 700 | * @return object |
| 659 | 701 | */ |
| 660 | 702 | function get_update_details( FS_Plugin_Tag $new_version ) { |
| 661 | - $update = new stdClass(); | |
| 662 | - $update->slug = $this->_fs->get_slug(); | |
| 663 | - $update->new_version = $new_version->version; | |
| 664 | - $update->url = WP_FS__ADDRESS; | |
| 665 | - $update->package = $new_version->url; | |
| 666 | - $update->tested = $new_version->tested_up_to_version; | |
| 667 | - $update->requires = $new_version->requires_platform_version; | |
| 703 | + $update = new stdClass(); | |
| 704 | + $update->slug = $this->_fs->get_slug(); | |
| 705 | + $update->new_version = $new_version->version; | |
| 706 | + $update->url = WP_FS__ADDRESS; | |
| 707 | + $update->package = $new_version->url; | |
| 708 | + $update->tested = self::get_tested_wp_version( $new_version->tested_up_to_version ); | |
| 709 | + $update->requires = $new_version->requires_platform_version; | |
| 710 | + $update->requires_php = $new_version->requires_programming_language_version; | |
| 668 | 711 | |
| 669 | 712 | $icon = $this->_fs->get_local_icon_url(); |
| 670 | 713 | |
| 671 | 714 | if ( ! empty( $icon ) ) { |
| @@ -675,18 +718,10 @@ | ||
| 675 | 718 | 'default' => $icon, |
| 676 | 719 | ); |
| 677 | 720 | } |
| 678 | 721 | |
| 679 | - if ( $this->_fs->is_premium() ) { | |
| 680 | - $latest_tag = $this->_fs->_fetch_latest_version( $this->_fs->get_id(), false ); | |
| 681 | - | |
| 682 | - if ( | |
| 683 | - isset( $latest_tag->readme ) && | |
| 684 | - isset( $latest_tag->readme->upgrade_notice ) && | |
| 685 | - ! empty( $latest_tag->readme->upgrade_notice ) | |
| 686 | - ) { | |
| 687 | - $update->upgrade_notice = $latest_tag->readme->upgrade_notice; | |
| 688 | - } | |
| 722 | + if ( $this->_fs->is_premium() && ! empty( $new_version->upgrade_notice ) ) { | |
| 723 | + $update->upgrade_notice = $new_version->upgrade_notice; | |
| 689 | 724 | } |
| 690 | 725 | |
| 691 | 726 | $update->{$this->_fs->get_module_type()} = $this->_fs->get_plugin_basename(); |
| 692 | 727 | |
| @@ -701,15 +736,10 @@ | ||
| 701 | 736 | * |
| 702 | 737 | * @return bool |
| 703 | 738 | */ |
| 704 | 739 | private function is_new_version_premium( FS_Plugin_Tag $new_version ) { |
| 705 | - $query_str = parse_url( $new_version->url, PHP_URL_QUERY ); | |
| 706 | - if ( empty( $query_str ) ) { | |
| 707 | - return false; | |
| 708 | - } | |
| 740 | + $params = fs_parse_url_params( $new_version->url ); | |
| 709 | 741 | |
| 710 | - parse_str( $query_str, $params ); | |
| 711 | - | |
| 712 | 742 | return ( isset( $params['is_premium'] ) && 'true' == $params['is_premium'] ); |
| 713 | 743 | } |
| 714 | 744 | |
| 715 | 745 | /** |
| @@ -805,11 +835,11 @@ | ||
| 805 | 835 | // Alias |
| 806 | 836 | $basename = $this->_fs->get_plugin_basename(); |
| 807 | 837 | |
| 808 | 838 | if ( ! is_object( $transient_data ) || |
| 809 | - ! isset( $transient_data->response ) || | |
| 839 | + ! isset( $transient_data->response ) || | |
| 810 | 840 | ! is_array( $transient_data->response ) || |
| 811 | - empty( $transient_data->response[ $basename ] ) | |
| 841 | + empty( $transient_data->response[ $basename ] ) | |
| 812 | 842 | ) { |
| 813 | 843 | return; |
| 814 | 844 | } |
| 815 | 845 | |
| @@ -834,30 +864,36 @@ | ||
| 834 | 864 | * |
| 835 | 865 | * @return bool|mixed |
| 836 | 866 | */ |
| 837 | 867 | static function _fetch_plugin_info_from_repository( $action, $args ) { |
| 838 | - $url = $http_url = 'http://api.wordpress.org/plugins/info/1.0/'; | |
| 839 | - if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) { | |
| 868 | + $url = $http_url = 'http://api.wordpress.org/plugins/info/1.2/'; | |
| 869 | + $url = add_query_arg( | |
| 870 | + array( | |
| 871 | + 'action' => $action, | |
| 872 | + 'request' => $args, | |
| 873 | + ), | |
| 874 | + $url | |
| 875 | + ); | |
| 876 | + | |
| 877 | + if ( wp_http_supports( array( 'ssl' ) ) ) { | |
| 840 | 878 | $url = set_url_scheme( $url, 'https' ); |
| 841 | 879 | } |
| 842 | 880 | |
| 843 | - $args = array( | |
| 844 | - 'timeout' => 15, | |
| 845 | - 'body' => array( | |
| 846 | - 'action' => $action, | |
| 847 | - 'request' => serialize( $args ) | |
| 848 | - ) | |
| 849 | - ); | |
| 881 | + // The new endpoint version serves only GET requests. | |
| 882 | + $request = wp_remote_get( $url, array( 'timeout' => 15 ) ); | |
| 850 | 883 | |
| 851 | - $request = wp_remote_post( $url, $args ); | |
| 852 | - | |
| 853 | 884 | if ( is_wp_error( $request ) ) { |
| 854 | 885 | return false; |
| 855 | 886 | } |
| 856 | 887 | |
| 857 | - $res = maybe_unserialize( wp_remote_retrieve_body( $request ) ); | |
| 888 | + $res = json_decode( wp_remote_retrieve_body( $request ), true ); | |
| 858 | 889 | |
| 859 | - if ( ! is_object( $res ) && ! is_array( $res ) ) { | |
| 890 | + if ( is_array( $res ) ) { | |
| 891 | + // Object casting is required in order to match the info/1.0 format. We are not decoding directly into an object as we need some fields to remain an array (e.g., $res->sections). | |
| 892 | + $res = (object) $res; | |
| 893 | + } | |
| 894 | + | |
| 895 | + if ( ! is_object( $res ) || isset( $res->error ) ) { | |
| 860 | 896 | return false; |
| 861 | 897 | } |
| 862 | 898 | |
| 863 | 899 | return $res; |
| @@ -863,8 +899,18 @@ | ||
| 863 | 899 | return $res; |
| 864 | 900 | } |
| 865 | 901 | |
| 866 | 902 | /** |
| 903 | + * Returns true if the product can fetch data from WordPress.org. | |
| 904 | + * | |
| 905 | + * @author Leo Fajardo (@leorw) | |
| 906 | + * @since 2.7.4 | |
| 907 | + */ | |
| 908 | + private function can_fetch_data_from_wp_org() { | |
| 909 | + return ( $this->_fs->is_org_repo_compliant() && $this->_fs->is_freemium() ); | |
| 910 | + } | |
| 911 | + | |
| 912 | + /** | |
| 867 | 913 | * Fetches module translation updates from wordpress.org. |
| 868 | 914 | * |
| 869 | 915 | * @author Leo Fajardo (@leorw) |
| 870 | 916 | * @since 2.1.2 |
| @@ -1046,9 +1092,9 @@ | ||
| 1046 | 1092 | $is_addon = true; |
| 1047 | 1093 | } |
| 1048 | 1094 | |
| 1049 | 1095 | $plugin_in_repo = false; |
| 1050 | - if ( ! $is_addon ) { | |
| 1096 | + if ( ! $is_addon && $this->can_fetch_data_from_wp_org() ) { | |
| 1051 | 1097 | // Try to fetch info from .org repository. |
| 1052 | 1098 | $data = self::_fetch_plugin_info_from_repository( $action, $args ); |
| 1053 | 1099 | |
| 1054 | 1100 | $plugin_in_repo = ( false !== $data ); |
| @@ -1092,8 +1138,9 @@ | ||
| 1092 | 1138 | |
| 1093 | 1139 | if ( ! $plugin_in_repo ) { |
| 1094 | 1140 | $data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created; |
| 1095 | 1141 | $data->requires = $new_version->requires_platform_version; |
| 1142 | + $data->requires_php = $new_version->requires_programming_language_version; | |
| 1096 | 1143 | $data->tested = $new_version->tested_up_to_version; |
| 1097 | 1144 | } |
| 1098 | 1145 | |
| 1099 | 1146 | $data->version = $new_version->version; |
| @@ -1145,12 +1192,31 @@ | ||
| 1145 | 1192 | $data = $new_version_readme_data; |
| 1146 | 1193 | } |
| 1147 | 1194 | } |
| 1148 | 1195 | |
| 1196 | + if ( ! empty( $data->tested ) ) { | |
| 1197 | + $data->tested = self::get_tested_wp_version( $data->tested ); | |
| 1198 | + } | |
| 1199 | + | |
| 1149 | 1200 | return $data; |
| 1150 | 1201 | } |
| 1151 | 1202 | |
| 1152 | 1203 | /** |
| 1204 | + * @since 2.5.3 If the current WordPress version is a patch of the tested version (e.g., 6.1.2 is a patch of 6.1), then override the tested version with the patch so developers won't need to release a new version just to bump the latest supported WP version. | |
| 1205 | + * | |
| 1206 | + * @param string|null $tested_up_to | |
| 1207 | + * | |
| 1208 | + * @return string|null | |
| 1209 | + */ | |
| 1210 | + private static function get_tested_wp_version( $tested_up_to ) { | |
| 1211 | + $current_wp_version = get_bloginfo( 'version' ); | |
| 1212 | + | |
| 1213 | + return ( ! empty($tested_up_to) && fs_starts_with( $current_wp_version, $tested_up_to . '.' ) ) ? | |
| 1214 | + $current_wp_version : | |
| 1215 | + $tested_up_to; | |
| 1216 | + } | |
| 1217 | + | |
| 1218 | + /** | |
| 1153 | 1219 | * @author Vova Feldman (@svovaf) |
| 1154 | 1220 | * @since 1.2.1.7 |
| 1155 | 1221 | * |
| 1156 | 1222 | * @param number|bool $addon_id |
| @@ -1159,9 +1225,9 @@ | ||
| 1159 | 1225 | * |
| 1160 | 1226 | * @return object |
| 1161 | 1227 | */ |
| 1162 | 1228 | private function get_latest_download_details( $addon_id = false, $newer_than = false, $fetch_readme = true ) { |
| 1163 | - return $this->_fs->_fetch_latest_version( $addon_id, true, WP_FS__TIME_24_HOURS_IN_SEC, $newer_than, $fetch_readme ); | |
| 1229 | + return $this->_fs->_fetch_latest_version( $addon_id, true, FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION, $newer_than, $fetch_readme ); | |
| 1164 | 1230 | } |
| 1165 | 1231 | |
| 1166 | 1232 | /** |
| 1167 | 1233 | * Checks if a given basename has a matching folder name |