Make WordPress Core

Ticket #40702: 40702-geoip.5.diff

File 40702-geoip.5.diff, 14.6 KB (added by iandunn, 7 years ago)

Replace the upgrade routine with a temporary hack, to improve UX

  • src/wp-admin/includes/ajax-actions.php

    diff --git src/wp-admin/includes/ajax-actions.php src/wp-admin/includes/ajax-actions.php
    index a2e829bf65..e132ac8101 100644
    function wp_ajax_get_community_events() { 
    312312        $saved_location = get_user_option( 'community-events-location', $user_id );
    313313        $events_client  = new WP_Community_Events( $user_id, $saved_location );
    314314        $events         = $events_client->get_events( $search, $timezone );
     315        $ip_changed     = false;
    315316
    316317        if ( is_wp_error( $events ) ) {
    317318                wp_send_json_error( array(
    318319                        'error' => $events->get_error_message(),
    319320                ) );
    320321        } else {
    321                 if ( isset( $events['location'] ) ) {
    322                         // Store the location network-wide, so the user doesn't have to set it on each site.
     322                if ( empty( $saved_location['ip'] ) && ! empty( $events['location']['ip'] ) ) {
     323                        $ip_changed = true;
     324                } elseif ( isset( $saved_location['ip'] ) && ! empty( $events['location']['ip'] ) && $saved_location['ip'] !== $events['location']['ip'] ) {
     325                        $ip_changed = true;
     326                }
     327
     328                /*
     329                 * The location should only be updated when it changes. The API doesn't always return
     330                 * a full location; sometimes it's missing the description or country. The location
     331                 * that was saved during the initial request is known to be good and complete, though.
     332                 * It should be left in tact until the user explicitly changes it (either by manually
     333                 * searching for a new location, or by changing their IP address).
     334                 *
     335                 * If the location were updated with an incomplete response from the API, then it could
     336                 * break assumptions that the UI makes (e.g., that there will always be a description
     337                 * that corresponds to a latitude/longitude location).
     338                 *
     339                 * The location is stored network-wide, so that the user doesn't have to set it on each site.
     340                 */
     341                if ( $ip_changed || $search ) {
    323342                        update_user_option( $user_id, 'community-events-location', $events['location'], true );
    324343                }
    325344
  • src/wp-admin/includes/class-wp-community-events.php

    diff --git src/wp-admin/includes/class-wp-community-events.php src/wp-admin/includes/class-wp-community-events.php
    index e27e731fff..9a41459740 100644
    class WP_Community_Events { 
    9494                        return $cached_events;
    9595                }
    9696
    97                 $request_url    = $this->get_request_url( $location_search, $timezone );
    98                 $response       = wp_remote_get( $request_url );
     97                $api_url        = 'https://api.wordpress.org/events/1.0/';
     98                $request_args   = $this->get_request_args( $location_search, $timezone );
     99                $response       = wp_remote_get( $api_url, $request_args );
    99100                $response_code  = wp_remote_retrieve_response_code( $response );
    100101                $response_body  = json_decode( wp_remote_retrieve_body( $response ), true );
    101102                $response_error = null;
    102                 $debugging_info = compact( 'request_url', 'response_code', 'response_body' );
     103                $debugging_info = compact( 'api_url', 'request_args', 'response_code', 'response_body' );
    103104
    104105                if ( is_wp_error( $response ) ) {
    105106                        $response_error = $response;
    class WP_Community_Events { 
    128129                                unset( $response_body['ttl'] );
    129130                        }
    130131
     132                        /*
     133                         * The IP in the response is usually the same as the one that was sent
     134                         * in the request, but in some cases it is different. In those cases,
     135                         * it's important to reset it back to the IP from the request.
     136                         *
     137                         * For example, if the IP sent in the request is private (e.g., 192.168.1.100),
     138                         * then the API will ignore that and use the corresponding public IP instead,
     139                         * and the public IP will get returned. If the public IP were saved, though,
     140                         * then get_cached_events() would always return `false`, because the transient
     141                         * would be generated based on the public IP when saving the cache, but generated
     142                         * based on the private IP when retrieving the cache.
     143                         */
     144                        if ( ! empty( $response_body['location']['ip'] ) ) {
     145                                $response_body['location']['ip'] = $request_args['body']['ip'];
     146                        }
     147
     148                        /*
     149                         * The API doesn't return a description for latitude/longitude requests,
     150                         * but the description is already saved in the user location, so that
     151                         * one can be used instead.
     152                         */
     153                        if ( $this->coordinates_match( $request_args['body'], $response_body['location'] ) && empty( $response_body['location']['description'] ) ) {
     154                                $response_body['location']['description'] = $this->user_location['description'];
     155                        }
     156
    131157                        $this->cache_events( $response_body, $expiration );
    132158
    133159                        $response_body = $this->trim_events( $response_body );
    class WP_Community_Events { 
    143169        }
    144170
    145171        /**
    146          * Builds a URL for requests to the w.org Events API.
     172         * Builds an array of args to use in an HTTP request to the w.org Events API.
    147173         *
    148174         * @access protected
    149175         * @since 4.8.0
    150176         *
    151177         * @param  string $search   Optional. City search string. Default empty string.
    152178         * @param  string $timezone Optional. Timezone string. Default empty string.
    153          * @return string The request URL.
     179         * @return @return array The request args.
    154180         */
    155         protected function get_request_url( $search = '', $timezone = '' ) {
    156                 $api_url = 'https://api.wordpress.org/events/1.0/';
    157                 $args    = array(
     181        protected function get_request_args( $search = '', $timezone = '' ) {
     182                $args = array(
    158183                        'number' => 5, // Get more than three in case some get trimmed out.
    159                         'ip'     => $this->get_client_ip(),
     184                        'ip'     => self::get_unsafe_client_ip(),
    160185                );
    161186
    162187                /*
    163                  * Send the minimal set of necessary arguments, in order to increase the
     188                 * Include the minimal set of necessary arguments, in order to increase the
    164189                 * chances of a cache-hit on the API side.
    165190                 */
    166191                if ( empty( $search ) && isset( $this->user_location['latitude'], $this->user_location['longitude'] ) ) {
    class WP_Community_Events { 
    178203                        }
    179204                }
    180205
    181                 return add_query_arg( $args, $api_url );
     206                // Wrap the args in an array compatible with the second parameter of `wp_remote_get()`.
     207                return array(
     208                        'body' => $args
     209                );
    182210        }
    183211
    184212        /**
    class WP_Community_Events { 
    207235         * @return false|string The anonymized address on success; the given address
    208236         *                      or false on failure.
    209237         */
    210         protected function get_client_ip() {
     238        public static function get_unsafe_client_ip() {
    211239                $client_ip = false;
    212240
    213241                // In order of preference, with the best ones for this purpose first.
    class WP_Community_Events { 
    250278        }
    251279
    252280        /**
     281         * Test if two pairs of latitude/longitude coordinates match each other.
     282         *
     283         * @since 4.8.0
     284         * @access protected
     285         *
     286         * @param array $a The first pair, with indexes 'latitude' and 'longitude'.
     287         * @param array $b The second pair, with indexes 'latitude' and 'longitude'.
     288         * @return bool True if they match, false if they don't.
     289         */
     290        protected function coordinates_match( $a, $b ) {
     291                if ( ! isset( $a['latitude'], $a['longitude'], $b['latitude'], $b['longitude'] ) ) {
     292                        return false;
     293                }
     294
     295                return $a['latitude'] === $b['latitude'] && $a['longitude'] === $b['longitude'];
     296        }
     297
     298        /**
    253299         * Generates a transient key based on user location.
    254300         *
    255301         * This could be reduced to a one-liner in the calling functions, but it's
    class WP_Community_Events { 
    266312        protected function get_events_transient_key( $location ) {
    267313                $key = false;
    268314
    269                 if ( isset( $location['latitude'], $location['longitude'] ) ) {
     315                if ( isset( $location['ip'] ) ) {
     316                        $key = 'community-events-' . md5( $location['ip'] );
     317                } else if ( isset( $location['latitude'], $location['longitude'] ) ) {
    270318                        $key = 'community-events-' . md5( $location['latitude'] . $location['longitude'] );
    271319                }
    272320
  • src/wp-admin/includes/dashboard.php

    diff --git src/wp-admin/includes/dashboard.php src/wp-admin/includes/dashboard.php
    index 922d2760b4..7ebbe5fa41 100644
    function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = ar 
    982982
    983983        $locale = get_locale();
    984984        $cache_key = 'dash_' . md5( $widget_id . '_' . $locale );
     985
     986        /*
     987         * Clear the cached 4.7 w.org feed markup during the upgrade to 4.8.0.
     988         *
     989         * This wasn't done in wp-admin/includes/upgrade.php because that would
     990         * trigger an upgrade screen for users, and it's not worth the UX cost
     991         * for such a small change.
     992         *
     993         * @todo This is a temporary hack for 4.8.0 only, and should be removed in 4.8.1.
     994         */
     995        if ( 'dashboard_primary' === $widget_id && false !== strpos( get_transient( $cache_key ), 'rssSummary' ) ) {
     996                delete_transient( $cache_key );
     997        }
     998
    985999        if ( false !== ( $output = get_transient( $cache_key ) ) ) {
    9861000                echo $output;
    9871001                return true;
    function wp_print_community_events_templates() { 
    12341248
    12351249        <script id="tmpl-community-events-no-upcoming-events" type="text/template">
    12361250                <li class="event-none">
    1237                         <?php printf(
    1238                                 /* translators: 1: the city the user searched for, 2: meetup organization documentation URL */
    1239                                 __( 'There aren&#8217;t any events scheduled near %1$s at the moment. Would you like to <a href="%2$s">organize one</a>?' ),
    1240                                 '{{ data.location.description }}',
    1241                                 __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' )
    1242                         ); ?>
     1251                        <# if ( data.location.description ) { #>
     1252                                <?php printf(
     1253                                        /* translators: 1: the city the user searched for, 2: meetup organization documentation URL */
     1254                                        __( 'There aren&#8217;t any events scheduled near %1$s at the moment. Would you like to <a href="%2$s">organize one</a>?' ),
     1255                                        '{{ data.location.description }}',
     1256                                        __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' )
     1257                                ); ?>
     1258
     1259                        <# } else { #>
     1260                                <?php printf(
     1261                                        /* translators: meetup organization documentation URL. */
     1262                                        __( 'There aren&#8217;t any events scheduled near you at the moment. Would you like to <a href="%s">organize one</a>?' ),
     1263                                        __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' )
     1264                                ); ?>
     1265                        <# } #>
    12431266                </li>
    12441267        </script>
    1245 
    12461268        <?php
    12471269}
    12481270
  • src/wp-admin/includes/upgrade.php

    diff --git src/wp-admin/includes/upgrade.php src/wp-admin/includes/upgrade.php
    index 23a5eee4d7..94ad771761 100644
    function upgrade_all() { 
    565565        if ( $wp_current_db_version < 37965 )
    566566                upgrade_460();
    567567
    568         if ( $wp_current_db_version < 40607 ) {
    569                 upgrade_480();
    570         }
    571 
    572568        maybe_disable_link_manager();
    573569
    574570        maybe_disable_automattic_widgets();
    function upgrade_460() { 
    17371733}
    17381734
    17391735/**
    1740  * Executes changes made in WordPress 4.8.0.
    1741  *
    1742  * @ignore
    1743  * @since 4.8.0
    1744  *
    1745  * @global int $wp_current_db_version Current database version.
    1746  */
    1747 function upgrade_480() {
    1748         global $wp_current_db_version;
    1749 
    1750         if ( $wp_current_db_version < 40607 ) {
    1751                 // This feature plugin was merged for #40702, so the plugin itself is no longer needed
    1752                 deactivate_plugins( array( 'nearby-wp-events/nearby-wordpress-events.php' ), true );
    1753 
    1754                 // The markup stored in this transient changed for #40702
    1755                 delete_transient( 'dash_' . md5( 'dashboard_primary' . '_' . get_locale() ) );
    1756         }
    1757 }
    1758 
    1759 /**
    17601736 * Executes network-level upgrade routines.
    17611737 *
    17621738 * @since 3.0.0
  • src/wp-admin/js/dashboard.js

    diff --git src/wp-admin/js/dashboard.js src/wp-admin/js/dashboard.js
    index 6a9b5033d4..9a91265af6 100644
    jQuery( function( $ ) { 
    369369                         * Determine which templates should be rendered and which elements
    370370                         * should be displayed.
    371371                         */
    372                         if ( templateParams.location ) {
     372                        if ( templateParams.location.ip ) {
     373                                /*
     374                                 * If the API determined the location by geolocating an IP, it will
     375                                 * provide events, but not a specific location.
     376                                 */
     377                                $locationMessage.text( communityEventsData.l10n.attend_event_near_generic );
     378
     379                                if ( templateParams.events.length ) {
     380                                        template = wp.template( 'community-events-event-list' );
     381                                        $results.html( template( templateParams ) );
     382                                } else {
     383                                        template = wp.template( 'community-events-no-upcoming-events' );
     384                                        $results.html( template( templateParams ) );
     385                                }
     386
     387                                elementVisibility['#community-events-location-message'] = true;
     388                                elementVisibility['.community-events-toggle-location']  = true;
     389                                elementVisibility['.community-events-results']          = true;
     390
     391                        } else if ( templateParams.location.description ) {
    373392                                template = wp.template( 'community-events-attend-event-near' );
    374393                                $locationMessage.html( template( templateParams ) );
    375394
    jQuery( function( $ ) { 
    427446                         * location, and displaying the form would unnecessarily clutter the
    428447                         * widget.
    429448                         */
    430                         if ( 'app' === initiatedBy && templateParams.location ) {
     449                        if ( 'app' === initiatedBy && ( templateParams.location.ip || templateParams.location.latitude ) ) {
    431450                                app.toggleLocationForm( 'hide' );
    432451                        } else {
    433452                                app.toggleLocationForm( 'show' );
  • src/wp-includes/script-loader.php

    diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php
    index 5dad398001..ff7d8bb855 100644
    function wp_localize_community_events() { 
    10121012
    10131013        require_once( ABSPATH . 'wp-admin/includes/class-wp-community-events.php' );
    10141014
    1015         $user_id       = get_current_user_id();
    1016         $user_location = get_user_option( 'community-events-location', $user_id );
    1017         $events_client = new WP_Community_Events( $user_id, $user_location );
     1015        $user_id            = get_current_user_id();
     1016        $saved_location     = get_user_option( 'community-events-location', $user_id );
     1017        $saved_ip_address   = isset( $saved_location['ip'] ) ? $saved_location['ip'] : false;
     1018        $current_ip_address = WP_Community_Events::get_unsafe_client_ip();
     1019
     1020        /*
     1021         * If the user's location is based on their IP address, then update their
     1022         * location when their IP address changes. This allows them to see events
     1023         * in their current city when travelling. Otherwise, they would always be
     1024         * shown events in the city where they were when they first loaded the
     1025         * Dashboard, which could have been months or years ago.
     1026         */
     1027        if ( $saved_ip_address && $current_ip_address && $current_ip_address !== $saved_ip_address ) {
     1028                $saved_location['ip'] = $current_ip_address;
     1029                update_user_option( $user_id, 'community-events-location', $saved_location, true );
     1030        }
     1031
     1032        $events_client = new WP_Community_Events( $user_id, $saved_location );
    10181033
    10191034        wp_localize_script( 'dashboard', 'communityEventsData', array(
    10201035                'nonce' => wp_create_nonce( 'community_events' ),
    function wp_localize_community_events() { 
    10231038                'l10n' => array(
    10241039                        'enter_closest_city' => __( 'Enter your closest city to find nearby events.' ),
    10251040                        'error_occurred_please_try_again' => __( 'An error occurred. Please try again.' ),
     1041                        'attend_event_near_generic' => __( 'Attend an upcoming event near you.' ),
    10261042
    10271043                        /*
    10281044                         * These specific examples were chosen to highlight the fact that a