WordPress.org

Make WordPress Core

Changeset 40607


Ignore:
Timestamp:
05/10/17 20:03:01 (2 months ago)
Author:
azaozz
Message:

Dashboard: Update the existing WordPress News dashboard widget to also include upcoming meetup events and WordCamps near the current user’s location.

Props @afercia, @andreamiddleton, @azaozz, @camikaos, @coreymckrill, @chanthaboune, @courtneypk, @dd32, @iandunn, @iseulde, @mapk, @mayukojpn, @melchoyce, @nao, @obenland, @pento, @samuelsidler, @stephdau, @tellyworth.
See #40702.

Location:
trunk
Files:
2 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/admin-ajax.php

    r38159 r40607  
    6565    'press-this-add-category', 'crop-image', 'generate-password', 'save-wporg-username', 'delete-plugin', 
    6666    'search-plugins', 'search-install-plugins', 'activate-plugin', 'update-theme', 'delete-theme', 
    67     'install-theme', 'get-post-thumbnail-html', 
     67    'install-theme', 'get-post-thumbnail-html', 'get-community-events', 
    6868); 
    6969 
  • trunk/src/wp-admin/css/dashboard.css

    r40556 r40607  
    302302} 
    303303 
     304/* Dashboard WordPress events */ 
     305 
     306.community-events-errors { 
     307    margin: 0; 
     308} 
     309 
     310.community-events-loading { 
     311    padding: 10px 12px 8px; 
     312} 
     313 
     314.community-events { 
     315    margin-bottom: 6px; 
     316    padding: 0 12px; 
     317} 
     318 
     319.community-events .spinner { 
     320    float: none; 
     321    margin: 0; 
     322    padding-bottom: 3px; 
     323} 
     324 
     325.community-events-errors[aria-hidden="true"], 
     326.community-events-errors *[aria-hidden="true"], 
     327.community-events-loading[aria-hidden="true"], 
     328.community-events[aria-hidden="true"], 
     329.community-events *[aria-hidden="true"] { 
     330    display: none; 
     331} 
     332 
     333.community-events .activity-block:first-child, 
     334.community-events h2 { 
     335    padding-top: 12px; 
     336    padding-bottom: 10px; 
     337} 
     338 
     339.community-events-form { 
     340    margin: 15px 0 5px; 
     341} 
     342 
     343.community-events-form .regular-text { 
     344    width: 40%; 
     345    height: 28px; 
     346} 
     347 
     348.community-events li.event-none { 
     349    border-left: 4px solid #0070AE; 
     350} 
     351 
     352.community-events-form label { 
     353    display: inline-block; 
     354    padding-bottom: 3px; 
     355} 
     356 
     357.community-events .activity-block > p { 
     358    margin-bottom: 0; 
     359    display: inline; 
     360} 
     361 
     362#community-events-submit { 
     363    margin-left: 2px; 
     364} 
     365 
     366.community-events .button-link:hover, 
     367.community-events .button-link:active { 
     368    color: #00a0d2; 
     369} 
     370 
     371.community-events-cancel.button.button-link { 
     372    color: #0073aa; 
     373    text-decoration: underline; 
     374    margin-left: 2px; 
     375} 
     376 
     377.community-events ul { 
     378    background-color: #fafafa; 
     379    padding-left: 0; 
     380    padding-right: 0; 
     381    padding-bottom: 0; 
     382} 
     383 
     384.community-events li { 
     385    margin: 0; 
     386    padding: 8px 12px; 
     387    color: #72777c; 
     388} 
     389.community-events li:first-child { 
     390    border-top: 1px solid #eee; 
     391} 
     392 
     393.community-events li ~ li { 
     394    border-top: 1px solid #eee; 
     395} 
     396 
     397.community-events .activity-block.last { 
     398    border-bottom: 1px solid #eee; 
     399    padding-top: 0; 
     400    margin-top: -1px; 
     401} 
     402 
     403.community-events .event-info { 
     404    display: block; 
     405} 
     406 
     407.event-icon { 
     408    height: 18px; 
     409    padding-right: 10px; 
     410    width: 18px; 
     411    display: none; /* Hide on smaller screens */ 
     412} 
     413 
     414.event-icon:before { 
     415    color: #82878C; 
     416    font-size: 18px; 
     417} 
     418.event-meetup .event-icon:before { 
     419    content: "\f484"; 
     420} 
     421.event-wordcamp .event-icon:before { 
     422    content: "\f486"; 
     423} 
     424 
     425.community-events .event-title { 
     426    font-weight: 600; 
     427    display: block; 
     428} 
     429 
     430.community-events .event-date, 
     431.community-events .event-time { 
     432    display: block; 
     433} 
     434 
     435.community-events-footer { 
     436    margin-top: 0; 
     437    margin-bottom: 0; 
     438    padding: 12px; 
     439    border-top: 1px solid #eee; 
     440    color: #ddd; 
     441} 
     442 
    304443/* Dashboard WordPress news */ 
    305444 
     
    334473 
    335474#dashboard_primary .rss-widget { 
    336     border-bottom: 1px solid #eee; 
    337475    font-size: 13px; 
    338     padding: 8px 12px 10px; 
     476    padding: 0 12px 0; 
    339477} 
    340478 
     
    358496 
    359497#dashboard_primary .rss-widget ul li { 
    360     margin-bottom: 8px; 
     498    padding: 4px 0; 
     499    margin: 0; 
    361500} 
    362501 
     
    8751014 
    8761015a.rsswidget { 
    877     font-size: 14px; 
     1016    font-size: 13px; 
    8781017    font-weight: 600; 
    879     line-height: 1.7em; 
     1018    line-height: 1.4em; 
    8801019} 
    8811020 
     
    10871226        width: 30px; 
    10881227        margin: 4px 10px 5px 0; 
     1228    } 
     1229 
     1230    .community-events-toggle-location { 
     1231        height: 38px; 
     1232    } 
     1233 
     1234    .community-events-form .regular-text { 
     1235        height: 31px; 
    10891236    } 
    10901237} 
     
    11111258    } 
    11121259} 
     1260 
     1261@media screen and (min-width: 355px) { 
     1262    .community-events .event-info { 
     1263        display: table-row; 
     1264        float: left; 
     1265        max-width: 59%; 
     1266    } 
     1267 
     1268    .event-icon, 
     1269    .event-icon[aria-hidden="true"] { 
     1270        display: table-cell; 
     1271    } 
     1272 
     1273    .event-info-inner { 
     1274        display: table-cell; 
     1275    } 
     1276 
     1277    .community-events .event-date-time { 
     1278        float: right; 
     1279        max-width: 39%; 
     1280    } 
     1281 
     1282    .community-events .event-date, 
     1283    .community-events .event-time { 
     1284        text-align: right; 
     1285    } 
     1286} 
  • trunk/src/wp-admin/includes/ajax-actions.php

    r40476 r40607  
    295295 
    296296    wp_die( wp_json_encode( $return ) ); 
     297} 
     298 
     299/** 
     300 * Handles AJAX requests for community events 
     301 * 
     302 * @since 4.8.0 
     303 */ 
     304function wp_ajax_get_community_events() { 
     305    require_once( ABSPATH . 'wp-admin/includes/class-wp-community-events.php' ); 
     306 
     307    check_ajax_referer( 'community_events' ); 
     308 
     309    $search         = isset( $_POST['location'] ) ? wp_unslash( $_POST['location'] ) : ''; 
     310    $timezone       = isset( $_POST['timezone'] ) ? wp_unslash( $_POST['timezone'] ) : ''; 
     311    $user_id        = get_current_user_id(); 
     312    $saved_location = get_user_option( 'community-events-location', $user_id ); 
     313    $events_client  = new WP_Community_Events( $user_id, $saved_location ); 
     314    $events         = $events_client->get_events( $search, $timezone ); 
     315 
     316    if ( is_wp_error( $events ) ) { 
     317        wp_send_json_error( array( 
     318            'error' => $events->get_error_message(), 
     319        ) ); 
     320    } else { 
     321        if ( isset( $events['location'] ) ) { 
     322            // Send only the data that the client will use. 
     323            $events['location'] = $events['location']['description']; 
     324 
     325            // Store the location network-wide, so the user doesn't have to set it on each site. 
     326            update_user_option( $user_id, 'community-events-location', $events['location'], true ); 
     327        } 
     328 
     329        wp_send_json_success( $events ); 
     330    } 
    297331} 
    298332 
  • trunk/src/wp-admin/includes/dashboard.php

    r40556 r40607  
    5353    } 
    5454 
    55     // WordPress News 
    56     wp_add_dashboard_widget( 'dashboard_primary', __( 'WordPress News' ), 'wp_dashboard_primary' ); 
     55    // WordPress Events and News 
     56    wp_add_dashboard_widget( 'dashboard_primary', __( 'WordPress Events and News' ), 'wp_dashboard_events_news' ); 
    5757 
    5858    if ( is_network_admin() ) { 
     
    128128    /** This action is documented in wp-admin/edit-form-advanced.php */ 
    129129    do_action( 'do_meta_boxes', $screen->id, 'side', '' ); 
     130} 
     131 
     132/** 
     133 * Gets the community events data that needs to be passed to dashboard.js. 
     134 * 
     135 * @since 4.8.0 
     136 * 
     137 * @return array The script data. 
     138 */ 
     139function wp_get_community_events_script_data() { 
     140    require_once( ABSPATH . 'wp-admin/includes/class-wp-community-events.php' ); 
     141 
     142    $user_id       = get_current_user_id(); 
     143    $user_location = get_user_option( 'community-events-location', $user_id ); 
     144    $events_client = new WP_Community_Events( $user_id, $user_location ); 
     145 
     146    $script_data = array( 
     147        'nonce' => wp_create_nonce( 'community_events' ), 
     148        'cache' => $events_client->get_cached_events(), 
     149 
     150        'l10n' => array( 
     151            'enter_closest_city' => __( 'Enter your closest city to find nearby events.' ), 
     152            'error_occurred_please_try_again' => __( 'An error occured. Please try again.' ), 
     153 
     154            /* 
     155             * These specific examples were chosen to highlight the fact that a 
     156             * state is not needed, even for cities whose name is not unique. 
     157             * It would be too cumbersome to include that in the instructions 
     158             * to the user, so it's left as an implication. 
     159             */ 
     160            /* translators: %s is the name of the city we couldn't locate. Replace the examples with cities in your locale, but test that they match the expected location before including them. Use endonyms (native locale names) whenever possible. */ 
     161            'could_not_locate_city' => __( "We couldn't locate %s. Please try another nearby city. For example: Kansas City; Springfield; Portland." ), 
     162 
     163            // This one is only used with wp.a11y.speak(), so it can/should be more brief. 
     164            /* translators: %s is the name of a city. */ 
     165            'city_updated' => __( 'City updated. Listing events near %s.' ), 
     166        ) 
     167    ); 
     168 
     169    return $script_data; 
    130170} 
    131171 
     
    10701110} 
    10711111 
     1112 
     1113/** 
     1114 * Renders the Events and News dashboard widget. 
     1115 * 
     1116 * @since 4.8.0 
     1117 */ 
     1118function wp_dashboard_events_news() { 
     1119    wp_print_community_events_markup(); 
     1120 
     1121    ?> 
     1122 
     1123    <div class="wordpress-news hide-if-no-js"> 
     1124        <?php wp_dashboard_primary(); ?> 
     1125    </div> 
     1126 
     1127    <p class="community-events-footer"> 
     1128        <a href="https://make.wordpress.org/community/meetups-landing-page" target="_blank"> 
     1129            <?php _e( 'Meetups' ); ?> <span class="dashicons dashicons-external"></span> 
     1130        </a> 
     1131 
     1132        | 
     1133 
     1134        <a href="https://central.wordcamp.org/schedule/" target="_blank"> 
     1135            <?php _e( 'WordCamps' ); ?> <span class="dashicons dashicons-external"></span> 
     1136        </a> 
     1137 
     1138        | 
     1139 
     1140        <?php // translators: If a Rosetta site exists (e.g. https://es.wordpress.org/news/), then use that. Otherwise, leave untranslated. ?> 
     1141        <a href="<?php _e( 'https://wordpress.org/news/' ); ?>" target="_blank"> 
     1142            <?php _e( 'News' ); ?> <span class="dashicons dashicons-external"></span> 
     1143        </a> 
     1144    </p> 
     1145 
     1146    <?php 
     1147} 
     1148 
     1149/** 
     1150 * Prints the markup for the Community Events section of the Events and News Dashboard widget. 
     1151 * 
     1152 * @since 4.8.0 
     1153 */ 
     1154function wp_print_community_events_markup() { 
     1155    $script_data = wp_get_community_events_script_data(); 
     1156 
     1157    ?> 
     1158 
     1159    <div class="community-events-errors notice notice-error inline hide-if-js"> 
     1160        <p class="hide-if-js"> 
     1161            <?php _e( 'This widget requires JavaScript.'); ?> 
     1162        </p> 
     1163 
     1164        <p class="community-events-error-occurred" aria-hidden="true"> 
     1165            <?php echo $script_data['l10n']['error_occurred_please_try_again']; ?> 
     1166        </p> 
     1167 
     1168        <p class="community-events-could-not-locate" aria-hidden="true"></p> 
     1169    </div> 
     1170 
     1171    <div class="community-events-loading hide-if-no-js"> 
     1172        <?php _e( 'Loading&hellip;'); ?> 
     1173    </div> 
     1174 
     1175    <?php 
     1176    /* 
     1177     * Hide the main element when the page first loads, because the content 
     1178     * won't be ready until wp.communityEvents.renderEventsTemplate() has run. 
     1179     */ 
     1180    ?> 
     1181    <div id="community-events" class="community-events" aria-hidden="true"> 
     1182        <div class="activity-block"> 
     1183            <p> 
     1184                <span id="community-events-location-message"></span> 
     1185 
     1186                <button class="button-link community-events-toggle-location" aria-label="<?php _e( 'Edit city'); ?>" aria-expanded="false"> 
     1187                    <span class="dashicons dashicons-edit"></span> 
     1188                </button> 
     1189            </p> 
     1190 
     1191            <form class="community-events-form" aria-hidden="true" action="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>" method="post"> 
     1192                <label for="community-events-location"> 
     1193                    <?php _e( 'City:' ); ?> 
     1194                </label> 
     1195                <?php /* translators: Replace with the name of a city in your locale that shows events. Use only the city name itself, without any region or country. Use the endonym instead of the English name. */ ?> 
     1196                <input id="community-events-location" class="regular-text" type="text" name="community-events-location" placeholder="<?php _e( 'Cincinnati' ); ?>" /> 
     1197 
     1198                <?php submit_button( __( 'Submit' ), 'secondary', 'community-events-submit', false ); ?> 
     1199 
     1200                <button class="community-events-cancel button button-link" type="button" aria-expanded="false"> 
     1201                    <?php _e( 'Cancel' ); ?> 
     1202                </button> 
     1203 
     1204                <span class="spinner"></span> 
     1205            </form> 
     1206        </div> 
     1207 
     1208        <ul class="community-events-results activity-block last"></ul> 
     1209    </div> 
     1210 
     1211    <?php 
     1212} 
     1213 
     1214/** 
     1215 * Renders the events templates for the Event and News widget. 
     1216 * 
     1217 * @since 4.8.0 
     1218 */ 
     1219function wp_print_community_events_templates() { 
     1220    $script_data = wp_get_community_events_script_data(); 
     1221 
     1222    ?> 
     1223 
     1224    <script id="tmpl-community-events-attend-event-near" type="text/template"> 
     1225        <?php printf( 
     1226            /* translators: %s is a placeholder for the name of a city. */ 
     1227            __( 'Attend an upcoming event near %s.' ), 
     1228            '<strong>{{ data.location }}</strong>' 
     1229        ); ?> 
     1230    </script> 
     1231 
     1232    <script id="tmpl-community-events-could-not-locate" type="text/template"> 
     1233        <?php printf( 
     1234            $script_data['l10n']['could_not_locate_city'], 
     1235            '<em>{{data.unknownCity}}</em>' 
     1236        ); ?> 
     1237    </script> 
     1238 
     1239    <script id="tmpl-community-events-event-list" type="text/template"> 
     1240        <# _.each( data.events, function( event ) { #> 
     1241            <li class="event event-{{ event.type }} wp-clearfix"> 
     1242                <div class="event-info"> 
     1243                    <div class="dashicons event-icon" aria-hidden="true"></div> 
     1244                    <div class="event-info-inner"> 
     1245                        <a class="event-title" href="{{ event.url }}">{{ event.title }}</a> 
     1246                        <span class="event-city">{{ event.location.location }}</span> 
     1247                    </div> 
     1248                </div> 
     1249 
     1250                <div class="event-date-time"> 
     1251                    <span class="event-date">{{ event.formatted_date }}</span> 
     1252                    <# if ( 'meetup' === event.type ) { #> 
     1253                        <span class="event-time">{{ event.formatted_time }}</span> 
     1254                    <# } #> 
     1255                </div> 
     1256            </li> 
     1257        <# } ) #> 
     1258    </script> 
     1259 
     1260    <script id="tmpl-community-events-no-upcoming-events" type="text/template"> 
     1261        <li class="event-none"> 
     1262            <?php printf( 
     1263                /* translators: 1: the city the user searched for, 2: meetup organization documentation URL */ 
     1264                __( 'There aren&#8217;t any events scheduled near %1$s at the moment. Would you like to <a href="%2$s">organize one</a>?' ), 
     1265                '{{data.location}}', 
     1266                __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' ) 
     1267            ); ?> 
     1268        </li> 
     1269    </script> 
     1270 
     1271    <?php 
     1272} 
     1273 
    10721274/** 
    10731275 * WordPress News dashboard widget. 
    10741276 * 
    10751277 * @since 2.7.0 
     1278 * @since 4.8.0 Removed popular plugins feed. 
    10761279 */ 
    10771280function wp_dashboard_primary() { 
     
    11061309            'title'        => apply_filters( 'dashboard_primary_title', __( 'WordPress Blog' ) ), 
    11071310            'items'        => 1, 
    1108             'show_summary' => 1, 
     1311            'show_summary' => 0, 
    11091312            'show_author'  => 0, 
    1110             'show_date'    => 1, 
     1313            'show_date'    => 0, 
    11111314        ), 
    11121315        'planet' => array( 
     
    11531356    ); 
    11541357 
    1155     if ( ( ! wp_disallow_file_mods( 'dashboard_widget' ) ) && ( ! is_multisite() && is_blog_admin() && current_user_can( 'install_plugins' ) ) || ( is_network_admin() && current_user_can( 'manage_network_plugins' ) && current_user_can( 'install_plugins' ) ) ) { 
    1156         $feeds['plugins'] = array( 
    1157             'link'         => '', 
    1158             'url'          => array( 
    1159                 'popular' => 'http://wordpress.org/plugins/rss/browse/popular/', 
    1160             ), 
    1161             'title'        => '', 
    1162             'items'        => 1, 
    1163             'show_summary' => 0, 
    1164             'show_author'  => 0, 
    1165             'show_date'    => 0, 
    1166         ); 
    1167     } 
    1168  
    11691358    wp_dashboard_cached_rss_widget( 'dashboard_primary', 'wp_dashboard_primary_output', $feeds ); 
    11701359} 
     
    11741363 * 
    11751364 * @since 3.8.0 
     1365 * @since 4.8.0 Removed popular plugins feed. 
    11761366 * 
    11771367 * @param string $widget_id Widget ID. 
     
    11821372        $args['type'] = $type; 
    11831373        echo '<div class="rss-widget">'; 
    1184         if ( $type === 'plugins' ) { 
    1185             wp_dashboard_plugins_output( $args['url'], $args ); 
    1186         } else { 
    11871374            wp_widget_rss_output( $args['url'], $args ); 
    1188         } 
    11891375        echo "</div>"; 
    11901376    } 
    1191 } 
    1192  
    1193 /** 
    1194  * Display plugins text for the WordPress news widget. 
    1195  * 
    1196  * @since 2.5.0 
    1197  * 
    1198  * @param string $rss  The RSS feed URL. 
    1199  * @param array  $args Array of arguments for this RSS feed. 
    1200  */ 
    1201 function wp_dashboard_plugins_output( $rss, $args = array() ) { 
    1202     // Plugin feeds plus link to install them 
    1203     $popular = fetch_feed( $args['url']['popular'] ); 
    1204  
    1205     if ( false === $plugin_slugs = get_transient( 'plugin_slugs' ) ) { 
    1206         $plugin_slugs = array_keys( get_plugins() ); 
    1207         set_transient( 'plugin_slugs', $plugin_slugs, DAY_IN_SECONDS ); 
    1208     } 
    1209  
    1210     echo '<ul>'; 
    1211  
    1212     foreach ( array( $popular ) as $feed ) { 
    1213         if ( is_wp_error( $feed ) || ! $feed->get_item_quantity() ) 
    1214             continue; 
    1215  
    1216         $items = $feed->get_items(0, 5); 
    1217  
    1218         // Pick a random, non-installed plugin 
    1219         while ( true ) { 
    1220             // Abort this foreach loop iteration if there's no plugins left of this type 
    1221             if ( 0 == count($items) ) 
    1222                 continue 2; 
    1223  
    1224             $item_key = array_rand($items); 
    1225             $item = $items[$item_key]; 
    1226  
    1227             list($link, $frag) = explode( '#', $item->get_link() ); 
    1228  
    1229             $link = esc_url($link); 
    1230             if ( preg_match( '|/([^/]+?)/?$|', $link, $matches ) ) 
    1231                 $slug = $matches[1]; 
    1232             else { 
    1233                 unset( $items[$item_key] ); 
    1234                 continue; 
    1235             } 
    1236  
    1237             // Is this random plugin's slug already installed? If so, try again. 
    1238             reset( $plugin_slugs ); 
    1239             foreach ( $plugin_slugs as $plugin_slug ) { 
    1240                 if ( $slug == substr( $plugin_slug, 0, strlen( $slug ) ) ) { 
    1241                     unset( $items[$item_key] ); 
    1242                     continue 2; 
    1243                 } 
    1244             } 
    1245  
    1246             // If we get to this point, then the random plugin isn't installed and we can stop the while(). 
    1247             break; 
    1248         } 
    1249  
    1250         // Eliminate some common badly formed plugin descriptions 
    1251         while ( ( null !== $item_key = array_rand($items) ) && false !== strpos( $items[$item_key]->get_description(), 'Plugin Name:' ) ) 
    1252             unset($items[$item_key]); 
    1253  
    1254         if ( !isset($items[$item_key]) ) 
    1255             continue; 
    1256  
    1257         $raw_title = $item->get_title(); 
    1258  
    1259         $ilink = wp_nonce_url('plugin-install.php?tab=plugin-information&plugin=' . $slug, 'install-plugin_' . $slug) . '&amp;TB_iframe=true&amp;width=600&amp;height=800'; 
    1260         echo '<li class="dashboard-news-plugin"><span>' . __( 'Popular Plugin' ) . ':</span> ' . esc_html( $raw_title ) . 
    1261             '&nbsp;<a href="' . $ilink . '" class="thickbox open-plugin-details-modal" aria-label="' . 
    1262             /* translators: %s: plugin name */ 
    1263             esc_attr( sprintf( __( 'Install %s' ), $raw_title ) ) . '">(' . __( 'Install' ) . ')</a></li>'; 
    1264  
    1265         $feed->__destruct(); 
    1266         unset( $feed ); 
    1267     } 
    1268  
    1269     echo '</ul>'; 
    12701377} 
    12711378 
  • trunk/src/wp-admin/includes/deprecated.php

    r38470 r40607  
    12961296 
    12971297/** 
     1298 * Display plugins text for the WordPress news widget. 
     1299 * 
     1300 * @since 2.5.0 
     1301 * @deprecated 4.8.0 
     1302 * 
     1303 * @param string $rss  The RSS feed URL. 
     1304 * @param array  $args Array of arguments for this RSS feed. 
     1305 */ 
     1306function wp_dashboard_plugins_output( $rss, $args = array() ) { 
     1307    _deprecated_function( __FUNCTION__, '4.8.0' ); 
     1308 
     1309    // Plugin feeds plus link to install them 
     1310    $popular = fetch_feed( $args['url']['popular'] ); 
     1311 
     1312    if ( false === $plugin_slugs = get_transient( 'plugin_slugs' ) ) { 
     1313        $plugin_slugs = array_keys( get_plugins() ); 
     1314        set_transient( 'plugin_slugs', $plugin_slugs, DAY_IN_SECONDS ); 
     1315    } 
     1316 
     1317    echo '<ul>'; 
     1318 
     1319    foreach ( array( $popular ) as $feed ) { 
     1320        if ( is_wp_error( $feed ) || ! $feed->get_item_quantity() ) 
     1321            continue; 
     1322 
     1323        $items = $feed->get_items(0, 5); 
     1324 
     1325        // Pick a random, non-installed plugin 
     1326        while ( true ) { 
     1327            // Abort this foreach loop iteration if there's no plugins left of this type 
     1328            if ( 0 == count($items) ) 
     1329                continue 2; 
     1330 
     1331            $item_key = array_rand($items); 
     1332            $item = $items[$item_key]; 
     1333 
     1334            list($link, $frag) = explode( '#', $item->get_link() ); 
     1335 
     1336            $link = esc_url($link); 
     1337            if ( preg_match( '|/([^/]+?)/?$|', $link, $matches ) ) 
     1338                $slug = $matches[1]; 
     1339            else { 
     1340                unset( $items[$item_key] ); 
     1341                continue; 
     1342            } 
     1343 
     1344            // Is this random plugin's slug already installed? If so, try again. 
     1345            reset( $plugin_slugs ); 
     1346            foreach ( $plugin_slugs as $plugin_slug ) { 
     1347                if ( $slug == substr( $plugin_slug, 0, strlen( $slug ) ) ) { 
     1348                    unset( $items[$item_key] ); 
     1349                    continue 2; 
     1350                } 
     1351            } 
     1352 
     1353            // If we get to this point, then the random plugin isn't installed and we can stop the while(). 
     1354            break; 
     1355        } 
     1356 
     1357        // Eliminate some common badly formed plugin descriptions 
     1358        while ( ( null !== $item_key = array_rand($items) ) && false !== strpos( $items[$item_key]->get_description(), 'Plugin Name:' ) ) 
     1359            unset($items[$item_key]); 
     1360 
     1361        if ( !isset($items[$item_key]) ) 
     1362            continue; 
     1363 
     1364        $raw_title = $item->get_title(); 
     1365 
     1366        $ilink = wp_nonce_url('plugin-install.php?tab=plugin-information&plugin=' . $slug, 'install-plugin_' . $slug) . '&amp;TB_iframe=true&amp;width=600&amp;height=800'; 
     1367        echo '<li class="dashboard-news-plugin"><span>' . __( 'Popular Plugin' ) . ':</span> ' . esc_html( $raw_title ) . 
     1368            '&nbsp;<a href="' . $ilink . '" class="thickbox open-plugin-details-modal" aria-label="' . 
     1369            /* translators: %s: plugin name */ 
     1370            esc_attr( sprintf( __( 'Install %s' ), $raw_title ) ) . '">(' . __( 'Install' ) . ')</a></li>'; 
     1371 
     1372        $feed->__destruct(); 
     1373        unset( $feed ); 
     1374    } 
     1375 
     1376    echo '</ul>'; 
     1377} 
     1378 
     1379/** 
    12981380 * This was once used to move child posts to a new parent. 
    12991381 * 
  • trunk/src/wp-admin/includes/upgrade.php

    r40296 r40607  
    565565    if ( $wp_current_db_version < 37965 ) 
    566566        upgrade_460(); 
     567 
     568    if ( $wp_current_db_version < 40500 ) { //todo update to commit for #40702 
     569        upgrade_480(); 
     570    } 
    567571 
    568572    maybe_disable_link_manager(); 
     
    17341738 
    17351739/** 
     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 */ 
     1747function upgrade_480() { 
     1748    global $wp_current_db_version; 
     1749 
     1750    if ( $wp_current_db_version < 40500 ) { // todo update to commit for #40702 
     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/** 
    17361760 * Executes network-level upgrade routines. 
    17371761 * 
  • trunk/src/wp-admin/index.php

    r38899 r40607  
    1616 
    1717wp_enqueue_script( 'dashboard' ); 
     18wp_localize_script( 'dashboard', 'communityEventsData', wp_get_community_events_script_data() ); 
     19 
    1820if ( current_user_can( 'edit_theme_options' ) ) 
    1921    wp_enqueue_script( 'customize-loader' ); 
     
    139141 
    140142<?php 
     143wp_print_community_events_templates(); 
     144 
    141145require( ABSPATH . 'wp-admin/admin-footer.php' ); 
  • trunk/src/wp-admin/js/dashboard.js

    r34977 r40607  
    11/* global pagenow, ajaxurl, postboxes, wpActiveEditor:true */ 
    22var ajaxWidgets, ajaxPopulateWidgets, quickPressLoad; 
     3window.wp = window.wp || {}; 
    34 
    45jQuery(document).ready( function($) { 
     
    188189 
    189190} ); 
     191 
     192jQuery( function( $ ) { 
     193    'use strict'; 
     194     
     195    var communityEventsData = window.communityEventsData || {}; 
     196 
     197    var app = window.wp.communityEvents = { 
     198        initialized: false, 
     199        model: null, 
     200 
     201        /** 
     202         * Initializes the wp.communityEvents object. 
     203         * 
     204         * @since 4.8.0 
     205         */ 
     206        init: function() { 
     207            if ( app.initialized ) { 
     208                return; 
     209            } 
     210 
     211            var $container = $( '#community-events' ); 
     212 
     213            /* 
     214             * When JavaScript is disabled, the errors container is shown, so 
     215             * that "This widget requires Javascript" message can be seen. 
     216             * 
     217             * When JS is enabled, the container is hidden at first, and then 
     218             * revealed during the template rendering, if there actually are 
     219             * errors to show. 
     220             * 
     221             * The display indicator switches from `hide-if-js` to `aria-hidden` 
     222             * here in order to maintain consistency with all the other fields 
     223             * that key off of `aria-hidden` to determine their visibility. 
     224             * `aria-hidden` can't be used initially, because there would be no 
     225             * way to set it to false when JavaScript is disabled, which would 
     226             * prevent people from seeing the "This widget requires JavaScript" 
     227             * message. 
     228             */ 
     229            $( '.community-events-errors' ) 
     230                .attr( 'aria-hidden', true ) 
     231                .removeClass( 'hide-if-js' ); 
     232 
     233            $container.on( 'click', '.community-events-toggle-location, .community-events-cancel', app.toggleLocationForm ); 
     234 
     235            $container.on( 'submit', '.community-events-form', function( event ) { 
     236                event.preventDefault(); 
     237 
     238                app.getEvents( { 
     239                    location: $( '#community-events-location' ).val() 
     240                }); 
     241            }); 
     242 
     243            if ( communityEventsData && communityEventsData.cache && communityEventsData.cache.location && communityEventsData.cache.events ) { 
     244                app.renderEventsTemplate( communityEventsData.cache, 'app' ); 
     245            } else { 
     246                app.getEvents(); 
     247            } 
     248 
     249            app.initialized = true; 
     250        }, 
     251 
     252        /** 
     253         * Toggles the visibility of the Edit Location form. 
     254         * 
     255         * @since 4.8.0 
     256         * 
     257         * @param {event|string} action 'show' or 'hide' to specify a state; 
     258         *                              Or an event object to flip between states 
     259         */ 
     260        toggleLocationForm: function( action ) { 
     261            var $toggleButton = $( '.community-events-toggle-location' ), 
     262                $cancelButton = $( '.community-events-cancel' ), 
     263                $form         = $( '.community-events-form' ); 
     264 
     265            if ( 'object' === typeof action ) { 
     266                // Strict comparison doesn't work in this case. 
     267                action = 'true' == $toggleButton.attr( 'aria-expanded' ) ? 'hide' : 'show'; 
     268            } 
     269 
     270            if ( 'hide' === action ) { 
     271                $toggleButton.attr( 'aria-expanded', false ); 
     272                $cancelButton.attr( 'aria-expanded', false ); 
     273                $form.attr( 'aria-hidden', true ); 
     274            } else { 
     275                $toggleButton.attr( 'aria-expanded', true ); 
     276                $cancelButton.attr( 'aria-expanded', true ); 
     277                $form.attr( 'aria-hidden', false ); 
     278            } 
     279        }, 
     280 
     281        /** 
     282         * Sends REST API requests to fetch events for the widget. 
     283         * 
     284         * @since 4.8.0 
     285         * 
     286         * @param {object} requestParams 
     287         */ 
     288        getEvents: function( requestParams ) { 
     289            var initiatedBy, 
     290                app = this, 
     291                $spinner = $( '.community-events-form' ).children( '.spinner' ); 
     292 
     293            requestParams          = requestParams || {}; 
     294            requestParams._wpnonce = communityEventsData.nonce; 
     295            requestParams.timezone = window.Intl ? window.Intl.DateTimeFormat().resolvedOptions().timeZone : ''; 
     296 
     297            initiatedBy = requestParams.location ? 'user' : 'app'; 
     298 
     299            $spinner.addClass( 'is-active' ); 
     300 
     301            wp.ajax.post( 'get-community-events', requestParams ) 
     302                .always( function() { 
     303                    $spinner.removeClass( 'is-active' ); 
     304                }) 
     305 
     306                .done( function( response ) { 
     307                    if ( 'no_location_available' === response.error ) { 
     308                        if ( requestParams.location ) { 
     309                            response.unknownCity = requestParams.location; 
     310                        } else { 
     311                            /* 
     312                             * No location was passed, which means that this was an automatic query 
     313                             * based on IP, locale, and timezone. Since the user didn't initiate it, 
     314                             * it should fail silently. Otherwise, the error could confuse and/or 
     315                             * annoy them. 
     316                             */ 
     317 
     318                            delete response.error; 
     319                        } 
     320                    } 
     321                    app.renderEventsTemplate( response, initiatedBy ); 
     322                }) 
     323 
     324                .fail( function() { 
     325                    app.renderEventsTemplate( { 
     326                        'location' : false, 
     327                        'error'    : true 
     328                    }, initiatedBy ); 
     329                }); 
     330        }, 
     331 
     332        /** 
     333         * Renders the template for the Events section of the Events & News widget. 
     334         * 
     335         * @since 4.8.0 
     336         * 
     337         * @param {Object} templateParams The various parameters that will get passed to wp.template 
     338         * @param {string} initiatedBy    'user' to indicate that this was triggered manually by the user; 
     339         *                                'app' to indicate it was triggered automatically by the app itself. 
     340         */ 
     341        renderEventsTemplate: function( templateParams, initiatedBy ) { 
     342            var template, 
     343                elementVisibility, 
     344                l10nPlaceholder  = /%(?:\d\$)?s/g, // Match `%s`, `%1$s`, `%2$s`, etc. 
     345                $locationMessage = $( '#community-events-location-message' ), 
     346                $results         = $( '.community-events-results' ); 
     347 
     348            /* 
     349             * Hide all toggleable elements by default, to keep the logic simple. 
     350             * Otherwise, each block below would have to turn hide everything that 
     351             * could have been shown at an earlier point. 
     352             * 
     353             * The exception to that is that the .community-events container. It's hidden 
     354             * when the page is first loaded, because the content isn't ready yet, 
     355             * but once we've reached this point, it should always be shown. 
     356             */ 
     357            elementVisibility = { 
     358                '.community-events'                  : true, 
     359                '.community-events-loading'          : false, 
     360                '.community-events-errors'           : false, 
     361                '.community-events-error-occurred'   : false, 
     362                '.community-events-could-not-locate' : false, 
     363                '#community-events-location-message' : false, 
     364                '.community-events-toggle-location'  : false, 
     365                '.community-events-results'          : false 
     366            }; 
     367 
     368            /* 
     369             * Determine which templates should be rendered and which elements 
     370             * should be displayed. 
     371             */ 
     372            if ( templateParams.location ) { 
     373                template = wp.template( 'community-events-attend-event-near' ); 
     374                $locationMessage.html( template( templateParams ) ); 
     375 
     376                if ( templateParams.events.length ) { 
     377                    template = wp.template( 'community-events-event-list' ); 
     378                    $results.html( template( templateParams ) ); 
     379                } else { 
     380                    template = wp.template( 'community-events-no-upcoming-events' ); 
     381                    $results.html( template( templateParams ) ); 
     382                } 
     383                wp.a11y.speak( communityEventsData.l10n.city_updated.replace( l10nPlaceholder, templateParams.location ) ); 
     384 
     385                elementVisibility['#community-events-location-message'] = true; 
     386                elementVisibility['.community-events-toggle-location']  = true; 
     387                elementVisibility['.community-events-results']          = true; 
     388 
     389            } else if ( templateParams.unknownCity ) { 
     390                template = wp.template( 'community-events-could-not-locate' ); 
     391                $( '.community-events-could-not-locate' ).html( template( templateParams ) ); 
     392                wp.a11y.speak( communityEventsData.l10n.could_not_locate_city.replace( l10nPlaceholder, templateParams.unknownCity ) ); 
     393 
     394                elementVisibility['.community-events-errors']           = true; 
     395                elementVisibility['.community-events-could-not-locate'] = true; 
     396 
     397            } else if ( templateParams.error && 'user' === initiatedBy ) { 
     398                /* 
     399                 * Errors messages are only shown for requests that were initiated 
     400                 * by the user, not for ones that were initiated by the app itself. 
     401                 * Showing error messages for an event that user isn't aware of 
     402                 * could be confusing or unnecessarily distracting. 
     403                 */ 
     404                wp.a11y.speak( communityEventsData.l10n.error_occurred_please_try_again ); 
     405 
     406                elementVisibility['.community-events-errors']         = true; 
     407                elementVisibility['.community-events-error-occurred'] = true; 
     408 
     409            } else { 
     410                $locationMessage.text( communityEventsData.l10n.enter_closest_city ); 
     411 
     412                elementVisibility['#community-events-location-message'] = true; 
     413                elementVisibility['.community-events-toggle-location']  = true; 
     414            } 
     415 
     416            // Set the visibility of toggleable elements. 
     417            _.each( elementVisibility, function( isVisible, element ) { 
     418                $( element ).attr( 'aria-hidden', ! isVisible ); 
     419            }); 
     420 
     421            $( '.community-events-toggle-location' ).attr( 'aria-expanded', elementVisibility['.community-events-toggle-location'] ); 
     422 
     423            /* 
     424             * During the initial page load, the location form should be hidden 
     425             * by default if the user has saved a valid location during a previous 
     426             * session. It's safe to assume that they want to continue using that 
     427             * location, and displaying the form would unnecessarily clutter the 
     428             * widget. 
     429             */ 
     430            if ( 'app' === initiatedBy && templateParams.location ) { 
     431                app.toggleLocationForm( 'hide' ); 
     432            } else { 
     433                app.toggleLocationForm( 'show' ); 
     434            } 
     435        } 
     436    }; 
     437 
     438    if ( $( '#dashboard_primary' ).is( ':visible' ) ) { 
     439        app.init(); 
     440    } else { 
     441        $( document ).on( 'postbox-toggled', function( event, postbox ) { 
     442            var $postbox = $( postbox ); 
     443 
     444            if ( 'dashboard_primary' === $postbox.attr( 'id' ) && $postbox.is( ':visible' ) ) { 
     445                app.init(); 
     446            } 
     447        }); 
     448    } 
     449}); 
  • trunk/src/wp-admin/network/index.php

    r38721 r40607  
    5555 
    5656wp_enqueue_script( 'dashboard' ); 
     57wp_localize_script( 'dashboard', 'communityEventsData', wp_get_community_events_script_data() ); 
    5758wp_enqueue_script( 'plugin-install' ); 
    5859add_thickbox(); 
     
    7475</div><!-- wrap --> 
    7576 
    76 <?php include( ABSPATH . 'wp-admin/admin-footer.php' ); ?> 
     77<?php 
     78wp_print_community_events_templates(); 
     79include( ABSPATH . 'wp-admin/admin-footer.php' ); 
  • trunk/src/wp-includes/script-loader.php

    r40283 r40607  
    725725        ) ); 
    726726 
    727         $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox' ), false, 1 ); 
     727        $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox', 'wp-util', 'wp-a11y' ), false, 1 ); 
    728728 
    729729        $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" ); 
Note: See TracChangeset for help on using the changeset viewer.