Make WordPress Core

Ticket #40702: 40702-geoip.3.diff

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

Expands on geoip.2 for better IP location handling; adds better handling for searched locations

  • 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..66046031ff 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;
     316
     317        if ( empty( $saved_location['ip'] ) && isset( $events['location']['ip'] ) ) {
     318                $ip_changed = true;
     319        } elseif ( isset( $saved_location['ip'], $events['location']['ip'] ) && $saved_location['ip'] !== $events['location']['ip'] ) {
     320                $ip_changed = true;
     321        }
    315322
    316323        if ( is_wp_error( $events ) ) {
    317324                wp_send_json_error( array(
    318325                        'error' => $events->get_error_message(),
    319326                ) );
    320327        } else {
    321                 if ( isset( $events['location'] ) ) {
    322                         // Store the location network-wide, so the user doesn't have to set it on each site.
     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..ec823d1569 100644
    class WP_Community_Events { 
    128128                                unset( $response_body['ttl'] );
    129129                        }
    130130
     131                        /*
     132                         * The IP in the response is usually the same as the one that was sent
     133                         * in the request, but in some cases it is different. In those cases,
     134                         * it's important to reset it back to the IP from the request.
     135                         *
     136                         * For example, if the IP sent in the request is private (e.g., 192.168.1.100),
     137                         * then the API will ignore that and use the corresponding public IP instead,
     138                         * and the public IP will get returned. If the public IP were saved, though,
     139                         * then get_cached_events() would always return `false`, because the transient
     140                         * would be generated based on the public IP when saving the cache, but generated
     141                         * based on the private IP when retrieving the cache.
     142                         */
     143                        if ( ! empty( $response_body['location']['ip'] ) ) {
     144                                $response_body['location']['ip'] = WP_Community_Events::get_unsafe_client_ip();
     145                        }
     146
     147                        /*
     148                         * The API doesn't return a description for latitude/longitude requests,
     149                         * but the description is already saved in the user location, so that
     150                         * one can be used instead.
     151                         */
     152                        if ( $this->coordinates_match( $this->user_location, $response_body['location'] ) && empty( $response_body['location']['description'] ) ) {
     153                                $response_body['location']['description'] = $this->user_location['description'];
     154                        }
     155
    131156                        $this->cache_events( $response_body, $expiration );
    132157
    133158                        $response_body = $this->trim_events( $response_body );
    class WP_Community_Events { 
    156181                $api_url = 'https://api.wordpress.org/events/1.0/';
    157182                $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                /*
    class WP_Community_Events { 
    207232         * @return false|string The anonymized address on success; the given address
    208233         *                      or false on failure.
    209234         */
    210         protected function get_client_ip() {
     235        public static function get_unsafe_client_ip() {
    211236                $client_ip = false;
    212237
    213238                // In order of preference, with the best ones for this purpose first.
    class WP_Community_Events { 
    250275        }
    251276
    252277        /**
     278         * Test if two pairs of latitude/longitude coordinates match each other.
     279         *
     280         * @since 4.8.0
     281         * @access protected
     282         *
     283         * @param array $a The first pair, with indexes 'latitude' and 'longitude'.
     284         * @param array $b The second pair, with indexes 'latitude' and 'longitude'.
     285         * @return bool True if they match, false if they don't.
     286         */
     287        protected function coordinates_match( $a, $b ) {
     288                if ( ! isset( $a['latitude'], $a['longitude'], $b['latitude'], $b['longitude'] ) ) {
     289                        return false;
     290                }
     291
     292                return $a['latitude'] === $b['latitude'] && $a['longitude'] === $b['longitude'];
     293        }
     294
     295        /**
    253296         * Generates a transient key based on user location.
    254297         *
    255298         * This could be reduced to a one-liner in the calling functions, but it's
    class WP_Community_Events { 
    266309        protected function get_events_transient_key( $location ) {
    267310                $key = false;
    268311
    269                 if ( isset( $location['latitude'], $location['longitude'] ) ) {
     312                if ( isset( $location['ip'] ) ) {
     313                        $key = 'community-events-' . md5( $location['ip'] );
     314                } else if ( isset( $location['latitude'], $location['longitude'] ) ) {
    270315                        $key = 'community-events-' . md5( $location['latitude'] . $location['longitude'] );
    271316                }
    272317
  • src/wp-admin/includes/dashboard.php

    diff --git src/wp-admin/includes/dashboard.php src/wp-admin/includes/dashboard.php
    index 922d2760b4..8c5daef7dd 100644
    function wp_print_community_events_templates() { 
    12341234
    12351235        <script id="tmpl-community-events-no-upcoming-events" type="text/template">
    12361236                <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                         ); ?>
     1237                        <# if ( data.location.description ) { #>
     1238                                <?php printf(
     1239                                        /* translators: 1: the city the user searched for, 2: meetup organization documentation URL */
     1240                                        __( 'There aren&#8217;t any events scheduled near %1$s at the moment. Would you like to <a href="%2$s">organize one</a>?' ),
     1241                                        '{{ data.location.description }}',
     1242                                        __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' )
     1243                                ); ?>
     1244
     1245                        <# } else { #>
     1246                                <?php printf(
     1247                                        /* translators: meetup organization documentation URL. */
     1248                                        __( 'There aren&#8217;t any events scheduled near you at the moment. Would you like to <a href="%s">organize one</a>?' ),
     1249                                        __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' )
     1250                                ); ?>
     1251                        <# } #>
    12431252                </li>
    12441253        </script>
    1245 
    12461254        <?php
    12471255}
    12481256
  • 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..29cf10f5f7 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 !== $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