| 1 | diff --git src/wp-admin/admin-ajax.php src/wp-admin/admin-ajax.php |
|---|
| 2 | index 3213d55028..e0f4464d94 100644 |
|---|
| 3 | --- src/wp-admin/admin-ajax.php |
|---|
| 4 | +++ src/wp-admin/admin-ajax.php |
|---|
| 5 | @@ -64,7 +64,7 @@ $core_actions_post = array( |
|---|
| 6 | 'parse-media-shortcode', 'destroy-sessions', 'install-plugin', 'update-plugin', 'press-this-save-post', |
|---|
| 7 | 'press-this-add-category', 'crop-image', 'generate-password', 'save-wporg-username', 'delete-plugin', |
|---|
| 8 | 'search-plugins', 'search-install-plugins', 'activate-plugin', 'update-theme', 'delete-theme', |
|---|
| 9 | - 'install-theme', 'get-post-thumbnail-html', 'get-community-events', |
|---|
| 10 | + 'install-theme', 'get-post-thumbnail-html', |
|---|
| 11 | ); |
|---|
| 12 | |
|---|
| 13 | // Deprecated |
|---|
| 14 | diff --git src/wp-admin/includes/ajax-actions.php src/wp-admin/includes/ajax-actions.php |
|---|
| 15 | index 029e20e62b..3342cad375 100644 |
|---|
| 16 | --- src/wp-admin/includes/ajax-actions.php |
|---|
| 17 | +++ src/wp-admin/includes/ajax-actions.php |
|---|
| 18 | @@ -297,40 +297,6 @@ function wp_ajax_autocomplete_user() { |
|---|
| 19 | } |
|---|
| 20 | |
|---|
| 21 | /** |
|---|
| 22 | - * Handles AJAX requests for community events |
|---|
| 23 | - * |
|---|
| 24 | - * @since 4.8.0 |
|---|
| 25 | - */ |
|---|
| 26 | -function wp_ajax_get_community_events() { |
|---|
| 27 | - require_once( ABSPATH . 'wp-admin/includes/class-wp-community-events.php' ); |
|---|
| 28 | - |
|---|
| 29 | - check_ajax_referer( 'community_events' ); |
|---|
| 30 | - |
|---|
| 31 | - $search = isset( $_POST['location'] ) ? wp_unslash( $_POST['location'] ) : ''; |
|---|
| 32 | - $timezone = isset( $_POST['timezone'] ) ? wp_unslash( $_POST['timezone'] ) : ''; |
|---|
| 33 | - $user_id = get_current_user_id(); |
|---|
| 34 | - $saved_location = get_user_option( 'community-events-location', $user_id ); |
|---|
| 35 | - $events_client = new WP_Community_Events( $user_id, $saved_location ); |
|---|
| 36 | - $events = $events_client->get_events( $search, $timezone ); |
|---|
| 37 | - |
|---|
| 38 | - if ( is_wp_error( $events ) ) { |
|---|
| 39 | - wp_send_json_error( array( |
|---|
| 40 | - 'error' => $events->get_error_message(), |
|---|
| 41 | - ) ); |
|---|
| 42 | - } else { |
|---|
| 43 | - if ( isset( $events['location'] ) ) { |
|---|
| 44 | - // Send only the data that the client will use. |
|---|
| 45 | - $events['location'] = $events['location']['description']; |
|---|
| 46 | - |
|---|
| 47 | - // Store the location network-wide, so the user doesn't have to set it on each site. |
|---|
| 48 | - update_user_option( $user_id, 'community-events-location', $events['location'], true ); |
|---|
| 49 | - } |
|---|
| 50 | - |
|---|
| 51 | - wp_send_json_success( $events ); |
|---|
| 52 | - } |
|---|
| 53 | -} |
|---|
| 54 | - |
|---|
| 55 | -/** |
|---|
| 56 | * Ajax handler for dashboard widgets. |
|---|
| 57 | * |
|---|
| 58 | * @since 3.4.0 |
|---|
| 59 | diff --git src/wp-admin/includes/dashboard.php src/wp-admin/includes/dashboard.php |
|---|
| 60 | index b25426e753..514ff58af4 100644 |
|---|
| 61 | --- src/wp-admin/includes/dashboard.php |
|---|
| 62 | +++ src/wp-admin/includes/dashboard.php |
|---|
| 63 | @@ -144,7 +144,8 @@ function wp_get_community_events_script_data() { |
|---|
| 64 | $events_client = new WP_Community_Events( $user_id, $user_location ); |
|---|
| 65 | |
|---|
| 66 | $script_data = array( |
|---|
| 67 | - 'nonce' => wp_create_nonce( 'community_events' ), |
|---|
| 68 | + 'rest_url' => rest_url( '/' ), |
|---|
| 69 | + 'rest_nonce' => wp_create_nonce( 'wp_rest' ), |
|---|
| 70 | 'cache' => $events_client->get_cached_events(), |
|---|
| 71 | |
|---|
| 72 | 'l10n' => array( |
|---|
| 73 | @@ -1243,7 +1244,7 @@ function wp_print_community_events_templates() { |
|---|
| 74 | <?php printf( |
|---|
| 75 | /* translators: %s is a placeholder for the name of a city. */ |
|---|
| 76 | __( 'Attend an upcoming event near %s.' ), |
|---|
| 77 | - '<strong>{{ data.location }}</strong>' |
|---|
| 78 | + '<strong>{{ data.location.description }}</strong>' |
|---|
| 79 | ); ?> |
|---|
| 80 | </script> |
|---|
| 81 | |
|---|
| 82 | @@ -1265,12 +1266,14 @@ function wp_print_community_events_templates() { |
|---|
| 83 | </div> |
|---|
| 84 | </div> |
|---|
| 85 | |
|---|
| 86 | - <div class="event-date-time"> |
|---|
| 87 | - <span class="event-date">{{ event.formatted_date }}</span> |
|---|
| 88 | - <# if ( 'meetup' === event.type ) { #> |
|---|
| 89 | - <span class="event-time">{{ event.formatted_time }}</span> |
|---|
| 90 | - <# } #> |
|---|
| 91 | - </div> |
|---|
| 92 | + <# if ( event.date && event.date.formatted ) { #> |
|---|
| 93 | + <div class="event-date-time"> |
|---|
| 94 | + <span class="event-date">{{ event.date.formatted.date }}</span> |
|---|
| 95 | + <# if ( 'meetup' === event.type ) { #> |
|---|
| 96 | + <span class="event-time">{{ event.date.formatted.time }}</span> |
|---|
| 97 | + <# } #> |
|---|
| 98 | + </div> |
|---|
| 99 | + <# } #> |
|---|
| 100 | </li> |
|---|
| 101 | <# } ) #> |
|---|
| 102 | </script> |
|---|
| 103 | @@ -1280,7 +1283,7 @@ function wp_print_community_events_templates() { |
|---|
| 104 | <?php printf( |
|---|
| 105 | /* translators: 1: the city the user searched for, 2: meetup organization documentation URL */ |
|---|
| 106 | __( 'There aren’t any events scheduled near %1$s at the moment. Would you like to <a href="%2$s">organize one</a>?' ), |
|---|
| 107 | - '{{data.location}}', |
|---|
| 108 | + '{{data.location.description}}', |
|---|
| 109 | __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' ) |
|---|
| 110 | ); ?> |
|---|
| 111 | </li> |
|---|
| 112 | diff --git src/wp-admin/js/dashboard.js src/wp-admin/js/dashboard.js |
|---|
| 113 | index 6a9b5033d4..d3d2bcc644 100644 |
|---|
| 114 | --- src/wp-admin/js/dashboard.js |
|---|
| 115 | +++ src/wp-admin/js/dashboard.js |
|---|
| 116 | @@ -191,7 +191,7 @@ jQuery(document).ready( function($) { |
|---|
| 117 | |
|---|
| 118 | jQuery( function( $ ) { |
|---|
| 119 | 'use strict'; |
|---|
| 120 | - |
|---|
| 121 | + |
|---|
| 122 | var communityEventsData = window.communityEventsData || {}; |
|---|
| 123 | |
|---|
| 124 | var app = window.wp.communityEvents = { |
|---|
| 125 | @@ -288,45 +288,71 @@ jQuery( function( $ ) { |
|---|
| 126 | getEvents: function( requestParams ) { |
|---|
| 127 | var initiatedBy, |
|---|
| 128 | app = this, |
|---|
| 129 | - $spinner = $( '.community-events-form' ).children( '.spinner' ); |
|---|
| 130 | + $spinner = $( '.community-events-form' ).children( '.spinner' ), |
|---|
| 131 | + dashboardLoadPromise = wp.api.init( { 'versionString': 'wp/dashboard/v1/' } ); |
|---|
| 132 | +; |
|---|
| 133 | |
|---|
| 134 | requestParams = requestParams || {}; |
|---|
| 135 | - requestParams._wpnonce = communityEventsData.nonce; |
|---|
| 136 | requestParams.timezone = window.Intl ? window.Intl.DateTimeFormat().resolvedOptions().timeZone : ''; |
|---|
| 137 | + requestParams._embed = 1; |
|---|
| 138 | |
|---|
| 139 | initiatedBy = requestParams.location ? 'user' : 'app'; |
|---|
| 140 | |
|---|
| 141 | $spinner.addClass( 'is-active' ); |
|---|
| 142 | |
|---|
| 143 | - wp.ajax.post( 'get-community-events', requestParams ) |
|---|
| 144 | - .always( function() { |
|---|
| 145 | - $spinner.removeClass( 'is-active' ); |
|---|
| 146 | - }) |
|---|
| 147 | - |
|---|
| 148 | - .done( function( response ) { |
|---|
| 149 | - if ( 'no_location_available' === response.error ) { |
|---|
| 150 | - if ( requestParams.location ) { |
|---|
| 151 | - response.unknownCity = requestParams.location; |
|---|
| 152 | - } else { |
|---|
| 153 | - /* |
|---|
| 154 | - * No location was passed, which means that this was an automatic query |
|---|
| 155 | - * based on IP, locale, and timezone. Since the user didn't initiate it, |
|---|
| 156 | - * it should fail silently. Otherwise, the error could confuse and/or |
|---|
| 157 | - * annoy them. |
|---|
| 158 | - */ |
|---|
| 159 | - |
|---|
| 160 | - delete response.error; |
|---|
| 161 | + dashboardLoadPromise.done( function( endpoint ) { |
|---|
| 162 | + if ( ! app.model ) { |
|---|
| 163 | + app.model = new wp.api.collections.CommunityEventsMyLocation(); |
|---|
| 164 | + } |
|---|
| 165 | + requestParams = requestParams || {}; |
|---|
| 166 | + requestParams.timezone = window.Intl ? window.Intl.DateTimeFormat().resolvedOptions().timeZone : ''; |
|---|
| 167 | + requestParams._embed = 1; |
|---|
| 168 | + initiatedBy = requestParams.location ? 'user' : 'app'; |
|---|
| 169 | + |
|---|
| 170 | + $spinner.addClass( 'is-active' ); |
|---|
| 171 | + |
|---|
| 172 | + |
|---|
| 173 | + var meModel = app.model.fetch( { |
|---|
| 174 | + 'data': requestParams, |
|---|
| 175 | + 'success': function( model, response ) { |
|---|
| 176 | + var events = response._embedded && response._embedded.events ? response._embedded.events[0] : []; |
|---|
| 177 | + var location = response; |
|---|
| 178 | + |
|---|
| 179 | + delete response._embedded; |
|---|
| 180 | + delete response._links; |
|---|
| 181 | + |
|---|
| 182 | + app.renderEventsTemplate({ |
|---|
| 183 | + location: location, |
|---|
| 184 | + events: events |
|---|
| 185 | + }, initiatedBy ); |
|---|
| 186 | + |
|---|
| 187 | + }, |
|---|
| 188 | + 'error': function( model, response ) { |
|---|
| 189 | + |
|---|
| 190 | + if ( 'rest_cannot_retrieve_user_location' === response.code && requestParams.location ) { |
|---|
| 191 | + app.renderEventsTemplate({ |
|---|
| 192 | + unknownCity: requestParams.location, |
|---|
| 193 | + location: false, |
|---|
| 194 | + events: [] |
|---|
| 195 | + }, initiatedBy ); |
|---|
| 196 | + |
|---|
| 197 | + return; |
|---|
| 198 | } |
|---|
| 199 | + |
|---|
| 200 | + app.renderEventsTemplate( { |
|---|
| 201 | + 'location' : false, |
|---|
| 202 | + 'error' : true |
|---|
| 203 | + }, initiatedBy ); |
|---|
| 204 | } |
|---|
| 205 | - app.renderEventsTemplate( response, initiatedBy ); |
|---|
| 206 | - }) |
|---|
| 207 | - |
|---|
| 208 | - .fail( function() { |
|---|
| 209 | - app.renderEventsTemplate( { |
|---|
| 210 | - 'location' : false, |
|---|
| 211 | - 'error' : true |
|---|
| 212 | - }, initiatedBy ); |
|---|
| 213 | - }); |
|---|
| 214 | + } ); |
|---|
| 215 | + |
|---|
| 216 | + meModel |
|---|
| 217 | + .always( function() { |
|---|
| 218 | + $spinner.removeClass( 'is-active' ); |
|---|
| 219 | + }); |
|---|
| 220 | + }); |
|---|
| 221 | }, |
|---|
| 222 | |
|---|
| 223 | /** |
|---|
| 224 | diff --git src/wp-includes/js/wp-api.js src/wp-includes/js/wp-api.js |
|---|
| 225 | index 3f950a47f5..bf3257b80b 100644 |
|---|
| 226 | --- src/wp-includes/js/wp-api.js |
|---|
| 227 | +++ src/wp-includes/js/wp-api.js |
|---|
| 228 | @@ -1282,14 +1282,20 @@ |
|---|
| 229 | |
|---|
| 230 | // Function that returns a constructed url passed on the parent. |
|---|
| 231 | url: function() { |
|---|
| 232 | + var hasParent = ! _.isEmpty( this.parent ); |
|---|
| 233 | return routeModel.get( 'apiRoot' ) + routeModel.get( 'versionString' ) + |
|---|
| 234 | - parentName + '/' + this.parent + '/' + |
|---|
| 235 | + parentName + '/' + |
|---|
| 236 | + ( hasParent ? ( this.parent + '/' ) : '' ) + |
|---|
| 237 | routeName; |
|---|
| 238 | }, |
|---|
| 239 | |
|---|
| 240 | // Specify the model that this collection contains. |
|---|
| 241 | model: function( attrs, options ) { |
|---|
| 242 | - return new loadingObjects.models[ modelClassName ]( attrs, options ); |
|---|
| 243 | + if ( loadingObjects.models[ modelClassName ] ) { |
|---|
| 244 | + return new loadingObjects.models[ modelClassName ]( attrs, options ); |
|---|
| 245 | + } else { |
|---|
| 246 | + return new Backbone.Model(); |
|---|
| 247 | + } |
|---|
| 248 | }, |
|---|
| 249 | |
|---|
| 250 | // Include a reference to the original class name. |
|---|
| 251 | diff --git src/wp-includes/rest-api.php src/wp-includes/rest-api.php |
|---|
| 252 | index ec7c50d27b..891be7ceb6 100644 |
|---|
| 253 | --- src/wp-includes/rest-api.php |
|---|
| 254 | +++ src/wp-includes/rest-api.php |
|---|
| 255 | @@ -237,6 +237,14 @@ function create_initial_rest_routes() { |
|---|
| 256 | // Settings. |
|---|
| 257 | $controller = new WP_REST_Settings_Controller; |
|---|
| 258 | $controller->register_routes(); |
|---|
| 259 | + |
|---|
| 260 | + // Community events. |
|---|
| 261 | + $controller = new WP_REST_Community_Events_Events_Controller; |
|---|
| 262 | + $controller->register_routes(); |
|---|
| 263 | + |
|---|
| 264 | + // Community events location. |
|---|
| 265 | + $controller = new WP_REST_Community_Events_Location_Controller; |
|---|
| 266 | + $controller->register_routes(); |
|---|
| 267 | } |
|---|
| 268 | |
|---|
| 269 | /** |
|---|
| 270 | diff --git src/wp-includes/rest-api/endpoints/dashboard/class-wp-rest-community-events-events-controller.php src/wp-includes/rest-api/endpoints/dashboard/class-wp-rest-community-events-events-controller.php |
|---|
| 271 | new file mode 100644 |
|---|
| 272 | index 0000000000..570abd2adc |
|---|
| 273 | --- /dev/null |
|---|
| 274 | +++ src/wp-includes/rest-api/endpoints/dashboard/class-wp-rest-community-events-events-controller.php |
|---|
| 275 | @@ -0,0 +1,281 @@ |
|---|
| 276 | +<?php |
|---|
| 277 | +/** |
|---|
| 278 | + * REST API: WP_REST_Community_Events_Events_Controller class |
|---|
| 279 | + * |
|---|
| 280 | + * @package WordPress |
|---|
| 281 | + * @subpackage REST_API |
|---|
| 282 | + * @since 4.8.0 |
|---|
| 283 | + */ |
|---|
| 284 | + |
|---|
| 285 | +/** |
|---|
| 286 | + * Core class to access community events via the REST API. |
|---|
| 287 | + * |
|---|
| 288 | + * @since 4.8.0 |
|---|
| 289 | + * |
|---|
| 290 | + * @see WP_REST_Controller |
|---|
| 291 | + */ |
|---|
| 292 | +class WP_REST_Community_Events_Events_Controller extends WP_REST_Controller { |
|---|
| 293 | + |
|---|
| 294 | + /** |
|---|
| 295 | + * Constructor. |
|---|
| 296 | + * |
|---|
| 297 | + * @since 4.8.0 |
|---|
| 298 | + * @access public |
|---|
| 299 | + */ |
|---|
| 300 | + public function __construct() { |
|---|
| 301 | + $this->namespace = 'wp/dashboard/v1'; |
|---|
| 302 | + $this->rest_base = 'community-events/events'; |
|---|
| 303 | + } |
|---|
| 304 | + |
|---|
| 305 | + /** |
|---|
| 306 | + * Registers the routes for the objects of the controller. |
|---|
| 307 | + * |
|---|
| 308 | + * @since 4.8.0 |
|---|
| 309 | + * @access public |
|---|
| 310 | + * |
|---|
| 311 | + * @see register_rest_route() |
|---|
| 312 | + */ |
|---|
| 313 | + public function register_routes() { |
|---|
| 314 | + |
|---|
| 315 | + register_rest_route( $this->namespace, '/' . $this->rest_base . '/me', array( |
|---|
| 316 | + array( |
|---|
| 317 | + 'methods' => WP_REST_Server::READABLE, |
|---|
| 318 | + 'callback' => array( $this, 'get_current_items' ), |
|---|
| 319 | + 'permission_callback' => array( $this, 'get_current_items_permissions_check' ), |
|---|
| 320 | + 'args' => $this->get_collection_params(), |
|---|
| 321 | + ), |
|---|
| 322 | + 'schema' => array( $this, 'get_public_item_schema' ), |
|---|
| 323 | + ) ); |
|---|
| 324 | + } |
|---|
| 325 | + |
|---|
| 326 | + /** |
|---|
| 327 | + * Checks whether a given request has permission to read community events. |
|---|
| 328 | + * |
|---|
| 329 | + * @since 4.8.0 |
|---|
| 330 | + * @access public |
|---|
| 331 | + * |
|---|
| 332 | + * @param WP_REST_Request $request Full details about the request. |
|---|
| 333 | + * @return WP_Error|true True if the request has read access, WP_Error object otherwise. |
|---|
| 334 | + */ |
|---|
| 335 | + public function get_current_items_permissions_check( $request ) { |
|---|
| 336 | + if ( ! is_user_logged_in() ) { |
|---|
| 337 | + return new WP_Error( 'rest_not_logged_in', __( 'You are not currently logged in.' ), array( 'status' => 401 ) ); |
|---|
| 338 | + } |
|---|
| 339 | + |
|---|
| 340 | + return true; |
|---|
| 341 | + } |
|---|
| 342 | + |
|---|
| 343 | + /** |
|---|
| 344 | + * Retrieves community events. |
|---|
| 345 | + * |
|---|
| 346 | + * @since 4.8.0 |
|---|
| 347 | + * @access public |
|---|
| 348 | + * |
|---|
| 349 | + * @param WP_REST_Request $request Full details about the request. |
|---|
| 350 | + * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. |
|---|
| 351 | + */ |
|---|
| 352 | + public function get_current_items( $request ) { |
|---|
| 353 | + require_once( ABSPATH . 'wp-admin/includes/class-wp-community-events.php' ); |
|---|
| 354 | + |
|---|
| 355 | + $user_id = get_current_user_id(); |
|---|
| 356 | + |
|---|
| 357 | + $location = $request->get_param( 'location' ); |
|---|
| 358 | + $timezone = $request->get_param( 'timezone' ); |
|---|
| 359 | + |
|---|
| 360 | + $saved_location = get_user_option( 'community-events-location', $user_id ); |
|---|
| 361 | + $events_client = new WP_Community_Events( $user_id, $saved_location ); |
|---|
| 362 | + $events = $events_client->get_events( $location, $timezone ); |
|---|
| 363 | + |
|---|
| 364 | + $data = array(); |
|---|
| 365 | + |
|---|
| 366 | + // Store the location network-wide, so the user doesn't have to set it on each site. |
|---|
| 367 | + if ( ! is_wp_error( $events ) ) { |
|---|
| 368 | + if ( isset( $events['location'] ) ) { |
|---|
| 369 | + update_user_option( $user_id, 'community-events-location', $events['location'], true ); |
|---|
| 370 | + } |
|---|
| 371 | + |
|---|
| 372 | + if ( isset( $events['events'] ) ) { |
|---|
| 373 | + foreach ( $events['events'] as $event ) { |
|---|
| 374 | + $data[] = $this->prepare_item_for_response( $event, $request ); |
|---|
| 375 | + } |
|---|
| 376 | + } |
|---|
| 377 | + } |
|---|
| 378 | + |
|---|
| 379 | + return rest_ensure_response( $data ); |
|---|
| 380 | + } |
|---|
| 381 | + |
|---|
| 382 | + /** |
|---|
| 383 | + * Prepares a single event output for response. |
|---|
| 384 | + * |
|---|
| 385 | + * @since 4.8.0 |
|---|
| 386 | + * @access public |
|---|
| 387 | + * |
|---|
| 388 | + * @param array $event Event data array from the API. |
|---|
| 389 | + * @param WP_REST_Request $request Request object. |
|---|
| 390 | + * @return array Item prepared for response. |
|---|
| 391 | + */ |
|---|
| 392 | + public function prepare_item_for_response( $event, $request ) { |
|---|
| 393 | + $data = array(); |
|---|
| 394 | + |
|---|
| 395 | + $keys_to_copy = array( 'type', 'title', 'url', 'meetup', 'meetup_url' ); |
|---|
| 396 | + foreach ( $keys_to_copy as $key ) { |
|---|
| 397 | + if ( isset( $event[ $key ] ) ) { |
|---|
| 398 | + $data[ $key ] = $event[ $key ]; |
|---|
| 399 | + } else { |
|---|
| 400 | + $data[ $key ] = null; |
|---|
| 401 | + } |
|---|
| 402 | + } |
|---|
| 403 | + |
|---|
| 404 | + $data['date'] = array( |
|---|
| 405 | + 'raw' => isset( $event['date'] ) ? $event['date'] : null, |
|---|
| 406 | + 'formatted' => array( |
|---|
| 407 | + 'date' => isset( $event['formatted_date'] ) ? $event['formatted_date'] : null, |
|---|
| 408 | + 'time' => isset( $event['formatted_time'] ) ? $event['formatted_time'] : null, |
|---|
| 409 | + ), |
|---|
| 410 | + ); |
|---|
| 411 | + |
|---|
| 412 | + $data['location'] = isset( $event['location'] ) ? $event['location'] : null; |
|---|
| 413 | + |
|---|
| 414 | + return $data; |
|---|
| 415 | + } |
|---|
| 416 | + |
|---|
| 417 | + /** |
|---|
| 418 | + * Retrieves a community event's schema, conforming to JSON Schema. |
|---|
| 419 | + * |
|---|
| 420 | + * @since 4.8.0 |
|---|
| 421 | + * @access public |
|---|
| 422 | + * |
|---|
| 423 | + * @return array Item schema data. |
|---|
| 424 | + */ |
|---|
| 425 | + public function get_item_schema() { |
|---|
| 426 | + return array( |
|---|
| 427 | + '$schema' => 'http://json-schema.org/schema#', |
|---|
| 428 | + 'title' => 'community_event', |
|---|
| 429 | + 'type' => 'object', |
|---|
| 430 | + 'properties' => array( |
|---|
| 431 | + 'type' => array( |
|---|
| 432 | + 'description' => __( 'Type for the event.' ), |
|---|
| 433 | + 'type' => 'string', |
|---|
| 434 | + 'enum' => array( 'meetup', 'wordcamp' ), |
|---|
| 435 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 436 | + 'readonly' => true, |
|---|
| 437 | + ), |
|---|
| 438 | + 'title' => array( |
|---|
| 439 | + 'description' => __( 'Title for the event.' ), |
|---|
| 440 | + 'type' => 'string', |
|---|
| 441 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 442 | + 'readonly' => true, |
|---|
| 443 | + ), |
|---|
| 444 | + 'url' => array( |
|---|
| 445 | + 'description' => __( 'Website URL for the event.' ), |
|---|
| 446 | + 'type' => 'string', |
|---|
| 447 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 448 | + 'readonly' => true, |
|---|
| 449 | + ), |
|---|
| 450 | + 'meetup' => array( |
|---|
| 451 | + 'description' => __( 'Name of the meetup, if the event is a meetup.' ), |
|---|
| 452 | + 'type' => 'string', |
|---|
| 453 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 454 | + 'readonly' => true, |
|---|
| 455 | + ), |
|---|
| 456 | + 'meetup_url' => array( |
|---|
| 457 | + 'description' => __( 'URL for the meetup on meetup.com, if the event is a meetup.' ), |
|---|
| 458 | + 'type' => 'string', |
|---|
| 459 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 460 | + 'readonly' => true, |
|---|
| 461 | + ), |
|---|
| 462 | + 'date' => array( |
|---|
| 463 | + 'description' => __( 'Date and time information for the event.' ), |
|---|
| 464 | + 'type' => 'object', |
|---|
| 465 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 466 | + 'readonly' => true, |
|---|
| 467 | + 'properties' => array( |
|---|
| 468 | + 'raw' => array( |
|---|
| 469 | + 'description' => __( 'Unformatted date and time string.' ), |
|---|
| 470 | + 'type' => 'string', |
|---|
| 471 | + 'format' => 'date-time', |
|---|
| 472 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 473 | + 'readonly' => true, |
|---|
| 474 | + ), |
|---|
| 475 | + 'formatted' => array( |
|---|
| 476 | + 'description' => __( 'Formatted date and time information for the event.' ), |
|---|
| 477 | + 'type' => 'object', |
|---|
| 478 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 479 | + 'readonly' => true, |
|---|
| 480 | + 'properties' => array( |
|---|
| 481 | + 'date' => array( |
|---|
| 482 | + 'description' => __( 'Formatted event date.' ), |
|---|
| 483 | + 'type' => 'string', |
|---|
| 484 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 485 | + 'readonly' => true, |
|---|
| 486 | + ), |
|---|
| 487 | + 'time' => array( |
|---|
| 488 | + 'description' => __( 'Formatted event time.' ), |
|---|
| 489 | + 'type' => 'string', |
|---|
| 490 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 491 | + 'readonly' => true, |
|---|
| 492 | + ), |
|---|
| 493 | + ), |
|---|
| 494 | + ), |
|---|
| 495 | + ), |
|---|
| 496 | + ), |
|---|
| 497 | + 'location' => array( |
|---|
| 498 | + 'description' => __( 'Location information for the event.' ), |
|---|
| 499 | + 'type' => 'object', |
|---|
| 500 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 501 | + 'readonly' => true, |
|---|
| 502 | + 'properties' => array( |
|---|
| 503 | + 'location' => array( |
|---|
| 504 | + 'description' => __( 'Location name for the event.' ), |
|---|
| 505 | + 'type' => 'string', |
|---|
| 506 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 507 | + 'readonly' => true, |
|---|
| 508 | + ), |
|---|
| 509 | + 'country' => array( |
|---|
| 510 | + 'description' => __( 'Two-letter country code for the event.' ), |
|---|
| 511 | + 'type' => 'string', |
|---|
| 512 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 513 | + 'readonly' => true, |
|---|
| 514 | + ), |
|---|
| 515 | + 'latitude' => array( |
|---|
| 516 | + 'description' => __( 'Latitude for the event.' ), |
|---|
| 517 | + 'type' => 'number', |
|---|
| 518 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 519 | + 'readonly' => true, |
|---|
| 520 | + ), |
|---|
| 521 | + 'longitude' => array( |
|---|
| 522 | + 'description' => __( 'Longitude for the event.' ), |
|---|
| 523 | + 'type' => 'number', |
|---|
| 524 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 525 | + 'readonly' => true, |
|---|
| 526 | + ), |
|---|
| 527 | + ), |
|---|
| 528 | + ), |
|---|
| 529 | + ), |
|---|
| 530 | + ); |
|---|
| 531 | + } |
|---|
| 532 | + |
|---|
| 533 | + /** |
|---|
| 534 | + * Retrieves the query params for collections. |
|---|
| 535 | + * |
|---|
| 536 | + * @since 4.8.0 |
|---|
| 537 | + * @access public |
|---|
| 538 | + * |
|---|
| 539 | + * @return array Collection parameters. |
|---|
| 540 | + */ |
|---|
| 541 | + public function get_collection_params() { |
|---|
| 542 | + return array( |
|---|
| 543 | + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), |
|---|
| 544 | + 'location' => array( |
|---|
| 545 | + 'description' => __( 'Optional city name to help determine the location for the events.' ), |
|---|
| 546 | + 'type' => 'string', |
|---|
| 547 | + 'default' => '', |
|---|
| 548 | + ), |
|---|
| 549 | + 'timezone' => array( |
|---|
| 550 | + 'description' => __( 'Optional timezone to help determine the location for the events.' ), |
|---|
| 551 | + 'type' => 'string', |
|---|
| 552 | + 'default' => '', |
|---|
| 553 | + ), |
|---|
| 554 | + ); |
|---|
| 555 | + } |
|---|
| 556 | +} |
|---|
| 557 | diff --git src/wp-includes/rest-api/endpoints/dashboard/class-wp-rest-community-events-location-controller.php src/wp-includes/rest-api/endpoints/dashboard/class-wp-rest-community-events-location-controller.php |
|---|
| 558 | new file mode 100644 |
|---|
| 559 | index 0000000000..3763885731 |
|---|
| 560 | --- /dev/null |
|---|
| 561 | +++ src/wp-includes/rest-api/endpoints/dashboard/class-wp-rest-community-events-location-controller.php |
|---|
| 562 | @@ -0,0 +1,217 @@ |
|---|
| 563 | +<?php |
|---|
| 564 | +/** |
|---|
| 565 | + * REST API: WP_REST_Community_Events_Location_Controller class |
|---|
| 566 | + * |
|---|
| 567 | + * @package WordPress |
|---|
| 568 | + * @subpackage REST_API |
|---|
| 569 | + * @since 4.8.0 |
|---|
| 570 | + */ |
|---|
| 571 | + |
|---|
| 572 | +/** |
|---|
| 573 | + * Core class to access community events user locations via the REST API. |
|---|
| 574 | + * |
|---|
| 575 | + * @since 4.8.0 |
|---|
| 576 | + * |
|---|
| 577 | + * @see WP_REST_Controller |
|---|
| 578 | + */ |
|---|
| 579 | +class WP_REST_Community_Events_Location_Controller extends WP_REST_Controller { |
|---|
| 580 | + |
|---|
| 581 | + /** |
|---|
| 582 | + * Constructor. |
|---|
| 583 | + * |
|---|
| 584 | + * @since 4.8.0 |
|---|
| 585 | + * @access public |
|---|
| 586 | + */ |
|---|
| 587 | + public function __construct() { |
|---|
| 588 | + $this->namespace = 'wp/dashboard/v1'; |
|---|
| 589 | + $this->rest_base = 'community-events'; |
|---|
| 590 | + } |
|---|
| 591 | + |
|---|
| 592 | + /** |
|---|
| 593 | + * Registers the routes for the objects of the controller. |
|---|
| 594 | + * |
|---|
| 595 | + * @since 4.8.0 |
|---|
| 596 | + * @access public |
|---|
| 597 | + * |
|---|
| 598 | + * @see register_rest_route() |
|---|
| 599 | + */ |
|---|
| 600 | + public function register_routes() { |
|---|
| 601 | + |
|---|
| 602 | + register_rest_route( $this->namespace, '/' . $this->rest_base . '/my-location', array( |
|---|
| 603 | + array( |
|---|
| 604 | + 'methods' => WP_REST_Server::READABLE, |
|---|
| 605 | + 'callback' => array( $this, 'get_current_item' ), |
|---|
| 606 | + 'permission_callback' => array( $this, 'get_current_item_permissions_check' ), |
|---|
| 607 | + 'args' => $this->get_item_params(), |
|---|
| 608 | + ), |
|---|
| 609 | + 'schema' => array( $this, 'get_public_item_schema' ), |
|---|
| 610 | + ) ); |
|---|
| 611 | + } |
|---|
| 612 | + |
|---|
| 613 | + /** |
|---|
| 614 | + * Checks whether a given request has permission to read the current user's community events location. |
|---|
| 615 | + * |
|---|
| 616 | + * @since 4.8.0 |
|---|
| 617 | + * @access public |
|---|
| 618 | + * |
|---|
| 619 | + * @param WP_REST_Request $request Full details about the request. |
|---|
| 620 | + * @return WP_Error|true True if the request has read access, WP_Error object otherwise. |
|---|
| 621 | + */ |
|---|
| 622 | + public function get_current_item_permissions_check( $request ) { |
|---|
| 623 | + if ( ! is_user_logged_in() ) { |
|---|
| 624 | + return new WP_Error( 'rest_not_logged_in', __( 'You are not currently logged in.' ), array( 'status' => 401 ) ); |
|---|
| 625 | + } |
|---|
| 626 | + |
|---|
| 627 | + return true; |
|---|
| 628 | + } |
|---|
| 629 | + |
|---|
| 630 | + /** |
|---|
| 631 | + * Retrieves the community events location for the current user. |
|---|
| 632 | + * |
|---|
| 633 | + * @since 4.8.0 |
|---|
| 634 | + * @access public |
|---|
| 635 | + * |
|---|
| 636 | + * @param WP_REST_Request $request Full details about the request. |
|---|
| 637 | + * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. |
|---|
| 638 | + */ |
|---|
| 639 | + public function get_current_item( $request ) { |
|---|
| 640 | + require_once( ABSPATH . 'wp-admin/includes/class-wp-community-events.php' ); |
|---|
| 641 | + |
|---|
| 642 | + $user_id = get_current_user_id(); |
|---|
| 643 | + |
|---|
| 644 | + $location = $request->get_param( 'location' ); |
|---|
| 645 | + $timezone = $request->get_param( 'timezone' ); |
|---|
| 646 | + |
|---|
| 647 | + $saved_location = get_user_option( 'community-events-location', $user_id ); |
|---|
| 648 | + $events_client = new WP_Community_Events( $user_id, $saved_location ); |
|---|
| 649 | + $events = $events_client->get_events( $location, $timezone ); |
|---|
| 650 | + |
|---|
| 651 | + // Store the location network-wide, so the user doesn't have to set it on each site. |
|---|
| 652 | + if ( ! is_wp_error( $events ) ) { |
|---|
| 653 | + if ( isset( $events['error'] ) && 'no_location_available' === $events['error'] ) { |
|---|
| 654 | + return new WP_Error( 'rest_cannot_retrieve_user_location', __( 'The user location could not be retrieved.' ) ); |
|---|
| 655 | + } |
|---|
| 656 | + |
|---|
| 657 | + if ( isset( $events['location'] ) ) { |
|---|
| 658 | + update_user_option( $user_id, 'community-events-location', $events['location'], true ); |
|---|
| 659 | + |
|---|
| 660 | + $data = $this->prepare_item_for_response( $events['location'], $request ); |
|---|
| 661 | + |
|---|
| 662 | + return rest_ensure_response( $data ); |
|---|
| 663 | + } |
|---|
| 664 | + } |
|---|
| 665 | + |
|---|
| 666 | + return $events; |
|---|
| 667 | + } |
|---|
| 668 | + |
|---|
| 669 | + /** |
|---|
| 670 | + * Prepares a single location output for response. |
|---|
| 671 | + * |
|---|
| 672 | + * @since 4.8.0 |
|---|
| 673 | + * @access public |
|---|
| 674 | + * |
|---|
| 675 | + * @param array $location Location data array from the API. |
|---|
| 676 | + * @param WP_REST_Request $request Request object. |
|---|
| 677 | + * @return WP_REST_Response Response object. |
|---|
| 678 | + */ |
|---|
| 679 | + public function prepare_item_for_response( $location, $request ) { |
|---|
| 680 | + $data = array( |
|---|
| 681 | + 'description' => isset( $location['description'] ) ? $location['description'] : null, |
|---|
| 682 | + 'country' => isset( $location['country'] ) ? $location['country'] : null, |
|---|
| 683 | + 'latitude' => isset( $location['latitude'] ) ? (float) $location['latitude'] : null, |
|---|
| 684 | + 'longitude' => isset( $location['longitude'] ) ? (float) $location['longitude'] : null, |
|---|
| 685 | + ); |
|---|
| 686 | + |
|---|
| 687 | + $response = rest_ensure_response( $data ); |
|---|
| 688 | + |
|---|
| 689 | + $url = rest_url( 'wp/dashboard/v1/community-events/events/me' ); |
|---|
| 690 | + |
|---|
| 691 | + $url_args = array(); |
|---|
| 692 | + |
|---|
| 693 | + if ( ! empty( $request['location'] ) ) { |
|---|
| 694 | + $url_args['location'] = $request['location']; |
|---|
| 695 | + } |
|---|
| 696 | + if ( ! empty( $request['timezone'] ) ) { |
|---|
| 697 | + $url_args['timezone'] = $request['timezone']; |
|---|
| 698 | + } |
|---|
| 699 | + |
|---|
| 700 | + if ( ! empty( $url_args ) ) { |
|---|
| 701 | + $url = add_query_arg( $url_args, $url ); |
|---|
| 702 | + } |
|---|
| 703 | + |
|---|
| 704 | + $response->add_links( array( |
|---|
| 705 | + 'events' => array( |
|---|
| 706 | + 'href' => $url, |
|---|
| 707 | + 'embeddable' => true, |
|---|
| 708 | + ), |
|---|
| 709 | + ) ); |
|---|
| 710 | + |
|---|
| 711 | + return $response; |
|---|
| 712 | + } |
|---|
| 713 | + |
|---|
| 714 | + /** |
|---|
| 715 | + * Retrieves a community events location schema, conforming to JSON Schema. |
|---|
| 716 | + * |
|---|
| 717 | + * @since 4.8.0 |
|---|
| 718 | + * @access public |
|---|
| 719 | + * |
|---|
| 720 | + * @return array Item schema data. |
|---|
| 721 | + */ |
|---|
| 722 | + public function get_item_schema() { |
|---|
| 723 | + return array( |
|---|
| 724 | + '$schema' => 'http://json-schema.org/schema#', |
|---|
| 725 | + 'title' => 'community_events_location', |
|---|
| 726 | + 'type' => 'object', |
|---|
| 727 | + 'properties' => array( |
|---|
| 728 | + 'description' => array( |
|---|
| 729 | + 'description' => __( 'Location description.' ), |
|---|
| 730 | + 'type' => 'string', |
|---|
| 731 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 732 | + 'readonly' => true, |
|---|
| 733 | + ), |
|---|
| 734 | + 'country' => array( |
|---|
| 735 | + 'description' => __( 'Two-letter country code.' ), |
|---|
| 736 | + 'type' => 'string', |
|---|
| 737 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 738 | + 'readonly' => true, |
|---|
| 739 | + ), |
|---|
| 740 | + 'latitude' => array( |
|---|
| 741 | + 'description' => __( 'Latitude.' ), |
|---|
| 742 | + 'type' => 'number', |
|---|
| 743 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 744 | + 'readonly' => true, |
|---|
| 745 | + ), |
|---|
| 746 | + 'longitude' => array( |
|---|
| 747 | + 'description' => __( 'Longitude.' ), |
|---|
| 748 | + 'type' => 'number', |
|---|
| 749 | + 'context' => array( 'view', 'edit', 'embed' ), |
|---|
| 750 | + 'readonly' => true, |
|---|
| 751 | + ), |
|---|
| 752 | + ), |
|---|
| 753 | + ); |
|---|
| 754 | + } |
|---|
| 755 | + |
|---|
| 756 | + /** |
|---|
| 757 | + * Retrieves the params for a single item. |
|---|
| 758 | + * |
|---|
| 759 | + * @since 4.8.0 |
|---|
| 760 | + * @access public |
|---|
| 761 | + * |
|---|
| 762 | + * @return array Item parameters. |
|---|
| 763 | + */ |
|---|
| 764 | + public function get_item_params() { |
|---|
| 765 | + return array( |
|---|
| 766 | + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), |
|---|
| 767 | + 'location' => array( |
|---|
| 768 | + 'description' => __( 'Optional city name to help determine the location.' ), |
|---|
| 769 | + 'type' => 'string', |
|---|
| 770 | + 'default' => '', |
|---|
| 771 | + ), |
|---|
| 772 | + 'timezone' => array( |
|---|
| 773 | + 'description' => __( 'Optional timezone to help determine the location.' ), |
|---|
| 774 | + 'type' => 'string', |
|---|
| 775 | + 'default' => '', |
|---|
| 776 | + ), |
|---|
| 777 | + ); |
|---|
| 778 | + } |
|---|
| 779 | +} |
|---|
| 780 | diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php |
|---|
| 781 | index f1eda9ee38..c02fe73588 100644 |
|---|
| 782 | --- src/wp-includes/script-loader.php |
|---|
| 783 | +++ src/wp-includes/script-loader.php |
|---|
| 784 | @@ -732,7 +732,7 @@ function wp_default_scripts( &$scripts ) { |
|---|
| 785 | 'current' => __( 'Current Color' ), |
|---|
| 786 | ) ); |
|---|
| 787 | |
|---|
| 788 | - $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox', 'wp-util', 'wp-a11y' ), false, 1 ); |
|---|
| 789 | + $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox', 'wp-util', 'wp-a11y', 'wp-api' ), false, 1 ); |
|---|
| 790 | |
|---|
| 791 | $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" ); |
|---|
| 792 | |
|---|
| 793 | diff --git src/wp-settings.php src/wp-settings.php |
|---|
| 794 | index d1505c502d..060ab4c782 100644 |
|---|
| 795 | --- src/wp-settings.php |
|---|
| 796 | +++ src/wp-settings.php |
|---|
| 797 | @@ -234,6 +234,8 @@ require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.p |
|---|
| 798 | require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' ); |
|---|
| 799 | require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-comments-controller.php' ); |
|---|
| 800 | require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-settings-controller.php' ); |
|---|
| 801 | +require( ABSPATH . WPINC . '/rest-api/endpoints/dashboard/class-wp-rest-community-events-events-controller.php' ); |
|---|
| 802 | +require( ABSPATH . WPINC . '/rest-api/endpoints/dashboard/class-wp-rest-community-events-location-controller.php' ); |
|---|
| 803 | require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php' ); |
|---|
| 804 | require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php' ); |
|---|
| 805 | require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php' ); |
|---|