WordPress REST API (Version 2) - Version 2.0-beta8.1

Version Description

Download this release

Release Info

Developer danielbachhuber
Plugin Icon 128x128 WordPress REST API (Version 2)
Version 2.0-beta8.1
Comparing to
See all releases

Code changes from version 2.0-beta9.1 to 2.0-beta8.1

CHANGELOG.md CHANGED
@@ -1,125 +1,11 @@
1
  # Changelog
2
 
3
- ## 2.0 Beta 9.1
4
 
5
  - Ensure media of private posts are private too.
6
 
7
  Reported by @danielbachhuber on 2016-01-08.
8
 
9
- ## 2.0 Beta 9.0
10
-
11
- - BREAKING CHANGE: Move tags and categories to top-level endpoints.
12
-
13
- Tags are now accessible at `/wp/v2/tags`, and categories accessible at `/wp/v2/categories`. Post terms reside at `/wp/v2/posts/<id>/tags` and `/wp/v2/<id>/categories`.
14
-
15
- (props @danielbachhuber, [#1802](https://github.com/WP-API/WP-API/pull/1802))
16
-
17
- - BREAKING CHANGE: Return object for requests to `/wp/v2/taxonomies`.
18
-
19
- This is consistent with `/wp/v2/types` and `/wp/v2/statuses`.
20
-
21
- (props @danielbachhuber, [#1825](https://github.com/WP-API/WP-API/pull/1825))
22
-
23
- - BREAKING CHANGE: Remove `rest_get_timezone()`.
24
-
25
- `json_get_timezone()` was only ever used in v1. This function causes fatals, and shouldn't be used.
26
-
27
- (props @danielbachhuber, [#1823](https://github.com/WP-API/WP-API/pull/1823))
28
-
29
- - BREAKING CHANGE: Rename `register_api_field()` to `register_rest_field()`.
30
-
31
- Introduces a `register_api_field()` function for backwards compat, which calls `_doing_it_wrong()`. However, `register_api_field()` won't ever be committed to WordPress core, so you should update your function calls.
32
-
33
- (props @danielbachhuber, [#1824](https://github.com/WP-API/WP-API/pull/1824))
34
-
35
- - BREAKING CHANGE: Change taxonomies' `post_type` argument to `type`.
36
-
37
- It's consistent with how we're exposing post types in the API.
38
-
39
- (props @danielbachhuber, [#1824](https://github.com/WP-API/WP-API/pull/1824))
40
-
41
- - Sync infrastructure with shipped in WordPress 4.4.
42
-
43
- * `wp-includes/rest-api/rest-functions.php` is removed, and its functions moved into `wp-includes/rest-api.php`.
44
- * Send nocache headers for REST requests. [#34832](https://core.trac.wordpress.org/ticket/34832)
45
- * Fix handling of HEAD requests. [#34837](https://core.trac.wordpress.org/ticket/34837)
46
- * Mark `WP_REST_Server::get_raw_data()` as static. [#34768](https://core.trac.wordpress.org/ticket/34768)
47
- * Unabbreviate error string. [#34818](https://core.trac.wordpress.org/ticket/34818)
48
-
49
- - Change terms endpoints to use `term_id` not `tt_id`.
50
-
51
- (props @joehoyle, [#1837](https://github.com/WP-API/WP-API/pull/1837))
52
-
53
- - Standardize declaration of `context` param for `GET` requests across controllers.
54
-
55
- However, we're still inconsistent in which controllers expose which params. Follow [#1845](https://github.com/WP-API/WP-API/issues/1845) for further discussion.
56
-
57
- (props @danielbachhuber, [#1795](https://github.com/WP-API/WP-API/pull/1795), [#1835](https://github.com/WP-API/WP-API/pull/1835), [#1838](https://github.com/WP-API/WP-API/pull/1838))
58
-
59
- - Link types / taxonomies to their collections, and vice versa.
60
-
61
- Collections link to their type / taxonomy with the `about` relation; types / taxonomies link to their colletion with the `item` relation, which is imperfect and may change in the future.
62
-
63
- (props @danielbachhuber, [#1814](https://github.com/WP-API/WP-API/pull/1814), [#1817](https://github.com/WP-API/WP-API/pull/1817), [#1829](https://github.com/WP-API/WP-API/pull/1829). [#1846](https://github.com/WP-API/WP-API/pull/1846))
64
-
65
- - Add missing 'wp/v2' in Location Response header when creating new Post Meta.
66
-
67
- (props @johanmynhardt, [#1790](https://github.com/WP-API/WP-API/pull/1790))
68
-
69
- - Expose Post collection query params, including `author`, `order`, `orderby` and `status`.
70
-
71
- (props @danielbachhuber, [#1793](https://github.com/WP-API/WP-API/pull/1793))
72
-
73
- - Ignore sticky posts by default.
74
-
75
- (props @danielbachhuber, [#1801](https://github.com/WP-API/WP-API/pull/1801))
76
-
77
- - Include `full` image size in attachment `sizes` attribute.
78
-
79
- (props @danielbachhuber, [#1806](https://github.com/WP-API/WP-API/pull/1806))
80
-
81
- - In text strings, use `id` instead of `ID`.
82
-
83
- `ID` is an implementation artifact. Our Resources use `id`.
84
-
85
- (props @danielbachhuber, [#1803](https://github.com/WP-API/WP-API/pull/1803))
86
-
87
- - Ensure `attachment.sizes[]` use `mime_type` instead of `mime-type`.
88
-
89
- (props @danielbachhuber, [#1809](https://github.com/WP-API/WP-API/pull/1809))
90
-
91
- - Introduce `rest_authorization_required_code()`.
92
-
93
- Many controllers returned incorrect HTTP codes, which this also fixes.
94
-
95
- (props @danielbachhuber, [#1808](https://github.com/WP-API/WP-API/pull/1808))
96
-
97
- - Respect core's `comment_registration` setting.
98
-
99
- If it's enabled, require users to be logged in to comment.
100
-
101
- (props @danielbachhuber, [#1826](https://github.com/WP-API/WP-API/pull/1826))
102
-
103
- - Default to wildcard when searching users.
104
-
105
- (props @danielbachhuber, [#1827](https://github.com/WP-API/WP-API/pull/1827))
106
-
107
- - Bring the wp-api.js library up to date for v2 of the REST API.
108
-
109
- (props @adamsilverstein, [#1828](https://github.com/WP-API/WP-API/pull/1828))
110
-
111
- - Add `rest_prepare_status` filter.
112
-
113
- (props @danielbachhuber, [#1830](https://github.com/WP-API/WP-API/pull/1830))
114
-
115
- - Make `prepare_*` filters more consistent.
116
-
117
- (props @danielbachhuber, [#1831](https://github.com/WP-API/WP-API/pull/1831))
118
-
119
- - Add `rest_prepare_post_type` filter for post types.
120
-
121
- (props @danielbachhuber, [#1833](https://github.com/WP-API/WP-API/pull/1833))
122
-
123
  ## 2.0 Beta 8.0
124
 
125
  - Prevent fatals when uploading attachment by including admin utilities.
1
  # Changelog
2
 
3
+ ## 2.0 Beta 8.1
4
 
5
  - Ensure media of private posts are private too.
6
 
7
  Reported by @danielbachhuber on 2016-01-08.
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  ## 2.0 Beta 8.0
10
 
11
  - Prevent fatals when uploading attachment by including admin utilities.
core/rest-api.php CHANGED
@@ -26,7 +26,6 @@ if ( file_exists( ABSPATH . WPINC . '/class-wp-http-response.php' ) ) {
26
 
27
  /** Main API functions */
28
  include_once( dirname( __FILE__ ) . '/wp-includes/functions.php' );
29
- include_once( dirname( __FILE__ ) . '/wp-includes/rest-api.php' );
30
 
31
  /** WP_REST_Server class */
32
  include_once( dirname( __FILE__ ) . '/wp-includes/rest-api/class-wp-rest-server.php' );
@@ -40,6 +39,9 @@ include_once( dirname( __FILE__ ) . '/wp-includes/rest-api/class-wp-rest-respons
40
  /** WP_REST_Request class */
41
  require_once( dirname( __FILE__ ) . '/wp-includes/rest-api/class-wp-rest-request.php' );
42
 
 
 
 
43
  /** REST filters */
44
  include_once( dirname( __FILE__ ) . '/wp-includes/filters.php' );
45
 
26
 
27
  /** Main API functions */
28
  include_once( dirname( __FILE__ ) . '/wp-includes/functions.php' );
 
29
 
30
  /** WP_REST_Server class */
31
  include_once( dirname( __FILE__ ) . '/wp-includes/rest-api/class-wp-rest-server.php' );
39
  /** WP_REST_Request class */
40
  require_once( dirname( __FILE__ ) . '/wp-includes/rest-api/class-wp-rest-request.php' );
41
 
42
+ /** REST functions */
43
+ include_once( dirname( __FILE__ ) . '/wp-includes/rest-api/rest-functions.php' );
44
+
45
  /** REST filters */
46
  include_once( dirname( __FILE__ ) . '/wp-includes/filters.php' );
47
 
core/wp-includes/rest-api.php CHANGED
@@ -4,7 +4,6 @@
4
  *
5
  * @package WordPress
6
  * @subpackage REST_API
7
- * @since 4.4.0
8
  */
9
 
10
  /**
@@ -14,637 +13,14 @@
14
  */
15
  define( 'REST_API_VERSION', '2.0' );
16
 
17
- /**
18
- * Registers a REST API route.
19
- *
20
- * @since 4.4.0
21
- *
22
- * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
23
- *
24
- * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
25
- * @param string $route The base URL for route you are adding.
26
- * @param array $args Optional. Either an array of options for the endpoint, or an array of arrays for
27
- * multiple methods. Default empty array.
28
- * @param bool $override Optional. If the route already exists, should we override it? True overrides,
29
- * false merges (with newer overriding if duplicate keys exist). Default false.
30
- * @return bool True on success, false on error.
31
- */
32
- function register_rest_route( $namespace, $route, $args = array(), $override = false ) {
33
- /** @var WP_REST_Server $wp_rest_server */
34
- global $wp_rest_server;
35
-
36
- if ( empty( $namespace ) ) {
37
- /*
38
- * Non-namespaced routes are not allowed, with the exception of the main
39
- * and namespace indexes. If you really need to register a
40
- * non-namespaced route, call `WP_REST_Server::register_route` directly.
41
- */
42
- _doing_it_wrong( 'register_rest_route', __( 'Routes must be namespaced with plugin or theme name and version.' ), '4.4.0' );
43
- return false;
44
- } else if ( empty( $route ) ) {
45
- _doing_it_wrong( 'register_rest_route', __( 'Route must be specified.' ), '4.4.0' );
46
- return false;
47
- }
48
-
49
- if ( isset( $args['callback'] ) ) {
50
- // Upgrade a single set to multiple.
51
- $args = array( $args );
52
- }
53
-
54
- $defaults = array(
55
- 'methods' => 'GET',
56
- 'callback' => null,
57
- 'args' => array(),
58
- );
59
- foreach ( $args as $key => &$arg_group ) {
60
- if ( ! is_numeric( $arg_group ) ) {
61
- // Route option, skip here.
62
- continue;
63
- }
64
-
65
- $arg_group = array_merge( $defaults, $arg_group );
66
- }
67
-
68
- $full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' );
69
- $wp_rest_server->register_route( $namespace, $full_route, $args, $override );
70
- return true;
71
- }
72
-
73
- /**
74
- * Registers rewrite rules for the API.
75
- *
76
- * @since 4.4.0
77
- *
78
- * @see rest_api_register_rewrites()
79
- * @global WP $wp Current WordPress environment instance.
80
- */
81
- function rest_api_init() {
82
- rest_api_register_rewrites();
83
-
84
- global $wp;
85
- $wp->add_query_var( 'rest_route' );
86
- }
87
-
88
- /**
89
- * Adds REST rewrite rules.
90
- *
91
- * @since 4.4.0
92
- *
93
- * @see add_rewrite_rule()
94
- */
95
- function rest_api_register_rewrites() {
96
- add_rewrite_rule( '^' . rest_get_url_prefix() . '/?$','index.php?rest_route=/','top' );
97
- add_rewrite_rule( '^' . rest_get_url_prefix() . '/(.*)?','index.php?rest_route=/$matches[1]','top' );
98
- }
99
-
100
- /**
101
- * Registers the default REST API filters.
102
- *
103
- * Attached to the {@see 'rest_api_init'} action
104
- * to make testing and disabling these filters easier.
105
- *
106
- * @since 4.4.0
107
- */
108
- function rest_api_default_filters() {
109
- // Deprecated reporting.
110
- add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 );
111
- add_filter( 'deprecated_function_trigger_error', '__return_false' );
112
- add_action( 'deprecated_argument_run', 'rest_handle_deprecated_argument', 10, 3 );
113
- add_filter( 'deprecated_argument_trigger_error', '__return_false' );
114
-
115
- // Default serving.
116
- add_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
117
- add_filter( 'rest_post_dispatch', 'rest_send_allow_header', 10, 3 );
118
-
119
- add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 );
120
- }
121
-
122
- /**
123
- * Loads the REST API.
124
- *
125
- * @since 4.4.0
126
- *
127
- * @global WP $wp Current WordPress environment instance.
128
- * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
129
- */
130
- function rest_api_loaded() {
131
- if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
132
- return;
133
- }
134
-
135
- /**
136
- * Whether this is a REST Request.
137
- *
138
- * @since 4.4.0
139
- * @var bool
140
- */
141
- define( 'REST_REQUEST', true );
142
-
143
- /** @var WP_REST_Server $wp_rest_server */
144
- global $wp_rest_server;
145
-
146
- /**
147
- * Filter the REST Server Class.
148
- *
149
- * This filter allows you to adjust the server class used by the API, using a
150
- * different class to handle requests.
151
- *
152
- * @since 4.4.0
153
- *
154
- * @param string $class_name The name of the server class. Default 'WP_REST_Server'.
155
- */
156
- $wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' );
157
- $wp_rest_server = new $wp_rest_server_class;
158
-
159
- /**
160
- * Fires when preparing to serve an API request.
161
- *
162
- * Endpoint objects should be created and register their hooks on this action rather
163
- * than another action to ensure they're only loaded when needed.
164
- *
165
- * @since 4.4.0
166
- *
167
- * @param WP_REST_Server $wp_rest_server Server object.
168
- */
169
- do_action( 'rest_api_init', $wp_rest_server );
170
-
171
- // Fire off the request.
172
- $wp_rest_server->serve_request( $GLOBALS['wp']->query_vars['rest_route'] );
173
-
174
- // We're done.
175
- die();
176
- }
177
-
178
- /**
179
- * Retrieves the URL prefix for any API resource.
180
- *
181
- * @since 4.4.0
182
- *
183
- * @return string Prefix.
184
- */
185
- function rest_get_url_prefix() {
186
- /**
187
- * Filter the REST URL prefix.
188
- *
189
- * @since 4.4.0
190
- *
191
- * @param string $prefix URL prefix. Default 'wp-json'.
192
- */
193
- return apply_filters( 'rest_url_prefix', 'wp-json' );
194
- }
195
-
196
- /**
197
- * Retrieves the URL to a REST endpoint on a site.
198
- *
199
- * Note: The returned URL is NOT escaped.
200
- *
201
- * @since 4.4.0
202
- *
203
- * @todo Check if this is even necessary
204
- *
205
- * @param int $blog_id Optional. Blog ID. Default of null returns URL for current blog.
206
- * @param string $path Optional. REST route. Default '/'.
207
- * @param string $scheme Optional. Sanitization scheme. Default 'rest'.
208
- * @return string Full URL to the endpoint.
209
- */
210
- function get_rest_url( $blog_id = null, $path = '/', $scheme = 'rest' ) {
211
- if ( empty( $path ) ) {
212
- $path = '/';
213
- }
214
-
215
- if ( is_multisite() && get_blog_option( $blog_id, 'permalink_structure' ) || get_option( 'permalink_structure' ) ) {
216
- $url = get_home_url( $blog_id, rest_get_url_prefix(), $scheme );
217
- $url .= '/' . ltrim( $path, '/' );
218
- } else {
219
- $url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
220
-
221
- $path = '/' . ltrim( $path, '/' );
222
-
223
- $url = add_query_arg( 'rest_route', $path, $url );
224
- }
225
-
226
- if ( is_ssl() ) {
227
- // If the current host is the same as the REST URL host, force the REST URL scheme to HTTPS.
228
- if ( $_SERVER['SERVER_NAME'] === parse_url( get_home_url( $blog_id ), PHP_URL_HOST ) ) {
229
- $url = set_url_scheme( $url, 'https' );
230
- }
231
- }
232
-
233
- /**
234
- * Filter the REST URL.
235
- *
236
- * Use this filter to adjust the url returned by the `get_rest_url` function.
237
- *
238
- * @since 4.4.0
239
- *
240
- * @param string $url REST URL.
241
- * @param string $path REST route.
242
- * @param int $blog_id Blog ID.
243
- * @param string $scheme Sanitization scheme.
244
- */
245
- return apply_filters( 'rest_url', $url, $path, $blog_id, $scheme );
246
- }
247
-
248
- /**
249
- * Retrieves the URL to a REST endpoint.
250
- *
251
- * Note: The returned URL is NOT escaped.
252
- *
253
- * @since 4.4.0
254
- *
255
- * @param string $path Optional. REST route. Default empty.
256
- * @param string $scheme Optional. Sanitization scheme. Default 'json'.
257
- * @return string Full URL to the endpoint.
258
- */
259
- function rest_url( $path = '', $scheme = 'json' ) {
260
- return get_rest_url( null, $path, $scheme );
261
- }
262
-
263
- /**
264
- * Do a REST request.
265
- *
266
- * Used primarily to route internal requests through WP_REST_Server.
267
- *
268
- * @since 4.4.0
269
- *
270
- * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
271
- *
272
- * @param WP_REST_Request|string $request Request.
273
- * @return WP_REST_Response REST response.
274
- */
275
- function rest_do_request( $request ) {
276
- global $wp_rest_server;
277
- $request = rest_ensure_request( $request );
278
- return $wp_rest_server->dispatch( $request );
279
- }
280
-
281
- /**
282
- * Ensures request arguments are a request object (for consistency).
283
- *
284
- * @since 4.4.0
285
- *
286
- * @param array|WP_REST_Request $request Request to check.
287
- * @return WP_REST_Request REST request instance.
288
- */
289
- function rest_ensure_request( $request ) {
290
- if ( $request instanceof WP_REST_Request ) {
291
- return $request;
292
- }
293
-
294
- return new WP_REST_Request( 'GET', '', $request );
295
- }
296
-
297
- /**
298
- * Ensures a REST response is a response object (for consistency).
299
- *
300
- * This implements WP_HTTP_Response, allowing usage of `set_status`/`header`/etc
301
- * without needing to double-check the object. Will also allow WP_Error to indicate error
302
- * responses, so users should immediately check for this value.
303
- *
304
- * @since 4.4.0
305
- *
306
- * @param WP_Error|WP_HTTP_Response|mixed $response Response to check.
307
- * @return mixed WP_Error if response generated an error, WP_HTTP_Response if response
308
- * is a already an instance, otherwise returns a new WP_REST_Response instance.
309
- */
310
- function rest_ensure_response( $response ) {
311
- if ( is_wp_error( $response ) ) {
312
- return $response;
313
- }
314
-
315
- if ( $response instanceof WP_HTTP_Response ) {
316
- return $response;
317
- }
318
-
319
- return new WP_REST_Response( $response );
320
- }
321
-
322
- /**
323
- * Handles _deprecated_function() errors.
324
- *
325
- * @since 4.4.0
326
- *
327
- * @param string $function Function name.
328
- * @param string $replacement Replacement function name.
329
- * @param string $version Version.
330
- */
331
- function rest_handle_deprecated_function( $function, $replacement, $version ) {
332
- if ( ! empty( $replacement ) ) {
333
- /* translators: 1: function name, 2: WordPress version number, 3: new function name */
334
- $string = sprintf( __( '%1$s (since %2$s; use %3$s instead)' ), $function, $version, $replacement );
335
- } else {
336
- /* translators: 1: function name, 2: WordPress version number */
337
- $string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
338
- }
339
-
340
- header( sprintf( 'X-WP-DeprecatedFunction: %s', $string ) );
341
- }
342
-
343
- /**
344
- * Handles _deprecated_argument() errors.
345
- *
346
- * @since 4.4.0
347
- *
348
- * @param string $function Function name.
349
- * @param string $replacement Replacement function name.
350
- * @param string $version Version.
351
- */
352
- function rest_handle_deprecated_argument( $function, $replacement, $version ) {
353
- if ( ! empty( $replacement ) ) {
354
- /* translators: 1: function name, 2: WordPress version number, 3: new argument name */
355
- $string = sprintf( __( '%1$s (since %2$s; %3$s)' ), $function, $version, $replacement );
356
- } else {
357
- /* translators: 1: function name, 2: WordPress version number */
358
- $string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
359
- }
360
-
361
- header( sprintf( 'X-WP-DeprecatedParam: %s', $string ) );
362
- }
363
-
364
- /**
365
- * Sends Cross-Origin Resource Sharing headers with API requests.
366
- *
367
- * @since 4.4.0
368
- *
369
- * @param mixed $value Response data.
370
- * @return mixed Response data.
371
- */
372
- function rest_send_cors_headers( $value ) {
373
- $origin = get_http_origin();
374
-
375
- if ( $origin ) {
376
- header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
377
- header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' );
378
- header( 'Access-Control-Allow-Credentials: true' );
379
- }
380
-
381
- return $value;
382
- }
383
-
384
- /**
385
- * Handles OPTIONS requests for the server.
386
- *
387
- * This is handled outside of the server code, as it doesn't obey normal route
388
- * mapping.
389
- *
390
- * @since 4.4.0
391
- *
392
- * @param mixed $response Current response, either response or `null` to indicate pass-through.
393
- * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server).
394
- * @param WP_REST_Request $request The request that was used to make current response.
395
- * @return WP_REST_Response Modified response, either response or `null` to indicate pass-through.
396
- */
397
- function rest_handle_options_request( $response, $handler, $request ) {
398
- if ( ! empty( $response ) || $request->get_method() !== 'OPTIONS' ) {
399
- return $response;
400
- }
401
-
402
- $response = new WP_REST_Response();
403
- $data = array();
404
-
405
- $accept = array();
406
-
407
- foreach ( $handler->get_routes() as $route => $endpoints ) {
408
- $match = preg_match( '@^' . $route . '$@i', $request->get_route(), $args );
409
-
410
- if ( ! $match ) {
411
- continue;
412
- }
413
-
414
- $data = $handler->get_data_for_route( $route, $endpoints, 'help' );
415
- $accept = array_merge( $accept, $data['methods'] );
416
- break;
417
- }
418
- $response->header( 'Accept', implode( ', ', $accept ) );
419
-
420
- $response->set_data( $data );
421
- return $response;
422
- }
423
-
424
- /**
425
- * Sends the "Allow" header to state all methods that can be sent to the current route.
426
- *
427
- * @since 4.4.0
428
- *
429
- * @param WP_REST_Response $response Current response being served.
430
- * @param WP_REST_Server $server ResponseHandler instance (usually WP_REST_Server).
431
- * @param WP_REST_Request $request The request that was used to make current response.
432
- * @return WP_REST_Response Response to be served, with "Allow" header if route has allowed methods.
433
- */
434
- function rest_send_allow_header( $response, $server, $request ) {
435
- $matched_route = $response->get_matched_route();
436
-
437
- if ( ! $matched_route ) {
438
- return $response;
439
- }
440
-
441
- $routes = $server->get_routes();
442
-
443
- $allowed_methods = array();
444
-
445
- // Get the allowed methods across the routes.
446
- foreach ( $routes[ $matched_route ] as $_handler ) {
447
- foreach ( $_handler['methods'] as $handler_method => $value ) {
448
-
449
- if ( ! empty( $_handler['permission_callback'] ) ) {
450
-
451
- $permission = call_user_func( $_handler['permission_callback'], $request );
452
-
453
- $allowed_methods[ $handler_method ] = true === $permission;
454
- } else {
455
- $allowed_methods[ $handler_method ] = true;
456
- }
457
- }
458
- }
459
-
460
- // Strip out all the methods that are not allowed (false values).
461
- $allowed_methods = array_filter( $allowed_methods );
462
-
463
- if ( $allowed_methods ) {
464
- $response->header( 'Allow', implode( ', ', array_map( 'strtoupper', array_keys( $allowed_methods ) ) ) );
465
- }
466
-
467
- return $response;
468
- }
469
-
470
- /**
471
- * Adds the REST API URL to the WP RSD endpoint.
472
- *
473
- * @since 4.4.0
474
- *
475
- * @see get_rest_url()
476
- */
477
- function rest_output_rsd() {
478
- $api_root = get_rest_url();
479
-
480
- if ( empty( $api_root ) ) {
481
- return;
482
- }
483
- ?>
484
- <api name="WP-API" blogID="1" preferred="false" apiLink="<?php echo esc_url( $api_root ); ?>" />
485
- <?php
486
- }
487
-
488
- /**
489
- * Outputs the REST API link tag into page header.
490
- *
491
- * @since 4.4.0
492
- *
493
- * @see get_rest_url()
494
- */
495
- function rest_output_link_wp_head() {
496
- $api_root = get_rest_url();
497
-
498
- if ( empty( $api_root ) ) {
499
- return;
500
- }
501
-
502
- echo "<link rel='https://api.w.org/' href='" . esc_url( $api_root ) . "' />\n";
503
- }
504
-
505
- /**
506
- * Sends a Link header for the REST API.
507
- *
508
- * @since 4.4.0
509
- */
510
- function rest_output_link_header() {
511
- if ( headers_sent() ) {
512
- return;
513
- }
514
-
515
- $api_root = get_rest_url();
516
-
517
- if ( empty( $api_root ) ) {
518
- return;
519
- }
520
-
521
- header( 'Link: <' . esc_url_raw( $api_root ) . '>; rel="https://api.w.org/"', false );
522
- }
523
-
524
- /**
525
- * Checks for errors when using cookie-based authentication.
526
- *
527
- * WordPress' built-in cookie authentication is always active
528
- * for logged in users. However, the API has to check nonces
529
- * for each request to ensure users are not vulnerable to CSRF.
530
- *
531
- * @since 4.4.0
532
- *
533
- * @global mixed $wp_rest_auth_cookie
534
- *
535
- * @param WP_Error|mixed $result Error from another authentication handler, null if we should handle it,
536
- * or another value if not.
537
- * @return WP_Error|mixed|bool WP_Error if the cookie is invalid, the $result, otherwise true.
538
- */
539
- function rest_cookie_check_errors( $result ) {
540
- if ( ! empty( $result ) ) {
541
- return $result;
542
- }
543
-
544
- global $wp_rest_auth_cookie;
545
-
546
- /*
547
- * Is cookie authentication being used? (If we get an auth
548
- * error, but we're still logged in, another authentication
549
- * must have been used).
550
- */
551
- if ( true !== $wp_rest_auth_cookie && is_user_logged_in() ) {
552
- return $result;
553
- }
554
-
555
- // Determine if there is a nonce.
556
- $nonce = null;
557
-
558
- if ( isset( $_REQUEST['_wpnonce'] ) ) {
559
- $nonce = $_REQUEST['_wpnonce'];
560
- } elseif ( isset( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
561
- $nonce = $_SERVER['HTTP_X_WP_NONCE'];
562
- }
563
-
564
- if ( null === $nonce ) {
565
- // No nonce at all, so act as if it's an unauthenticated request.
566
- wp_set_current_user( 0 );
567
- return true;
568
- }
569
-
570
- // Check the nonce.
571
- $result = wp_verify_nonce( $nonce, 'wp_rest' );
572
-
573
- if ( ! $result ) {
574
- return new WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie nonce is invalid' ), array( 'status' => 403 ) );
575
- }
576
-
577
- return true;
578
- }
579
-
580
- /**
581
- * Collects cookie authentication status.
582
- *
583
- * Collects errors from wp_validate_auth_cookie for use by rest_cookie_check_errors.
584
- *
585
- * @since 4.4.0
586
- *
587
- * @see current_action()
588
- * @global mixed $wp_rest_auth_cookie
589
- */
590
- function rest_cookie_collect_status() {
591
- global $wp_rest_auth_cookie;
592
-
593
- $status_type = current_action();
594
-
595
- if ( 'auth_cookie_valid' !== $status_type ) {
596
- $wp_rest_auth_cookie = substr( $status_type, 12 );
597
- return;
598
- }
599
-
600
- $wp_rest_auth_cookie = true;
601
- }
602
-
603
- /**
604
- * Parses an RFC3339 timestamp into a DateTime.
605
- *
606
- * @since 4.4.0
607
- *
608
- * @param string $date RFC3339 timestamp.
609
- * @param bool $force_utc Optional. Whether to force UTC timezone instead of using
610
- * the timestamp's timezone. Default false.
611
- * @return DateTime DateTime instance.
612
- */
613
- function rest_parse_date( $date, $force_utc = false ) {
614
- if ( $force_utc ) {
615
- $date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
616
- }
617
-
618
- $regex = '#^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?$#';
619
-
620
- if ( ! preg_match( $regex, $date, $matches ) ) {
621
- return false;
622
- }
623
-
624
- return strtotime( $date );
625
- }
626
-
627
- /**
628
- * Retrieves a local date with its GMT equivalent, in MySQL datetime format.
629
- *
630
- * @since 4.4.0
631
- *
632
- * @see rest_parse_date()
633
- *
634
- * @param string $date RFC3339 timestamp.
635
- * @param bool $force_utc Whether a UTC timestamp should be forced. Default false.
636
- * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
637
- * null on failure.
638
- */
639
- function rest_get_date_with_gmt( $date, $force_utc = false ) {
640
- $date = rest_parse_date( $date, $force_utc );
641
 
642
- if ( empty( $date ) ) {
643
- return null;
644
- }
645
 
646
- $utc = date( 'Y-m-d H:i:s', $date );
647
- $local = get_date_from_gmt( $utc );
648
 
649
- return array( $local, $utc );
650
- }
4
  *
5
  * @package WordPress
6
  * @subpackage REST_API
 
7
  */
8
 
9
  /**
13
  */
14
  define( 'REST_API_VERSION', '2.0' );
15
 
16
+ /** WP_REST_Server class */
17
+ require_once( ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ /** WP_REST_Response class */
20
+ require_once( ABSPATH . WPINC . '/rest-api/class-wp-rest-response.php' );
 
21
 
22
+ /** WP_REST_Request class */
23
+ require_once( ABSPATH . WPINC . '/rest-api/class-wp-rest-request.php' );
24
 
25
+ /** REST functions */
26
+ require_once( ABSPATH . WPINC . '/rest-api/rest-functions.php' );
core/wp-includes/rest-api/class-wp-rest-request.php CHANGED
@@ -847,7 +847,7 @@ class WP_REST_Request implements ArrayAccess {
847
  $valid_check = call_user_func( $arg['validate_callback'], $param, $this, $key );
848
 
849
  if ( false === $valid_check ) {
850
- $invalid_params[ $key ] = __( 'Invalid parameter.' );
851
  }
852
 
853
  if ( is_wp_error( $valid_check ) ) {
847
  $valid_check = call_user_func( $arg['validate_callback'], $param, $this, $key );
848
 
849
  if ( false === $valid_check ) {
850
+ $invalid_params[ $key ] = __( 'Invalid param.' );
851
  }
852
 
853
  if ( is_wp_error( $valid_check ) ) {
core/wp-includes/rest-api/class-wp-rest-server.php CHANGED
@@ -236,20 +236,6 @@ class WP_REST_Server {
236
  $this->send_header( 'Access-Control-Expose-Headers', 'X-WP-Total, X-WP-TotalPages' );
237
  $this->send_header( 'Access-Control-Allow-Headers', 'Authorization' );
238
 
239
- /**
240
- * Send nocache headers on authenticated requests.
241
- *
242
- * @since 4.4.0
243
- *
244
- * @param bool $rest_send_nocache_headers Whether to send no-cache headers.
245
- */
246
- $send_no_cache_headers = apply_filters( 'rest_send_nocache_headers', is_user_logged_in() );
247
- if ( $send_no_cache_headers ) {
248
- foreach ( wp_get_nocache_headers() as $header => $header_value ) {
249
- $this->send_header( $header, $header_value );
250
- }
251
- }
252
-
253
  /**
254
  * Filter whether the REST API is enabled.
255
  *
@@ -794,8 +780,7 @@ class WP_REST_Server {
794
  $callback = $handler['callback'];
795
  $response = null;
796
 
797
- $checked_method = 'HEAD' === $method ? 'GET' : $method;
798
- if ( empty( $handler['methods'][ $checked_method ] ) ) {
799
  continue;
800
  }
801
 
@@ -1173,7 +1158,7 @@ class WP_REST_Server {
1173
  *
1174
  * @return string Raw request data.
1175
  */
1176
- public static function get_raw_data() {
1177
  global $HTTP_RAW_POST_DATA;
1178
 
1179
  /*
236
  $this->send_header( 'Access-Control-Expose-Headers', 'X-WP-Total, X-WP-TotalPages' );
237
  $this->send_header( 'Access-Control-Allow-Headers', 'Authorization' );
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  /**
240
  * Filter whether the REST API is enabled.
241
  *
780
  $callback = $handler['callback'];
781
  $response = null;
782
 
783
+ if ( empty( $handler['methods'][ $method ] ) ) {
 
784
  continue;
785
  }
786
 
1158
  *
1159
  * @return string Raw request data.
1160
  */
1161
+ public function get_raw_data() {
1162
  global $HTTP_RAW_POST_DATA;
1163
 
1164
  /*
core/wp-includes/rest-api/rest-functions.php ADDED
@@ -0,0 +1,643 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * REST API functions.
4
+ *
5
+ * @package WordPress
6
+ * @subpackage REST_API
7
+ * @since 4.4.0
8
+ */
9
+
10
+ /**
11
+ * Registers a REST API route.
12
+ *
13
+ * @since 4.4.0
14
+ *
15
+ * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
16
+ *
17
+ * @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
18
+ * @param string $route The base URL for route you are adding.
19
+ * @param array $args Optional. Either an array of options for the endpoint, or an array of arrays for
20
+ * multiple methods. Default empty array.
21
+ * @param bool $override Optional. If the route already exists, should we override it? True overrides,
22
+ * false merges (with newer overriding if duplicate keys exist). Default false.
23
+ * @return bool True on success, false on error.
24
+ */
25
+ function register_rest_route( $namespace, $route, $args = array(), $override = false ) {
26
+ /** @var WP_REST_Server $wp_rest_server */
27
+ global $wp_rest_server;
28
+
29
+ if ( empty( $namespace ) ) {
30
+ /*
31
+ * Non-namespaced routes are not allowed, with the exception of the main
32
+ * and namespace indexes. If you really need to register a
33
+ * non-namespaced route, call `WP_REST_Server::register_route` directly.
34
+ */
35
+ _doing_it_wrong( 'register_rest_route', 'Routes must be namespaced with plugin or theme name and version.', '4.4.0' );
36
+ return false;
37
+ } else if ( empty( $route ) ) {
38
+ _doing_it_wrong( 'register_rest_route', 'Route must be specified.', '4.4.0' );
39
+ return false;
40
+ }
41
+
42
+ if ( isset( $args['callback'] ) ) {
43
+ // Upgrade a single set to multiple.
44
+ $args = array( $args );
45
+ }
46
+
47
+ $defaults = array(
48
+ 'methods' => 'GET',
49
+ 'callback' => null,
50
+ 'args' => array(),
51
+ );
52
+ foreach ( $args as $key => &$arg_group ) {
53
+ if ( ! is_numeric( $arg_group ) ) {
54
+ // Route option, skip here.
55
+ continue;
56
+ }
57
+
58
+ $arg_group = array_merge( $defaults, $arg_group );
59
+ }
60
+
61
+ $full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' );
62
+ $wp_rest_server->register_route( $namespace, $full_route, $args, $override );
63
+ return true;
64
+ }
65
+
66
+ /**
67
+ * Registers rewrite rules for the API.
68
+ *
69
+ * @since 4.4.0
70
+ *
71
+ * @see rest_api_register_rewrites()
72
+ * @global WP $wp Current WordPress environment instance.
73
+ */
74
+ function rest_api_init() {
75
+ rest_api_register_rewrites();
76
+
77
+ global $wp;
78
+ $wp->add_query_var( 'rest_route' );
79
+ }
80
+
81
+ /**
82
+ * Adds REST rewrite rules.
83
+ *
84
+ * @since 4.4.0
85
+ *
86
+ * @see add_rewrite_rule()
87
+ */
88
+ function rest_api_register_rewrites() {
89
+ add_rewrite_rule( '^' . rest_get_url_prefix() . '/?$','index.php?rest_route=/','top' );
90
+ add_rewrite_rule( '^' . rest_get_url_prefix() . '/(.*)?','index.php?rest_route=/$matches[1]','top' );
91
+ }
92
+
93
+ /**
94
+ * Registers the default REST API filters.
95
+ *
96
+ * Attached to the {@see 'rest_api_init'} action
97
+ * to make testing and disabling these filters easier.
98
+ *
99
+ * @since 4.4.0
100
+ */
101
+ function rest_api_default_filters() {
102
+ // Deprecated reporting.
103
+ add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 );
104
+ add_filter( 'deprecated_function_trigger_error', '__return_false' );
105
+ add_action( 'deprecated_argument_run', 'rest_handle_deprecated_argument', 10, 3 );
106
+ add_filter( 'deprecated_argument_trigger_error', '__return_false' );
107
+
108
+ // Default serving.
109
+ add_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
110
+ add_filter( 'rest_post_dispatch', 'rest_send_allow_header', 10, 3 );
111
+
112
+ add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 );
113
+ }
114
+
115
+ /**
116
+ * Loads the REST API.
117
+ *
118
+ * @since 4.4.0
119
+ *
120
+ * @global WP $wp Current WordPress environment instance.
121
+ * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
122
+ */
123
+ function rest_api_loaded() {
124
+ if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
125
+ return;
126
+ }
127
+
128
+ /**
129
+ * Whether this is a REST Request.
130
+ *
131
+ * @since 4.4.0
132
+ * @var bool
133
+ */
134
+ define( 'REST_REQUEST', true );
135
+
136
+ /** @var WP_REST_Server $wp_rest_server */
137
+ global $wp_rest_server;
138
+
139
+ /**
140
+ * Filter the REST Server Class.
141
+ *
142
+ * This filter allows you to adjust the server class used by the API, using a
143
+ * different class to handle requests.
144
+ *
145
+ * @since 4.4.0
146
+ *
147
+ * @param string $class_name The name of the server class. Default 'WP_REST_Server'.
148
+ */
149
+ $wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' );
150
+ $wp_rest_server = new $wp_rest_server_class;
151
+
152
+ /**
153
+ * Fires when preparing to serve an API request.
154
+ *
155
+ * Endpoint objects should be created and register their hooks on this action rather
156
+ * than another action to ensure they're only loaded when needed.
157
+ *
158
+ * @since 4.4.0
159
+ *
160
+ * @param WP_REST_Server $wp_rest_server Server object.
161
+ */
162
+ do_action( 'rest_api_init', $wp_rest_server );
163
+
164
+ // Fire off the request.
165
+ $wp_rest_server->serve_request( $GLOBALS['wp']->query_vars['rest_route'] );
166
+
167
+ // We're done.
168
+ die();
169
+ }
170
+
171
+ /**
172
+ * Retrieves the URL prefix for any API resource.
173
+ *
174
+ * @since 4.4.0
175
+ *
176
+ * @return string Prefix.
177
+ */
178
+ function rest_get_url_prefix() {
179
+ /**
180
+ * Filter the REST URL prefix.
181
+ *
182
+ * @since 4.4.0
183
+ *
184
+ * @param string $prefix URL prefix. Default 'wp-json'.
185
+ */
186
+ return apply_filters( 'rest_url_prefix', 'wp-json' );
187
+ }
188
+
189
+ /**
190
+ * Retrieves the URL to a REST endpoint on a site.
191
+ *
192
+ * Note: The returned URL is NOT escaped.
193
+ *
194
+ * @since 4.4.0
195
+ *
196
+ * @todo Check if this is even necessary
197
+ *
198
+ * @param int $blog_id Optional. Blog ID. Default of null returns URL for current blog.
199
+ * @param string $path Optional. REST route. Default '/'.
200
+ * @param string $scheme Optional. Sanitization scheme. Default 'rest'.
201
+ * @return string Full URL to the endpoint.
202
+ */
203
+ function get_rest_url( $blog_id = null, $path = '/', $scheme = 'rest' ) {
204
+ if ( empty( $path ) ) {
205
+ $path = '/';
206
+ }
207
+
208
+ if ( is_multisite() && get_blog_option( $blog_id, 'permalink_structure' ) || get_option( 'permalink_structure' ) ) {
209
+ $url = get_home_url( $blog_id, rest_get_url_prefix(), $scheme );
210
+ $url .= '/' . ltrim( $path, '/' );
211
+ } else {
212
+ $url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
213
+
214
+ $path = '/' . ltrim( $path, '/' );
215
+
216
+ $url = add_query_arg( 'rest_route', $path, $url );
217
+ }
218
+
219
+ if ( is_ssl() ) {
220
+ // If the current host is the same as the REST URL host, force the REST URL scheme to HTTPS.
221
+ if ( $_SERVER['SERVER_NAME'] === parse_url( get_home_url( $blog_id ), PHP_URL_HOST ) ) {
222
+ $url = set_url_scheme( $url, 'https' );
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Filter the REST URL.
228
+ *
229
+ * Use this filter to adjust the url returned by the `get_rest_url` function.
230
+ *
231
+ * @since 4.4.0
232
+ *
233
+ * @param string $url REST URL.
234
+ * @param string $path REST route.
235
+ * @param int $blog_id Blog ID.
236
+ * @param string $scheme Sanitization scheme.
237
+ */
238
+ return apply_filters( 'rest_url', $url, $path, $blog_id, $scheme );
239
+ }
240
+
241
+ /**
242
+ * Retrieves the URL to a REST endpoint.
243
+ *
244
+ * Note: The returned URL is NOT escaped.
245
+ *
246
+ * @since 4.4.0
247
+ *
248
+ * @param string $path Optional. REST route. Default empty.
249
+ * @param string $scheme Optional. Sanitization scheme. Default 'json'.
250
+ * @return string Full URL to the endpoint.
251
+ */
252
+ function rest_url( $path = '', $scheme = 'json' ) {
253
+ return get_rest_url( null, $path, $scheme );
254
+ }
255
+
256
+ /**
257
+ * Do a REST request.
258
+ *
259
+ * Used primarily to route internal requests through WP_REST_Server.
260
+ *
261
+ * @since 4.4.0
262
+ *
263
+ * @global WP_REST_Server $wp_rest_server ResponseHandler instance (usually WP_REST_Server).
264
+ *
265
+ * @param WP_REST_Request|string $request Request.
266
+ * @return WP_REST_Response REST response.
267
+ */
268
+ function rest_do_request( $request ) {
269
+ global $wp_rest_server;
270
+ $request = rest_ensure_request( $request );
271
+ return $wp_rest_server->dispatch( $request );
272
+ }
273
+
274
+ /**
275
+ * Ensures request arguments are a request object (for consistency).
276
+ *
277
+ * @since 4.4.0
278
+ *
279
+ * @param array|WP_REST_Request $request Request to check.
280
+ * @return WP_REST_Request REST request instance.
281
+ */
282
+ function rest_ensure_request( $request ) {
283
+ if ( $request instanceof WP_REST_Request ) {
284
+ return $request;
285
+ }
286
+
287
+ return new WP_REST_Request( 'GET', '', $request );
288
+ }
289
+
290
+ /**
291
+ * Ensures a REST response is a response object (for consistency).
292
+ *
293
+ * This implements WP_HTTP_Response, allowing usage of `set_status`/`header`/etc
294
+ * without needing to double-check the object. Will also allow WP_Error to indicate error
295
+ * responses, so users should immediately check for this value.
296
+ *
297
+ * @since 4.4.0
298
+ *
299
+ * @param WP_Error|WP_HTTP_Response|mixed $response Response to check.
300
+ * @return mixed WP_Error if response generated an error, WP_HTTP_Response if response
301
+ * is a already an instance, otherwise returns a new WP_REST_Response instance.
302
+ */
303
+ function rest_ensure_response( $response ) {
304
+ if ( is_wp_error( $response ) ) {
305
+ return $response;
306
+ }
307
+
308
+ if ( $response instanceof WP_HTTP_Response ) {
309
+ return $response;
310
+ }
311
+
312
+ return new WP_REST_Response( $response );
313
+ }
314
+
315
+ /**
316
+ * Handles _deprecated_function() errors.
317
+ *
318
+ * @since 4.4.0
319
+ *
320
+ * @param string $function Function name.
321
+ * @param string $replacement Replacement function name.
322
+ * @param string $version Version.
323
+ */
324
+ function rest_handle_deprecated_function( $function, $replacement, $version ) {
325
+ if ( ! empty( $replacement ) ) {
326
+ /* translators: 1: function name, 2: WordPress version number, 3: new function name */
327
+ $string = sprintf( __( '%1$s (since %2$s; use %3$s instead)' ), $function, $version, $replacement );
328
+ } else {
329
+ /* translators: 1: function name, 2: WordPress version number */
330
+ $string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
331
+ }
332
+
333
+ header( sprintf( 'X-WP-DeprecatedFunction: %s', $string ) );
334
+ }
335
+
336
+ /**
337
+ * Handles _deprecated_argument() errors.
338
+ *
339
+ * @since 4.4.0
340
+ *
341
+ * @param string $function Function name.
342
+ * @param string $replacement Replacement function name.
343
+ * @param string $version Version.
344
+ */
345
+ function rest_handle_deprecated_argument( $function, $replacement, $version ) {
346
+ if ( ! empty( $replacement ) ) {
347
+ /* translators: 1: function name, 2: WordPress version number, 3: new argument name */
348
+ $string = sprintf( __( '%1$s (since %2$s; %3$s)' ), $function, $version, $replacement );
349
+ } else {
350
+ /* translators: 1: function name, 2: WordPress version number */
351
+ $string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
352
+ }
353
+
354
+ header( sprintf( 'X-WP-DeprecatedParam: %s', $string ) );
355
+ }
356
+
357
+ /**
358
+ * Sends Cross-Origin Resource Sharing headers with API requests.
359
+ *
360
+ * @since 4.4.0
361
+ *
362
+ * @param mixed $value Response data.
363
+ * @return mixed Response data.
364
+ */
365
+ function rest_send_cors_headers( $value ) {
366
+ $origin = get_http_origin();
367
+
368
+ if ( $origin ) {
369
+ header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
370
+ header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' );
371
+ header( 'Access-Control-Allow-Credentials: true' );
372
+ }
373
+
374
+ return $value;
375
+ }
376
+
377
+ /**
378
+ * Handles OPTIONS requests for the server.
379
+ *
380
+ * This is handled outside of the server code, as it doesn't obey normal route
381
+ * mapping.
382
+ *
383
+ * @since 4.4.0
384
+ *
385
+ * @param mixed $response Current response, either response or `null` to indicate pass-through.
386
+ * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server).
387
+ * @param WP_REST_Request $request The request that was used to make current response.
388
+ * @return WP_REST_Response Modified response, either response or `null` to indicate pass-through.
389
+ */
390
+ function rest_handle_options_request( $response, $handler, $request ) {
391
+ if ( ! empty( $response ) || $request->get_method() !== 'OPTIONS' ) {
392
+ return $response;
393
+ }
394
+
395
+ $response = new WP_REST_Response();
396
+ $data = array();
397
+
398
+ $accept = array();
399
+
400
+ foreach ( $handler->get_routes() as $route => $endpoints ) {
401
+ $match = preg_match( '@^' . $route . '$@i', $request->get_route(), $args );
402
+
403
+ if ( ! $match ) {
404
+ continue;
405
+ }
406
+
407
+ $data = $handler->get_data_for_route( $route, $endpoints, 'help' );
408
+ $accept = array_merge( $accept, $data['methods'] );
409
+ break;
410
+ }
411
+ $response->header( 'Accept', implode( ', ', $accept ) );
412
+
413
+ $response->set_data( $data );
414
+ return $response;
415
+ }
416
+
417
+ /**
418
+ * Sends the "Allow" header to state all methods that can be sent to the current route.
419
+ *
420
+ * @since 4.4.0
421
+ *
422
+ * @param WP_REST_Response $response Current response being served.
423
+ * @param WP_REST_Server $server ResponseHandler instance (usually WP_REST_Server).
424
+ * @param WP_REST_Request $request The request that was used to make current response.
425
+ * @return WP_REST_Response Response to be served, with "Allow" header if route has allowed methods.
426
+ */
427
+ function rest_send_allow_header( $response, $server, $request ) {
428
+ $matched_route = $response->get_matched_route();
429
+
430
+ if ( ! $matched_route ) {
431
+ return $response;
432
+ }
433
+
434
+ $routes = $server->get_routes();
435
+
436
+ $allowed_methods = array();
437
+
438
+ // Get the allowed methods across the routes.
439
+ foreach ( $routes[ $matched_route ] as $_handler ) {
440
+ foreach ( $_handler['methods'] as $handler_method => $value ) {
441
+
442
+ if ( ! empty( $_handler['permission_callback'] ) ) {
443
+
444
+ $permission = call_user_func( $_handler['permission_callback'], $request );
445
+
446
+ $allowed_methods[ $handler_method ] = true === $permission;
447
+ } else {
448
+ $allowed_methods[ $handler_method ] = true;
449
+ }
450
+ }
451
+ }
452
+
453
+ // Strip out all the methods that are not allowed (false values).
454
+ $allowed_methods = array_filter( $allowed_methods );
455
+
456
+ if ( $allowed_methods ) {
457
+ $response->header( 'Allow', implode( ', ', array_map( 'strtoupper', array_keys( $allowed_methods ) ) ) );
458
+ }
459
+
460
+ return $response;
461
+ }
462
+
463
+ /**
464
+ * Adds the REST API URL to the WP RSD endpoint.
465
+ *
466
+ * @since 4.4.0
467
+ *
468
+ * @see get_rest_url()
469
+ */
470
+ function rest_output_rsd() {
471
+ $api_root = get_rest_url();
472
+
473
+ if ( empty( $api_root ) ) {
474
+ return;
475
+ }
476
+ ?>
477
+ <api name="WP-API" blogID="1" preferred="false" apiLink="<?php echo esc_url( $api_root ); ?>" />
478
+ <?php
479
+ }
480
+
481
+ /**
482
+ * Outputs the REST API link tag into page header.
483
+ *
484
+ * @since 4.4.0
485
+ *
486
+ * @see get_rest_url()
487
+ */
488
+ function rest_output_link_wp_head() {
489
+ $api_root = get_rest_url();
490
+
491
+ if ( empty( $api_root ) ) {
492
+ return;
493
+ }
494
+
495
+ echo "<link rel='https://api.w.org/' href='" . esc_url( $api_root ) . "' />\n";
496
+ }
497
+
498
+ /**
499
+ * Sends a Link header for the REST API.
500
+ *
501
+ * @since 4.4.0
502
+ */
503
+ function rest_output_link_header() {
504
+ if ( headers_sent() ) {
505
+ return;
506
+ }
507
+
508
+ $api_root = get_rest_url();
509
+
510
+ if ( empty( $api_root ) ) {
511
+ return;
512
+ }
513
+
514
+ header( 'Link: <' . esc_url_raw( $api_root ) . '>; rel="https://api.w.org/"', false );
515
+ }
516
+
517
+ /**
518
+ * Checks for errors when using cookie-based authentication.
519
+ *
520
+ * WordPress' built-in cookie authentication is always active
521
+ * for logged in users. However, the API has to check nonces
522
+ * for each request to ensure users are not vulnerable to CSRF.
523
+ *
524
+ * @since 4.4.0
525
+ *
526
+ * @global mixed $wp_rest_auth_cookie
527
+ *
528
+ * @param WP_Error|mixed $result Error from another authentication handler, null if we should handle it,
529
+ * or another value if not.
530
+ * @return WP_Error|mixed|bool WP_Error if the cookie is invalid, the $result, otherwise true.
531
+ */
532
+ function rest_cookie_check_errors( $result ) {
533
+ if ( ! empty( $result ) ) {
534
+ return $result;
535
+ }
536
+
537
+ global $wp_rest_auth_cookie;
538
+
539
+ /*
540
+ * Is cookie authentication being used? (If we get an auth
541
+ * error, but we're still logged in, another authentication
542
+ * must have been used).
543
+ */
544
+ if ( true !== $wp_rest_auth_cookie && is_user_logged_in() ) {
545
+ return $result;
546
+ }
547
+
548
+ // Determine if there is a nonce.
549
+ $nonce = null;
550
+
551
+ if ( isset( $_REQUEST['_wpnonce'] ) ) {
552
+ $nonce = $_REQUEST['_wpnonce'];
553
+ } elseif ( isset( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
554
+ $nonce = $_SERVER['HTTP_X_WP_NONCE'];
555
+ }
556
+
557
+ if ( null === $nonce ) {
558
+ // No nonce at all, so act as if it's an unauthenticated request.
559
+ wp_set_current_user( 0 );
560
+ return true;
561
+ }
562
+
563
+ // Check the nonce.
564
+ $result = wp_verify_nonce( $nonce, 'wp_rest' );
565
+
566
+ if ( ! $result ) {
567
+ return new WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie nonce is invalid' ), array( 'status' => 403 ) );
568
+ }
569
+
570
+ return true;
571
+ }
572
+
573
+ /**
574
+ * Collects cookie authentication status.
575
+ *
576
+ * Collects errors from wp_validate_auth_cookie for use by rest_cookie_check_errors.
577
+ *
578
+ * @since 4.4.0
579
+ *
580
+ * @see current_action()
581
+ * @global mixed $wp_rest_auth_cookie
582
+ */
583
+ function rest_cookie_collect_status() {
584
+ global $wp_rest_auth_cookie;
585
+
586
+ $status_type = current_action();
587
+
588
+ if ( 'auth_cookie_valid' !== $status_type ) {
589
+ $wp_rest_auth_cookie = substr( $status_type, 12 );
590
+ return;
591
+ }
592
+
593
+ $wp_rest_auth_cookie = true;
594
+ }
595
+
596
+ /**
597
+ * Parses an RFC3339 timestamp into a DateTime.
598
+ *
599
+ * @since 4.4.0
600
+ *
601
+ * @param string $date RFC3339 timestamp.
602
+ * @param bool $force_utc Optional. Whether to force UTC timezone instead of using
603
+ * the timestamp's timezone. Default false.
604
+ * @return DateTime DateTime instance.
605
+ */
606
+ function rest_parse_date( $date, $force_utc = false ) {
607
+ if ( $force_utc ) {
608
+ $date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
609
+ }
610
+
611
+ $regex = '#^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?$#';
612
+
613
+ if ( ! preg_match( $regex, $date, $matches ) ) {
614
+ return false;
615
+ }
616
+
617
+ return strtotime( $date );
618
+ }
619
+
620
+ /**
621
+ * Retrieves a local date with its GMT equivalent, in MySQL datetime format.
622
+ *
623
+ * @since 4.4.0
624
+ *
625
+ * @see rest_parse_date()
626
+ *
627
+ * @param string $date RFC3339 timestamp.
628
+ * @param bool $force_utc Whether a UTC timestamp should be forced. Default false.
629
+ * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),
630
+ * null on failure.
631
+ */
632
+ function rest_get_date_with_gmt( $date, $force_utc = false ) {
633
+ $date = rest_parse_date( $date, $force_utc );
634
+
635
+ if ( empty( $date ) ) {
636
+ return null;
637
+ }
638
+
639
+ $utc = date( 'Y-m-d H:i:s', $date );
640
+ $local = get_date_from_gmt( $utc );
641
+
642
+ return array( $local, $utc );
643
+ }
extras.php CHANGED
@@ -68,6 +68,38 @@ function rest_get_avatar_sizes() {
68
  return apply_filters( 'rest_avatar_sizes', array( 24, 48, 96 ) );
69
  }
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /**
72
  * Retrieves the avatar url for a user who provided a user ID or email address.
73
  *
68
  return apply_filters( 'rest_avatar_sizes', array( 24, 48, 96 ) );
69
  }
70
 
71
+ /**
72
+ * Retrieves the timezone object for the site.
73
+ *
74
+ * @since 4.4.0
75
+ *
76
+ * @return DateTimeZone DateTimeZone instance.
77
+ */
78
+ function rest_get_timezone() {
79
+ static $zone = null;
80
+
81
+ if ( null !== $zone ) {
82
+ return $zone;
83
+ }
84
+
85
+ $tzstring = get_option( 'timezone_string' );
86
+
87
+ if ( ! $tzstring ) {
88
+ // Create a UTC+- zone if no timezone string exists.
89
+ $current_offset = get_option( 'gmt_offset' );
90
+ if ( 0 === $current_offset ) {
91
+ $tzstring = 'UTC';
92
+ } elseif ( $current_offset < 0 ) {
93
+ $tzstring = 'Etc/GMT' . $current_offset;
94
+ } else {
95
+ $tzstring = 'Etc/GMT+' . $current_offset;
96
+ }
97
+ }
98
+ $zone = new DateTimeZone( $tzstring );
99
+
100
+ return $zone;
101
+ }
102
+
103
  /**
104
  * Retrieves the avatar url for a user who provided a user ID or email address.
105
  *
lib/endpoints/class-wp-rest-attachments-controller.php CHANGED
@@ -21,7 +21,7 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
21
  $parent = get_post( (int) $request['post'] );
22
  $post_parent_type = get_post_type_object( $parent->post_type );
23
  if ( ! current_user_can( $post_parent_type->cap->edit_post, $request['post'] ) ) {
24
- return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) );
25
  }
26
  }
27
 
@@ -183,31 +183,15 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
183
  $img_url_basename = wp_basename( $data['source_url'] );
184
 
185
  foreach ( $data['media_details']['sizes'] as $size => &$size_data ) {
186
-
187
- if ( isset( $size_data['mime-type'] ) ) {
188
- $size_data['mime_type'] = $size_data['mime-type'];
189
- unset( $size_data['mime-type'] );
190
- }
191
-
192
  // Use the same method image_downsize() does
193
  $image_src = wp_get_attachment_image_src( $post->ID, $size );
 
194
  if ( ! $image_src ) {
195
  continue;
196
  }
197
 
198
  $size_data['source_url'] = $image_src[0];
199
  }
200
-
201
- $full_src = wp_get_attachment_image_src( $post->ID, 'full' );
202
- if ( ! empty( $full_src ) ) {
203
- $data['media_details']['sizes']['full'] = array(
204
- 'file' => wp_basename( $full_src[0] ),
205
- 'width' => $full_src[1],
206
- 'height' => $full_src[2],
207
- 'mime_type' => $post->post_mime_type,
208
- 'source_url' => $full_src[0],
209
- );
210
- }
211
  } else {
212
  $data['media_details']['sizes'] = new stdClass;
213
  }
@@ -217,20 +201,21 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
217
  $data = $this->filter_response_by_context( $data, $context );
218
 
219
  // Wrap the data in a response object
220
- $response = rest_ensure_response( $data );
221
 
222
- $response->add_links( $this->prepare_links( $post ) );
223
 
224
  /**
225
  * Filter an attachment returned from the API.
226
  *
227
  * Allows modification of the attachment right before it is returned.
228
  *
229
- * @param WP_REST_Response $response The response object.
230
- * @param WP_Post $post The original attachment post.
231
- * @param WP_REST_Request $request Request used to generate the response.
 
232
  */
233
- return apply_filters( 'rest_prepare_attachment', $response, $post, $request );
234
  }
235
 
236
  /**
@@ -280,7 +265,7 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
280
  'readonly' => true,
281
  );
282
  $schema['properties']['post'] = array(
283
- 'description' => 'The id for the associated post of the attachment.',
284
  'type' => 'integer',
285
  'context' => array( 'view', 'edit' ),
286
  );
21
  $parent = get_post( (int) $request['post'] );
22
  $post_parent_type = get_post_type_object( $parent->post_type );
23
  if ( ! current_user_can( $post_parent_type->cap->edit_post, $request['post'] ) ) {
24
+ return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => 401 ) );
25
  }
26
  }
27
 
183
  $img_url_basename = wp_basename( $data['source_url'] );
184
 
185
  foreach ( $data['media_details']['sizes'] as $size => &$size_data ) {
 
 
 
 
 
 
186
  // Use the same method image_downsize() does
187
  $image_src = wp_get_attachment_image_src( $post->ID, $size );
188
+
189
  if ( ! $image_src ) {
190
  continue;
191
  }
192
 
193
  $size_data['source_url'] = $image_src[0];
194
  }
 
 
 
 
 
 
 
 
 
 
 
195
  } else {
196
  $data['media_details']['sizes'] = new stdClass;
197
  }
201
  $data = $this->filter_response_by_context( $data, $context );
202
 
203
  // Wrap the data in a response object
204
+ $data = rest_ensure_response( $data );
205
 
206
+ $data->add_links( $this->prepare_links( $post ) );
207
 
208
  /**
209
  * Filter an attachment returned from the API.
210
  *
211
  * Allows modification of the attachment right before it is returned.
212
  *
213
+ * @param array $data Key value array of attachment data: alt_text, caption, description,
214
+ * media_type, media_details, post, source_url. Piossibly media_details.
215
+ * @param WP_Post $post The attachment post.
216
+ * @param WP_REST_Request $request Request used to generate the response.
217
  */
218
+ return apply_filters( 'rest_prepare_attachment', $data, $post, $request );
219
  }
220
 
221
  /**
265
  'readonly' => true,
266
  );
267
  $schema['properties']['post'] = array(
268
+ 'description' => 'The ID for the associated post of the attachment.',
269
  'type' => 'integer',
270
  'context' => array( 'view', 'edit' ),
271
  );
lib/endpoints/class-wp-rest-comments-controller.php CHANGED
@@ -10,12 +10,13 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
10
  */
11
  public function register_routes() {
12
 
 
13
  register_rest_route( 'wp/v2', '/comments', array(
14
  array(
15
  'methods' => WP_REST_Server::READABLE,
16
  'callback' => array( $this, 'get_items' ),
17
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
18
- 'args' => $this->get_collection_params(),
19
  ),
20
  array(
21
  'methods' => WP_REST_Server::CREATABLE,
@@ -33,7 +34,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
33
  'callback' => array( $this, 'get_item' ),
34
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
35
  'args' => array(
36
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
 
 
37
  ),
38
  ),
39
  array(
@@ -118,12 +121,12 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
118
 
119
  $comment = get_comment( $id );
120
  if ( empty( $comment ) ) {
121
- return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment id.' ), array( 'status' => 404 ) );
122
  }
123
 
124
  $post = get_post( $comment->comment_post_ID );
125
  if ( empty( $post ) ) {
126
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
127
  }
128
 
129
  $data = $this->prepare_item_for_response( $comment, $request );
@@ -145,7 +148,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
145
 
146
  $post = get_post( $request['post'] );
147
  if ( empty( $post ) ) {
148
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
149
  }
150
 
151
  $prepared_comment = $this->prepare_item_for_database( $request );
@@ -237,7 +240,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
237
 
238
  $comment = get_comment( $id );
239
  if ( empty( $comment ) ) {
240
- return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment id.' ), array( 'status' => 404 ) );
241
  }
242
 
243
  if ( isset( $request['type'] ) && $request['type'] !== $comment->comment_type ) {
@@ -290,7 +293,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
290
 
291
  $comment = get_comment( $id );
292
  if ( empty( $comment ) ) {
293
- return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment id.' ), array( 'status' => 404 ) );
294
  }
295
 
296
  /**
@@ -362,7 +365,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
362
  }
363
 
364
  if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'manage_comments' ) ) {
365
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view comments with edit context.' ), array( 'status' => rest_authorization_required_code() ) );
366
  }
367
 
368
  return true;
@@ -384,17 +387,17 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
384
  }
385
 
386
  if ( ! $this->check_read_permission( $comment ) ) {
387
- return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot read this comment.' ), array( 'status' => rest_authorization_required_code() ) );
388
  }
389
 
390
  $post = get_post( $comment->comment_post_ID );
391
 
392
  if ( $post && ! $this->check_read_post_permission( $post ) ) {
393
- return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) );
394
  }
395
 
396
  if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
397
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view this comment with edit context.' ), array( 'status' => rest_authorization_required_code() ) );
398
  }
399
 
400
  return true;
@@ -408,19 +411,15 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
408
  */
409
  public function create_item_permissions_check( $request ) {
410
 
411
- if ( ! is_user_logged_in() && get_option( 'comment_registration' ) ) {
412
- return new WP_Error( 'rest_comment_login_required', __( 'Sorry, you must be logged in to comment.' ), array( 'status' => 401 ) );
413
- }
414
-
415
  // Limit who can set comment `author`, `karma` or `status` to anything other than the default.
416
  if ( isset( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( 'moderate_comments' ) ) {
417
- return new WP_Error( 'rest_comment_invalid_author', __( 'Comment author invalid.' ), array( 'status' => rest_authorization_required_code() ) );
418
  }
419
  if ( isset( $request['karma'] ) && $request['karma'] > 0 && ! current_user_can( 'moderate_comments' ) ) {
420
- return new WP_Error( 'rest_comment_invalid_karma', __( 'Sorry, you cannot set karma for comments.' ), array( 'status' => rest_authorization_required_code() ) );
421
  }
422
  if ( isset( $request['status'] ) && ! current_user_can( 'moderate_comments' ) ) {
423
- return new WP_Error( 'rest_comment_invalid_status', __( 'Sorry, you cannot set status for comments.' ), array( 'status' => rest_authorization_required_code() ) );
424
  }
425
 
426
  // If the post id isn't specified, presume we can create.
@@ -433,7 +432,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
433
  if ( $post ) {
434
 
435
  if ( ! $this->check_read_post_permission( $post ) ) {
436
- return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) );
437
  }
438
 
439
  if ( ! comments_open( $post->ID ) ) {
@@ -457,7 +456,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
457
  $comment = get_comment( $id );
458
 
459
  if ( $comment && ! $this->check_edit_permission( $comment ) ) {
460
- return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you can not edit this comment.' ), array( 'status' => rest_authorization_required_code() ) );
461
  }
462
 
463
  return true;
@@ -509,20 +508,11 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
509
  $data = $this->add_additional_fields_to_object( $data, $request );
510
 
511
  // Wrap the data in a response object
512
- $response = rest_ensure_response( $data );
513
 
514
- $response->add_links( $this->prepare_links( $comment ) );
515
 
516
- /**
517
- * Filter a comment returned from the API.
518
- *
519
- * Allows modification of the comment right before it is returned.
520
- *
521
- * @param WP_REST_Response $response The response object.
522
- * @param object $comment The original comment object.
523
- * @param WP_REST_Request $request Request used to generate the response.
524
- */
525
- return apply_filters( 'rest_prepare_comment', $response, $comment, $request );
526
  }
527
 
528
  /**
@@ -768,7 +758,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
768
  'readonly' => true,
769
  ),
770
  'author' => array(
771
- 'description' => 'The id of the user object, if author was a user.',
772
  'type' => 'integer',
773
  'context' => array( 'view', 'edit', 'embed' ),
774
  ),
@@ -858,7 +848,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
858
  'readonly' => true,
859
  ),
860
  'parent' => array(
861
- 'description' => 'The id for the parent of the object.',
862
  'type' => 'integer',
863
  'context' => array( 'view', 'edit', 'embed' ),
864
  'arg_options' => array(
@@ -866,7 +856,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
866
  ),
867
  ),
868
  'post' => array(
869
- 'description' => 'The id of the associated post object.',
870
  'type' => 'integer',
871
  'context' => array( 'view', 'edit' ),
872
  ),
@@ -899,9 +889,6 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
899
  */
900
  public function get_collection_params() {
901
  $query_params = parent::get_collection_params();
902
-
903
- $query_params['context']['default'] = 'view';
904
-
905
  $query_params['author_email'] = array(
906
  'default' => null,
907
  'description' => 'Limit result set to that from a specific author email.',
10
  */
11
  public function register_routes() {
12
 
13
+ $query_params = $this->get_collection_params();
14
  register_rest_route( 'wp/v2', '/comments', array(
15
  array(
16
  'methods' => WP_REST_Server::READABLE,
17
  'callback' => array( $this, 'get_items' ),
18
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
19
+ 'args' => $query_params,
20
  ),
21
  array(
22
  'methods' => WP_REST_Server::CREATABLE,
34
  'callback' => array( $this, 'get_item' ),
35
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
36
  'args' => array(
37
+ 'context' => array(
38
+ 'default' => 'view',
39
+ ),
40
  ),
41
  ),
42
  array(
121
 
122
  $comment = get_comment( $id );
123
  if ( empty( $comment ) ) {
124
+ return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) );
125
  }
126
 
127
  $post = get_post( $comment->comment_post_ID );
128
  if ( empty( $post ) ) {
129
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
130
  }
131
 
132
  $data = $this->prepare_item_for_response( $comment, $request );
148
 
149
  $post = get_post( $request['post'] );
150
  if ( empty( $post ) ) {
151
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
152
  }
153
 
154
  $prepared_comment = $this->prepare_item_for_database( $request );
240
 
241
  $comment = get_comment( $id );
242
  if ( empty( $comment ) ) {
243
+ return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) );
244
  }
245
 
246
  if ( isset( $request['type'] ) && $request['type'] !== $comment->comment_type ) {
293
 
294
  $comment = get_comment( $id );
295
  if ( empty( $comment ) ) {
296
+ return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) );
297
  }
298
 
299
  /**
365
  }
366
 
367
  if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'manage_comments' ) ) {
368
+ return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view comments with edit context.' ), array( 'status' => 403 ) );
369
  }
370
 
371
  return true;
387
  }
388
 
389
  if ( ! $this->check_read_permission( $comment ) ) {
390
+ return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot read this comment.' ), array( 'status' => 403 ) );
391
  }
392
 
393
  $post = get_post( $comment->comment_post_ID );
394
 
395
  if ( $post && ! $this->check_read_post_permission( $post ) ) {
396
+ return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => 403 ) );
397
  }
398
 
399
  if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
400
+ return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view this comment with edit context.' ), array( 'status' => 403 ) );
401
  }
402
 
403
  return true;
411
  */
412
  public function create_item_permissions_check( $request ) {
413
 
 
 
 
 
414
  // Limit who can set comment `author`, `karma` or `status` to anything other than the default.
415
  if ( isset( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( 'moderate_comments' ) ) {
416
+ return new WP_Error( 'rest_comment_invalid_author', __( 'Comment author invalid.' ), array( 'status' => 403 ) );
417
  }
418
  if ( isset( $request['karma'] ) && $request['karma'] > 0 && ! current_user_can( 'moderate_comments' ) ) {
419
+ return new WP_Error( 'rest_comment_invalid_karma', __( 'Sorry, you cannot set karma for comments.' ), array( 'status' => 403 ) );
420
  }
421
  if ( isset( $request['status'] ) && ! current_user_can( 'moderate_comments' ) ) {
422
+ return new WP_Error( 'rest_comment_invalid_status', __( 'Sorry, you cannot set status for comments.' ), array( 'status' => 403 ) );
423
  }
424
 
425
  // If the post id isn't specified, presume we can create.
432
  if ( $post ) {
433
 
434
  if ( ! $this->check_read_post_permission( $post ) ) {
435
+ return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => 403 ) );
436
  }
437
 
438
  if ( ! comments_open( $post->ID ) ) {
456
  $comment = get_comment( $id );
457
 
458
  if ( $comment && ! $this->check_edit_permission( $comment ) ) {
459
+ return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you can not edit this comment.' ), array( 'status' => 403 ) );
460
  }
461
 
462
  return true;
508
  $data = $this->add_additional_fields_to_object( $data, $request );
509
 
510
  // Wrap the data in a response object
511
+ $data = rest_ensure_response( $data );
512
 
513
+ $data->add_links( $this->prepare_links( $comment ) );
514
 
515
+ return apply_filters( 'rest_prepare_comment', $data, $comment, $request );
 
 
 
 
 
 
 
 
 
516
  }
517
 
518
  /**
758
  'readonly' => true,
759
  ),
760
  'author' => array(
761
+ 'description' => 'The ID of the user object, if author was a user.',
762
  'type' => 'integer',
763
  'context' => array( 'view', 'edit', 'embed' ),
764
  ),
848
  'readonly' => true,
849
  ),
850
  'parent' => array(
851
+ 'description' => 'The ID for the parent of the object.',
852
  'type' => 'integer',
853
  'context' => array( 'view', 'edit', 'embed' ),
854
  'arg_options' => array(
856
  ),
857
  ),
858
  'post' => array(
859
+ 'description' => 'The ID of the associated post object.',
860
  'type' => 'integer',
861
  'context' => array( 'view', 'edit' ),
862
  ),
889
  */
890
  public function get_collection_params() {
891
  $query_params = parent::get_collection_params();
 
 
 
892
  $query_params['author_email'] = array(
893
  'default' => null,
894
  'description' => 'Limit result set to that from a specific author email.',
lib/endpoints/class-wp-rest-controller.php CHANGED
@@ -219,7 +219,6 @@ abstract class WP_REST_Controller {
219
  */
220
  public function get_collection_params() {
221
  return array(
222
- 'context' => $this->get_context_param(),
223
  'page' => array(
224
  'description' => 'Current page of the collection.',
225
  'type' => 'integer',
@@ -240,37 +239,6 @@ abstract class WP_REST_Controller {
240
  );
241
  }
242
 
243
- /**
244
- * Get the magical context param.
245
- *
246
- * Ensures consistent description between endpoints, and populates enum from schema.
247
- *
248
- * @param array $args
249
- * @return array
250
- */
251
- public function get_context_param( $args = array() ) {
252
- $param_details = array(
253
- 'description' => 'Scope under which the request is made; determines fields present in response.',
254
- 'type' => 'string',
255
- );
256
- $schema = $this->get_item_schema();
257
- $contexts = array();
258
- if ( empty( $schema['properties'] ) ) {
259
- return array_merge( $param_details, $args );
260
- }
261
- $contexts = array();
262
- foreach ( $schema['properties'] as $key => $attributes ) {
263
- if ( ! empty( $attributes['context'] ) ) {
264
- $contexts = array_merge( $contexts, $attributes['context'] );
265
- }
266
- }
267
- if ( ! empty( $contexts ) ) {
268
- $param_details['enum'] = array_unique( $contexts );
269
- rsort( $param_details['enum'] );
270
- }
271
- return array_merge( $param_details, $args );
272
- }
273
-
274
  /**
275
  * Add the values from additional fields to a data object.
276
  *
219
  */
220
  public function get_collection_params() {
221
  return array(
 
222
  'page' => array(
223
  'description' => 'Current page of the collection.',
224
  'type' => 'integer',
239
  );
240
  }
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  /**
243
  * Add the values from additional fields to a data object.
244
  *
lib/endpoints/class-wp-rest-meta-controller.php CHANGED
@@ -40,7 +40,11 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
40
  'methods' => WP_REST_Server::READABLE,
41
  'callback' => array( $this, 'get_items' ),
42
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
43
- 'args' => $this->get_collection_params(),
 
 
 
 
44
  ),
45
  array(
46
  'methods' => WP_REST_Server::CREATABLE,
@@ -57,7 +61,9 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
57
  'callback' => array( $this, 'get_item' ),
58
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
59
  'args' => array(
60
- 'context' => $this->get_context_param( array( 'default' => 'edit' ) ),
 
 
61
  ),
62
  ),
63
  array(
@@ -120,19 +126,6 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
120
  return $schema;
121
  }
122
 
123
- /**
124
- * Get the query params for collections
125
- *
126
- * @return array
127
- */
128
- public function get_collection_params() {
129
- $params = parent::get_collection_params();
130
- $new_params = array();
131
- $new_params['context'] = $params['context'];
132
- $new_params['context']['default'] = 'edit';
133
- return $new_params;
134
- }
135
-
136
  /**
137
  * Get the meta ID column for the relevant table.
138
  *
@@ -198,7 +191,7 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
198
  $meta = get_metadata_by_mid( $this->parent_type, $mid );
199
 
200
  if ( empty( $meta ) ) {
201
- return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta id.' ), array( 'status' => 404 ) );
202
  }
203
 
204
  if ( absint( $meta->$parent_column ) !== $parent_id ) {
@@ -272,7 +265,7 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
272
  $current = get_metadata_by_mid( $this->parent_type, $mid );
273
 
274
  if ( empty( $current ) ) {
275
- return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta id.' ), array( 'status' => 404 ) );
276
  }
277
 
278
  if ( absint( $current->$parent_column ) !== $parent_id ) {
@@ -406,7 +399,7 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
406
 
407
  $response->set_status( 201 );
408
  $data = $response->get_data();
409
- $response->header( 'Location', rest_url( 'wp/v2' . '/' . $this->parent_base . '/' . $parent_id . '/meta/' . $data['id'] ) );
410
 
411
  /* This action is documented in lib/endpoints/class-wp-rest-meta-controller.php */
412
  do_action( 'rest_insert_meta', $data, $request, true );
@@ -434,7 +427,7 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
434
  $current = get_metadata_by_mid( $this->parent_type, $mid );
435
 
436
  if ( empty( $current ) ) {
437
- return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta id.' ), array( 'status' => 404 ) );
438
  }
439
 
440
  if ( absint( $current->$parent_column ) !== (int) $parent_id ) {
40
  'methods' => WP_REST_Server::READABLE,
41
  'callback' => array( $this, 'get_items' ),
42
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
43
+ 'args' => array(
44
+ 'context' => array(
45
+ 'default' => 'view',
46
+ ),
47
+ ),
48
  ),
49
  array(
50
  'methods' => WP_REST_Server::CREATABLE,
61
  'callback' => array( $this, 'get_item' ),
62
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
63
  'args' => array(
64
+ 'context' => array(
65
+ 'default' => 'view',
66
+ ),
67
  ),
68
  ),
69
  array(
126
  return $schema;
127
  }
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  /**
130
  * Get the meta ID column for the relevant table.
131
  *
191
  $meta = get_metadata_by_mid( $this->parent_type, $mid );
192
 
193
  if ( empty( $meta ) ) {
194
+ return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta ID.' ), array( 'status' => 404 ) );
195
  }
196
 
197
  if ( absint( $meta->$parent_column ) !== $parent_id ) {
265
  $current = get_metadata_by_mid( $this->parent_type, $mid );
266
 
267
  if ( empty( $current ) ) {
268
+ return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta ID.' ), array( 'status' => 404 ) );
269
  }
270
 
271
  if ( absint( $current->$parent_column ) !== $parent_id ) {
399
 
400
  $response->set_status( 201 );
401
  $data = $response->get_data();
402
+ $response->header( 'Location', rest_url( $this->parent_base . '/' . $parent_id . '/meta/' . $data['id'] ) );
403
 
404
  /* This action is documented in lib/endpoints/class-wp-rest-meta-controller.php */
405
  do_action( 'rest_insert_meta', $data, $request, true );
427
  $current = get_metadata_by_mid( $this->parent_type, $mid );
428
 
429
  if ( empty( $current ) ) {
430
+ return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta ID.' ), array( 'status' => 404 ) );
431
  }
432
 
433
  if ( absint( $current->$parent_column ) !== (int) $parent_id ) {
lib/endpoints/class-wp-rest-meta-posts-controller.php CHANGED
@@ -45,16 +45,16 @@ class WP_REST_Meta_Posts_Controller extends WP_REST_Meta_Controller {
45
  $parent = get_post( (int) $request['parent_id'] );
46
 
47
  if ( empty( $parent ) || empty( $parent->ID ) ) {
48
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
49
  }
50
 
51
  if ( ! $this->parent_controller->check_read_permission( $parent ) ) {
52
- return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view this post.' ), array( 'status' => rest_authorization_required_code() ) );
53
  }
54
 
55
  $post_type = get_post_type_object( $parent->post_type );
56
  if ( ! current_user_can( $post_type->cap->edit_post, $parent->ID ) ) {
57
- return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view the meta for this post.' ), array( 'status' => rest_authorization_required_code() ) );
58
  }
59
  return true;
60
  }
@@ -99,16 +99,16 @@ class WP_REST_Meta_Posts_Controller extends WP_REST_Meta_Controller {
99
  $parent = get_post( (int) $request['parent_id'] );
100
 
101
  if ( empty( $parent ) || empty( $parent->ID ) ) {
102
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
103
  }
104
 
105
  if ( ! $this->parent_controller->check_read_permission( $parent ) ) {
106
- return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view this post.' ), array( 'status' => rest_authorization_required_code() ) );
107
  }
108
 
109
  $post_type = get_post_type_object( $parent->post_type );
110
  if ( ! current_user_can( $post_type->cap->delete_post, $parent->ID ) ) {
111
- return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot delete the meta for this post.' ), array( 'status' => rest_authorization_required_code() ) );
112
  }
113
  return true;
114
  }
45
  $parent = get_post( (int) $request['parent_id'] );
46
 
47
  if ( empty( $parent ) || empty( $parent->ID ) ) {
48
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
49
  }
50
 
51
  if ( ! $this->parent_controller->check_read_permission( $parent ) ) {
52
+ return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view this post.' ), array( 'status' => 403 ) );
53
  }
54
 
55
  $post_type = get_post_type_object( $parent->post_type );
56
  if ( ! current_user_can( $post_type->cap->edit_post, $parent->ID ) ) {
57
+ return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view the meta for this post.' ), array( 'status' => 403 ) );
58
  }
59
  return true;
60
  }
99
  $parent = get_post( (int) $request['parent_id'] );
100
 
101
  if ( empty( $parent ) || empty( $parent->ID ) ) {
102
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
103
  }
104
 
105
  if ( ! $this->parent_controller->check_read_permission( $parent ) ) {
106
+ return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view this post.' ), array( 'status' => 403 ) );
107
  }
108
 
109
  $post_type = get_post_type_object( $parent->post_type );
110
  if ( ! current_user_can( $post_type->cap->delete_post, $parent->ID ) ) {
111
+ return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot delete the meta for this post.' ), array( 'status' => 403 ) );
112
  }
113
  return true;
114
  }
lib/endpoints/class-wp-rest-post-statuses-controller.php CHANGED
@@ -11,7 +11,6 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
11
  array(
12
  'methods' => WP_REST_Server::READABLE,
13
  'callback' => array( $this, 'get_items' ),
14
- 'args' => $this->get_collection_params(),
15
  ),
16
  'schema' => array( $this, 'get_public_item_schema' ),
17
  ) );
@@ -20,9 +19,6 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
20
  array(
21
  'methods' => WP_REST_Server::READABLE,
22
  'callback' => array( $this, 'get_item' ),
23
- 'args' => array(
24
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
25
- ),
26
  ),
27
  'schema' => array( $this, 'get_public_item_schema' ),
28
  ) );
@@ -74,7 +70,7 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
74
  */
75
  public function prepare_item_for_response( $status, $request ) {
76
  if ( ( false === $status->public && ! is_user_logged_in() ) || ( true === $status->internal && is_user_logged_in() ) ) {
77
- return new WP_Error( 'rest_cannot_read_status', __( 'Cannot view status.' ), array( 'status' => rest_authorization_required_code() ) );
78
  }
79
 
80
  $data = array(
@@ -91,26 +87,17 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
91
  $data = $this->filter_response_by_context( $data, $context );
92
  $data = $this->add_additional_fields_to_object( $data, $request );
93
 
94
- $response = rest_ensure_response( $data );
95
 
96
  $posts_controller = new WP_REST_Posts_Controller( 'post' );
97
 
98
  if ( 'publish' === $status->name ) {
99
- $response->add_link( 'archives', rest_url( '/wp/v2/' . $posts_controller->get_post_type_base( 'post' ) ) );
100
  } else {
101
- $response->add_link( 'archives', add_query_arg( 'status', $status->name, rest_url( '/wp/v2/' . $posts_controller->get_post_type_base( 'post' ) ) ) );
102
  }
103
 
104
- /**
105
- * Filter a status returned from the API.
106
- *
107
- * Allows modification of the status data right before it is returned.
108
- *
109
- * @param WP_REST_Response $response The response object.
110
- * @param object $status The original status object.
111
- * @param WP_REST_Request $request Request used to generate the response.
112
- */
113
- return apply_filters( 'rest_prepare_status', $response, $status, $request );
114
  }
115
 
116
  /**
@@ -164,15 +151,4 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
164
  return $this->add_additional_fields_schema( $schema );
165
  }
166
 
167
- /**
168
- * Get the query params for collections
169
- *
170
- * @return array
171
- */
172
- public function get_collection_params() {
173
- return array(
174
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
175
- );
176
- }
177
-
178
  }
11
  array(
12
  'methods' => WP_REST_Server::READABLE,
13
  'callback' => array( $this, 'get_items' ),
 
14
  ),
15
  'schema' => array( $this, 'get_public_item_schema' ),
16
  ) );
19
  array(
20
  'methods' => WP_REST_Server::READABLE,
21
  'callback' => array( $this, 'get_item' ),
 
 
 
22
  ),
23
  'schema' => array( $this, 'get_public_item_schema' ),
24
  ) );
70
  */
71
  public function prepare_item_for_response( $status, $request ) {
72
  if ( ( false === $status->public && ! is_user_logged_in() ) || ( true === $status->internal && is_user_logged_in() ) ) {
73
+ return new WP_Error( 'rest_cannot_read_status', __( 'Cannot view status.' ), array( 'status' => 403 ) );
74
  }
75
 
76
  $data = array(
87
  $data = $this->filter_response_by_context( $data, $context );
88
  $data = $this->add_additional_fields_to_object( $data, $request );
89
 
90
+ $data = rest_ensure_response( $data );
91
 
92
  $posts_controller = new WP_REST_Posts_Controller( 'post' );
93
 
94
  if ( 'publish' === $status->name ) {
95
+ $data->add_link( 'archives', rest_url( '/wp/v2/' . $posts_controller->get_post_type_base( 'post' ) ) );
96
  } else {
97
+ $data->add_link( 'archives', add_query_arg( 'status', $status->name, rest_url( '/wp/v2/' . $posts_controller->get_post_type_base( 'post' ) ) ) );
98
  }
99
 
100
+ return $data;
 
 
 
 
 
 
 
 
 
101
  }
102
 
103
  /**
151
  return $this->add_additional_fields_schema( $schema );
152
  }
153
 
 
 
 
 
 
 
 
 
 
 
 
154
  }
lib/endpoints/class-wp-rest-post-types-controller.php CHANGED
@@ -11,7 +11,11 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
11
  array(
12
  'methods' => WP_REST_Server::READABLE,
13
  'callback' => array( $this, 'get_items' ),
14
- 'args' => $this->get_collection_params(),
 
 
 
 
15
  ),
16
  'schema' => array( $this, 'get_public_item_schema' ),
17
  ) );
@@ -20,9 +24,6 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
20
  array(
21
  'methods' => WP_REST_Server::READABLE,
22
  'callback' => array( $this, 'get_item' ),
23
- 'args' => array(
24
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
25
- ),
26
  ),
27
  'schema' => array( $this, 'get_public_item_schema' ),
28
  ) );
@@ -41,7 +42,7 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
41
  if ( is_wp_error( $post_type ) ) {
42
  continue;
43
  }
44
- $data[ $obj->name ] = $this->prepare_response_for_collection( $post_type );
45
  }
46
  return $data;
47
  }
@@ -69,7 +70,7 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
69
  */
70
  public function prepare_item_for_response( $post_type, $request ) {
71
  if ( false === $post_type->public ) {
72
- return new WP_Error( 'rest_cannot_read_type', __( 'Cannot view type.' ), array( 'status' => rest_authorization_required_code() ) );
73
  }
74
 
75
  $data = array(
@@ -83,29 +84,7 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
83
  $data = $this->filter_response_by_context( $data, $context );
84
  $data = $this->add_additional_fields_to_object( $data, $request );
85
 
86
- // Wrap the data in a response object.
87
- $response = rest_ensure_response( $data );
88
-
89
- $base = ! empty( $post_type->rest_base ) ? $post_type->rest_base : $post_type->name;
90
- $response->add_links( array(
91
- 'collection' => array(
92
- 'href' => rest_url( 'wp/v2/types' ),
93
- ),
94
- 'item' => array(
95
- 'href' => rest_url( sprintf( 'wp/v2/%s', $base ) ),
96
- ),
97
- ) );
98
-
99
- /**
100
- * Filter a post type returned from the API.
101
- *
102
- * Allows modification of the post type data right before it is returned.
103
- *
104
- * @param WP_REST_Response $response The response object.
105
- * @param object $item The original post type object.
106
- * @param WP_REST_Request $request Request used to generate the response.
107
- */
108
- return apply_filters( 'rest_prepare_post_type', $response, $post_type, $request );
109
  }
110
 
111
  /**
@@ -149,15 +128,4 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
149
  return $this->add_additional_fields_schema( $schema );
150
  }
151
 
152
- /**
153
- * Get the query params for collections
154
- *
155
- * @return array
156
- */
157
- public function get_collection_params() {
158
- return array(
159
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
160
- );
161
- }
162
-
163
  }
11
  array(
12
  'methods' => WP_REST_Server::READABLE,
13
  'callback' => array( $this, 'get_items' ),
14
+ 'args' => array(
15
+ 'post_type' => array(
16
+ 'sanitize_callback' => 'sanitize_key',
17
+ ),
18
+ ),
19
  ),
20
  'schema' => array( $this, 'get_public_item_schema' ),
21
  ) );
24
  array(
25
  'methods' => WP_REST_Server::READABLE,
26
  'callback' => array( $this, 'get_item' ),
 
 
 
27
  ),
28
  'schema' => array( $this, 'get_public_item_schema' ),
29
  ) );
42
  if ( is_wp_error( $post_type ) ) {
43
  continue;
44
  }
45
+ $data[ $obj->name ] = $post_type;
46
  }
47
  return $data;
48
  }
70
  */
71
  public function prepare_item_for_response( $post_type, $request ) {
72
  if ( false === $post_type->public ) {
73
+ return new WP_Error( 'rest_cannot_read_type', __( 'Cannot view type.' ), array( 'status' => 403 ) );
74
  }
75
 
76
  $data = array(
84
  $data = $this->filter_response_by_context( $data, $context );
85
  $data = $this->add_additional_fields_to_object( $data, $request );
86
 
87
+ return $data;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
  /**
128
  return $this->add_additional_fields_schema( $schema );
129
  }
130
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
lib/endpoints/class-wp-rest-posts-controller.php CHANGED
@@ -15,12 +15,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
15
 
16
  $base = $this->get_post_type_base( $this->post_type );
17
 
 
 
18
  register_rest_route( 'wp/v2', '/' . $base, array(
19
  array(
20
  'methods' => WP_REST_Server::READABLE,
21
  'callback' => array( $this, 'get_items' ),
22
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
23
- 'args' => $this->get_collection_params(),
24
  ),
25
  array(
26
  'methods' => WP_REST_Server::CREATABLE,
@@ -37,7 +39,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
37
  'callback' => array( $this, 'get_item' ),
38
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
39
  'args' => array(
40
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
 
 
41
  ),
42
  ),
43
  array(
@@ -69,12 +73,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
69
  */
70
  public function get_items( $request ) {
71
  $args = array();
72
- $args['author'] = $request['author'];
73
  $args['paged'] = $request['page'];
74
  $args['posts_per_page'] = $request['per_page'];
75
  $args['post_parent'] = $request['parent'];
76
- $args['post_status'] = $request['status'];
77
- $args['s'] = $request['search'];
78
 
79
  if ( is_array( $request['filter'] ) ) {
80
  $args = array_merge( $args, $request['filter'] );
@@ -147,7 +148,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
147
  $post = get_post( $id );
148
 
149
  if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
150
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
151
  }
152
 
153
  $data = $this->prepare_item_for_response( $post, $request );
@@ -243,7 +244,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
243
  $post = get_post( $id );
244
 
245
  if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
246
- return new WP_Error( 'rest_post_invalid_id', __( 'Post id is invalid.' ), array( 'status' => 400 ) );
247
  }
248
 
249
  $post = $this->prepare_item_for_database( $request );
@@ -314,7 +315,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
314
  $post = get_post( $id );
315
 
316
  if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
317
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
318
  }
319
 
320
  $supports_trash = ( EMPTY_TRASH_DAYS > 0 );
@@ -333,7 +334,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
333
  $supports_trash = apply_filters( 'rest_post_trashable', $supports_trash, $post );
334
 
335
  if ( ! $this->check_delete_permission( $post ) ) {
336
- return new WP_Error( 'rest_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
337
  }
338
 
339
  $request = new WP_REST_Request( 'GET', '/wp/v2/' . $this->get_post_type_base( $this->post_type ) . '/' . $post->ID );
@@ -395,7 +396,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
395
  $post_type = get_post_type_object( $this->post_type );
396
 
397
  if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
398
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts in this post type' ), array( 'status' => rest_authorization_required_code() ) );
399
  }
400
 
401
  return true;
@@ -412,7 +413,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
412
  $post = get_post( (int) $request['id'] );
413
 
414
  if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
415
- return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post' ), array( 'status' => rest_authorization_required_code() ) );
416
  }
417
 
418
  if ( $post ) {
@@ -433,15 +434,15 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
433
  $post_type = get_post_type_object( $this->post_type );
434
 
435
  if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
436
- return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' => rest_authorization_required_code() ) );
437
  }
438
 
439
  if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
440
- return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to create posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
441
  }
442
 
443
  if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
444
- return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
445
  }
446
 
447
  return current_user_can( $post_type->cap->create_posts );
@@ -463,15 +464,15 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
463
  }
464
 
465
  if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
466
- return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' => rest_authorization_required_code() ) );
467
  }
468
 
469
  if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
470
- return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to update posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
471
  }
472
 
473
  if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
474
- return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
475
  }
476
 
477
  return true;
@@ -488,7 +489,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
488
  $post = get_post( $request['id'] );
489
 
490
  if ( $post && ! $this->check_delete_permission( $post ) ) {
491
- return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete posts.' ), array( 'status' => rest_authorization_required_code() ) );
492
  }
493
 
494
  return true;
@@ -523,10 +524,6 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
523
  $query_args['post_status'] = 'inherit';
524
  }
525
 
526
- if ( 'post' !== $this->post_type || ! isset( $query_args['ignore_sticky_posts'] ) ) {
527
- $query_args['ignore_sticky_posts'] = true;
528
- }
529
-
530
  return $query_args;
531
  }
532
 
@@ -646,7 +643,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
646
  * Prepare a single post for create or update.
647
  *
648
  * @param WP_REST_Request $request Request object.
649
- * @return WP_Error|object $prepared_post Post object.
650
  */
651
  protected function prepare_item_for_database( $request ) {
652
  $prepared_post = new stdClass;
@@ -762,7 +759,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
762
  if ( ! empty( $schema['properties']['parent'] ) && ! empty( $request['parent'] ) ) {
763
  $parent = get_post( (int) $request['parent'] );
764
  if ( empty( $parent ) ) {
765
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent id.' ), array( 'status' => 400 ) );
766
  }
767
 
768
  $prepared_post->post_parent = (int) $parent->ID;
@@ -812,13 +809,13 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
812
  break;
813
  case 'private':
814
  if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
815
- return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create private posts in this post type' ), array( 'status' => rest_authorization_required_code() ) );
816
  }
817
  break;
818
  case 'publish':
819
  case 'future':
820
  if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
821
- return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type' ), array( 'status' => rest_authorization_required_code() ) );
822
  }
823
  break;
824
  default:
@@ -854,7 +851,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
854
  $author = get_userdata( $post_author );
855
 
856
  if ( ! $author ) {
857
- return new WP_Error( 'rest_invalid_author', __( 'Invalid author id.' ), array( 'status' => 400 ) );
858
  }
859
  }
860
 
@@ -875,7 +872,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
875
  if ( $result ) {
876
  return true;
877
  } else {
878
- return new WP_Error( 'rest_invalid_featured_image', __( 'Invalid featured image id.' ), array( 'status' => 400 ) );
879
  }
880
  } else {
881
  return delete_post_thumbnail( $post_id );
@@ -920,7 +917,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
920
  *
921
  * Correctly handles posts with the inherit status.
922
  *
923
- * @param object $post Post object.
924
  * @return bool Can we read it?
925
  */
926
  public function check_read_permission( $post ) {
@@ -961,7 +958,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
961
  /**
962
  * Check if we can edit a post.
963
  *
964
- * @param object $post Post object.
965
  * @return bool Can we edit it?
966
  */
967
  protected function check_update_permission( $post ) {
@@ -977,7 +974,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
977
  /**
978
  * Check if we can create a post.
979
  *
980
- * @param object $post Post object.
981
  * @return bool Can we create it?.
982
  */
983
  protected function check_create_permission( $post ) {
@@ -993,7 +990,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
993
  /**
994
  * Check if we can delete a post.
995
  *
996
- * @param object $post Post object.
997
  * @return bool Can we delete it?
998
  */
999
  protected function check_delete_permission( $post ) {
@@ -1136,9 +1133,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
1136
  $data = $this->add_additional_fields_to_object( $data, $request );
1137
 
1138
  // Wrap the data in a response object.
1139
- $response = rest_ensure_response( $data );
1140
 
1141
- $response->add_links( $this->prepare_links( $post ) );
1142
 
1143
  /**
1144
  * Filter the post data for a response.
@@ -1146,11 +1143,11 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
1146
  * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
1147
  * prepared for the response.
1148
  *
1149
- * @param WP_REST_Response $response The response object.
1150
- * @param WP_Post $post Post object.
1151
- * @param WP_REST_Request $request Request object.
1152
  */
1153
- return apply_filters( 'rest_prepare_' . $this->post_type, $response, $post, $request );
1154
  }
1155
 
1156
  /**
@@ -1165,13 +1162,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
1165
  // Entity meta
1166
  $links = array(
1167
  'self' => array(
1168
- 'href' => rest_url( trailingslashit( $base ) . $post->ID ),
1169
  ),
1170
  'collection' => array(
1171
- 'href' => rest_url( $base ),
1172
- ),
1173
- 'about' => array(
1174
- 'href' => rest_url( '/wp/v2/types/' . $this->post_type ),
1175
  ),
1176
  );
1177
 
@@ -1233,7 +1227,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
1233
  }
1234
 
1235
  $tax_base = ! empty( $taxonomy_obj->rest_base ) ? $taxonomy_obj->rest_base : $tax;
1236
- $terms_url = rest_url( trailingslashit( $base ) . $post->ID . '/' . $tax_base );
1237
 
1238
  $links['https://api.w.org/term'][] = array(
1239
  'href' => $terms_url,
@@ -1355,7 +1349,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
1355
  $post_type_obj = get_post_type_object( $this->post_type );
1356
  if ( $post_type_obj->hierarchical ) {
1357
  $schema['properties']['parent'] = array(
1358
- 'description' => 'The id for the parent of the object.',
1359
  'type' => 'integer',
1360
  'context' => array( 'view', 'edit' ),
1361
  );
@@ -1451,7 +1445,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
1451
 
1452
  case 'author':
1453
  $schema['properties']['author'] = array(
1454
- 'description' => 'The id for the author of the object.',
1455
  'type' => 'integer',
1456
  'context' => array( 'view', 'edit', 'embed' ),
1457
  );
@@ -1479,7 +1473,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
1479
 
1480
  case 'thumbnail':
1481
  $schema['properties']['featured_image'] = array(
1482
- 'description' => 'The id of the featured image for the object.',
1483
  'type' => 'integer',
1484
  'context' => array( 'view', 'edit' ),
1485
  );
@@ -1547,61 +1541,8 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
1547
  */
1548
  public function get_collection_params() {
1549
  $params = parent::get_collection_params();
1550
-
1551
- $params['context']['default'] = 'view';
1552
-
1553
- if ( post_type_supports( $this->post_type, 'author' ) ) {
1554
- $params['author'] = array(
1555
- 'description' => 'Limit result set to posts assigned to a specific author.',
1556
- 'type' => 'integer',
1557
- 'default' => null,
1558
- 'sanitize_callback' => 'absint',
1559
- );
1560
- }
1561
- $params['order'] = array(
1562
- 'description' => 'Order sort attribute ascending or descending.',
1563
- 'type' => 'string',
1564
- 'default' => 'asc',
1565
- 'enum' => array( 'asc', 'desc' ),
1566
- );
1567
- $params['orderby'] = array(
1568
- 'description' => 'Sort collection by object attribute.',
1569
- 'type' => 'string',
1570
- 'default' => 'name',
1571
- 'enum' => array(
1572
- 'id',
1573
- 'title',
1574
- 'slug',
1575
- ),
1576
- );
1577
- $params['status'] = array(
1578
- 'default' => 'publish',
1579
- 'description' => 'Limit result set to posts assigned a specific status.',
1580
- 'sanitize_callback' => 'sanitize_key',
1581
- 'type' => 'string',
1582
- 'validate_callback' => array( $this, 'validate_user_can_query_private_statuses' ),
1583
- );
1584
  $params['filter'] = array();
1585
  return $params;
1586
  }
1587
 
1588
- /**
1589
- * Validate whether the user can query private statuses
1590
- *
1591
- * @param mixed $value
1592
- * @param WP_REST_Request $request
1593
- * @param string $parameter
1594
- * @return WP_Error|bool
1595
- */
1596
- public function validate_user_can_query_private_statuses( $value, $request, $parameter ) {
1597
- if ( 'publish' === $value ) {
1598
- return true;
1599
- }
1600
- $post_type_obj = get_post_type_object( $this->post_type );
1601
- if ( current_user_can( $post_type_obj->cap->edit_posts ) ) {
1602
- return true;
1603
- }
1604
- return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden' ), array( 'status' => rest_authorization_required_code() ) );
1605
- }
1606
-
1607
  }
15
 
16
  $base = $this->get_post_type_base( $this->post_type );
17
 
18
+ $posts_args = $this->get_collection_params();
19
+
20
  register_rest_route( 'wp/v2', '/' . $base, array(
21
  array(
22
  'methods' => WP_REST_Server::READABLE,
23
  'callback' => array( $this, 'get_items' ),
24
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
25
+ 'args' => $posts_args,
26
  ),
27
  array(
28
  'methods' => WP_REST_Server::CREATABLE,
39
  'callback' => array( $this, 'get_item' ),
40
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
41
  'args' => array(
42
+ 'context' => array(
43
+ 'default' => 'view',
44
+ ),
45
  ),
46
  ),
47
  array(
73
  */
74
  public function get_items( $request ) {
75
  $args = array();
 
76
  $args['paged'] = $request['page'];
77
  $args['posts_per_page'] = $request['per_page'];
78
  $args['post_parent'] = $request['parent'];
 
 
79
 
80
  if ( is_array( $request['filter'] ) ) {
81
  $args = array_merge( $args, $request['filter'] );
148
  $post = get_post( $id );
149
 
150
  if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
151
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
152
  }
153
 
154
  $data = $this->prepare_item_for_response( $post, $request );
244
  $post = get_post( $id );
245
 
246
  if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
247
+ return new WP_Error( 'rest_post_invalid_id', __( 'Post ID is invalid.' ), array( 'status' => 400 ) );
248
  }
249
 
250
  $post = $this->prepare_item_for_database( $request );
315
  $post = get_post( $id );
316
 
317
  if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
318
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
319
  }
320
 
321
  $supports_trash = ( EMPTY_TRASH_DAYS > 0 );
334
  $supports_trash = apply_filters( 'rest_post_trashable', $supports_trash, $post );
335
 
336
  if ( ! $this->check_delete_permission( $post ) ) {
337
+ return new WP_Error( 'rest_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => 401 ) );
338
  }
339
 
340
  $request = new WP_REST_Request( 'GET', '/wp/v2/' . $this->get_post_type_base( $this->post_type ) . '/' . $post->ID );
396
  $post_type = get_post_type_object( $this->post_type );
397
 
398
  if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
399
+ return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts in this post type' ), array( 'status' => 403 ) );
400
  }
401
 
402
  return true;
413
  $post = get_post( (int) $request['id'] );
414
 
415
  if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
416
+ return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post' ), array( 'status' => 403 ) );
417
  }
418
 
419
  if ( $post ) {
434
  $post_type = get_post_type_object( $this->post_type );
435
 
436
  if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
437
+ return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' => 403 ) );
438
  }
439
 
440
  if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
441
+ return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to create posts as this user.' ), array( 'status' => 403 ) );
442
  }
443
 
444
  if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
445
+ return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' => 403 ) );
446
  }
447
 
448
  return current_user_can( $post_type->cap->create_posts );
464
  }
465
 
466
  if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
467
+ return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' => 403 ) );
468
  }
469
 
470
  if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
471
+ return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to update posts as this user.' ), array( 'status' => 403 ) );
472
  }
473
 
474
  if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
475
+ return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' => 403 ) );
476
  }
477
 
478
  return true;
489
  $post = get_post( $request['id'] );
490
 
491
  if ( $post && ! $this->check_delete_permission( $post ) ) {
492
+ return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete posts.' ), array( 'status' => 403 ) );
493
  }
494
 
495
  return true;
524
  $query_args['post_status'] = 'inherit';
525
  }
526
 
 
 
 
 
527
  return $query_args;
528
  }
529
 
643
  * Prepare a single post for create or update.
644
  *
645
  * @param WP_REST_Request $request Request object.
646
+ * @return WP_Error|obj $prepared_post Post object.
647
  */
648
  protected function prepare_item_for_database( $request ) {
649
  $prepared_post = new stdClass;
759
  if ( ! empty( $schema['properties']['parent'] ) && ! empty( $request['parent'] ) ) {
760
  $parent = get_post( (int) $request['parent'] );
761
  if ( empty( $parent ) ) {
762
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent ID.' ), array( 'status' => 400 ) );
763
  }
764
 
765
  $prepared_post->post_parent = (int) $parent->ID;
809
  break;
810
  case 'private':
811
  if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
812
+ return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create private posts in this post type' ), array( 'status' => 403 ) );
813
  }
814
  break;
815
  case 'publish':
816
  case 'future':
817
  if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
818
+ return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type' ), array( 'status' => 403 ) );
819
  }
820
  break;
821
  default:
851
  $author = get_userdata( $post_author );
852
 
853
  if ( ! $author ) {
854
+ return new WP_Error( 'rest_invalid_author', __( 'Invalid author ID.' ), array( 'status' => 400 ) );
855
  }
856
  }
857
 
872
  if ( $result ) {
873
  return true;
874
  } else {
875
+ return new WP_Error( 'rest_invalid_featured_image', __( 'Invalid featured image ID.' ), array( 'status' => 400 ) );
876
  }
877
  } else {
878
  return delete_post_thumbnail( $post_id );
917
  *
918
  * Correctly handles posts with the inherit status.
919
  *
920
+ * @param obj $post Post object.
921
  * @return bool Can we read it?
922
  */
923
  public function check_read_permission( $post ) {
958
  /**
959
  * Check if we can edit a post.
960
  *
961
+ * @param obj $post Post object.
962
  * @return bool Can we edit it?
963
  */
964
  protected function check_update_permission( $post ) {
974
  /**
975
  * Check if we can create a post.
976
  *
977
+ * @param obj $post Post object.
978
  * @return bool Can we create it?.
979
  */
980
  protected function check_create_permission( $post ) {
990
  /**
991
  * Check if we can delete a post.
992
  *
993
+ * @param obj $post Post object.
994
  * @return bool Can we delete it?
995
  */
996
  protected function check_delete_permission( $post ) {
1133
  $data = $this->add_additional_fields_to_object( $data, $request );
1134
 
1135
  // Wrap the data in a response object.
1136
+ $data = rest_ensure_response( $data );
1137
 
1138
+ $data->add_links( $this->prepare_links( $post ) );
1139
 
1140
  /**
1141
  * Filter the post data for a response.
1143
  * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
1144
  * prepared for the response.
1145
  *
1146
+ * @param array $data An array of post data, prepared for response.
1147
+ * @param WP_Post $post Post object.
1148
+ * @param WP_REST_Request $request Request object.
1149
  */
1150
+ return apply_filters( 'rest_prepare_' . $this->post_type, $data, $post, $request );
1151
  }
1152
 
1153
  /**
1162
  // Entity meta
1163
  $links = array(
1164
  'self' => array(
1165
+ 'href' => rest_url( trailingslashit( $base ) . $post->ID ),
1166
  ),
1167
  'collection' => array(
1168
+ 'href' => rest_url( $base ),
 
 
 
1169
  ),
1170
  );
1171
 
1227
  }
1228
 
1229
  $tax_base = ! empty( $taxonomy_obj->rest_base ) ? $taxonomy_obj->rest_base : $tax;
1230
+ $terms_url = rest_url( trailingslashit( $base ) . $post->ID . '/terms/' . $tax_base );
1231
 
1232
  $links['https://api.w.org/term'][] = array(
1233
  'href' => $terms_url,
1349
  $post_type_obj = get_post_type_object( $this->post_type );
1350
  if ( $post_type_obj->hierarchical ) {
1351
  $schema['properties']['parent'] = array(
1352
+ 'description' => 'The ID for the parent of the object.',
1353
  'type' => 'integer',
1354
  'context' => array( 'view', 'edit' ),
1355
  );
1445
 
1446
  case 'author':
1447
  $schema['properties']['author'] = array(
1448
+ 'description' => 'The ID for the author of the object.',
1449
  'type' => 'integer',
1450
  'context' => array( 'view', 'edit', 'embed' ),
1451
  );
1473
 
1474
  case 'thumbnail':
1475
  $schema['properties']['featured_image'] = array(
1476
+ 'description' => 'ID of the featured image for the object.',
1477
  'type' => 'integer',
1478
  'context' => array( 'view', 'edit' ),
1479
  );
1541
  */
1542
  public function get_collection_params() {
1543
  $params = parent::get_collection_params();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1544
  $params['filter'] = array();
1545
  return $params;
1546
  }
1547
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1548
  }
lib/endpoints/class-wp-rest-posts-terms-controller.php CHANGED
@@ -19,24 +19,22 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
19
  $base = $this->posts_controller->get_post_type_base( $this->post_type );
20
  $tax_base = $this->terms_controller->get_taxonomy_base( $this->taxonomy );
21
 
22
- register_rest_route( 'wp/v2', sprintf( '/%s/(?P<post_id>[\d]+)/%s', $base, $tax_base ), array(
 
23
  array(
24
  'methods' => WP_REST_Server::READABLE,
25
  'callback' => array( $this, 'get_items' ),
26
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
27
- 'args' => $this->get_collection_params(),
28
  ),
29
  'schema' => array( $this, 'get_public_item_schema' ),
30
  ) );
31
 
32
- register_rest_route( 'wp/v2', sprintf( '/%s/(?P<post_id>[\d]+)/%s/(?P<term_id>[\d]+)', $base, $tax_base ), array(
33
  array(
34
  'methods' => WP_REST_Server::READABLE,
35
  'callback' => array( $this, 'get_item' ),
36
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
37
- 'args' => array(
38
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
39
- ),
40
  ),
41
  array(
42
  'methods' => WP_REST_Server::CREATABLE,
@@ -106,11 +104,11 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
106
 
107
  $terms = wp_get_object_terms( $post->ID, $this->taxonomy );
108
 
109
- if ( ! in_array( $term_id, wp_list_pluck( $terms, 'term_id' ) ) ) {
110
- return new WP_Error( 'rest_post_not_in_term', __( 'Invalid taxonomy for post id.' ), array( 'status' => 404 ) );
111
  }
112
 
113
- $term = $this->terms_controller->prepare_item_for_response( get_term( $term_id, $this->taxonomy ), $request );
114
 
115
  $response = rest_ensure_response( $term );
116
 
@@ -132,14 +130,14 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
132
  return $is_request_valid;
133
  }
134
 
135
- $term = get_term( $term_id, $this->taxonomy );
136
  $tt_ids = wp_set_object_terms( $post->ID, $term->term_id, $this->taxonomy, true );
137
 
138
  if ( is_wp_error( $tt_ids ) ) {
139
  return $tt_ids;
140
  }
141
 
142
- $term = $this->terms_controller->prepare_item_for_response( get_term( $term_id, $this->taxonomy ), $request );
143
 
144
  $response = rest_ensure_response( $term );
145
  $response->set_status( 201 );
@@ -216,17 +214,17 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
216
  $post = get_post( (int) $request['post_id'] );
217
 
218
  if ( empty( $post ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) {
219
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
220
  }
221
 
222
  if ( ! $this->posts_controller->check_read_permission( $post ) ) {
223
- return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view this post.' ), array( 'status' => rest_authorization_required_code() ) );
224
  }
225
 
226
  if ( ! empty( $request['term_id'] ) ) {
227
  $term_id = absint( $request['term_id'] );
228
 
229
- $term = get_term( $term_id, $this->taxonomy );
230
  if ( ! $term || $term->taxonomy !== $this->taxonomy ) {
231
  return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
232
  }
@@ -290,7 +288,6 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
290
  */
291
  public function get_collection_params() {
292
  $query_params = array();
293
- $query_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
294
  $query_params['order'] = array(
295
  'description' => 'Order sort attribute ascending or descending.',
296
  'type' => 'string',
19
  $base = $this->posts_controller->get_post_type_base( $this->post_type );
20
  $tax_base = $this->terms_controller->get_taxonomy_base( $this->taxonomy );
21
 
22
+ $query_params = $this->get_collection_params();
23
+ register_rest_route( 'wp/v2', sprintf( '/%s/(?P<post_id>[\d]+)/terms/%s', $base, $tax_base ), array(
24
  array(
25
  'methods' => WP_REST_Server::READABLE,
26
  'callback' => array( $this, 'get_items' ),
27
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
28
+ 'args' => $query_params,
29
  ),
30
  'schema' => array( $this, 'get_public_item_schema' ),
31
  ) );
32
 
33
+ register_rest_route( 'wp/v2', sprintf( '/%s/(?P<post_id>[\d]+)/terms/%s/(?P<term_id>[\d]+)', $base, $tax_base ), array(
34
  array(
35
  'methods' => WP_REST_Server::READABLE,
36
  'callback' => array( $this, 'get_item' ),
37
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
 
 
 
38
  ),
39
  array(
40
  'methods' => WP_REST_Server::CREATABLE,
104
 
105
  $terms = wp_get_object_terms( $post->ID, $this->taxonomy );
106
 
107
+ if ( ! in_array( $term_id, wp_list_pluck( $terms, 'term_taxonomy_id' ) ) ) {
108
+ return new WP_Error( 'rest_post_not_in_term', __( 'Invalid taxonomy for post ID.' ), array( 'status' => 404 ) );
109
  }
110
 
111
+ $term = $this->terms_controller->prepare_item_for_response( get_term_by( 'term_taxonomy_id', $term_id, $this->taxonomy ), $request );
112
 
113
  $response = rest_ensure_response( $term );
114
 
130
  return $is_request_valid;
131
  }
132
 
133
+ $term = get_term_by( 'term_taxonomy_id', $term_id, $this->taxonomy );
134
  $tt_ids = wp_set_object_terms( $post->ID, $term->term_id, $this->taxonomy, true );
135
 
136
  if ( is_wp_error( $tt_ids ) ) {
137
  return $tt_ids;
138
  }
139
 
140
+ $term = $this->terms_controller->prepare_item_for_response( get_term_by( 'term_taxonomy_id', $term_id, $this->taxonomy ), $request );
141
 
142
  $response = rest_ensure_response( $term );
143
  $response->set_status( 201 );
214
  $post = get_post( (int) $request['post_id'] );
215
 
216
  if ( empty( $post ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) {
217
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
218
  }
219
 
220
  if ( ! $this->posts_controller->check_read_permission( $post ) ) {
221
+ return new WP_Error( 'rest_forbidden', __( 'Sorry, you cannot view this post.' ), array( 'status' => 403 ) );
222
  }
223
 
224
  if ( ! empty( $request['term_id'] ) ) {
225
  $term_id = absint( $request['term_id'] );
226
 
227
+ $term = get_term_by( 'term_taxonomy_id', $term_id, $this->taxonomy );
228
  if ( ! $term || $term->taxonomy !== $this->taxonomy ) {
229
  return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
230
  }
288
  */
289
  public function get_collection_params() {
290
  $query_params = array();
 
291
  $query_params['order'] = array(
292
  'description' => 'Order sort attribute ascending or descending.',
293
  'type' => 'string',
lib/endpoints/class-wp-rest-revisions-controller.php CHANGED
@@ -22,7 +22,11 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
22
  'methods' => WP_REST_Server::READABLE,
23
  'callback' => array( $this, 'get_items' ),
24
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
25
- 'args' => $this->get_collection_params(),
 
 
 
 
26
  ),
27
 
28
  'schema' => array( $this, 'get_public_item_schema' ),
@@ -34,7 +38,9 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
34
  'callback' => array( $this, 'get_item' ),
35
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
36
  'args' => array(
37
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
 
 
38
  ),
39
  ),
40
  array(
@@ -58,7 +64,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
58
 
59
  $parent = get_post( $request['parent_id'] );
60
  if ( ! $request['parent_id'] || ! $parent || $this->parent_post_type !== $parent->post_type ) {
61
- return new WP_Error( 'rest_post_invalid_parent_id', __( 'Invalid post parent id.' ), array( 'status' => 404 ) );
62
  }
63
 
64
  $revisions = wp_get_post_revisions( $request['parent_id'] );
@@ -85,7 +91,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
85
  }
86
  $parent_post_type_obj = get_post_type_object( $parent->post_type );
87
  if ( ! current_user_can( $parent_post_type_obj->cap->edit_post, $parent->ID ) ) {
88
- return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot view revisions of this post.' ), array( 'status' => rest_authorization_required_code() ) );
89
  }
90
 
91
  return true;
@@ -101,12 +107,12 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
101
 
102
  $parent = get_post( $request['parent_id'] );
103
  if ( ! $request['parent_id'] || ! $parent || $this->parent_post_type !== $parent->post_type ) {
104
- return new WP_Error( 'rest_post_invalid_parent_id', __( 'Invalid post parent id.' ), array( 'status' => 404 ) );
105
  }
106
 
107
  $revision = get_post( $request['id'] );
108
  if ( ! $revision || 'revision' !== $revision->post_type ) {
109
- return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision id.' ), array( 'status' => 404 ) );
110
  }
111
 
112
  $response = $this->prepare_item_for_response( $revision, $request );
@@ -215,16 +221,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
215
  $response->add_link( 'parent', rest_url( sprintf( 'wp/%s/%d', $this->parent_base, $data['parent'] ) ) );
216
  }
217
 
218
- /**
219
- * Filter a revision returned from the API.
220
- *
221
- * Allows modification of the revision right before it is returned.
222
- *
223
- * @param WP_REST_Response $response The response object.
224
- * @param WP_Post $post The original revision object.
225
- * @param WP_REST_Request $request Request used to generate the response.
226
- */
227
- return apply_filters( 'rest_prepare_revision', $response, $post, $request );
228
  }
229
 
230
  /**
@@ -262,7 +259,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
262
  */
263
  'properties' => array(
264
  'author' => array(
265
- 'description' => 'The id for the author of the object.',
266
  'type' => 'integer',
267
  'context' => array( 'view' ),
268
  ),
@@ -301,7 +298,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
301
  'context' => array( 'view' ),
302
  ),
303
  'parent' => array(
304
- 'description' => 'The id for the parent of the object.',
305
  'type' => 'integer',
306
  'context' => array( 'view' ),
307
  ),
@@ -352,15 +349,4 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
352
  return $this->add_additional_fields_schema( $schema );
353
  }
354
 
355
- /**
356
- * Get the query params for collections
357
- *
358
- * @return array
359
- */
360
- public function get_collection_params() {
361
- return array(
362
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
363
- );
364
- }
365
-
366
  }
22
  'methods' => WP_REST_Server::READABLE,
23
  'callback' => array( $this, 'get_items' ),
24
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
25
+ 'args' => array(
26
+ 'context' => array(
27
+ 'default' => 'view',
28
+ ),
29
+ ),
30
  ),
31
 
32
  'schema' => array( $this, 'get_public_item_schema' ),
38
  'callback' => array( $this, 'get_item' ),
39
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
40
  'args' => array(
41
+ 'context' => array(
42
+ 'default' => 'view',
43
+ ),
44
  ),
45
  ),
46
  array(
64
 
65
  $parent = get_post( $request['parent_id'] );
66
  if ( ! $request['parent_id'] || ! $parent || $this->parent_post_type !== $parent->post_type ) {
67
+ return new WP_Error( 'rest_post_invalid_parent_id', __( 'Invalid post parent ID.' ), array( 'status' => 404 ) );
68
  }
69
 
70
  $revisions = wp_get_post_revisions( $request['parent_id'] );
91
  }
92
  $parent_post_type_obj = get_post_type_object( $parent->post_type );
93
  if ( ! current_user_can( $parent_post_type_obj->cap->edit_post, $parent->ID ) ) {
94
+ return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot view revisions of this post.' ), array( 'status' => 403 ) );
95
  }
96
 
97
  return true;
107
 
108
  $parent = get_post( $request['parent_id'] );
109
  if ( ! $request['parent_id'] || ! $parent || $this->parent_post_type !== $parent->post_type ) {
110
+ return new WP_Error( 'rest_post_invalid_parent_id', __( 'Invalid post parent ID.' ), array( 'status' => 404 ) );
111
  }
112
 
113
  $revision = get_post( $request['id'] );
114
  if ( ! $revision || 'revision' !== $revision->post_type ) {
115
+ return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision ID.' ), array( 'status' => 404 ) );
116
  }
117
 
118
  $response = $this->prepare_item_for_response( $revision, $request );
221
  $response->add_link( 'parent', rest_url( sprintf( 'wp/%s/%d', $this->parent_base, $data['parent'] ) ) );
222
  }
223
 
224
+ return $response;
 
 
 
 
 
 
 
 
 
225
  }
226
 
227
  /**
259
  */
260
  'properties' => array(
261
  'author' => array(
262
+ 'description' => 'The ID for the author of the object.',
263
  'type' => 'integer',
264
  'context' => array( 'view' ),
265
  ),
298
  'context' => array( 'view' ),
299
  ),
300
  'parent' => array(
301
+ 'description' => 'The ID for the parent of the object.',
302
  'type' => 'integer',
303
  'context' => array( 'view' ),
304
  ),
349
  return $this->add_additional_fields_schema( $schema );
350
  }
351
 
 
 
 
 
 
 
 
 
 
 
 
352
  }
lib/endpoints/class-wp-rest-taxonomies-controller.php CHANGED
@@ -11,7 +11,11 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
11
  array(
12
  'methods' => WP_REST_Server::READABLE,
13
  'callback' => array( $this, 'get_items' ),
14
- 'args' => $this->get_collection_params(),
 
 
 
 
15
  ),
16
  'schema' => array( $this, 'get_public_item_schema' ),
17
  ) );
@@ -21,9 +25,6 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
21
  'methods' => WP_REST_Server::READABLE,
22
  'callback' => array( $this, 'get_item' ),
23
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
24
- 'args' => array(
25
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
26
- ),
27
  ),
28
  'schema' => array( $this, 'get_public_item_schema' ),
29
  ) );
@@ -36,8 +37,8 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
36
  * @return array
37
  */
38
  public function get_items( $request ) {
39
- if ( ! empty( $request['type'] ) ) {
40
- $taxonomies = get_object_taxonomies( $request['type'], 'objects' );
41
  } else {
42
  $taxonomies = get_taxonomies( '', 'objects' );
43
  }
@@ -47,8 +48,7 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
47
  if ( is_wp_error( $tax ) ) {
48
  continue;
49
  }
50
- $tax = $this->prepare_response_for_collection( $tax );
51
- $data[ $tax_type ] = $tax;
52
  }
53
  return $data;
54
  }
@@ -110,29 +110,16 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
110
  $data = $this->filter_response_by_context( $data, $context );
111
  $data = $this->add_additional_fields_to_object( $data, $request );
112
 
113
- // Wrap the data in a response object.
114
- $response = rest_ensure_response( $data );
115
-
116
- $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
117
- $response->add_links( array(
118
- 'collection' => array(
119
- 'href' => rest_url( 'wp/v2/taxonomies' ),
120
- ),
121
- 'item' => array(
122
- 'href' => rest_url( sprintf( 'wp/v2/%s', $base ) ),
123
- ),
124
- ) );
125
-
126
  /**
127
  * Filter a taxonomy returned from the API.
128
  *
129
  * Allows modification of the taxonomy data right before it is returned.
130
  *
131
- * @param WP_REST_Response $response The response object.
132
- * @param object $item The original taxonomy object.
133
- * @param WP_REST_Request $request Request used to generate the response.
134
  */
135
- return apply_filters( 'rest_prepare_taxonomy', $response, $taxonomy, $request );
136
  }
137
 
138
  /**
@@ -186,19 +173,4 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
186
  return $this->add_additional_fields_schema( $schema );
187
  }
188
 
189
- /**
190
- * Get the query params for collections
191
- *
192
- * @return array
193
- */
194
- public function get_collection_params() {
195
- $new_params = array();
196
- $new_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
197
- $new_params['type'] = array(
198
- 'description' => 'Limit results to taxonomies associated with a specific post type.',
199
- 'type' => 'string',
200
- );
201
- return $new_params;
202
- }
203
-
204
  }
11
  array(
12
  'methods' => WP_REST_Server::READABLE,
13
  'callback' => array( $this, 'get_items' ),
14
+ 'args' => array(
15
+ 'post_type' => array(
16
+ 'sanitize_callback' => 'sanitize_key',
17
+ ),
18
+ ),
19
  ),
20
  'schema' => array( $this, 'get_public_item_schema' ),
21
  ) );
25
  'methods' => WP_REST_Server::READABLE,
26
  'callback' => array( $this, 'get_item' ),
27
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
 
 
 
28
  ),
29
  'schema' => array( $this, 'get_public_item_schema' ),
30
  ) );
37
  * @return array
38
  */
39
  public function get_items( $request ) {
40
+ if ( ! empty( $request['post_type'] ) ) {
41
+ $taxonomies = get_object_taxonomies( $request['post_type'], 'objects' );
42
  } else {
43
  $taxonomies = get_taxonomies( '', 'objects' );
44
  }
48
  if ( is_wp_error( $tax ) ) {
49
  continue;
50
  }
51
+ $data[] = $tax;
 
52
  }
53
  return $data;
54
  }
110
  $data = $this->filter_response_by_context( $data, $context );
111
  $data = $this->add_additional_fields_to_object( $data, $request );
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  /**
114
  * Filter a taxonomy returned from the API.
115
  *
116
  * Allows modification of the taxonomy data right before it is returned.
117
  *
118
+ * @param array $data Key value array of taxonomy data.
119
+ * @param object $item The taxonomy object.
120
+ * @param WP_REST_Request $request Request used to generate the response.
121
  */
122
+ return apply_filters( 'rest_prepare_taxonomy', $data, $taxonomy, $request );
123
  }
124
 
125
  /**
173
  return $this->add_additional_fields_schema( $schema );
174
  }
175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
lib/endpoints/class-wp-rest-terms-controller.php CHANGED
@@ -20,12 +20,13 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
20
  public function register_routes() {
21
 
22
  $base = $this->get_taxonomy_base( $this->taxonomy );
23
- register_rest_route( 'wp/v2', '/' . $base, array(
 
24
  array(
25
  'methods' => WP_REST_Server::READABLE,
26
  'callback' => array( $this, 'get_items' ),
27
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
28
- 'args' => $this->get_collection_params(),
29
  ),
30
  array(
31
  'methods' => WP_REST_Server::CREATABLE,
@@ -36,14 +37,11 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
36
 
37
  'schema' => array( $this, 'get_public_item_schema' ),
38
  ));
39
- register_rest_route( 'wp/v2', '/' . $base . '/(?P<id>[\d]+)', array(
40
  array(
41
  'methods' => WP_REST_Server::READABLE,
42
  'callback' => array( $this, 'get_item' ),
43
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
44
- 'args' => array(
45
- 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
46
- ),
47
  ),
48
  array(
49
  'methods' => WP_REST_Server::EDITABLE,
@@ -85,8 +83,9 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
85
  // Only query top-level terms.
86
  $prepared_args['parent'] = 0;
87
  } else {
88
- if ( $request['parent'] ) {
89
- $prepared_args['parent'] = $request['parent'];
 
90
  }
91
  }
92
  }
@@ -112,7 +111,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
112
  $max_pages = ceil( $total_terms / $request['per_page'] );
113
  $response->header( 'X-WP-TotalPages', (int) $max_pages );
114
 
115
- $base = add_query_arg( $request->get_query_params(), rest_url( '/wp/v2/' . $this->get_taxonomy_base( $this->taxonomy ) ) );
116
  if ( $request['page'] > 1 ) {
117
  $prev_page = $request['page'] - 1;
118
  if ( $prev_page > $max_pages ) {
@@ -138,7 +137,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
138
  */
139
  public function get_item( $request ) {
140
 
141
- $term = get_term( (int) $request['id'], $this->taxonomy );
142
  if ( ! $term || $term->taxonomy !== $this->taxonomy ) {
143
  return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
144
  }
@@ -174,7 +173,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
174
  return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set term parent, taxonomy is not hierarchical.' ), array( 'status' => 400 ) );
175
  }
176
 
177
- $parent = get_term( (int) $request['parent'], $this->taxonomy );
178
 
179
  if ( ! $parent ) {
180
  return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 404 ) );
@@ -187,11 +186,11 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
187
  if ( is_wp_error( $term ) ) {
188
 
189
  // If we're going to inform the client that the term exists, give them the identifier
190
- // they can actually use.
191
 
192
  if ( ( $term_id = $term->get_error_data( 'term_exists' ) ) ) {
193
  $existing_term = get_term( $term_id, $this->taxonomy );
194
- $term->add_data( $existing_term->term_id, 'term_exists' );
195
  }
196
 
197
  return $term;
@@ -200,12 +199,12 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
200
  $this->update_additional_fields_for_object( $term, $request );
201
 
202
  $response = $this->get_item( array(
203
- 'id' => $term['term_id'],
204
  ) );
205
 
206
  $response = rest_ensure_response( $response );
207
  $response->set_status( 201 );
208
- $response->header( 'Location', rest_url( '/wp/v2/' . $this->get_taxonomy_base( $this->taxonomy ) . '/' . $term['term_id'] ) );
209
  return $response;
210
  }
211
 
@@ -233,7 +232,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
233
  return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set term parent, taxonomy is not hierarchical.' ), array( 'status' => 400 ) );
234
  }
235
 
236
- $parent = get_term( (int) $request['parent'], $this->taxonomy );
237
 
238
  if ( ! $parent ) {
239
  return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 400 ) );
@@ -242,7 +241,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
242
  $prepared_args['parent'] = $parent->term_id;
243
  }
244
 
245
- $term = get_term( (int) $request['id'], $this->taxonomy );
246
  if ( ! $term ) {
247
  return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
248
  }
@@ -255,10 +254,10 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
255
  }
256
  }
257
 
258
- $this->update_additional_fields_for_object( get_term( (int) $request['id'], $this->taxonomy ), $request );
259
 
260
  $response = $this->get_item( array(
261
- 'id' => $term->term_id,
262
  ) );
263
 
264
  return rest_ensure_response( $response );
@@ -273,8 +272,8 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
273
  public function delete_item( $request ) {
274
 
275
  // Get the actual term_id
276
- $term = get_term( (int) $request['id'], $this->taxonomy );
277
- $get_request = new WP_REST_Request( 'GET', rest_url( 'wp/v2/' . $this->get_taxonomy_base( $term->taxonomy ) . '/' . (int) $request['id'] ) );
278
  $get_request->set_param( 'context', 'view' );
279
  $response = $this->prepare_item_for_response( $term, $get_request );
280
 
@@ -391,7 +390,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
391
  return $valid;
392
  }
393
 
394
- $term = get_term( (int) $request['id'], $this->taxonomy );
395
  if ( ! $term ) {
396
  return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
397
  }
@@ -428,8 +427,16 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
428
  */
429
  public function prepare_item_for_response( $item, $request ) {
430
 
 
 
 
 
 
 
 
 
431
  $data = array(
432
- 'id' => (int) $item->term_id,
433
  'count' => (int) $item->count,
434
  'description' => $item->description,
435
  'link' => get_term_link( $item ),
@@ -439,27 +446,27 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
439
  );
440
  $schema = $this->get_item_schema();
441
  if ( ! empty( $schema['properties']['parent'] ) ) {
442
- $data['parent'] = (int) $item->parent;
443
  }
444
 
445
  $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
446
  $data = $this->filter_response_by_context( $data, $context );
447
  $data = $this->add_additional_fields_to_object( $data, $request );
448
 
449
- $response = rest_ensure_response( $data );
450
 
451
- $response->add_links( $this->prepare_links( $item ) );
452
 
453
  /**
454
  * Filter a term item returned from the API.
455
  *
456
  * Allows modification of the term data right before it is returned.
457
  *
458
- * @param WP_REST_Response $response The response object.
459
- * @param object $item The original term object.
460
- * @param WP_REST_Request $request Request used to generate the response.
461
  */
462
- return apply_filters( 'rest_prepare_term', $response, $item, $request );
463
  }
464
 
465
  /**
@@ -469,24 +476,21 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
469
  * @return array Links for the given term.
470
  */
471
  protected function prepare_links( $term ) {
472
- $base = '/wp/v2/' . $this->get_taxonomy_base( $term->taxonomy );
473
  $links = array(
474
  'self' => array(
475
- 'href' => rest_url( trailingslashit( $base ) . $term->term_id ),
476
  ),
477
  'collection' => array(
478
  'href' => rest_url( $base ),
479
  ),
480
- 'about' => array(
481
- 'href' => rest_url( sprintf( 'wp/v2/taxonomies/%s', $this->taxonomy ) ),
482
- ),
483
  );
484
 
485
  if ( $term->parent ) {
486
- $parent_term = get_term( (int) $term->parent, $term->taxonomy );
487
  if ( $parent_term ) {
488
  $links['up'] = array(
489
- 'href' => rest_url( sprintf( 'wp/v2/%s/%d', $this->get_taxonomy_base( $parent_term->taxonomy ), $parent_term->term_id ) ),
490
  'embeddable' => true,
491
  );
492
  }
@@ -562,7 +566,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
562
  $taxonomy = get_taxonomy( $this->taxonomy );
563
  if ( $taxonomy->hierarchical ) {
564
  $schema['properties']['parent'] = array(
565
- 'description' => 'The id for the parent of the object.',
566
  'type' => 'integer',
567
  'context' => array( 'view' ),
568
  );
@@ -577,9 +581,16 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
577
  */
578
  public function get_collection_params() {
579
  $query_params = parent::get_collection_params();
580
-
581
- $query_params['context']['default'] = 'view';
582
-
 
 
 
 
 
 
 
583
  $query_params['order'] = array(
584
  'description' => 'Order sort attribute ascending or descending.',
585
  'type' => 'string',
20
  public function register_routes() {
21
 
22
  $base = $this->get_taxonomy_base( $this->taxonomy );
23
+ $query_params = $this->get_collection_params();
24
+ register_rest_route( 'wp/v2', '/terms/' . $base, array(
25
  array(
26
  'methods' => WP_REST_Server::READABLE,
27
  'callback' => array( $this, 'get_items' ),
28
  'permission_callback' => array( $this, 'get_items_permissions_check' ),
29
+ 'args' => $query_params,
30
  ),
31
  array(
32
  'methods' => WP_REST_Server::CREATABLE,
37
 
38
  'schema' => array( $this, 'get_public_item_schema' ),
39
  ));
40
+ register_rest_route( 'wp/v2', '/terms/' . $base . '/(?P<id>[\d]+)', array(
41
  array(
42
  'methods' => WP_REST_Server::READABLE,
43
  'callback' => array( $this, 'get_item' ),
44
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
 
 
 
45
  ),
46
  array(
47
  'methods' => WP_REST_Server::EDITABLE,
83
  // Only query top-level terms.
84
  $prepared_args['parent'] = 0;
85
  } else {
86
+ $parent = get_term_by( 'term_taxonomy_id', (int) $request['parent'], $this->taxonomy );
87
+ if ( $parent ) {
88
+ $prepared_args['parent'] = $parent->term_id;
89
  }
90
  }
91
  }
111
  $max_pages = ceil( $total_terms / $request['per_page'] );
112
  $response->header( 'X-WP-TotalPages', (int) $max_pages );
113
 
114
+ $base = add_query_arg( $request->get_query_params(), rest_url( '/wp/v2/terms/' . $this->get_taxonomy_base( $this->taxonomy ) ) );
115
  if ( $request['page'] > 1 ) {
116
  $prev_page = $request['page'] - 1;
117
  if ( $prev_page > $max_pages ) {
137
  */
138
  public function get_item( $request ) {
139
 
140
+ $term = get_term_by( 'term_taxonomy_id', (int) $request['id'], $this->taxonomy );
141
  if ( ! $term || $term->taxonomy !== $this->taxonomy ) {
142
  return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
143
  }
173
  return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set term parent, taxonomy is not hierarchical.' ), array( 'status' => 400 ) );
174
  }
175
 
176
+ $parent = get_term_by( 'term_taxonomy_id', (int) $request['parent'], $this->taxonomy );
177
 
178
  if ( ! $parent ) {
179
  return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 404 ) );
186
  if ( is_wp_error( $term ) ) {
187
 
188
  // If we're going to inform the client that the term exists, give them the identifier
189
+ // they can actually use (term_taxonomy_id) -- NOT term_id.
190
 
191
  if ( ( $term_id = $term->get_error_data( 'term_exists' ) ) ) {
192
  $existing_term = get_term( $term_id, $this->taxonomy );
193
+ $term->add_data( $existing_term->term_taxonomy_id, 'term_exists' );
194
  }
195
 
196
  return $term;
199
  $this->update_additional_fields_for_object( $term, $request );
200
 
201
  $response = $this->get_item( array(
202
+ 'id' => $term['term_taxonomy_id'],
203
  ) );
204
 
205
  $response = rest_ensure_response( $response );
206
  $response->set_status( 201 );
207
+ $response->header( 'Location', rest_url( '/wp/v2/terms/' . $this->get_taxonomy_base( $this->taxonomy ) . '/' . $term['term_taxonomy_id'] ) );
208
  return $response;
209
  }
210
 
232
  return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set term parent, taxonomy is not hierarchical.' ), array( 'status' => 400 ) );
233
  }
234
 
235
+ $parent = get_term_by( 'term_taxonomy_id', (int) $request['parent'], $this->taxonomy );
236
 
237
  if ( ! $parent ) {
238
  return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 400 ) );
241
  $prepared_args['parent'] = $parent->term_id;
242
  }
243
 
244
+ $term = get_term_by( 'term_taxonomy_id', (int) $request['id'], $this->taxonomy );
245
  if ( ! $term ) {
246
  return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
247
  }
254
  }
255
  }
256
 
257
+ $this->update_additional_fields_for_object( get_term_by( 'term_taxonomy_id', (int) $request['id'], $this->taxonomy ), $request );
258
 
259
  $response = $this->get_item( array(
260
+ 'id' => $term->term_taxonomy_id,
261
  ) );
262
 
263
  return rest_ensure_response( $response );
272
  public function delete_item( $request ) {
273
 
274
  // Get the actual term_id
275
+ $term = get_term_by( 'term_taxonomy_id', (int) $request['id'], $this->taxonomy );
276
+ $get_request = new WP_REST_Request( 'GET', rest_url( 'wp/v2/terms/' . $this->get_taxonomy_base( $term->taxonomy ) . '/' . (int) $request['id'] ) );
277
  $get_request->set_param( 'context', 'view' );
278
  $response = $this->prepare_item_for_response( $term, $get_request );
279
 
390
  return $valid;
391
  }
392
 
393
+ $term = get_term_by( 'term_taxonomy_id', (int) $request['id'], $this->taxonomy );
394
  if ( ! $term ) {
395
  return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
396
  }
427
  */
428
  public function prepare_item_for_response( $item, $request ) {
429
 
430
+ $parent_id = 0;
431
+ if ( $item->parent ) {
432
+ $parent_term = get_term_by( 'id', (int) $item->parent, $item->taxonomy );
433
+ if ( $parent_term ) {
434
+ $parent_id = $parent_term->term_taxonomy_id;
435
+ }
436
+ }
437
+
438
  $data = array(
439
+ 'id' => (int) $item->term_taxonomy_id,
440
  'count' => (int) $item->count,
441
  'description' => $item->description,
442
  'link' => get_term_link( $item ),
446
  );
447
  $schema = $this->get_item_schema();
448
  if ( ! empty( $schema['properties']['parent'] ) ) {
449
+ $data['parent'] = (int) $parent_id;
450
  }
451
 
452
  $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
453
  $data = $this->filter_response_by_context( $data, $context );
454
  $data = $this->add_additional_fields_to_object( $data, $request );
455
 
456
+ $data = rest_ensure_response( $data );
457
 
458
+ $data->add_links( $this->prepare_links( $item ) );
459
 
460
  /**
461
  * Filter a term item returned from the API.
462
  *
463
  * Allows modification of the term data right before it is returned.
464
  *
465
+ * @param array $data Key value array of term data.
466
+ * @param object $item The term object.
467
+ * @param WP_REST_Request $request Request used to generate the response.
468
  */
469
+ return apply_filters( 'rest_prepare_term', $data, $item, $request );
470
  }
471
 
472
  /**
476
  * @return array Links for the given term.
477
  */
478
  protected function prepare_links( $term ) {
479
+ $base = '/wp/v2/terms/' . $this->get_taxonomy_base( $term->taxonomy );
480
  $links = array(
481
  'self' => array(
482
+ 'href' => rest_url( trailingslashit( $base ) . $term->term_taxonomy_id ),
483
  ),
484
  'collection' => array(
485
  'href' => rest_url( $base ),
486
  ),
 
 
 
487
  );
488
 
489
  if ( $term->parent ) {
490
+ $parent_term = get_term_by( 'id', (int) $term->parent, $term->taxonomy );
491
  if ( $parent_term ) {
492
  $links['up'] = array(
493
+ 'href' => rest_url( sprintf( 'wp/v2/terms/%s/%d', $this->get_taxonomy_base( $parent_term->taxonomy ), $parent_term->term_taxonomy_id ) ),
494
  'embeddable' => true,
495
  );
496
  }
566
  $taxonomy = get_taxonomy( $this->taxonomy );
567
  if ( $taxonomy->hierarchical ) {
568
  $schema['properties']['parent'] = array(
569
+ 'description' => 'The ID for the parent of the object.',
570
  'type' => 'integer',
571
  'context' => array( 'view' ),
572
  );
581
  */
582
  public function get_collection_params() {
583
  $query_params = parent::get_collection_params();
584
+ $query_params['context'] = array(
585
+ 'description' => 'Change the response format based on request context.',
586
+ 'default' => 'view',
587
+ 'sanitize_callback' => 'sanitize_key',
588
+ 'type' => 'string',
589
+ 'enum' => array(
590
+ 'embed',
591
+ 'view',
592
+ ),
593
+ );
594
  $query_params['order'] = array(
595
  'description' => 'Order sort attribute ascending or descending.',
596
  'type' => 'string',
lib/endpoints/class-wp-rest-users-controller.php CHANGED
@@ -10,11 +10,12 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
10
  */
11
  public function register_routes() {
12
 
 
13
  register_rest_route( 'wp/v2', '/users', array(
14
  array(
15
  'methods' => WP_REST_Server::READABLE,
16
  'callback' => array( $this, 'get_items' ),
17
- 'args' => $this->get_collection_params(),
18
  ),
19
  array(
20
  'methods' => WP_REST_Server::CREATABLE,
@@ -35,7 +36,9 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
35
  'callback' => array( $this, 'get_item' ),
36
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
37
  'args' => array(
38
- 'context' => $this->get_context_param( array( 'default' => 'embed' ) ),
 
 
39
  ),
40
  ),
41
  array(
@@ -98,10 +101,6 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
98
  $request['context'] = 'embed';
99
  }
100
 
101
- if ( '' !== $prepared_args['search'] ) {
102
- $prepared_args['search'] = '*' . $prepared_args['search'] . '*';
103
- }
104
-
105
  /**
106
  * Filter arguments, before passing to WP_User_Query, when querying users via the REST API.
107
  *
@@ -163,7 +162,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
163
  $user = get_userdata( $id );
164
 
165
  if ( empty( $id ) || empty( $user->ID ) ) {
166
- return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user id.' ), array( 'status' => 404 ) );
167
  }
168
 
169
  $user = $this->prepare_item_for_response( $user, $request );
@@ -272,7 +271,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
272
 
273
  $user = get_userdata( $id );
274
  if ( ! $user ) {
275
- return new WP_Error( 'rest_user_invalid_id', __( 'User id is invalid.' ), array( 'status' => 400 ) );
276
  }
277
 
278
  if ( email_exists( $request['email'] ) && $request['email'] !== $user->user_email ) {
@@ -334,12 +333,12 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
334
 
335
  $user = get_userdata( $id );
336
  if ( ! $user ) {
337
- return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user id.' ), array( 'status' => 400 ) );
338
  }
339
 
340
  if ( ! empty( $reassign ) ) {
341
  if ( $reassign === $id || ! get_userdata( $reassign ) ) {
342
- return new WP_Error( 'rest_user_invalid_reassign', __( 'Invalid user id.' ), array( 'status' => 400 ) );
343
  }
344
  }
345
 
@@ -383,7 +382,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
383
  $user = get_userdata( $id );
384
 
385
  if ( empty( $id ) || empty( $user->ID ) ) {
386
- return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user id.' ), array( 'status' => 404 ) );
387
  }
388
 
389
  if ( get_current_user_id() === $id ) {
@@ -393,11 +392,11 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
393
  $context = ! empty( $request['context'] ) && in_array( $request['context'], array( 'edit', 'view', 'embed' ) ) ? $request['context'] : 'embed';
394
 
395
  if ( 'edit' === $context && ! current_user_can( 'edit_user', $id ) ) {
396
- return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user with edit context' ), array( 'status' => rest_authorization_required_code() ) );
397
  } else if ( 'view' === $context && ! current_user_can( 'list_users' ) ) {
398
- return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user with view context' ), array( 'status' => rest_authorization_required_code() ) );
399
  } else if ( 'embed' === $context && ! count_user_posts( $id ) && ! current_user_can( 'edit_user', $id ) && ! current_user_can( 'list_users' ) ) {
400
- return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user' ), array( 'status' => rest_authorization_required_code() ) );
401
  }
402
 
403
  return true;
@@ -412,7 +411,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
412
  public function create_item_permissions_check( $request ) {
413
 
414
  if ( ! current_user_can( 'create_users' ) ) {
415
- return new WP_Error( 'rest_cannot_create_user', __( 'Sorry, you are not allowed to create users.' ), array( 'status' => rest_authorization_required_code() ) );
416
  }
417
 
418
  return true;
@@ -429,11 +428,11 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
429
  $id = (int) $request['id'];
430
 
431
  if ( ! current_user_can( 'edit_user', $id ) ) {
432
- return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit users.' ), array( 'status' => rest_authorization_required_code() ) );
433
  }
434
 
435
  if ( ! empty( $request['role'] ) && ! current_user_can( 'edit_users' ) ) {
436
- return new WP_Error( 'rest_cannot_edit_roles', __( 'Sorry, you are not allowed to edit roles of users.' ), array( 'status' => rest_authorization_required_code() ) );
437
  }
438
 
439
  return true;
@@ -451,7 +450,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
451
  $reassign = isset( $request['reassign'] ) ? absint( $request['reassign'] ) : null;
452
 
453
  if ( ! current_user_can( 'delete_user', $id ) ) {
454
- return new WP_Error( 'rest_user_cannot_delete', __( 'Sorry, you are not allowed to delete this user.' ), array( 'status' => rest_authorization_required_code() ) );
455
  }
456
 
457
  return true;
@@ -490,18 +489,18 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
490
  $data = $this->add_additional_fields_to_object( $data, $request );
491
 
492
  // Wrap the data in a response object
493
- $response = rest_ensure_response( $data );
494
 
495
- $response->add_links( $this->prepare_links( $user ) );
496
 
497
  /**
498
  * Filter user data returned from the REST API.
499
  *
500
- * @param WP_REST_Response $response The response object.
501
- * @param object $user User object used to create response.
502
- * @param WP_REST_Request $request Request object.
503
  */
504
- return apply_filters( 'rest_prepare_user', $response, $user, $request );
505
  }
506
 
507
  /**
@@ -609,7 +608,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
609
  return true;
610
  }
611
 
612
- return new WP_Error( 'rest_user_invalid_role', __( 'You cannot give users that role.' ), array( 'status' => rest_authorization_required_code() ) );
613
  }
614
 
615
  /**
@@ -767,9 +766,13 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
767
  */
768
  public function get_collection_params() {
769
  $query_params = parent::get_collection_params();
770
-
771
- $query_params['context']['default'] = 'view';
772
-
 
 
 
 
773
  $query_params['order'] = array(
774
  'default' => 'asc',
775
  'description' => 'Order sort attribute ascending or descending.',
10
  */
11
  public function register_routes() {
12
 
13
+ $query_params = $this->get_collection_params();
14
  register_rest_route( 'wp/v2', '/users', array(
15
  array(
16
  'methods' => WP_REST_Server::READABLE,
17
  'callback' => array( $this, 'get_items' ),
18
+ 'args' => $query_params,
19
  ),
20
  array(
21
  'methods' => WP_REST_Server::CREATABLE,
36
  'callback' => array( $this, 'get_item' ),
37
  'permission_callback' => array( $this, 'get_item_permissions_check' ),
38
  'args' => array(
39
+ 'context' => array(
40
+ 'default' => 'embed',
41
+ ),
42
  ),
43
  ),
44
  array(
101
  $request['context'] = 'embed';
102
  }
103
 
 
 
 
 
104
  /**
105
  * Filter arguments, before passing to WP_User_Query, when querying users via the REST API.
106
  *
162
  $user = get_userdata( $id );
163
 
164
  if ( empty( $id ) || empty( $user->ID ) ) {
165
+ return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) );
166
  }
167
 
168
  $user = $this->prepare_item_for_response( $user, $request );
271
 
272
  $user = get_userdata( $id );
273
  if ( ! $user ) {
274
+ return new WP_Error( 'rest_user_invalid_id', __( 'User ID is invalid.' ), array( 'status' => 400 ) );
275
  }
276
 
277
  if ( email_exists( $request['email'] ) && $request['email'] !== $user->user_email ) {
333
 
334
  $user = get_userdata( $id );
335
  if ( ! $user ) {
336
+ return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 400 ) );
337
  }
338
 
339
  if ( ! empty( $reassign ) ) {
340
  if ( $reassign === $id || ! get_userdata( $reassign ) ) {
341
+ return new WP_Error( 'rest_user_invalid_reassign', __( 'Invalid user ID.' ), array( 'status' => 400 ) );
342
  }
343
  }
344
 
382
  $user = get_userdata( $id );
383
 
384
  if ( empty( $id ) || empty( $user->ID ) ) {
385
+ return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) );
386
  }
387
 
388
  if ( get_current_user_id() === $id ) {
392
  $context = ! empty( $request['context'] ) && in_array( $request['context'], array( 'edit', 'view', 'embed' ) ) ? $request['context'] : 'embed';
393
 
394
  if ( 'edit' === $context && ! current_user_can( 'edit_user', $id ) ) {
395
+ return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user with edit context' ), array( 'status' => 403 ) );
396
  } else if ( 'view' === $context && ! current_user_can( 'list_users' ) ) {
397
+ return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user with view context' ), array( 'status' => 403 ) );
398
  } else if ( 'embed' === $context && ! count_user_posts( $id ) && ! current_user_can( 'edit_user', $id ) && ! current_user_can( 'list_users' ) ) {
399
+ return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user' ), array( 'status' => 403 ) );
400
  }
401
 
402
  return true;
411
  public function create_item_permissions_check( $request ) {
412
 
413
  if ( ! current_user_can( 'create_users' ) ) {
414
+ return new WP_Error( 'rest_cannot_create_user', __( 'Sorry, you are not allowed to create users.' ), array( 'status' => 403 ) );
415
  }
416
 
417
  return true;
428
  $id = (int) $request['id'];
429
 
430
  if ( ! current_user_can( 'edit_user', $id ) ) {
431
+ return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit users.' ), array( 'status' => 403 ) );
432
  }
433
 
434
  if ( ! empty( $request['role'] ) && ! current_user_can( 'edit_users' ) ) {
435
+ return new WP_Error( 'rest_cannot_edit_roles', __( 'Sorry, you are not allowed to edit roles of users.' ), array( 'status' => 403 ) );
436
  }
437
 
438
  return true;
450
  $reassign = isset( $request['reassign'] ) ? absint( $request['reassign'] ) : null;
451
 
452
  if ( ! current_user_can( 'delete_user', $id ) ) {
453
+ return new WP_Error( 'rest_user_cannot_delete', __( 'Sorry, you are not allowed to delete this user.' ), array( 'status' => 403 ) );
454
  }
455
 
456
  return true;
489
  $data = $this->add_additional_fields_to_object( $data, $request );
490
 
491
  // Wrap the data in a response object
492
+ $data = rest_ensure_response( $data );
493
 
494
+ $data->add_links( $this->prepare_links( $user ) );
495
 
496
  /**
497
  * Filter user data returned from the REST API.
498
  *
499
+ * @param WP_REST_Response $data Response data.
500
+ * @param object $user User object used to create response.
501
+ * @param WP_REST_Request $request Request object.
502
  */
503
+ return apply_filters( 'rest_prepare_user', $data, $user, $request );
504
  }
505
 
506
  /**
608
  return true;
609
  }
610
 
611
+ return new WP_Error( 'rest_user_invalid_role', __( 'You cannot give users that role.' ), array( 'status' => 403 ) );
612
  }
613
 
614
  /**
766
  */
767
  public function get_collection_params() {
768
  $query_params = parent::get_collection_params();
769
+ $query_params['context'] = array(
770
+ 'default' => 'view',
771
+ 'description' => 'Change the response format based on request context.',
772
+ 'enum' => array( 'embed', 'view', 'edit' ),
773
+ 'sanitize_callback' => 'sanitize_key',
774
+ 'type' => 'string',
775
+ );
776
  $query_params['order'] = array(
777
  'default' => 'asc',
778
  'description' => 'Order sort attribute ascending or descending.',
plugin.php CHANGED
@@ -4,7 +4,7 @@
4
  * Description: JSON-based REST API for WordPress, developed as part of GSoC 2013.
5
  * Author: WP REST API Team
6
  * Author URI: http://wp-api.org
7
- * Version: 2.0-beta9.1
8
  * Plugin URI: https://github.com/WP-API/WP-API
9
  * License: GPL2+
10
  */
@@ -133,13 +133,13 @@ function _add_extra_api_taxonomy_arguments() {
133
 
134
  if ( isset( $wp_taxonomies['category'] ) ) {
135
  $wp_taxonomies['category']->show_in_rest = true;
136
- $wp_taxonomies['category']->rest_base = 'categories';
137
  $wp_taxonomies['category']->rest_controller_class = 'WP_REST_Terms_Controller';
138
  }
139
 
140
  if ( isset( $wp_taxonomies['post_tag'] ) ) {
141
  $wp_taxonomies['post_tag']->show_in_rest = true;
142
- $wp_taxonomies['post_tag']->rest_base = 'tags';
143
  $wp_taxonomies['post_tag']->rest_controller_class = 'WP_REST_Terms_Controller';
144
  }
145
  }
@@ -220,18 +220,7 @@ function create_initial_rest_routes() {
220
  $controller->register_routes();
221
  }
222
 
223
- if ( ! function_exists( 'rest_authorization_required_code' ) ) {
224
- /**
225
- * Returns a contextual HTTP error code for authorization failure.
226
- *
227
- * @return integer
228
- */
229
- function rest_authorization_required_code() {
230
- return is_user_logged_in() ? 403 : 401;
231
- }
232
- }
233
-
234
- if ( ! function_exists( 'register_rest_field' ) ) {
235
  /**
236
  * Registers a new field on an existing WordPress object type.
237
  *
@@ -254,7 +243,7 @@ if ( ! function_exists( 'register_rest_field' ) ) {
254
  * this field. Default is 'null', no schema entry will be returned.
255
  * }
256
  */
257
- function register_rest_field( $object_type, $attribute, $args = array() ) {
258
  $defaults = array(
259
  'get_callback' => null,
260
  'update_callback' => null,
@@ -272,13 +261,3 @@ if ( ! function_exists( 'register_rest_field' ) ) {
272
  }
273
  }
274
  }
275
-
276
- if ( ! function_exists( 'register_api_field' ) ) {
277
- /**
278
- * Backwards compat shim
279
- */
280
- function register_api_field( $object_type, $attributes, $args = array() ) {
281
- _deprecated_function( 'register_api_field', 'WPAPI-2.0', 'register_rest_field' );
282
- register_rest_field( $object_type, $attributes, $args );
283
- }
284
- }
4
  * Description: JSON-based REST API for WordPress, developed as part of GSoC 2013.
5
  * Author: WP REST API Team
6
  * Author URI: http://wp-api.org
7
+ * Version: 2.0-beta8.1
8
  * Plugin URI: https://github.com/WP-API/WP-API
9
  * License: GPL2+
10
  */
133
 
134
  if ( isset( $wp_taxonomies['category'] ) ) {
135
  $wp_taxonomies['category']->show_in_rest = true;
136
+ $wp_taxonomies['category']->rest_base = 'category';
137
  $wp_taxonomies['category']->rest_controller_class = 'WP_REST_Terms_Controller';
138
  }
139
 
140
  if ( isset( $wp_taxonomies['post_tag'] ) ) {
141
  $wp_taxonomies['post_tag']->show_in_rest = true;
142
+ $wp_taxonomies['post_tag']->rest_base = 'tag';
143
  $wp_taxonomies['post_tag']->rest_controller_class = 'WP_REST_Terms_Controller';
144
  }
145
  }
220
  $controller->register_routes();
221
  }
222
 
223
+ if ( ! function_exists( 'register_api_field' ) ) {
 
 
 
 
 
 
 
 
 
 
 
224
  /**
225
  * Registers a new field on an existing WordPress object type.
226
  *
243
  * this field. Default is 'null', no schema entry will be returned.
244
  * }
245
  */
246
+ function register_api_field( $object_type, $attribute, $args = array() ) {
247
  $defaults = array(
248
  'get_callback' => null,
249
  'update_callback' => null,
261
  }
262
  }
263
  }
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: rmccue, rachelbaker, danielbachhuber, joehoyle
3
  Tags: json, rest, api, rest-api
4
  Requires at least: 4.3
5
  Tested up to: 4.4
6
- Stable tag: 2.0-beta9.1
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -36,126 +36,12 @@ For full-flavoured API support, you'll need to be using pretty permalinks to use
36
 
37
  == Changelog ==
38
 
39
- = 2.0 Beta 9.1 =
40
 
41
  * Ensure media of private posts are private too.
42
 
43
  Reported by @danielbachhuber on 2016-01-08.
44
 
45
- = 2.0 Beta 9.0 =
46
-
47
- * BREAKING CHANGE: Move tags and categories to top-level endpoints.
48
-
49
- Tags are now accessible at `/wp/v2/tags`, and categories accessible at `/wp/v2/categories`. Post terms reside at `/wp/v2/posts/<id>/tags` and `/wp/v2/<id>/categories`.
50
-
51
- (props @danielbachhuber, [#1802](https://github.com/WP-API/WP-API/pull/1802))
52
-
53
- * BREAKING CHANGE: Return object for requests to `/wp/v2/taxonomies`.
54
-
55
- This is consistent with `/wp/v2/types` and `/wp/v2/statuses`.
56
-
57
- (props @danielbachhuber, [#1825](https://github.com/WP-API/WP-API/pull/1825))
58
-
59
- * BREAKING CHANGE: Remove `rest_get_timezone()`.
60
-
61
- `json_get_timezone()` was only ever used in v1. This function causes fatals, and shouldn't be used.
62
-
63
- (props @danielbachhuber, [#1823](https://github.com/WP-API/WP-API/pull/1823))
64
-
65
- * BREAKING CHANGE: Rename `register_api_field()` to `register_rest_field()`.
66
-
67
- Introduces a `register_api_field()` function for backwards compat, which calls `_doing_it_wrong()`. However, `register_api_field()` won't ever be committed to WordPress core, so you should update your function calls.
68
-
69
- (props @danielbachhuber, [#1824](https://github.com/WP-API/WP-API/pull/1824))
70
-
71
- * BREAKING CHANGE: Change taxonomies' `post_type` argument to `type`.
72
-
73
- It's consistent with how we're exposing post types in the API.
74
-
75
- (props @danielbachhuber, [#1824](https://github.com/WP-API/WP-API/pull/1824))
76
-
77
- * Sync infrastructure with shipped in WordPress 4.4.
78
-
79
- * `wp-includes/rest-api/rest-functions.php` is removed, and its functions moved into `wp-includes/rest-api.php`.
80
- * Send nocache headers for REST requests. [#34832](https://core.trac.wordpress.org/ticket/34832)
81
- * Fix handling of HEAD requests. [#34837](https://core.trac.wordpress.org/ticket/34837)
82
- * Mark `WP_REST_Server::get_raw_data()` as static. [#34768](https://core.trac.wordpress.org/ticket/34768)
83
- * Unabbreviate error string. [#34818](https://core.trac.wordpress.org/ticket/34818)
84
-
85
- * Change terms endpoints to use `term_id` not `tt_id`.
86
-
87
- (props @joehoyle, [#1837](https://github.com/WP-API/WP-API/pull/1837))
88
-
89
- * Standardize declaration of `context` param for `GET` requests across controllers.
90
-
91
- However, we're still inconsistent in which controllers expose which params. Follow [#1845](https://github.com/WP-API/WP-API/issues/1845) for further discussion.
92
-
93
- (props @danielbachhuber, [#1795](https://github.com/WP-API/WP-API/pull/1795), [#1835](https://github.com/WP-API/WP-API/pull/1835), [#1838](https://github.com/WP-API/WP-API/pull/1838))
94
-
95
- * Link types / taxonomies to their collections, and vice versa.
96
-
97
- Collections link to their type / taxonomy with the `about` relation; types / taxonomies link to their colletion with the `item` relation, which is imperfect and may change in the future.
98
-
99
- (props @danielbachhuber, [#1814](https://github.com/WP-API/WP-API/pull/1814), [#1817](https://github.com/WP-API/WP-API/pull/1817), [#1829](https://github.com/WP-API/WP-API/pull/1829). [#1846](https://github.com/WP-API/WP-API/pull/1846))
100
-
101
- * Add missing 'wp/v2' in Location Response header when creating new Post Meta.
102
-
103
- (props @johanmynhardt, [#1790](https://github.com/WP-API/WP-API/pull/1790))
104
-
105
- * Expose Post collection query params, including `author`, `order`, `orderby` and `status`.
106
-
107
- (props @danielbachhuber, [#1793](https://github.com/WP-API/WP-API/pull/1793))
108
-
109
- * Ignore sticky posts by default.
110
-
111
- (props @danielbachhuber, [#1801](https://github.com/WP-API/WP-API/pull/1801))
112
-
113
- * Include `full` image size in attachment `sizes` attribute.
114
-
115
- (props @danielbachhuber, [#1806](https://github.com/WP-API/WP-API/pull/1806))
116
-
117
- * In text strings, use `id` instead of `ID`.
118
-
119
- `ID` is an implementation artifact. Our Resources use `id`.
120
-
121
- (props @danielbachhuber, [#1803](https://github.com/WP-API/WP-API/pull/1803))
122
-
123
- * Ensure `attachment.sizes[]` use `mime_type` instead of `mime-type`.
124
-
125
- (props @danielbachhuber, [#1809](https://github.com/WP-API/WP-API/pull/1809))
126
-
127
- * Introduce `rest_authorization_required_code()`.
128
-
129
- Many controllers returned incorrect HTTP codes, which this also fixes.
130
-
131
- (props @danielbachhuber, [#1808](https://github.com/WP-API/WP-API/pull/1808))
132
-
133
- * Respect core's `comment_registration` setting.
134
-
135
- If it's enabled, require users to be logged in to comment.
136
-
137
- (props @danielbachhuber, [#1826](https://github.com/WP-API/WP-API/pull/1826))
138
-
139
- * Default to wildcard when searching users.
140
-
141
- (props @danielbachhuber, [#1827](https://github.com/WP-API/WP-API/pull/1827))
142
-
143
- * Bring the wp-api.js library up to date for v2 of the REST API.
144
-
145
- (props @adamsilverstein, [#1828](https://github.com/WP-API/WP-API/pull/1828))
146
-
147
- * Add `rest_prepare_status` filter.
148
-
149
- (props @danielbachhuber, [#1830](https://github.com/WP-API/WP-API/pull/1830))
150
-
151
- * Make `prepare_*` filters more consistent.
152
-
153
- (props @danielbachhuber, [#1831](https://github.com/WP-API/WP-API/pull/1831))
154
-
155
- * Add `rest_prepare_post_type` filter for post types.
156
-
157
- (props @danielbachhuber, [#1833](https://github.com/WP-API/WP-API/pull/1833))
158
-
159
  = 2.0 Beta 8.0 =
160
 
161
  * Prevent fatals when uploading attachment by including admin utilities.
3
  Tags: json, rest, api, rest-api
4
  Requires at least: 4.3
5
  Tested up to: 4.4
6
+ Stable tag: 2.0-beta8.1
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
36
 
37
  == Changelog ==
38
 
39
+ = 2.0 Beta 8.1 =
40
 
41
  * Ensure media of private posts are private too.
42
 
43
  Reported by @danielbachhuber on 2016-01-08.
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  = 2.0 Beta 8.0 =
46
 
47
  * Prevent fatals when uploading attachment by including admin utilities.
wp-api.js CHANGED
@@ -1,25 +1,13 @@
1
- (function( window, undefined ) {
2
-
3
  'use strict';
4
 
5
- function WP_API() {
6
- this.models = {};
7
- this.collections = {};
8
- this.views = {};
9
- }
10
-
11
  window.wp = window.wp || {};
12
- wp.api = wp.api || new WP_API();
13
-
14
- })( window );
15
-
16
- (function( window, undefined ) {
17
-
18
- 'use strict';
19
 
20
- window.wp = window.wp || {};
21
- wp.api = wp.api || {};
22
- wp.api.utils = wp.api.utils || {};
 
 
23
 
24
  /**
25
  * ECMAScript 5 shim, from MDN.
@@ -87,15 +75,6 @@
87
  return timestamp;
88
  };
89
 
90
- })( window );
91
-
92
- /* global WP_API_Settings:false */
93
- // Suppress warning about parse function's unused "options" argument:
94
- /* jshint unused:false */
95
- (function( wp, WP_API_Settings, Backbone, window, undefined ) {
96
-
97
- 'use strict';
98
-
99
  /**
100
  * Array of parseable dates.
101
  *
@@ -205,8 +184,8 @@
205
  /**
206
  * Private Backbone base model for all models.
207
  */
208
- var WPApiBaseModel = Backbone.Model.extend(
209
- /** @lends WPApiBaseModel.prototype */
210
  {
211
  /**
212
  * Set nonce header before every Backbone sync.
@@ -237,13 +216,9 @@
237
  );
238
 
239
  /**
240
- * Backbone model for a single user.
241
- *
242
- *
243
- * @param {Object} attributes
244
- * @param {int} attributes.id The user id. Optional. Defaults to 'me', fetching the current user.
245
  */
246
- wp.api.models.User = WPApiBaseModel.extend(
247
  /** @lends User.prototype */
248
  {
249
  idAttribute: 'id',
@@ -251,7 +226,7 @@
251
  urlRoot: WP_API_Settings.root + 'wp/v2/users',
252
 
253
  defaults: {
254
- id: 'me',
255
  avatar_url: {},
256
  capabilities: {},
257
  description: '',
@@ -273,12 +248,9 @@
273
  );
274
 
275
  /**
276
- * Model for a single taxonomy.
277
- *
278
- * @param {Object} attributes
279
- * @param {string} attributes.slug The taxonomy slug.
280
  */
281
- wp.api.models.Taxonomy = WPApiBaseModel.extend(
282
  /** @lends Taxonomy.prototype */
283
  {
284
  idAttribute: 'slug',
@@ -298,17 +270,24 @@
298
  );
299
 
300
  /**
301
- * Backbone model for a single term.
302
- *
303
- * @param {Object} attributes
304
- * @param {int} id attributesm id.
305
  */
306
- wp.api.models.Term = WPApiBaseModel.extend(
307
  /** @lends Term.prototype */
308
  {
309
  idAttribute: 'id',
310
 
311
- urlRoot: WP_API_Settings.root + 'wp/v2/terms/tag',
 
 
 
 
 
 
 
 
 
 
312
 
313
  defaults: {
314
  id: null,
@@ -326,12 +305,9 @@
326
  );
327
 
328
  /**
329
- * Backbone model for a single post.
330
- *
331
- * @param {Object} attributes
332
- * @param {int} attributes.id The post id.
333
  */
334
- wp.api.models.Post = WPApiBaseModel.extend( _.extend(
335
  /** @lends Post.prototype */
336
  {
337
  idAttribute: 'id',
@@ -347,6 +323,7 @@
347
  modified: new Date(),
348
  modified_gmt: new Date(),
349
  password: '',
 
350
  status: 'draft',
351
  type: 'post',
352
  title: {},
@@ -364,12 +341,9 @@
364
  );
365
 
366
  /**
367
- * Backbone model for a single page.
368
- *
369
- * @param {Object} attributes
370
- * @param {int} attributes.id The page id.
371
  */
372
- wp.api.models.Page = WPApiBaseModel.extend( _.extend(
373
  /** @lends Page.prototype */
374
  {
375
  idAttribute: 'id',
@@ -403,17 +377,24 @@
403
  );
404
 
405
  /**
406
- * Backbone model for a single post revision.
407
- *
408
- * @param {Object} attributes
409
- * @param {int} attributes.parent The id of the post that this revision belongs to.
410
- * @param {int} attributes.id The revision id.
411
  */
412
- wp.api.models.PostRevision = WPApiBaseModel.extend( _.extend(
413
- /** @lends PostRevision.prototype */
414
  {
415
  idAttribute: 'id',
416
 
 
 
 
 
 
 
 
 
 
 
 
417
  defaults: {
418
  id: null,
419
  author: null,
@@ -428,30 +409,15 @@
428
  content: {},
429
  excerpt: {},
430
  _links: {}
431
- },
432
-
433
- /**
434
- * Return URL for the model.
435
- *
436
- * @returns {string}.
437
- */
438
- url: function() {
439
- var id = this.get( 'id' ) || '',
440
- parent = this.get( 'parent' ) || '';
441
-
442
- return WP_API_Settings.root + 'wp/v2/posts/' + parent + '/revisions/' + id;
443
  }
444
 
445
  }, TimeStampedMixin, HierarchicalMixin )
446
  );
447
 
448
  /**
449
- * Backbone model for a single media item.
450
- *
451
- * @param {Object} attributes
452
- * @param {int} attributes.id The media item id.
453
  */
454
- wp.api.models.Media = WPApiBaseModel.extend( _.extend(
455
  /** @lends Media.prototype */
456
  {
457
  idAttribute: 'id',
@@ -482,24 +448,29 @@
482
  post: null,
483
  source_url: '',
484
  _links: {}
485
- }
486
 
487
- }, TimeStampedMixin )
 
 
 
 
 
 
 
 
 
 
488
  );
489
 
490
  /**
491
- * Backbone model for a single comment.
492
- *
493
- * @param {Object} attributes
494
- * @param {int} attributes.id The comment id.
495
  */
496
- wp.api.models.Comment = WPApiBaseModel.extend( _.extend(
497
  /** @lends Comment.prototype */
498
  {
499
  idAttribute: 'id',
500
 
501
- urlRoot: WP_API_Settings.root + 'wp/v2/comments',
502
-
503
  defaults: {
504
  id: null,
505
  author: null,
@@ -514,26 +485,38 @@
514
  karma: 0,
515
  link: '',
516
  parent: 0,
 
517
  status: 'hold',
518
  type: '',
519
  _links: {}
520
- }
 
 
 
 
 
 
 
 
 
 
 
 
521
 
 
 
522
  }, TimeStampedMixin, HierarchicalMixin )
523
  );
524
 
525
  /**
526
- * Backbone model for a single post type.
527
- *
528
- * @param {Object} attributes
529
- * @param {string} attributes.slug The post type slug.
530
  */
531
- wp.api.models.PostType = WPApiBaseModel.extend(
532
  /** @lends PostType.prototype */
533
  {
534
  idAttribute: 'slug',
535
 
536
- urlRoot: WP_API_Settings.root + 'wp/v2/types',
537
 
538
  defaults: {
539
  slug: null,
@@ -557,24 +540,21 @@
557
  *
558
  * @returns {boolean}.
559
  */
560
- destroy: function() {
561
  return false;
562
  }
563
  }
564
  );
565
 
566
  /**
567
- * Backbone model for a a single post status.
568
- *
569
- * @param {Object} attributes
570
- * @param {string} attributes.slug The post status slug.
571
  */
572
- wp.api.models.PostStatus = WPApiBaseModel.extend(
573
  /** @lends PostStatus.prototype */
574
  {
575
  idAttribute: 'slug',
576
 
577
- urlRoot: WP_API_Settings.root + 'wp/v2/statuses',
578
 
579
  defaults: {
580
  slug: null,
@@ -601,54 +581,12 @@
601
  *
602
  * @returns {boolean}.
603
  */
604
- destroy: function() {
605
  return false;
606
  }
607
  }
608
  );
609
 
610
- /**
611
- * API Schema model. Contains meta information about the API.
612
- */
613
- wp.api.models.Schema = WPApiBaseModel.extend(
614
- /** @lends Shema.prototype */
615
- {
616
- url: WP_API_Settings.root + 'wp/v2',
617
-
618
- defaults: {
619
- namespace: '',
620
- _links: '',
621
- routes: {}
622
- },
623
-
624
- /**
625
- * Prevent model from being saved.
626
- *
627
- * @returns {boolean}.
628
- */
629
- save: function() {
630
- return false;
631
- },
632
-
633
- /**
634
- * Prevent model from being deleted.
635
- *
636
- * @returns {boolean}.
637
- */
638
- destroy: function() {
639
- return false;
640
- }
641
- }
642
- );
643
-
644
-
645
- })( wp, WP_API_Settings, Backbone, window );
646
-
647
- /* global WP_API_Settings:false */
648
- (function( wp, WP_API_Settings, Backbone, _, window, undefined ) {
649
-
650
- 'use strict';
651
-
652
  /**
653
  * Contains basic collection functionality such as pagination.
654
  */
@@ -979,4 +917,4 @@
979
  * Todo: Handle post meta.
980
  */
981
 
982
- })( wp, WP_API_Settings, Backbone, _, window );
1
+ ( function( WP_API_Settings, Backbone, _, window, undefined ) {
 
2
  'use strict';
3
 
 
 
 
 
 
 
4
  window.wp = window.wp || {};
 
 
 
 
 
 
 
5
 
6
+ wp.api = {
7
+ models: {},
8
+ collections: {},
9
+ utils: {}
10
+ };
11
 
12
  /**
13
  * ECMAScript 5 shim, from MDN.
75
  return timestamp;
76
  };
77
 
 
 
 
 
 
 
 
 
 
78
  /**
79
  * Array of parseable dates.
80
  *
184
  /**
185
  * Private Backbone base model for all models.
186
  */
187
+ var BaseModel = Backbone.Model.extend(
188
+ /** @lends BaseModel.prototype */
189
  {
190
  /**
191
  * Set nonce header before every Backbone sync.
216
  );
217
 
218
  /**
219
+ * Backbone model for single users.
 
 
 
 
220
  */
221
+ wp.api.models.User = BaseModel.extend(
222
  /** @lends User.prototype */
223
  {
224
  idAttribute: 'id',
226
  urlRoot: WP_API_Settings.root + 'wp/v2/users',
227
 
228
  defaults: {
229
+ id: null,
230
  avatar_url: {},
231
  capabilities: {},
232
  description: '',
248
  );
249
 
250
  /**
251
+ * Model for Taxonomy.
 
 
 
252
  */
253
+ wp.api.models.Taxonomy = BaseModel.extend(
254
  /** @lends Taxonomy.prototype */
255
  {
256
  idAttribute: 'slug',
270
  );
271
 
272
  /**
273
+ * Backbone model for term.
 
 
 
274
  */
275
+ wp.api.models.Term = BaseModel.extend(
276
  /** @lends Term.prototype */
277
  {
278
  idAttribute: 'id',
279
 
280
+ /**
281
+ * Return URL for the model.
282
+ *
283
+ * @returns {string}
284
+ */
285
+ url: function() {
286
+ var id = this.get( 'id' );
287
+ id = id || '';
288
+
289
+ return WP_API_Settings.root + 'wp/v2/taxonomies/' + this.get( 'taxonomy' ) + '/terms/' + id;
290
+ },
291
 
292
  defaults: {
293
  id: null,
305
  );
306
 
307
  /**
308
+ * Backbone model for single posts.
 
 
 
309
  */
310
+ wp.api.models.Post = BaseModel.extend( _.extend(
311
  /** @lends Post.prototype */
312
  {
313
  idAttribute: 'id',
323
  modified: new Date(),
324
  modified_gmt: new Date(),
325
  password: '',
326
+ slug: '',
327
  status: 'draft',
328
  type: 'post',
329
  title: {},
341
  );
342
 
343
  /**
344
+ * Backbone model for pages.
 
 
 
345
  */
346
+ wp.api.models.Page = BaseModel.extend( _.extend(
347
  /** @lends Page.prototype */
348
  {
349
  idAttribute: 'id',
377
  );
378
 
379
  /**
380
+ * Backbone model for revisions.
 
 
 
 
381
  */
382
+ wp.api.models.Revision = BaseModel.extend( _.extend(
383
+ /** @lends Revision.prototype */
384
  {
385
  idAttribute: 'id',
386
 
387
+ /**
388
+ * Return URL for the model.
389
+ *
390
+ * @returns {string}.
391
+ */
392
+ url: function() {
393
+ var id = this.get( 'id' ) || '';
394
+
395
+ return WP_API_Settings.root + 'wp/v2/posts/' + id + '/revisions';
396
+ },
397
+
398
  defaults: {
399
  id: null,
400
  author: null,
409
  content: {},
410
  excerpt: {},
411
  _links: {}
 
 
 
 
 
 
 
 
 
 
 
 
412
  }
413
 
414
  }, TimeStampedMixin, HierarchicalMixin )
415
  );
416
 
417
  /**
418
+ * Backbone model for media items.
 
 
 
419
  */
420
+ wp.api.models.Media = BaseModel.extend( _.extend(
421
  /** @lends Media.prototype */
422
  {
423
  idAttribute: 'id',
448
  post: null,
449
  source_url: '',
450
  _links: {}
451
+ },
452
 
453
+ /**
454
+ * @class Represent a media item.
455
+ * @augments Backbone.Model.
456
+ * @constructs
457
+ */
458
+ initialize: function() {
459
+
460
+ // Todo: what of the parent model is a page?
461
+ this.parentModel = wp.api.models.Post;
462
+ }
463
+ }, TimeStampedMixin, HierarchicalMixin )
464
  );
465
 
466
  /**
467
+ * Backbone model for comments.
 
 
 
468
  */
469
+ wp.api.models.Comment = BaseModel.extend( _.extend(
470
  /** @lends Comment.prototype */
471
  {
472
  idAttribute: 'id',
473
 
 
 
474
  defaults: {
475
  id: null,
476
  author: null,
485
  karma: 0,
486
  link: '',
487
  parent: 0,
488
+ post: null,
489
  status: 'hold',
490
  type: '',
491
  _links: {}
492
+ },
493
+
494
+ /**
495
+ * Return URL for model.
496
+ *
497
+ * @returns {string}.
498
+ */
499
+ url: function() {
500
+ var post_id = this.get( 'post' );
501
+ post_id = post_id || '';
502
+
503
+ var id = this.get( 'id' );
504
+ id = id || '';
505
 
506
+ return WP_API_Settings.root + 'wp/v2/posts/' + post_id + '/comments/' + id;
507
+ }
508
  }, TimeStampedMixin, HierarchicalMixin )
509
  );
510
 
511
  /**
512
+ * Backbone model for single post types.
 
 
 
513
  */
514
+ wp.api.models.PostType = BaseModel.extend(
515
  /** @lends PostType.prototype */
516
  {
517
  idAttribute: 'slug',
518
 
519
+ urlRoot: WP_API_Settings.root + 'wp/v2/posts/types',
520
 
521
  defaults: {
522
  slug: null,
540
  *
541
  * @returns {boolean}.
542
  */
543
+ 'delete': function() {
544
  return false;
545
  }
546
  }
547
  );
548
 
549
  /**
550
+ * Backbone model for a post status.
 
 
 
551
  */
552
+ wp.api.models.PostStatus = BaseModel.extend(
553
  /** @lends PostStatus.prototype */
554
  {
555
  idAttribute: 'slug',
556
 
557
+ urlRoot: WP_API_Settings.root + 'wp/v2/posts/statuses',
558
 
559
  defaults: {
560
  slug: null,
581
  *
582
  * @returns {boolean}.
583
  */
584
+ 'delete': function() {
585
  return false;
586
  }
587
  }
588
  );
589
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
  /**
591
  * Contains basic collection functionality such as pagination.
592
  */
917
  * Todo: Handle post meta.
918
  */
919
 
920
+ })( WP_API_Settings, Backbone, _, window, ( void 0 ) );