Version Description
Download this release
Release Info
Developer | danielbachhuber |
Plugin | WordPress REST API (Version 2) |
Version | 2.0-beta10 |
Comparing to | |
See all releases |
Code changes from version 2.0-beta3.1 to 2.0-beta10
- CHANGELOG.md +674 -2
- README.md +4 -1
- compatibility-v1.php +0 -110
- docs/README.md +0 -26
- docs/routes/routes.md +0 -1569
- extras.php +69 -236
- lib/endpoints/class-wp-rest-attachments-controller.php +113 -33
- lib/endpoints/class-wp-rest-comments-controller.php +230 -205
- lib/endpoints/class-wp-rest-controller.php +98 -58
- lib/endpoints/class-wp-rest-meta-controller.php +70 -35
- lib/endpoints/class-wp-rest-meta-posts-controller.php +6 -6
- lib/endpoints/class-wp-rest-post-statuses-controller.php +46 -21
- lib/endpoints/class-wp-rest-post-types-controller.php +67 -32
- lib/endpoints/class-wp-rest-posts-controller.php +411 -216
- lib/endpoints/class-wp-rest-posts-terms-controller.php +60 -28
- lib/endpoints/class-wp-rest-revisions-controller.php +65 -38
- lib/endpoints/class-wp-rest-taxonomies-controller.php +80 -38
- lib/endpoints/class-wp-rest-terms-controller.php +197 -150
- lib/endpoints/class-wp-rest-users-controller.php +223 -161
- lib/infrastructure/class-jsonserializable.php +0 -18
- lib/infrastructure/class-wp-http-response.php +0 -112
- lib/infrastructure/class-wp-http-responseinterface.php +0 -35
- lib/infrastructure/class-wp-rest-request.php +0 -764
- lib/infrastructure/class-wp-rest-response.php +0 -176
- lib/infrastructure/class-wp-rest-server.php +0 -962
- plugin.php +137 -565
- readme.txt +962 -7
- wp-api.js +405 -410
CHANGELOG.md
CHANGED
@@ -1,11 +1,683 @@
|
|
1 |
# Changelog
|
2 |
|
3 |
-
## 2.0 Beta
|
4 |
|
5 |
-
- Ensure media of private posts are private too.
|
6 |
|
7 |
Reported by @danielbachhuber on 2016-01-08.
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
## 2.0 Beta 3.0
|
10 |
|
11 |
- Add ability to declare sanitization and default options for schema fields.
|
1 |
# Changelog
|
2 |
|
3 |
+
## 2.0 Beta 10.0
|
4 |
|
5 |
+
- SECURITY: Ensure media of private posts are private too.
|
6 |
|
7 |
Reported by @danielbachhuber on 2016-01-08.
|
8 |
|
9 |
+
- BREAKING CHANGE: Removes compatibility repo for WordPress 4.3.
|
10 |
+
|
11 |
+
WordPress 4.4 is now the minimum supported WordPress version.
|
12 |
+
|
13 |
+
(props @danielbachhuber, [#1848](https://github.com/WP-API/WP-API/pull/1848))
|
14 |
+
|
15 |
+
- BREAKING CHANGE: Changes link relation for types and taxonomies.
|
16 |
+
|
17 |
+
In Beta 9, this link relation was introduced as `item`, which isn't correct. The relation has been changed to `https://api.w.org/items`.
|
18 |
+
|
19 |
+
(props @danielbachhuber, [#1853](https://github.com/WP-API/WP-API/pull/1853))
|
20 |
+
|
21 |
+
- BREAKING CHANGE: Introduces `edit` context for `wp/v2/types` and `wp/v2/taxonomies`.
|
22 |
+
|
23 |
+
Some fields have moved into this context, which require `edit_posts` and `manage_terms`, respectively.
|
24 |
+
|
25 |
+
(props @danielbachhuber, [#1894](https://github.com/WP-API/WP-API/pull/1894), [#1864](https://github.com/WP-API/WP-API/pull/1864))
|
26 |
+
|
27 |
+
- BREAKING CHANGE: Removes `post_format` as a term `_link` for Posts.
|
28 |
+
|
29 |
+
Post formats aren't a custom taxonomy in the eyes of the REST API.
|
30 |
+
|
31 |
+
(props @danielbachhuber, [#1854](https://github.com/WP-API/WP-API/pull/1854))
|
32 |
+
|
33 |
+
- Declares `parent` query param for Pages.
|
34 |
+
|
35 |
+
(props @danielbachhuber, [#1975](https://github.com/WP-API/WP-API/pull/1975))
|
36 |
+
|
37 |
+
- Permits logged-in users to query for media.
|
38 |
+
|
39 |
+
(props @danielbachhuber, [#1973](https://github.com/WP-API/WP-API/pull/1973))
|
40 |
+
|
41 |
+
- Removes duplicated query params from Terms controller.
|
42 |
+
|
43 |
+
(props @danielbachhuber, [#1963](https://github.com/WP-API/WP-API/pull/1963))
|
44 |
+
|
45 |
+
- Adds `include` param to `/wp/v2/posts`, `/wp/v2/users`, `/wp/v2/<taxonomy>` and `/wp/v2/comments`.
|
46 |
+
|
47 |
+
(props @danielbachhuber, [#1961](https://github.com/WP-API/WP-API/pull/1961), [#1964](https://github.com/WP-API/WP-API/pull/1964), [#1968](https://github.com/WP-API/WP-API/pull/1968), [#1971](https://github.com/WP-API/WP-API/pull/1971))
|
48 |
+
|
49 |
+
- Ensures `GET /wp/v2/posts` respects `order` and `orderby` params.
|
50 |
+
|
51 |
+
(props @danielbachhuber, [#1962](https://github.com/WP-API/WP-API/pull/1962))
|
52 |
+
|
53 |
+
- Fixes fatal by loading `wp-admin/includes/user.php` to expose `wp_delete_user()`.
|
54 |
+
|
55 |
+
(props @danielbachhuber, [#1958](https://github.com/WP-API/WP-API/pull/1958))
|
56 |
+
|
57 |
+
- Permits making a post sticky when also supplying an empty password.
|
58 |
+
|
59 |
+
(props @westonruter, [#1949](https://github.com/WP-API/WP-API/pull/1949))
|
60 |
+
|
61 |
+
- Uses `WP_REST_Request` internally across controllers.
|
62 |
+
|
63 |
+
(props @danielbachhuber, [#1933](https://github.com/WP-API/WP-API/pull/1933), [#1939](https://github.com/WP-API/WP-API/pull/1939), [#1934](https://github.com/WP-API/WP-API/pull/1934), [#1938](https://github.com/WP-API/WP-API/pull/1938))
|
64 |
+
|
65 |
+
- Cleans up permissions checks in `WP_REST_Terms_Controller`.
|
66 |
+
|
67 |
+
(props @danielbachhuber, [#1941](https://github.com/WP-API/WP-API/pull/1941))
|
68 |
+
|
69 |
+
- Uses `show_in_rest` to determine publicness for post types.
|
70 |
+
|
71 |
+
(props @danielbachhuber, [#1942](https://github.com/WP-API/WP-API/pull/1942))
|
72 |
+
|
73 |
+
- Makes `description` strings available for translation.
|
74 |
+
|
75 |
+
(props @danielbachhuber, [#1944](https://github.com/WP-API/WP-API/pull/1944))
|
76 |
+
|
77 |
+
- Checks `assign_terms` cap for taxonomy when managing post terms.
|
78 |
+
|
79 |
+
(props @danielbachhuber, [#1940](https://github.com/WP-API/WP-API/pull/1940))
|
80 |
+
|
81 |
+
- Defer to `edit_posts` of the custom post type when accessing private query vars.
|
82 |
+
|
83 |
+
(props @danielbachhuber, [#1886](https://github.com/WP-API/WP-API/pull/1886))
|
84 |
+
|
85 |
+
- Allows Terms collection params to be filtered.
|
86 |
+
|
87 |
+
(props @rachelbaker, [#1882](https://github.com/WP-API/WP-API/pull/1882))
|
88 |
+
|
89 |
+
- Renames post terms create/delete permissions callback.
|
90 |
+
|
91 |
+
(props @wpsmith, [#1923](https://github.com/WP-API/WP-API/pull/1923))
|
92 |
+
|
93 |
+
- Fixes invalid use of 'uri' as schema `type`.
|
94 |
+
|
95 |
+
(props @wpsmith, [#1913](https://github.com/WP-API/WP-API/pull/1913))
|
96 |
+
|
97 |
+
- Casts integer with (int) over intval for speed.
|
98 |
+
|
99 |
+
(props @wpsmith, [#1907](https://github.com/WP-API/WP-API/pull/1907))
|
100 |
+
|
101 |
+
- Fixes PHP Doc typo for `validate_schema_property` and `sanitize_schema_property`.
|
102 |
+
|
103 |
+
(props @wpsmith, @danielbachhuber, [#1909](https://github.com/WP-API/WP-API/pull/1909), [#1910](https://github.com/WP-API/WP-API/pull/1910))
|
104 |
+
|
105 |
+
- Adds a helpful description to the `filter` argument.
|
106 |
+
|
107 |
+
(props @danielbachhuber, [#1885](https://github.com/WP-API/WP-API/pull/1885))
|
108 |
+
|
109 |
+
- Changes order of Users response to match schema order.
|
110 |
+
|
111 |
+
(props @rachelbaker, [#1879](https://github.com/WP-API/WP-API/pull/1879))
|
112 |
+
|
113 |
+
- Adjusts Posts pagination headers for `filter` params.
|
114 |
+
|
115 |
+
(props @rachelbaker, [#1878](https://github.com/WP-API/WP-API/pull/1878))
|
116 |
+
|
117 |
+
- Uses proper status code when failing to get comments of private post.
|
118 |
+
|
119 |
+
(props @danielbachhuber, [#1866](https://github.com/WP-API/WP-API/pull/1867))
|
120 |
+
|
121 |
+
- Fixes invalid capability for comments get items permissions callback.
|
122 |
+
|
123 |
+
`manage_comments` doesn't exist; `moderate_comments` does.
|
124 |
+
|
125 |
+
(props @danielbachhuber, [#1866](https://github.com/WP-API/WP-API/pull/1866))
|
126 |
+
|
127 |
+
- Permits creating comments without an assigned post.
|
128 |
+
|
129 |
+
(props @danielbachhuber, [#1857](https://github.com/WP-API/WP-API/pull/1857))
|
130 |
+
|
131 |
+
- Prevents error notice when `show_in_rest` isn't set for a post type.
|
132 |
+
|
133 |
+
(props @danielbachhuber, [#1852](https://github.com/WP-API/WP-API/pull/1852))
|
134 |
+
|
135 |
+
## 2.0 Beta 9.0
|
136 |
+
|
137 |
+
- BREAKING CHANGE: Move tags and categories to top-level endpoints.
|
138 |
+
|
139 |
+
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`.
|
140 |
+
|
141 |
+
(props @danielbachhuber, [#1802](https://github.com/WP-API/WP-API/pull/1802))
|
142 |
+
|
143 |
+
- BREAKING CHANGE: Return object for requests to `/wp/v2/taxonomies`.
|
144 |
+
|
145 |
+
This is consistent with `/wp/v2/types` and `/wp/v2/statuses`.
|
146 |
+
|
147 |
+
(props @danielbachhuber, [#1825](https://github.com/WP-API/WP-API/pull/1825))
|
148 |
+
|
149 |
+
- BREAKING CHANGE: Remove `rest_get_timezone()`.
|
150 |
+
|
151 |
+
`json_get_timezone()` was only ever used in v1. This function causes fatals, and shouldn't be used.
|
152 |
+
|
153 |
+
(props @danielbachhuber, [#1823](https://github.com/WP-API/WP-API/pull/1823))
|
154 |
+
|
155 |
+
- BREAKING CHANGE: Rename `register_api_field()` to `register_rest_field()`.
|
156 |
+
|
157 |
+
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.
|
158 |
+
|
159 |
+
(props @danielbachhuber, [#1824](https://github.com/WP-API/WP-API/pull/1824))
|
160 |
+
|
161 |
+
- BREAKING CHANGE: Change taxonomies' `post_type` argument to `type`.
|
162 |
+
|
163 |
+
It's consistent with how we're exposing post types in the API.
|
164 |
+
|
165 |
+
(props @danielbachhuber, [#1824](https://github.com/WP-API/WP-API/pull/1824))
|
166 |
+
|
167 |
+
- Sync infrastructure with shipped in WordPress 4.4.
|
168 |
+
|
169 |
+
* `wp-includes/rest-api/rest-functions.php` is removed, and its functions moved into `wp-includes/rest-api.php`.
|
170 |
+
* Send nocache headers for REST requests. [#34832](https://core.trac.wordpress.org/ticket/34832)
|
171 |
+
* Fix handling of HEAD requests. [#34837](https://core.trac.wordpress.org/ticket/34837)
|
172 |
+
* Mark `WP_REST_Server::get_raw_data()` as static. [#34768](https://core.trac.wordpress.org/ticket/34768)
|
173 |
+
* Unabbreviate error string. [#34818](https://core.trac.wordpress.org/ticket/34818)
|
174 |
+
|
175 |
+
- Change terms endpoints to use `term_id` not `tt_id`.
|
176 |
+
|
177 |
+
(props @joehoyle, [#1837](https://github.com/WP-API/WP-API/pull/1837))
|
178 |
+
|
179 |
+
- Standardize declaration of `context` param for `GET` requests across controllers.
|
180 |
+
|
181 |
+
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.
|
182 |
+
|
183 |
+
(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))
|
184 |
+
|
185 |
+
- Link types / taxonomies to their collections, and vice versa.
|
186 |
+
|
187 |
+
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.
|
188 |
+
|
189 |
+
(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))
|
190 |
+
|
191 |
+
- Add missing 'wp/v2' in Location Response header when creating new Post Meta.
|
192 |
+
|
193 |
+
(props @johanmynhardt, [#1790](https://github.com/WP-API/WP-API/pull/1790))
|
194 |
+
|
195 |
+
- Expose Post collection query params, including `author`, `order`, `orderby` and `status`.
|
196 |
+
|
197 |
+
(props @danielbachhuber, [#1793](https://github.com/WP-API/WP-API/pull/1793))
|
198 |
+
|
199 |
+
- Ignore sticky posts by default.
|
200 |
+
|
201 |
+
(props @danielbachhuber, [#1801](https://github.com/WP-API/WP-API/pull/1801))
|
202 |
+
|
203 |
+
- Include `full` image size in attachment `sizes` attribute.
|
204 |
+
|
205 |
+
(props @danielbachhuber, [#1806](https://github.com/WP-API/WP-API/pull/1806))
|
206 |
+
|
207 |
+
- In text strings, use `id` instead of `ID`.
|
208 |
+
|
209 |
+
`ID` is an implementation artifact. Our Resources use `id`.
|
210 |
+
|
211 |
+
(props @danielbachhuber, [#1803](https://github.com/WP-API/WP-API/pull/1803))
|
212 |
+
|
213 |
+
- Ensure `attachment.sizes[]` use `mime_type` instead of `mime-type`.
|
214 |
+
|
215 |
+
(props @danielbachhuber, [#1809](https://github.com/WP-API/WP-API/pull/1809))
|
216 |
+
|
217 |
+
- Introduce `rest_authorization_required_code()`.
|
218 |
+
|
219 |
+
Many controllers returned incorrect HTTP codes, which this also fixes.
|
220 |
+
|
221 |
+
(props @danielbachhuber, [#1808](https://github.com/WP-API/WP-API/pull/1808))
|
222 |
+
|
223 |
+
- Respect core's `comment_registration` setting.
|
224 |
+
|
225 |
+
If it's enabled, require users to be logged in to comment.
|
226 |
+
|
227 |
+
(props @danielbachhuber, [#1826](https://github.com/WP-API/WP-API/pull/1826))
|
228 |
+
|
229 |
+
- Default to wildcard when searching users.
|
230 |
+
|
231 |
+
(props @danielbachhuber, [#1827](https://github.com/WP-API/WP-API/pull/1827))
|
232 |
+
|
233 |
+
- Bring the wp-api.js library up to date for v2 of the REST API.
|
234 |
+
|
235 |
+
(props @adamsilverstein, [#1828](https://github.com/WP-API/WP-API/pull/1828))
|
236 |
+
|
237 |
+
- Add `rest_prepare_status` filter.
|
238 |
+
|
239 |
+
(props @danielbachhuber, [#1830](https://github.com/WP-API/WP-API/pull/1830))
|
240 |
+
|
241 |
+
- Make `prepare_*` filters more consistent.
|
242 |
+
|
243 |
+
(props @danielbachhuber, [#1831](https://github.com/WP-API/WP-API/pull/1831))
|
244 |
+
|
245 |
+
- Add `rest_prepare_post_type` filter for post types.
|
246 |
+
|
247 |
+
(props @danielbachhuber, [#1833](https://github.com/WP-API/WP-API/pull/1833))
|
248 |
+
|
249 |
+
## 2.0 Beta 8.0
|
250 |
+
|
251 |
+
- Prevent fatals when uploading attachment by including admin utilities.
|
252 |
+
|
253 |
+
(props @danielbachhuber, [#1756](https://github.com/WP-API/WP-API/pull/1756))
|
254 |
+
|
255 |
+
- Return 201 status code when creating a term.
|
256 |
+
|
257 |
+
(props @danielbachhuber, [#1753](https://github.com/WP-API/WP-API/pull/1753))
|
258 |
+
|
259 |
+
- Don't permit requesting terms cross routes.
|
260 |
+
|
261 |
+
Clients should only be able to request categories from the category route, and tags from the tag route.
|
262 |
+
|
263 |
+
(props @danielbachhuber, [#1764](https://github.com/WP-API/WP-API/pull/1764))
|
264 |
+
|
265 |
+
- Set `fields=>id` when using `WP_User_Query` to fix large memory usage
|
266 |
+
|
267 |
+
(props @joehoyle, [#1770](https://github.com/WP-API/WP-API/pull/1770))
|
268 |
+
|
269 |
+
- Fix Post `_link` to attached attachments.
|
270 |
+
|
271 |
+
(props @danielbachhuber, [#1777](https://github.com/WP-API/WP-API/pull/1777))
|
272 |
+
|
273 |
+
- Add support for getting a post with a custom public status.
|
274 |
+
|
275 |
+
(props @danielbachhuber, [#1765](https://github.com/WP-API/WP-API/pull/1765))
|
276 |
+
|
277 |
+
- Ensure post content doesn't get double-slashed on update.
|
278 |
+
|
279 |
+
(props @joehoyle, [#1772](https://github.com/WP-API/WP-API/pull/1772))
|
280 |
+
|
281 |
+
- Change 'int' to 'integer' for `WP_REST_Controller::validate_schema_property()`
|
282 |
+
|
283 |
+
(props @wpsmith, [#1759](https://github.com/WP-API/WP-API/pull/1759))
|
284 |
+
|
285 |
+
## 2.0 Beta 7.0
|
286 |
+
|
287 |
+
- Sync infrastructure from WordPress core as of r35691.
|
288 |
+
|
289 |
+
* Remove `register_api_field()` because it's conceptually tied to `WP_REST_Controller` [#34730](https://core.trac.wordpress.org/ticket/34730)
|
290 |
+
* Update the REST API header links to use api.w.org [#34303](https://core.trac.wordpress.org/ticket/34303)
|
291 |
+
* Require the `$namespace` argument in `register_rest_route()` [#34416](https://core.trac.wordpress.org/ticket/34416)
|
292 |
+
* Include `enum` and `description` in help data [#34543](https://core.trac.wordpress.org/ticket/34543)
|
293 |
+
* Save `preg_match` iterations in `WP_REST_Server` [#34488](https://core.trac.wordpress.org/ticket/34488)
|
294 |
+
* Don't return route URL in `WP_REST_Request:get_params()` [#34647](https://core.trac.wordpress.org/ticket/34647)
|
295 |
+
|
296 |
+
- Restore `register_api_field()` within the plugin.
|
297 |
+
|
298 |
+
(props @danielbachhuber, [#1748](https://github.com/WP-API/WP-API/pull/1748))
|
299 |
+
|
300 |
+
- Require admin functions for use of `wp_handle_upload()`, fixing fatal.
|
301 |
+
|
302 |
+
(props @joehoyle, [#1746](https://github.com/WP-API/WP-API/pull/1746))
|
303 |
+
|
304 |
+
- Properly handle requesting terms where `parent=0` and `0` is a string.
|
305 |
+
|
306 |
+
(props @danielbachhuber, [#1739](https://github.com/WP-API/WP-API/pull/1739))
|
307 |
+
|
308 |
+
- Prevent PHP error notice when `&filter` isn't an array.
|
309 |
+
|
310 |
+
(props @danielbachhuber, [#1734](https://github.com/WP-API/WP-API/pull/1734))
|
311 |
+
|
312 |
+
- Change link relations to use api.w.org.
|
313 |
+
|
314 |
+
(props @danielbachhuber, [#1726](https://github.com/WP-API/WP-API/pull/1726))
|
315 |
+
|
316 |
+
|
317 |
+
## 2.0 Beta 6.0
|
318 |
+
|
319 |
+
- Remove global inclusion of wp-admin/includes/admin.php
|
320 |
+
|
321 |
+
For a long time, the REST API loaded wp-admin/includes/admin.php to make use of specific admin utilities. Now, it only loads those admin utilities when it needs them.
|
322 |
+
|
323 |
+
If your custom endpoints make use of admin utilities, you'll need to make sure to load wp-admin/includes/admin.php before you use them.
|
324 |
+
|
325 |
+
(props @joehoyle, [#1696](https://github.com/WP-API/WP-API/pull/1696))
|
326 |
+
|
327 |
+
- Link directly to the featured image in a Post's links.
|
328 |
+
|
329 |
+
(props @rmccue, [#1563](https://github.com/WP-API/WP-API/pull/1563), [#1711](https://github.com/WP-API/WP-API/pull/1711))
|
330 |
+
|
331 |
+
- Provide object type as callback argument for custom API fields.
|
332 |
+
|
333 |
+
(props @jtsternberg, [#1714](https://github.com/WP-API/WP-API/pull/1714))
|
334 |
+
|
335 |
+
- Change users schema order to be order of importance instead of alpha.
|
336 |
+
|
337 |
+
(props @rachelbaker, [#1708](https://github.com/WP-API/WP-API/pull/1708))
|
338 |
+
|
339 |
+
- Clarify documentation for `date` and `modified` attributes.
|
340 |
+
|
341 |
+
(props @danielbachhuber, [#1715](https://github.com/WP-API/WP-API/pull/1715))
|
342 |
+
|
343 |
+
- Update the wp-api.js client from the client-js repo.
|
344 |
+
|
345 |
+
(props @rachelbaker, [#1709](https://github.com/WP-API/WP-API/pull/1709))
|
346 |
+
|
347 |
+
- Fix the `format` enum to be an array of strings.
|
348 |
+
|
349 |
+
(props @joehoyle, [#1707](https://github.com/WP-API/WP-API/pull/1707))
|
350 |
+
|
351 |
+
- Run revisions for collection through `prepare_response_for_collection()`.
|
352 |
+
|
353 |
+
(props @danielbachhuber, @rachelbaker, [#1671](https://github.com/WP-API/WP-API/pull/1671))
|
354 |
+
|
355 |
+
- Expose `date_gmt` for `view` context of Posts and Comments.
|
356 |
+
|
357 |
+
(props @danielbachhuber, [#1690](https://github.com/WP-API/WP-API/pull/1690))
|
358 |
+
|
359 |
+
- Fix PHP and JS docblock formatting.
|
360 |
+
|
361 |
+
(props @ahmadawais, [#1699](https://github.com/WP-API/WP-API/pull/1698), [#1699](https://github.com/WP-API/WP-API/pull/1699), [#1701](https://github.com/WP-API/WP-API/pull/1701), [#1700](https://github.com/WP-API/WP-API/pull/1700), [#1702](https://github.com/WP-API/WP-API/pull/1702), [#1703](https://github.com/WP-API/WP-API/pull/1703))
|
362 |
+
|
363 |
+
- Include `media_details` attribute for attachments in embed context.
|
364 |
+
|
365 |
+
For image attachments, media_details includes a sizes array of image sizes, which is useful for templating.
|
366 |
+
|
367 |
+
(props @danielbachhuber, [#1667](https://github.com/WP-API/WP-API/pull/1667))
|
368 |
+
|
369 |
+
- Make `WP_REST_Controller` error messages more helpful by specifying method to subclass.
|
370 |
+
|
371 |
+
(props @danielbachhuber, [#1670](https://github.com/WP-API/WP-API/pull/1670))
|
372 |
+
|
373 |
+
- Expose `slug` in `embed` context for Users.
|
374 |
+
|
375 |
+
`user_nicename` is a public attribute, used in user URLs, so this is safe data to present.
|
376 |
+
|
377 |
+
(props @danielbachhuber, [#1666](https://github.com/WP-API/WP-API/pull/1666))
|
378 |
+
|
379 |
+
- Handle falsy value from `wp_count_terms()`, fixing fatal.
|
380 |
+
|
381 |
+
(props @joehoyle, [#1641](https://github.com/WP-API/WP-API/pull/1641))
|
382 |
+
|
383 |
+
- Correct methods in `WP_REST_SERVER::EDITABLE` description.
|
384 |
+
|
385 |
+
(props @rachelbaker, [#1601](https://github.com/WP-API/WP-API/pull/1601))
|
386 |
+
|
387 |
+
- Add the embed context to Users collection query params.
|
388 |
+
|
389 |
+
(props @rachelbaker, [#1591](https://github.com/WP-API/WP-API/pull/1591))
|
390 |
+
|
391 |
+
- Add Terms Controller collection args details.
|
392 |
+
|
393 |
+
(props @rachelbaker, [#1603](https://github.com/WP-API/WP-API/pull/1603))
|
394 |
+
|
395 |
+
- Set comment author details from current user.
|
396 |
+
|
397 |
+
(props @rmccue, [#1580](https://github.com/WP-API/WP-API/pull/1580))
|
398 |
+
|
399 |
+
- More hook documentation.
|
400 |
+
|
401 |
+
(props @adamsilverstein, [#1556](https://github.com/WP-API/WP-API/pull/1556), [#1560](https://github.com/WP-API/WP-API/pull/1560))
|
402 |
+
|
403 |
+
- Return the trashed status of deleted posts/comments.
|
404 |
+
|
405 |
+
When a post or a comment is deleted, returns a flag to say whether it's been trashed or properly deleted.
|
406 |
+
|
407 |
+
(props @pento, [#1499](https://github.com/WP-API/WP-API/pull/1499))
|
408 |
+
|
409 |
+
- In `WP_REST_Posts_Controller::update_item()`, check the post ID based on the proper post type.
|
410 |
+
|
411 |
+
(props @rachelbaker, [#1497](https://github.com/WP-API/WP-API/pull/1497))
|
412 |
+
|
413 |
+
## 2.0 Beta 5.0
|
414 |
+
|
415 |
+
- Load api-core as a compatibility library
|
416 |
+
|
417 |
+
Now api-core has been merged into WordPress trunk (for 4.4) we should no longer load the infrastructure code
|
418 |
+
when it's already available. This also fixes a fatal error for users who were on trunk.
|
419 |
+
|
420 |
+
(props @rmccue)
|
421 |
+
|
422 |
+
- Switch to new mysql_to_rfc3339
|
423 |
+
|
424 |
+
(props @rmccue)
|
425 |
+
|
426 |
+
- Double-check term taxonomy
|
427 |
+
|
428 |
+
(props @rmccue)
|
429 |
+
|
430 |
+
- Load admin functions
|
431 |
+
|
432 |
+
This was removed from the latest beta of WordPress in the REST API infrastructure, a more long term fix is planned.
|
433 |
+
|
434 |
+
(props @joehoyle)
|
435 |
+
|
436 |
+
- Add Add compat shim for renamed `rest_mysql_to_rfc3339()`
|
437 |
+
|
438 |
+
(props @danielbachhuber)
|
439 |
+
|
440 |
+
- Compat shim for `wp_is_numeric_array()`
|
441 |
+
|
442 |
+
(props @danielbachhuber)
|
443 |
+
|
444 |
+
- Revert Switch to register_post_type_args filter
|
445 |
+
|
446 |
+
(props @joehoyle)
|
447 |
+
|
448 |
+
## 2.0 Beta 4.0
|
449 |
+
|
450 |
+
- Show public user information through the user controller.
|
451 |
+
|
452 |
+
In WordPress as of [r32683](https://core.trac.wordpress.org/changeset/32683) (scheduled for 4.3), `WP_User_Query` now has support for getting users with published posts.
|
453 |
+
|
454 |
+
To match current behaviour in WordPress themes and feeds, we now expose this public user information. This includes the avatar, description, user ID, custom URL, display name, and URL, for users who have published at least one post on the site. This information is available to all clients; other fields and data for all users are still only available when authenticated.
|
455 |
+
|
456 |
+
(props @joehoyle, @rmccue, @Shelob9, [#1397][gh-1397], [#839][gh-839], [#1435][gh-1435])
|
457 |
+
|
458 |
+
- Send schema in OPTIONS requests and index.
|
459 |
+
|
460 |
+
Rather than using separate `/schema` endpoints, the schema for items is now available through an OPTIONS request to the route. This means that full documentation is now available for endpoints through an OPTIONS request; this includes available methods, what data you can pass to the endpoint, and the data you'll get back.
|
461 |
+
|
462 |
+
This data is now also available in the main index and namespace indexes. Simply request the index with `context=help` to get full schema data. Warning: this response will be huge. The schema for single endpoints is also available in the collection's OPTIONS response.
|
463 |
+
|
464 |
+
**⚠️ This breaks backwards compatibility** for clients relying on schemas being at their own routes. These clients should instead send `OPTIONS` requests.
|
465 |
+
|
466 |
+
Custom endpoints can register their own schema via the `schema` option on the route. This option should live side-by-side with the endpoints (similar to `relation` in WP's meta queries), so your registration call will look something like:
|
467 |
+
|
468 |
+
```php
|
469 |
+
register_rest_route( 'test-ns', '/test', array(
|
470 |
+
array(
|
471 |
+
'methods' => 'GET',
|
472 |
+
'callback' => 'my_test_callback',
|
473 |
+
),
|
474 |
+
|
475 |
+
'schema' => 'my_schema_callback',
|
476 |
+
) );
|
477 |
+
```
|
478 |
+
|
479 |
+
(props @rmccue, [#1415][gh-1415], [#1222][gh-1222], [#1305][gh-1305])
|
480 |
+
|
481 |
+
- Update JavaScript API for version 2.
|
482 |
+
|
483 |
+
Our fantastic JavaScript API from version 1 is now available for version 2, refreshed with the latest and greatest changes.
|
484 |
+
|
485 |
+
As a refresher: if you want to use it, simply make your script depend on `wp-api` when you enqueue it. If you want to enqueue the script manually, add `wp_enqueue_script( 'wp-api' )` to a callback on `wp_enqueue_scripts`.
|
486 |
+
|
487 |
+
(props @tlovett1, @kadamwhite, @nathanrice, [#1374][gh-1374], [#1320][gh-1320])
|
488 |
+
|
489 |
+
- Embed links inside items in a collection.
|
490 |
+
|
491 |
+
Previously when fetching a collection of items, you only received the items themselves. To fetch the links as well via embedding, you needed to make a request to the single item with `_embed` set.
|
492 |
+
|
493 |
+
No longer! You can now request a collection with embeds enabled (try `/wp/v2/posts?_embed`). This will embed links inside each item, allowing you to build interface items much easier (for example, post archive pages can get featured image data at the same time).
|
494 |
+
|
495 |
+
This also applies to custom endpoints. Any endpoint that returns a list of objects will automatically have the embedding applied to objects inside the list.
|
496 |
+
|
497 |
+
(props @rmccue, [#1459][gh-1459], [#865][gh-865])
|
498 |
+
|
499 |
+
- Fix potential XSS vulnerability.
|
500 |
+
|
501 |
+
Requests from other origins could potentially run code on the API domain, allowing cross-origin access to authentication cookies or similar.
|
502 |
+
|
503 |
+
Reported by @xknown on 2015-07-23.
|
504 |
+
|
505 |
+
- Move `/posts` `WP_Query` vars back to `filter` param.
|
506 |
+
|
507 |
+
In version 1, we had internal `WP_Query` vars available via `filter` (e.g. `filter[s]=search+term`). For our first betas of version 2, we tried something different and exposed these directly on the endpoint. The experiment has now concluded; we didn't like this that much, so `filter` is back.
|
508 |
+
|
509 |
+
We plan on adding nicer looking arguments to collections in future releases, with a view towards being consistent across different collections. We also plan on opening up the underlying query vars via `filter` for users, comments, and terms as well.
|
510 |
+
|
511 |
+
**⚠️ This breaks backwards compatibility** for users using WP Query vars. Simply change your `x=y` parameter to `filter[x]=y`.
|
512 |
+
|
513 |
+
(props @WP-API, [#1420][gh-1420])
|
514 |
+
|
515 |
+
- Respect `rest_base` for taxonomies.
|
516 |
+
|
517 |
+
**⚠️ This breaks backwards compatibility** by changing the `/wp/v2/posts/{id}/terms/post_tag` endpoint to `/wp/v2/posts/{id}/tag`.
|
518 |
+
|
519 |
+
(props @joehoyle, [#1466][gh-1466])
|
520 |
+
|
521 |
+
- Add permission check for retrieving the posts collection in edit context.
|
522 |
+
|
523 |
+
By extension of the fact that getting any individual post yields a forbidden context error when the `context=edit` and the user is not authorized, the user should also not be permitted to list any post items when unauthorized.
|
524 |
+
|
525 |
+
(props @danielpunkass, [#1412][gh-1412])
|
526 |
+
|
527 |
+
- Ensure the REST API URL always has a trailing slash.
|
528 |
+
|
529 |
+
Previously, when pretty permalinks were enabled, the API URL during autodiscovery looked like `/wp-json`, whereas the non-pretty permalink URL looked like `?rest_route=/`. These are now consistent, and always end with a slash character to simplify client URL building.
|
530 |
+
|
531 |
+
(props @danielpunkass, @rmccue, [#1426][gh-1426], [#1442][gh-1442], [#1455][gh-1455], [#1467][gh-1467])
|
532 |
+
|
533 |
+
- Use `wp_json_encode` instead of `json_encode`
|
534 |
+
|
535 |
+
Since WordPress 4.1, `wp_json_encode` has been available to ensure encoded values are sane, and that non-UTF8 encodings are supported. We now use this function rather than doing the encode ourselves.
|
536 |
+
|
537 |
+
(props @rmccue, @pento, [#1417][gh-1417])
|
538 |
+
|
539 |
+
- Add `role` to schema for users.
|
540 |
+
|
541 |
+
The available roles you can assign to a user are now available in the schema as an `enum`.
|
542 |
+
|
543 |
+
(props @joehoyle, [#1400][gh-1400])
|
544 |
+
|
545 |
+
- Use the schema for validation inside the comments controller.
|
546 |
+
|
547 |
+
Previously, the schema was merely a decorative element for documentation inside the comments controller. To bring it inline with our other controllers, the schema is now used internally for validation.
|
548 |
+
|
549 |
+
(props @joehoyle, [#1422][gh-1422])
|
550 |
+
|
551 |
+
- Don't set the Location header in update responses.
|
552 |
+
|
553 |
+
Previously, the Location header was sent when updating resources due to some inadvertent copypasta. This header should only be sent when creating to direct clients to the new resource, and isn't required when you're already on the correct resource.
|
554 |
+
|
555 |
+
(props @rachelbaker, [#1441][gh-1441])
|
556 |
+
|
557 |
+
- Re-enable the `rest_insert_post` action hook for `WP_REST_Posts_Controller`
|
558 |
+
|
559 |
+
This was disabled during 2.0 development to avoid breaking lots of plugins on the `json_insert_post` action. Now that we've changed namespaces and are Mostly Stable (tm), we can re-enable the action.
|
560 |
+
|
561 |
+
(props @jaredcobb, [#1427][gh-1427], [#1424][gh-1424])
|
562 |
+
|
563 |
+
- Fix post taxonomy terms link URLs.
|
564 |
+
|
565 |
+
When moving the routes in a previous beta, we forgot to correct the links on post objects to the new correct route. Sorry!
|
566 |
+
|
567 |
+
(props @rachelbaker, @joehoyle, [#1447][gh-1447], [#1383][gh-1383])
|
568 |
+
|
569 |
+
- Use `wp_get_attachment_image_src()` on the image sizes in attachments.
|
570 |
+
|
571 |
+
Since the first versions of the API, we've been building attachment URLs via `str_replace`. Who knows why we were doing this, but it caused problems with custom attachment URLs (such as CDN-hosted images). This now correctly uses the internal functions and filters.
|
572 |
+
|
573 |
+
(props @joehoyle, [#1462][gh-1462])
|
574 |
+
|
575 |
+
- Make the embed context a default, not forced.
|
576 |
+
|
577 |
+
If you want embeds to bring in full data rather than with `context=edit`, you can now change the link to specify `context=view` explicitly.
|
578 |
+
|
579 |
+
(props @rmccue, [#1464][gh-1464])
|
580 |
+
|
581 |
+
- Ensure we always use the `term_taxonomy_id` and never expose `term_id` publicly.
|
582 |
+
|
583 |
+
Previously, `term_id` was inadvertently exposed in some error responses.
|
584 |
+
|
585 |
+
(props @jdolan, [#1430][gh-1430])
|
586 |
+
|
587 |
+
- Fix adding alt text to attachments on creation.
|
588 |
+
|
589 |
+
Previously, this could only be set when updating an attachment, not when creating one.
|
590 |
+
|
591 |
+
(props @joehoyle, [#1398][gh-1398])
|
592 |
+
|
593 |
+
- Throw an error when registering routes without a namespace.
|
594 |
+
|
595 |
+
Namespaces should **always** be provided when registering routes. We now throw a `doing_it_wrong` error when attempting to register one. (Previously, this caused a warning, or an invalid internal route.)
|
596 |
+
|
597 |
+
If you *really* need to register namespaceless routes (e.g. to replicate an existing API), call `WP_REST_Server::register_route` directly rather than using the convenience function.
|
598 |
+
|
599 |
+
(props @joehoyle, @rmccue, [#1355][gh-1355])
|
600 |
+
|
601 |
+
- Show links on embeds.
|
602 |
+
|
603 |
+
Previously, links were accidentally stripped from embedded response data.
|
604 |
+
|
605 |
+
(props @rmccue, [#1472][gh-1472])
|
606 |
+
|
607 |
+
- Clarify insufficient permisssion error when editing posts.
|
608 |
+
|
609 |
+
(props @danielpunkass, [#1411][gh-1411])
|
610 |
+
|
611 |
+
- Improve @return inline docs for rest_ensure_response()
|
612 |
+
|
613 |
+
(props @Shelob9, [#1328][gh-1328])
|
614 |
+
|
615 |
+
- Check taxonomies exist before trying to set properties.
|
616 |
+
|
617 |
+
(props @joehoyle, @rachelbaker, [#1354][gh-1354])
|
618 |
+
|
619 |
+
- Update controllers to ensure we use `sanitize_callback` wherever possible.
|
620 |
+
|
621 |
+
(props @joehoyle, [#1399][gh-1399])
|
622 |
+
|
623 |
+
- Add more phpDoc documentation, and correct existing documentation.
|
624 |
+
|
625 |
+
(props @Shelob9, @rmccue, [#1432][gh-1432], [#1433][gh-1433], [#1465][gh-1465])
|
626 |
+
|
627 |
+
- Update testing infrastructure.
|
628 |
+
|
629 |
+
Travis now runs our coding standards tests in parallel, and now uses the new, faster container-based testing infrastructure.
|
630 |
+
|
631 |
+
(props @ntwb, @frozzare, [#1449][gh-1449], [#1457][gh-1457])
|
632 |
+
|
633 |
+
[View all changes](https://github.com/WP-API/WP-API/compare/2.0-beta3...2.0-beta4)
|
634 |
+
|
635 |
+
[gh-839]: https://github.com/WP-API/WP-API/issues/839
|
636 |
+
[gh-865]: https://github.com/WP-API/WP-API/issues/865
|
637 |
+
[gh-1222]: https://github.com/WP-API/WP-API/issues/1222
|
638 |
+
[gh-1305]: https://github.com/WP-API/WP-API/issues/1305
|
639 |
+
[gh-1310]: https://github.com/WP-API/WP-API/issues/1310
|
640 |
+
[gh-1320]: https://github.com/WP-API/WP-API/issues/1320
|
641 |
+
[gh-1328]: https://github.com/WP-API/WP-API/issues/1328
|
642 |
+
[gh-1354]: https://github.com/WP-API/WP-API/issues/1354
|
643 |
+
[gh-1355]: https://github.com/WP-API/WP-API/issues/1355
|
644 |
+
[gh-1372]: https://github.com/WP-API/WP-API/issues/1372
|
645 |
+
[gh-1374]: https://github.com/WP-API/WP-API/issues/1374
|
646 |
+
[gh-1383]: https://github.com/WP-API/WP-API/issues/1383
|
647 |
+
[gh-1397]: https://github.com/WP-API/WP-API/issues/1397
|
648 |
+
[gh-1398]: https://github.com/WP-API/WP-API/issues/1398
|
649 |
+
[gh-1399]: https://github.com/WP-API/WP-API/issues/1399
|
650 |
+
[gh-1400]: https://github.com/WP-API/WP-API/issues/1400
|
651 |
+
[gh-1402]: https://github.com/WP-API/WP-API/issues/1402
|
652 |
+
[gh-1411]: https://github.com/WP-API/WP-API/issues/1411
|
653 |
+
[gh-1412]: https://github.com/WP-API/WP-API/issues/1412
|
654 |
+
[gh-1413]: https://github.com/WP-API/WP-API/issues/1413
|
655 |
+
[gh-1415]: https://github.com/WP-API/WP-API/issues/1415
|
656 |
+
[gh-1417]: https://github.com/WP-API/WP-API/issues/1417
|
657 |
+
[gh-1420]: https://github.com/WP-API/WP-API/issues/1420
|
658 |
+
[gh-1422]: https://github.com/WP-API/WP-API/issues/1422
|
659 |
+
[gh-1424]: https://github.com/WP-API/WP-API/issues/1424
|
660 |
+
[gh-1426]: https://github.com/WP-API/WP-API/issues/1426
|
661 |
+
[gh-1427]: https://github.com/WP-API/WP-API/issues/1427
|
662 |
+
[gh-1430]: https://github.com/WP-API/WP-API/issues/1430
|
663 |
+
[gh-1432]: https://github.com/WP-API/WP-API/issues/1432
|
664 |
+
[gh-1433]: https://github.com/WP-API/WP-API/issues/1433
|
665 |
+
[gh-1435]: https://github.com/WP-API/WP-API/issues/1435
|
666 |
+
[gh-1441]: https://github.com/WP-API/WP-API/issues/1441
|
667 |
+
[gh-1442]: https://github.com/WP-API/WP-API/issues/1442
|
668 |
+
[gh-1447]: https://github.com/WP-API/WP-API/issues/1447
|
669 |
+
[gh-1449]: https://github.com/WP-API/WP-API/issues/1449
|
670 |
+
[gh-1455]: https://github.com/WP-API/WP-API/issues/1455
|
671 |
+
[gh-1455]: https://github.com/WP-API/WP-API/issues/1455
|
672 |
+
[gh-1457]: https://github.com/WP-API/WP-API/issues/1457
|
673 |
+
[gh-1459]: https://github.com/WP-API/WP-API/issues/1459
|
674 |
+
[gh-1462]: https://github.com/WP-API/WP-API/issues/1462
|
675 |
+
[gh-1464]: https://github.com/WP-API/WP-API/issues/1464
|
676 |
+
[gh-1465]: https://github.com/WP-API/WP-API/issues/1465
|
677 |
+
[gh-1466]: https://github.com/WP-API/WP-API/issues/1466
|
678 |
+
[gh-1467]: https://github.com/WP-API/WP-API/issues/1467
|
679 |
+
[gh-1472]: https://github.com/WP-API/WP-API/issues/1472
|
680 |
+
|
681 |
## 2.0 Beta 3.0
|
682 |
|
683 |
- Add ability to declare sanitization and default options for schema fields.
|
README.md
CHANGED
@@ -4,6 +4,7 @@ Access your WordPress site's data through an easy-to-use HTTP REST API.
|
|
4 |
|
5 |
[![Build Status](https://travis-ci.org/WP-API/WP-API.svg?branch=develop)](https://travis-ci.org/WP-API/WP-API)
|
6 |
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/WP-API/WP-API/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/WP-API/WP-API/?branch=develop)
|
|
|
7 |
|
8 |
## WARNING
|
9 |
|
@@ -26,7 +27,7 @@ Retrieving or updating data is as simple as sending a HTTP request.
|
|
26 |
|
27 |
Want to get your site's posts? Simply send a `GET` request to `/wp-json/wp/v2/posts`.
|
28 |
Update user with ID 4? Send a `POST` request to `/wp-json/wp/v2/users/4`. Get all
|
29 |
-
posts with the search term "awesome"? `GET /wp-json/wp/v2/posts?s=awesome`.
|
30 |
It's that easy.
|
31 |
|
32 |
WP API exposes a simple yet easy interface to WP Query, the posts API, post meta
|
@@ -49,6 +50,8 @@ There's no fixed timeline for integration into core at this time, but getting cl
|
|
49 |
Drop this directory in and activate it. You need to be using pretty permalinks
|
50 |
to use the plugin, as it uses custom rewrite rules to power the API.
|
51 |
|
|
|
|
|
52 |
## Issue Tracking
|
53 |
|
54 |
All tickets for the project are being tracked on [GitHub][]. You can also take a
|
4 |
|
5 |
[![Build Status](https://travis-ci.org/WP-API/WP-API.svg?branch=develop)](https://travis-ci.org/WP-API/WP-API)
|
6 |
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/WP-API/WP-API/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/WP-API/WP-API/?branch=develop)
|
7 |
+
[![codecov.io](http://codecov.io/github/WP-API/WP-API/coverage.svg?branch=develop)](http://codecov.io/github/WP-API/WP-API?branch=develop)
|
8 |
|
9 |
## WARNING
|
10 |
|
27 |
|
28 |
Want to get your site's posts? Simply send a `GET` request to `/wp-json/wp/v2/posts`.
|
29 |
Update user with ID 4? Send a `POST` request to `/wp-json/wp/v2/users/4`. Get all
|
30 |
+
posts with the search term "awesome"? `GET /wp-json/wp/v2/posts?filter[s]=awesome`.
|
31 |
It's that easy.
|
32 |
|
33 |
WP API exposes a simple yet easy interface to WP Query, the posts API, post meta
|
50 |
Drop this directory in and activate it. You need to be using pretty permalinks
|
51 |
to use the plugin, as it uses custom rewrite rules to power the API.
|
52 |
|
53 |
+
Also, be sure to use the Subversion `trunk` branch of WordPress Core as there are potentially recent commits to Core that the REST API relies on. See the [WordPress.org website](https://wordpress.org/download/svn/) for simple instructions.
|
54 |
+
|
55 |
## Issue Tracking
|
56 |
|
57 |
All tickets for the project are being tracked on [GitHub][]. You can also take a
|
compatibility-v1.php
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
add_filter( 'json_endpoints', 'json_v1_compatible_routes', 1000 );
|
4 |
-
add_filter( 'json_dispatch_request', 'json_v1_compatible_dispatch', 10, 3 );
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Make version 1 routes compatible with v2
|
8 |
-
*
|
9 |
-
* @param array $routes API routes
|
10 |
-
* @return array Filtered routes
|
11 |
-
*/
|
12 |
-
function json_v1_compatible_routes( $routes ) {
|
13 |
-
foreach ( $routes as $key => &$route ) {
|
14 |
-
// Single, with new-style registration
|
15 |
-
if ( isset( $route['callback'] ) || empty( $route ) ) {
|
16 |
-
continue;
|
17 |
-
}
|
18 |
-
|
19 |
-
// Multiple, with new-style registration
|
20 |
-
$first = reset( $route );
|
21 |
-
if ( isset( $first['callback'] ) ) {
|
22 |
-
continue;
|
23 |
-
}
|
24 |
-
|
25 |
-
// Old-style, map to new-style
|
26 |
-
if ( count( $route ) <= 2 && isset( $route[1] ) && ! is_array( $route[1] ) ) {
|
27 |
-
$route = array( $route );
|
28 |
-
}
|
29 |
-
|
30 |
-
foreach ( $route as &$handler ) {
|
31 |
-
$methods = isset( $handler[1] ) ? $handler[1] : WP_REST_Server::METHOD_GET;
|
32 |
-
|
33 |
-
$handler = array(
|
34 |
-
'callback' => $handler[0],
|
35 |
-
'methods' => $methods,
|
36 |
-
'v1_compat' => true,
|
37 |
-
);
|
38 |
-
}
|
39 |
-
}
|
40 |
-
|
41 |
-
return $routes;
|
42 |
-
}
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Use Reflection to match request parameters to function parameters
|
46 |
-
*
|
47 |
-
* @param mixed $result Result to use
|
48 |
-
* @param WP_JSON_Request $request Request object
|
49 |
-
* @return mixed
|
50 |
-
*/
|
51 |
-
function json_v1_compatible_dispatch( $result, $request ) {
|
52 |
-
// Allow other plugins to hijack too
|
53 |
-
if ( null !== $result ) {
|
54 |
-
return $result;
|
55 |
-
}
|
56 |
-
|
57 |
-
// Do we need the compatibility shim?
|
58 |
-
$params = $request->get_attributes();
|
59 |
-
if ( empty( $params['v1_compat'] ) ) {
|
60 |
-
return $result;
|
61 |
-
}
|
62 |
-
|
63 |
-
// Build up the arguments, old-style
|
64 |
-
$args = array_merge( $request->get_url_params(), $request->get_query_params() );
|
65 |
-
if ( $request->get_method() === 'POST' ) {
|
66 |
-
$args = array_merge( $args, $request->get_body_params() );
|
67 |
-
}
|
68 |
-
|
69 |
-
$args = json_v1_sort_callback_params( $params['callback'], $args );
|
70 |
-
if ( is_wp_error( $args ) ) {
|
71 |
-
return $args;
|
72 |
-
}
|
73 |
-
|
74 |
-
return call_user_func_array( $params['callback'], $args );
|
75 |
-
}
|
76 |
-
|
77 |
-
/**
|
78 |
-
* Sort parameters by order specified in method declaration
|
79 |
-
*
|
80 |
-
* Takes a callback and a list of available params, then filters and sorts
|
81 |
-
* by the parameters the method actually needs, using the Reflection API
|
82 |
-
*
|
83 |
-
* @param callback $callback
|
84 |
-
* @param array $params
|
85 |
-
* @return array
|
86 |
-
*/
|
87 |
-
function json_v1_sort_callback_params( $callback, $provided ) {
|
88 |
-
if ( is_array( $callback ) ) {
|
89 |
-
$ref_func = new ReflectionMethod( $callback[0], $callback[1] );
|
90 |
-
} else {
|
91 |
-
$ref_func = new ReflectionFunction( $callback );
|
92 |
-
}
|
93 |
-
|
94 |
-
$wanted = $ref_func->getParameters();
|
95 |
-
$ordered_parameters = array();
|
96 |
-
|
97 |
-
foreach ( $wanted as $param ) {
|
98 |
-
if ( isset( $provided[ $param->getName() ] ) ) {
|
99 |
-
// We have this parameters in the list to choose from
|
100 |
-
$ordered_parameters[] = $provided[ $param->getName() ];
|
101 |
-
} elseif ( $param->isDefaultValueAvailable() ) {
|
102 |
-
// We don't have this parameter, but it's optional
|
103 |
-
$ordered_parameters[] = $param->getDefaultValue();
|
104 |
-
} else {
|
105 |
-
// We don't have this parameter and it wasn't optional, abort!
|
106 |
-
return new WP_Error( 'json_missing_callback_param', sprintf( __( 'Missing parameter %s' ), $param->getName() ), array( 'status' => 400 ) );
|
107 |
-
}
|
108 |
-
}
|
109 |
-
return $ordered_parameters;
|
110 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/README.md
DELETED
@@ -1,26 +0,0 @@
|
|
1 |
-
API Documentation
|
2 |
-
=================
|
3 |
-
Learn how the JSON REST API works from the ground up!
|
4 |
-
|
5 |
-
First time interacting with the API? Start with the [Getting Started][] guide,
|
6 |
-
which will introduce you to the basic concepts for working with the API.
|
7 |
-
|
8 |
-
From there, progress on to other [guides][] to learn in detail about parts of
|
9 |
-
the API.
|
10 |
-
|
11 |
-
Take a look at more detailed information on [post][post-routes] or
|
12 |
-
[media][media-routes], or read about [maximizing compatibility][compatibility]
|
13 |
-
with older clients.
|
14 |
-
|
15 |
-
Dive in deeper into the [schema details][schema] to better understand the little
|
16 |
-
details, or read about the [philosophy][] behind them. Read about the
|
17 |
-
[implementation details][implementation] on how the API works internally.
|
18 |
-
|
19 |
-
[Getting Started]: http://wp-api.org/guides/getting-started.html
|
20 |
-
[guides]: http://wp-api.org/guides.html
|
21 |
-
[post-routes]: http://wp-api.org/#posts
|
22 |
-
[media-routes]: http://wp-api.org/#media
|
23 |
-
[compatibility]: compatibility.md
|
24 |
-
[schema]: schema.md
|
25 |
-
[philosophy]: internals/philosophy.md
|
26 |
-
[implementation]: internals/implementation.md
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/routes/routes.md
DELETED
@@ -1,1569 +0,0 @@
|
|
1 |
-
Posts
|
2 |
-
=====
|
3 |
-
|
4 |
-
Create a Post
|
5 |
-
-------------
|
6 |
-
|
7 |
-
POST /posts
|
8 |
-
|
9 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
10 |
-
|
11 |
-
### Input
|
12 |
-
The `data` parameter consists of the elements of the Post object to be
|
13 |
-
created. This data can be submitted via a regular HTTP multipart body, with
|
14 |
-
the Post keys and values set to the `data` parameter, or through a direct JSON
|
15 |
-
body.
|
16 |
-
|
17 |
-
That is, the following are equivalent:
|
18 |
-
|
19 |
-
```
|
20 |
-
Content-Type: application/x-www-form-urlencoded
|
21 |
-
|
22 |
-
data[title]=Hello%20World!&data[content_raw]=Content&data[excerpt_raw]=Excerpt
|
23 |
-
```
|
24 |
-
|
25 |
-
```
|
26 |
-
Content-Type: application/json
|
27 |
-
|
28 |
-
{"title":"Hello World!","content_raw":"Content","excerpt_raw":"Excerpt"}
|
29 |
-
```
|
30 |
-
|
31 |
-
The `data` parameter should be an object containing the following key value
|
32 |
-
pairs:
|
33 |
-
|
34 |
-
* `title` - Title of the post. (string) __*required*__
|
35 |
-
* `content_raw` - Full text of the post. (string) __*required*__
|
36 |
-
* `excerpt_raw` - Text for excerpt of the post. (string) *optional*
|
37 |
-
* `name` - Slug of the post. (string) *optional*
|
38 |
-
* `status` - Post status of the post: `draft`, `publish`, `pending`, `future`,
|
39 |
-
`private`, or any custom registered status. If providing a status of
|
40 |
-
`future`, you must specify a `date` in order for the post to be published as
|
41 |
-
expected. Default is `draft`. (string) *optional*
|
42 |
-
* `type` - Post type of the post: `post`, `page`, `link`, `nav_menu_item`, or
|
43 |
-
a any custom registered type. Default is `post`. (string) *optional*
|
44 |
-
* `date` - Date and time the post was, or should be, published in local time.
|
45 |
-
Date should be an RFC3339 timestamp](http://tools.ietf.org/html/rfc3339).
|
46 |
-
Example: 2014-01-01T12:20:52Z. Default is the local date and time. (string)
|
47 |
-
*optional*
|
48 |
-
* `date_gmt` - Date and time the post was, or should be, published in UTC time.
|
49 |
-
Date should be an [RFC3339 timestamp](http://tools.ietf.org/html/rfc3339).
|
50 |
-
Example: 201401-01T12:20:52Z. Default is the current GMT date and time.
|
51 |
-
(string) *optional*
|
52 |
-
* `author` - Author of the post. Author can be provided as a string of the
|
53 |
-
author's ID or as the User object of the author. Default is current user.
|
54 |
-
(object \| string) *optional*
|
55 |
-
* `password` - Password for protecting the post. Default is empty string.
|
56 |
-
(string) *optional*
|
57 |
-
* `post_parent` - Post ID of the post parent. Default is 0. (integer)
|
58 |
-
*optional*
|
59 |
-
* `post_format` - Format of the post. Default is `standard`. (string)
|
60 |
-
*optional*
|
61 |
-
* `menu_order` - The order in which posts specified as the `page` type should
|
62 |
-
appear in supported menus. Default 0. (integer) *optional*
|
63 |
-
* `comment_status` - Comment status for the post: `open` or `closed`.
|
64 |
-
Indicates whether users can submit comments to the post. Default is the
|
65 |
-
option 'default_comment_status', or 'closed'. (string) *optional*
|
66 |
-
* `ping_status` - Ping status for the post: `open` or `closed`. Indicates
|
67 |
-
whether users can submit pingbacks or trackbacks to the post. Default is the
|
68 |
-
option 'default_ping_status'. (string) *optional*
|
69 |
-
* `sticky` - Sticky status for the post: `true` or `false`. Default is
|
70 |
-
`false`. (boolean) *optional*
|
71 |
-
* `post_meta` - Post meta entries of the post. Post meta should be an array
|
72 |
-
of one or more Meta objects for each post meta entry. See the Create Meta
|
73 |
-
for a Post endpoint for the key value pairs. (array) *optional*
|
74 |
-
|
75 |
-
|
76 |
-
### Response
|
77 |
-
On a successful creation, a 201 Created status is given, indicating that the
|
78 |
-
post has been created. The post is available canonically from the URL specified
|
79 |
-
in the Location header.
|
80 |
-
|
81 |
-
The new Post entity is also returned in the body for convienience.
|
82 |
-
|
83 |
-
If the client is not authenticated, a 403 Forbidden response is given.
|
84 |
-
|
85 |
-
Retrieve Posts
|
86 |
-
--------------
|
87 |
-
The Posts endpoint returns a Post Collection containing a subset of the site's
|
88 |
-
posts.
|
89 |
-
|
90 |
-
GET /posts
|
91 |
-
|
92 |
-
### Input
|
93 |
-
#### `filter`
|
94 |
-
The `filter` parameter controls the parameters used to query for posts.
|
95 |
-
|
96 |
-
**Note:** Only "public" query variables are available via the API, as not all
|
97 |
-
query variables are safe to expose. "Private" query variables are also available
|
98 |
-
when authenticated as a user with `edit_posts`. Other query variables can be
|
99 |
-
registered via the `query_vars` filter, or `json_query_vars` for API-specific
|
100 |
-
query variables.
|
101 |
-
|
102 |
-
Extended documentation on the query variables is available from
|
103 |
-
[the codex](http://codex.wordpress.org/Class_Reference/WP_Query).
|
104 |
-
|
105 |
-
The following query variables are available to the API:
|
106 |
-
|
107 |
-
* `m`
|
108 |
-
* `p`
|
109 |
-
* `posts`
|
110 |
-
* `w`
|
111 |
-
* `cat`
|
112 |
-
* `withcomments`
|
113 |
-
* `withoutcomments`
|
114 |
-
* `s`
|
115 |
-
* `search`
|
116 |
-
* `exact`
|
117 |
-
* `sentence`
|
118 |
-
* `calendar`
|
119 |
-
* `page`
|
120 |
-
* `paged`
|
121 |
-
* `more`
|
122 |
-
* `tb`
|
123 |
-
* `pb`
|
124 |
-
* `author`
|
125 |
-
* `order`
|
126 |
-
* `orderby`
|
127 |
-
* `year`
|
128 |
-
* `monthnum`
|
129 |
-
* `day`
|
130 |
-
* `hour`
|
131 |
-
* `minute`
|
132 |
-
* `second`
|
133 |
-
* `name`
|
134 |
-
* `category_name`
|
135 |
-
* `tag`
|
136 |
-
* `feed`
|
137 |
-
* `author_name`
|
138 |
-
* `static`
|
139 |
-
* `pagename`
|
140 |
-
* `page_id`
|
141 |
-
* `error`
|
142 |
-
* `comments_popup`
|
143 |
-
* `attachment`
|
144 |
-
* `attachment_id`
|
145 |
-
* `subpost`
|
146 |
-
* `subpost_id`
|
147 |
-
* `preview`
|
148 |
-
* `robots`
|
149 |
-
* `taxonomy`
|
150 |
-
* `term`
|
151 |
-
* `cpage`
|
152 |
-
* `posts_per_page`
|
153 |
-
|
154 |
-
In addition, the following are available when authenticated as a user with
|
155 |
-
`edit_posts`:
|
156 |
-
|
157 |
-
* `offset`
|
158 |
-
* `posts_per_archive_page`
|
159 |
-
* `showposts`
|
160 |
-
* `nopaging`
|
161 |
-
* `post_type`
|
162 |
-
* `post_status`
|
163 |
-
* `category__in`
|
164 |
-
* `category__not_in`
|
165 |
-
* `category__and`
|
166 |
-
* `tag__in`
|
167 |
-
* `tag__not_in`
|
168 |
-
* `tag__and`
|
169 |
-
* `tag_slug__in`
|
170 |
-
* `tag_slug__and`
|
171 |
-
* `tag_id`
|
172 |
-
* `post_mime_type`
|
173 |
-
* `perm`
|
174 |
-
* `comments_per_page`
|
175 |
-
* `post__in`
|
176 |
-
* `post__not_in`
|
177 |
-
* `post_parent`
|
178 |
-
* `post_parent__in`
|
179 |
-
* `post_parent__not_in`
|
180 |
-
|
181 |
-
```
|
182 |
-
GET /posts?filter[posts_per_page]=8&filter[order]=ASC
|
183 |
-
```
|
184 |
-
|
185 |
-
#### `context`
|
186 |
-
The `context` parameter controls the format of the data to return. See the
|
187 |
-
Retrieve a Post endpoint for available contexts.
|
188 |
-
|
189 |
-
Default is "view". (string)
|
190 |
-
|
191 |
-
|
192 |
-
#### `type`
|
193 |
-
The `type` parameter specifies the post type to retrieve. This can either be a
|
194 |
-
string or an array of types.
|
195 |
-
|
196 |
-
Note that arrays are specified using the `[]` URL syntax. e.g.
|
197 |
-
|
198 |
-
```
|
199 |
-
GET /posts?type[]=post&type[]=page
|
200 |
-
```
|
201 |
-
|
202 |
-
Default is "post". (string)
|
203 |
-
|
204 |
-
|
205 |
-
### Response
|
206 |
-
The response is a Post Collection document containing the requested Posts if
|
207 |
-
available.
|
208 |
-
|
209 |
-
|
210 |
-
Retrieve a Post
|
211 |
-
---------------
|
212 |
-
|
213 |
-
GET /posts/<id>
|
214 |
-
|
215 |
-
### Input
|
216 |
-
#### `context`
|
217 |
-
The `context` parameter controls the format of the data to return. The
|
218 |
-
following contexts are available:
|
219 |
-
|
220 |
-
* `view`: The default context. Gives the normal User entity.
|
221 |
-
* `edit`: Context used for extra fields relevant to updating a user. Includes
|
222 |
-
the `title_raw`, `content_raw`, `guid_raw` and `post_meta` fields, suitable
|
223 |
-
for editing the post.
|
224 |
-
* `parent`: Context used when embedding the response inside another (e.g. post
|
225 |
-
author). This is intended as a minimal subset of the user data to reduce
|
226 |
-
response size. Returns the `parent` field as an ID, rather than an embedded
|
227 |
-
post, to ensure we don't traverse the entire post hierarchy.
|
228 |
-
|
229 |
-
### Response
|
230 |
-
The response is a Post entity containing the requested Post if available. The
|
231 |
-
fields available on the Post depend on the `context` parameter.
|
232 |
-
|
233 |
-
|
234 |
-
Edit a Post
|
235 |
-
-----------
|
236 |
-
|
237 |
-
PUT /posts/<id>
|
238 |
-
|
239 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
240 |
-
|
241 |
-
For compatibility reasons, this endpoint also accepts the POST and PATCH
|
242 |
-
methods. Both of these methods have the same behaviour as using PUT. It is
|
243 |
-
recommended to use PUT if available to fit with REST convention.
|
244 |
-
|
245 |
-
### Input
|
246 |
-
The `data` parameter consists of Post ID and the elements of the Post object
|
247 |
-
to be modified. This data can be submitted via a regular HTTP multipart body,
|
248 |
-
with the Post keys and values set to the `data` parameter, or through a direct
|
249 |
-
JSON body. See the Create Post endpoint for an example.
|
250 |
-
|
251 |
-
The `data` parameter should be an object containing the following key value
|
252 |
-
pairs:
|
253 |
-
|
254 |
-
* `ID` - Unique ID of the post. (integer) __*required*__
|
255 |
-
* `title` - Title of the post. (string) __*required*__
|
256 |
-
* `content_raw` - Full text of the post. (string) __*required*__
|
257 |
-
* `excerpt_raw` - Text for excerpt of the post. (string) *optional*
|
258 |
-
* `name` - Slug of the post. (string) *optional*
|
259 |
-
* `status` - Post status of the post: `draft`, `publish`, `pending`, `future`,
|
260 |
-
`private`, or any custom registered status. If providing a status of
|
261 |
-
`future`, you must specify a `date` in order for the post to be published as
|
262 |
-
expected. Default is `draft`. (string) *optional*
|
263 |
-
* `type` - Post type of the post: `post`, `page`, `link`, `nav_menu_item`, or
|
264 |
-
a any custom registered type. Default is `post`. (string) *optional*
|
265 |
-
* `date` - Date and time the post was, or should be, published in local time.
|
266 |
-
Date should be an RFC3339 timestamp](http://tools.ietf.org/html/rfc3339).
|
267 |
-
Example: 2014-01-01T12:20:52Z. Default is the local date and time. (string)
|
268 |
-
*optional*
|
269 |
-
* `date_gmt` - Date and time the post was, or should be, published in UTC time.
|
270 |
-
Date should be an [RFC3339 timestamp](http://tools.ietf.org/html/rfc3339).
|
271 |
-
Example: 201401-01T12:20:52Z. Default is the current GMT date and time.
|
272 |
-
(string) *optional*
|
273 |
-
* `author` - Author of the post. Author can be provided as a string of the
|
274 |
-
author's ID or as the User object of the author. Default is current user.
|
275 |
-
(object \| string) *optional*
|
276 |
-
* `password` - Password for protecting the post. Default is empty string.
|
277 |
-
(string) *optional*
|
278 |
-
* `post_parent` - Post ID of the post parent. Default is 0. (integer)
|
279 |
-
*optional*
|
280 |
-
* `post_format` - Format of the post. Default is `standard`. (string)
|
281 |
-
*optional*
|
282 |
-
* `menu_order` - The order in which posts specified as the `page` type should
|
283 |
-
appear in supported menus. Default 0. (integer) *optional*
|
284 |
-
* `comment_status` - Comment status for the post: `open` or `closed`.
|
285 |
-
Indicates whether users can submit comments to the post. Default is the
|
286 |
-
option 'default_comment_status', or 'closed'. (string) *optional*
|
287 |
-
* `ping_status` - Ping status for the post: `open` or `closed`. Indicates
|
288 |
-
whether users can submit pingbacks or trackbacks to the post. Default is the
|
289 |
-
option 'default_ping_status'. (string) *optional*
|
290 |
-
* `sticky` - Sticky status for the post: `true` or `false`. Default is
|
291 |
-
`false`. (boolean) *optional*
|
292 |
-
* `post_meta` - Post meta entries of the post. Post meta should be an array
|
293 |
-
of one or more Meta objects for each post meta entry. See the Edit Meta
|
294 |
-
for a Post endpoint for the key value pairs. (array) *optional*
|
295 |
-
|
296 |
-
|
297 |
-
### Response
|
298 |
-
On a successful update, a 200 OK status is given, indicating the post has been
|
299 |
-
updated. The updated Post entity is returned in the body.
|
300 |
-
|
301 |
-
If the client is not authenticated, a 403 Forbidden response is sent.
|
302 |
-
|
303 |
-
Delete a Post
|
304 |
-
-------------
|
305 |
-
|
306 |
-
DELETE /posts/<id>
|
307 |
-
|
308 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
309 |
-
|
310 |
-
### Input
|
311 |
-
#### `force`
|
312 |
-
The `force` parameter controls whether the post is permanently deleted or not.
|
313 |
-
By default, this is set to false, indicating that the post will be sent to an
|
314 |
-
intermediate storage (such as the trash) allowing it to be restored later. If
|
315 |
-
set to true, the post will not be able to be restored by the user.
|
316 |
-
|
317 |
-
Default is false. (boolean)
|
318 |
-
|
319 |
-
### Response
|
320 |
-
On successful deletion, a 202 Accepted status code will be returned, indicating
|
321 |
-
that the post has been moved to the trash for permanent deletion at a
|
322 |
-
later date.
|
323 |
-
|
324 |
-
If force was set to true, a 200 OK status code will be returned instead,
|
325 |
-
indicating that the post has been permanently deleted.
|
326 |
-
|
327 |
-
If the client is not authenticated, a 403 Forbidden status code will be returned.
|
328 |
-
|
329 |
-
Retrieve Revisions for a Post
|
330 |
-
------------------------
|
331 |
-
|
332 |
-
GET /posts/<id>/revisions
|
333 |
-
|
334 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
335 |
-
|
336 |
-
### Response
|
337 |
-
If successful, returns a 200 OK status code and revisions for the given post.
|
338 |
-
|
339 |
-
If the client is not authenticated, a 403 Forbidden status code will be returned.
|
340 |
-
|
341 |
-
|
342 |
-
Create Meta for a Post
|
343 |
-
------------------------
|
344 |
-
|
345 |
-
POST /posts/<id>/meta
|
346 |
-
|
347 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
348 |
-
|
349 |
-
Note that the access rules for metadata apply here (see [Retrieve Meta for
|
350 |
-
a Post](http://wp-api.org/#posts_retrieve-meta-for-a-post) ). Any submitted data that violates an access rule (e.g. sending
|
351 |
-
serialized data) will result in a 403 error.
|
352 |
-
|
353 |
-
### Input
|
354 |
-
The supplied data should be a Meta object. This data can be submitted via a
|
355 |
-
regular HTTP multipart body, with the Meta key and value set with the `data`
|
356 |
-
parameter, or through a direct JSON body.
|
357 |
-
|
358 |
-
The `data` parameter should be an object containing the following key value
|
359 |
-
pairs:
|
360 |
-
|
361 |
-
* `key` - The post meta key to be created. (string) *required*
|
362 |
-
* `value` - The post meta value for the key provided. (string) *required*
|
363 |
-
|
364 |
-
### Response
|
365 |
-
On a successful creation, a 201 Created status is given, indicating that the
|
366 |
-
Meta has been created. The post meta is available canonically from the URL
|
367 |
-
specified in the Location header.
|
368 |
-
|
369 |
-
The new Meta entity is also returned in the body for convienience.
|
370 |
-
|
371 |
-
If the client is not authenticated, a 403 Forbidden status code will be returned.
|
372 |
-
|
373 |
-
Retrieve Meta for a Post
|
374 |
-
------------------------
|
375 |
-
|
376 |
-
GET /posts/<id>/meta
|
377 |
-
|
378 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
379 |
-
|
380 |
-
WordPress metadata follows some special rules for access:
|
381 |
-
|
382 |
-
* Metadata is only available to authenticated clients, as the fields are "raw"
|
383 |
-
values from the database. The API cannot ensure that it's not leaking private
|
384 |
-
data, although we're working on changing WordPress to support this.
|
385 |
-
|
386 |
-
* "Complex" metadata is not available from the API. Only simple values, such as
|
387 |
-
numbers, strings, and booleans, are available via the meta endpoints. Complex
|
388 |
-
values, such as arrays and objects do not have a lossless (one-to-one)
|
389 |
-
representation in JSON. Exposing the serialized value could leak internal
|
390 |
-
implementation details and pose a security risk.
|
391 |
-
|
392 |
-
* "Protected" metadata is not available from the API. This includes any metadata
|
393 |
-
with a key prefixed with `_`, as well as any meta marked as protected by
|
394 |
-
plugins. Protected meta is used to store internal data by many plugins and
|
395 |
-
cannot be exposed to external clients.
|
396 |
-
|
397 |
-
### Response
|
398 |
-
The response is a Meta entity containing all the post_meta for the specified
|
399 |
-
Post if available.
|
400 |
-
|
401 |
-
Returns a 403 Forbidden status code if the client is not authenticated.
|
402 |
-
|
403 |
-
Retrieve a Meta for a Post
|
404 |
-
------------------------
|
405 |
-
|
406 |
-
GET /posts/<id>/meta/<mid>
|
407 |
-
|
408 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
409 |
-
|
410 |
-
Note that the access rules for metadata apply here (see [Retrieve Meta for
|
411 |
-
a Post](http://wp-api.org/#posts_retrieve-meta-for-a-post) ).
|
412 |
-
|
413 |
-
### Response
|
414 |
-
The response is a Meta entity containing the post_meta for the specified Meta and
|
415 |
-
Post if available.
|
416 |
-
|
417 |
-
Returns a 403 Forbidden status code if the client is not authenticated.
|
418 |
-
|
419 |
-
Edit a Meta for a Post
|
420 |
-
------------------------
|
421 |
-
|
422 |
-
PUT /posts/<id>/meta/<mid>
|
423 |
-
|
424 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
425 |
-
|
426 |
-
Note that the access rules for metadata apply here (see [Retrieve Meta for
|
427 |
-
a Post](http://wp-api.org/#posts_retrieve-meta-for-a-post) ). Any submitted data that violates an access rule (e.g. sending
|
428 |
-
serialized data) will result in a 403 error.
|
429 |
-
|
430 |
-
### Input
|
431 |
-
The supplied data should be a Meta object. This data can be submitted via a
|
432 |
-
regular HTTP multipart body, with the Meta key and value set with the `data`
|
433 |
-
parameter, or through a direct JSON body.
|
434 |
-
|
435 |
-
The `data` parameter should be an array containing the following key value pairs:
|
436 |
-
|
437 |
-
* `key` - The post meta key to be updated. (string) *required*
|
438 |
-
* `value` - The post meta value for the key provided. (string) *required*
|
439 |
-
|
440 |
-
### Response
|
441 |
-
On a successful update, a 200 OK status is given, indicating the post_meta has
|
442 |
-
been updated. The updated Meta entity is returned in the body.
|
443 |
-
|
444 |
-
If the client is not authenticated, a 403 Forbidden status code is returned.
|
445 |
-
|
446 |
-
Delete a Meta for a Post
|
447 |
-
-------------
|
448 |
-
|
449 |
-
DELETE /posts/<id>/meta/<mid>
|
450 |
-
|
451 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
452 |
-
|
453 |
-
Note that the access rules for metadata apply here (see Retrieve Meta for
|
454 |
-
a Post). Attempting to delete data that violates an access rule (e.g. sending
|
455 |
-
serialized data) will result in a 403 error.
|
456 |
-
|
457 |
-
### Response
|
458 |
-
On successful deletion, a 200 OK status code will be returned, indicating
|
459 |
-
that the post_meta has been permanently deleted.
|
460 |
-
|
461 |
-
If the client is not authenticated, a 403 Forbidden status code is returned.
|
462 |
-
|
463 |
-
Media
|
464 |
-
=====
|
465 |
-
|
466 |
-
|
467 |
-
Create an Attachment
|
468 |
-
--------------------
|
469 |
-
The Create Attachment endpoint is used to create the raw data for an attachment.
|
470 |
-
This is a binary object (blob), such as image data or a video.
|
471 |
-
|
472 |
-
POST /media
|
473 |
-
|
474 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
475 |
-
|
476 |
-
### Input
|
477 |
-
The attachment creation endpoint can accept data in two forms.
|
478 |
-
|
479 |
-
The primary input method accepts raw data POSTed with the corresponding content
|
480 |
-
type set via the `Content-Type` HTTP header. This is the preferred submission
|
481 |
-
method.
|
482 |
-
|
483 |
-
The secondary input method accepts data POSTed via `multipart/form-data`, as per
|
484 |
-
[RFC 2388][]. The uploaded file should be submitted with the name field set to
|
485 |
-
"file", and the filename field set to the relevant filename for the file.
|
486 |
-
|
487 |
-
In addition, a `Content-MD5` header can be set with the MD5 hash of the file, to
|
488 |
-
enable the server to check for consistency errors. If the supplied hash does not
|
489 |
-
match the hash calculated on the server, a 412 Precondition Failed header will
|
490 |
-
be issued.
|
491 |
-
|
492 |
-
[RFC 2388]: http://tools.ietf.org/html/rfc2388
|
493 |
-
|
494 |
-
### Response
|
495 |
-
On a successful creation, a 201 Created status is given, indicating that the
|
496 |
-
attachment has been created. The attachment is available canonically from the
|
497 |
-
URL specified in the Location header.
|
498 |
-
|
499 |
-
The new Attachment entity is also returned in the body for convienience.
|
500 |
-
|
501 |
-
Returns a 403 Forbidden status code if the client is not authenticated.
|
502 |
-
|
503 |
-
Get Attachments
|
504 |
-
---------------
|
505 |
-
The Attachments endpoint returns an Attachment collection containing a subset of
|
506 |
-
the site's attachments.
|
507 |
-
|
508 |
-
This endpoint is an extended version of the Post retrieval endpoint.
|
509 |
-
|
510 |
-
GET /media
|
511 |
-
|
512 |
-
### Input
|
513 |
-
#### `fields`
|
514 |
-
...
|
515 |
-
|
516 |
-
### Response
|
517 |
-
The response is an Attachment entity containing the requested Attachment if
|
518 |
-
available.
|
519 |
-
|
520 |
-
|
521 |
-
Users
|
522 |
-
=====
|
523 |
-
|
524 |
-
|
525 |
-
Create a User
|
526 |
-
-------------
|
527 |
-
|
528 |
-
POST /users
|
529 |
-
|
530 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
531 |
-
|
532 |
-
### Input
|
533 |
-
The supplied data should be a User object. This data can be submitted via a
|
534 |
-
regular HTTP multipart body, with User values set as values to the `data`
|
535 |
-
parameter, or through a direct JSON body.
|
536 |
-
|
537 |
-
That is, the following are equivalent:
|
538 |
-
|
539 |
-
Content-Type: application/x-www-form-urlencoded
|
540 |
-
|
541 |
-
data[username]=newuser&data[name]=New%20User&data[password]=secret
|
542 |
-
|
543 |
-
|
544 |
-
Content-Type: application/json
|
545 |
-
|
546 |
-
{"username":"newuser","name":"New User","password":"secret"}
|
547 |
-
|
548 |
-
### Response
|
549 |
-
On a successful creation, a 201 Created status is given, indicating that the
|
550 |
-
user has been created. The user is available canonically from the URL specified
|
551 |
-
in the Location header.
|
552 |
-
|
553 |
-
The new User entity is also returned in the body for convenience.
|
554 |
-
|
555 |
-
A 403 Forbidden status is returned if the client is not authenticated.
|
556 |
-
|
557 |
-
Retrieve Users
|
558 |
-
--------------
|
559 |
-
The Users endpoint returns a User Collection containing a subset of the site's
|
560 |
-
users.
|
561 |
-
|
562 |
-
GET /users
|
563 |
-
|
564 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
565 |
-
|
566 |
-
|
567 |
-
### Input
|
568 |
-
#### `filter`
|
569 |
-
The `filter` parameter controls the query parameters. It is essentially a subset
|
570 |
-
of the parameters available to [`WP_User_Query`](http://codex.wordpress.org/Class_Reference/WP_User_Query).
|
571 |
-
|
572 |
-
The parameter should be an array of the following key/value pairs:
|
573 |
-
|
574 |
-
* `number` - Number of users to retrieve, use `-1` for all users. Default
|
575 |
-
is set by the site. (integer)
|
576 |
-
* `offset` - Number of users to skip. Default is 0. (integer)
|
577 |
-
* `orderby` - Parameter to search by, as per [`WP_User_Query`](https://codex.wordpress.org/Class_Reference/WP_User_Query#Order_.26_Orderby_Parameters).
|
578 |
-
Default is "user_login". (string)
|
579 |
-
* `order` - Order to sort by. Default is "ASC". (string, "ASC" or "DESC")
|
580 |
-
* `s` - Keyword to search for. (string)
|
581 |
-
|
582 |
-
### Response
|
583 |
-
The response is a User Collection document containing the requested Users if
|
584 |
-
available.
|
585 |
-
|
586 |
-
A 403 Forbidden status is returned if the client is not authenticated.
|
587 |
-
|
588 |
-
|
589 |
-
Retrieve a User
|
590 |
-
---------------
|
591 |
-
|
592 |
-
GET /users/<id>
|
593 |
-
|
594 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
595 |
-
|
596 |
-
### Input
|
597 |
-
#### `context`
|
598 |
-
The `context` parameter controls the format of the data to return. The following
|
599 |
-
contexts are available:
|
600 |
-
|
601 |
-
* `view`: The default context. Gives the normal User entity.
|
602 |
-
* `edit`: Context used for extra fields relevant to updating a user. Includes
|
603 |
-
the `extra_capabilities` field; this field contains the capabilities assigned
|
604 |
-
to the user themselves, rather than those inherited from their roles. Requires [authentication](http://wp-api.org/guides/authentication.html).
|
605 |
-
* `embed`: Context used when embedding the response inside another (e.g. post
|
606 |
-
author). This is intended as a minimal subset of the user data to reduce
|
607 |
-
response size. Excludes `roles` and `capabilities`.
|
608 |
-
|
609 |
-
Default is "view". (string)
|
610 |
-
|
611 |
-
### Response
|
612 |
-
The response is a User entity containing the requested User if available. The
|
613 |
-
fields available on the User depend on the `context` parameter.
|
614 |
-
|
615 |
-
A 403 Forbidden status is returned if the client is not authenticated.
|
616 |
-
|
617 |
-
|
618 |
-
Retrieve Current User
|
619 |
-
-------------
|
620 |
-
|
621 |
-
GET /users/me
|
622 |
-
|
623 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
624 |
-
|
625 |
-
This endpoint offers a permalink to get the current user, without needing to
|
626 |
-
know the user's ID.
|
627 |
-
|
628 |
-
### Input
|
629 |
-
#### `context`
|
630 |
-
The `context` parameter controls the format of the data to return. See the
|
631 |
-
Retrieve a User endpoint for available contexts.
|
632 |
-
|
633 |
-
Default is "view". (string)
|
634 |
-
|
635 |
-
### Response
|
636 |
-
If the client is currently logged in, a 302 Found status is given. The User is
|
637 |
-
available canonically from the URL specified in the Location header.
|
638 |
-
|
639 |
-
The User entity containing the current User is also returned in the body for
|
640 |
-
convenience. The fields available on the User depend on the `context` parameter.
|
641 |
-
|
642 |
-
If the client is not logged in, a 403 Forbidden status is given.
|
643 |
-
|
644 |
-
|
645 |
-
Edit a User
|
646 |
-
-----------
|
647 |
-
|
648 |
-
PUT /users/<id>
|
649 |
-
|
650 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
651 |
-
|
652 |
-
For compatibility reasons, this endpoint also accepts the POST and PATCH
|
653 |
-
methods. Both of these methods have the same behaviour as using PUT. It is
|
654 |
-
recommended to use PUT if available to fit with REST convention.
|
655 |
-
|
656 |
-
### Input
|
657 |
-
The supplied data should be a User object. This data can be submitted via a
|
658 |
-
regular HTTP multipart body, with User values set as values to the `data`
|
659 |
-
parameter, or through a direct JSON body. See the Create User endpoint for an
|
660 |
-
example.
|
661 |
-
|
662 |
-
### Response
|
663 |
-
On a successful update, a 200 OK status is given, indicating the user has been
|
664 |
-
updated. The updated User entity is returned in the body.
|
665 |
-
|
666 |
-
If the client is not logged in, a 403 Forbidden status is given.
|
667 |
-
|
668 |
-
Delete a User
|
669 |
-
-------------
|
670 |
-
|
671 |
-
DELETE /users/<id>
|
672 |
-
|
673 |
-
Requires [authentication](http://wp-api.org/guides/authentication.html)
|
674 |
-
|
675 |
-
### Input
|
676 |
-
#### `force`
|
677 |
-
The `force` parameter controls whether the user is permanently deleted or not.
|
678 |
-
By default, this is set to false, indicating that the user will be sent to an
|
679 |
-
intermediate storage (such as the trash) allowing it to be restored later. If
|
680 |
-
set to true, the user will not be able to be restored.
|
681 |
-
|
682 |
-
Default is false. (boolean)
|
683 |
-
|
684 |
-
#### `reassign`
|
685 |
-
The `reassign` parameter controls whether the deleted user's content is
|
686 |
-
reassigned to a new User or not. If set to `null`, the deleted user's content
|
687 |
-
will not be reassigned.
|
688 |
-
|
689 |
-
Default is null. (integer)
|
690 |
-
|
691 |
-
|
692 |
-
### Response
|
693 |
-
On successful deletion, a 202 Accepted status code will be returned, indicating
|
694 |
-
that the user has been moved to the trash for permanent deletion at a
|
695 |
-
later date.
|
696 |
-
|
697 |
-
If force was set to true, a 200 OK status code will be returned instead,
|
698 |
-
indicating that the user has been permanently deleted.
|
699 |
-
|
700 |
-
If the client is not authenticated, a 403 Forbidden status is given.
|
701 |
-
|
702 |
-
Taxonomies
|
703 |
-
==========
|
704 |
-
|
705 |
-
|
706 |
-
Retrieve All Taxonomies
|
707 |
-
-----------------------
|
708 |
-
The Taxonomies endpoint returns a collection containing objects for each of the
|
709 |
-
site's registered taxonomies.
|
710 |
-
|
711 |
-
GET /taxonomies
|
712 |
-
|
713 |
-
|
714 |
-
### Response
|
715 |
-
The response is a collection document containing all registered taxonomies.
|
716 |
-
|
717 |
-
|
718 |
-
Retrieve a Taxonomy
|
719 |
-
-------------------
|
720 |
-
|
721 |
-
GET /taxonomies/<taxonomy>
|
722 |
-
|
723 |
-
### Response
|
724 |
-
The response is a Taxonomy entity containing the requested Taxonomy, if available.
|
725 |
-
|
726 |
-
|
727 |
-
Retrieve Terms for a Taxonomy
|
728 |
-
-----------------------------
|
729 |
-
|
730 |
-
GET /taxonomies/<taxonomy>/terms
|
731 |
-
|
732 |
-
### Response
|
733 |
-
The response is a collection of taxonomy terms for the specified Taxonomy, if
|
734 |
-
available.
|
735 |
-
|
736 |
-
Retrieve a Taxonomy Term
|
737 |
-
------------------------
|
738 |
-
|
739 |
-
GET /taxonomies/<taxonomy>/terms/<id>
|
740 |
-
|
741 |
-
### Response
|
742 |
-
The response is a Taxonomy entity object containing the Taxonomy with the
|
743 |
-
requested ID, if available.
|
744 |
-
|
745 |
-
SCHEMA
|
746 |
-
============
|
747 |
-
The API is designed around two types of responses: entities, and collections.
|
748 |
-
Entities are JSON objects representing internal objects, both abstract and
|
749 |
-
WordPress objects. Collections are JSON arrays of Entities.
|
750 |
-
|
751 |
-
This document is for clients and providers wanting to ensure full compliance
|
752 |
-
with the specification.
|
753 |
-
|
754 |
-
|
755 |
-
Definitions
|
756 |
-
==========
|
757 |
-
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
|
758 |
-
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
|
759 |
-
interpreted as described in [RFC2119][].
|
760 |
-
|
761 |
-
* Provider: A site making the API available for use
|
762 |
-
* Consumer: An application accessing and interacting with the API
|
763 |
-
* slug: A URL-friendly human-readable identifier, usually derived from the title
|
764 |
-
of the entity.
|
765 |
-
|
766 |
-
[RFC2119]: http://tools.ietf.org/html/rfc2119
|
767 |
-
|
768 |
-
|
769 |
-
### ABNF
|
770 |
-
Augmented Backus-Naur Form (ABNF) is to be interpreted as described in
|
771 |
-
[RFC5234][]. In addition, the following basic rules are used to describe basic
|
772 |
-
parsing constructs above the standard JSON parsing rules.
|
773 |
-
|
774 |
-
token = 1*<any OCTET except CTLs> ; DQUOTE must be escaped with "\"
|
775 |
-
|
776 |
-
Note that as per ABNF, literal strings are case insensitive. That is:
|
777 |
-
|
778 |
-
example-field = "id"
|
779 |
-
example-field = "ID"
|
780 |
-
|
781 |
-
Providers SHOULD use the capitalisation as per this specification to ensure
|
782 |
-
maximum compatibility with consumers. Consumers SHOULD ignore the case of
|
783 |
-
literal strings when parsing data.
|
784 |
-
|
785 |
-
[RFC5234]: http://tools.ietf.org/html/rfc5234
|
786 |
-
|
787 |
-
|
788 |
-
Entities
|
789 |
-
========
|
790 |
-
|
791 |
-
Index
|
792 |
-
-----
|
793 |
-
The Index entity is a JSON object with site properties. The following properties
|
794 |
-
are defined for the Index entity object.
|
795 |
-
|
796 |
-
### `name`
|
797 |
-
The `name` field is a string with the site's name.
|
798 |
-
|
799 |
-
### `description`
|
800 |
-
The `description` field is a string with the site's description.
|
801 |
-
|
802 |
-
### `URL`
|
803 |
-
The `URL` field is a string with the URL to the site itself.
|
804 |
-
|
805 |
-
### `routes`
|
806 |
-
The `routes` field is an object with keys as a route and the values as a route
|
807 |
-
descriptor.
|
808 |
-
|
809 |
-
The route is a string giving the URL template for the route, relative to the API
|
810 |
-
root. The template contains URL parts separated by forward slashes, with each
|
811 |
-
URL part either a static string, or a route variable encased in angle brackets.
|
812 |
-
|
813 |
-
route = ( "/"
|
814 |
-
/ *( "/" ( token / route-variable ) ) )
|
815 |
-
route-variable = "<" token ">"
|
816 |
-
|
817 |
-
These routes can be converted into URLs by replacing all route variables with
|
818 |
-
their relevant values, then concatenating the relative URL to the API base.
|
819 |
-
|
820 |
-
The route descriptor is an object with the following defined properties.
|
821 |
-
|
822 |
-
* `supports`: A JSON array of supported HTTP methods (verbs). Possible values
|
823 |
-
are "HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"
|
824 |
-
* `accepts_json`: A boolean indicating whether data can be passed directly via a
|
825 |
-
POST request body. Default for missing properties is false.
|
826 |
-
* `meta`: An Entity Meta entity. Typical `links` values consist of a `self` link
|
827 |
-
pointing to the route's full URL.
|
828 |
-
|
829 |
-
### `meta`
|
830 |
-
The `meta` field is a Entity Meta entity with metadata relating to the entity
|
831 |
-
representation.
|
832 |
-
|
833 |
-
Typical `links` values for the meta object consist of a `help` key with the
|
834 |
-
value indicating a human-readable documentation page about the API.
|
835 |
-
|
836 |
-
### Example
|
837 |
-
|
838 |
-
{
|
839 |
-
"name": "My WordPress Site",
|
840 |
-
"description": "Just another WordPress site",
|
841 |
-
"URL": "http:\/\/example.com",
|
842 |
-
"routes": {
|
843 |
-
"\/": {
|
844 |
-
"supports": [
|
845 |
-
"HEAD",
|
846 |
-
"GET"
|
847 |
-
],
|
848 |
-
"meta": {
|
849 |
-
"self": "http:\/\/example.com\/wp-json\/"
|
850 |
-
}
|
851 |
-
},
|
852 |
-
"\/posts": {
|
853 |
-
"supports": [
|
854 |
-
"HEAD",
|
855 |
-
"GET",
|
856 |
-
"POST"
|
857 |
-
],
|
858 |
-
"meta": {
|
859 |
-
"self": "http:\/\/example.com\/wp-json\/posts"
|
860 |
-
},
|
861 |
-
"accepts_json": true
|
862 |
-
},
|
863 |
-
"\/posts\/<id>": {
|
864 |
-
"supports": [
|
865 |
-
"HEAD",
|
866 |
-
"GET",
|
867 |
-
"POST",
|
868 |
-
"PUT",
|
869 |
-
"PATCH",
|
870 |
-
"DELETE"
|
871 |
-
],
|
872 |
-
"accepts_json": true
|
873 |
-
},
|
874 |
-
"\/posts\/<id>\/revisions": {
|
875 |
-
"supports": [
|
876 |
-
"HEAD",
|
877 |
-
"GET"
|
878 |
-
]
|
879 |
-
},
|
880 |
-
"\/posts\/<id>\/comments": {
|
881 |
-
"supports": [
|
882 |
-
"HEAD",
|
883 |
-
"GET",
|
884 |
-
"POST"
|
885 |
-
],
|
886 |
-
"accepts_json": true
|
887 |
-
},
|
888 |
-
"\/posts\/<id>\/comments\/<comment>": {
|
889 |
-
"supports": [
|
890 |
-
"HEAD",
|
891 |
-
"GET",
|
892 |
-
"POST",
|
893 |
-
"PUT",
|
894 |
-
"PATCH",
|
895 |
-
"DELETE"
|
896 |
-
],
|
897 |
-
"accepts_json": true
|
898 |
-
},
|
899 |
-
},
|
900 |
-
"meta": {
|
901 |
-
"links": {
|
902 |
-
"help": "https:\/\/github.com\/WP-API\/WP-API",
|
903 |
-
"profile": "https:\/\/raw.github.com\/WP-API\/WP-API\/master\/docs\/schema.json"
|
904 |
-
}
|
905 |
-
}
|
906 |
-
}
|
907 |
-
|
908 |
-
Post
|
909 |
-
----
|
910 |
-
The Post entity is a JSON object of post properties. Unless otherwise defined,
|
911 |
-
properties are available in all contexts. The following properties are defined
|
912 |
-
for the Post entity object:
|
913 |
-
|
914 |
-
### `title`
|
915 |
-
The `title` field is a string with the post's title.
|
916 |
-
|
917 |
-
### `date`, `date_gmt`
|
918 |
-
The `date` and `date_gmt` fields are strings with the post's creation date and
|
919 |
-
time in the local time and UTC respectively. These fields follow the [RFC3339][]
|
920 |
-
Section 5.6 datetime representation.
|
921 |
-
|
922 |
-
date = date-time
|
923 |
-
date_gmt = date-time
|
924 |
-
|
925 |
-
[RFC3339]: http://tools.ietf.org/html/rfc3339
|
926 |
-
|
927 |
-
### `modified`, `modified_gmt`
|
928 |
-
The `modified` and `modified_gmt` fields are strings with the post's last
|
929 |
-
modification date and time in the local time and UTC respectively. These fields
|
930 |
-
follow the [RFC3339][] Section 5.6 datetime representation.
|
931 |
-
|
932 |
-
modified = date-time
|
933 |
-
modified_gmt = date-time
|
934 |
-
|
935 |
-
### `date_tz`, `modified_tz`
|
936 |
-
The `date_tz` and `modified_tz` fields are strings with the timezone applying to
|
937 |
-
the `date` and `modified` fields respectively. The timezone is a [Olsen zoneinfo
|
938 |
-
database][] identifier. While the `date` and `modified` fields include timezone
|
939 |
-
offset information, the `date_tz` and `modified_tz` fields allow proper data
|
940 |
-
operations across Daylight Savings Time boundaries.
|
941 |
-
|
942 |
-
Note that in addition to the normal Olsen timezones, manual offsets may be
|
943 |
-
given. These manual offsets use the deprecated `Etc/GMT+...` zones and specify
|
944 |
-
an integer offset in hours from UTC.
|
945 |
-
|
946 |
-
timezone = Olsen-timezone / manual-offset
|
947 |
-
manual-offset = "Etc/GMT" ("-" / "+") 1*2( DIGIT )
|
948 |
-
|
949 |
-
Consumers SHOULD use the fields if they perform mathematical operations on the
|
950 |
-
`date` and `modified` fields (such as adding an hour to the last modification
|
951 |
-
date) rather than relying on the `time-offset` in the `date` or
|
952 |
-
`modified` fields.
|
953 |
-
|
954 |
-
[Olsen zoneinfo database]: https://en.wikipedia.org/wiki/Tz_database
|
955 |
-
|
956 |
-
### `status`
|
957 |
-
The `status` field is a string with the post's status. This status relates to
|
958 |
-
where the post is in the editorial process. These are usually set values, but
|
959 |
-
some providers may have extra post statuses.
|
960 |
-
|
961 |
-
post-status = "draft" / "pending" / "private" / "publish" / "trash" / token
|
962 |
-
|
963 |
-
Consumers who encounter an unknown or missing post status SHOULD treat it the
|
964 |
-
same as a "draft" status.
|
965 |
-
|
966 |
-
### `type`
|
967 |
-
The `type` field is a string with the post's type. This field is specific to
|
968 |
-
providers, with the most basic representation being "post". The type of the
|
969 |
-
post usually relates to the fields in the Post entity, with other types having
|
970 |
-
additional fields specific to the type.
|
971 |
-
|
972 |
-
post-type = "post" / token
|
973 |
-
|
974 |
-
Consumers who encounter an unknown or missing post type SHOULD treat it the same
|
975 |
-
as a "post" type.
|
976 |
-
|
977 |
-
### `name`
|
978 |
-
The `name` field is a string with the post's slug.
|
979 |
-
|
980 |
-
### `author`
|
981 |
-
The `author` field is a User entity with the user who created the post.
|
982 |
-
|
983 |
-
### `password`
|
984 |
-
The `password` field is a string with the post's password. A zero-length
|
985 |
-
password indicates that the post does not have a password.
|
986 |
-
|
987 |
-
Consumers who encounter a missing password MUST treat it the same as a
|
988 |
-
zero-length password.
|
989 |
-
|
990 |
-
### `content`
|
991 |
-
The `content` field is a string with the post's content.
|
992 |
-
|
993 |
-
### `excerpt`
|
994 |
-
The `excerpt` field is a string with the post's excerpt. This is usually a
|
995 |
-
shortened version of the post content, suitable for displaying in
|
996 |
-
collection views.
|
997 |
-
|
998 |
-
Consumers who encounter a missing excerpt MAY present a shortened version of the
|
999 |
-
`content` field instead.
|
1000 |
-
|
1001 |
-
### `content_raw`, `excerpt_raw`
|
1002 |
-
The `content_raw` and `excerpt_raw` fields are strings with the post's content
|
1003 |
-
and excerpt respectively. Unlike the `content` and `excerpt` fields, the value
|
1004 |
-
has not been passed through internal filtering, and is suitable for editing.
|
1005 |
-
|
1006 |
-
(Context Availability: `edit`)
|
1007 |
-
|
1008 |
-
### `parent`
|
1009 |
-
The `parent` field is an integer or JSON object with the post's parent
|
1010 |
-
post ID. A literal zero indicates that the post does not have a parent
|
1011 |
-
post.
|
1012 |
-
|
1013 |
-
post-parent = "0" / 1*DIGIT
|
1014 |
-
|
1015 |
-
Consumers who encounter a missing parent ID MUST treat it the same as a parent
|
1016 |
-
post ID of 0.
|
1017 |
-
|
1018 |
-
Parent fields will be expanded into a full Post entity in the `view` or `edit`
|
1019 |
-
contexts, but only one level deep. The embedded Post entity will be rendered
|
1020 |
-
using the `parent` context.
|
1021 |
-
|
1022 |
-
In the `parent` context, the field will contain an integer with the post's
|
1023 |
-
parent post ID as above.
|
1024 |
-
|
1025 |
-
### `link`
|
1026 |
-
The `link` field is a string with the full URL to the post's canonical view.
|
1027 |
-
This is typically the human-readable location of the entity.
|
1028 |
-
|
1029 |
-
### `guid`
|
1030 |
-
The `guid` field is a string with the post's globally unique identifier (GUID).
|
1031 |
-
|
1032 |
-
The GUID is typically in URL form, as this is a relatively easy way of ensuring
|
1033 |
-
that the GUID is globally unique. However, consumers MUST NOT treat the GUID as
|
1034 |
-
a URL, and MUST treat the GUID as a string of arbitrary characters.
|
1035 |
-
|
1036 |
-
### `menu_order`
|
1037 |
-
The `menu_order` field is an integer with the post's sorting position. This is
|
1038 |
-
typically used to affect sorting when displaying the post in menus or lists.
|
1039 |
-
Larger integers should be treated as sorting before smaller integers.
|
1040 |
-
|
1041 |
-
menu-order = 1*DIGIT / "-" 1*DIGIT
|
1042 |
-
|
1043 |
-
Consumers who encounter a missing sorting position MUST treat it the same as a
|
1044 |
-
sorting position of 0.
|
1045 |
-
|
1046 |
-
### `comment_status`
|
1047 |
-
The `comment_status` field is a string with the post's current commenting
|
1048 |
-
status. This field indicates whether users can submit comments to the post.
|
1049 |
-
|
1050 |
-
post-comment-status = "open" / "closed" / token
|
1051 |
-
|
1052 |
-
Providers MAY use statuses other than "open" or "closed" to indicate other
|
1053 |
-
statuses. Consumers who encounter an unknown or missing comment status SHOULD
|
1054 |
-
treat it as "closed".
|
1055 |
-
|
1056 |
-
### `ping_status`
|
1057 |
-
The `ping_status` field is a string with the post's current pingback/trackback
|
1058 |
-
status. This field indicates whether users can submit pingbacks or trackbacks
|
1059 |
-
to the post.
|
1060 |
-
|
1061 |
-
ping-status = "open" / "closed" / token
|
1062 |
-
|
1063 |
-
Providers MAY use statuses other than "open" or "closed" to indicate other
|
1064 |
-
statuses. Consumers who encounter an unknown or missing ping status SHOULD treat
|
1065 |
-
it as "closed".
|
1066 |
-
|
1067 |
-
### `sticky`
|
1068 |
-
The `sticky` field is a boolean indicating whether the post is marked as a
|
1069 |
-
sticky post. Consumers typically display sticky posts before other posts in
|
1070 |
-
collection views.
|
1071 |
-
|
1072 |
-
### `post_thumbnail`
|
1073 |
-
The `post_thumbnail` field is a Media entity.
|
1074 |
-
|
1075 |
-
### `post_format`
|
1076 |
-
The `post_format` field is a string with the post format. The post format
|
1077 |
-
indicates how some meta fields should be displayed. For example, posts with the
|
1078 |
-
"link" format may wish to display an extra link to a URL specified in a meta
|
1079 |
-
field or emphasise a link in the post content.
|
1080 |
-
|
1081 |
-
post-format = "standard" / "aside" / "gallery" / "image" / "link" / "status" / "quote" / "video" / "audio" / "chat"
|
1082 |
-
|
1083 |
-
Providers MUST NOT use post formats not specified by this specification, unless
|
1084 |
-
specified in a subsequent version of the specification. Consumers MUST treat
|
1085 |
-
unknown post formats as "standard".
|
1086 |
-
|
1087 |
-
### `terms`
|
1088 |
-
The `terms` field is a Term collection.
|
1089 |
-
|
1090 |
-
### `post_meta`
|
1091 |
-
The `meta` field is a Metadata entity with metadata relating to the post.
|
1092 |
-
|
1093 |
-
### `meta`
|
1094 |
-
The `meta` field is a Entity Meta entity with metadata relating to the entity
|
1095 |
-
representation.
|
1096 |
-
|
1097 |
-
### Example
|
1098 |
-
|
1099 |
-
{
|
1100 |
-
"ID": 1,
|
1101 |
-
"title": "Hello world!q",
|
1102 |
-
"status": "publish",
|
1103 |
-
"type": "post",
|
1104 |
-
"author": {
|
1105 |
-
"ID": 1,
|
1106 |
-
"name": "admin",
|
1107 |
-
"slug": "admin",
|
1108 |
-
"URL": "",
|
1109 |
-
"avatar": "http:\/\/0.gravatar.com\/avatar\/c57c8945079831fa3c19caef02e44614&d=404&r=G",
|
1110 |
-
"meta": {
|
1111 |
-
"links": {
|
1112 |
-
"self": "http:\/\/example.com\/wp-json\/users\/1",
|
1113 |
-
"archives": "http:\/\/example.com\/wp-json\/users\/1\/posts"
|
1114 |
-
}
|
1115 |
-
},
|
1116 |
-
"first_name": "",
|
1117 |
-
"last_name": ""
|
1118 |
-
},
|
1119 |
-
"content": "<p>Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!<\/p>\n",
|
1120 |
-
"parent": 0,
|
1121 |
-
"link": "http:\/\/example.com\/2013\/06\/02\/hello-world\/",
|
1122 |
-
"date": "2013-06-02T05:28:00+10:00",
|
1123 |
-
"modified": "2013-06-30T13:56:57+10:00",
|
1124 |
-
"format": "standard",
|
1125 |
-
"slug": "hello-world",
|
1126 |
-
"guid": "http:\/\/example.com\/?p=1",
|
1127 |
-
"excerpt": "",
|
1128 |
-
"menu_order": 0,
|
1129 |
-
"comment_status": "open",
|
1130 |
-
"ping_status": "open",
|
1131 |
-
"sticky": false,
|
1132 |
-
"date_tz": "Australia\/Brisbane",
|
1133 |
-
"date_gmt": "2013-06-02T05:28:00+00:00",
|
1134 |
-
"modified_tz": "Australia\/Brisbane",
|
1135 |
-
"modified_gmt": "2013-06-30T03:56:57+00:00",
|
1136 |
-
"password": "",
|
1137 |
-
"post_meta": [
|
1138 |
-
],
|
1139 |
-
"meta": {
|
1140 |
-
"links": {
|
1141 |
-
"self": "http:\/\/example.com\/wp-json\/posts\/1",
|
1142 |
-
"author": "http:\/\/example.com\/wp-json\/users\/1",
|
1143 |
-
"collection": "http:\/\/example.com\/wp-json\/posts",
|
1144 |
-
"replies": "http:\/\/example.com\/wp-json\/posts\/1\/comments",
|
1145 |
-
"version-history": "http:\/\/example.com\/wp-json\/posts\/1\/revisions"
|
1146 |
-
}
|
1147 |
-
},
|
1148 |
-
"featured_image": null,
|
1149 |
-
"terms": {
|
1150 |
-
"category": {
|
1151 |
-
"ID": 1,
|
1152 |
-
"name": "Uncategorized",
|
1153 |
-
"slug": "uncategorized",
|
1154 |
-
"parent": null,
|
1155 |
-
"count": 7,
|
1156 |
-
"meta": {
|
1157 |
-
"links": {
|
1158 |
-
"collection": "http:\/\/example.com\/wp-json\/taxonomies\/category\/terms",
|
1159 |
-
"self": "http:\/\/example.com\/wp-json\/taxonomies\/category\/terms\/1"
|
1160 |
-
}
|
1161 |
-
}
|
1162 |
-
}
|
1163 |
-
}
|
1164 |
-
}
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
Entity Meta
|
1169 |
-
-----------
|
1170 |
-
The Entity Meta entity is a JSON object with custom metadata relating to the
|
1171 |
-
representation of the parent entity.
|
1172 |
-
|
1173 |
-
The following properties are defined for the Entity Meta entity object:
|
1174 |
-
|
1175 |
-
### `links`
|
1176 |
-
The `links` field is a JSON object with hyperlinks to related entities. Each
|
1177 |
-
item's key is a link relation as per the [IANA Link Relations registry][] with
|
1178 |
-
the value of the item being the corresponding link URL.
|
1179 |
-
|
1180 |
-
Typical link relations are:
|
1181 |
-
|
1182 |
-
* `self`: A URL pointing to the current entity's location.
|
1183 |
-
* `up`: A URL pointing to the parent entity's location.
|
1184 |
-
* `collection`: A URL pointing to a collection that the entity is a member of.
|
1185 |
-
|
1186 |
-
[IANA Link Relations registry]: http://www.iana.org/assignments/link-relations/link-relations.xml
|
1187 |
-
|
1188 |
-
|
1189 |
-
User
|
1190 |
-
----
|
1191 |
-
The User entity is a JSON object with user properties. The following properties
|
1192 |
-
are defined for the User entity object:
|
1193 |
-
|
1194 |
-
### `ID`
|
1195 |
-
The `ID` field is an integer with the user's ID.
|
1196 |
-
|
1197 |
-
### `name`
|
1198 |
-
The `name` field is a string with the user's display name.
|
1199 |
-
|
1200 |
-
### `slug`
|
1201 |
-
The `slug` field is a string with the user's slug.
|
1202 |
-
|
1203 |
-
### `URL`
|
1204 |
-
The `URL` field is a string with the URL to the author's site. This is typically
|
1205 |
-
an external link of the author's choice.
|
1206 |
-
|
1207 |
-
### `avatar`
|
1208 |
-
The `avatar` field is a string with the URL to the author's avatar image.
|
1209 |
-
|
1210 |
-
Providers SHOULD ensure that for users without an avatar image, this field is
|
1211 |
-
either zero-length or the URL returns a HTTP 404 error code on access. Consumers
|
1212 |
-
MAY display a default avatar instead of a zero-length or URL which returns
|
1213 |
-
a HTTP 404 error code.
|
1214 |
-
|
1215 |
-
### `meta`
|
1216 |
-
The `meta` field is a Entity Meta entity with metadata relating to the entity
|
1217 |
-
representation.
|
1218 |
-
|
1219 |
-
|
1220 |
-
Metadata
|
1221 |
-
--------
|
1222 |
-
The Metadata entity is a JSON array with metadata fields. Each metadata field is
|
1223 |
-
a JSON object with `id`, `key` and `value` fields.
|
1224 |
-
|
1225 |
-
### `id`
|
1226 |
-
The `id` field of the metadata field is a positive integer with the internal
|
1227 |
-
metadata ID.
|
1228 |
-
|
1229 |
-
### `key`
|
1230 |
-
The `key` field of the metadata field is a string with the metadata field name.
|
1231 |
-
|
1232 |
-
### `value`
|
1233 |
-
The `value` field of the metadata field is a string with the metadata
|
1234 |
-
field value.
|
1235 |
-
|
1236 |
-
|
1237 |
-
Comment
|
1238 |
-
-------
|
1239 |
-
The Comment entity is a JSON object with comment properties. The following
|
1240 |
-
properties are defined for the Comment entity object:
|
1241 |
-
|
1242 |
-
### `ID`
|
1243 |
-
The `ID` field is an integer with the comment's ID.
|
1244 |
-
|
1245 |
-
### `content`
|
1246 |
-
The `content` field is a string with the comment's content.
|
1247 |
-
|
1248 |
-
### `status`
|
1249 |
-
The `status` field is a string with the comment's status. This field indicates
|
1250 |
-
whether the comment is in the publishing process, or if it has been deleted or
|
1251 |
-
marked as spam.
|
1252 |
-
|
1253 |
-
comment-status = "hold" / "approved" / "spam" / "trash" / token
|
1254 |
-
|
1255 |
-
Providers MAY use other values to indicate other statuses. Consumers who
|
1256 |
-
encounter an unknown or missing status SHOULD treat it as "hold".
|
1257 |
-
|
1258 |
-
### `type`
|
1259 |
-
The `type` field is a string with the comment's type. This is usually one of the
|
1260 |
-
following, but providers may provide additional values.
|
1261 |
-
|
1262 |
-
comment-type = "comment" / "trackback" / "pingback" / token
|
1263 |
-
|
1264 |
-
Providers MAY use other values to indicate other types. Consumers who encounter
|
1265 |
-
an unknown or missing status SHOULD treat it as "comment".
|
1266 |
-
|
1267 |
-
### `post`
|
1268 |
-
The `post` field is an integer with the parent post for the comment, or a Post
|
1269 |
-
entity describing the parent post. A literal zero indicates that the comment
|
1270 |
-
does not have a parent post.
|
1271 |
-
|
1272 |
-
comment-post-parent = "0" / 1*DIGIT
|
1273 |
-
|
1274 |
-
Consumers who encounter a missing post ID MUST treat it the same as a parent
|
1275 |
-
post ID of 0.
|
1276 |
-
|
1277 |
-
### `parent`
|
1278 |
-
The `post` field is an integer with the parent comment, or a Comment entity
|
1279 |
-
describing the parent comment. A literal zero indicates that the comment does
|
1280 |
-
not have a parent comment.
|
1281 |
-
|
1282 |
-
comment-parent = "0" / 1*DIGIT
|
1283 |
-
|
1284 |
-
Consumers who encounter a missing parent ID MUST treat it the same as a parent
|
1285 |
-
comment ID of 0.
|
1286 |
-
|
1287 |
-
### `author`
|
1288 |
-
The `author` field is a User entity with the comment author's data, or a
|
1289 |
-
User-like object for anonymous authors. The User-like object contains the
|
1290 |
-
following properties:
|
1291 |
-
|
1292 |
-
#### `ID`
|
1293 |
-
The `ID` property on the User-like object is always set to `0` for anonymous
|
1294 |
-
authors.
|
1295 |
-
|
1296 |
-
#### `name`
|
1297 |
-
The `name` property on the User-like object is a string with the author's name.
|
1298 |
-
|
1299 |
-
#### `URL`
|
1300 |
-
The `URL` property on the User-like object is a string with the author's URL.
|
1301 |
-
|
1302 |
-
#### `avatar`
|
1303 |
-
The `avatar` property on the User-like object is a string with the URL to the
|
1304 |
-
author's avatar image.
|
1305 |
-
|
1306 |
-
This property should be treated the same as the avatar property on the
|
1307 |
-
User entity.
|
1308 |
-
|
1309 |
-
|
1310 |
-
### `date`, `date_gmt`
|
1311 |
-
The `date` and `date_gmt` fields are strings with the post's creation date and
|
1312 |
-
time in the local time and UTC respectively. These fields follow the [RFC3339][]
|
1313 |
-
Section 5.6 datetime representation.
|
1314 |
-
|
1315 |
-
date = date-time
|
1316 |
-
date_gmt = date-time
|
1317 |
-
|
1318 |
-
This field should be treated the same as the `date` and `date_gmt` properties on
|
1319 |
-
a Post entity.
|
1320 |
-
|
1321 |
-
[RFC3339]: http://tools.ietf.org/html/rfc3339
|
1322 |
-
|
1323 |
-
### `date_tz`, `modified_tz`
|
1324 |
-
The `date_tz` and `modified_tz` fields are strings with the timezone applying to
|
1325 |
-
the `date` and `modified` fields respectively. The timezone is a [Olsen zoneinfo
|
1326 |
-
database][] identifier. While the `date` field includes timezone offset
|
1327 |
-
information, the `date_tz` field allows proper data operations across Daylight
|
1328 |
-
Savings Time boundaries.
|
1329 |
-
|
1330 |
-
This field should be treated the same as the `date_tz` property on a
|
1331 |
-
Post entity.
|
1332 |
-
|
1333 |
-
|
1334 |
-
Media
|
1335 |
-
-----
|
1336 |
-
The Media entity is a JSON object based on the Post entity. It contains all
|
1337 |
-
properties of the Post entity, with the following additional properties defined:
|
1338 |
-
|
1339 |
-
### `source`
|
1340 |
-
The `source` field is a string with the URL of the entity's original file. For
|
1341 |
-
image media, this is the source file that intermediate representations are
|
1342 |
-
generated from. For non-image media, this is the attached media file itself.
|
1343 |
-
|
1344 |
-
### `is_image`
|
1345 |
-
The `is_image` field is a boolean which indicates whether the entity's
|
1346 |
-
associated file should be handled as an image.
|
1347 |
-
|
1348 |
-
### `attachment_meta`
|
1349 |
-
The `attachment_meta` field is a Media Meta entity. If the file is not an image
|
1350 |
-
(as indicated by the `is_image` field), this is an empty JSON object.
|
1351 |
-
|
1352 |
-
|
1353 |
-
Media Meta
|
1354 |
-
----------
|
1355 |
-
The Media Meta entity is a JSON object with properties relating to the
|
1356 |
-
associated Media entity. The following properties are defined for the entity:
|
1357 |
-
|
1358 |
-
### `width`
|
1359 |
-
The `width` field is an integer with the original file's width in pixels.
|
1360 |
-
|
1361 |
-
### `height`
|
1362 |
-
The `height` field is an integer with the original file's height in pixels.
|
1363 |
-
|
1364 |
-
### `file`
|
1365 |
-
The `file` field is a string with the path to the original file, relative to the
|
1366 |
-
site's upload directory.
|
1367 |
-
|
1368 |
-
### `sizes`
|
1369 |
-
The `sizes` field is a JSON object mapping intermediate image sizes to image
|
1370 |
-
data objects. The key of each item is the size of the intermediate image as an
|
1371 |
-
internal string representation. The value of each item has the following
|
1372 |
-
properties defined.
|
1373 |
-
|
1374 |
-
* `file`: The filename of the intermediate file, relative to the directory of
|
1375 |
-
the original file.
|
1376 |
-
* `width`: The width of the intermediate file in pixels.
|
1377 |
-
* `height`: The height of the intermediate file in pixels.
|
1378 |
-
* `mime-type`: The MIME type of the intermediate file.
|
1379 |
-
* `url`: The full URL to the intermediate file.
|
1380 |
-
|
1381 |
-
### `image_meta`
|
1382 |
-
The `image_meta` field is a JSON object mapping image meta properties to their
|
1383 |
-
values. This data is taken from the EXIF data on the original image. The
|
1384 |
-
following properties are defined.
|
1385 |
-
|
1386 |
-
* `aperture`: The aperture used to create the original image as a decimal number
|
1387 |
-
(with two decimal places).
|
1388 |
-
* `credit`: Credit for the original image.
|
1389 |
-
* `camera`: The camera used to create the original image.
|
1390 |
-
* `created_timestamp`: When the file was created, as a Unix timestamp.
|
1391 |
-
* `copyright`: Copyright for the original image.
|
1392 |
-
* `focal_length`: The focal length used to create the original image as a
|
1393 |
-
decimal string.
|
1394 |
-
* `iso`: The ISO used to create the original image.
|
1395 |
-
* `shutter_speed`: The shutter speed used to create the original image, as a
|
1396 |
-
decimal string.
|
1397 |
-
* `title`: The original title of the image.
|
1398 |
-
|
1399 |
-
|
1400 |
-
Documents
|
1401 |
-
=========
|
1402 |
-
|
1403 |
-
Index
|
1404 |
-
-----
|
1405 |
-
The Index document is the root endpoint for the API server and describes the
|
1406 |
-
contents and abilities of the API server.
|
1407 |
-
|
1408 |
-
### Body
|
1409 |
-
The body of an Index document is an Index entity.
|
1410 |
-
|
1411 |
-
### Example
|
1412 |
-
|
1413 |
-
{
|
1414 |
-
"name":"My WordPress Site",
|
1415 |
-
"description":"Just another WordPress site",
|
1416 |
-
"URL":"http:\/\/example.com",
|
1417 |
-
"routes": {
|
1418 |
-
"\/": {
|
1419 |
-
"supports": [ "HEAD", "GET" ]
|
1420 |
-
},
|
1421 |
-
"\/posts": {
|
1422 |
-
"supports": [ "HEAD", "GET", "POST" ],
|
1423 |
-
"accepts_json": true
|
1424 |
-
},
|
1425 |
-
"\/posts\/<id>": {
|
1426 |
-
"supports": [ "HEAD", "GET", "POST", "PUT", "PATCH", "DELETE" ]
|
1427 |
-
},
|
1428 |
-
"\/posts\/<id>\/revisions": {
|
1429 |
-
"supports": [ "HEAD", "GET" ]
|
1430 |
-
},
|
1431 |
-
"\/posts\/<id>\/comments": {
|
1432 |
-
"supports": [ "HEAD", "GET", "POST" ],
|
1433 |
-
"accepts_json":true
|
1434 |
-
}
|
1435 |
-
},
|
1436 |
-
"meta": {
|
1437 |
-
"links": {
|
1438 |
-
"help":"http:\/\/codex.wordpress.org\/JSON_API"
|
1439 |
-
}
|
1440 |
-
}
|
1441 |
-
}
|
1442 |
-
|
1443 |
-
|
1444 |
-
Post
|
1445 |
-
----
|
1446 |
-
A Post document is defined as the representation of a post item, analogous to an
|
1447 |
-
Atom item.
|
1448 |
-
|
1449 |
-
### Headers
|
1450 |
-
The following headers are sent when a Post is the main entity:
|
1451 |
-
|
1452 |
-
* `Link`:
|
1453 |
-
* `rel="alternate"; type=text/html`: The permalink for the Post
|
1454 |
-
* `rel="collection"`: The endpoint of the Post Collection the Post is
|
1455 |
-
contained in
|
1456 |
-
* `rel="replies"`: The endpoint of the associated Comment Collection
|
1457 |
-
* `rel="version-history"`: The endpoint of the Post Collection containing
|
1458 |
-
the revisions of the Post
|
1459 |
-
|
1460 |
-
|
1461 |
-
### Body
|
1462 |
-
The body of a Post document is a Post entity.
|
1463 |
-
|
1464 |
-
|
1465 |
-
### Example
|
1466 |
-
|
1467 |
-
HTTP/1.1 200 OK
|
1468 |
-
Date: Mon, 07 Jan 2013 03:35:14 GMT
|
1469 |
-
Last-Modified: Mon, 07 Jan 2013 03:35:14 GMT
|
1470 |
-
Link: <http://localhost/wptrunk/?p=1>; rel="alternate"; type=text/html
|
1471 |
-
Link: <http://localhost/wptrunk/wp-json/users/1>; rel="author"
|
1472 |
-
Link: <http://localhost/wptrunk/wp-json/posts>; rel="collection"
|
1473 |
-
Link: <http://localhost/wptrunk/wp-json/posts/158/comments>; rel="replies"
|
1474 |
-
Link: <http://localhost/wptrunk/wp-json/posts/158/revisions>; rel="version-history"
|
1475 |
-
Content-Type: application/json; charset=UTF-8
|
1476 |
-
|
1477 |
-
{
|
1478 |
-
"ID":158,
|
1479 |
-
"title":"This is a test!",
|
1480 |
-
"status":"publish",
|
1481 |
-
"type":"post",
|
1482 |
-
"author":{
|
1483 |
-
"ID":1,
|
1484 |
-
"name":"admin",
|
1485 |
-
"slug":"admin",
|
1486 |
-
"URL":"",
|
1487 |
-
"avatar":"http:\/\/0.gravatar.com\/avatar\/c57c8945079831fa3c19caef02e44614&d=404&r=G",
|
1488 |
-
"meta":{
|
1489 |
-
"links":{
|
1490 |
-
"self":"http:\/\/localhost\/wptrunk\/wp-json\/users\/1",
|
1491 |
-
"archives":"http:\/\/localhost\/wptrunk\/wp-json\/users\/1\/posts"
|
1492 |
-
}
|
1493 |
-
}
|
1494 |
-
},
|
1495 |
-
"content":"Hello.\r\n\r\nHah.",
|
1496 |
-
"parent":0,
|
1497 |
-
"link":"http:\/\/localhost\/wptrunk\/158\/this-is-a-test\/",
|
1498 |
-
"date":"2013-01-07T13:35:14+10:00",
|
1499 |
-
"modified":"2013-01-07T13:49:40+10:00",
|
1500 |
-
"format":"standard",
|
1501 |
-
"slug":"this-is-a-test",
|
1502 |
-
"guid":"http:\/\/localhost\/wptrunk\/?p=158",
|
1503 |
-
"excerpt":"",
|
1504 |
-
"menu_order":0,
|
1505 |
-
"comment_status":"open",
|
1506 |
-
"ping_status":"open",
|
1507 |
-
"sticky":false,
|
1508 |
-
"date_tz":"Australia\/Brisbane",
|
1509 |
-
"date_gmt":"2013-01-07T03:35:14+00:00",
|
1510 |
-
"modified_tz":"Australia\/Brisbane",
|
1511 |
-
"modified_gmt":"2013-01-07T03:49:40+00:00",
|
1512 |
-
"post_thumbnail":[],
|
1513 |
-
"terms":{
|
1514 |
-
"category":{
|
1515 |
-
"ID":1,
|
1516 |
-
"name":"Uncategorized",
|
1517 |
-
"slug":"uncategorized",
|
1518 |
-
"group":0,
|
1519 |
-
"parent":0,
|
1520 |
-
"count":4,
|
1521 |
-
"meta":{
|
1522 |
-
"links":{
|
1523 |
-
"collection":"http:\/\/localhost\/wptrunk\/wp-json\/taxonomy\/category",
|
1524 |
-
"self":"http:\/\/localhost\/wptrunk\/wp-json\/taxonomy\/category\/terms\/1"
|
1525 |
-
}
|
1526 |
-
}
|
1527 |
-
}
|
1528 |
-
},
|
1529 |
-
"post_meta":[],
|
1530 |
-
"meta":{
|
1531 |
-
"links":{
|
1532 |
-
"self":"http:\/\/localhost\/wptrunk\/wp-json\/posts\/158",
|
1533 |
-
"author":"http:\/\/localhost\/wptrunk\/wp-json\/users\/1",
|
1534 |
-
"collection":"http:\/\/localhost\/wptrunk\/wp-json\/posts",
|
1535 |
-
"replies":"http:\/\/localhost\/wptrunk\/wp-json\/posts\/158\/comments",
|
1536 |
-
"version-history":"http:\/\/localhost\/wptrunk\/wp-json\/posts\/158\/revisions"
|
1537 |
-
}
|
1538 |
-
}
|
1539 |
-
}
|
1540 |
-
|
1541 |
-
|
1542 |
-
Post Collection
|
1543 |
-
---------------
|
1544 |
-
A Post Collection document is defined as a collection of Post entities.
|
1545 |
-
|
1546 |
-
### Headers
|
1547 |
-
The following headers are sent when a Post Collection is the main entity:
|
1548 |
-
|
1549 |
-
* `Link`:
|
1550 |
-
* `rel="item"` - Each item in the collection has a corresponding Link header
|
1551 |
-
containing the location of the endpoint for that resource.
|
1552 |
-
|
1553 |
-
|
1554 |
-
### Body
|
1555 |
-
The Post Collection document is a JSON array of Post entities.
|
1556 |
-
|
1557 |
-
|
1558 |
-
User
|
1559 |
-
----
|
1560 |
-
The User document describes a member of the site.
|
1561 |
-
|
1562 |
-
### Body
|
1563 |
-
The body of a User document is a User entity.
|
1564 |
-
|
1565 |
-
|
1566 |
-
Appendix A: JSON Schema
|
1567 |
-
=======================
|
1568 |
-
The JSON Schema describing the entities in this document is available in
|
1569 |
-
schema.json.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extras.php
CHANGED
@@ -1,30 +1,20 @@
|
|
1 |
<?php
|
2 |
-
|
3 |
/**
|
4 |
-
* Extra File
|
|
|
|
|
5 |
*
|
6 |
* @package WordPress
|
7 |
* @subpackage JSON API
|
8 |
-
*
|
9 |
-
* @TODO fix this doc block (Make it better maybe?)
|
10 |
*/
|
11 |
|
12 |
add_action( 'wp_enqueue_scripts', 'rest_register_scripts', -100 );
|
13 |
add_action( 'admin_enqueue_scripts', 'rest_register_scripts', -100 );
|
14 |
-
add_action( 'xmlrpc_rsd_apis', 'rest_output_rsd' );
|
15 |
-
add_action( 'wp_head', 'rest_output_link_wp_head', 10, 0 );
|
16 |
-
add_action( 'template_redirect', 'rest_output_link_header', 11, 0 );
|
17 |
-
add_action( 'auth_cookie_malformed', 'rest_cookie_collect_status' );
|
18 |
-
add_action( 'auth_cookie_expired', 'rest_cookie_collect_status' );
|
19 |
-
add_action( 'auth_cookie_bad_username', 'rest_cookie_collect_status' );
|
20 |
-
add_action( 'auth_cookie_bad_hash', 'rest_cookie_collect_status' );
|
21 |
-
add_action( 'auth_cookie_valid', 'rest_cookie_collect_status' );
|
22 |
-
add_filter( 'rest_authentication_errors', 'rest_cookie_check_errors', 100 );
|
23 |
-
|
24 |
-
|
25 |
|
26 |
/**
|
27 |
-
*
|
|
|
|
|
28 |
*
|
29 |
* @see wp_register_scripts()
|
30 |
*/
|
@@ -36,134 +26,14 @@ function rest_register_scripts() {
|
|
36 |
}
|
37 |
|
38 |
/**
|
39 |
-
*
|
40 |
-
*/
|
41 |
-
function rest_output_rsd() {
|
42 |
-
$api_root = get_rest_url();
|
43 |
-
|
44 |
-
if ( empty( $api_root ) ) {
|
45 |
-
return;
|
46 |
-
}
|
47 |
-
?>
|
48 |
-
<api name="WP-API" blogID="1" preferred="false" apiLink="<?php echo esc_url( $api_root ); ?>" />
|
49 |
-
<?php
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Output API link tag into page header.
|
54 |
-
*
|
55 |
-
* @see get_rest_url()
|
56 |
-
*/
|
57 |
-
function rest_output_link_wp_head() {
|
58 |
-
$api_root = get_rest_url();
|
59 |
-
|
60 |
-
if ( empty( $api_root ) ) {
|
61 |
-
return;
|
62 |
-
}
|
63 |
-
|
64 |
-
echo "<link rel='https://github.com/WP-API/WP-API' href='" . esc_url( $api_root ) . "' />\n";
|
65 |
-
}
|
66 |
-
|
67 |
-
/**
|
68 |
-
* Send a Link header for the API.
|
69 |
-
*/
|
70 |
-
function rest_output_link_header() {
|
71 |
-
if ( headers_sent() ) {
|
72 |
-
return;
|
73 |
-
}
|
74 |
-
|
75 |
-
$api_root = get_rest_url();
|
76 |
-
|
77 |
-
if ( empty($api_root) ) {
|
78 |
-
return;
|
79 |
-
}
|
80 |
-
|
81 |
-
header( 'Link: <' . esc_url_raw( $api_root ) . '>; rel="https://github.com/WP-API/WP-API"', false );
|
82 |
-
}
|
83 |
-
|
84 |
-
/**
|
85 |
-
* Check for errors when using cookie-based authentication.
|
86 |
-
*
|
87 |
-
* WordPress' built-in cookie authentication is always active
|
88 |
-
* for logged in users. However, the API has to check nonces
|
89 |
-
* for each request to ensure users are not vulnerable to CSRF.
|
90 |
-
*
|
91 |
-
* @global mixed $wp_rest_auth_cookie
|
92 |
-
*
|
93 |
-
* @param WP_Error|mixed $result Error from another authentication handler,
|
94 |
-
* null if we should handle it, or another
|
95 |
-
* value if not
|
96 |
-
* @return WP_Error|mixed|bool WP_Error if the cookie is invalid, the $result,
|
97 |
-
* otherwise true.
|
98 |
-
*/
|
99 |
-
function rest_cookie_check_errors( $result ) {
|
100 |
-
if ( ! empty( $result ) ) {
|
101 |
-
return $result;
|
102 |
-
}
|
103 |
-
|
104 |
-
global $wp_rest_auth_cookie;
|
105 |
-
|
106 |
-
/*
|
107 |
-
* Is cookie authentication being used? (If we get an auth
|
108 |
-
* error, but we're still logged in, another authentication
|
109 |
-
* must have been used.)
|
110 |
-
*/
|
111 |
-
if ( true !== $wp_rest_auth_cookie && is_user_logged_in() ) {
|
112 |
-
return $result;
|
113 |
-
}
|
114 |
-
|
115 |
-
// Is there a nonce?
|
116 |
-
$nonce = null;
|
117 |
-
if ( isset( $_REQUEST['_wp_rest_nonce'] ) ) {
|
118 |
-
$nonce = $_REQUEST['_wp_rest_nonce'];
|
119 |
-
} elseif ( isset( $_SERVER['HTTP_X_WP_NONCE'] ) ) {
|
120 |
-
$nonce = $_SERVER['HTTP_X_WP_NONCE'];
|
121 |
-
}
|
122 |
-
|
123 |
-
if ( null === $nonce ) {
|
124 |
-
// No nonce at all, so act as if it's an unauthenticated request.
|
125 |
-
wp_set_current_user( 0 );
|
126 |
-
return true;
|
127 |
-
}
|
128 |
-
|
129 |
-
// Check the nonce.
|
130 |
-
$result = wp_verify_nonce( $nonce, 'wp_rest' );
|
131 |
-
if ( ! $result ) {
|
132 |
-
return new WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie nonce is invalid' ), array( 'status' => 403 ) );
|
133 |
-
}
|
134 |
-
|
135 |
-
return true;
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Collect cookie authentication status.
|
140 |
*
|
141 |
-
*
|
142 |
-
* use by {@see rest_cookie_check_errors}.
|
143 |
*
|
144 |
-
* @see
|
145 |
-
* @global mixed $wp_rest_auth_cookie
|
146 |
-
*/
|
147 |
-
function rest_cookie_collect_status() {
|
148 |
-
global $wp_rest_auth_cookie;
|
149 |
-
|
150 |
-
$status_type = current_action();
|
151 |
-
|
152 |
-
if ( 'auth_cookie_valid' !== $status_type ) {
|
153 |
-
$wp_rest_auth_cookie = substr( $status_type, 12 );
|
154 |
-
return;
|
155 |
-
}
|
156 |
-
|
157 |
-
$wp_rest_auth_cookie = true;
|
158 |
-
}
|
159 |
-
|
160 |
-
/**
|
161 |
-
* Retrieve the avatar urls in various sizes based on a given email address.
|
162 |
-
*
|
163 |
-
* {@see get_avatar_url()}
|
164 |
*
|
165 |
* @param string $email Email address.
|
166 |
-
* @return array $urls
|
167 |
*/
|
168 |
function rest_get_avatar_urls( $email ) {
|
169 |
$avatar_sizes = rest_get_avatar_sizes();
|
@@ -177,120 +47,47 @@ function rest_get_avatar_urls( $email ) {
|
|
177 |
}
|
178 |
|
179 |
/**
|
180 |
-
*
|
|
|
|
|
181 |
*
|
182 |
-
* @return array
|
183 |
*/
|
184 |
function rest_get_avatar_sizes() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
return apply_filters( 'rest_avatar_sizes', array( 24, 48, 96 ) );
|
186 |
}
|
187 |
|
188 |
/**
|
189 |
-
*
|
190 |
*
|
191 |
-
*
|
192 |
-
* @param bool $force_utc Force UTC timezone instead of using the timestamp's TZ.
|
193 |
-
* @return DateTime DateTime instance.
|
194 |
-
*/
|
195 |
-
function rest_parse_date( $date, $force_utc = false ) {
|
196 |
-
if ( $force_utc ) {
|
197 |
-
$date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
|
198 |
-
}
|
199 |
-
|
200 |
-
$regex = '#^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?$#';
|
201 |
-
|
202 |
-
if ( ! preg_match( $regex, $date, $matches ) ) {
|
203 |
-
return false;
|
204 |
-
}
|
205 |
-
|
206 |
-
return strtotime( $date );
|
207 |
-
}
|
208 |
-
|
209 |
-
/**
|
210 |
-
* Get a local date with its GMT equivalent, in MySQL datetime format.
|
211 |
*
|
212 |
-
* @
|
213 |
-
* @
|
214 |
-
* @
|
215 |
-
* null on failure.
|
216 |
-
*/
|
217 |
-
function rest_get_date_with_gmt( $date, $force_utc = false ) {
|
218 |
-
$date = rest_parse_date( $date, $force_utc );
|
219 |
-
|
220 |
-
if ( empty( $date ) ) {
|
221 |
-
return null;
|
222 |
-
}
|
223 |
-
|
224 |
-
$utc = date( 'Y-m-d H:i:s', $date );
|
225 |
-
$local = get_date_from_gmt( $utc );
|
226 |
-
|
227 |
-
return array( $local, $utc );
|
228 |
-
}
|
229 |
-
|
230 |
-
/**
|
231 |
-
* Parses and formats a MySQL datetime (Y-m-d H:i:s) for ISO8601/RFC3339
|
232 |
-
*
|
233 |
-
* Explicitly strips timezones, as datetimes are not saved with any timezone
|
234 |
-
* information. Including any information on the offset could be misleading.
|
235 |
-
*
|
236 |
-
* @param string $date
|
237 |
-
*/
|
238 |
-
function rest_mysql_to_rfc3339( $date_string ) {
|
239 |
-
$formatted = mysql2date( 'c', $date_string, false );
|
240 |
-
|
241 |
-
// Strip timezone information
|
242 |
-
return preg_replace( '/(?:Z|[+-]\d{2}(?::\d{2})?)$/', '', $formatted );
|
243 |
-
}
|
244 |
-
|
245 |
-
|
246 |
-
/**
|
247 |
-
* Get the timezone object for the site.
|
248 |
-
*
|
249 |
-
* @return DateTimeZone DateTimeZone instance.
|
250 |
-
*/
|
251 |
-
function rest_get_timezone() {
|
252 |
-
static $zone = null;
|
253 |
-
|
254 |
-
if ( null !== $zone ) {
|
255 |
-
return $zone;
|
256 |
-
}
|
257 |
-
|
258 |
-
$tzstring = get_option( 'timezone_string' );
|
259 |
-
|
260 |
-
if ( ! $tzstring ) {
|
261 |
-
// Create a UTC+- zone if no timezone string exists
|
262 |
-
$current_offset = get_option( 'gmt_offset' );
|
263 |
-
if ( 0 === $current_offset ) {
|
264 |
-
$tzstring = 'UTC';
|
265 |
-
} elseif ( $current_offset < 0 ) {
|
266 |
-
$tzstring = 'Etc/GMT' . $current_offset;
|
267 |
-
} else {
|
268 |
-
$tzstring = 'Etc/GMT+' . $current_offset;
|
269 |
-
}
|
270 |
-
}
|
271 |
-
$zone = new DateTimeZone( $tzstring );
|
272 |
-
|
273 |
-
return $zone;
|
274 |
-
}
|
275 |
-
|
276 |
-
/**
|
277 |
-
* Retrieve the avatar url for a user who provided a user ID or email address.
|
278 |
-
*
|
279 |
-
* @deprecated WPAPI-2.0
|
280 |
-
* {@see get_avatar()} doesn't return just the URL, so we have to
|
281 |
-
* extract it here.
|
282 |
*
|
283 |
* @param string $email Email address.
|
284 |
* @return string URL for the user's avatar, empty string otherwise.
|
285 |
*/
|
286 |
function rest_get_avatar_url( $email ) {
|
287 |
_deprecated_function( 'rest_get_avatar_url', 'WPAPI-2.0', 'rest_get_avatar_urls' );
|
288 |
-
|
289 |
-
|
290 |
-
*/
|
291 |
if ( function_exists( 'get_avatar_url' ) ) {
|
292 |
return esc_url_raw( get_avatar_url( $email ) );
|
293 |
}
|
|
|
294 |
$avatar_html = get_avatar( $email );
|
295 |
|
296 |
// Strip the avatar url from the get_avatar img tag.
|
@@ -302,3 +99,39 @@ function rest_get_avatar_url( $email ) {
|
|
302 |
|
303 |
return '';
|
304 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
+
* Extra File
|
4 |
+
*
|
5 |
+
* Contains extra functions from plugin.php go.
|
6 |
*
|
7 |
* @package WordPress
|
8 |
* @subpackage JSON API
|
|
|
|
|
9 |
*/
|
10 |
|
11 |
add_action( 'wp_enqueue_scripts', 'rest_register_scripts', -100 );
|
12 |
add_action( 'admin_enqueue_scripts', 'rest_register_scripts', -100 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
/**
|
15 |
+
* Registers REST API JavaScript helpers.
|
16 |
+
*
|
17 |
+
* @since 4.4.0
|
18 |
*
|
19 |
* @see wp_register_scripts()
|
20 |
*/
|
26 |
}
|
27 |
|
28 |
/**
|
29 |
+
* Retrieves the avatar urls in various sizes based on a given email address.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
*
|
31 |
+
* @since 4.4.0
|
|
|
32 |
*
|
33 |
+
* @see get_avatar_url()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
*
|
35 |
* @param string $email Email address.
|
36 |
+
* @return array $urls Gravatar url for each size.
|
37 |
*/
|
38 |
function rest_get_avatar_urls( $email ) {
|
39 |
$avatar_sizes = rest_get_avatar_sizes();
|
47 |
}
|
48 |
|
49 |
/**
|
50 |
+
* Retrieves the pixel sizes for avatars.
|
51 |
+
*
|
52 |
+
* @since 4.4.0
|
53 |
*
|
54 |
+
* @return array List of pixel sizes for avatars. Default `[ 24, 48, 96 ]`.
|
55 |
*/
|
56 |
function rest_get_avatar_sizes() {
|
57 |
+
/**
|
58 |
+
* Filter the REST avatar sizes.
|
59 |
+
*
|
60 |
+
* Use this filter to adjust the array of sizes returned by the
|
61 |
+
* `rest_get_avatar_sizes` function.
|
62 |
+
*
|
63 |
+
* @since 4.4.0
|
64 |
+
*
|
65 |
+
* @param array $sizes An array of int values that are the pixel sizes for avatars.
|
66 |
+
* Default `[ 24, 48, 96 ]`.
|
67 |
+
*/
|
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 |
*
|
74 |
+
* get_avatar() doesn't return just the URL, so we have to extract it here.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
*
|
76 |
+
* @since 4.4.0
|
77 |
+
* @deprecated WPAPI-2.0 rest_get_avatar_urls()
|
78 |
+
* @see rest_get_avatar_urls()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
*
|
80 |
* @param string $email Email address.
|
81 |
* @return string URL for the user's avatar, empty string otherwise.
|
82 |
*/
|
83 |
function rest_get_avatar_url( $email ) {
|
84 |
_deprecated_function( 'rest_get_avatar_url', 'WPAPI-2.0', 'rest_get_avatar_urls' );
|
85 |
+
|
86 |
+
// Use the WP Core `get_avatar_url()` function introduced in 4.2.
|
|
|
87 |
if ( function_exists( 'get_avatar_url' ) ) {
|
88 |
return esc_url_raw( get_avatar_url( $email ) );
|
89 |
}
|
90 |
+
|
91 |
$avatar_html = get_avatar( $email );
|
92 |
|
93 |
// Strip the avatar url from the get_avatar img tag.
|
99 |
|
100 |
return '';
|
101 |
}
|
102 |
+
|
103 |
+
if ( ! function_exists( 'wp_is_numeric_array' ) ) {
|
104 |
+
/**
|
105 |
+
* Determines if the variable is a numeric-indexed array.
|
106 |
+
*
|
107 |
+
* @since 4.4.0
|
108 |
+
*
|
109 |
+
* @param mixed $data Variable to check.
|
110 |
+
* @return bool Whether the variable is a list.
|
111 |
+
*/
|
112 |
+
function wp_is_numeric_array( $data ) {
|
113 |
+
if ( ! is_array( $data ) ) {
|
114 |
+
return false;
|
115 |
+
}
|
116 |
+
|
117 |
+
$keys = array_keys( $data );
|
118 |
+
$string_keys = array_filter( $keys, 'is_string' );
|
119 |
+
return count( $string_keys ) === 0;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Parses and formats a MySQL datetime (Y-m-d H:i:s) for ISO8601/RFC3339.
|
125 |
+
*
|
126 |
+
* Explicitly strips timezones, as datetimes are not saved with any timezone
|
127 |
+
* information. Including any information on the offset could be misleading.
|
128 |
+
*
|
129 |
+
* @deprecated WPAPI-2.0 mysql_to_rfc3339()
|
130 |
+
*
|
131 |
+
* @param string $date_string Date string to parse and format.
|
132 |
+
* @return string Date formatted for ISO8601/RFC3339.
|
133 |
+
*/
|
134 |
+
function rest_mysql_to_rfc3339( $date_string ) {
|
135 |
+
_deprecated_function( 'rest_mysql_to_rfc3339', 'WPAPI-2.0', 'mysql_to_rfc3339' );
|
136 |
+
return mysql_to_rfc3339( $date_string );
|
137 |
+
}
|
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' =>
|
25 |
}
|
26 |
}
|
27 |
|
@@ -71,18 +71,34 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
71 |
return $id;
|
72 |
}
|
73 |
|
|
|
|
|
|
|
74 |
wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
|
75 |
|
|
|
|
|
|
|
|
|
76 |
$this->update_additional_fields_for_object( $attachment, $request );
|
77 |
|
78 |
-
$
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
$response = rest_ensure_response( $response );
|
83 |
$response->set_status( 201 );
|
84 |
$response->header( 'Location', rest_url( '/wp/v2/' . $this->get_post_type_base( $attachment->post_type ) . '/' . $id ) );
|
85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
return $response;
|
87 |
|
88 |
}
|
@@ -103,16 +119,18 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
103 |
$data = $response->get_data();
|
104 |
|
105 |
if ( isset( $request['alt_text'] ) ) {
|
106 |
-
update_post_meta( $data['id'], '_wp_attachment_image_alt',
|
107 |
}
|
108 |
|
109 |
-
$
|
110 |
-
|
111 |
-
|
112 |
-
)
|
113 |
-
|
114 |
-
|
115 |
-
|
|
|
|
|
116 |
}
|
117 |
|
118 |
/**
|
@@ -125,15 +143,15 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
125 |
$prepared_attachment = parent::prepare_item_for_database( $request );
|
126 |
|
127 |
if ( isset( $request['caption'] ) ) {
|
128 |
-
$prepared_attachment->post_excerpt =
|
129 |
}
|
130 |
|
131 |
if ( isset( $request['description'] ) ) {
|
132 |
-
$prepared_attachment->post_content =
|
133 |
}
|
134 |
|
135 |
if ( isset( $request['post'] ) ) {
|
136 |
-
$prepared_attachment->post_parent = (int) $request['
|
137 |
}
|
138 |
|
139 |
return $prepared_attachment;
|
@@ -165,8 +183,30 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
165 |
$img_url_basename = wp_basename( $data['source_url'] );
|
166 |
|
167 |
foreach ( $data['media_details']['sizes'] as $size => &$size_data ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
// Use the same method image_downsize() does
|
169 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
}
|
171 |
} else {
|
172 |
$data['media_details']['sizes'] = new stdClass;
|
@@ -177,11 +217,20 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
177 |
$data = $this->filter_response_by_context( $data, $context );
|
178 |
|
179 |
// Wrap the data in a response object
|
180 |
-
$
|
181 |
-
|
182 |
-
$
|
183 |
-
|
184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
}
|
186 |
|
187 |
/**
|
@@ -194,40 +243,49 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
194 |
$schema = parent::get_item_schema();
|
195 |
|
196 |
$schema['properties']['alt_text'] = array(
|
197 |
-
'description' => 'Alternative text to display when attachment is not displayed.',
|
198 |
'type' => 'string',
|
199 |
'context' => array( 'view', 'edit', 'embed' ),
|
|
|
|
|
|
|
200 |
);
|
201 |
$schema['properties']['caption'] = array(
|
202 |
-
'description' => 'The caption for the attachment.',
|
203 |
'type' => 'string',
|
204 |
'context' => array( 'view', 'edit' ),
|
|
|
|
|
|
|
205 |
);
|
206 |
$schema['properties']['description'] = array(
|
207 |
-
'description' => 'The description for the attachment.',
|
208 |
'type' => 'string',
|
209 |
'context' => array( 'view', 'edit' ),
|
|
|
|
|
|
|
210 |
);
|
211 |
$schema['properties']['media_type'] = array(
|
212 |
-
'description' => 'Type of attachment.',
|
213 |
'type' => 'string',
|
214 |
'enum' => array( 'image', 'file' ),
|
215 |
'context' => array( 'view', 'edit', 'embed' ),
|
216 |
'readonly' => true,
|
217 |
);
|
218 |
$schema['properties']['media_details'] = array(
|
219 |
-
'description' => 'Details about the attachment file, specific to its type.',
|
220 |
'type' => 'object',
|
221 |
-
'context' => array( 'view', 'edit' ),
|
222 |
'readonly' => true,
|
223 |
);
|
224 |
$schema['properties']['post'] = array(
|
225 |
-
'description' => 'The
|
226 |
'type' => 'integer',
|
227 |
'context' => array( 'view', 'edit' ),
|
228 |
);
|
229 |
$schema['properties']['source_url'] = array(
|
230 |
-
'description' => 'URL to the original attachment file.',
|
231 |
'type' => 'string',
|
232 |
'format' => 'uri',
|
233 |
'context' => array( 'view', 'edit', 'embed' ),
|
@@ -287,6 +345,9 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
287 |
// Get the content-type
|
288 |
$type = array_shift( $headers['content_type'] );
|
289 |
|
|
|
|
|
|
|
290 |
// Save the file
|
291 |
$tmpfname = wp_tempnam( $filename );
|
292 |
|
@@ -321,6 +382,22 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
321 |
return $sideloaded;
|
322 |
}
|
323 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
324 |
/**
|
325 |
* Handle an upload via multipart/form-data ($_FILES)
|
326 |
*
|
@@ -334,8 +411,9 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
334 |
}
|
335 |
|
336 |
// Verify hash, if given
|
337 |
-
if ( ! empty( $headers['
|
338 |
-
$
|
|
|
339 |
$actual = md5_file( $files['file']['tmp_name'] );
|
340 |
if ( $expected !== $actual ) {
|
341 |
return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected' ), array( 'status' => 412 ) );
|
@@ -351,7 +429,9 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
|
351 |
$overrides['action'] = 'wp_handle_mock_upload';
|
352 |
}
|
353 |
|
354 |
-
|
|
|
|
|
355 |
|
356 |
if ( isset( $file['error'] ) ) {
|
357 |
return new WP_Error( 'rest_upload_unknown_error', $file['error'], array( 'status' => 500 ) );
|
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 |
|
71 |
return $id;
|
72 |
}
|
73 |
|
74 |
+
/** Include admin functions to get access to wp_generate_attachment_metadata() */
|
75 |
+
require_once ABSPATH . 'wp-admin/includes/admin.php';
|
76 |
+
|
77 |
wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
|
78 |
|
79 |
+
if ( isset( $request['alt_text'] ) ) {
|
80 |
+
update_post_meta( $id, '_wp_attachment_image_alt', sanitize_text_field( $request['alt_text'] ) );
|
81 |
+
}
|
82 |
+
|
83 |
$this->update_additional_fields_for_object( $attachment, $request );
|
84 |
|
85 |
+
$get_request = new WP_REST_Request;
|
86 |
+
$get_request->set_param( 'id', $id );
|
87 |
+
$get_request->set_param( 'context', 'edit' );
|
88 |
+
$response = $this->get_item( $get_request );
|
89 |
$response = rest_ensure_response( $response );
|
90 |
$response->set_status( 201 );
|
91 |
$response->header( 'Location', rest_url( '/wp/v2/' . $this->get_post_type_base( $attachment->post_type ) . '/' . $id ) );
|
92 |
|
93 |
+
/**
|
94 |
+
* Fires after a single attachment is created or updated via the REST API.
|
95 |
+
*
|
96 |
+
* @param object $attachment Inserted attachment.
|
97 |
+
* @param WP_REST_Request $request The request sent to the API.
|
98 |
+
* @param bool $creating True when creating an attachment, false when updating.
|
99 |
+
*/
|
100 |
+
do_action( 'rest_insert_attachment', $attachment, $request, true );
|
101 |
+
|
102 |
return $response;
|
103 |
|
104 |
}
|
119 |
$data = $response->get_data();
|
120 |
|
121 |
if ( isset( $request['alt_text'] ) ) {
|
122 |
+
update_post_meta( $data['id'], '_wp_attachment_image_alt', $request['alt_text'] );
|
123 |
}
|
124 |
|
125 |
+
$get_request = new WP_REST_Request;
|
126 |
+
$get_request->set_param( 'id', $data['id'] );
|
127 |
+
$get_request->set_param( 'context', 'edit' );
|
128 |
+
$response = $this->get_item( $get_request );
|
129 |
+
|
130 |
+
/* This action is documented in lib/endpoints/class-wp-rest-attachments-controller.php */
|
131 |
+
do_action( 'rest_insert_attachment', $data, $request, false );
|
132 |
+
|
133 |
+
return rest_ensure_response( $response );
|
134 |
}
|
135 |
|
136 |
/**
|
143 |
$prepared_attachment = parent::prepare_item_for_database( $request );
|
144 |
|
145 |
if ( isset( $request['caption'] ) ) {
|
146 |
+
$prepared_attachment->post_excerpt = $request['caption'];
|
147 |
}
|
148 |
|
149 |
if ( isset( $request['description'] ) ) {
|
150 |
+
$prepared_attachment->post_content = $request['description'];
|
151 |
}
|
152 |
|
153 |
if ( isset( $request['post'] ) ) {
|
154 |
+
$prepared_attachment->post_parent = (int) $request['post'];
|
155 |
}
|
156 |
|
157 |
return $prepared_attachment;
|
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;
|
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 |
/**
|
243 |
$schema = parent::get_item_schema();
|
244 |
|
245 |
$schema['properties']['alt_text'] = array(
|
246 |
+
'description' => __( 'Alternative text to display when attachment is not displayed.' ),
|
247 |
'type' => 'string',
|
248 |
'context' => array( 'view', 'edit', 'embed' ),
|
249 |
+
'arg_options' => array(
|
250 |
+
'sanitize_callback' => 'sanitize_text_field',
|
251 |
+
),
|
252 |
);
|
253 |
$schema['properties']['caption'] = array(
|
254 |
+
'description' => __( 'The caption for the attachment.' ),
|
255 |
'type' => 'string',
|
256 |
'context' => array( 'view', 'edit' ),
|
257 |
+
'arg_options' => array(
|
258 |
+
'sanitize_callback' => 'wp_filter_post_kses',
|
259 |
+
),
|
260 |
);
|
261 |
$schema['properties']['description'] = array(
|
262 |
+
'description' => __( 'The description for the attachment.' ),
|
263 |
'type' => 'string',
|
264 |
'context' => array( 'view', 'edit' ),
|
265 |
+
'arg_options' => array(
|
266 |
+
'sanitize_callback' => 'wp_filter_post_kses',
|
267 |
+
),
|
268 |
);
|
269 |
$schema['properties']['media_type'] = array(
|
270 |
+
'description' => __( 'Type of attachment.' ),
|
271 |
'type' => 'string',
|
272 |
'enum' => array( 'image', 'file' ),
|
273 |
'context' => array( 'view', 'edit', 'embed' ),
|
274 |
'readonly' => true,
|
275 |
);
|
276 |
$schema['properties']['media_details'] = array(
|
277 |
+
'description' => __( 'Details about the attachment file, specific to its type.' ),
|
278 |
'type' => 'object',
|
279 |
+
'context' => array( 'view', 'edit', 'embed' ),
|
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 |
);
|
287 |
$schema['properties']['source_url'] = array(
|
288 |
+
'description' => __( 'URL to the original attachment file.' ),
|
289 |
'type' => 'string',
|
290 |
'format' => 'uri',
|
291 |
'context' => array( 'view', 'edit', 'embed' ),
|
345 |
// Get the content-type
|
346 |
$type = array_shift( $headers['content_type'] );
|
347 |
|
348 |
+
/** Include admin functions to get access to wp_tempnam() and wp_handle_sideload() */
|
349 |
+
require_once ABSPATH . 'wp-admin/includes/admin.php';
|
350 |
+
|
351 |
// Save the file
|
352 |
$tmpfname = wp_tempnam( $filename );
|
353 |
|
382 |
return $sideloaded;
|
383 |
}
|
384 |
|
385 |
+
/**
|
386 |
+
* Get the query params for collections of attachments.
|
387 |
+
*
|
388 |
+
* @return array
|
389 |
+
*/
|
390 |
+
public function get_collection_params() {
|
391 |
+
$params = parent::get_collection_params();
|
392 |
+
$params['parent'] = array(
|
393 |
+
'description' => __( 'Limit results to attachments from a specified parent.' ),
|
394 |
+
'type' => 'integer',
|
395 |
+
'default' => null,
|
396 |
+
'sanitize_callback' => 'absint',
|
397 |
+
);
|
398 |
+
return $params;
|
399 |
+
}
|
400 |
+
|
401 |
/**
|
402 |
* Handle an upload via multipart/form-data ($_FILES)
|
403 |
*
|
411 |
}
|
412 |
|
413 |
// Verify hash, if given
|
414 |
+
if ( ! empty( $headers['content_md5'] ) ) {
|
415 |
+
$content_md5 = array_shift( $headers['content_md5'] );
|
416 |
+
$expected = trim( $content_md5 );
|
417 |
$actual = md5_file( $files['file']['tmp_name'] );
|
418 |
if ( $expected !== $actual ) {
|
419 |
return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected' ), array( 'status' => 412 ) );
|
429 |
$overrides['action'] = 'wp_handle_mock_upload';
|
430 |
}
|
431 |
|
432 |
+
/** Include admin functions to get access to wp_handle_upload() */
|
433 |
+
require_once ABSPATH . 'wp-admin/includes/admin.php';
|
434 |
+
$file = wp_handle_upload( $files['file'], $overrides );
|
435 |
|
436 |
if ( isset( $file['error'] ) ) {
|
437 |
return new WP_Error( 'rest_upload_unknown_error', $file['error'], array( 'status' => 500 ) );
|
lib/endpoints/class-wp-rest-comments-controller.php
CHANGED
@@ -10,66 +10,21 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
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' => $
|
20 |
),
|
21 |
array(
|
22 |
'methods' => WP_REST_Server::CREATABLE,
|
23 |
'callback' => array( $this, 'create_item' ),
|
24 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
25 |
-
'args' =>
|
26 |
-
'post' => array(
|
27 |
-
'required' => true,
|
28 |
-
'sanitize_callback' => 'absint',
|
29 |
-
),
|
30 |
-
'type' => array(
|
31 |
-
'default' => '',
|
32 |
-
'sanitize_callback' => 'sanitize_key',
|
33 |
-
),
|
34 |
-
'parent' => array(
|
35 |
-
'default' => 0,
|
36 |
-
'sanitize_callback' => 'absint',
|
37 |
-
),
|
38 |
-
'content' => array(
|
39 |
-
'default' => '',
|
40 |
-
'sanitize_callback' => 'wp_filter_post_kses',
|
41 |
-
),
|
42 |
-
'author' => array(
|
43 |
-
'default' => 0,
|
44 |
-
'sanitize_callback' => 'absint',
|
45 |
-
),
|
46 |
-
'author_name' => array(
|
47 |
-
'default' => '',
|
48 |
-
'sanitize_callback' => 'sanitize_text_field',
|
49 |
-
),
|
50 |
-
'author_email' => array(
|
51 |
-
'default' => '',
|
52 |
-
'sanitize_callback' => 'sanitize_email',
|
53 |
-
),
|
54 |
-
'author_url' => array(
|
55 |
-
'default' => '',
|
56 |
-
'sanitize_callback' => 'esc_url_raw',
|
57 |
-
),
|
58 |
-
'karma' => array(
|
59 |
-
'default' => 0,
|
60 |
-
'sanitize_callback' => 'absint',
|
61 |
-
),
|
62 |
-
'status' => array(
|
63 |
-
'sanitize_callback' => 'sanitize_key',
|
64 |
-
),
|
65 |
-
'date' => array(
|
66 |
-
'default' => current_time( 'mysql' ),
|
67 |
-
),
|
68 |
-
'date_gmt' => array(
|
69 |
-
'default' => current_time( 'mysql', true ),
|
70 |
-
),
|
71 |
-
),
|
72 |
),
|
|
|
|
|
73 |
) );
|
74 |
|
75 |
register_rest_route( 'wp/v2', '/comments/(?P<id>[\d]+)', array(
|
@@ -78,49 +33,14 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
78 |
'callback' => array( $this, 'get_item' ),
|
79 |
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
80 |
'args' => array(
|
81 |
-
'context'
|
82 |
-
'default' => 'view',
|
83 |
-
),
|
84 |
),
|
85 |
),
|
86 |
array(
|
87 |
'methods' => WP_REST_Server::EDITABLE,
|
88 |
'callback' => array( $this, 'update_item' ),
|
89 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
90 |
-
'args' =>
|
91 |
-
'post' => array(
|
92 |
-
'sanitize_callback' => 'absint',
|
93 |
-
),
|
94 |
-
'type' => array(
|
95 |
-
'sanitize_callback' => 'sanitize_key',
|
96 |
-
),
|
97 |
-
'parent' => array(
|
98 |
-
'sanitize_callback' => 'absint',
|
99 |
-
),
|
100 |
-
'content' => array(
|
101 |
-
'sanitize_callback' => 'wp_filter_post_kses',
|
102 |
-
),
|
103 |
-
'author' => array(
|
104 |
-
'sanitize_callback' => 'absint',
|
105 |
-
),
|
106 |
-
'author_name' => array(
|
107 |
-
'sanitize_callback' => 'sanitize_text_field',
|
108 |
-
),
|
109 |
-
'author_email' => array(
|
110 |
-
'sanitize_callback' => 'sanitize_email',
|
111 |
-
),
|
112 |
-
'author_url' => array(
|
113 |
-
'sanitize_callback' => 'esc_url_raw',
|
114 |
-
),
|
115 |
-
'karma' => array(
|
116 |
-
'sanitize_callback' => 'absint',
|
117 |
-
),
|
118 |
-
'status' => array(
|
119 |
-
'sanitize_callback' => 'sanitize_key',
|
120 |
-
),
|
121 |
-
'date' => array(),
|
122 |
-
'date_gmt' => array(),
|
123 |
-
),
|
124 |
),
|
125 |
array(
|
126 |
'methods' => WP_REST_Server::DELETABLE,
|
@@ -130,11 +50,8 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
130 |
'force' => array(),
|
131 |
),
|
132 |
),
|
133 |
-
) );
|
134 |
|
135 |
-
|
136 |
-
'methods' => WP_REST_Server::READABLE,
|
137 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
138 |
) );
|
139 |
}
|
140 |
|
@@ -201,12 +118,14 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
201 |
|
202 |
$comment = get_comment( $id );
|
203 |
if ( empty( $comment ) ) {
|
204 |
-
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment
|
205 |
}
|
206 |
|
207 |
-
|
208 |
-
|
209 |
-
|
|
|
|
|
210 |
}
|
211 |
|
212 |
$data = $this->prepare_item_for_response( $comment, $request );
|
@@ -226,18 +145,46 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
226 |
return new WP_Error( 'rest_comment_exists', __( 'Cannot create existing comment.' ), array( 'status' => 400 ) );
|
227 |
}
|
228 |
|
229 |
-
$post = get_post( $request['post'] );
|
230 |
-
if ( empty( $post ) ) {
|
231 |
-
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
|
232 |
-
}
|
233 |
-
|
234 |
$prepared_comment = $this->prepare_item_for_database( $request );
|
|
|
235 |
// Setting remaining values before wp_insert_comment so we can
|
236 |
// use wp_allow_comment().
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
237 |
$prepared_comment['comment_author_IP'] = '127.0.0.1';
|
238 |
$prepared_comment['comment_agent'] = '';
|
239 |
$prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment );
|
240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
$prepared_comment = apply_filters( 'rest_pre_insert_comment', $prepared_comment, $request );
|
242 |
|
243 |
$comment_id = wp_insert_comment( $prepared_comment );
|
@@ -253,10 +200,10 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
253 |
$this->update_additional_fields_for_object( get_comment( $comment_id ), $request );
|
254 |
|
255 |
$context = current_user_can( 'moderate_comments' ) ? 'edit' : 'view';
|
256 |
-
$
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
$response = rest_ensure_response( $response );
|
261 |
if ( is_wp_error( $response ) ) {
|
262 |
return $response;
|
@@ -264,6 +211,15 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
264 |
$response->set_status( 201 );
|
265 |
$response->header( 'Location', rest_url( '/wp/v2/comments/' . $comment_id ) );
|
266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
267 |
return $response;
|
268 |
}
|
269 |
|
@@ -278,7 +234,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
278 |
|
279 |
$comment = get_comment( $id );
|
280 |
if ( empty( $comment ) ) {
|
281 |
-
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment
|
282 |
}
|
283 |
|
284 |
if ( isset( $request['type'] ) && $request['type'] !== $comment->comment_type ) {
|
@@ -308,24 +264,22 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
308 |
|
309 |
$this->update_additional_fields_for_object( get_comment( $id ), $request );
|
310 |
|
311 |
-
$
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
$response = rest_ensure_response( $response );
|
316 |
-
if ( is_wp_error( $response ) ) {
|
317 |
-
return $response;
|
318 |
-
}
|
319 |
-
$response->header( 'Location', rest_url( '/wp/v2/comments/' . $comment->comment_ID ) );
|
320 |
|
321 |
-
|
|
|
|
|
|
|
322 |
}
|
323 |
|
324 |
/**
|
325 |
* Delete a comment.
|
326 |
*
|
327 |
* @param WP_REST_Request $request Full details about the request.
|
328 |
-
* @return WP_Error|
|
329 |
*/
|
330 |
public function delete_item( $request ) {
|
331 |
$id = (int) $request['id'];
|
@@ -333,23 +287,27 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
333 |
|
334 |
$comment = get_comment( $id );
|
335 |
if ( empty( $comment ) ) {
|
336 |
-
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment
|
337 |
}
|
338 |
|
339 |
/**
|
340 |
-
* Filter whether
|
|
|
|
|
341 |
*
|
342 |
-
* @param boolean $supports_trash
|
343 |
-
* @param
|
344 |
*/
|
345 |
-
$supports_trash = apply_filters( '
|
346 |
|
347 |
-
$get_request = new WP_REST_Request
|
|
|
348 |
$get_request->set_param( 'context', 'edit' );
|
349 |
$response = $this->prepare_item_for_response( $comment, $get_request );
|
350 |
|
351 |
if ( $force ) {
|
352 |
$result = wp_delete_comment( $comment->comment_ID, true );
|
|
|
353 |
} else {
|
354 |
// If we don't support trashing for this type, error out
|
355 |
if ( ! $supports_trash ) {
|
@@ -357,12 +315,29 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
357 |
}
|
358 |
|
359 |
$result = wp_trash_comment( $comment->comment_ID );
|
|
|
360 |
}
|
361 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
362 |
if ( ! $result ) {
|
363 |
return new WP_Error( 'rest_cannot_delete', __( 'The comment cannot be deleted.' ), array( 'status' => 500 ) );
|
364 |
}
|
365 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
366 |
return $response;
|
367 |
}
|
368 |
|
@@ -380,12 +355,12 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
380 |
$post = get_post( (int) $request['post'] );
|
381 |
|
382 |
if ( $post && ! $this->check_read_post_permission( $post ) ) {
|
383 |
-
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ) );
|
384 |
}
|
385 |
}
|
386 |
|
387 |
-
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( '
|
388 |
-
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view comments with edit context.' ), array( 'status' =>
|
389 |
}
|
390 |
|
391 |
return true;
|
@@ -407,17 +382,17 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
407 |
}
|
408 |
|
409 |
if ( ! $this->check_read_permission( $comment ) ) {
|
410 |
-
return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot read this comment.' ), array( 'status' =>
|
411 |
}
|
412 |
|
413 |
$post = get_post( $comment->comment_post_ID );
|
414 |
|
415 |
if ( $post && ! $this->check_read_post_permission( $post ) ) {
|
416 |
-
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' =>
|
417 |
}
|
418 |
|
419 |
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
|
420 |
-
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view this comment with edit context.' ), array( 'status' =>
|
421 |
}
|
422 |
|
423 |
return true;
|
@@ -431,28 +406,25 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
431 |
*/
|
432 |
public function create_item_permissions_check( $request ) {
|
433 |
|
|
|
|
|
|
|
|
|
434 |
// Limit who can set comment `author`, `karma` or `status` to anything other than the default.
|
435 |
if ( isset( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( 'moderate_comments' ) ) {
|
436 |
-
return new WP_Error( 'rest_comment_invalid_author', __( 'Comment author invalid.' ), array( 'status' =>
|
437 |
}
|
438 |
if ( isset( $request['karma'] ) && $request['karma'] > 0 && ! current_user_can( 'moderate_comments' ) ) {
|
439 |
-
return new WP_Error( 'rest_comment_invalid_karma', __( 'Sorry, you cannot set karma for comments.' ), array( 'status' =>
|
440 |
}
|
441 |
if ( isset( $request['status'] ) && ! current_user_can( 'moderate_comments' ) ) {
|
442 |
-
return new WP_Error( 'rest_comment_invalid_status', __( 'Sorry, you cannot set status for comments.' ), array( 'status' =>
|
443 |
}
|
444 |
|
445 |
-
|
446 |
-
if ( ! isset( $request['post'] ) ) {
|
447 |
-
return true;
|
448 |
-
}
|
449 |
-
|
450 |
-
$post = get_post( (int) $request['post'] );
|
451 |
-
|
452 |
-
if ( $post ) {
|
453 |
|
454 |
if ( ! $this->check_read_post_permission( $post ) ) {
|
455 |
-
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' =>
|
456 |
}
|
457 |
|
458 |
if ( ! comments_open( $post->ID ) ) {
|
@@ -476,7 +448,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
476 |
$comment = get_comment( $id );
|
477 |
|
478 |
if ( $comment && ! $this->check_edit_permission( $comment ) ) {
|
479 |
-
return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you can not edit this comment.' ), array( 'status' =>
|
480 |
}
|
481 |
|
482 |
return true;
|
@@ -497,7 +469,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
497 |
*
|
498 |
* @param object $comment Comment object.
|
499 |
* @param WP_REST_Request $request Request object.
|
500 |
-
* @return
|
501 |
*/
|
502 |
public function prepare_item_for_response( $comment, $request ) {
|
503 |
$data = array(
|
@@ -511,8 +483,8 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
511 |
'author_ip' => $comment->comment_author_IP,
|
512 |
'author_avatar_urls' => rest_get_avatar_urls( $comment->comment_author_email ),
|
513 |
'author_user_agent' => $comment->comment_agent,
|
514 |
-
'date' =>
|
515 |
-
'date_gmt' =>
|
516 |
'content' => array(
|
517 |
'rendered' => apply_filters( 'comment_text', $comment->comment_content, $comment ),
|
518 |
'raw' => $comment->comment_content,
|
@@ -528,11 +500,20 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
528 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
529 |
|
530 |
// Wrap the data in a response object
|
531 |
-
$
|
532 |
|
533 |
-
$
|
534 |
|
535 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
536 |
}
|
537 |
|
538 |
/**
|
@@ -591,17 +572,17 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
591 |
* @return array $prepared_args
|
592 |
*/
|
593 |
protected function prepare_items_query( $request ) {
|
594 |
-
$order_by = sanitize_key( $request['orderby'] );
|
595 |
|
596 |
$prepared_args = array(
|
597 |
-
'
|
598 |
-
'
|
599 |
-
'
|
600 |
-
'
|
601 |
-
'
|
602 |
-
'
|
603 |
-
'
|
604 |
-
'
|
|
|
605 |
);
|
606 |
|
607 |
$prepared_args['offset'] = $prepared_args['number'] * ( absint( $request['page'] ) - 1 );
|
@@ -645,6 +626,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
645 |
case 'parent':
|
646 |
$normalized = $prefix . 'parent';
|
647 |
break;
|
|
|
|
|
|
|
648 |
default:
|
649 |
$normalized = $prefix . $query_param;
|
650 |
break;
|
@@ -731,18 +715,13 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
731 |
$date_data = rest_get_date_with_gmt( $request['date'] );
|
732 |
|
733 |
if ( ! empty( $date_data ) ) {
|
734 |
-
list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) =
|
735 |
-
$date_data;
|
736 |
-
} else {
|
737 |
-
return new WP_Error( 'rest_invalid_date', __( 'The date you provided is invalid.' ), array( 'status' => 400 ) );
|
738 |
}
|
739 |
} elseif ( ! empty( $request['date_gmt'] ) ) {
|
740 |
$date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
|
741 |
|
742 |
if ( ! empty( $date_data ) ) {
|
743 |
list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data;
|
744 |
-
} else {
|
745 |
-
return new WP_Error( 'rest_invalid_date', __( 'The date you provided is invalid.' ), array( 'status' => 400 ) );
|
746 |
}
|
747 |
}
|
748 |
|
@@ -760,8 +739,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
760 |
$avatar_sizes = rest_get_avatar_sizes();
|
761 |
foreach ( $avatar_sizes as $size ) {
|
762 |
$avatar_properties[ $size ] = array(
|
763 |
-
'description' => 'Avatar URL with image size of
|
764 |
-
'type' => '
|
|
|
765 |
'context' => array( 'embed', 'view', 'edit' ),
|
766 |
);
|
767 |
}
|
@@ -772,113 +752,133 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
772 |
'type' => 'object',
|
773 |
'properties' => array(
|
774 |
'id' => array(
|
775 |
-
'description' => 'Unique identifier for the object.',
|
776 |
'type' => 'integer',
|
777 |
'context' => array( 'view', 'edit', 'embed' ),
|
778 |
'readonly' => true,
|
779 |
-
|
780 |
'author' => array(
|
781 |
-
'description' => 'The
|
782 |
'type' => 'integer',
|
783 |
'context' => array( 'view', 'edit', 'embed' ),
|
784 |
-
|
785 |
'author_avatar_urls' => array(
|
786 |
-
'description' => 'Avatar URLs for the object author.',
|
787 |
'type' => 'object',
|
788 |
'context' => array( 'view', 'edit', 'embed' ),
|
789 |
'readonly' => true,
|
790 |
'properties' => $avatar_properties,
|
791 |
-
|
792 |
'author_email' => array(
|
793 |
-
'description' => 'Email address for the object author.',
|
794 |
'type' => 'string',
|
795 |
'format' => 'email',
|
796 |
'context' => array( 'edit' ),
|
797 |
-
|
798 |
'author_ip' => array(
|
799 |
-
'description' => 'IP address for the object author.',
|
800 |
'type' => 'string',
|
801 |
'context' => array( 'edit' ),
|
802 |
'readonly' => true,
|
803 |
-
|
804 |
'author_name' => array(
|
805 |
-
'description' => 'Display name for the object author.',
|
806 |
'type' => 'string',
|
807 |
'context' => array( 'view', 'edit', 'embed' ),
|
|
|
|
|
|
|
808 |
),
|
|
|
809 |
'author_url' => array(
|
810 |
-
'description' => 'URL for the object author.',
|
811 |
'type' => 'string',
|
812 |
'format' => 'uri',
|
813 |
'context' => array( 'view', 'edit', 'embed' ),
|
814 |
-
|
815 |
'author_user_agent' => array(
|
816 |
-
'description' => 'User agent for the object author.',
|
817 |
'type' => 'string',
|
818 |
'context' => array( 'edit' ),
|
819 |
'readonly' => true,
|
820 |
-
|
821 |
'content' => array(
|
822 |
-
'description' => 'The content for the object.',
|
823 |
'type' => 'object',
|
824 |
'context' => array( 'view', 'edit', 'embed' ),
|
825 |
'properties' => array(
|
826 |
'raw' => array(
|
827 |
-
'description' => 'Content for the object, as it exists in the database.',
|
828 |
'type' => 'string',
|
829 |
'context' => array( 'edit' ),
|
830 |
-
|
831 |
'rendered' => array(
|
832 |
-
'description' => 'Content for the object, transformed for display.',
|
833 |
'type' => 'string',
|
834 |
'context' => array( 'view', 'edit', 'embed' ),
|
835 |
-
),
|
836 |
),
|
837 |
),
|
|
|
|
|
|
|
|
|
|
|
838 |
'date' => array(
|
839 |
-
'description' => 'The date the object was published.',
|
840 |
'type' => 'string',
|
841 |
'format' => 'date-time',
|
842 |
'context' => array( 'view', 'edit', 'embed' ),
|
843 |
),
|
844 |
'date_gmt' => array(
|
845 |
-
'description' => 'The date the object was published as GMT.',
|
846 |
'type' => 'string',
|
847 |
'format' => 'date-time',
|
848 |
-
'context' => array( 'edit' ),
|
849 |
),
|
850 |
'karma' => array(
|
851 |
-
'description' => 'Karma for the object.',
|
852 |
'type' => 'integer',
|
853 |
'context' => array( 'edit' ),
|
854 |
-
'readonly' => true,
|
855 |
),
|
856 |
'link' => array(
|
857 |
-
'description' => 'URL to the object.',
|
858 |
'type' => 'string',
|
859 |
'format' => 'uri',
|
860 |
'context' => array( 'view', 'edit', 'embed' ),
|
861 |
'readonly' => true,
|
862 |
),
|
863 |
'parent' => array(
|
864 |
-
'description' => 'The
|
865 |
'type' => 'integer',
|
866 |
'context' => array( 'view', 'edit', 'embed' ),
|
|
|
|
|
|
|
867 |
),
|
868 |
'post' => array(
|
869 |
-
'description' => 'The
|
870 |
'type' => 'integer',
|
871 |
'context' => array( 'view', 'edit' ),
|
|
|
|
|
|
|
872 |
),
|
873 |
'status' => array(
|
874 |
-
'description' => 'State of the object.',
|
875 |
'type' => 'string',
|
876 |
'context' => array( 'view', 'edit' ),
|
|
|
|
|
|
|
877 |
),
|
878 |
'type' => array(
|
879 |
-
'description' => 'Type of Comment for the object.',
|
880 |
'type' => 'string',
|
881 |
'context' => array( 'view', 'edit', 'embed' ),
|
|
|
|
|
|
|
|
|
882 |
),
|
883 |
),
|
884 |
);
|
@@ -892,76 +892,101 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
|
|
892 |
*/
|
893 |
public function get_collection_params() {
|
894 |
$query_params = parent::get_collection_params();
|
|
|
|
|
|
|
895 |
$query_params['author_email'] = array(
|
896 |
'default' => null,
|
897 |
-
'description' => 'Limit result set to that from a specific author email.',
|
898 |
'format' => 'email',
|
899 |
'sanitize_callback' => 'sanitize_email',
|
900 |
'type' => 'string',
|
901 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
902 |
$query_params['karma'] = array(
|
903 |
'default' => null,
|
904 |
-
'description' => 'Limit result set to that of a particular comment karma.',
|
905 |
'sanitize_callback' => 'absint',
|
906 |
'type' => 'integer',
|
907 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
908 |
$query_params['parent'] = array(
|
909 |
'default' => null,
|
910 |
-
'description' => 'Limit result set to that of a specific comment parent id.',
|
911 |
'sanitize_callback' => 'absint',
|
912 |
'type' => 'integer',
|
913 |
);
|
914 |
$query_params['post'] = array(
|
915 |
'default' => null,
|
916 |
-
'description' => 'Limit result set to comments assigned to a specific post id.',
|
917 |
'sanitize_callback' => 'absint',
|
918 |
'type' => 'integer',
|
919 |
);
|
920 |
$query_params['post_author'] = array(
|
921 |
'default' => null,
|
922 |
-
'description' => 'Limit result set to comments associated with posts of a specific post author id.',
|
923 |
'sanitize_callback' => 'absint',
|
924 |
'type' => 'integer',
|
925 |
);
|
926 |
$query_params['post_slug'] = array(
|
927 |
'default' => null,
|
928 |
-
'description' => 'Limit result set to comments associated with posts of a specific post slug.',
|
929 |
'sanitize_callback' => 'sanitize_title',
|
930 |
'type' => 'string',
|
931 |
);
|
932 |
$query_params['post_parent'] = array(
|
933 |
'default' => null,
|
934 |
-
'description' => 'Limit result set to comments associated with posts of a specific post parent id.',
|
935 |
'sanitize_callback' => 'absint',
|
936 |
'type' => 'integer',
|
937 |
);
|
938 |
$query_params['post_status'] = array(
|
939 |
'default' => null,
|
940 |
-
'description' => 'Limit result set to comments associated with posts of a specific post status.',
|
941 |
'sanitize_callback' => 'sanitize_key',
|
942 |
'type' => 'string',
|
943 |
);
|
944 |
$query_params['post_type'] = array(
|
945 |
'default' => null,
|
946 |
-
'description' => 'Limit result set to comments associated with posts of a specific post type.',
|
947 |
'sanitize_callback' => 'sanitize_key',
|
948 |
'type' => 'string',
|
949 |
);
|
950 |
$query_params['status'] = array(
|
951 |
'default' => 'approve',
|
952 |
-
'description' => 'Limit result set to comments assigned a specific status.',
|
953 |
'sanitize_callback' => 'sanitize_key',
|
954 |
'type' => 'string',
|
955 |
);
|
956 |
$query_params['type'] = array(
|
957 |
'default' => 'comment',
|
958 |
-
'description' => 'Limit result set to comments assigned a specific type.',
|
959 |
'sanitize_callback' => 'sanitize_key',
|
960 |
'type' => 'string',
|
961 |
);
|
962 |
$query_params['user'] = array(
|
963 |
'default' => null,
|
964 |
-
'description' => 'Limit result set to comments assigned to a specific user id.',
|
965 |
'sanitize_callback' => 'absint',
|
966 |
'type' => 'integer',
|
967 |
);
|
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,
|
22 |
'callback' => array( $this, 'create_item' ),
|
23 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
24 |
+
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
),
|
26 |
+
|
27 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
28 |
) );
|
29 |
|
30 |
register_rest_route( 'wp/v2', '/comments/(?P<id>[\d]+)', array(
|
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(
|
40 |
'methods' => WP_REST_Server::EDITABLE,
|
41 |
'callback' => array( $this, 'update_item' ),
|
42 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
43 |
+
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
),
|
45 |
array(
|
46 |
'methods' => WP_REST_Server::DELETABLE,
|
50 |
'force' => array(),
|
51 |
),
|
52 |
),
|
|
|
53 |
|
54 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
|
|
|
|
55 |
) );
|
56 |
}
|
57 |
|
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 |
+
if ( ! empty( $comment->comment_post_ID ) ) {
|
125 |
+
$post = get_post( $comment->comment_post_ID );
|
126 |
+
if ( empty( $post ) ) {
|
127 |
+
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
|
128 |
+
}
|
129 |
}
|
130 |
|
131 |
$data = $this->prepare_item_for_response( $comment, $request );
|
145 |
return new WP_Error( 'rest_comment_exists', __( 'Cannot create existing comment.' ), array( 'status' => 400 ) );
|
146 |
}
|
147 |
|
|
|
|
|
|
|
|
|
|
|
148 |
$prepared_comment = $this->prepare_item_for_database( $request );
|
149 |
+
|
150 |
// Setting remaining values before wp_insert_comment so we can
|
151 |
// use wp_allow_comment().
|
152 |
+
if ( ! isset( $prepared_comment['comment_date_gmt'] ) ) {
|
153 |
+
$prepared_comment['comment_date_gmt'] = current_time( 'mysql', true );
|
154 |
+
}
|
155 |
+
|
156 |
+
// Set author data if the user's logged in
|
157 |
+
$missing_author = empty( $prepared_comment['user_id'] )
|
158 |
+
&& empty( $prepared_comment['comment_author'] )
|
159 |
+
&& empty( $prepared_comment['comment_author_email'] )
|
160 |
+
&& empty( $prepared_comment['comment_author_url'] );
|
161 |
+
|
162 |
+
if ( is_user_logged_in() && $missing_author ) {
|
163 |
+
$user = wp_get_current_user();
|
164 |
+
$prepared_comment['user_id'] = $user->ID;
|
165 |
+
$prepared_comment['comment_author'] = $user->display_name;
|
166 |
+
$prepared_comment['comment_author_email'] = $user->user_email;
|
167 |
+
$prepared_comment['comment_author_url'] = $user->user_url;
|
168 |
+
}
|
169 |
+
|
170 |
+
if ( ! isset( $prepared_comment['comment_author_email'] ) ) {
|
171 |
+
$prepared_comment['comment_author_email'] = '';
|
172 |
+
}
|
173 |
+
if ( ! isset( $prepared_comment['comment_author_url'] ) ) {
|
174 |
+
$prepared_comment['comment_author_url'] = '';
|
175 |
+
}
|
176 |
$prepared_comment['comment_author_IP'] = '127.0.0.1';
|
177 |
$prepared_comment['comment_agent'] = '';
|
178 |
$prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment );
|
179 |
|
180 |
+
/**
|
181 |
+
* Filter a comment before it is inserted via the REST API.
|
182 |
+
*
|
183 |
+
* Allows modification of the comment right before it is inserted via `wp_insert_comment`.
|
184 |
+
*
|
185 |
+
* @param array $prepared_comment The prepared comment data for `wp_insert_comment`.
|
186 |
+
* @param WP_REST_Request $request Request used to insert the comment.
|
187 |
+
*/
|
188 |
$prepared_comment = apply_filters( 'rest_pre_insert_comment', $prepared_comment, $request );
|
189 |
|
190 |
$comment_id = wp_insert_comment( $prepared_comment );
|
200 |
$this->update_additional_fields_for_object( get_comment( $comment_id ), $request );
|
201 |
|
202 |
$context = current_user_can( 'moderate_comments' ) ? 'edit' : 'view';
|
203 |
+
$get_request = new WP_REST_Request;
|
204 |
+
$get_request->set_param( 'id', $comment_id );
|
205 |
+
$get_request->set_param( 'context', $context );
|
206 |
+
$response = $this->get_item( $get_request );
|
207 |
$response = rest_ensure_response( $response );
|
208 |
if ( is_wp_error( $response ) ) {
|
209 |
return $response;
|
211 |
$response->set_status( 201 );
|
212 |
$response->header( 'Location', rest_url( '/wp/v2/comments/' . $comment_id ) );
|
213 |
|
214 |
+
/**
|
215 |
+
* Fires after a comment is created or updated via the REST API.
|
216 |
+
*
|
217 |
+
* @param array $prepared_comment Inserted comment data.
|
218 |
+
* @param WP_REST_Request $request The request sent to the API.
|
219 |
+
* @param bool $creating True when creating a comment, false when updating.
|
220 |
+
*/
|
221 |
+
do_action( 'rest_insert_comment', $prepared_comment, $request, true );
|
222 |
+
|
223 |
return $response;
|
224 |
}
|
225 |
|
234 |
|
235 |
$comment = get_comment( $id );
|
236 |
if ( empty( $comment ) ) {
|
237 |
+
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment id.' ), array( 'status' => 404 ) );
|
238 |
}
|
239 |
|
240 |
if ( isset( $request['type'] ) && $request['type'] !== $comment->comment_type ) {
|
264 |
|
265 |
$this->update_additional_fields_for_object( get_comment( $id ), $request );
|
266 |
|
267 |
+
$get_request = new WP_REST_Request;
|
268 |
+
$get_request->set_param( 'id', $id );
|
269 |
+
$get_request->set_param( 'context', 'edit' );
|
270 |
+
$response = $this->get_item( $get_request );
|
|
|
|
|
|
|
|
|
|
|
271 |
|
272 |
+
/* This action is documented in lib/endpoints/class-wp-rest-comments-controller.php */
|
273 |
+
do_action( 'rest_insert_comment', $prepared_args, $request, false );
|
274 |
+
|
275 |
+
return rest_ensure_response( $response );
|
276 |
}
|
277 |
|
278 |
/**
|
279 |
* Delete a comment.
|
280 |
*
|
281 |
* @param WP_REST_Request $request Full details about the request.
|
282 |
+
* @return WP_Error|WP_REST_Response
|
283 |
*/
|
284 |
public function delete_item( $request ) {
|
285 |
$id = (int) $request['id'];
|
287 |
|
288 |
$comment = get_comment( $id );
|
289 |
if ( empty( $comment ) ) {
|
290 |
+
return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment id.' ), array( 'status' => 404 ) );
|
291 |
}
|
292 |
|
293 |
/**
|
294 |
+
* Filter whether a comment is trashable.
|
295 |
+
*
|
296 |
+
* Return false to disable trash support for the post.
|
297 |
*
|
298 |
+
* @param boolean $supports_trash Whether the post type support trashing.
|
299 |
+
* @param WP_Post $comment The comment object being considered for trashing support.
|
300 |
*/
|
301 |
+
$supports_trash = apply_filters( 'rest_comment_trashable', ( EMPTY_TRASH_DAYS > 0 ), $comment );
|
302 |
|
303 |
+
$get_request = new WP_REST_Request;
|
304 |
+
$get_request->set_param( 'id', $id );
|
305 |
$get_request->set_param( 'context', 'edit' );
|
306 |
$response = $this->prepare_item_for_response( $comment, $get_request );
|
307 |
|
308 |
if ( $force ) {
|
309 |
$result = wp_delete_comment( $comment->comment_ID, true );
|
310 |
+
$status = 'deleted';
|
311 |
} else {
|
312 |
// If we don't support trashing for this type, error out
|
313 |
if ( ! $supports_trash ) {
|
315 |
}
|
316 |
|
317 |
$result = wp_trash_comment( $comment->comment_ID );
|
318 |
+
$status = 'trashed';
|
319 |
}
|
320 |
|
321 |
+
$data = $response->get_data();
|
322 |
+
$data = array(
|
323 |
+
'data' => $data,
|
324 |
+
$status => true,
|
325 |
+
);
|
326 |
+
$response->set_data( $data );
|
327 |
+
|
328 |
if ( ! $result ) {
|
329 |
return new WP_Error( 'rest_cannot_delete', __( 'The comment cannot be deleted.' ), array( 'status' => 500 ) );
|
330 |
}
|
331 |
|
332 |
+
/**
|
333 |
+
* Fires after a comment is deleted via the REST API.
|
334 |
+
*
|
335 |
+
* @param object $comment The deleted comment data.
|
336 |
+
* @param array $data Delete status data.
|
337 |
+
* @param WP_REST_Request $request The request sent to the API.
|
338 |
+
*/
|
339 |
+
do_action( 'rest_delete_comment', $comment, $data, $request );
|
340 |
+
|
341 |
return $response;
|
342 |
}
|
343 |
|
355 |
$post = get_post( (int) $request['post'] );
|
356 |
|
357 |
if ( $post && ! $this->check_read_post_permission( $post ) ) {
|
358 |
+
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) );
|
359 |
}
|
360 |
}
|
361 |
|
362 |
+
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
|
363 |
+
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view comments with edit context.' ), array( 'status' => rest_authorization_required_code() ) );
|
364 |
}
|
365 |
|
366 |
return true;
|
382 |
}
|
383 |
|
384 |
if ( ! $this->check_read_permission( $comment ) ) {
|
385 |
+
return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot read this comment.' ), array( 'status' => rest_authorization_required_code() ) );
|
386 |
}
|
387 |
|
388 |
$post = get_post( $comment->comment_post_ID );
|
389 |
|
390 |
if ( $post && ! $this->check_read_post_permission( $post ) ) {
|
391 |
+
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) );
|
392 |
}
|
393 |
|
394 |
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
|
395 |
+
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view this comment with edit context.' ), array( 'status' => rest_authorization_required_code() ) );
|
396 |
}
|
397 |
|
398 |
return true;
|
406 |
*/
|
407 |
public function create_item_permissions_check( $request ) {
|
408 |
|
409 |
+
if ( ! is_user_logged_in() && get_option( 'comment_registration' ) ) {
|
410 |
+
return new WP_Error( 'rest_comment_login_required', __( 'Sorry, you must be logged in to comment.' ), array( 'status' => 401 ) );
|
411 |
+
}
|
412 |
+
|
413 |
// Limit who can set comment `author`, `karma` or `status` to anything other than the default.
|
414 |
if ( isset( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( 'moderate_comments' ) ) {
|
415 |
+
return new WP_Error( 'rest_comment_invalid_author', __( 'Comment author invalid.' ), array( 'status' => rest_authorization_required_code() ) );
|
416 |
}
|
417 |
if ( isset( $request['karma'] ) && $request['karma'] > 0 && ! current_user_can( 'moderate_comments' ) ) {
|
418 |
+
return new WP_Error( 'rest_comment_invalid_karma', __( 'Sorry, you cannot set karma for comments.' ), array( 'status' => rest_authorization_required_code() ) );
|
419 |
}
|
420 |
if ( isset( $request['status'] ) && ! current_user_can( 'moderate_comments' ) ) {
|
421 |
+
return new WP_Error( 'rest_comment_invalid_status', __( 'Sorry, you cannot set status for comments.' ), array( 'status' => rest_authorization_required_code() ) );
|
422 |
}
|
423 |
|
424 |
+
if ( ! empty( $request['post'] ) && $post = get_post( (int) $request['post'] ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
425 |
|
426 |
if ( ! $this->check_read_post_permission( $post ) ) {
|
427 |
+
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) );
|
428 |
}
|
429 |
|
430 |
if ( ! comments_open( $post->ID ) ) {
|
448 |
$comment = get_comment( $id );
|
449 |
|
450 |
if ( $comment && ! $this->check_edit_permission( $comment ) ) {
|
451 |
+
return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you can not edit this comment.' ), array( 'status' => rest_authorization_required_code() ) );
|
452 |
}
|
453 |
|
454 |
return true;
|
469 |
*
|
470 |
* @param object $comment Comment object.
|
471 |
* @param WP_REST_Request $request Request object.
|
472 |
+
* @return WP_REST_Response
|
473 |
*/
|
474 |
public function prepare_item_for_response( $comment, $request ) {
|
475 |
$data = array(
|
483 |
'author_ip' => $comment->comment_author_IP,
|
484 |
'author_avatar_urls' => rest_get_avatar_urls( $comment->comment_author_email ),
|
485 |
'author_user_agent' => $comment->comment_agent,
|
486 |
+
'date' => mysql_to_rfc3339( $comment->comment_date ),
|
487 |
+
'date_gmt' => mysql_to_rfc3339( $comment->comment_date_gmt ),
|
488 |
'content' => array(
|
489 |
'rendered' => apply_filters( 'comment_text', $comment->comment_content, $comment ),
|
490 |
'raw' => $comment->comment_content,
|
500 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
501 |
|
502 |
// Wrap the data in a response object
|
503 |
+
$response = rest_ensure_response( $data );
|
504 |
|
505 |
+
$response->add_links( $this->prepare_links( $comment ) );
|
506 |
|
507 |
+
/**
|
508 |
+
* Filter a comment returned from the API.
|
509 |
+
*
|
510 |
+
* Allows modification of the comment right before it is returned.
|
511 |
+
*
|
512 |
+
* @param WP_REST_Response $response The response object.
|
513 |
+
* @param object $comment The original comment object.
|
514 |
+
* @param WP_REST_Request $request Request used to generate the response.
|
515 |
+
*/
|
516 |
+
return apply_filters( 'rest_prepare_comment', $response, $comment, $request );
|
517 |
}
|
518 |
|
519 |
/**
|
572 |
* @return array $prepared_args
|
573 |
*/
|
574 |
protected function prepare_items_query( $request ) {
|
|
|
575 |
|
576 |
$prepared_args = array(
|
577 |
+
'comment__in' => $request['include'],
|
578 |
+
'number' => $request['per_page'],
|
579 |
+
'post_id' => $request['post'] ? $request['post'] : '',
|
580 |
+
'parent' => isset( $request['parent'] ) ? $request['parent'] : '',
|
581 |
+
'search' => $request['search'],
|
582 |
+
'orderby' => $this->normalize_query_param( $request['orderby'] ),
|
583 |
+
'order' => $request['order'],
|
584 |
+
'status' => 'approve',
|
585 |
+
'type' => 'comment',
|
586 |
);
|
587 |
|
588 |
$prepared_args['offset'] = $prepared_args['number'] * ( absint( $request['page'] ) - 1 );
|
626 |
case 'parent':
|
627 |
$normalized = $prefix . 'parent';
|
628 |
break;
|
629 |
+
case 'include':
|
630 |
+
$normalized = 'comment__in';
|
631 |
+
break;
|
632 |
default:
|
633 |
$normalized = $prefix . $query_param;
|
634 |
break;
|
715 |
$date_data = rest_get_date_with_gmt( $request['date'] );
|
716 |
|
717 |
if ( ! empty( $date_data ) ) {
|
718 |
+
list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data;
|
|
|
|
|
|
|
719 |
}
|
720 |
} elseif ( ! empty( $request['date_gmt'] ) ) {
|
721 |
$date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
|
722 |
|
723 |
if ( ! empty( $date_data ) ) {
|
724 |
list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data;
|
|
|
|
|
725 |
}
|
726 |
}
|
727 |
|
739 |
$avatar_sizes = rest_get_avatar_sizes();
|
740 |
foreach ( $avatar_sizes as $size ) {
|
741 |
$avatar_properties[ $size ] = array(
|
742 |
+
'description' => sprintf( __( 'Avatar URL with image size of %d pixels.' ), $size ),
|
743 |
+
'type' => 'string',
|
744 |
+
'format' => 'uri',
|
745 |
'context' => array( 'embed', 'view', 'edit' ),
|
746 |
);
|
747 |
}
|
752 |
'type' => 'object',
|
753 |
'properties' => array(
|
754 |
'id' => array(
|
755 |
+
'description' => __( 'Unique identifier for the object.' ),
|
756 |
'type' => 'integer',
|
757 |
'context' => array( 'view', 'edit', 'embed' ),
|
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 |
+
),
|
765 |
'author_avatar_urls' => array(
|
766 |
+
'description' => __( 'Avatar URLs for the object author.' ),
|
767 |
'type' => 'object',
|
768 |
'context' => array( 'view', 'edit', 'embed' ),
|
769 |
'readonly' => true,
|
770 |
'properties' => $avatar_properties,
|
771 |
+
),
|
772 |
'author_email' => array(
|
773 |
+
'description' => __( 'Email address for the object author.' ),
|
774 |
'type' => 'string',
|
775 |
'format' => 'email',
|
776 |
'context' => array( 'edit' ),
|
777 |
+
),
|
778 |
'author_ip' => array(
|
779 |
+
'description' => __( 'IP address for the object author.' ),
|
780 |
'type' => 'string',
|
781 |
'context' => array( 'edit' ),
|
782 |
'readonly' => true,
|
783 |
+
),
|
784 |
'author_name' => array(
|
785 |
+
'description' => __( 'Display name for the object author.' ),
|
786 |
'type' => 'string',
|
787 |
'context' => array( 'view', 'edit', 'embed' ),
|
788 |
+
'arg_options' => array(
|
789 |
+
'sanitize_callback' => 'sanitize_text_field',
|
790 |
+
'default' => '',
|
791 |
),
|
792 |
+
),
|
793 |
'author_url' => array(
|
794 |
+
'description' => __( 'URL for the object author.' ),
|
795 |
'type' => 'string',
|
796 |
'format' => 'uri',
|
797 |
'context' => array( 'view', 'edit', 'embed' ),
|
798 |
+
),
|
799 |
'author_user_agent' => array(
|
800 |
+
'description' => __( 'User agent for the object author.' ),
|
801 |
'type' => 'string',
|
802 |
'context' => array( 'edit' ),
|
803 |
'readonly' => true,
|
804 |
+
),
|
805 |
'content' => array(
|
806 |
+
'description' => __( 'The content for the object.' ),
|
807 |
'type' => 'object',
|
808 |
'context' => array( 'view', 'edit', 'embed' ),
|
809 |
'properties' => array(
|
810 |
'raw' => array(
|
811 |
+
'description' => __( 'Content for the object, as it exists in the database.' ),
|
812 |
'type' => 'string',
|
813 |
'context' => array( 'edit' ),
|
814 |
+
),
|
815 |
'rendered' => array(
|
816 |
+
'description' => __( 'Content for the object, transformed for display.' ),
|
817 |
'type' => 'string',
|
818 |
'context' => array( 'view', 'edit', 'embed' ),
|
|
|
819 |
),
|
820 |
),
|
821 |
+
'arg_options' => array(
|
822 |
+
'sanitize_callback' => 'wp_filter_post_kses',
|
823 |
+
'default' => '',
|
824 |
+
),
|
825 |
+
),
|
826 |
'date' => array(
|
827 |
+
'description' => __( 'The date the object was published.' ),
|
828 |
'type' => 'string',
|
829 |
'format' => 'date-time',
|
830 |
'context' => array( 'view', 'edit', 'embed' ),
|
831 |
),
|
832 |
'date_gmt' => array(
|
833 |
+
'description' => __( 'The date the object was published as GMT.' ),
|
834 |
'type' => 'string',
|
835 |
'format' => 'date-time',
|
836 |
+
'context' => array( 'view', 'edit' ),
|
837 |
),
|
838 |
'karma' => array(
|
839 |
+
'description' => __( 'Karma for the object.' ),
|
840 |
'type' => 'integer',
|
841 |
'context' => array( 'edit' ),
|
|
|
842 |
),
|
843 |
'link' => array(
|
844 |
+
'description' => __( 'URL to the object.' ),
|
845 |
'type' => 'string',
|
846 |
'format' => 'uri',
|
847 |
'context' => array( 'view', 'edit', 'embed' ),
|
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(
|
855 |
+
'default' => 0,
|
856 |
+
),
|
857 |
),
|
858 |
'post' => array(
|
859 |
+
'description' => __( 'The id of the associated post object.' ),
|
860 |
'type' => 'integer',
|
861 |
'context' => array( 'view', 'edit' ),
|
862 |
+
'arg_options' => array(
|
863 |
+
'default' => 0,
|
864 |
+
),
|
865 |
),
|
866 |
'status' => array(
|
867 |
+
'description' => __( 'State of the object.' ),
|
868 |
'type' => 'string',
|
869 |
'context' => array( 'view', 'edit' ),
|
870 |
+
'arg_options' => array(
|
871 |
+
'sanitize_callback' => 'sanitize_key',
|
872 |
+
),
|
873 |
),
|
874 |
'type' => array(
|
875 |
+
'description' => __( 'Type of Comment for the object.' ),
|
876 |
'type' => 'string',
|
877 |
'context' => array( 'view', 'edit', 'embed' ),
|
878 |
+
'arg_options' => array(
|
879 |
+
'sanitize_callback' => 'sanitize_key',
|
880 |
+
'default' => '',
|
881 |
+
),
|
882 |
),
|
883 |
),
|
884 |
);
|
892 |
*/
|
893 |
public function get_collection_params() {
|
894 |
$query_params = parent::get_collection_params();
|
895 |
+
|
896 |
+
$query_params['context']['default'] = 'view';
|
897 |
+
|
898 |
$query_params['author_email'] = array(
|
899 |
'default' => null,
|
900 |
+
'description' => __( 'Limit result set to that from a specific author email.' ),
|
901 |
'format' => 'email',
|
902 |
'sanitize_callback' => 'sanitize_email',
|
903 |
'type' => 'string',
|
904 |
);
|
905 |
+
$query_params['include'] = array(
|
906 |
+
'description' => __( 'Limit result set to specific ids.' ),
|
907 |
+
'type' => 'array',
|
908 |
+
'default' => array(),
|
909 |
+
'sanitize_callback' => 'wp_parse_id_list',
|
910 |
+
);
|
911 |
$query_params['karma'] = array(
|
912 |
'default' => null,
|
913 |
+
'description' => __( 'Limit result set to that of a particular comment karma.' ),
|
914 |
'sanitize_callback' => 'absint',
|
915 |
'type' => 'integer',
|
916 |
);
|
917 |
+
$query_params['order'] = array(
|
918 |
+
'description' => __( 'Order sort attribute ascending or descending.' ),
|
919 |
+
'type' => 'string',
|
920 |
+
'sanitize_callback' => 'sanitize_key',
|
921 |
+
'default' => 'asc',
|
922 |
+
'enum' => array(
|
923 |
+
'asc',
|
924 |
+
'desc',
|
925 |
+
),
|
926 |
+
);
|
927 |
+
$query_params['orderby'] = array(
|
928 |
+
'description' => __( 'Sort collection by object attribute.' ),
|
929 |
+
'type' => 'string',
|
930 |
+
'sanitize_callback' => 'sanitize_key',
|
931 |
+
'default' => 'date_gmt',
|
932 |
+
);
|
933 |
$query_params['parent'] = array(
|
934 |
'default' => null,
|
935 |
+
'description' => __( 'Limit result set to that of a specific comment parent id.' ),
|
936 |
'sanitize_callback' => 'absint',
|
937 |
'type' => 'integer',
|
938 |
);
|
939 |
$query_params['post'] = array(
|
940 |
'default' => null,
|
941 |
+
'description' => __( 'Limit result set to comments assigned to a specific post id.' ),
|
942 |
'sanitize_callback' => 'absint',
|
943 |
'type' => 'integer',
|
944 |
);
|
945 |
$query_params['post_author'] = array(
|
946 |
'default' => null,
|
947 |
+
'description' => __( 'Limit result set to comments associated with posts of a specific post author id.' ),
|
948 |
'sanitize_callback' => 'absint',
|
949 |
'type' => 'integer',
|
950 |
);
|
951 |
$query_params['post_slug'] = array(
|
952 |
'default' => null,
|
953 |
+
'description' => __( 'Limit result set to comments associated with posts of a specific post slug.' ),
|
954 |
'sanitize_callback' => 'sanitize_title',
|
955 |
'type' => 'string',
|
956 |
);
|
957 |
$query_params['post_parent'] = array(
|
958 |
'default' => null,
|
959 |
+
'description' => __( 'Limit result set to comments associated with posts of a specific post parent id.' ),
|
960 |
'sanitize_callback' => 'absint',
|
961 |
'type' => 'integer',
|
962 |
);
|
963 |
$query_params['post_status'] = array(
|
964 |
'default' => null,
|
965 |
+
'description' => __( 'Limit result set to comments associated with posts of a specific post status.' ),
|
966 |
'sanitize_callback' => 'sanitize_key',
|
967 |
'type' => 'string',
|
968 |
);
|
969 |
$query_params['post_type'] = array(
|
970 |
'default' => null,
|
971 |
+
'description' => __( 'Limit result set to comments associated with posts of a specific post type.' ),
|
972 |
'sanitize_callback' => 'sanitize_key',
|
973 |
'type' => 'string',
|
974 |
);
|
975 |
$query_params['status'] = array(
|
976 |
'default' => 'approve',
|
977 |
+
'description' => __( 'Limit result set to comments assigned a specific status.' ),
|
978 |
'sanitize_callback' => 'sanitize_key',
|
979 |
'type' => 'string',
|
980 |
);
|
981 |
$query_params['type'] = array(
|
982 |
'default' => 'comment',
|
983 |
+
'description' => __( 'Limit result set to comments assigned a specific type.' ),
|
984 |
'sanitize_callback' => 'sanitize_key',
|
985 |
'type' => 'string',
|
986 |
);
|
987 |
$query_params['user'] = array(
|
988 |
'default' => null,
|
989 |
+
'description' => __( 'Limit result set to comments assigned to a specific user id.' ),
|
990 |
'sanitize_callback' => 'absint',
|
991 |
'type' => 'integer',
|
992 |
);
|
lib/endpoints/class-wp-rest-controller.php
CHANGED
@@ -11,124 +11,124 @@ abstract class WP_REST_Controller {
|
|
11 |
}
|
12 |
|
13 |
/**
|
14 |
-
* Get a collection of items
|
15 |
*
|
16 |
* @param WP_REST_Request $request Full data about the request.
|
17 |
* @return WP_Error|WP_REST_Response
|
18 |
*/
|
19 |
public function get_items( $request ) {
|
20 |
-
return new WP_Error( 'invalid-method', __(
|
21 |
}
|
22 |
|
23 |
/**
|
24 |
-
* Get one item from the collection
|
25 |
*
|
26 |
* @param WP_REST_Request $request Full data about the request.
|
27 |
* @return WP_Error|WP_REST_Response
|
28 |
*/
|
29 |
public function get_item( $request ) {
|
30 |
-
return new WP_Error( 'invalid-method', __(
|
31 |
}
|
32 |
|
33 |
/**
|
34 |
-
* Create one item from the collection
|
35 |
*
|
36 |
* @param WP_REST_Request $request Full data about the request.
|
37 |
-
* @return WP_Error|
|
38 |
*/
|
39 |
public function create_item( $request ) {
|
40 |
-
return new WP_Error( 'invalid-method', __(
|
41 |
}
|
42 |
|
43 |
/**
|
44 |
-
* Update one item from the collection
|
45 |
*
|
46 |
* @param WP_REST_Request $request Full data about the request.
|
47 |
-
* @return WP_Error|
|
48 |
*/
|
49 |
public function update_item( $request ) {
|
50 |
-
return new WP_Error( 'invalid-method', __(
|
51 |
}
|
52 |
|
53 |
/**
|
54 |
-
* Delete one item from the collection
|
55 |
*
|
56 |
* @param WP_REST_Request $request Full data about the request.
|
57 |
-
* @return WP_Error|
|
58 |
*/
|
59 |
public function delete_item( $request ) {
|
60 |
-
return new WP_Error( 'invalid-method', __(
|
61 |
}
|
62 |
|
63 |
/**
|
64 |
-
* Check if a given request has access to get items
|
65 |
*
|
66 |
* @param WP_REST_Request $request Full data about the request.
|
67 |
* @return WP_Error|bool
|
68 |
*/
|
69 |
public function get_items_permissions_check( $request ) {
|
70 |
-
return new WP_Error( 'invalid-method', __(
|
71 |
}
|
72 |
|
73 |
/**
|
74 |
-
* Check if a given request has access to get a specific item
|
75 |
*
|
76 |
* @param WP_REST_Request $request Full data about the request.
|
77 |
* @return WP_Error|bool
|
78 |
*/
|
79 |
public function get_item_permissions_check( $request ) {
|
80 |
-
return new WP_Error( 'invalid-method', __(
|
81 |
}
|
82 |
|
83 |
/**
|
84 |
-
* Check if a given request has access to create items
|
85 |
*
|
86 |
* @param WP_REST_Request $request Full data about the request.
|
87 |
* @return WP_Error|bool
|
88 |
*/
|
89 |
public function create_item_permissions_check( $request ) {
|
90 |
-
return new WP_Error( 'invalid-method', __(
|
91 |
}
|
92 |
|
93 |
/**
|
94 |
-
* Check if a given request has access to update a specific item
|
95 |
*
|
96 |
* @param WP_REST_Request $request Full data about the request.
|
97 |
* @return WP_Error|bool
|
98 |
*/
|
99 |
public function update_item_permissions_check( $request ) {
|
100 |
-
return new WP_Error( 'invalid-method', __(
|
101 |
}
|
102 |
|
103 |
/**
|
104 |
-
* Check if a given request has access to delete a specific item
|
105 |
*
|
106 |
* @param WP_REST_Request $request Full data about the request.
|
107 |
* @return WP_Error|bool
|
108 |
*/
|
109 |
public function delete_item_permissions_check( $request ) {
|
110 |
-
return new WP_Error( 'invalid-method', __(
|
111 |
}
|
112 |
|
113 |
/**
|
114 |
-
* Prepare the item for create or update operation
|
115 |
*
|
116 |
-
* @param WP_REST_Request $request Request object
|
117 |
* @return WP_Error|object $prepared_item
|
118 |
*/
|
119 |
protected function prepare_item_for_database( $request ) {
|
120 |
-
return new WP_Error( 'invalid-method', __(
|
121 |
}
|
122 |
|
123 |
/**
|
124 |
-
* Prepare the item for the REST response
|
125 |
*
|
126 |
* @param mixed $item WordPress representation of the item.
|
127 |
* @param WP_REST_Request $request Request object.
|
128 |
* @return mixed
|
129 |
*/
|
130 |
public function prepare_item_for_response( $item, $request ) {
|
131 |
-
return new WP_Error( 'invalid-method', __(
|
132 |
}
|
133 |
|
134 |
/**
|
@@ -152,7 +152,7 @@ abstract class WP_REST_Controller {
|
|
152 |
}
|
153 |
|
154 |
/**
|
155 |
-
* Filter a response based on the context defined in the schema
|
156 |
*
|
157 |
* @param array $data
|
158 |
* @param string $context
|
@@ -186,7 +186,7 @@ abstract class WP_REST_Controller {
|
|
186 |
}
|
187 |
|
188 |
/**
|
189 |
-
* Get the item's schema, conforming to JSON Schema
|
190 |
*
|
191 |
* @return array
|
192 |
*/
|
@@ -213,26 +213,27 @@ abstract class WP_REST_Controller {
|
|
213 |
}
|
214 |
|
215 |
/**
|
216 |
-
* Get the query params for collections
|
217 |
*
|
218 |
* @return array
|
219 |
*/
|
220 |
public function get_collection_params() {
|
221 |
return array(
|
|
|
222 |
'page' => array(
|
223 |
-
'description' => 'Current page of the collection.',
|
224 |
'type' => 'integer',
|
225 |
'default' => 1,
|
226 |
'sanitize_callback' => 'absint',
|
227 |
),
|
228 |
'per_page' => array(
|
229 |
-
'description' => 'Maximum number of items to be returned in result set.',
|
230 |
'type' => 'integer',
|
231 |
'default' => 10,
|
232 |
'sanitize_callback' => 'absint',
|
233 |
),
|
234 |
'search' => array(
|
235 |
-
'description' => 'Limit results to those matching a string.',
|
236 |
'type' => 'string',
|
237 |
'sanitize_callback' => 'sanitize_text_field',
|
238 |
),
|
@@ -240,11 +241,42 @@ abstract class WP_REST_Controller {
|
|
240 |
}
|
241 |
|
242 |
/**
|
243 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
244 |
*
|
245 |
* @param array $object
|
246 |
* @param WP_REST_Request $request
|
247 |
-
* @return array modified object with additional fields
|
248 |
*/
|
249 |
protected function add_additional_fields_to_object( $object, $request ) {
|
250 |
|
@@ -256,7 +288,7 @@ abstract class WP_REST_Controller {
|
|
256 |
continue;
|
257 |
}
|
258 |
|
259 |
-
$object[ $field_name ] = call_user_func( $field_options['get_callback'], $object, $field_name, $request );
|
260 |
}
|
261 |
|
262 |
return $object;
|
@@ -278,21 +310,21 @@ abstract class WP_REST_Controller {
|
|
278 |
continue;
|
279 |
}
|
280 |
|
281 |
-
// Don't run the update callbacks if the data wasn't passed in the request
|
282 |
if ( ! isset( $request[ $field_name ] ) ) {
|
283 |
continue;
|
284 |
}
|
285 |
|
286 |
-
$result = call_user_func( $field_options['update_callback'], $request[ $field_name ], $object, $field_name, $request );
|
287 |
}
|
288 |
}
|
289 |
|
290 |
/**
|
291 |
-
* Add the schema from additional fields to an schema array
|
292 |
*
|
293 |
* The type of object is inferred from the passed schema.
|
294 |
*
|
295 |
-
* @param array $schema Schema array
|
296 |
*/
|
297 |
protected function add_additional_fields_schema( $schema ) {
|
298 |
if ( ! $schema || ! isset( $schema['title'] ) ) {
|
@@ -300,7 +332,7 @@ abstract class WP_REST_Controller {
|
|
300 |
}
|
301 |
|
302 |
/**
|
303 |
-
* Can't use $this->get_object_type otherwise we cause an inf loop
|
304 |
*/
|
305 |
$object_type = $schema['title'];
|
306 |
|
@@ -318,7 +350,7 @@ abstract class WP_REST_Controller {
|
|
318 |
}
|
319 |
|
320 |
/**
|
321 |
-
* Get all the registered additional fields for a given object-type
|
322 |
*
|
323 |
* @param string $object_type
|
324 |
* @return array
|
@@ -360,12 +392,14 @@ abstract class WP_REST_Controller {
|
|
360 |
/**
|
361 |
* Get an array of endpoint arguments from the item schema for the controller.
|
362 |
*
|
363 |
-
* @param $
|
364 |
-
*
|
365 |
-
*
|
366 |
-
*
|
|
|
|
|
367 |
*/
|
368 |
-
public function get_endpoint_args_for_item_schema( $
|
369 |
|
370 |
$schema = $this->get_item_schema();
|
371 |
$schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array();
|
@@ -373,7 +407,7 @@ abstract class WP_REST_Controller {
|
|
373 |
|
374 |
foreach ( $schema_properties as $field_id => $params ) {
|
375 |
|
376 |
-
//
|
377 |
if ( ! empty( $params['readonly'] ) ) {
|
378 |
continue;
|
379 |
}
|
@@ -383,16 +417,22 @@ abstract class WP_REST_Controller {
|
|
383 |
'sanitize_callback' => array( $this, 'sanitize_schema_property' ),
|
384 |
);
|
385 |
|
386 |
-
if ( isset( $params['default'] ) ) {
|
387 |
$endpoint_args[ $field_id ]['default'] = $params['default'];
|
388 |
}
|
389 |
|
390 |
-
if ( $
|
391 |
$endpoint_args[ $field_id ]['required'] = true;
|
392 |
}
|
393 |
|
394 |
-
// Merge in any options provided by the schema property
|
395 |
if ( isset( $params['arg_options'] ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
396 |
$endpoint_args[ $field_id ] = array_merge( $endpoint_args[ $field_id ], $params['arg_options'] );
|
397 |
}
|
398 |
}
|
@@ -401,7 +441,7 @@ abstract class WP_REST_Controller {
|
|
401 |
}
|
402 |
|
403 |
/**
|
404 |
-
* Validate
|
405 |
*
|
406 |
* @param mixed $value
|
407 |
* @param WP_REST_Request $request
|
@@ -412,7 +452,7 @@ abstract class WP_REST_Controller {
|
|
412 |
|
413 |
/**
|
414 |
* We don't currently validate against empty values, as lots of checks
|
415 |
-
* can
|
416 |
* value it's self.
|
417 |
*/
|
418 |
if ( ! $value ) {
|
@@ -437,7 +477,7 @@ abstract class WP_REST_Controller {
|
|
437 |
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $parameter, 'integer' ) );
|
438 |
}
|
439 |
|
440 |
-
if ( 'string' === $property['type']&& ! is_string( $value ) ) {
|
441 |
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $parameter, 'string' ) );
|
442 |
}
|
443 |
|
@@ -461,7 +501,7 @@ abstract class WP_REST_Controller {
|
|
461 |
}
|
462 |
|
463 |
/**
|
464 |
-
* Sanitize
|
465 |
*
|
466 |
* @param mixed $value
|
467 |
* @param WP_REST_Request $request
|
@@ -479,7 +519,7 @@ abstract class WP_REST_Controller {
|
|
479 |
$property = $schema['properties'][ $parameter ];
|
480 |
|
481 |
if ( 'integer' === $property['type'] ) {
|
482 |
-
return
|
483 |
}
|
484 |
|
485 |
if ( isset( $property['format'] ) ) {
|
@@ -489,7 +529,7 @@ abstract class WP_REST_Controller {
|
|
489 |
|
490 |
case 'email' :
|
491 |
// as sanitize_email is very lossy, we just want to
|
492 |
-
// make sure the string is safe
|
493 |
if ( sanitize_email( $value ) ) {
|
494 |
return sanitize_email( $value );
|
495 |
}
|
11 |
}
|
12 |
|
13 |
/**
|
14 |
+
* Get a collection of items.
|
15 |
*
|
16 |
* @param WP_REST_Request $request Full data about the request.
|
17 |
* @return WP_Error|WP_REST_Response
|
18 |
*/
|
19 |
public function get_items( $request ) {
|
20 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
21 |
}
|
22 |
|
23 |
/**
|
24 |
+
* Get one item from the collection.
|
25 |
*
|
26 |
* @param WP_REST_Request $request Full data about the request.
|
27 |
* @return WP_Error|WP_REST_Response
|
28 |
*/
|
29 |
public function get_item( $request ) {
|
30 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
31 |
}
|
32 |
|
33 |
/**
|
34 |
+
* Create one item from the collection.
|
35 |
*
|
36 |
* @param WP_REST_Request $request Full data about the request.
|
37 |
+
* @return WP_Error|WP_REST_Response
|
38 |
*/
|
39 |
public function create_item( $request ) {
|
40 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
41 |
}
|
42 |
|
43 |
/**
|
44 |
+
* Update one item from the collection.
|
45 |
*
|
46 |
* @param WP_REST_Request $request Full data about the request.
|
47 |
+
* @return WP_Error|WP_REST_Response
|
48 |
*/
|
49 |
public function update_item( $request ) {
|
50 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
51 |
}
|
52 |
|
53 |
/**
|
54 |
+
* Delete one item from the collection.
|
55 |
*
|
56 |
* @param WP_REST_Request $request Full data about the request.
|
57 |
+
* @return WP_Error|WP_REST_Response
|
58 |
*/
|
59 |
public function delete_item( $request ) {
|
60 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
61 |
}
|
62 |
|
63 |
/**
|
64 |
+
* Check if a given request has access to get items.
|
65 |
*
|
66 |
* @param WP_REST_Request $request Full data about the request.
|
67 |
* @return WP_Error|bool
|
68 |
*/
|
69 |
public function get_items_permissions_check( $request ) {
|
70 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
71 |
}
|
72 |
|
73 |
/**
|
74 |
+
* Check if a given request has access to get a specific item.
|
75 |
*
|
76 |
* @param WP_REST_Request $request Full data about the request.
|
77 |
* @return WP_Error|bool
|
78 |
*/
|
79 |
public function get_item_permissions_check( $request ) {
|
80 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
81 |
}
|
82 |
|
83 |
/**
|
84 |
+
* Check if a given request has access to create items.
|
85 |
*
|
86 |
* @param WP_REST_Request $request Full data about the request.
|
87 |
* @return WP_Error|bool
|
88 |
*/
|
89 |
public function create_item_permissions_check( $request ) {
|
90 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
91 |
}
|
92 |
|
93 |
/**
|
94 |
+
* Check if a given request has access to update a specific item.
|
95 |
*
|
96 |
* @param WP_REST_Request $request Full data about the request.
|
97 |
* @return WP_Error|bool
|
98 |
*/
|
99 |
public function update_item_permissions_check( $request ) {
|
100 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
101 |
}
|
102 |
|
103 |
/**
|
104 |
+
* Check if a given request has access to delete a specific item.
|
105 |
*
|
106 |
* @param WP_REST_Request $request Full data about the request.
|
107 |
* @return WP_Error|bool
|
108 |
*/
|
109 |
public function delete_item_permissions_check( $request ) {
|
110 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
111 |
}
|
112 |
|
113 |
/**
|
114 |
+
* Prepare the item for create or update operation.
|
115 |
*
|
116 |
+
* @param WP_REST_Request $request Request object.
|
117 |
* @return WP_Error|object $prepared_item
|
118 |
*/
|
119 |
protected function prepare_item_for_database( $request ) {
|
120 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
121 |
}
|
122 |
|
123 |
/**
|
124 |
+
* Prepare the item for the REST response.
|
125 |
*
|
126 |
* @param mixed $item WordPress representation of the item.
|
127 |
* @param WP_REST_Request $request Request object.
|
128 |
* @return mixed
|
129 |
*/
|
130 |
public function prepare_item_for_response( $item, $request ) {
|
131 |
+
return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
|
132 |
}
|
133 |
|
134 |
/**
|
152 |
}
|
153 |
|
154 |
/**
|
155 |
+
* Filter a response based on the context defined in the schema.
|
156 |
*
|
157 |
* @param array $data
|
158 |
* @param string $context
|
186 |
}
|
187 |
|
188 |
/**
|
189 |
+
* Get the item's schema, conforming to JSON Schema.
|
190 |
*
|
191 |
* @return array
|
192 |
*/
|
213 |
}
|
214 |
|
215 |
/**
|
216 |
+
* Get the query params for collections.
|
217 |
*
|
218 |
* @return array
|
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',
|
226 |
'default' => 1,
|
227 |
'sanitize_callback' => 'absint',
|
228 |
),
|
229 |
'per_page' => array(
|
230 |
+
'description' => __( 'Maximum number of items to be returned in result set.' ),
|
231 |
'type' => 'integer',
|
232 |
'default' => 10,
|
233 |
'sanitize_callback' => 'absint',
|
234 |
),
|
235 |
'search' => array(
|
236 |
+
'description' => __( 'Limit results to those matching a string.' ),
|
237 |
'type' => 'string',
|
238 |
'sanitize_callback' => 'sanitize_text_field',
|
239 |
),
|
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 |
*
|
277 |
* @param array $object
|
278 |
* @param WP_REST_Request $request
|
279 |
+
* @return array modified object with additional fields.
|
280 |
*/
|
281 |
protected function add_additional_fields_to_object( $object, $request ) {
|
282 |
|
288 |
continue;
|
289 |
}
|
290 |
|
291 |
+
$object[ $field_name ] = call_user_func( $field_options['get_callback'], $object, $field_name, $request, $this->get_object_type() );
|
292 |
}
|
293 |
|
294 |
return $object;
|
310 |
continue;
|
311 |
}
|
312 |
|
313 |
+
// Don't run the update callbacks if the data wasn't passed in the request.
|
314 |
if ( ! isset( $request[ $field_name ] ) ) {
|
315 |
continue;
|
316 |
}
|
317 |
|
318 |
+
$result = call_user_func( $field_options['update_callback'], $request[ $field_name ], $object, $field_name, $request, $this->get_object_type() );
|
319 |
}
|
320 |
}
|
321 |
|
322 |
/**
|
323 |
+
* Add the schema from additional fields to an schema array.
|
324 |
*
|
325 |
* The type of object is inferred from the passed schema.
|
326 |
*
|
327 |
+
* @param array $schema Schema array.
|
328 |
*/
|
329 |
protected function add_additional_fields_schema( $schema ) {
|
330 |
if ( ! $schema || ! isset( $schema['title'] ) ) {
|
332 |
}
|
333 |
|
334 |
/**
|
335 |
+
* Can't use $this->get_object_type otherwise we cause an inf loop.
|
336 |
*/
|
337 |
$object_type = $schema['title'];
|
338 |
|
350 |
}
|
351 |
|
352 |
/**
|
353 |
+
* Get all the registered additional fields for a given object-type.
|
354 |
*
|
355 |
* @param string $object_type
|
356 |
* @return array
|
392 |
/**
|
393 |
* Get an array of endpoint arguments from the item schema for the controller.
|
394 |
*
|
395 |
+
* @param string $method HTTP method of the request. The arguments
|
396 |
+
* for `CREATABLE` requests are checked for required
|
397 |
+
* values and may fall-back to a given default, this
|
398 |
+
* is not done on `EDITABLE` requests. Default is
|
399 |
+
* WP_REST_Server::CREATABLE.
|
400 |
+
* @return array $endpoint_args
|
401 |
*/
|
402 |
+
public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) {
|
403 |
|
404 |
$schema = $this->get_item_schema();
|
405 |
$schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array();
|
407 |
|
408 |
foreach ( $schema_properties as $field_id => $params ) {
|
409 |
|
410 |
+
// Arguments specified as `readonly` are not allowed to be set.
|
411 |
if ( ! empty( $params['readonly'] ) ) {
|
412 |
continue;
|
413 |
}
|
417 |
'sanitize_callback' => array( $this, 'sanitize_schema_property' ),
|
418 |
);
|
419 |
|
420 |
+
if ( WP_REST_Server::CREATABLE === $method && isset( $params['default'] ) ) {
|
421 |
$endpoint_args[ $field_id ]['default'] = $params['default'];
|
422 |
}
|
423 |
|
424 |
+
if ( WP_REST_Server::CREATABLE === $method && ! empty( $params['required'] ) ) {
|
425 |
$endpoint_args[ $field_id ]['required'] = true;
|
426 |
}
|
427 |
|
428 |
+
// Merge in any options provided by the schema property.
|
429 |
if ( isset( $params['arg_options'] ) ) {
|
430 |
+
|
431 |
+
// Only use required / default from arg_options on CREATABLE endpoints.
|
432 |
+
if ( WP_REST_Server::CREATABLE !== $method ) {
|
433 |
+
$params['arg_options'] = array_diff_key( $params['arg_options'], array( 'required' => '', 'default' => '' ) );
|
434 |
+
}
|
435 |
+
|
436 |
$endpoint_args[ $field_id ] = array_merge( $endpoint_args[ $field_id ], $params['arg_options'] );
|
437 |
}
|
438 |
}
|
441 |
}
|
442 |
|
443 |
/**
|
444 |
+
* Validate a parameter value that's based on a property from the item schema.
|
445 |
*
|
446 |
* @param mixed $value
|
447 |
* @param WP_REST_Request $request
|
452 |
|
453 |
/**
|
454 |
* We don't currently validate against empty values, as lots of checks
|
455 |
+
* can unintentionally fail, as the callback will often handle an empty
|
456 |
* value it's self.
|
457 |
*/
|
458 |
if ( ! $value ) {
|
477 |
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $parameter, 'integer' ) );
|
478 |
}
|
479 |
|
480 |
+
if ( 'string' === $property['type'] && ! is_string( $value ) ) {
|
481 |
return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $parameter, 'string' ) );
|
482 |
}
|
483 |
|
501 |
}
|
502 |
|
503 |
/**
|
504 |
+
* Sanitize a parameter value that's based on a property from the item schema.
|
505 |
*
|
506 |
* @param mixed $value
|
507 |
* @param WP_REST_Request $request
|
519 |
$property = $schema['properties'][ $parameter ];
|
520 |
|
521 |
if ( 'integer' === $property['type'] ) {
|
522 |
+
return (int) $value;
|
523 |
}
|
524 |
|
525 |
if ( isset( $property['format'] ) ) {
|
529 |
|
530 |
case 'email' :
|
531 |
// as sanitize_email is very lossy, we just want to
|
532 |
+
// make sure the string is safe.
|
533 |
if ( sanitize_email( $value ) ) {
|
534 |
return sanitize_email( $value );
|
535 |
}
|
lib/endpoints/class-wp-rest-meta-controller.php
CHANGED
@@ -40,23 +40,16 @@ 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' =>
|
44 |
-
'context' => array(
|
45 |
-
'default' => 'view',
|
46 |
-
),
|
47 |
-
),
|
48 |
),
|
49 |
array(
|
50 |
'methods' => WP_REST_Server::CREATABLE,
|
51 |
'callback' => array( $this, 'create_item' ),
|
52 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
53 |
-
'args' =>
|
54 |
-
'key' => array(
|
55 |
-
'required' => true,
|
56 |
-
),
|
57 |
-
'value' => array(),
|
58 |
-
),
|
59 |
),
|
|
|
|
|
60 |
) );
|
61 |
register_rest_route( 'wp/v2', '/' . $this->parent_base . '/(?P<parent_id>[\d]+)/meta/(?P<id>[\d]+)', array(
|
62 |
array(
|
@@ -64,30 +57,27 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
64 |
'callback' => array( $this, 'get_item' ),
|
65 |
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
66 |
'args' => array(
|
67 |
-
'context'
|
68 |
-
'default' => 'view',
|
69 |
-
),
|
70 |
),
|
71 |
),
|
72 |
array(
|
73 |
'methods' => WP_REST_Server::EDITABLE,
|
74 |
'callback' => array( $this, 'update_item' ),
|
75 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
76 |
-
'args' =>
|
77 |
-
'key' => array(),
|
78 |
-
'value' => array(),
|
79 |
-
),
|
80 |
),
|
81 |
array(
|
82 |
'methods' => WP_REST_Server::DELETABLE,
|
83 |
'callback' => array( $this, 'delete_item' ),
|
84 |
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
85 |
-
'args' => array(
|
|
|
|
|
|
|
|
|
86 |
),
|
87 |
-
|
88 |
-
|
89 |
-
'methods' => WP_REST_Server::READABLE,
|
90 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
91 |
) );
|
92 |
}
|
93 |
|
@@ -106,17 +96,22 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
106 |
*/
|
107 |
'properties' => array(
|
108 |
'id' => array(
|
109 |
-
'description' => 'Unique identifier for the object.',
|
110 |
-
'type' => '
|
111 |
'context' => array( 'edit' ),
|
|
|
112 |
),
|
113 |
'key' => array(
|
114 |
-
'description' => 'The key for the custom field.',
|
115 |
'type' => 'string',
|
116 |
'context' => array( 'edit' ),
|
|
|
|
|
|
|
|
|
117 |
),
|
118 |
'value' => array(
|
119 |
-
'description' => 'The value of the custom field.',
|
120 |
'type' => 'string',
|
121 |
'context' => array( 'edit' ),
|
122 |
),
|
@@ -125,6 +120,19 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
125 |
return $schema;
|
126 |
}
|
127 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
/**
|
129 |
* Get the meta ID column for the relevant table.
|
130 |
*
|
@@ -190,7 +198,7 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
190 |
$meta = get_metadata_by_mid( $this->parent_type, $mid );
|
191 |
|
192 |
if ( empty( $meta ) ) {
|
193 |
-
return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta
|
194 |
}
|
195 |
|
196 |
if ( absint( $meta->$parent_column ) !== $parent_id ) {
|
@@ -239,6 +247,14 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
239 |
$parent_column = $this->get_parent_column();
|
240 |
$response->add_link( 'about', rest_url( 'wp/' . $this->parent_base . '/' . $data->$parent_column ), array( 'embeddable' => true ) );
|
241 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
242 |
return apply_filters( 'rest_prepare_meta_value', $response, $request );
|
243 |
}
|
244 |
|
@@ -256,7 +272,7 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
256 |
$current = get_metadata_by_mid( $this->parent_type, $mid );
|
257 |
|
258 |
if ( empty( $current ) ) {
|
259 |
-
return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta
|
260 |
}
|
261 |
|
262 |
if ( absint( $current->$parent_column ) !== $parent_id ) {
|
@@ -319,6 +335,15 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
319 |
) );
|
320 |
$response = $this->get_item( $request );
|
321 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
322 |
return rest_ensure_response( $response );
|
323 |
}
|
324 |
|
@@ -355,14 +380,14 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
355 |
return new WP_Error( $code, __( 'Invalid provided meta data for action.' ), array( 'status' => 400 ) );
|
356 |
}
|
357 |
|
358 |
-
if ( is_protected_meta( $request['key'] ) ) {
|
359 |
-
return new WP_Error( 'rest_meta_protected', sprintf( __( '%s is marked as a protected field.' ), $request['key'] ), array( 'status' => 403 ) );
|
360 |
-
}
|
361 |
-
|
362 |
if ( empty( $request['key'] ) ) {
|
363 |
return new WP_Error( 'rest_meta_invalid_key', __( 'Invalid meta key.' ), array( 'status' => 400 ) );
|
364 |
}
|
365 |
|
|
|
|
|
|
|
|
|
366 |
$meta_key = wp_slash( $request['key'] );
|
367 |
$value = wp_slash( $request['value'] );
|
368 |
|
@@ -381,7 +406,10 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
381 |
|
382 |
$response->set_status( 201 );
|
383 |
$data = $response->get_data();
|
384 |
-
$response->header( 'Location', rest_url( $this->parent_base . '/' . $parent_id . '/meta/' . $data['id'] ) );
|
|
|
|
|
|
|
385 |
|
386 |
return $response;
|
387 |
}
|
@@ -406,7 +434,7 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
406 |
$current = get_metadata_by_mid( $this->parent_type, $mid );
|
407 |
|
408 |
if ( empty( $current ) ) {
|
409 |
-
return new WP_Error( 'rest_meta_invalid_id', __( 'Invalid meta
|
410 |
}
|
411 |
|
412 |
if ( absint( $current->$parent_column ) !== (int) $parent_id ) {
|
@@ -427,6 +455,13 @@ abstract class WP_REST_Meta_Controller extends WP_REST_Controller {
|
|
427 |
return new WP_Error( 'rest_meta_could_not_delete', __( 'Could not delete meta.' ), array( 'status' => 500 ) );
|
428 |
}
|
429 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
430 |
return rest_ensure_response( array( 'message' => __( 'Deleted meta' ) ) );
|
431 |
}
|
432 |
}
|
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,
|
47 |
'callback' => array( $this, 'create_item' ),
|
48 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
49 |
+
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
|
|
|
|
|
|
|
|
|
|
|
50 |
),
|
51 |
+
|
52 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
53 |
) );
|
54 |
register_rest_route( 'wp/v2', '/' . $this->parent_base . '/(?P<parent_id>[\d]+)/meta/(?P<id>[\d]+)', array(
|
55 |
array(
|
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(
|
64 |
'methods' => WP_REST_Server::EDITABLE,
|
65 |
'callback' => array( $this, 'update_item' ),
|
66 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
67 |
+
'args' => $this->get_endpoint_args_for_item_schema( false ),
|
|
|
|
|
|
|
68 |
),
|
69 |
array(
|
70 |
'methods' => WP_REST_Server::DELETABLE,
|
71 |
'callback' => array( $this, 'delete_item' ),
|
72 |
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
73 |
+
'args' => array(
|
74 |
+
'force' => array(
|
75 |
+
'default' => false,
|
76 |
+
),
|
77 |
+
),
|
78 |
),
|
79 |
+
|
80 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
|
|
|
|
81 |
) );
|
82 |
}
|
83 |
|
96 |
*/
|
97 |
'properties' => array(
|
98 |
'id' => array(
|
99 |
+
'description' => __( 'Unique identifier for the object.' ),
|
100 |
+
'type' => 'integer',
|
101 |
'context' => array( 'edit' ),
|
102 |
+
'readonly' => true,
|
103 |
),
|
104 |
'key' => array(
|
105 |
+
'description' => __( 'The key for the custom field.' ),
|
106 |
'type' => 'string',
|
107 |
'context' => array( 'edit' ),
|
108 |
+
'required' => true,
|
109 |
+
'arg_options' => array(
|
110 |
+
'sanitize_callback' => 'sanitize_text_field',
|
111 |
+
),
|
112 |
),
|
113 |
'value' => array(
|
114 |
+
'description' => __( 'The value of the custom field.' ),
|
115 |
'type' => 'string',
|
116 |
'context' => array( 'edit' ),
|
117 |
),
|
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 |
$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 ) {
|
247 |
$parent_column = $this->get_parent_column();
|
248 |
$response->add_link( 'about', rest_url( 'wp/' . $this->parent_base . '/' . $data->$parent_column ), array( 'embeddable' => true ) );
|
249 |
|
250 |
+
/**
|
251 |
+
* Filter a meta value returned from the API.
|
252 |
+
*
|
253 |
+
* Allows modification of the meta value right before it is returned.
|
254 |
+
*
|
255 |
+
* @param array $response Key value array of meta data: id, key, value.
|
256 |
+
* @param WP_REST_Request $request Request used to generate the response.
|
257 |
+
*/
|
258 |
return apply_filters( 'rest_prepare_meta_value', $response, $request );
|
259 |
}
|
260 |
|
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 ) {
|
335 |
) );
|
336 |
$response = $this->get_item( $request );
|
337 |
|
338 |
+
/**
|
339 |
+
* Fires after meta is added to an object or updated via the REST API.
|
340 |
+
*
|
341 |
+
* @param array $value The inserted meta data.
|
342 |
+
* @param WP_REST_Request $request The request sent to the API.
|
343 |
+
* @param bool $creating True when adding meta, false when updating.
|
344 |
+
*/
|
345 |
+
do_action( 'rest_insert_meta', $value, $request, false );
|
346 |
+
|
347 |
return rest_ensure_response( $response );
|
348 |
}
|
349 |
|
380 |
return new WP_Error( $code, __( 'Invalid provided meta data for action.' ), array( 'status' => 400 ) );
|
381 |
}
|
382 |
|
|
|
|
|
|
|
|
|
383 |
if ( empty( $request['key'] ) ) {
|
384 |
return new WP_Error( 'rest_meta_invalid_key', __( 'Invalid meta key.' ), array( 'status' => 400 ) );
|
385 |
}
|
386 |
|
387 |
+
if ( is_protected_meta( $request['key'] ) ) {
|
388 |
+
return new WP_Error( 'rest_meta_protected', sprintf( __( '%s is marked as a protected field.' ), $request['key'] ), array( 'status' => 403 ) );
|
389 |
+
}
|
390 |
+
|
391 |
$meta_key = wp_slash( $request['key'] );
|
392 |
$value = wp_slash( $request['value'] );
|
393 |
|
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 );
|
413 |
|
414 |
return $response;
|
415 |
}
|
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 ) {
|
455 |
return new WP_Error( 'rest_meta_could_not_delete', __( 'Could not delete meta.' ), array( 'status' => 500 ) );
|
456 |
}
|
457 |
|
458 |
+
/**
|
459 |
+
* Fires after a meta value is deleted via the REST API.
|
460 |
+
*
|
461 |
+
* @param WP_REST_Request $request The request sent to the API.
|
462 |
+
*/
|
463 |
+
do_action( 'rest_delete_meta', $request );
|
464 |
+
|
465 |
return rest_ensure_response( array( 'message' => __( 'Deleted meta' ) ) );
|
466 |
}
|
467 |
}
|
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
|
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' =>
|
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' =>
|
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
|
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' =>
|
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' =>
|
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' => 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 |
$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 |
}
|
lib/endpoints/class-wp-rest-post-statuses-controller.php
CHANGED
@@ -8,18 +8,23 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
|
|
8 |
public function register_routes() {
|
9 |
|
10 |
register_rest_route( 'wp/v2', '/statuses', array(
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
'
|
17 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
18 |
) );
|
19 |
|
20 |
register_rest_route( 'wp/v2', '/statuses/(?P<status>[\w-]+)', array(
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
) );
|
24 |
}
|
25 |
|
@@ -69,7 +74,7 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
|
|
69 |
*/
|
70 |
public function prepare_item_for_response( $status, $request ) {
|
71 |
if ( ( false === $status->public && ! is_user_logged_in() ) || ( true === $status->internal && is_user_logged_in() ) ) {
|
72 |
-
return new WP_Error( 'rest_cannot_read_status', __( 'Cannot view status.' ), array( 'status' =>
|
73 |
}
|
74 |
|
75 |
$data = array(
|
@@ -86,17 +91,26 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
|
|
86 |
$data = $this->filter_response_by_context( $data, $context );
|
87 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
88 |
|
89 |
-
$
|
90 |
|
91 |
$posts_controller = new WP_REST_Posts_Controller( 'post' );
|
92 |
|
93 |
if ( 'publish' === $status->name ) {
|
94 |
-
$
|
95 |
} else {
|
96 |
-
$
|
97 |
}
|
98 |
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
}
|
101 |
|
102 |
/**
|
@@ -111,37 +125,37 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
|
|
111 |
'type' => 'object',
|
112 |
'properties' => array(
|
113 |
'name' => array(
|
114 |
-
'description' => 'The title for the status.',
|
115 |
'type' => 'string',
|
116 |
'context' => array( 'view' ),
|
117 |
),
|
118 |
'private' => array(
|
119 |
-
'description' => 'Whether posts with this status should be private.',
|
120 |
'type' => 'boolean',
|
121 |
'context' => array( 'view' ),
|
122 |
),
|
123 |
'protected' => array(
|
124 |
-
'description' => 'Whether posts with this status should be protected.',
|
125 |
'type' => 'boolean',
|
126 |
'context' => array( 'view' ),
|
127 |
),
|
128 |
'public' => array(
|
129 |
-
'description' => 'Whether posts of this status should be shown in the front end of the site.',
|
130 |
'type' => 'boolean',
|
131 |
'context' => array( 'view' ),
|
132 |
),
|
133 |
'queryable' => array(
|
134 |
-
'description' => 'Whether posts with this status should be publicly-queryable.',
|
135 |
'type' => 'boolean',
|
136 |
'context' => array( 'view' ),
|
137 |
),
|
138 |
'show_in_list' => array(
|
139 |
-
'description' => 'Whether to include posts in the edit listing for their post type.',
|
140 |
'type' => 'boolean',
|
141 |
'context' => array( 'view' ),
|
142 |
),
|
143 |
'slug' => array(
|
144 |
-
'description' => 'An alphanumeric identifier for the status.',
|
145 |
'type' => 'string',
|
146 |
'context' => array( 'view' ),
|
147 |
),
|
@@ -150,4 +164,15 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller {
|
|
150 |
return $this->add_additional_fields_schema( $schema );
|
151 |
}
|
152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
}
|
8 |
public function register_routes() {
|
9 |
|
10 |
register_rest_route( 'wp/v2', '/statuses', array(
|
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 |
) );
|
18 |
|
19 |
register_rest_route( 'wp/v2', '/statuses/(?P<status>[\w-]+)', array(
|
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 |
) );
|
29 |
}
|
30 |
|
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 |
$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 |
/**
|
125 |
'type' => 'object',
|
126 |
'properties' => array(
|
127 |
'name' => array(
|
128 |
+
'description' => __( 'The title for the status.' ),
|
129 |
'type' => 'string',
|
130 |
'context' => array( 'view' ),
|
131 |
),
|
132 |
'private' => array(
|
133 |
+
'description' => __( 'Whether posts with this status should be private.' ),
|
134 |
'type' => 'boolean',
|
135 |
'context' => array( 'view' ),
|
136 |
),
|
137 |
'protected' => array(
|
138 |
+
'description' => __( 'Whether posts with this status should be protected.' ),
|
139 |
'type' => 'boolean',
|
140 |
'context' => array( 'view' ),
|
141 |
),
|
142 |
'public' => array(
|
143 |
+
'description' => __( 'Whether posts of this status should be shown in the front end of the site.' ),
|
144 |
'type' => 'boolean',
|
145 |
'context' => array( 'view' ),
|
146 |
),
|
147 |
'queryable' => array(
|
148 |
+
'description' => __( 'Whether posts with this status should be publicly-queryable.' ),
|
149 |
'type' => 'boolean',
|
150 |
'context' => array( 'view' ),
|
151 |
),
|
152 |
'show_in_list' => array(
|
153 |
+
'description' => __( 'Whether to include posts in the edit listing for their post type.' ),
|
154 |
'type' => 'boolean',
|
155 |
'context' => array( 'view' ),
|
156 |
),
|
157 |
'slug' => array(
|
158 |
+
'description' => __( 'An alphanumeric identifier for the status.' ),
|
159 |
'type' => 'string',
|
160 |
'context' => array( 'view' ),
|
161 |
),
|
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 |
}
|
lib/endpoints/class-wp-rest-post-types-controller.php
CHANGED
@@ -8,23 +8,23 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
|
|
8 |
public function register_routes() {
|
9 |
|
10 |
register_rest_route( 'wp/v2', '/types', array(
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
'
|
15 |
-
'sanitize_callback' => 'sanitize_key',
|
16 |
-
),
|
17 |
),
|
18 |
-
|
19 |
-
|
20 |
-
register_rest_route( 'wp/v2', '/types/schema', array(
|
21 |
-
'methods' => WP_REST_Server::READABLE,
|
22 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
23 |
) );
|
24 |
|
25 |
register_rest_route( 'wp/v2', '/types/(?P<type>[\w-]+)', array(
|
26 |
-
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
) );
|
29 |
}
|
30 |
|
@@ -36,12 +36,12 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
|
|
36 |
*/
|
37 |
public function get_items( $request ) {
|
38 |
$data = array();
|
39 |
-
foreach ( get_post_types( array(
|
40 |
-
|
41 |
-
if ( is_wp_error( $post_type ) ) {
|
42 |
continue;
|
43 |
}
|
44 |
-
$
|
|
|
45 |
}
|
46 |
return $data;
|
47 |
}
|
@@ -57,6 +57,12 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
|
|
57 |
if ( empty( $obj ) ) {
|
58 |
return new WP_Error( 'rest_type_invalid', __( 'Invalid type.' ), array( 'status' => 404 ) );
|
59 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
return $this->prepare_item_for_response( $obj, $request );
|
61 |
}
|
62 |
|
@@ -68,10 +74,6 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
|
|
68 |
* @return array Post type data
|
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' => 403 ) );
|
73 |
-
}
|
74 |
-
|
75 |
$data = array(
|
76 |
'description' => $post_type->description,
|
77 |
'hierarchical' => $post_type->hierarchical,
|
@@ -83,7 +85,29 @@ 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
}
|
88 |
|
89 |
/**
|
@@ -98,33 +122,44 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller {
|
|
98 |
'type' => 'object',
|
99 |
'properties' => array(
|
100 |
'description' => array(
|
101 |
-
'description' => 'A human-readable description of the object.',
|
102 |
'type' => 'string',
|
103 |
-
'context' => array( 'view' ),
|
104 |
),
|
105 |
'hierarchical' => array(
|
106 |
-
'description' => 'Whether or not the type should have children.',
|
107 |
'type' => 'boolean',
|
108 |
-
'context' => array( 'view' ),
|
109 |
),
|
110 |
'labels' => array(
|
111 |
-
'description' => 'Human-readable labels for the type for various contexts.',
|
112 |
'type' => 'object',
|
113 |
-
'context' => array( '
|
114 |
),
|
115 |
'name' => array(
|
116 |
-
'description' => 'The title for the object.',
|
117 |
'type' => 'string',
|
118 |
-
'context' => array( 'view' ),
|
119 |
),
|
120 |
'slug' => array(
|
121 |
-
'description' => 'An alphanumeric identifier for the object.',
|
122 |
'type' => 'string',
|
123 |
-
'context' => array( 'view' ),
|
124 |
),
|
125 |
),
|
126 |
);
|
127 |
return $this->add_additional_fields_schema( $schema );
|
128 |
}
|
129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
}
|
8 |
public function register_routes() {
|
9 |
|
10 |
register_rest_route( 'wp/v2', '/types', array(
|
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 |
) );
|
18 |
|
19 |
register_rest_route( 'wp/v2', '/types/(?P<type>[\w-]+)', array(
|
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 |
) );
|
29 |
}
|
30 |
|
36 |
*/
|
37 |
public function get_items( $request ) {
|
38 |
$data = array();
|
39 |
+
foreach ( get_post_types( array(), 'object' ) as $obj ) {
|
40 |
+
if ( empty( $obj->show_in_rest ) || ( 'edit' === $request['context'] && ! current_user_can( $obj->cap->edit_posts ) ) ) {
|
|
|
41 |
continue;
|
42 |
}
|
43 |
+
$post_type = $this->prepare_item_for_response( $obj, $request );
|
44 |
+
$data[ $obj->name ] = $this->prepare_response_for_collection( $post_type );
|
45 |
}
|
46 |
return $data;
|
47 |
}
|
57 |
if ( empty( $obj ) ) {
|
58 |
return new WP_Error( 'rest_type_invalid', __( 'Invalid type.' ), array( 'status' => 404 ) );
|
59 |
}
|
60 |
+
if ( empty( $obj->show_in_rest ) ) {
|
61 |
+
return new WP_Error( 'rest_cannot_read_type', __( 'Cannot view type.' ), array( 'status' => rest_authorization_required_code() ) );
|
62 |
+
}
|
63 |
+
if ( 'edit' === $request['context'] && ! current_user_can( $obj->cap->edit_posts ) ) {
|
64 |
+
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to manage this type.' ), array( 'status' => rest_authorization_required_code() ) );
|
65 |
+
}
|
66 |
return $this->prepare_item_for_response( $obj, $request );
|
67 |
}
|
68 |
|
74 |
* @return array Post type data
|
75 |
*/
|
76 |
public function prepare_item_for_response( $post_type, $request ) {
|
|
|
|
|
|
|
|
|
77 |
$data = array(
|
78 |
'description' => $post_type->description,
|
79 |
'hierarchical' => $post_type->hierarchical,
|
85 |
$data = $this->filter_response_by_context( $data, $context );
|
86 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
87 |
|
88 |
+
// Wrap the data in a response object.
|
89 |
+
$response = rest_ensure_response( $data );
|
90 |
+
|
91 |
+
$base = ! empty( $post_type->rest_base ) ? $post_type->rest_base : $post_type->name;
|
92 |
+
$response->add_links( array(
|
93 |
+
'collection' => array(
|
94 |
+
'href' => rest_url( 'wp/v2/types' ),
|
95 |
+
),
|
96 |
+
'https://api.w.org/items' => array(
|
97 |
+
'href' => rest_url( sprintf( 'wp/v2/%s', $base ) ),
|
98 |
+
),
|
99 |
+
) );
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Filter a post type returned from the API.
|
103 |
+
*
|
104 |
+
* Allows modification of the post type data right before it is returned.
|
105 |
+
*
|
106 |
+
* @param WP_REST_Response $response The response object.
|
107 |
+
* @param object $item The original post type object.
|
108 |
+
* @param WP_REST_Request $request Request used to generate the response.
|
109 |
+
*/
|
110 |
+
return apply_filters( 'rest_prepare_post_type', $response, $post_type, $request );
|
111 |
}
|
112 |
|
113 |
/**
|
122 |
'type' => 'object',
|
123 |
'properties' => array(
|
124 |
'description' => array(
|
125 |
+
'description' => __( 'A human-readable description of the object.' ),
|
126 |
'type' => 'string',
|
127 |
+
'context' => array( 'view', 'edit' ),
|
128 |
),
|
129 |
'hierarchical' => array(
|
130 |
+
'description' => __( 'Whether or not the type should have children.' ),
|
131 |
'type' => 'boolean',
|
132 |
+
'context' => array( 'view', 'edit' ),
|
133 |
),
|
134 |
'labels' => array(
|
135 |
+
'description' => __( 'Human-readable labels for the type for various contexts.' ),
|
136 |
'type' => 'object',
|
137 |
+
'context' => array( 'edit' ),
|
138 |
),
|
139 |
'name' => array(
|
140 |
+
'description' => __( 'The title for the object.' ),
|
141 |
'type' => 'string',
|
142 |
+
'context' => array( 'view', 'edit' ),
|
143 |
),
|
144 |
'slug' => array(
|
145 |
+
'description' => __( 'An alphanumeric identifier for the object.' ),
|
146 |
'type' => 'string',
|
147 |
+
'context' => array( 'view', 'edit' ),
|
148 |
),
|
149 |
),
|
150 |
);
|
151 |
return $this->add_additional_fields_schema( $schema );
|
152 |
}
|
153 |
|
154 |
+
/**
|
155 |
+
* Get the query params for collections
|
156 |
+
*
|
157 |
+
* @return array
|
158 |
+
*/
|
159 |
+
public function get_collection_params() {
|
160 |
+
return array(
|
161 |
+
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
|
162 |
+
);
|
163 |
+
}
|
164 |
+
|
165 |
}
|
lib/endpoints/class-wp-rest-posts-controller.php
CHANGED
@@ -15,38 +15,21 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
15 |
|
16 |
$base = $this->get_post_type_base( $this->post_type );
|
17 |
|
18 |
-
$posts_args = array(
|
19 |
-
'context' => array(
|
20 |
-
'default' => 'view',
|
21 |
-
),
|
22 |
-
'page' => array(
|
23 |
-
'default' => 1,
|
24 |
-
'sanitize_callback' => 'absint',
|
25 |
-
),
|
26 |
-
'per_page' => array(
|
27 |
-
'default' => 10,
|
28 |
-
'sanitize_callback' => 'absint',
|
29 |
-
),
|
30 |
-
);
|
31 |
-
|
32 |
-
foreach ( $this->get_allowed_query_vars() as $var ) {
|
33 |
-
if ( ! isset( $posts_args[ $var ] ) ) {
|
34 |
-
$posts_args[ $var ] = array();
|
35 |
-
}
|
36 |
-
}
|
37 |
-
|
38 |
register_rest_route( 'wp/v2', '/' . $base, array(
|
39 |
array(
|
40 |
'methods' => WP_REST_Server::READABLE,
|
41 |
'callback' => array( $this, 'get_items' ),
|
42 |
-
'
|
|
|
43 |
),
|
44 |
array(
|
45 |
'methods' => WP_REST_Server::CREATABLE,
|
46 |
'callback' => array( $this, 'create_item' ),
|
47 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
48 |
-
'args' => $this->get_endpoint_args_for_item_schema(
|
49 |
),
|
|
|
|
|
50 |
) );
|
51 |
register_rest_route( 'wp/v2', '/' . $base . '/(?P<id>[\d]+)', array(
|
52 |
array(
|
@@ -54,16 +37,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
54 |
'callback' => array( $this, 'get_item' ),
|
55 |
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
56 |
'args' => array(
|
57 |
-
'context' => array(
|
58 |
-
'default' => 'view',
|
59 |
-
),
|
60 |
),
|
61 |
),
|
62 |
array(
|
63 |
'methods' => WP_REST_Server::EDITABLE,
|
64 |
'callback' => array( $this, 'update_item' ),
|
65 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
66 |
-
'args' => $this->get_endpoint_args_for_item_schema(
|
67 |
),
|
68 |
array(
|
69 |
'methods' => WP_REST_Server::DELETABLE,
|
@@ -75,34 +56,45 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
75 |
),
|
76 |
),
|
77 |
),
|
78 |
-
|
79 |
-
|
80 |
-
'methods' => WP_REST_Server::READABLE,
|
81 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
82 |
) );
|
83 |
}
|
84 |
|
85 |
/**
|
86 |
-
* Get a collection of posts
|
87 |
*
|
88 |
-
* @param WP_REST_Request $request Full details about the request
|
89 |
* @return WP_Error|WP_REST_Response
|
90 |
*/
|
91 |
public function get_items( $request ) {
|
92 |
-
$args
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
$args['post_type'] = $this->post_type;
|
94 |
-
$args['paged'] = $args['page'];
|
95 |
-
$args['posts_per_page'] = $args['per_page'];
|
96 |
-
unset( $args['page'] );
|
97 |
|
98 |
/**
|
99 |
-
*
|
100 |
*
|
101 |
-
*
|
102 |
* collection request.
|
103 |
*
|
104 |
-
* @param array
|
105 |
-
* @param WP_REST_Request $request
|
106 |
*/
|
107 |
$args = apply_filters( 'rest_post_query', $args, $request );
|
108 |
$query_args = $this->prepare_items_query( $args );
|
@@ -122,24 +114,35 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
122 |
|
123 |
$response = rest_ensure_response( $posts );
|
124 |
$count_query = new WP_Query();
|
|
|
|
|
|
|
125 |
unset( $query_args['paged'] );
|
|
|
126 |
$query_result = $count_query->query( $query_args );
|
127 |
$total_posts = $count_query->found_posts;
|
128 |
$response->header( 'X-WP-Total', (int) $total_posts );
|
129 |
-
$max_pages = ceil( $total_posts / $
|
130 |
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
131 |
|
132 |
-
$
|
133 |
-
if ( $
|
134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
if ( $prev_page > $max_pages ) {
|
136 |
$prev_page = $max_pages;
|
137 |
}
|
138 |
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
139 |
$response->link_header( 'prev', $prev_link );
|
140 |
}
|
141 |
-
if ( $max_pages > $
|
142 |
-
$next_page = $
|
143 |
$next_link = add_query_arg( 'page', $next_page, $base );
|
144 |
$response->link_header( 'next', $next_link );
|
145 |
}
|
@@ -148,9 +151,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
148 |
}
|
149 |
|
150 |
/**
|
151 |
-
* Get a single post
|
152 |
*
|
153 |
-
* @param WP_REST_Request $request Full details about the request
|
154 |
* @return WP_Error|WP_REST_Response
|
155 |
*/
|
156 |
public function get_item( $request ) {
|
@@ -158,7 +161,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
158 |
$post = get_post( $id );
|
159 |
|
160 |
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
161 |
-
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post
|
162 |
}
|
163 |
|
164 |
$data = $this->prepare_item_for_response( $post, $request );
|
@@ -170,9 +173,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
170 |
}
|
171 |
|
172 |
/**
|
173 |
-
* Create a single post
|
174 |
*
|
175 |
-
* @param WP_REST_Request $request Full details about the request
|
176 |
* @return WP_Error|WP_REST_Response
|
177 |
*/
|
178 |
public function create_item( $request ) {
|
@@ -224,16 +227,18 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
224 |
$this->update_additional_fields_for_object( get_post( $post_id ), $request );
|
225 |
|
226 |
/**
|
227 |
-
*
|
228 |
-
* Media Controller has been migrated to new style.
|
229 |
*
|
230 |
-
*
|
|
|
|
|
231 |
*/
|
|
|
232 |
|
233 |
-
$
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
$response = rest_ensure_response( $response );
|
238 |
$response->set_status( 201 );
|
239 |
$response->header( 'Location', rest_url( '/wp/v2/' . $this->get_post_type_base( $post->post_type ) . '/' . $post_id ) );
|
@@ -242,25 +247,25 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
242 |
}
|
243 |
|
244 |
/**
|
245 |
-
* Update a single post
|
246 |
*
|
247 |
-
* @param WP_REST_Request $request Full details about the request
|
248 |
* @return WP_Error|WP_REST_Response
|
249 |
*/
|
250 |
public function update_item( $request ) {
|
251 |
$id = (int) $request['id'];
|
252 |
$post = get_post( $id );
|
253 |
|
254 |
-
if (
|
255 |
-
return new WP_Error( 'rest_post_invalid_id', __( 'Post
|
256 |
}
|
257 |
|
258 |
$post = $this->prepare_item_for_database( $request );
|
259 |
if ( is_wp_error( $post ) ) {
|
260 |
return $post;
|
261 |
}
|
262 |
-
|
263 |
-
$post_id = wp_update_post( $post, true );
|
264 |
if ( is_wp_error( $post_id ) ) {
|
265 |
if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ) ) ) {
|
266 |
$post_id->add_data( array( 'status' => 500 ) );
|
@@ -295,23 +300,27 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
295 |
$this->update_additional_fields_for_object( get_post( $post_id ), $request );
|
296 |
|
297 |
/**
|
298 |
-
* @TODO: Enable rest_insert_post() action after
|
299 |
* Media Controller has been migrated to new style.
|
300 |
*
|
301 |
* do_action( 'rest_insert_post', $post, $request );
|
302 |
*/
|
303 |
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
|
|
|
|
|
|
|
|
308 |
}
|
309 |
|
310 |
/**
|
311 |
-
* Delete a single post
|
312 |
*
|
313 |
-
* @param WP_REST_Request $request Full details about the request
|
314 |
-
* @return
|
315 |
*/
|
316 |
public function delete_item( $request ) {
|
317 |
$id = (int) $request['id'];
|
@@ -320,7 +329,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
320 |
$post = get_post( $id );
|
321 |
|
322 |
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
323 |
-
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post
|
324 |
}
|
325 |
|
326 |
$supports_trash = ( EMPTY_TRASH_DAYS > 0 );
|
@@ -329,31 +338,34 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
329 |
}
|
330 |
|
331 |
/**
|
332 |
-
* Filter whether
|
|
|
|
|
333 |
*
|
334 |
-
* @param boolean $supports_trash
|
335 |
-
* @param WP_Post $post Post
|
336 |
*/
|
337 |
-
$supports_trash = apply_filters( '
|
338 |
|
339 |
if ( ! $this->check_delete_permission( $post ) ) {
|
340 |
-
return new WP_Error( 'rest_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' =>
|
341 |
}
|
342 |
|
343 |
$request = new WP_REST_Request( 'GET', '/wp/v2/' . $this->get_post_type_base( $this->post_type ) . '/' . $post->ID );
|
344 |
$request->set_param( 'context', 'edit' );
|
345 |
$response = rest_do_request( $request );
|
346 |
|
347 |
-
// If we're forcing, then delete permanently
|
348 |
if ( $force ) {
|
349 |
$result = wp_delete_post( $id, true );
|
|
|
350 |
} else {
|
351 |
-
// If we don't support trashing for this type, error out
|
352 |
if ( ! $supports_trash ) {
|
353 |
return new WP_Error( 'rest_trash_not_supported', __( 'The post does not support trashing.' ), array( 'status' => 501 ) );
|
354 |
}
|
355 |
|
356 |
-
// Otherwise, only trash if we haven't already
|
357 |
if ( 'trash' === $post->post_status ) {
|
358 |
return new WP_Error( 'rest_already_deleted', __( 'The post has already been deleted.' ), array( 'status' => 410 ) );
|
359 |
}
|
@@ -361,17 +373,51 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
361 |
// (Note that internally this falls through to `wp_delete_post` if
|
362 |
// the trash is disabled.)
|
363 |
$result = wp_trash_post( $id );
|
|
|
364 |
}
|
365 |
|
366 |
if ( ! $result ) {
|
367 |
return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
|
368 |
}
|
369 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
370 |
return $response;
|
371 |
}
|
372 |
|
373 |
/**
|
374 |
-
* Check if a given request has access to read
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
375 |
*
|
376 |
* @param WP_REST_Request $request Full details about the request.
|
377 |
* @return bool|WP_Error
|
@@ -381,7 +427,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
381 |
$post = get_post( (int) $request['id'] );
|
382 |
|
383 |
if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
|
384 |
-
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to
|
385 |
}
|
386 |
|
387 |
if ( $post ) {
|
@@ -392,7 +438,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
392 |
}
|
393 |
|
394 |
/**
|
395 |
-
* Check if a given request has access to create a post
|
396 |
*
|
397 |
* @param WP_REST_Request $request Full details about the request.
|
398 |
* @return bool|WP_Error
|
@@ -402,22 +448,22 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
402 |
$post_type = get_post_type_object( $this->post_type );
|
403 |
|
404 |
if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
|
405 |
-
return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' =>
|
406 |
}
|
407 |
|
408 |
if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
|
409 |
-
return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to create posts as this user.' ), array( 'status' =>
|
410 |
}
|
411 |
|
412 |
if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
|
413 |
-
return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' =>
|
414 |
}
|
415 |
|
416 |
return current_user_can( $post_type->cap->create_posts );
|
417 |
}
|
418 |
|
419 |
/**
|
420 |
-
* Check if a given request has access to update a post
|
421 |
*
|
422 |
* @param WP_REST_Request $request Full details about the request.
|
423 |
* @return bool|WP_Error
|
@@ -432,22 +478,22 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
432 |
}
|
433 |
|
434 |
if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
|
435 |
-
return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' =>
|
436 |
}
|
437 |
|
438 |
if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
|
439 |
-
return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to update posts as this user.' ), array( 'status' =>
|
440 |
}
|
441 |
|
442 |
if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
|
443 |
-
return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' =>
|
444 |
}
|
445 |
|
446 |
return true;
|
447 |
}
|
448 |
|
449 |
/**
|
450 |
-
* Check if a given request has access to delete a post
|
451 |
*
|
452 |
* @param WP_REST_Request $request Full details about the request.
|
453 |
* @return bool|WP_Error
|
@@ -457,7 +503,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
457 |
$post = get_post( $request['id'] );
|
458 |
|
459 |
if ( $post && ! $this->check_delete_permission( $post ) ) {
|
460 |
-
return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete posts.' ), array( 'status' =>
|
461 |
}
|
462 |
|
463 |
return true;
|
@@ -476,7 +522,15 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
476 |
$query_args = array();
|
477 |
foreach ( $valid_vars as $var => $index ) {
|
478 |
if ( isset( $prepared_args[ $var ] ) ) {
|
479 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
480 |
}
|
481 |
}
|
482 |
|
@@ -484,6 +538,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
484 |
$query_args['post_status'] = 'inherit';
|
485 |
}
|
486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
487 |
return $query_args;
|
488 |
}
|
489 |
|
@@ -494,36 +556,50 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
494 |
*/
|
495 |
protected function get_allowed_query_vars() {
|
496 |
global $wp;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
497 |
$valid_vars = apply_filters( 'query_vars', $wp->public_query_vars );
|
498 |
|
499 |
-
|
|
|
500 |
/**
|
501 |
-
*
|
502 |
*
|
503 |
* If the user has the `edit_posts` capability, we also allow use of
|
504 |
* private query parameters, which are only undesirable on the
|
505 |
* frontend, but are safe for use in query strings.
|
506 |
*
|
507 |
* To disable anyway, use
|
508 |
-
* `add_filter('rest_private_query_vars', '__return_empty_array');`
|
509 |
*
|
510 |
-
* @param array $
|
|
|
511 |
*/
|
512 |
$private = apply_filters( 'rest_private_query_vars', $wp->private_query_vars );
|
513 |
$valid_vars = array_merge( $valid_vars, $private );
|
514 |
}
|
515 |
-
// Define our own in addition to WP's normal vars
|
516 |
-
$rest_valid = array( 'posts_per_page', 'ignore_sticky_posts', 'post_parent' );
|
517 |
$valid_vars = array_merge( $valid_vars, $rest_valid );
|
518 |
|
519 |
/**
|
520 |
-
*
|
521 |
*
|
522 |
-
* This filter allows you to add or remove query vars from the allowed
|
523 |
* list for all requests, including unauthenticated ones. To alter the
|
524 |
* vars for editors only, {@see rest_private_query_vars}.
|
525 |
*
|
526 |
-
* @param array
|
|
|
|
|
|
|
|
|
527 |
*/
|
528 |
$valid_vars = apply_filters( 'rest_query_vars', $valid_vars );
|
529 |
|
@@ -531,7 +607,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
531 |
}
|
532 |
|
533 |
/**
|
534 |
-
* Check the post excerpt and prepare it for single post output
|
535 |
*
|
536 |
* @param string $excerpt
|
537 |
* @return string|null $excerpt
|
@@ -541,6 +617,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
541 |
return __( 'There is no excerpt because this is a protected post.' );
|
542 |
}
|
543 |
|
|
|
544 |
$excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt ) );
|
545 |
|
546 |
if ( empty( $excerpt ) ) {
|
@@ -564,10 +641,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
564 |
}
|
565 |
|
566 |
if ( isset( $date ) ) {
|
567 |
-
return
|
568 |
}
|
569 |
|
570 |
-
return
|
571 |
}
|
572 |
|
573 |
protected function prepare_password_response( $password ) {
|
@@ -586,22 +663,22 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
586 |
}
|
587 |
|
588 |
/**
|
589 |
-
* Prepare a single post for create or update
|
590 |
*
|
591 |
-
* @param WP_REST_Request $request Request object
|
592 |
-
* @return WP_Error|
|
593 |
*/
|
594 |
protected function prepare_item_for_database( $request ) {
|
595 |
$prepared_post = new stdClass;
|
596 |
|
597 |
-
// ID
|
598 |
if ( isset( $request['id'] ) ) {
|
599 |
$prepared_post->ID = absint( $request['id'] );
|
600 |
}
|
601 |
|
602 |
$schema = $this->get_item_schema();
|
603 |
|
604 |
-
// Post title
|
605 |
if ( ! empty( $schema['properties']['title'] ) && isset( $request['title'] ) ) {
|
606 |
if ( is_string( $request['title'] ) ) {
|
607 |
$prepared_post->post_title = wp_filter_post_kses( $request['title'] );
|
@@ -610,7 +687,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
610 |
}
|
611 |
}
|
612 |
|
613 |
-
// Post content
|
614 |
if ( ! empty( $schema['properties']['content'] ) && isset( $request['content'] ) ) {
|
615 |
if ( is_string( $request['content'] ) ) {
|
616 |
$prepared_post->post_content = wp_filter_post_kses( $request['content'] );
|
@@ -619,7 +696,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
619 |
}
|
620 |
}
|
621 |
|
622 |
-
// Post excerpt
|
623 |
if ( ! empty( $schema['properties']['excerpt'] ) && isset( $request['excerpt'] ) ) {
|
624 |
if ( is_string( $request['excerpt'] ) ) {
|
625 |
$prepared_post->post_excerpt = wp_filter_post_kses( $request['excerpt'] );
|
@@ -628,9 +705,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
628 |
}
|
629 |
}
|
630 |
|
631 |
-
// Post type
|
632 |
if ( empty( $request['id'] ) ) {
|
633 |
-
// Creating new post, use default type for the controller
|
634 |
$prepared_post->post_type = $this->post_type;
|
635 |
} else {
|
636 |
// Updating a post, use previous type.
|
@@ -638,7 +715,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
638 |
}
|
639 |
$post_type = get_post_type_object( $prepared_post->post_type );
|
640 |
|
641 |
-
// Post status
|
642 |
if ( isset( $request['status'] ) ) {
|
643 |
$status = $this->handle_status_param( $request['status'], $post_type );
|
644 |
if ( is_wp_error( $status ) ) {
|
@@ -648,27 +725,23 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
648 |
$prepared_post->post_status = $status;
|
649 |
}
|
650 |
|
651 |
-
// Post date
|
652 |
if ( ! empty( $request['date'] ) ) {
|
653 |
$date_data = rest_get_date_with_gmt( $request['date'] );
|
654 |
|
655 |
if ( ! empty( $date_data ) ) {
|
656 |
list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
|
657 |
-
} else {
|
658 |
-
return new WP_Error( 'rest_invalid_date', __( 'The date you provided is invalid.' ), array( 'status' => 400 ) );
|
659 |
}
|
660 |
} elseif ( ! empty( $request['date_gmt'] ) ) {
|
661 |
$date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
|
662 |
|
663 |
if ( ! empty( $date_data ) ) {
|
664 |
list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
|
665 |
-
} else {
|
666 |
-
return new WP_Error( 'rest_invalid_date', __( 'The date you provided is invalid.' ), array( 'status' => 400 ) );
|
667 |
}
|
668 |
}
|
669 |
-
// Post slug
|
670 |
if ( isset( $request['slug'] ) ) {
|
671 |
-
$prepared_post->post_name =
|
672 |
}
|
673 |
|
674 |
// Author
|
@@ -681,8 +754,8 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
681 |
$prepared_post->post_author = $author;
|
682 |
}
|
683 |
|
684 |
-
// Post password
|
685 |
-
if ( isset( $request['password'] ) ) {
|
686 |
$prepared_post->post_password = $request['password'];
|
687 |
|
688 |
if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
|
@@ -700,33 +773,43 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
700 |
}
|
701 |
}
|
702 |
|
703 |
-
// Parent
|
704 |
$post_type_obj = get_post_type_object( $this->post_type );
|
705 |
if ( ! empty( $schema['properties']['parent'] ) && ! empty( $request['parent'] ) ) {
|
706 |
$parent = get_post( (int) $request['parent'] );
|
707 |
if ( empty( $parent ) ) {
|
708 |
-
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent
|
709 |
}
|
710 |
|
711 |
$prepared_post->post_parent = (int) $parent->ID;
|
712 |
}
|
713 |
|
714 |
-
// Menu order
|
715 |
if ( ! empty( $schema['properties']['menu_order'] ) && isset( $request['menu_order'] ) ) {
|
716 |
$prepared_post->menu_order = (int) $request['menu_order'];
|
717 |
}
|
718 |
|
719 |
-
// Comment status
|
720 |
if ( ! empty( $schema['properties']['comment_status'] ) && ! empty( $request['comment_status'] ) ) {
|
721 |
-
$prepared_post->comment_status =
|
722 |
}
|
723 |
|
724 |
-
// Ping status
|
725 |
if ( ! empty( $schema['properties']['ping_status'] ) && ! empty( $request['ping_status'] ) ) {
|
726 |
-
$prepared_post->ping_status =
|
727 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
728 |
|
729 |
-
return apply_filters( 'rest_pre_insert_' . $this->post_type, $prepared_post, $request );
|
730 |
}
|
731 |
|
732 |
/**
|
@@ -737,7 +820,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
737 |
* @return WP_Error|string $post_status
|
738 |
*/
|
739 |
protected function handle_status_param( $post_status, $post_type ) {
|
740 |
-
$post_status =
|
741 |
|
742 |
switch ( $post_status ) {
|
743 |
case 'draft':
|
@@ -745,13 +828,13 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
745 |
break;
|
746 |
case 'private':
|
747 |
if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
|
748 |
-
return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create private posts in this post type' ), array( 'status' =>
|
749 |
}
|
750 |
break;
|
751 |
case 'publish':
|
752 |
case 'future':
|
753 |
if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
|
754 |
-
return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type' ), array( 'status' =>
|
755 |
}
|
756 |
break;
|
757 |
default:
|
@@ -781,13 +864,13 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
781 |
$post_author = (int) $post_author;
|
782 |
}
|
783 |
|
784 |
-
// Only check edit others' posts if we are another user
|
785 |
if ( get_current_user_id() !== $post_author ) {
|
786 |
|
787 |
$author = get_userdata( $post_author );
|
788 |
|
789 |
if ( ! $author ) {
|
790 |
-
return new WP_Error( 'rest_invalid_author', __( 'Invalid author
|
791 |
}
|
792 |
}
|
793 |
|
@@ -795,7 +878,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
795 |
}
|
796 |
|
797 |
/**
|
798 |
-
* Determine the featured image based on a request param
|
799 |
*
|
800 |
* @param int $featured_image
|
801 |
* @param int $post_id
|
@@ -808,7 +891,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
808 |
if ( $result ) {
|
809 |
return true;
|
810 |
} else {
|
811 |
-
return new WP_Error( 'rest_invalid_featured_image', __( 'Invalid featured image
|
812 |
}
|
813 |
} else {
|
814 |
return delete_post_thumbnail( $post_id );
|
@@ -817,13 +900,13 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
817 |
}
|
818 |
|
819 |
/**
|
820 |
-
* Set the template for a page
|
821 |
*
|
822 |
* @param string $template
|
823 |
* @param integer $post_id
|
824 |
*/
|
825 |
public function handle_template( $template, $post_id ) {
|
826 |
-
if ( in_array( $template,
|
827 |
update_post_meta( $post_id, '_wp_page_template', $template );
|
828 |
} else {
|
829 |
update_post_meta( $post_id, '_wp_page_template', '' );
|
@@ -841,7 +924,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
841 |
$post_type = get_post_type_object( $post_type );
|
842 |
}
|
843 |
|
844 |
-
if ( ! empty( $post_type ) && $post_type->show_in_rest ) {
|
845 |
return true;
|
846 |
}
|
847 |
|
@@ -849,11 +932,11 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
849 |
}
|
850 |
|
851 |
/**
|
852 |
-
* Check if we can read a post
|
853 |
*
|
854 |
* Correctly handles posts with the inherit status.
|
855 |
*
|
856 |
-
* @param
|
857 |
* @return bool Can we read it?
|
858 |
*/
|
859 |
public function check_read_permission( $post ) {
|
@@ -871,6 +954,11 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
871 |
return true;
|
872 |
}
|
873 |
|
|
|
|
|
|
|
|
|
|
|
874 |
// Can we read the parent if we're inheriting?
|
875 |
if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) {
|
876 |
$parent = get_post( $post->post_parent );
|
@@ -878,7 +966,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
878 |
}
|
879 |
|
880 |
// If we don't have a parent, but the status is set to inherit, assume
|
881 |
-
// it's published (as per get_post_status())
|
882 |
if ( 'inherit' === $post->post_status ) {
|
883 |
return true;
|
884 |
}
|
@@ -887,9 +975,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
887 |
}
|
888 |
|
889 |
/**
|
890 |
-
* Check if we can edit a post
|
891 |
*
|
892 |
-
* @param
|
893 |
* @return bool Can we edit it?
|
894 |
*/
|
895 |
protected function check_update_permission( $post ) {
|
@@ -903,10 +991,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
903 |
}
|
904 |
|
905 |
/**
|
906 |
-
* Check if we can create a post
|
907 |
*
|
908 |
-
* @param
|
909 |
-
* @return bool Can we create it
|
910 |
*/
|
911 |
protected function check_create_permission( $post ) {
|
912 |
$post_type = get_post_type_object( $post->post_type );
|
@@ -919,9 +1007,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
919 |
}
|
920 |
|
921 |
/**
|
922 |
-
* Check if we can delete a post
|
923 |
*
|
924 |
-
* @param
|
925 |
* @return bool Can we delete it?
|
926 |
*/
|
927 |
protected function check_delete_permission( $post ) {
|
@@ -951,22 +1039,23 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
951 |
}
|
952 |
|
953 |
/**
|
954 |
-
* Prepare a single post output for response
|
955 |
*
|
956 |
-
* @param WP_Post $post Post object
|
957 |
-
* @param WP_REST_Request $request Request object
|
958 |
* @return WP_REST_Response $data
|
959 |
*/
|
960 |
public function prepare_item_for_response( $post, $request ) {
|
961 |
$GLOBALS['post'] = $post;
|
962 |
setup_postdata( $post );
|
963 |
|
964 |
-
// Base fields for every post
|
965 |
$data = array(
|
966 |
'id' => $post->ID,
|
967 |
'date' => $this->prepare_date_response( $post->post_date_gmt, $post->post_date ),
|
968 |
'date_gmt' => $this->prepare_date_response( $post->post_date_gmt ),
|
969 |
'guid' => array(
|
|
|
970 |
'rendered' => apply_filters( 'get_the_guid', $post->guid ),
|
971 |
'raw' => $post->guid,
|
972 |
),
|
@@ -996,10 +1085,11 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
996 |
|
997 |
$data['content'] = array(
|
998 |
'raw' => $post->post_content,
|
|
|
999 |
'rendered' => apply_filters( 'the_content', $post->post_content ),
|
1000 |
);
|
1001 |
|
1002 |
-
// Don't leave our cookie lying around: https://github.com/WP-API/WP-API/issues/1055
|
1003 |
if ( ! empty( $post->post_password ) ) {
|
1004 |
$_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = '';
|
1005 |
}
|
@@ -1050,7 +1140,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1050 |
|
1051 |
if ( ! empty( $schema['properties']['format'] ) ) {
|
1052 |
$data['format'] = get_post_format( $post->ID );
|
1053 |
-
// Fill in blank post format
|
1054 |
if ( empty( $data['format'] ) ) {
|
1055 |
$data['format'] = 'standard';
|
1056 |
}
|
@@ -1061,12 +1151,22 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1061 |
|
1062 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
1063 |
|
1064 |
-
// Wrap the data in a response object
|
1065 |
-
$
|
1066 |
|
1067 |
-
$
|
1068 |
|
1069 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1070 |
}
|
1071 |
|
1072 |
/**
|
@@ -1081,10 +1181,13 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1081 |
// Entity meta
|
1082 |
$links = array(
|
1083 |
'self' => array(
|
1084 |
-
'href'
|
1085 |
),
|
1086 |
'collection' => array(
|
1087 |
-
'href'
|
|
|
|
|
|
|
1088 |
),
|
1089 |
);
|
1090 |
|
@@ -1098,7 +1201,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1098 |
|
1099 |
if ( in_array( $post->post_type, array( 'post', 'page' ) ) || post_type_supports( $post->post_type, 'comments' ) ) {
|
1100 |
$replies_url = rest_url( '/wp/v2/comments' );
|
1101 |
-
$replies_url = add_query_arg( '
|
1102 |
$links['replies'] = array(
|
1103 |
'href' => $replies_url,
|
1104 |
'embeddable' => true,
|
@@ -1118,35 +1221,37 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1118 |
);
|
1119 |
}
|
1120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1121 |
if ( ! in_array( $post->post_type, array( 'attachment', 'nav_menu_item', 'revision' ) ) ) {
|
1122 |
$attachments_url = rest_url( 'wp/v2/media' );
|
1123 |
-
$attachments_url = add_query_arg( '
|
1124 |
-
$links['
|
1125 |
'href' => $attachments_url,
|
1126 |
-
'embeddable' => true,
|
1127 |
);
|
1128 |
}
|
1129 |
|
1130 |
$taxonomies = get_object_taxonomies( $post->post_type );
|
1131 |
if ( ! empty( $taxonomies ) ) {
|
1132 |
-
$links['
|
1133 |
|
1134 |
foreach ( $taxonomies as $tax ) {
|
1135 |
$taxonomy_obj = get_taxonomy( $tax );
|
1136 |
// Skip taxonomies that are not public.
|
1137 |
-
if ( false === $taxonomy_obj->public ) {
|
1138 |
continue;
|
1139 |
}
|
1140 |
|
1141 |
-
|
1142 |
-
|
1143 |
-
} else {
|
1144 |
-
$terms_url = rest_url( '/wp/v2/terms/' . $tax );
|
1145 |
-
}
|
1146 |
-
|
1147 |
-
$terms_url = add_query_arg( 'post', $post->ID, $terms_url );
|
1148 |
|
1149 |
-
$links['
|
1150 |
'href' => $terms_url,
|
1151 |
'taxonomy' => $tax,
|
1152 |
'embeddable' => true,
|
@@ -1155,7 +1260,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1155 |
}
|
1156 |
|
1157 |
if ( post_type_supports( $post->post_type, 'custom-fields' ) ) {
|
1158 |
-
$links['
|
1159 |
'href' => rest_url( trailingslashit( $base ) . $post->ID . '/meta' ),
|
1160 |
'embeddable' => true,
|
1161 |
);
|
@@ -1165,7 +1270,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1165 |
}
|
1166 |
|
1167 |
/**
|
1168 |
-
* Get the Post's schema, conforming to JSON Schema
|
1169 |
*
|
1170 |
* @return array
|
1171 |
*/
|
@@ -1177,82 +1282,87 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1177 |
'title' => $this->post_type,
|
1178 |
'type' => 'object',
|
1179 |
/*
|
1180 |
-
* Base properties for every Post
|
1181 |
*/
|
1182 |
'properties' => array(
|
1183 |
'date' => array(
|
1184 |
-
'description' =>
|
1185 |
'type' => 'string',
|
1186 |
'format' => 'date-time',
|
1187 |
'context' => array( 'view', 'edit', 'embed' ),
|
1188 |
),
|
1189 |
'date_gmt' => array(
|
1190 |
-
'description' => 'The date the object was published, as GMT.',
|
1191 |
'type' => 'string',
|
1192 |
'format' => 'date-time',
|
1193 |
-
'context' => array( 'edit' ),
|
1194 |
),
|
1195 |
'guid' => array(
|
1196 |
-
'description' => 'The globally unique identifier for the object.',
|
1197 |
'type' => 'object',
|
1198 |
'context' => array( 'view', 'edit' ),
|
1199 |
'readonly' => true,
|
1200 |
'properties' => array(
|
1201 |
'raw' => array(
|
1202 |
-
'description' => 'GUID for the object, as it exists in the database.',
|
1203 |
'type' => 'string',
|
1204 |
'context' => array( 'edit' ),
|
1205 |
),
|
1206 |
'rendered' => array(
|
1207 |
-
'description' => 'GUID for the object, transformed for display.',
|
1208 |
'type' => 'string',
|
1209 |
'context' => array( 'view', 'edit' ),
|
1210 |
),
|
1211 |
),
|
1212 |
),
|
1213 |
'id' => array(
|
1214 |
-
'description' => 'Unique identifier for the object.',
|
1215 |
'type' => 'integer',
|
1216 |
'context' => array( 'view', 'edit', 'embed' ),
|
1217 |
'readonly' => true,
|
1218 |
),
|
1219 |
'link' => array(
|
1220 |
-
'description' => 'URL to the object.',
|
1221 |
'type' => 'string',
|
1222 |
'format' => 'uri',
|
1223 |
'context' => array( 'view', 'edit', 'embed' ),
|
1224 |
'readonly' => true,
|
1225 |
),
|
1226 |
'modified' => array(
|
1227 |
-
'description' =>
|
1228 |
'type' => 'string',
|
1229 |
'format' => 'date-time',
|
1230 |
'context' => array( 'view', 'edit' ),
|
|
|
1231 |
),
|
1232 |
'modified_gmt' => array(
|
1233 |
-
'description' => 'The date the object was last modified, as GMT.',
|
1234 |
'type' => 'string',
|
1235 |
'format' => 'date-time',
|
1236 |
'context' => array( 'view', 'edit' ),
|
|
|
1237 |
),
|
1238 |
'password' => array(
|
1239 |
-
'description' => 'A password to protect access to the post.',
|
1240 |
'type' => 'string',
|
1241 |
'context' => array( 'edit' ),
|
1242 |
),
|
1243 |
'slug' => array(
|
1244 |
-
'description' => 'An alphanumeric identifier for the object unique to its type.',
|
1245 |
'type' => 'string',
|
1246 |
'context' => array( 'view', 'edit', 'embed' ),
|
|
|
|
|
|
|
1247 |
),
|
1248 |
'status' => array(
|
1249 |
-
'description' => 'A named status for the object.',
|
1250 |
'type' => 'string',
|
1251 |
'enum' => array_keys( get_post_stati( array( 'internal' => false ) ) ),
|
1252 |
'context' => array( 'edit' ),
|
1253 |
),
|
1254 |
'type' => array(
|
1255 |
-
'description' => 'Type of Post for the object.',
|
1256 |
'type' => 'string',
|
1257 |
'context' => array( 'view', 'edit', 'embed' ),
|
1258 |
'readonly' => true,
|
@@ -1263,7 +1373,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1263 |
$post_type_obj = get_post_type_object( $this->post_type );
|
1264 |
if ( $post_type_obj->hierarchical ) {
|
1265 |
$schema['properties']['parent'] = array(
|
1266 |
-
'description' => 'The
|
1267 |
'type' => 'integer',
|
1268 |
'context' => array( 'view', 'edit' ),
|
1269 |
);
|
@@ -1319,17 +1429,17 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1319 |
|
1320 |
case 'title':
|
1321 |
$schema['properties']['title'] = array(
|
1322 |
-
'description' => 'The title for the object.',
|
1323 |
'type' => 'object',
|
1324 |
'context' => array( 'view', 'edit', 'embed' ),
|
1325 |
'properties' => array(
|
1326 |
'raw' => array(
|
1327 |
-
'description' => 'Title for the object, as it exists in the database.',
|
1328 |
'type' => 'string',
|
1329 |
'context' => array( 'edit' ),
|
1330 |
),
|
1331 |
'rendered' => array(
|
1332 |
-
'description' => 'Title for the object, transformed for display.',
|
1333 |
'type' => 'string',
|
1334 |
'context' => array( 'view', 'edit', 'embed' ),
|
1335 |
),
|
@@ -1339,17 +1449,17 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1339 |
|
1340 |
case 'editor':
|
1341 |
$schema['properties']['content'] = array(
|
1342 |
-
'description' => 'The content for the object.',
|
1343 |
'type' => 'object',
|
1344 |
'context' => array( 'view', 'edit' ),
|
1345 |
'properties' => array(
|
1346 |
'raw' => array(
|
1347 |
-
'description' => 'Content for the object, as it exists in the database.',
|
1348 |
'type' => 'string',
|
1349 |
'context' => array( 'edit' ),
|
1350 |
),
|
1351 |
'rendered' => array(
|
1352 |
-
'description' => 'Content for the object, transformed for display.',
|
1353 |
'type' => 'string',
|
1354 |
'context' => array( 'view', 'edit' ),
|
1355 |
),
|
@@ -1359,7 +1469,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1359 |
|
1360 |
case 'author':
|
1361 |
$schema['properties']['author'] = array(
|
1362 |
-
'description' => 'The
|
1363 |
'type' => 'integer',
|
1364 |
'context' => array( 'view', 'edit', 'embed' ),
|
1365 |
);
|
@@ -1367,17 +1477,17 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1367 |
|
1368 |
case 'excerpt':
|
1369 |
$schema['properties']['excerpt'] = array(
|
1370 |
-
'description' => 'The excerpt for the object.',
|
1371 |
'type' => 'object',
|
1372 |
'context' => array( 'view', 'edit', 'embed' ),
|
1373 |
'properties' => array(
|
1374 |
'raw' => array(
|
1375 |
-
'description' => 'Excerpt for the object, as it exists in the database.',
|
1376 |
'type' => 'string',
|
1377 |
'context' => array( 'edit' ),
|
1378 |
),
|
1379 |
'rendered' => array(
|
1380 |
-
'description' => 'Excerpt for the object, transformed for display.',
|
1381 |
'type' => 'string',
|
1382 |
'context' => array( 'view', 'edit', 'embed' ),
|
1383 |
),
|
@@ -1387,7 +1497,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1387 |
|
1388 |
case 'thumbnail':
|
1389 |
$schema['properties']['featured_image'] = array(
|
1390 |
-
'description' => '
|
1391 |
'type' => 'integer',
|
1392 |
'context' => array( 'view', 'edit' ),
|
1393 |
);
|
@@ -1395,13 +1505,13 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1395 |
|
1396 |
case 'comments':
|
1397 |
$schema['properties']['comment_status'] = array(
|
1398 |
-
'description' => 'Whether or not comments are open on the object.',
|
1399 |
'type' => 'string',
|
1400 |
'enum' => array( 'open', 'closed' ),
|
1401 |
'context' => array( 'view', 'edit' ),
|
1402 |
);
|
1403 |
$schema['properties']['ping_status'] = array(
|
1404 |
-
'description' => 'Whether or not the object can be pinged.',
|
1405 |
'type' => 'string',
|
1406 |
'enum' => array( 'open', 'closed' ),
|
1407 |
'context' => array( 'view', 'edit' ),
|
@@ -1410,7 +1520,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1410 |
|
1411 |
case 'page-attributes':
|
1412 |
$schema['properties']['menu_order'] = array(
|
1413 |
-
'description' => 'The order of the object in relation to other object of its type.',
|
1414 |
'type' => 'integer',
|
1415 |
'context' => array( 'view', 'edit' ),
|
1416 |
);
|
@@ -1418,9 +1528,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1418 |
|
1419 |
case 'post-formats':
|
1420 |
$schema['properties']['format'] = array(
|
1421 |
-
'description' => 'The format for the object.',
|
1422 |
'type' => 'string',
|
1423 |
-
'enum' => get_post_format_slugs(),
|
1424 |
'context' => array( 'view', 'edit' ),
|
1425 |
);
|
1426 |
break;
|
@@ -1430,7 +1540,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1430 |
|
1431 |
if ( 'post' === $this->post_type ) {
|
1432 |
$schema['properties']['sticky'] = array(
|
1433 |
-
'description' => 'Whether or not the object should be treated as sticky.',
|
1434 |
'type' => 'boolean',
|
1435 |
'context' => array( 'view', 'edit' ),
|
1436 |
);
|
@@ -1438,9 +1548,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1438 |
|
1439 |
if ( 'page' === $this->post_type ) {
|
1440 |
$schema['properties']['template'] = array(
|
1441 |
-
'description' => 'The theme file to use to display the object.',
|
1442 |
'type' => 'string',
|
1443 |
-
'enum' =>
|
1444 |
'context' => array( 'view', 'edit' ),
|
1445 |
);
|
1446 |
}
|
@@ -1448,4 +1558,89 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
|
|
1448 |
return $this->add_additional_fields_schema( $schema );
|
1449 |
}
|
1450 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1451 |
}
|
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,
|
27 |
'callback' => array( $this, 'create_item' ),
|
28 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
29 |
+
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
|
30 |
),
|
31 |
+
|
32 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
33 |
) );
|
34 |
register_rest_route( 'wp/v2', '/' . $base . '/(?P<id>[\d]+)', array(
|
35 |
array(
|
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(
|
44 |
'methods' => WP_REST_Server::EDITABLE,
|
45 |
'callback' => array( $this, 'update_item' ),
|
46 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
47 |
+
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
48 |
),
|
49 |
array(
|
50 |
'methods' => WP_REST_Server::DELETABLE,
|
56 |
),
|
57 |
),
|
58 |
),
|
59 |
+
|
60 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
|
|
|
|
61 |
) );
|
62 |
}
|
63 |
|
64 |
/**
|
65 |
+
* Get a collection of posts.
|
66 |
*
|
67 |
+
* @param WP_REST_Request $request Full details about the request.
|
68 |
* @return WP_Error|WP_REST_Response
|
69 |
*/
|
70 |
public function get_items( $request ) {
|
71 |
+
$args = array();
|
72 |
+
$args['author'] = $request['author'];
|
73 |
+
$args['order'] = $request['order'];
|
74 |
+
$args['orderby'] = $request['orderby'];
|
75 |
+
$args['paged'] = $request['page'];
|
76 |
+
$args['post__in'] = $request['include'];
|
77 |
+
$args['posts_per_page'] = $request['per_page'];
|
78 |
+
$args['post_parent'] = $request['parent'];
|
79 |
+
$args['post_status'] = $request['status'];
|
80 |
+
$args['s'] = $request['search'];
|
81 |
+
|
82 |
+
if ( is_array( $request['filter'] ) ) {
|
83 |
+
$args = array_merge( $args, $request['filter'] );
|
84 |
+
unset( $args['filter'] );
|
85 |
+
}
|
86 |
+
|
87 |
+
// Force the post_type argument, since it's not a user input variable.
|
88 |
$args['post_type'] = $this->post_type;
|
|
|
|
|
|
|
89 |
|
90 |
/**
|
91 |
+
* Filter the query arguments for a request.
|
92 |
*
|
93 |
+
* Enables adding extra arguments or setting defaults for a post
|
94 |
* collection request.
|
95 |
*
|
96 |
+
* @param array $args Key value array of query var to query value.
|
97 |
+
* @param WP_REST_Request $request The request used.
|
98 |
*/
|
99 |
$args = apply_filters( 'rest_post_query', $args, $request );
|
100 |
$query_args = $this->prepare_items_query( $args );
|
114 |
|
115 |
$response = rest_ensure_response( $posts );
|
116 |
$count_query = new WP_Query();
|
117 |
+
|
118 |
+
// Store paged value for pagination headers then unset for count query.
|
119 |
+
$page = (int) $query_args['paged'];
|
120 |
unset( $query_args['paged'] );
|
121 |
+
|
122 |
$query_result = $count_query->query( $query_args );
|
123 |
$total_posts = $count_query->found_posts;
|
124 |
$response->header( 'X-WP-Total', (int) $total_posts );
|
125 |
+
$max_pages = ceil( $total_posts / (int) $query_args['posts_per_page'] );
|
126 |
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
127 |
|
128 |
+
$request_params = $request->get_query_params();
|
129 |
+
if ( ! empty( $request_params['filter'] ) ) {
|
130 |
+
// Normalize the pagination params.
|
131 |
+
unset( $request_params['filter']['posts_per_page'] );
|
132 |
+
unset( $request_params['filter']['paged'] );
|
133 |
+
}
|
134 |
+
$base = add_query_arg( $request_params, rest_url( '/wp/v2/' . $this->get_post_type_base( $this->post_type ) ) );
|
135 |
+
|
136 |
+
if ( $page > 1 ) {
|
137 |
+
$prev_page = $page - 1;
|
138 |
if ( $prev_page > $max_pages ) {
|
139 |
$prev_page = $max_pages;
|
140 |
}
|
141 |
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
142 |
$response->link_header( 'prev', $prev_link );
|
143 |
}
|
144 |
+
if ( $max_pages > $page ) {
|
145 |
+
$next_page = $page + 1;
|
146 |
$next_link = add_query_arg( 'page', $next_page, $base );
|
147 |
$response->link_header( 'next', $next_link );
|
148 |
}
|
151 |
}
|
152 |
|
153 |
/**
|
154 |
+
* Get a single post.
|
155 |
*
|
156 |
+
* @param WP_REST_Request $request Full details about the request.
|
157 |
* @return WP_Error|WP_REST_Response
|
158 |
*/
|
159 |
public function get_item( $request ) {
|
161 |
$post = get_post( $id );
|
162 |
|
163 |
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
164 |
+
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
|
165 |
}
|
166 |
|
167 |
$data = $this->prepare_item_for_response( $post, $request );
|
173 |
}
|
174 |
|
175 |
/**
|
176 |
+
* Create a single post.
|
177 |
*
|
178 |
+
* @param WP_REST_Request $request Full details about the request.
|
179 |
* @return WP_Error|WP_REST_Response
|
180 |
*/
|
181 |
public function create_item( $request ) {
|
227 |
$this->update_additional_fields_for_object( get_post( $post_id ), $request );
|
228 |
|
229 |
/**
|
230 |
+
* Fires after a single post is created or updated via the REST API.
|
|
|
231 |
*
|
232 |
+
* @param object $post Inserted Post object (not a WP_Post object).
|
233 |
+
* @param WP_REST_Request $request Request object.
|
234 |
+
* @param bool $creating True when creating post, false when updating.
|
235 |
*/
|
236 |
+
do_action( 'rest_insert_post', $post, $request, true );
|
237 |
|
238 |
+
$get_request = new WP_REST_Request;
|
239 |
+
$get_request->set_param( 'id', $post_id );
|
240 |
+
$get_request->set_param( 'context', 'edit' );
|
241 |
+
$response = $this->get_item( $get_request );
|
242 |
$response = rest_ensure_response( $response );
|
243 |
$response->set_status( 201 );
|
244 |
$response->header( 'Location', rest_url( '/wp/v2/' . $this->get_post_type_base( $post->post_type ) . '/' . $post_id ) );
|
247 |
}
|
248 |
|
249 |
/**
|
250 |
+
* Update a single post.
|
251 |
*
|
252 |
+
* @param WP_REST_Request $request Full details about the request.
|
253 |
* @return WP_Error|WP_REST_Response
|
254 |
*/
|
255 |
public function update_item( $request ) {
|
256 |
$id = (int) $request['id'];
|
257 |
$post = get_post( $id );
|
258 |
|
259 |
+
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
260 |
+
return new WP_Error( 'rest_post_invalid_id', __( 'Post id is invalid.' ), array( 'status' => 400 ) );
|
261 |
}
|
262 |
|
263 |
$post = $this->prepare_item_for_database( $request );
|
264 |
if ( is_wp_error( $post ) ) {
|
265 |
return $post;
|
266 |
}
|
267 |
+
// convert the post object to an array, otherwise wp_update_post will expect non-escaped input
|
268 |
+
$post_id = wp_update_post( (array) $post, true );
|
269 |
if ( is_wp_error( $post_id ) ) {
|
270 |
if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ) ) ) {
|
271 |
$post_id->add_data( array( 'status' => 500 ) );
|
300 |
$this->update_additional_fields_for_object( get_post( $post_id ), $request );
|
301 |
|
302 |
/**
|
303 |
+
* @TODO: Enable rest_insert_post() action after.
|
304 |
* Media Controller has been migrated to new style.
|
305 |
*
|
306 |
* do_action( 'rest_insert_post', $post, $request );
|
307 |
*/
|
308 |
|
309 |
+
/* This action is documented in lib/endpoints/class-wp-rest-controller.php */
|
310 |
+
do_action( 'rest_insert_post', $post, $request, false );
|
311 |
+
|
312 |
+
$get_request = new WP_REST_Request;
|
313 |
+
$get_request->set_param( 'id', $post_id );
|
314 |
+
$get_request->set_param( 'context', 'edit' );
|
315 |
+
$response = $this->get_item( $get_request );
|
316 |
+
return rest_ensure_response( $response );
|
317 |
}
|
318 |
|
319 |
/**
|
320 |
+
* Delete a single post.
|
321 |
*
|
322 |
+
* @param WP_REST_Request $request Full details about the request.
|
323 |
+
* @return WP_REST_Response|WP_Error
|
324 |
*/
|
325 |
public function delete_item( $request ) {
|
326 |
$id = (int) $request['id'];
|
329 |
$post = get_post( $id );
|
330 |
|
331 |
if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
|
332 |
+
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) );
|
333 |
}
|
334 |
|
335 |
$supports_trash = ( EMPTY_TRASH_DAYS > 0 );
|
338 |
}
|
339 |
|
340 |
/**
|
341 |
+
* Filter whether a post is trashable.
|
342 |
+
*
|
343 |
+
* Return false to disable trash support for the post.
|
344 |
*
|
345 |
+
* @param boolean $supports_trash Whether the post type support trashing.
|
346 |
+
* @param WP_Post $post The Post object being considered for trashing support.
|
347 |
*/
|
348 |
+
$supports_trash = apply_filters( 'rest_post_trashable', $supports_trash, $post );
|
349 |
|
350 |
if ( ! $this->check_delete_permission( $post ) ) {
|
351 |
+
return new WP_Error( 'rest_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) );
|
352 |
}
|
353 |
|
354 |
$request = new WP_REST_Request( 'GET', '/wp/v2/' . $this->get_post_type_base( $this->post_type ) . '/' . $post->ID );
|
355 |
$request->set_param( 'context', 'edit' );
|
356 |
$response = rest_do_request( $request );
|
357 |
|
358 |
+
// If we're forcing, then delete permanently.
|
359 |
if ( $force ) {
|
360 |
$result = wp_delete_post( $id, true );
|
361 |
+
$status = 'deleted';
|
362 |
} else {
|
363 |
+
// If we don't support trashing for this type, error out.
|
364 |
if ( ! $supports_trash ) {
|
365 |
return new WP_Error( 'rest_trash_not_supported', __( 'The post does not support trashing.' ), array( 'status' => 501 ) );
|
366 |
}
|
367 |
|
368 |
+
// Otherwise, only trash if we haven't already.
|
369 |
if ( 'trash' === $post->post_status ) {
|
370 |
return new WP_Error( 'rest_already_deleted', __( 'The post has already been deleted.' ), array( 'status' => 410 ) );
|
371 |
}
|
373 |
// (Note that internally this falls through to `wp_delete_post` if
|
374 |
// the trash is disabled.)
|
375 |
$result = wp_trash_post( $id );
|
376 |
+
$status = 'trashed';
|
377 |
}
|
378 |
|
379 |
if ( ! $result ) {
|
380 |
return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
|
381 |
}
|
382 |
|
383 |
+
$data = $response->get_data();
|
384 |
+
$data = array(
|
385 |
+
'data' => $data,
|
386 |
+
$status => true,
|
387 |
+
);
|
388 |
+
$response->set_data( $data );
|
389 |
+
|
390 |
+
/**
|
391 |
+
* Fires after a single post is deleted or trashed via the REST API.
|
392 |
+
*
|
393 |
+
* @param object $post The deleted or trashed post.
|
394 |
+
* @param array $data The response data.
|
395 |
+
* @param WP_REST_Request $request The request sent to the API.
|
396 |
+
*/
|
397 |
+
do_action( 'rest_delete_post', $post, $data, $request );
|
398 |
+
|
399 |
return $response;
|
400 |
}
|
401 |
|
402 |
/**
|
403 |
+
* Check if a given request has access to read /posts.
|
404 |
+
*
|
405 |
+
* @param WP_REST_Request $request Full details about the request.
|
406 |
+
* @return bool|WP_Error
|
407 |
+
*/
|
408 |
+
public function get_items_permissions_check( $request ) {
|
409 |
+
|
410 |
+
$post_type = get_post_type_object( $this->post_type );
|
411 |
+
|
412 |
+
if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
|
413 |
+
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() ) );
|
414 |
+
}
|
415 |
+
|
416 |
+
return true;
|
417 |
+
}
|
418 |
+
|
419 |
+
/**
|
420 |
+
* Check if a given request has access to read a post.
|
421 |
*
|
422 |
* @param WP_REST_Request $request Full details about the request.
|
423 |
* @return bool|WP_Error
|
427 |
$post = get_post( (int) $request['id'] );
|
428 |
|
429 |
if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) {
|
430 |
+
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post' ), array( 'status' => rest_authorization_required_code() ) );
|
431 |
}
|
432 |
|
433 |
if ( $post ) {
|
438 |
}
|
439 |
|
440 |
/**
|
441 |
+
* Check if a given request has access to create a post.
|
442 |
*
|
443 |
* @param WP_REST_Request $request Full details about the request.
|
444 |
* @return bool|WP_Error
|
448 |
$post_type = get_post_type_object( $this->post_type );
|
449 |
|
450 |
if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
|
451 |
+
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() ) );
|
452 |
}
|
453 |
|
454 |
if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
|
455 |
+
return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to create posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
|
456 |
}
|
457 |
|
458 |
if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
|
459 |
+
return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
|
460 |
}
|
461 |
|
462 |
return current_user_can( $post_type->cap->create_posts );
|
463 |
}
|
464 |
|
465 |
/**
|
466 |
+
* Check if a given request has access to update a post.
|
467 |
*
|
468 |
* @param WP_REST_Request $request Full details about the request.
|
469 |
* @return bool|WP_Error
|
478 |
}
|
479 |
|
480 |
if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
|
481 |
+
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() ) );
|
482 |
}
|
483 |
|
484 |
if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
|
485 |
+
return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to update posts as this user.' ), array( 'status' => rest_authorization_required_code() ) );
|
486 |
}
|
487 |
|
488 |
if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
|
489 |
+
return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
|
490 |
}
|
491 |
|
492 |
return true;
|
493 |
}
|
494 |
|
495 |
/**
|
496 |
+
* Check if a given request has access to delete a post.
|
497 |
*
|
498 |
* @param WP_REST_Request $request Full details about the request.
|
499 |
* @return bool|WP_Error
|
503 |
$post = get_post( $request['id'] );
|
504 |
|
505 |
if ( $post && ! $this->check_delete_permission( $post ) ) {
|
506 |
+
return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete posts.' ), array( 'status' => rest_authorization_required_code() ) );
|
507 |
}
|
508 |
|
509 |
return true;
|
522 |
$query_args = array();
|
523 |
foreach ( $valid_vars as $var => $index ) {
|
524 |
if ( isset( $prepared_args[ $var ] ) ) {
|
525 |
+
/**
|
526 |
+
* Filter the query_vars used in `get_items` for the constructed query.
|
527 |
+
*
|
528 |
+
* The dynamic portion of the hook name, $var, refers to the query_var key.
|
529 |
+
*
|
530 |
+
* @param mixed $prepared_args[ $var ] The query_var value.
|
531 |
+
*
|
532 |
+
*/
|
533 |
+
$query_args[ $var ] = apply_filters( "rest_query_var-{$var}", $prepared_args[ $var ] );
|
534 |
}
|
535 |
}
|
536 |
|
538 |
$query_args['post_status'] = 'inherit';
|
539 |
}
|
540 |
|
541 |
+
if ( 'post' !== $this->post_type || ! isset( $query_args['ignore_sticky_posts'] ) ) {
|
542 |
+
$query_args['ignore_sticky_posts'] = true;
|
543 |
+
}
|
544 |
+
|
545 |
+
if ( 'include' === $query_args['orderby'] ) {
|
546 |
+
$query_args['orderby'] = 'post__in';
|
547 |
+
}
|
548 |
+
|
549 |
return $query_args;
|
550 |
}
|
551 |
|
556 |
*/
|
557 |
protected function get_allowed_query_vars() {
|
558 |
global $wp;
|
559 |
+
|
560 |
+
/**
|
561 |
+
* Filter the publicly allowed query vars.
|
562 |
+
*
|
563 |
+
* Allows adjusting of the default query vars that are made public.
|
564 |
+
*
|
565 |
+
* @param array Array of allowed WP_Query query vars.
|
566 |
+
*/
|
567 |
$valid_vars = apply_filters( 'query_vars', $wp->public_query_vars );
|
568 |
|
569 |
+
$post_type_obj = get_post_type_object( $this->post_type );
|
570 |
+
if ( current_user_can( $post_type_obj->cap->edit_posts ) ) {
|
571 |
/**
|
572 |
+
* Filter the allowed 'private' query vars for authorized users.
|
573 |
*
|
574 |
* If the user has the `edit_posts` capability, we also allow use of
|
575 |
* private query parameters, which are only undesirable on the
|
576 |
* frontend, but are safe for use in query strings.
|
577 |
*
|
578 |
* To disable anyway, use
|
579 |
+
* `add_filter( 'rest_private_query_vars', '__return_empty_array' );`
|
580 |
*
|
581 |
+
* @param array $private_query_vars Array of allowed query vars for authorized users.
|
582 |
+
* }
|
583 |
*/
|
584 |
$private = apply_filters( 'rest_private_query_vars', $wp->private_query_vars );
|
585 |
$valid_vars = array_merge( $valid_vars, $private );
|
586 |
}
|
587 |
+
// Define our own in addition to WP's normal vars.
|
588 |
+
$rest_valid = array( 'post__in', 'posts_per_page', 'ignore_sticky_posts', 'post_parent' );
|
589 |
$valid_vars = array_merge( $valid_vars, $rest_valid );
|
590 |
|
591 |
/**
|
592 |
+
* Filter allowed query vars for the REST API.
|
593 |
*
|
594 |
+
* This filter allows you to add or remove query vars from the final allowed
|
595 |
* list for all requests, including unauthenticated ones. To alter the
|
596 |
* vars for editors only, {@see rest_private_query_vars}.
|
597 |
*
|
598 |
+
* @param array {
|
599 |
+
* Array of allowed WP_Query query vars.
|
600 |
+
*
|
601 |
+
* @param string $allowed_query_var The query var to allow.
|
602 |
+
* }
|
603 |
*/
|
604 |
$valid_vars = apply_filters( 'rest_query_vars', $valid_vars );
|
605 |
|
607 |
}
|
608 |
|
609 |
/**
|
610 |
+
* Check the post excerpt and prepare it for single post output.
|
611 |
*
|
612 |
* @param string $excerpt
|
613 |
* @return string|null $excerpt
|
617 |
return __( 'There is no excerpt because this is a protected post.' );
|
618 |
}
|
619 |
|
620 |
+
/** This filter is documented in wp-includes/post-template.php */
|
621 |
$excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt ) );
|
622 |
|
623 |
if ( empty( $excerpt ) ) {
|
641 |
}
|
642 |
|
643 |
if ( isset( $date ) ) {
|
644 |
+
return mysql_to_rfc3339( $date );
|
645 |
}
|
646 |
|
647 |
+
return mysql_to_rfc3339( $date_gmt );
|
648 |
}
|
649 |
|
650 |
protected function prepare_password_response( $password ) {
|
663 |
}
|
664 |
|
665 |
/**
|
666 |
+
* Prepare a single post for create or update.
|
667 |
*
|
668 |
+
* @param WP_REST_Request $request Request object.
|
669 |
+
* @return WP_Error|object $prepared_post Post object.
|
670 |
*/
|
671 |
protected function prepare_item_for_database( $request ) {
|
672 |
$prepared_post = new stdClass;
|
673 |
|
674 |
+
// ID.
|
675 |
if ( isset( $request['id'] ) ) {
|
676 |
$prepared_post->ID = absint( $request['id'] );
|
677 |
}
|
678 |
|
679 |
$schema = $this->get_item_schema();
|
680 |
|
681 |
+
// Post title.
|
682 |
if ( ! empty( $schema['properties']['title'] ) && isset( $request['title'] ) ) {
|
683 |
if ( is_string( $request['title'] ) ) {
|
684 |
$prepared_post->post_title = wp_filter_post_kses( $request['title'] );
|
687 |
}
|
688 |
}
|
689 |
|
690 |
+
// Post content.
|
691 |
if ( ! empty( $schema['properties']['content'] ) && isset( $request['content'] ) ) {
|
692 |
if ( is_string( $request['content'] ) ) {
|
693 |
$prepared_post->post_content = wp_filter_post_kses( $request['content'] );
|
696 |
}
|
697 |
}
|
698 |
|
699 |
+
// Post excerpt.
|
700 |
if ( ! empty( $schema['properties']['excerpt'] ) && isset( $request['excerpt'] ) ) {
|
701 |
if ( is_string( $request['excerpt'] ) ) {
|
702 |
$prepared_post->post_excerpt = wp_filter_post_kses( $request['excerpt'] );
|
705 |
}
|
706 |
}
|
707 |
|
708 |
+
// Post type.
|
709 |
if ( empty( $request['id'] ) ) {
|
710 |
+
// Creating new post, use default type for the controller.
|
711 |
$prepared_post->post_type = $this->post_type;
|
712 |
} else {
|
713 |
// Updating a post, use previous type.
|
715 |
}
|
716 |
$post_type = get_post_type_object( $prepared_post->post_type );
|
717 |
|
718 |
+
// Post status.
|
719 |
if ( isset( $request['status'] ) ) {
|
720 |
$status = $this->handle_status_param( $request['status'], $post_type );
|
721 |
if ( is_wp_error( $status ) ) {
|
725 |
$prepared_post->post_status = $status;
|
726 |
}
|
727 |
|
728 |
+
// Post date.
|
729 |
if ( ! empty( $request['date'] ) ) {
|
730 |
$date_data = rest_get_date_with_gmt( $request['date'] );
|
731 |
|
732 |
if ( ! empty( $date_data ) ) {
|
733 |
list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
|
|
|
|
|
734 |
}
|
735 |
} elseif ( ! empty( $request['date_gmt'] ) ) {
|
736 |
$date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
|
737 |
|
738 |
if ( ! empty( $date_data ) ) {
|
739 |
list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
|
|
|
|
|
740 |
}
|
741 |
}
|
742 |
+
// Post slug.
|
743 |
if ( isset( $request['slug'] ) ) {
|
744 |
+
$prepared_post->post_name = $request['slug'];
|
745 |
}
|
746 |
|
747 |
// Author
|
754 |
$prepared_post->post_author = $author;
|
755 |
}
|
756 |
|
757 |
+
// Post password.
|
758 |
+
if ( isset( $request['password'] ) && '' !== $request['password'] ) {
|
759 |
$prepared_post->post_password = $request['password'];
|
760 |
|
761 |
if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
|
773 |
}
|
774 |
}
|
775 |
|
776 |
+
// Parent.
|
777 |
$post_type_obj = get_post_type_object( $this->post_type );
|
778 |
if ( ! empty( $schema['properties']['parent'] ) && ! empty( $request['parent'] ) ) {
|
779 |
$parent = get_post( (int) $request['parent'] );
|
780 |
if ( empty( $parent ) ) {
|
781 |
+
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent id.' ), array( 'status' => 400 ) );
|
782 |
}
|
783 |
|
784 |
$prepared_post->post_parent = (int) $parent->ID;
|
785 |
}
|
786 |
|
787 |
+
// Menu order.
|
788 |
if ( ! empty( $schema['properties']['menu_order'] ) && isset( $request['menu_order'] ) ) {
|
789 |
$prepared_post->menu_order = (int) $request['menu_order'];
|
790 |
}
|
791 |
|
792 |
+
// Comment status.
|
793 |
if ( ! empty( $schema['properties']['comment_status'] ) && ! empty( $request['comment_status'] ) ) {
|
794 |
+
$prepared_post->comment_status = $request['comment_status'];
|
795 |
}
|
796 |
|
797 |
+
// Ping status.
|
798 |
if ( ! empty( $schema['properties']['ping_status'] ) && ! empty( $request['ping_status'] ) ) {
|
799 |
+
$prepared_post->ping_status = $request['ping_status'];
|
800 |
}
|
801 |
+
/**
|
802 |
+
* Filter the query_vars used in `get_items` for the constructed query.
|
803 |
+
*
|
804 |
+
* The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
|
805 |
+
* prepared for insertion.
|
806 |
+
*
|
807 |
+
* @param object $prepared_post An object representing a single post prepared
|
808 |
+
* for inserting or updating the database.
|
809 |
+
* @param WP_REST_Request $request Request object.
|
810 |
+
*/
|
811 |
+
return apply_filters( "rest_pre_insert_{$this->post_type}", $prepared_post, $request );
|
812 |
|
|
|
813 |
}
|
814 |
|
815 |
/**
|
820 |
* @return WP_Error|string $post_status
|
821 |
*/
|
822 |
protected function handle_status_param( $post_status, $post_type ) {
|
823 |
+
$post_status = $post_status;
|
824 |
|
825 |
switch ( $post_status ) {
|
826 |
case 'draft':
|
828 |
break;
|
829 |
case 'private':
|
830 |
if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
|
831 |
+
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() ) );
|
832 |
}
|
833 |
break;
|
834 |
case 'publish':
|
835 |
case 'future':
|
836 |
if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
|
837 |
+
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() ) );
|
838 |
}
|
839 |
break;
|
840 |
default:
|
864 |
$post_author = (int) $post_author;
|
865 |
}
|
866 |
|
867 |
+
// Only check edit others' posts if we are another user.
|
868 |
if ( get_current_user_id() !== $post_author ) {
|
869 |
|
870 |
$author = get_userdata( $post_author );
|
871 |
|
872 |
if ( ! $author ) {
|
873 |
+
return new WP_Error( 'rest_invalid_author', __( 'Invalid author id.' ), array( 'status' => 400 ) );
|
874 |
}
|
875 |
}
|
876 |
|
878 |
}
|
879 |
|
880 |
/**
|
881 |
+
* Determine the featured image based on a request param.
|
882 |
*
|
883 |
* @param int $featured_image
|
884 |
* @param int $post_id
|
891 |
if ( $result ) {
|
892 |
return true;
|
893 |
} else {
|
894 |
+
return new WP_Error( 'rest_invalid_featured_image', __( 'Invalid featured image id.' ), array( 'status' => 400 ) );
|
895 |
}
|
896 |
} else {
|
897 |
return delete_post_thumbnail( $post_id );
|
900 |
}
|
901 |
|
902 |
/**
|
903 |
+
* Set the template for a page.
|
904 |
*
|
905 |
* @param string $template
|
906 |
* @param integer $post_id
|
907 |
*/
|
908 |
public function handle_template( $template, $post_id ) {
|
909 |
+
if ( in_array( $template, array_keys( wp_get_theme()->get_page_templates( get_post( $post_id ) ) ) ) ) {
|
910 |
update_post_meta( $post_id, '_wp_page_template', $template );
|
911 |
} else {
|
912 |
update_post_meta( $post_id, '_wp_page_template', '' );
|
924 |
$post_type = get_post_type_object( $post_type );
|
925 |
}
|
926 |
|
927 |
+
if ( ! empty( $post_type ) && ! empty( $post_type->show_in_rest ) ) {
|
928 |
return true;
|
929 |
}
|
930 |
|
932 |
}
|
933 |
|
934 |
/**
|
935 |
+
* Check if we can read a post.
|
936 |
*
|
937 |
* Correctly handles posts with the inherit status.
|
938 |
*
|
939 |
+
* @param object $post Post object.
|
940 |
* @return bool Can we read it?
|
941 |
*/
|
942 |
public function check_read_permission( $post ) {
|
954 |
return true;
|
955 |
}
|
956 |
|
957 |
+
$post_status_obj = get_post_status_object( $post->post_status );
|
958 |
+
if ( $post_status_obj && $post_status_obj->public ) {
|
959 |
+
return true;
|
960 |
+
}
|
961 |
+
|
962 |
// Can we read the parent if we're inheriting?
|
963 |
if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) {
|
964 |
$parent = get_post( $post->post_parent );
|
966 |
}
|
967 |
|
968 |
// If we don't have a parent, but the status is set to inherit, assume
|
969 |
+
// it's published (as per get_post_status()).
|
970 |
if ( 'inherit' === $post->post_status ) {
|
971 |
return true;
|
972 |
}
|
975 |
}
|
976 |
|
977 |
/**
|
978 |
+
* Check if we can edit a post.
|
979 |
*
|
980 |
+
* @param object $post Post object.
|
981 |
* @return bool Can we edit it?
|
982 |
*/
|
983 |
protected function check_update_permission( $post ) {
|
991 |
}
|
992 |
|
993 |
/**
|
994 |
+
* Check if we can create a post.
|
995 |
*
|
996 |
+
* @param object $post Post object.
|
997 |
+
* @return bool Can we create it?.
|
998 |
*/
|
999 |
protected function check_create_permission( $post ) {
|
1000 |
$post_type = get_post_type_object( $post->post_type );
|
1007 |
}
|
1008 |
|
1009 |
/**
|
1010 |
+
* Check if we can delete a post.
|
1011 |
*
|
1012 |
+
* @param object $post Post object.
|
1013 |
* @return bool Can we delete it?
|
1014 |
*/
|
1015 |
protected function check_delete_permission( $post ) {
|
1039 |
}
|
1040 |
|
1041 |
/**
|
1042 |
+
* Prepare a single post output for response.
|
1043 |
*
|
1044 |
+
* @param WP_Post $post Post object.
|
1045 |
+
* @param WP_REST_Request $request Request object.
|
1046 |
* @return WP_REST_Response $data
|
1047 |
*/
|
1048 |
public function prepare_item_for_response( $post, $request ) {
|
1049 |
$GLOBALS['post'] = $post;
|
1050 |
setup_postdata( $post );
|
1051 |
|
1052 |
+
// Base fields for every post.
|
1053 |
$data = array(
|
1054 |
'id' => $post->ID,
|
1055 |
'date' => $this->prepare_date_response( $post->post_date_gmt, $post->post_date ),
|
1056 |
'date_gmt' => $this->prepare_date_response( $post->post_date_gmt ),
|
1057 |
'guid' => array(
|
1058 |
+
/** This filter is documented in wp-includes/post-template.php */
|
1059 |
'rendered' => apply_filters( 'get_the_guid', $post->guid ),
|
1060 |
'raw' => $post->guid,
|
1061 |
),
|
1085 |
|
1086 |
$data['content'] = array(
|
1087 |
'raw' => $post->post_content,
|
1088 |
+
/** This filter is documented in wp-includes/post-template.php */
|
1089 |
'rendered' => apply_filters( 'the_content', $post->post_content ),
|
1090 |
);
|
1091 |
|
1092 |
+
// Don't leave our cookie lying around: https://github.com/WP-API/WP-API/issues/1055.
|
1093 |
if ( ! empty( $post->post_password ) ) {
|
1094 |
$_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = '';
|
1095 |
}
|
1140 |
|
1141 |
if ( ! empty( $schema['properties']['format'] ) ) {
|
1142 |
$data['format'] = get_post_format( $post->ID );
|
1143 |
+
// Fill in blank post format.
|
1144 |
if ( empty( $data['format'] ) ) {
|
1145 |
$data['format'] = 'standard';
|
1146 |
}
|
1151 |
|
1152 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
1153 |
|
1154 |
+
// Wrap the data in a response object.
|
1155 |
+
$response = rest_ensure_response( $data );
|
1156 |
|
1157 |
+
$response->add_links( $this->prepare_links( $post ) );
|
1158 |
|
1159 |
+
/**
|
1160 |
+
* Filter the post data for a response.
|
1161 |
+
*
|
1162 |
+
* The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being
|
1163 |
+
* prepared for the response.
|
1164 |
+
*
|
1165 |
+
* @param WP_REST_Response $response The response object.
|
1166 |
+
* @param WP_Post $post Post object.
|
1167 |
+
* @param WP_REST_Request $request Request object.
|
1168 |
+
*/
|
1169 |
+
return apply_filters( 'rest_prepare_' . $this->post_type, $response, $post, $request );
|
1170 |
}
|
1171 |
|
1172 |
/**
|
1181 |
// Entity meta
|
1182 |
$links = array(
|
1183 |
'self' => array(
|
1184 |
+
'href' => rest_url( trailingslashit( $base ) . $post->ID ),
|
1185 |
),
|
1186 |
'collection' => array(
|
1187 |
+
'href' => rest_url( $base ),
|
1188 |
+
),
|
1189 |
+
'about' => array(
|
1190 |
+
'href' => rest_url( '/wp/v2/types/' . $this->post_type ),
|
1191 |
),
|
1192 |
);
|
1193 |
|
1201 |
|
1202 |
if ( in_array( $post->post_type, array( 'post', 'page' ) ) || post_type_supports( $post->post_type, 'comments' ) ) {
|
1203 |
$replies_url = rest_url( '/wp/v2/comments' );
|
1204 |
+
$replies_url = add_query_arg( 'post', $post->ID, $replies_url );
|
1205 |
$links['replies'] = array(
|
1206 |
'href' => $replies_url,
|
1207 |
'embeddable' => true,
|
1221 |
);
|
1222 |
}
|
1223 |
|
1224 |
+
// If we have a featured image, add that.
|
1225 |
+
if ( $featured_image = get_post_thumbnail_id( $post->ID ) ) {
|
1226 |
+
$image_url = rest_url( 'wp/v2/media/' . $featured_image );
|
1227 |
+
$links['https://api.w.org/featuredmedia'] = array(
|
1228 |
+
'href' => $image_url,
|
1229 |
+
'embeddable' => true,
|
1230 |
+
);
|
1231 |
+
}
|
1232 |
if ( ! in_array( $post->post_type, array( 'attachment', 'nav_menu_item', 'revision' ) ) ) {
|
1233 |
$attachments_url = rest_url( 'wp/v2/media' );
|
1234 |
+
$attachments_url = add_query_arg( 'parent', $post->ID, $attachments_url );
|
1235 |
+
$links['https://api.w.org/attachment'] = array(
|
1236 |
'href' => $attachments_url,
|
|
|
1237 |
);
|
1238 |
}
|
1239 |
|
1240 |
$taxonomies = get_object_taxonomies( $post->post_type );
|
1241 |
if ( ! empty( $taxonomies ) ) {
|
1242 |
+
$links['https://api.w.org/term'] = array();
|
1243 |
|
1244 |
foreach ( $taxonomies as $tax ) {
|
1245 |
$taxonomy_obj = get_taxonomy( $tax );
|
1246 |
// Skip taxonomies that are not public.
|
1247 |
+
if ( false === $taxonomy_obj->public || 'post_format' === $tax ) {
|
1248 |
continue;
|
1249 |
}
|
1250 |
|
1251 |
+
$tax_base = ! empty( $taxonomy_obj->rest_base ) ? $taxonomy_obj->rest_base : $tax;
|
1252 |
+
$terms_url = rest_url( trailingslashit( $base ) . $post->ID . '/' . $tax_base );
|
|
|
|
|
|
|
|
|
|
|
1253 |
|
1254 |
+
$links['https://api.w.org/term'][] = array(
|
1255 |
'href' => $terms_url,
|
1256 |
'taxonomy' => $tax,
|
1257 |
'embeddable' => true,
|
1260 |
}
|
1261 |
|
1262 |
if ( post_type_supports( $post->post_type, 'custom-fields' ) ) {
|
1263 |
+
$links['https://api.w.org/meta'] = array(
|
1264 |
'href' => rest_url( trailingslashit( $base ) . $post->ID . '/meta' ),
|
1265 |
'embeddable' => true,
|
1266 |
);
|
1270 |
}
|
1271 |
|
1272 |
/**
|
1273 |
+
* Get the Post's schema, conforming to JSON Schema.
|
1274 |
*
|
1275 |
* @return array
|
1276 |
*/
|
1282 |
'title' => $this->post_type,
|
1283 |
'type' => 'object',
|
1284 |
/*
|
1285 |
+
* Base properties for every Post.
|
1286 |
*/
|
1287 |
'properties' => array(
|
1288 |
'date' => array(
|
1289 |
+
'description' => __( "The date the object was published, in the site's timezone." ),
|
1290 |
'type' => 'string',
|
1291 |
'format' => 'date-time',
|
1292 |
'context' => array( 'view', 'edit', 'embed' ),
|
1293 |
),
|
1294 |
'date_gmt' => array(
|
1295 |
+
'description' => __( 'The date the object was published, as GMT.' ),
|
1296 |
'type' => 'string',
|
1297 |
'format' => 'date-time',
|
1298 |
+
'context' => array( 'view', 'edit' ),
|
1299 |
),
|
1300 |
'guid' => array(
|
1301 |
+
'description' => __( 'The globally unique identifier for the object.' ),
|
1302 |
'type' => 'object',
|
1303 |
'context' => array( 'view', 'edit' ),
|
1304 |
'readonly' => true,
|
1305 |
'properties' => array(
|
1306 |
'raw' => array(
|
1307 |
+
'description' => __( 'GUID for the object, as it exists in the database.' ),
|
1308 |
'type' => 'string',
|
1309 |
'context' => array( 'edit' ),
|
1310 |
),
|
1311 |
'rendered' => array(
|
1312 |
+
'description' => __( 'GUID for the object, transformed for display.' ),
|
1313 |
'type' => 'string',
|
1314 |
'context' => array( 'view', 'edit' ),
|
1315 |
),
|
1316 |
),
|
1317 |
),
|
1318 |
'id' => array(
|
1319 |
+
'description' => __( 'Unique identifier for the object.' ),
|
1320 |
'type' => 'integer',
|
1321 |
'context' => array( 'view', 'edit', 'embed' ),
|
1322 |
'readonly' => true,
|
1323 |
),
|
1324 |
'link' => array(
|
1325 |
+
'description' => __( 'URL to the object.' ),
|
1326 |
'type' => 'string',
|
1327 |
'format' => 'uri',
|
1328 |
'context' => array( 'view', 'edit', 'embed' ),
|
1329 |
'readonly' => true,
|
1330 |
),
|
1331 |
'modified' => array(
|
1332 |
+
'description' => __( "The date the object was last modified, in the site's timezone." ),
|
1333 |
'type' => 'string',
|
1334 |
'format' => 'date-time',
|
1335 |
'context' => array( 'view', 'edit' ),
|
1336 |
+
'readonly' => true,
|
1337 |
),
|
1338 |
'modified_gmt' => array(
|
1339 |
+
'description' => __( 'The date the object was last modified, as GMT.' ),
|
1340 |
'type' => 'string',
|
1341 |
'format' => 'date-time',
|
1342 |
'context' => array( 'view', 'edit' ),
|
1343 |
+
'readonly' => true,
|
1344 |
),
|
1345 |
'password' => array(
|
1346 |
+
'description' => __( 'A password to protect access to the post.' ),
|
1347 |
'type' => 'string',
|
1348 |
'context' => array( 'edit' ),
|
1349 |
),
|
1350 |
'slug' => array(
|
1351 |
+
'description' => __( 'An alphanumeric identifier for the object unique to its type.' ),
|
1352 |
'type' => 'string',
|
1353 |
'context' => array( 'view', 'edit', 'embed' ),
|
1354 |
+
'arg_options' => array(
|
1355 |
+
'sanitize_callback' => 'sanitize_title',
|
1356 |
+
),
|
1357 |
),
|
1358 |
'status' => array(
|
1359 |
+
'description' => __( 'A named status for the object.' ),
|
1360 |
'type' => 'string',
|
1361 |
'enum' => array_keys( get_post_stati( array( 'internal' => false ) ) ),
|
1362 |
'context' => array( 'edit' ),
|
1363 |
),
|
1364 |
'type' => array(
|
1365 |
+
'description' => __( 'Type of Post for the object.' ),
|
1366 |
'type' => 'string',
|
1367 |
'context' => array( 'view', 'edit', 'embed' ),
|
1368 |
'readonly' => true,
|
1373 |
$post_type_obj = get_post_type_object( $this->post_type );
|
1374 |
if ( $post_type_obj->hierarchical ) {
|
1375 |
$schema['properties']['parent'] = array(
|
1376 |
+
'description' => __( 'The id for the parent of the object.' ),
|
1377 |
'type' => 'integer',
|
1378 |
'context' => array( 'view', 'edit' ),
|
1379 |
);
|
1429 |
|
1430 |
case 'title':
|
1431 |
$schema['properties']['title'] = array(
|
1432 |
+
'description' => __( 'The title for the object.' ),
|
1433 |
'type' => 'object',
|
1434 |
'context' => array( 'view', 'edit', 'embed' ),
|
1435 |
'properties' => array(
|
1436 |
'raw' => array(
|
1437 |
+
'description' => __( 'Title for the object, as it exists in the database.' ),
|
1438 |
'type' => 'string',
|
1439 |
'context' => array( 'edit' ),
|
1440 |
),
|
1441 |
'rendered' => array(
|
1442 |
+
'description' => __( 'Title for the object, transformed for display.' ),
|
1443 |
'type' => 'string',
|
1444 |
'context' => array( 'view', 'edit', 'embed' ),
|
1445 |
),
|
1449 |
|
1450 |
case 'editor':
|
1451 |
$schema['properties']['content'] = array(
|
1452 |
+
'description' => __( 'The content for the object.' ),
|
1453 |
'type' => 'object',
|
1454 |
'context' => array( 'view', 'edit' ),
|
1455 |
'properties' => array(
|
1456 |
'raw' => array(
|
1457 |
+
'description' => __( 'Content for the object, as it exists in the database.' ),
|
1458 |
'type' => 'string',
|
1459 |
'context' => array( 'edit' ),
|
1460 |
),
|
1461 |
'rendered' => array(
|
1462 |
+
'description' => __( 'Content for the object, transformed for display.' ),
|
1463 |
'type' => 'string',
|
1464 |
'context' => array( 'view', 'edit' ),
|
1465 |
),
|
1469 |
|
1470 |
case 'author':
|
1471 |
$schema['properties']['author'] = array(
|
1472 |
+
'description' => __( 'The id for the author of the object.' ),
|
1473 |
'type' => 'integer',
|
1474 |
'context' => array( 'view', 'edit', 'embed' ),
|
1475 |
);
|
1477 |
|
1478 |
case 'excerpt':
|
1479 |
$schema['properties']['excerpt'] = array(
|
1480 |
+
'description' => __( 'The excerpt for the object.' ),
|
1481 |
'type' => 'object',
|
1482 |
'context' => array( 'view', 'edit', 'embed' ),
|
1483 |
'properties' => array(
|
1484 |
'raw' => array(
|
1485 |
+
'description' => __( 'Excerpt for the object, as it exists in the database.' ),
|
1486 |
'type' => 'string',
|
1487 |
'context' => array( 'edit' ),
|
1488 |
),
|
1489 |
'rendered' => array(
|
1490 |
+
'description' => __( 'Excerpt for the object, transformed for display.' ),
|
1491 |
'type' => 'string',
|
1492 |
'context' => array( 'view', 'edit', 'embed' ),
|
1493 |
),
|
1497 |
|
1498 |
case 'thumbnail':
|
1499 |
$schema['properties']['featured_image'] = array(
|
1500 |
+
'description' => __( 'The id of the featured image for the object.' ),
|
1501 |
'type' => 'integer',
|
1502 |
'context' => array( 'view', 'edit' ),
|
1503 |
);
|
1505 |
|
1506 |
case 'comments':
|
1507 |
$schema['properties']['comment_status'] = array(
|
1508 |
+
'description' => __( 'Whether or not comments are open on the object.' ),
|
1509 |
'type' => 'string',
|
1510 |
'enum' => array( 'open', 'closed' ),
|
1511 |
'context' => array( 'view', 'edit' ),
|
1512 |
);
|
1513 |
$schema['properties']['ping_status'] = array(
|
1514 |
+
'description' => __( 'Whether or not the object can be pinged.' ),
|
1515 |
'type' => 'string',
|
1516 |
'enum' => array( 'open', 'closed' ),
|
1517 |
'context' => array( 'view', 'edit' ),
|
1520 |
|
1521 |
case 'page-attributes':
|
1522 |
$schema['properties']['menu_order'] = array(
|
1523 |
+
'description' => __( 'The order of the object in relation to other object of its type.' ),
|
1524 |
'type' => 'integer',
|
1525 |
'context' => array( 'view', 'edit' ),
|
1526 |
);
|
1528 |
|
1529 |
case 'post-formats':
|
1530 |
$schema['properties']['format'] = array(
|
1531 |
+
'description' => __( 'The format for the object.' ),
|
1532 |
'type' => 'string',
|
1533 |
+
'enum' => array_values( get_post_format_slugs() ),
|
1534 |
'context' => array( 'view', 'edit' ),
|
1535 |
);
|
1536 |
break;
|
1540 |
|
1541 |
if ( 'post' === $this->post_type ) {
|
1542 |
$schema['properties']['sticky'] = array(
|
1543 |
+
'description' => __( 'Whether or not the object should be treated as sticky.' ),
|
1544 |
'type' => 'boolean',
|
1545 |
'context' => array( 'view', 'edit' ),
|
1546 |
);
|
1548 |
|
1549 |
if ( 'page' === $this->post_type ) {
|
1550 |
$schema['properties']['template'] = array(
|
1551 |
+
'description' => __( 'The theme file to use to display the object.' ),
|
1552 |
'type' => 'string',
|
1553 |
+
'enum' => array_keys( wp_get_theme()->get_page_templates() ),
|
1554 |
'context' => array( 'view', 'edit' ),
|
1555 |
);
|
1556 |
}
|
1558 |
return $this->add_additional_fields_schema( $schema );
|
1559 |
}
|
1560 |
|
1561 |
+
/**
|
1562 |
+
* Get the query params for collections of attachments.
|
1563 |
+
*
|
1564 |
+
* @return array
|
1565 |
+
*/
|
1566 |
+
public function get_collection_params() {
|
1567 |
+
$params = parent::get_collection_params();
|
1568 |
+
|
1569 |
+
$params['context']['default'] = 'view';
|
1570 |
+
|
1571 |
+
if ( post_type_supports( $this->post_type, 'author' ) ) {
|
1572 |
+
$params['author'] = array(
|
1573 |
+
'description' => __( 'Limit result set to posts assigned to a specific author.' ),
|
1574 |
+
'type' => 'integer',
|
1575 |
+
'default' => null,
|
1576 |
+
'sanitize_callback' => 'absint',
|
1577 |
+
);
|
1578 |
+
}
|
1579 |
+
$params['include'] = array(
|
1580 |
+
'description' => __( 'Limit result set to specific ids.' ),
|
1581 |
+
'type' => 'array',
|
1582 |
+
'default' => array(),
|
1583 |
+
'sanitize_callback' => 'wp_parse_id_list',
|
1584 |
+
);
|
1585 |
+
$params['order'] = array(
|
1586 |
+
'description' => __( 'Order sort attribute ascending or descending.' ),
|
1587 |
+
'type' => 'string',
|
1588 |
+
'default' => 'desc',
|
1589 |
+
'enum' => array( 'asc', 'desc' ),
|
1590 |
+
);
|
1591 |
+
$params['orderby'] = array(
|
1592 |
+
'description' => __( 'Sort collection by object attribute.' ),
|
1593 |
+
'type' => 'string',
|
1594 |
+
'default' => 'date',
|
1595 |
+
'enum' => array(
|
1596 |
+
'date',
|
1597 |
+
'id',
|
1598 |
+
'include',
|
1599 |
+
'title',
|
1600 |
+
'slug',
|
1601 |
+
),
|
1602 |
+
);
|
1603 |
+
|
1604 |
+
$post_type_obj = get_post_type_object( $this->post_type );
|
1605 |
+
if ( $post_type_obj->hierarchical ) {
|
1606 |
+
$params['parent'] = array(
|
1607 |
+
'description' => _( 'Limit result set to that of a specific parent id.' ),
|
1608 |
+
'type' => 'integer',
|
1609 |
+
'sanitize_callback' => 'absint',
|
1610 |
+
'default' => null,
|
1611 |
+
);
|
1612 |
+
}
|
1613 |
+
|
1614 |
+
$params['status'] = array(
|
1615 |
+
'default' => 'attachment' === $this->post_type ? 'inherit' : 'publish',
|
1616 |
+
'description' => __( 'Limit result set to posts assigned a specific status.' ),
|
1617 |
+
'sanitize_callback' => 'sanitize_key',
|
1618 |
+
'type' => 'string',
|
1619 |
+
'validate_callback' => array( $this, 'validate_user_can_query_private_statuses' ),
|
1620 |
+
);
|
1621 |
+
$params['filter'] = array(
|
1622 |
+
'description' => __( 'Use WP Query arguments to modify the response; private query vars require appropriate authorization.' ),
|
1623 |
+
);
|
1624 |
+
return $params;
|
1625 |
+
}
|
1626 |
+
|
1627 |
+
/**
|
1628 |
+
* Validate whether the user can query private statuses
|
1629 |
+
*
|
1630 |
+
* @param mixed $value
|
1631 |
+
* @param WP_REST_Request $request
|
1632 |
+
* @param string $parameter
|
1633 |
+
* @return WP_Error|bool
|
1634 |
+
*/
|
1635 |
+
public function validate_user_can_query_private_statuses( $value, $request, $parameter ) {
|
1636 |
+
if ( 'publish' === $value || ( 'attachment' === $this->post_type && 'inherit' === $value ) ) {
|
1637 |
+
return true;
|
1638 |
+
}
|
1639 |
+
$post_type_obj = get_post_type_object( $this->post_type );
|
1640 |
+
if ( current_user_can( $post_type_obj->cap->edit_posts ) ) {
|
1641 |
+
return true;
|
1642 |
+
}
|
1643 |
+
return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden' ), array( 'status' => rest_authorization_required_code() ) );
|
1644 |
+
}
|
1645 |
+
|
1646 |
}
|
lib/endpoints/class-wp-rest-posts-terms-controller.php
CHANGED
@@ -16,39 +16,44 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
|
|
16 |
*/
|
17 |
public function register_routes() {
|
18 |
|
19 |
-
$base
|
|
|
20 |
|
21 |
-
$
|
22 |
-
register_rest_route( 'wp/v2', sprintf( '/%s/(?P<post_id>[\d]+)/terms/%s', $base, $this->taxonomy ), 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' => $
|
28 |
),
|
|
|
29 |
) );
|
30 |
|
31 |
-
register_rest_route( 'wp/v2', sprintf( '/%s/(?P<post_id>[\d]+)
|
32 |
array(
|
33 |
'methods' => WP_REST_Server::READABLE,
|
34 |
'callback' => array( $this, 'get_item' ),
|
35 |
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
|
|
|
|
|
|
36 |
),
|
37 |
array(
|
38 |
'methods' => WP_REST_Server::CREATABLE,
|
39 |
'callback' => array( $this, 'create_item' ),
|
40 |
-
'permission_callback' => array( $this, '
|
41 |
),
|
42 |
array(
|
43 |
'methods' => WP_REST_Server::DELETABLE,
|
44 |
'callback' => array( $this, 'delete_item' ),
|
45 |
-
'permission_callback' => array( $this, '
|
|
|
|
|
|
|
|
|
|
|
46 |
),
|
47 |
-
|
48 |
-
|
49 |
-
register_rest_route( 'wp/v2', sprintf( '/%s/(?P<post_id>[\d]+)/terms/%s', $base, $this->taxonomy ) . '/schema', array(
|
50 |
-
'methods' => WP_REST_Server::READABLE,
|
51 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
52 |
) );
|
53 |
}
|
54 |
|
@@ -101,11 +106,11 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
|
|
101 |
|
102 |
$terms = wp_get_object_terms( $post->ID, $this->taxonomy );
|
103 |
|
104 |
-
if ( ! in_array( $term_id, wp_list_pluck( $terms, '
|
105 |
-
return new WP_Error( 'rest_post_not_in_term', __( 'Invalid taxonomy for post
|
106 |
}
|
107 |
|
108 |
-
$term = $this->terms_controller->prepare_item_for_response(
|
109 |
|
110 |
$response = rest_ensure_response( $term );
|
111 |
|
@@ -127,17 +132,27 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
|
|
127 |
return $is_request_valid;
|
128 |
}
|
129 |
|
130 |
-
$
|
|
|
131 |
|
132 |
if ( is_wp_error( $tt_ids ) ) {
|
133 |
return $tt_ids;
|
134 |
}
|
135 |
|
136 |
-
$term = $this->terms_controller->prepare_item_for_response(
|
137 |
|
138 |
$response = rest_ensure_response( $term );
|
139 |
$response->set_status( 201 );
|
140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
return $term;
|
142 |
}
|
143 |
|
@@ -170,6 +185,15 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
|
|
170 |
return $remove;
|
171 |
}
|
172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
return $previous_item;
|
174 |
}
|
175 |
|
@@ -185,23 +209,25 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
|
|
185 |
/**
|
186 |
* Validate the API request for relationship requests.
|
187 |
*
|
188 |
-
* @param WP_REST_Request $request
|
189 |
* @return WP_Error|true
|
190 |
*/
|
191 |
protected function validate_request( $request ) {
|
|
|
192 |
|
193 |
-
$
|
194 |
-
|
|
|
195 |
|
196 |
-
|
197 |
-
|
198 |
-
return $post_check;
|
199 |
}
|
200 |
|
201 |
if ( ! empty( $request['term_id'] ) ) {
|
202 |
$term_id = absint( $request['term_id'] );
|
203 |
|
204 |
-
|
|
|
205 |
return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
|
206 |
}
|
207 |
}
|
@@ -239,12 +265,17 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
|
|
239 |
}
|
240 |
|
241 |
/**
|
242 |
-
* Check if a given request has access to
|
243 |
*
|
244 |
* @param WP_REST_Request $request Full details about the request.
|
245 |
* @return bool|WP_Error
|
246 |
*/
|
247 |
-
public function
|
|
|
|
|
|
|
|
|
|
|
248 |
|
249 |
$post_request = new WP_REST_Request();
|
250 |
$post_request->set_param( 'id', $request['post_id'] );
|
@@ -264,14 +295,15 @@ class WP_REST_Posts_Terms_Controller extends WP_REST_Controller {
|
|
264 |
*/
|
265 |
public function get_collection_params() {
|
266 |
$query_params = array();
|
|
|
267 |
$query_params['order'] = array(
|
268 |
-
'description' => 'Order sort attribute ascending or descending.',
|
269 |
'type' => 'string',
|
270 |
'default' => 'asc',
|
271 |
'enum' => array( 'asc', 'desc' ),
|
272 |
);
|
273 |
$query_params['orderby'] = array(
|
274 |
-
'description' => 'Sort collection by object attribute.',
|
275 |
'type' => 'string',
|
276 |
'default' => 'name',
|
277 |
'enum' => array(
|
16 |
*/
|
17 |
public function register_routes() {
|
18 |
|
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,
|
43 |
'callback' => array( $this, 'create_item' ),
|
44 |
+
'permission_callback' => array( $this, 'manage_item_permissions_check' ),
|
45 |
),
|
46 |
array(
|
47 |
'methods' => WP_REST_Server::DELETABLE,
|
48 |
'callback' => array( $this, 'delete_item' ),
|
49 |
+
'permission_callback' => array( $this, 'manage_item_permissions_check' ),
|
50 |
+
'args' => array(
|
51 |
+
'force' => array(
|
52 |
+
'default' => false,
|
53 |
+
),
|
54 |
+
),
|
55 |
),
|
56 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
|
|
|
|
|
|
|
|
57 |
) );
|
58 |
}
|
59 |
|
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 |
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 );
|
146 |
|
147 |
+
/**
|
148 |
+
* Fires after a term is added to a post via the REST API.
|
149 |
+
*
|
150 |
+
* @param array $term The added term data.
|
151 |
+
* @param WP_Post $post The post the term was added to.
|
152 |
+
* @param WP_REST_Request $request The request sent to the API.
|
153 |
+
*/
|
154 |
+
do_action( 'rest_insert_term', $term, $post, $request );
|
155 |
+
|
156 |
return $term;
|
157 |
}
|
158 |
|
185 |
return $remove;
|
186 |
}
|
187 |
|
188 |
+
/**
|
189 |
+
* Fires after a term is removed from a post via the REST API.
|
190 |
+
*
|
191 |
+
* @param array $previous_item The removed term data.
|
192 |
+
* @param WP_Post $post The post the term was removed from.
|
193 |
+
* @param WP_REST_Request $request The request sent to the API.
|
194 |
+
*/
|
195 |
+
do_action( 'rest_remove_term', $previous_item, $post, $request );
|
196 |
+
|
197 |
return $previous_item;
|
198 |
}
|
199 |
|
209 |
/**
|
210 |
* Validate the API request for relationship requests.
|
211 |
*
|
212 |
+
* @param WP_REST_Request $request Full data about the request.
|
213 |
* @return WP_Error|true
|
214 |
*/
|
215 |
protected function validate_request( $request ) {
|
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 |
}
|
233 |
}
|
265 |
}
|
266 |
|
267 |
/**
|
268 |
+
* Check if a given request has access to manage a post/term relationship.
|
269 |
*
|
270 |
* @param WP_REST_Request $request Full details about the request.
|
271 |
* @return bool|WP_Error
|
272 |
*/
|
273 |
+
public function manage_item_permissions_check( $request ) {
|
274 |
+
|
275 |
+
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
276 |
+
if ( ! current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
|
277 |
+
return new WP_Error( 'rest_cannot_assign', __( 'Sorry, you are not allowed to assign terms.' ), array( 'status' => rest_authorization_required_code() ) );
|
278 |
+
}
|
279 |
|
280 |
$post_request = new WP_REST_Request();
|
281 |
$post_request->set_param( 'id', $request['post_id'] );
|
295 |
*/
|
296 |
public function get_collection_params() {
|
297 |
$query_params = array();
|
298 |
+
$query_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
299 |
$query_params['order'] = array(
|
300 |
+
'description' => __( 'Order sort attribute ascending or descending.' ),
|
301 |
'type' => 'string',
|
302 |
'default' => 'asc',
|
303 |
'enum' => array( 'asc', 'desc' ),
|
304 |
);
|
305 |
$query_params['orderby'] = array(
|
306 |
+
'description' => __( 'Sort collection by object attribute.' ),
|
307 |
'type' => 'string',
|
308 |
'default' => 'name',
|
309 |
'enum' => array(
|
lib/endpoints/class-wp-rest-revisions-controller.php
CHANGED
@@ -18,14 +18,14 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
18 |
public function register_routes() {
|
19 |
|
20 |
register_rest_route( 'wp/v2', '/' . $this->parent_base . '/(?P<parent_id>[\d]+)/revisions', array(
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
'
|
26 |
-
'default' => 'view',
|
27 |
-
),
|
28 |
),
|
|
|
|
|
29 |
) );
|
30 |
|
31 |
register_rest_route( 'wp/v2', '/' . $this->parent_base . '/(?P<parent_id>[\d]+)/revisions/(?P<id>[\d]+)', array(
|
@@ -34,9 +34,7 @@ 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' => array(
|
38 |
-
'default' => 'view',
|
39 |
-
),
|
40 |
),
|
41 |
),
|
42 |
array(
|
@@ -44,12 +42,9 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
44 |
'callback' => array( $this, 'delete_item' ),
|
45 |
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
46 |
),
|
47 |
-
));
|
48 |
|
49 |
-
|
50 |
-
|
51 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
52 |
-
) );
|
53 |
|
54 |
}
|
55 |
|
@@ -63,16 +58,17 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
63 |
|
64 |
$parent = get_post( $request['parent_id'] );
|
65 |
if ( ! $request['parent_id'] || ! $parent || $this->parent_post_type !== $parent->post_type ) {
|
66 |
-
return new WP_Error( 'rest_post_invalid_parent_id', __( 'Invalid post parent
|
67 |
}
|
68 |
|
69 |
$revisions = wp_get_post_revisions( $request['parent_id'] );
|
70 |
|
71 |
-
$
|
72 |
foreach ( $revisions as $revision ) {
|
73 |
-
$
|
|
|
74 |
}
|
75 |
-
return $
|
76 |
}
|
77 |
|
78 |
/**
|
@@ -89,7 +85,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
89 |
}
|
90 |
$parent_post_type_obj = get_post_type_object( $parent->post_type );
|
91 |
if ( ! current_user_can( $parent_post_type_obj->cap->edit_post, $parent->ID ) ) {
|
92 |
-
return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot view revisions of this post.' ), array( 'status' =>
|
93 |
}
|
94 |
|
95 |
return true;
|
@@ -105,12 +101,12 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
105 |
|
106 |
$parent = get_post( $request['parent_id'] );
|
107 |
if ( ! $request['parent_id'] || ! $parent || $this->parent_post_type !== $parent->post_type ) {
|
108 |
-
return new WP_Error( 'rest_post_invalid_parent_id', __( 'Invalid post parent
|
109 |
}
|
110 |
|
111 |
$revision = get_post( $request['id'] );
|
112 |
if ( ! $revision || 'revision' !== $revision->post_type ) {
|
113 |
-
return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision
|
114 |
}
|
115 |
|
116 |
$response = $this->prepare_item_for_response( $revision, $request );
|
@@ -135,6 +131,17 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
135 |
*/
|
136 |
public function delete_item( $request ) {
|
137 |
$result = wp_delete_post( $request['id'], true );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
if ( $result ) {
|
139 |
return true;
|
140 |
} else {
|
@@ -163,7 +170,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
163 |
/**
|
164 |
* Prepare the revision for the REST response
|
165 |
*
|
166 |
-
* @param
|
167 |
* @param WP_REST_Request $request Request object.
|
168 |
* @return array
|
169 |
*/
|
@@ -208,7 +215,16 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
208 |
$response->add_link( 'parent', rest_url( sprintf( 'wp/%s/%d', $this->parent_base, $data['parent'] ) ) );
|
209 |
}
|
210 |
|
211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
}
|
213 |
|
214 |
/**
|
@@ -225,10 +241,10 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
225 |
}
|
226 |
|
227 |
if ( isset( $date ) ) {
|
228 |
-
return
|
229 |
}
|
230 |
|
231 |
-
return
|
232 |
}
|
233 |
|
234 |
/**
|
@@ -246,51 +262,51 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
246 |
*/
|
247 |
'properties' => array(
|
248 |
'author' => array(
|
249 |
-
'description' => 'The
|
250 |
'type' => 'integer',
|
251 |
'context' => array( 'view' ),
|
252 |
),
|
253 |
'date' => array(
|
254 |
-
'description' => 'The date the object was published.',
|
255 |
'type' => 'string',
|
256 |
'format' => 'date-time',
|
257 |
'context' => array( 'view' ),
|
258 |
),
|
259 |
'date_gmt' => array(
|
260 |
-
'description' => 'The date the object was published, as GMT.',
|
261 |
'type' => 'string',
|
262 |
'format' => 'date-time',
|
263 |
'context' => array( 'view' ),
|
264 |
),
|
265 |
'guid' => array(
|
266 |
-
'description' => 'GUID for the object, as it exists in the database.',
|
267 |
'type' => 'string',
|
268 |
'context' => array( 'view' ),
|
269 |
),
|
270 |
'id' => array(
|
271 |
-
'description' => 'Unique identifier for the object.',
|
272 |
'type' => 'integer',
|
273 |
'context' => array( 'view' ),
|
274 |
),
|
275 |
'modified' => array(
|
276 |
-
'description' => 'The date the object was last modified.',
|
277 |
'type' => 'string',
|
278 |
'format' => 'date-time',
|
279 |
'context' => array( 'view' ),
|
280 |
),
|
281 |
'modified_gmt' => array(
|
282 |
-
'description' => 'The date the object was last modified, as GMT.',
|
283 |
'type' => 'string',
|
284 |
'format' => 'date-time',
|
285 |
'context' => array( 'view' ),
|
286 |
),
|
287 |
'parent' => array(
|
288 |
-
'description' => 'The
|
289 |
'type' => 'integer',
|
290 |
'context' => array( 'view' ),
|
291 |
),
|
292 |
'slug' => array(
|
293 |
-
'description' => 'An alphanumeric identifier for the object unique to its type.',
|
294 |
'type' => 'string',
|
295 |
'context' => array( 'view' ),
|
296 |
),
|
@@ -308,7 +324,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
308 |
|
309 |
case 'title':
|
310 |
$schema['properties']['title'] = array(
|
311 |
-
'description' => 'Title for the object, as it exists in the database.',
|
312 |
'type' => 'string',
|
313 |
'context' => array( 'view' ),
|
314 |
);
|
@@ -316,7 +332,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
316 |
|
317 |
case 'content':
|
318 |
$schema['properties']['content'] = array(
|
319 |
-
'description' => 'Content for the object, as it exists in the database.',
|
320 |
'type' => 'string',
|
321 |
'context' => array( 'view' ),
|
322 |
);
|
@@ -324,7 +340,7 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
324 |
|
325 |
case 'excerpt':
|
326 |
$schema['properties']['excerpt'] = array(
|
327 |
-
'description' => 'Excerpt for the object, as it exists in the database.',
|
328 |
'type' => 'string',
|
329 |
'context' => array( 'view' ),
|
330 |
);
|
@@ -336,4 +352,15 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller {
|
|
336 |
return $this->add_additional_fields_schema( $schema );
|
337 |
}
|
338 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
339 |
}
|
18 |
public function register_routes() {
|
19 |
|
20 |
register_rest_route( 'wp/v2', '/' . $this->parent_base . '/(?P<parent_id>[\d]+)/revisions', 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' => $this->get_collection_params(),
|
|
|
|
|
26 |
),
|
27 |
+
|
28 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
29 |
) );
|
30 |
|
31 |
register_rest_route( 'wp/v2', '/' . $this->parent_base . '/(?P<parent_id>[\d]+)/revisions/(?P<id>[\d]+)', array(
|
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(
|
42 |
'callback' => array( $this, 'delete_item' ),
|
43 |
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
44 |
),
|
|
|
45 |
|
46 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
47 |
+
));
|
|
|
|
|
48 |
|
49 |
}
|
50 |
|
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'] );
|
65 |
|
66 |
+
$response = array();
|
67 |
foreach ( $revisions as $revision ) {
|
68 |
+
$data = $this->prepare_item_for_response( $revision, $request );
|
69 |
+
$response[] = $this->prepare_response_for_collection( $data );
|
70 |
}
|
71 |
+
return $response;
|
72 |
}
|
73 |
|
74 |
/**
|
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 |
|
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 );
|
131 |
*/
|
132 |
public function delete_item( $request ) {
|
133 |
$result = wp_delete_post( $request['id'], true );
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Fires after a revision is deleted via the REST API.
|
137 |
+
*
|
138 |
+
* @param (mixed) $result The revision object (if it was deleted or moved to the trash successfully)
|
139 |
+
* or false (failure). If the revision was moved to to the trash, $result represents
|
140 |
+
* its new state; if it was deleted, $result represents its state before deletion.
|
141 |
+
* @param WP_REST_Request $request The request sent to the API.
|
142 |
+
*/
|
143 |
+
do_action( 'rest_delete_revision', $result, $request );
|
144 |
+
|
145 |
if ( $result ) {
|
146 |
return true;
|
147 |
} else {
|
170 |
/**
|
171 |
* Prepare the revision for the REST response
|
172 |
*
|
173 |
+
* @param WP_Post $post Post revision object.
|
174 |
* @param WP_REST_Request $request Request object.
|
175 |
* @return array
|
176 |
*/
|
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 |
/**
|
241 |
}
|
242 |
|
243 |
if ( isset( $date ) ) {
|
244 |
+
return mysql_to_rfc3339( $date );
|
245 |
}
|
246 |
|
247 |
+
return mysql_to_rfc3339( $date_gmt );
|
248 |
}
|
249 |
|
250 |
/**
|
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 |
),
|
269 |
'date' => array(
|
270 |
+
'description' => __( 'The date the object was published.' ),
|
271 |
'type' => 'string',
|
272 |
'format' => 'date-time',
|
273 |
'context' => array( 'view' ),
|
274 |
),
|
275 |
'date_gmt' => array(
|
276 |
+
'description' => __( 'The date the object was published, as GMT.' ),
|
277 |
'type' => 'string',
|
278 |
'format' => 'date-time',
|
279 |
'context' => array( 'view' ),
|
280 |
),
|
281 |
'guid' => array(
|
282 |
+
'description' => __( 'GUID for the object, as it exists in the database.' ),
|
283 |
'type' => 'string',
|
284 |
'context' => array( 'view' ),
|
285 |
),
|
286 |
'id' => array(
|
287 |
+
'description' => __( 'Unique identifier for the object.' ),
|
288 |
'type' => 'integer',
|
289 |
'context' => array( 'view' ),
|
290 |
),
|
291 |
'modified' => array(
|
292 |
+
'description' => __( 'The date the object was last modified.' ),
|
293 |
'type' => 'string',
|
294 |
'format' => 'date-time',
|
295 |
'context' => array( 'view' ),
|
296 |
),
|
297 |
'modified_gmt' => array(
|
298 |
+
'description' => __( 'The date the object was last modified, as GMT.' ),
|
299 |
'type' => 'string',
|
300 |
'format' => 'date-time',
|
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 |
),
|
308 |
'slug' => array(
|
309 |
+
'description' => __( 'An alphanumeric identifier for the object unique to its type.' ),
|
310 |
'type' => 'string',
|
311 |
'context' => array( 'view' ),
|
312 |
),
|
324 |
|
325 |
case 'title':
|
326 |
$schema['properties']['title'] = array(
|
327 |
+
'description' => __( 'Title for the object, as it exists in the database.' ),
|
328 |
'type' => 'string',
|
329 |
'context' => array( 'view' ),
|
330 |
);
|
332 |
|
333 |
case 'content':
|
334 |
$schema['properties']['content'] = array(
|
335 |
+
'description' => __( 'Content for the object, as it exists in the database.' ),
|
336 |
'type' => 'string',
|
337 |
'context' => array( 'view' ),
|
338 |
);
|
340 |
|
341 |
case 'excerpt':
|
342 |
$schema['properties']['excerpt'] = array(
|
343 |
+
'description' => __( 'Excerpt for the object, as it exists in the database.' ),
|
344 |
'type' => 'string',
|
345 |
'context' => array( 'view' ),
|
346 |
);
|
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 |
}
|
lib/endpoints/class-wp-rest-taxonomies-controller.php
CHANGED
@@ -8,22 +8,24 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
|
|
8 |
public function register_routes() {
|
9 |
|
10 |
register_rest_route( 'wp/v2', '/taxonomies', array(
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
'
|
15 |
-
'sanitize_callback' => 'sanitize_key',
|
16 |
-
),
|
17 |
),
|
|
|
18 |
) );
|
19 |
-
|
20 |
-
'methods' => WP_REST_Server::READABLE,
|
21 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
22 |
-
) );
|
23 |
register_rest_route( 'wp/v2', '/taxonomies/(?P<taxonomy>[\w-]+)', array(
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
) );
|
28 |
}
|
29 |
|
@@ -34,18 +36,19 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
|
|
34 |
* @return array
|
35 |
*/
|
36 |
public function get_items( $request ) {
|
37 |
-
if ( ! empty( $request['
|
38 |
-
$taxonomies = get_object_taxonomies( $request['
|
39 |
} else {
|
40 |
$taxonomies = get_taxonomies( '', 'objects' );
|
41 |
}
|
42 |
$data = array();
|
43 |
foreach ( $taxonomies as $tax_type => $value ) {
|
44 |
-
|
45 |
-
if ( is_wp_error( $tax ) ) {
|
46 |
continue;
|
47 |
}
|
48 |
-
$
|
|
|
|
|
49 |
}
|
50 |
return $data;
|
51 |
}
|
@@ -74,8 +77,13 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
|
|
74 |
|
75 |
$tax_obj = get_taxonomy( $request['taxonomy'] );
|
76 |
|
77 |
-
if ( $tax_obj
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
79 |
}
|
80 |
|
81 |
return true;
|
@@ -89,9 +97,6 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
|
|
89 |
* @return array Taxonomy data
|
90 |
*/
|
91 |
public function prepare_item_for_response( $taxonomy, $request ) {
|
92 |
-
if ( empty( $taxonomy->show_in_rest ) ) {
|
93 |
-
return new WP_Error( 'rest_cannot_read_taxonomy', __( 'Cannot view taxonomy' ), array( 'status' => 403 ) );
|
94 |
-
}
|
95 |
|
96 |
$data = array(
|
97 |
'name' => $taxonomy->label,
|
@@ -107,7 +112,29 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
|
|
107 |
$data = $this->filter_response_by_context( $data, $context );
|
108 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
109 |
|
110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
}
|
112 |
|
113 |
/**
|
@@ -122,43 +149,58 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller {
|
|
122 |
'type' => 'object',
|
123 |
'properties' => array(
|
124 |
'description' => array(
|
125 |
-
'description' => 'A human-readable description of the object.',
|
126 |
'type' => 'string',
|
127 |
-
'context' => array( 'view' ),
|
128 |
),
|
129 |
'hierarchical' => array(
|
130 |
-
'description' => 'Whether or not the type should have children.',
|
131 |
'type' => 'boolean',
|
132 |
-
'context' => array( 'view' ),
|
133 |
),
|
134 |
'labels' => array(
|
135 |
-
'description' => 'Human-readable labels for the type for various contexts.',
|
136 |
'type' => 'object',
|
137 |
-
'context' => array( '
|
138 |
),
|
139 |
'name' => array(
|
140 |
-
'description' => 'The title for the object.',
|
141 |
'type' => 'string',
|
142 |
-
'context' => array( 'view' ),
|
143 |
),
|
144 |
'slug' => array(
|
145 |
-
'description' => 'An alphanumeric identifier for the object.',
|
146 |
'type' => 'string',
|
147 |
-
'context' => array( 'view' ),
|
148 |
),
|
149 |
'show_cloud' => array(
|
150 |
-
'description' => 'Whether or not the term cloud should be displayed.',
|
151 |
'type' => 'boolean',
|
152 |
-
'context' => array( '
|
153 |
),
|
154 |
'types' => array(
|
155 |
-
'description' => 'Types associated with taxonomy.',
|
156 |
'type' => 'array',
|
157 |
-
'context' => array( 'view' ),
|
158 |
),
|
159 |
),
|
160 |
);
|
161 |
return $this->add_additional_fields_schema( $schema );
|
162 |
}
|
163 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
}
|
8 |
public function register_routes() {
|
9 |
|
10 |
register_rest_route( 'wp/v2', '/taxonomies', array(
|
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 |
) );
|
18 |
+
|
|
|
|
|
|
|
19 |
register_rest_route( 'wp/v2', '/taxonomies/(?P<taxonomy>[\w-]+)', array(
|
20 |
+
array(
|
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 |
) );
|
30 |
}
|
31 |
|
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 |
}
|
44 |
$data = array();
|
45 |
foreach ( $taxonomies as $tax_type => $value ) {
|
46 |
+
if ( empty( $value->show_in_rest ) || ( 'edit' === $request['context'] && ! current_user_can( $value->cap->manage_terms ) ) ) {
|
|
|
47 |
continue;
|
48 |
}
|
49 |
+
$tax = $this->prepare_item_for_response( $value, $request );
|
50 |
+
$tax = $this->prepare_response_for_collection( $tax );
|
51 |
+
$data[ $tax_type ] = $tax;
|
52 |
}
|
53 |
return $data;
|
54 |
}
|
77 |
|
78 |
$tax_obj = get_taxonomy( $request['taxonomy'] );
|
79 |
|
80 |
+
if ( $tax_obj ) {
|
81 |
+
if ( empty( $tax_obj->show_in_rest ) ) {
|
82 |
+
return false;
|
83 |
+
}
|
84 |
+
if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->manage_terms ) ) {
|
85 |
+
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to manage this taxonomy.' ), array( 'status' => rest_authorization_required_code() ) );
|
86 |
+
}
|
87 |
}
|
88 |
|
89 |
return true;
|
97 |
* @return array Taxonomy data
|
98 |
*/
|
99 |
public function prepare_item_for_response( $taxonomy, $request ) {
|
|
|
|
|
|
|
100 |
|
101 |
$data = array(
|
102 |
'name' => $taxonomy->label,
|
112 |
$data = $this->filter_response_by_context( $data, $context );
|
113 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
114 |
|
115 |
+
// Wrap the data in a response object.
|
116 |
+
$response = rest_ensure_response( $data );
|
117 |
+
|
118 |
+
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
|
119 |
+
$response->add_links( array(
|
120 |
+
'collection' => array(
|
121 |
+
'href' => rest_url( 'wp/v2/taxonomies' ),
|
122 |
+
),
|
123 |
+
'https://api.w.org/items' => array(
|
124 |
+
'href' => rest_url( sprintf( 'wp/v2/%s', $base ) ),
|
125 |
+
),
|
126 |
+
) );
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Filter a taxonomy returned from the API.
|
130 |
+
*
|
131 |
+
* Allows modification of the taxonomy data right before it is returned.
|
132 |
+
*
|
133 |
+
* @param WP_REST_Response $response The response object.
|
134 |
+
* @param object $item The original taxonomy object.
|
135 |
+
* @param WP_REST_Request $request Request used to generate the response.
|
136 |
+
*/
|
137 |
+
return apply_filters( 'rest_prepare_taxonomy', $response, $taxonomy, $request );
|
138 |
}
|
139 |
|
140 |
/**
|
149 |
'type' => 'object',
|
150 |
'properties' => array(
|
151 |
'description' => array(
|
152 |
+
'description' => __( 'A human-readable description of the object.' ),
|
153 |
'type' => 'string',
|
154 |
+
'context' => array( 'view', 'edit' ),
|
155 |
),
|
156 |
'hierarchical' => array(
|
157 |
+
'description' => __( 'Whether or not the type should have children.' ),
|
158 |
'type' => 'boolean',
|
159 |
+
'context' => array( 'view', 'edit' ),
|
160 |
),
|
161 |
'labels' => array(
|
162 |
+
'description' => __( 'Human-readable labels for the type for various contexts.' ),
|
163 |
'type' => 'object',
|
164 |
+
'context' => array( 'edit' ),
|
165 |
),
|
166 |
'name' => array(
|
167 |
+
'description' => __( 'The title for the object.' ),
|
168 |
'type' => 'string',
|
169 |
+
'context' => array( 'view', 'edit' ),
|
170 |
),
|
171 |
'slug' => array(
|
172 |
+
'description' => __( 'An alphanumeric identifier for the object.' ),
|
173 |
'type' => 'string',
|
174 |
+
'context' => array( 'view', 'edit' ),
|
175 |
),
|
176 |
'show_cloud' => array(
|
177 |
+
'description' => __( 'Whether or not the term cloud should be displayed.' ),
|
178 |
'type' => 'boolean',
|
179 |
+
'context' => array( 'edit' ),
|
180 |
),
|
181 |
'types' => array(
|
182 |
+
'description' => __( 'Types associated with taxonomy.' ),
|
183 |
'type' => 'array',
|
184 |
+
'context' => array( 'view', 'edit' ),
|
185 |
),
|
186 |
),
|
187 |
);
|
188 |
return $this->add_additional_fields_schema( $schema );
|
189 |
}
|
190 |
|
191 |
+
/**
|
192 |
+
* Get the query params for collections
|
193 |
+
*
|
194 |
+
* @return array
|
195 |
+
*/
|
196 |
+
public function get_collection_params() {
|
197 |
+
$new_params = array();
|
198 |
+
$new_params['context'] = $this->get_context_param( array( 'default' => 'view' ) );
|
199 |
+
$new_params['type'] = array(
|
200 |
+
'description' => __( 'Limit results to taxonomies associated with a specific post type.' ),
|
201 |
+
'type' => 'string',
|
202 |
+
);
|
203 |
+
return $new_params;
|
204 |
+
}
|
205 |
+
|
206 |
}
|
lib/endpoints/class-wp-rest-terms-controller.php
CHANGED
@@ -20,65 +20,44 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
20 |
public function register_routes() {
|
21 |
|
22 |
$base = $this->get_taxonomy_base( $this->taxonomy );
|
23 |
-
|
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' => $
|
30 |
),
|
31 |
array(
|
32 |
'methods' => WP_REST_Server::CREATABLE,
|
33 |
'callback' => array( $this, 'create_item' ),
|
34 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
35 |
-
'args' =>
|
36 |
-
'name' => array(
|
37 |
-
'required' => true,
|
38 |
-
'sanitize_callback' => 'sanitize_text_field',
|
39 |
-
),
|
40 |
-
'description' => array(
|
41 |
-
'sanitize_callback' => 'wp_filter_post_kses',
|
42 |
-
),
|
43 |
-
'slug' => array(
|
44 |
-
'sanitize_callback' => 'sanitize_title',
|
45 |
-
),
|
46 |
-
'parent' => array(),
|
47 |
-
),
|
48 |
),
|
|
|
|
|
49 |
));
|
50 |
-
register_rest_route( 'wp/v2', '/
|
51 |
array(
|
52 |
'methods' => WP_REST_Server::READABLE,
|
53 |
'callback' => array( $this, 'get_item' ),
|
54 |
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
|
|
|
|
|
|
55 |
),
|
56 |
array(
|
57 |
'methods' => WP_REST_Server::EDITABLE,
|
58 |
'callback' => array( $this, 'update_item' ),
|
59 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
60 |
-
'args'
|
61 |
-
'name' => array(
|
62 |
-
'sanitize_callback' => 'sanitize_text_field',
|
63 |
-
),
|
64 |
-
'description' => array(
|
65 |
-
'sanitize_callback' => 'wp_filter_post_kses',
|
66 |
-
),
|
67 |
-
'slug' => array(
|
68 |
-
'sanitize_callback' => 'sanitize_title',
|
69 |
-
),
|
70 |
-
'parent' => array(),
|
71 |
-
),
|
72 |
),
|
73 |
array(
|
74 |
'methods' => WP_REST_Server::DELETABLE,
|
75 |
'callback' => array( $this, 'delete_item' ),
|
76 |
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
77 |
),
|
78 |
-
|
79 |
-
|
80 |
-
'methods' => WP_REST_Server::READABLE,
|
81 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
82 |
) );
|
83 |
}
|
84 |
|
@@ -89,22 +68,44 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
89 |
* @return WP_REST_Response|WP_Error
|
90 |
*/
|
91 |
public function get_items( $request ) {
|
92 |
-
$prepared_args = array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
|
94 |
-
$prepared_args['number'] = $request['per_page'];
|
95 |
$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
|
96 |
-
$prepared_args['search'] = $request['search'];
|
97 |
-
$prepared_args['order'] = $request['order'];
|
98 |
-
$prepared_args['orderby'] = $request['orderby'];
|
99 |
|
100 |
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
|
|
101 |
if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) {
|
102 |
-
|
103 |
-
|
104 |
-
$prepared_args['parent'] =
|
|
|
|
|
|
|
|
|
105 |
}
|
106 |
}
|
107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
$query_result = get_terms( $this->taxonomy, $prepared_args );
|
109 |
$response = array();
|
110 |
foreach ( $query_result as $term ) {
|
@@ -113,24 +114,35 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
113 |
}
|
114 |
|
115 |
$response = rest_ensure_response( $response );
|
|
|
|
|
|
|
|
|
116 |
unset( $prepared_args['number'] );
|
117 |
unset( $prepared_args['offset'] );
|
|
|
118 |
$total_terms = wp_count_terms( $this->taxonomy, $prepared_args );
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
$response->header( 'X-WP-Total', (int) $total_terms );
|
120 |
-
$max_pages = ceil( $total_terms / $
|
121 |
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
122 |
|
123 |
-
$base = add_query_arg( $request->get_query_params(), rest_url( '/wp/v2/
|
124 |
-
if ( $
|
125 |
-
$prev_page = $
|
126 |
if ( $prev_page > $max_pages ) {
|
127 |
$prev_page = $max_pages;
|
128 |
}
|
129 |
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
130 |
$response->link_header( 'prev', $prev_link );
|
131 |
}
|
132 |
-
if ( $max_pages > $
|
133 |
-
$next_page = $
|
134 |
$next_link = add_query_arg( 'page', $next_page, $base );
|
135 |
$response->link_header( 'next', $next_link );
|
136 |
}
|
@@ -146,8 +158,8 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
146 |
*/
|
147 |
public function get_item( $request ) {
|
148 |
|
149 |
-
$term =
|
150 |
-
if ( ! $term ) {
|
151 |
return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
|
152 |
}
|
153 |
if ( is_wp_error( $term ) ) {
|
@@ -182,7 +194,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
182 |
return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set term parent, taxonomy is not hierarchical.' ), array( 'status' => 400 ) );
|
183 |
}
|
184 |
|
185 |
-
$parent =
|
186 |
|
187 |
if ( ! $parent ) {
|
188 |
return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 404 ) );
|
@@ -193,16 +205,28 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
193 |
|
194 |
$term = wp_insert_term( $name, $this->taxonomy, $args );
|
195 |
if ( is_wp_error( $term ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
return $term;
|
197 |
}
|
198 |
|
199 |
$this->update_additional_fields_for_object( $term, $request );
|
200 |
|
201 |
-
$
|
202 |
-
|
203 |
-
|
204 |
|
205 |
-
|
|
|
|
|
|
|
206 |
}
|
207 |
|
208 |
/**
|
@@ -229,7 +253,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
229 |
return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set term parent, taxonomy is not hierarchical.' ), array( 'status' => 400 ) );
|
230 |
}
|
231 |
|
232 |
-
$parent =
|
233 |
|
234 |
if ( ! $parent ) {
|
235 |
return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 400 ) );
|
@@ -238,10 +262,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
238 |
$prepared_args['parent'] = $parent->term_id;
|
239 |
}
|
240 |
|
241 |
-
$term =
|
242 |
-
if ( ! $term ) {
|
243 |
-
return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
|
244 |
-
}
|
245 |
|
246 |
// Only update the term if we haz something to update.
|
247 |
if ( ! empty( $prepared_args ) ) {
|
@@ -251,11 +272,11 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
251 |
}
|
252 |
}
|
253 |
|
254 |
-
$this->update_additional_fields_for_object(
|
255 |
|
256 |
-
$
|
257 |
-
|
258 |
-
|
259 |
|
260 |
return rest_ensure_response( $response );
|
261 |
}
|
@@ -264,16 +285,24 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
264 |
* Delete a single term from a taxonomy
|
265 |
*
|
266 |
* @param WP_REST_Request $request Full details about the request
|
267 |
-
* @return
|
268 |
*/
|
269 |
public function delete_item( $request ) {
|
270 |
|
271 |
// Get the actual term_id
|
272 |
-
$term =
|
273 |
-
$get_request = new WP_REST_Request
|
|
|
274 |
$get_request->set_param( 'context', 'view' );
|
275 |
$response = $this->prepare_item_for_response( $term, $get_request );
|
276 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
$retval = wp_delete_term( $term->term_id, $term->taxonomy );
|
278 |
if ( ! $retval ) {
|
279 |
return new WP_Error( 'rest_cannot_delete', __( 'The term cannot be deleted.' ), array( 'status' => 500 ) );
|
@@ -289,18 +318,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
289 |
* @return bool|WP_Error
|
290 |
*/
|
291 |
public function get_items_permissions_check( $request ) {
|
292 |
-
|
293 |
-
$valid = $this->check_valid_taxonomy( $this->taxonomy );
|
294 |
-
if ( is_wp_error( $valid ) ) {
|
295 |
-
return $valid;
|
296 |
-
}
|
297 |
-
|
298 |
-
$tax_obj = get_taxonomy( $this->taxonomy );
|
299 |
-
if ( $tax_obj && false === $tax_obj->public ) {
|
300 |
-
return false;
|
301 |
-
}
|
302 |
-
|
303 |
-
return true;
|
304 |
}
|
305 |
|
306 |
/**
|
@@ -310,18 +328,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
310 |
* @return bool|WP_Error
|
311 |
*/
|
312 |
public function get_item_permissions_check( $request ) {
|
313 |
-
|
314 |
-
$valid = $this->check_valid_taxonomy( $this->taxonomy );
|
315 |
-
if ( is_wp_error( $valid ) ) {
|
316 |
-
return $valid;
|
317 |
-
}
|
318 |
-
|
319 |
-
$tax_obj = get_taxonomy( $this->taxonomy );
|
320 |
-
if ( $tax_obj && false === $tax_obj->public ) {
|
321 |
-
return false;
|
322 |
-
}
|
323 |
-
|
324 |
-
return true;
|
325 |
}
|
326 |
|
327 |
|
@@ -333,14 +340,13 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
333 |
*/
|
334 |
public function create_item_permissions_check( $request ) {
|
335 |
|
336 |
-
|
337 |
-
|
338 |
-
return $valid;
|
339 |
}
|
340 |
|
341 |
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
342 |
if ( ! current_user_can( $taxonomy_obj->cap->manage_terms ) ) {
|
343 |
-
return
|
344 |
}
|
345 |
|
346 |
return true;
|
@@ -354,14 +360,18 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
354 |
*/
|
355 |
public function update_item_permissions_check( $request ) {
|
356 |
|
357 |
-
|
358 |
-
|
359 |
-
|
|
|
|
|
|
|
|
|
360 |
}
|
361 |
|
362 |
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
363 |
-
if (
|
364 |
-
return
|
365 |
}
|
366 |
|
367 |
return true;
|
@@ -375,19 +385,18 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
375 |
*/
|
376 |
public function delete_item_permissions_check( $request ) {
|
377 |
|
378 |
-
|
379 |
-
|
380 |
-
return $valid;
|
381 |
}
|
382 |
|
383 |
-
$term =
|
384 |
if ( ! $term ) {
|
385 |
return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
|
386 |
}
|
387 |
|
388 |
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
389 |
-
if (
|
390 |
-
return
|
391 |
}
|
392 |
|
393 |
return true;
|
@@ -417,16 +426,8 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
417 |
*/
|
418 |
public function prepare_item_for_response( $item, $request ) {
|
419 |
|
420 |
-
$parent_id = 0;
|
421 |
-
if ( $item->parent ) {
|
422 |
-
$parent_term = get_term_by( 'id', (int) $item->parent, $item->taxonomy );
|
423 |
-
if ( $parent_term ) {
|
424 |
-
$parent_id = $parent_term->term_taxonomy_id;
|
425 |
-
}
|
426 |
-
}
|
427 |
-
|
428 |
$data = array(
|
429 |
-
'id' => (int) $item->
|
430 |
'count' => (int) $item->count,
|
431 |
'description' => $item->description,
|
432 |
'link' => get_term_link( $item ),
|
@@ -436,18 +437,27 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
436 |
);
|
437 |
$schema = $this->get_item_schema();
|
438 |
if ( ! empty( $schema['properties']['parent'] ) ) {
|
439 |
-
$data['parent'] = (int) $
|
440 |
}
|
441 |
|
442 |
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
443 |
$data = $this->filter_response_by_context( $data, $context );
|
444 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
445 |
|
446 |
-
$
|
447 |
|
448 |
-
$
|
449 |
|
450 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
451 |
}
|
452 |
|
453 |
/**
|
@@ -457,21 +467,24 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
457 |
* @return array Links for the given term.
|
458 |
*/
|
459 |
protected function prepare_links( $term ) {
|
460 |
-
$base = '/wp/v2/
|
461 |
$links = array(
|
462 |
'self' => array(
|
463 |
-
'href' => rest_url( trailingslashit( $base ) . $term->
|
464 |
),
|
465 |
'collection' => array(
|
466 |
'href' => rest_url( $base ),
|
467 |
),
|
|
|
|
|
|
|
468 |
);
|
469 |
|
470 |
if ( $term->parent ) {
|
471 |
-
$parent_term =
|
472 |
if ( $parent_term ) {
|
473 |
$links['up'] = array(
|
474 |
-
'href' => rest_url( sprintf( 'wp/v2
|
475 |
'embeddable' => true,
|
476 |
);
|
477 |
}
|
@@ -492,52 +505,62 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
492 |
'type' => 'object',
|
493 |
'properties' => array(
|
494 |
'id' => array(
|
495 |
-
'description' => 'Unique identifier for the object.',
|
496 |
'type' => 'integer',
|
497 |
'context' => array( 'view', 'embed' ),
|
498 |
'readonly' => true,
|
499 |
-
|
500 |
'count' => array(
|
501 |
-
'description' => 'Number of published posts for the object.',
|
502 |
'type' => 'integer',
|
503 |
'context' => array( 'view' ),
|
504 |
'readonly' => true,
|
505 |
-
|
506 |
'description' => array(
|
507 |
-
'description' => 'A human-readable description of the object.',
|
508 |
'type' => 'string',
|
509 |
'context' => array( 'view' ),
|
|
|
|
|
510 |
),
|
|
|
511 |
'link' => array(
|
512 |
-
'description' => 'URL to the object.',
|
513 |
'type' => 'string',
|
514 |
'format' => 'uri',
|
515 |
'context' => array( 'view', 'embed' ),
|
516 |
'readonly' => true,
|
517 |
-
|
518 |
'name' => array(
|
519 |
-
'description' => 'The title for the object.',
|
520 |
'type' => 'string',
|
521 |
'context' => array( 'view', 'embed' ),
|
|
|
|
|
522 |
),
|
|
|
|
|
523 |
'slug' => array(
|
524 |
-
'description' => 'An alphanumeric identifier for the object unique to its type.',
|
525 |
'type' => 'string',
|
526 |
'context' => array( 'view', 'embed' ),
|
|
|
|
|
527 |
),
|
|
|
528 |
'taxonomy' => array(
|
529 |
-
'description' => 'Type attribution for the object.',
|
530 |
'type' => 'string',
|
531 |
'enum' => array_keys( get_taxonomies() ),
|
532 |
'context' => array( 'view', 'embed' ),
|
533 |
'readonly' => true,
|
534 |
-
),
|
535 |
),
|
536 |
-
)
|
|
|
537 |
$taxonomy = get_taxonomy( $this->taxonomy );
|
538 |
if ( $taxonomy->hierarchical ) {
|
539 |
$schema['properties']['parent'] = array(
|
540 |
-
'description' => 'The
|
541 |
'type' => 'integer',
|
542 |
'context' => array( 'view' ),
|
543 |
);
|
@@ -552,26 +575,50 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
552 |
*/
|
553 |
public function get_collection_params() {
|
554 |
$query_params = parent::get_collection_params();
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
560 |
);
|
561 |
-
$query_params['orderby']
|
562 |
-
'description'
|
563 |
-
'type'
|
564 |
-
'
|
565 |
-
'
|
|
|
566 |
'id',
|
|
|
567 |
'name',
|
568 |
'slug',
|
|
|
|
|
|
|
|
|
569 |
),
|
570 |
);
|
|
|
|
|
|
|
|
|
|
|
571 |
$taxonomy = get_taxonomy( $this->taxonomy );
|
572 |
if ( $taxonomy->hierarchical ) {
|
573 |
$query_params['parent'] = array(
|
574 |
-
'description' => 'Limit result set to terms assigned to a specific parent term.',
|
575 |
'type' => 'integer',
|
576 |
'sanitize_callback' => 'absint',
|
577 |
);
|
@@ -585,11 +632,11 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
|
|
585 |
* @param string
|
586 |
* @return bool|WP_Error
|
587 |
*/
|
588 |
-
protected function
|
589 |
-
|
|
|
590 |
return true;
|
591 |
}
|
592 |
-
|
593 |
-
return new WP_Error( 'rest_taxonomy_invalid', __( "Taxonomy doesn't exist" ), array( 'status' => 404 ) );
|
594 |
}
|
595 |
}
|
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,
|
32 |
'callback' => array( $this, 'create_item' ),
|
33 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
34 |
+
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
),
|
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,
|
50 |
'callback' => array( $this, 'update_item' ),
|
51 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
52 |
+
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
),
|
54 |
array(
|
55 |
'methods' => WP_REST_Server::DELETABLE,
|
56 |
'callback' => array( $this, 'delete_item' ),
|
57 |
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
58 |
),
|
59 |
+
|
60 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
|
|
|
|
61 |
) );
|
62 |
}
|
63 |
|
68 |
* @return WP_REST_Response|WP_Error
|
69 |
*/
|
70 |
public function get_items( $request ) {
|
71 |
+
$prepared_args = array(
|
72 |
+
'include' => $request['include'],
|
73 |
+
'order' => $request['order'],
|
74 |
+
'orderby' => $request['orderby'],
|
75 |
+
'hide_empty' => $request['hide_empty'],
|
76 |
+
'number' => $request['per_page'],
|
77 |
+
'search' => $request['search'],
|
78 |
+
);
|
79 |
|
|
|
80 |
$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
|
|
|
|
|
|
|
81 |
|
82 |
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
83 |
+
|
84 |
if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) {
|
85 |
+
if ( 0 === $request['parent'] ) {
|
86 |
+
// Only query top-level terms.
|
87 |
+
$prepared_args['parent'] = 0;
|
88 |
+
} else {
|
89 |
+
if ( $request['parent'] ) {
|
90 |
+
$prepared_args['parent'] = $request['parent'];
|
91 |
+
}
|
92 |
}
|
93 |
}
|
94 |
|
95 |
+
/**
|
96 |
+
* Filter the query arguments, before passing them to `get_terms()`.
|
97 |
+
*
|
98 |
+
* Enables adding extra arguments or setting defaults for a terms
|
99 |
+
* collection request.
|
100 |
+
*
|
101 |
+
* @see https://developer.wordpress.org/reference/functions/get_terms/
|
102 |
+
*
|
103 |
+
* @param array $prepared_args Array of arguments to be
|
104 |
+
* passed to get_terms.
|
105 |
+
* @param WP_REST_Request $request The current request.
|
106 |
+
*/
|
107 |
+
$prepared_args = apply_filters( 'rest_terms_query', $prepared_args, $request );
|
108 |
+
|
109 |
$query_result = get_terms( $this->taxonomy, $prepared_args );
|
110 |
$response = array();
|
111 |
foreach ( $query_result as $term ) {
|
114 |
}
|
115 |
|
116 |
$response = rest_ensure_response( $response );
|
117 |
+
|
118 |
+
// Store pagation values for headers then unset for count query.
|
119 |
+
$per_page = (int) $prepared_args['number'];
|
120 |
+
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
|
121 |
unset( $prepared_args['number'] );
|
122 |
unset( $prepared_args['offset'] );
|
123 |
+
|
124 |
$total_terms = wp_count_terms( $this->taxonomy, $prepared_args );
|
125 |
+
|
126 |
+
// wp_count_terms can return a falsy value when the term has no children
|
127 |
+
if ( ! $total_terms ) {
|
128 |
+
$total_terms = 0;
|
129 |
+
}
|
130 |
+
|
131 |
$response->header( 'X-WP-Total', (int) $total_terms );
|
132 |
+
$max_pages = ceil( $total_terms / $per_page );
|
133 |
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
134 |
|
135 |
+
$base = add_query_arg( $request->get_query_params(), rest_url( '/wp/v2/' . $this->get_taxonomy_base( $this->taxonomy ) ) );
|
136 |
+
if ( $page > 1 ) {
|
137 |
+
$prev_page = $page - 1;
|
138 |
if ( $prev_page > $max_pages ) {
|
139 |
$prev_page = $max_pages;
|
140 |
}
|
141 |
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
142 |
$response->link_header( 'prev', $prev_link );
|
143 |
}
|
144 |
+
if ( $max_pages > $page ) {
|
145 |
+
$next_page = $page + 1;
|
146 |
$next_link = add_query_arg( 'page', $next_page, $base );
|
147 |
$response->link_header( 'next', $next_link );
|
148 |
}
|
158 |
*/
|
159 |
public function get_item( $request ) {
|
160 |
|
161 |
+
$term = get_term( (int) $request['id'], $this->taxonomy );
|
162 |
+
if ( ! $term || $term->taxonomy !== $this->taxonomy ) {
|
163 |
return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
|
164 |
}
|
165 |
if ( is_wp_error( $term ) ) {
|
194 |
return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set term parent, taxonomy is not hierarchical.' ), array( 'status' => 400 ) );
|
195 |
}
|
196 |
|
197 |
+
$parent = get_term( (int) $request['parent'], $this->taxonomy );
|
198 |
|
199 |
if ( ! $parent ) {
|
200 |
return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 404 ) );
|
205 |
|
206 |
$term = wp_insert_term( $name, $this->taxonomy, $args );
|
207 |
if ( is_wp_error( $term ) ) {
|
208 |
+
|
209 |
+
// If we're going to inform the client that the term exists, give them the identifier
|
210 |
+
// they can actually use.
|
211 |
+
|
212 |
+
if ( ( $term_id = $term->get_error_data( 'term_exists' ) ) ) {
|
213 |
+
$existing_term = get_term( $term_id, $this->taxonomy );
|
214 |
+
$term->add_data( $existing_term->term_id, 'term_exists' );
|
215 |
+
}
|
216 |
+
|
217 |
return $term;
|
218 |
}
|
219 |
|
220 |
$this->update_additional_fields_for_object( $term, $request );
|
221 |
|
222 |
+
$get_request = new WP_REST_Request;
|
223 |
+
$get_request->set_param( 'id', $term['term_id'] );
|
224 |
+
$response = $this->get_item( $get_request );
|
225 |
|
226 |
+
$response = rest_ensure_response( $response );
|
227 |
+
$response->set_status( 201 );
|
228 |
+
$response->header( 'Location', rest_url( '/wp/v2/' . $this->get_taxonomy_base( $this->taxonomy ) . '/' . $term['term_id'] ) );
|
229 |
+
return $response;
|
230 |
}
|
231 |
|
232 |
/**
|
253 |
return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set term parent, taxonomy is not hierarchical.' ), array( 'status' => 400 ) );
|
254 |
}
|
255 |
|
256 |
+
$parent = get_term( (int) $request['parent'], $this->taxonomy );
|
257 |
|
258 |
if ( ! $parent ) {
|
259 |
return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 400 ) );
|
262 |
$prepared_args['parent'] = $parent->term_id;
|
263 |
}
|
264 |
|
265 |
+
$term = get_term( (int) $request['id'], $this->taxonomy );
|
|
|
|
|
|
|
266 |
|
267 |
// Only update the term if we haz something to update.
|
268 |
if ( ! empty( $prepared_args ) ) {
|
272 |
}
|
273 |
}
|
274 |
|
275 |
+
$this->update_additional_fields_for_object( get_term( (int) $request['id'], $this->taxonomy ), $request );
|
276 |
|
277 |
+
$get_request = new WP_REST_Request;
|
278 |
+
$get_request->set_param( 'id', (int) $request['id'] );
|
279 |
+
$response = $this->get_item( $get_request );
|
280 |
|
281 |
return rest_ensure_response( $response );
|
282 |
}
|
285 |
* Delete a single term from a taxonomy
|
286 |
*
|
287 |
* @param WP_REST_Request $request Full details about the request
|
288 |
+
* @return WP_REST_Response|WP_Error
|
289 |
*/
|
290 |
public function delete_item( $request ) {
|
291 |
|
292 |
// Get the actual term_id
|
293 |
+
$term = get_term( (int) $request['id'], $this->taxonomy );
|
294 |
+
$get_request = new WP_REST_Request;
|
295 |
+
$get_request->set_param( 'id', (int) $request['id'] );
|
296 |
$get_request->set_param( 'context', 'view' );
|
297 |
$response = $this->prepare_item_for_response( $term, $get_request );
|
298 |
|
299 |
+
$data = $response->get_data();
|
300 |
+
$data = array(
|
301 |
+
'data' => $data,
|
302 |
+
'deleted' => true,
|
303 |
+
);
|
304 |
+
$response->set_data( $data );
|
305 |
+
|
306 |
$retval = wp_delete_term( $term->term_id, $term->taxonomy );
|
307 |
if ( ! $retval ) {
|
308 |
return new WP_Error( 'rest_cannot_delete', __( 'The term cannot be deleted.' ), array( 'status' => 500 ) );
|
318 |
* @return bool|WP_Error
|
319 |
*/
|
320 |
public function get_items_permissions_check( $request ) {
|
321 |
+
return $this->check_is_taxonomy_allowed( $this->taxonomy );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
322 |
}
|
323 |
|
324 |
/**
|
328 |
* @return bool|WP_Error
|
329 |
*/
|
330 |
public function get_item_permissions_check( $request ) {
|
331 |
+
return $this->check_is_taxonomy_allowed( $this->taxonomy );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
332 |
}
|
333 |
|
334 |
|
340 |
*/
|
341 |
public function create_item_permissions_check( $request ) {
|
342 |
|
343 |
+
if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) {
|
344 |
+
return false;
|
|
|
345 |
}
|
346 |
|
347 |
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
348 |
if ( ! current_user_can( $taxonomy_obj->cap->manage_terms ) ) {
|
349 |
+
return new WP_Error( 'rest_cannot_create', __( 'Sorry, you cannot create new terms.' ), array( 'status' => rest_authorization_required_code() ) );
|
350 |
}
|
351 |
|
352 |
return true;
|
360 |
*/
|
361 |
public function update_item_permissions_check( $request ) {
|
362 |
|
363 |
+
if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) {
|
364 |
+
return false;
|
365 |
+
}
|
366 |
+
|
367 |
+
$term = get_term( (int) $request['id'], $this->taxonomy );
|
368 |
+
if ( ! $term ) {
|
369 |
+
return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
|
370 |
}
|
371 |
|
372 |
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
373 |
+
if ( ! current_user_can( $taxonomy_obj->cap->edit_terms ) ) {
|
374 |
+
return new WP_Error( 'rest_cannot_update', __( 'Sorry, you cannot update terms.' ), array( 'status' => rest_authorization_required_code() ) );
|
375 |
}
|
376 |
|
377 |
return true;
|
385 |
*/
|
386 |
public function delete_item_permissions_check( $request ) {
|
387 |
|
388 |
+
if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) {
|
389 |
+
return false;
|
|
|
390 |
}
|
391 |
|
392 |
+
$term = get_term( (int) $request['id'], $this->taxonomy );
|
393 |
if ( ! $term ) {
|
394 |
return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) );
|
395 |
}
|
396 |
|
397 |
$taxonomy_obj = get_taxonomy( $this->taxonomy );
|
398 |
+
if ( ! current_user_can( $taxonomy_obj->cap->delete_terms ) ) {
|
399 |
+
return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you cannot delete terms.' ), array( 'status' => rest_authorization_required_code() ) );
|
400 |
}
|
401 |
|
402 |
return true;
|
426 |
*/
|
427 |
public function prepare_item_for_response( $item, $request ) {
|
428 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
429 |
$data = array(
|
430 |
+
'id' => (int) $item->term_id,
|
431 |
'count' => (int) $item->count,
|
432 |
'description' => $item->description,
|
433 |
'link' => get_term_link( $item ),
|
437 |
);
|
438 |
$schema = $this->get_item_schema();
|
439 |
if ( ! empty( $schema['properties']['parent'] ) ) {
|
440 |
+
$data['parent'] = (int) $item->parent;
|
441 |
}
|
442 |
|
443 |
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
|
444 |
$data = $this->filter_response_by_context( $data, $context );
|
445 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
446 |
|
447 |
+
$response = rest_ensure_response( $data );
|
448 |
|
449 |
+
$response->add_links( $this->prepare_links( $item ) );
|
450 |
|
451 |
+
/**
|
452 |
+
* Filter a term item returned from the API.
|
453 |
+
*
|
454 |
+
* Allows modification of the term data right before it is returned.
|
455 |
+
*
|
456 |
+
* @param WP_REST_Response $response The response object.
|
457 |
+
* @param object $item The original term object.
|
458 |
+
* @param WP_REST_Request $request Request used to generate the response.
|
459 |
+
*/
|
460 |
+
return apply_filters( 'rest_prepare_term', $response, $item, $request );
|
461 |
}
|
462 |
|
463 |
/**
|
467 |
* @return array Links for the given term.
|
468 |
*/
|
469 |
protected function prepare_links( $term ) {
|
470 |
+
$base = '/wp/v2/' . $this->get_taxonomy_base( $term->taxonomy );
|
471 |
$links = array(
|
472 |
'self' => array(
|
473 |
+
'href' => rest_url( trailingslashit( $base ) . $term->term_id ),
|
474 |
),
|
475 |
'collection' => array(
|
476 |
'href' => rest_url( $base ),
|
477 |
),
|
478 |
+
'about' => array(
|
479 |
+
'href' => rest_url( sprintf( 'wp/v2/taxonomies/%s', $this->taxonomy ) ),
|
480 |
+
),
|
481 |
);
|
482 |
|
483 |
if ( $term->parent ) {
|
484 |
+
$parent_term = get_term( (int) $term->parent, $term->taxonomy );
|
485 |
if ( $parent_term ) {
|
486 |
$links['up'] = array(
|
487 |
+
'href' => rest_url( sprintf( 'wp/v2/%s/%d', $this->get_taxonomy_base( $parent_term->taxonomy ), $parent_term->term_id ) ),
|
488 |
'embeddable' => true,
|
489 |
);
|
490 |
}
|
505 |
'type' => 'object',
|
506 |
'properties' => array(
|
507 |
'id' => array(
|
508 |
+
'description' => __( 'Unique identifier for the object.' ),
|
509 |
'type' => 'integer',
|
510 |
'context' => array( 'view', 'embed' ),
|
511 |
'readonly' => true,
|
512 |
+
),
|
513 |
'count' => array(
|
514 |
+
'description' => __( 'Number of published posts for the object.' ),
|
515 |
'type' => 'integer',
|
516 |
'context' => array( 'view' ),
|
517 |
'readonly' => true,
|
518 |
+
),
|
519 |
'description' => array(
|
520 |
+
'description' => __( 'A human-readable description of the object.' ),
|
521 |
'type' => 'string',
|
522 |
'context' => array( 'view' ),
|
523 |
+
'arg_options' => array(
|
524 |
+
'sanitize_callback' => 'wp_filter_post_kses',
|
525 |
),
|
526 |
+
),
|
527 |
'link' => array(
|
528 |
+
'description' => __( 'URL to the object.' ),
|
529 |
'type' => 'string',
|
530 |
'format' => 'uri',
|
531 |
'context' => array( 'view', 'embed' ),
|
532 |
'readonly' => true,
|
533 |
+
),
|
534 |
'name' => array(
|
535 |
+
'description' => __( 'The title for the object.' ),
|
536 |
'type' => 'string',
|
537 |
'context' => array( 'view', 'embed' ),
|
538 |
+
'arg_options' => array(
|
539 |
+
'sanitize_callback' => 'sanitize_text_field',
|
540 |
),
|
541 |
+
'required' => true,
|
542 |
+
),
|
543 |
'slug' => array(
|
544 |
+
'description' => __( 'An alphanumeric identifier for the object unique to its type.' ),
|
545 |
'type' => 'string',
|
546 |
'context' => array( 'view', 'embed' ),
|
547 |
+
'arg_options' => array(
|
548 |
+
'sanitize_callback' => 'sanitize_title',
|
549 |
),
|
550 |
+
),
|
551 |
'taxonomy' => array(
|
552 |
+
'description' => __( 'Type attribution for the object.' ),
|
553 |
'type' => 'string',
|
554 |
'enum' => array_keys( get_taxonomies() ),
|
555 |
'context' => array( 'view', 'embed' ),
|
556 |
'readonly' => true,
|
|
|
557 |
),
|
558 |
+
),
|
559 |
+
);
|
560 |
$taxonomy = get_taxonomy( $this->taxonomy );
|
561 |
if ( $taxonomy->hierarchical ) {
|
562 |
$schema['properties']['parent'] = array(
|
563 |
+
'description' => __( 'The id for the parent of the object.' ),
|
564 |
'type' => 'integer',
|
565 |
'context' => array( 'view' ),
|
566 |
);
|
575 |
*/
|
576 |
public function get_collection_params() {
|
577 |
$query_params = parent::get_collection_params();
|
578 |
+
|
579 |
+
$query_params['context']['default'] = 'view';
|
580 |
+
|
581 |
+
$query_params['include'] = array(
|
582 |
+
'description' => __( 'Limit result set to specific ids.' ),
|
583 |
+
'type' => 'array',
|
584 |
+
'default' => array(),
|
585 |
+
'sanitize_callback' => 'wp_parse_id_list',
|
586 |
+
);
|
587 |
+
$query_params['order'] = array(
|
588 |
+
'description' => __( 'Order sort attribute ascending or descending.' ),
|
589 |
+
'type' => 'string',
|
590 |
+
'sanitize_callback' => 'sanitize_key',
|
591 |
+
'default' => 'asc',
|
592 |
+
'enum' => array(
|
593 |
+
'asc',
|
594 |
+
'desc',
|
595 |
+
),
|
596 |
);
|
597 |
+
$query_params['orderby'] = array(
|
598 |
+
'description' => __( 'Sort collection by object attribute.' ),
|
599 |
+
'type' => 'string',
|
600 |
+
'sanitize_callback' => 'sanitize_key',
|
601 |
+
'default' => 'name',
|
602 |
+
'enum' => array(
|
603 |
'id',
|
604 |
+
'include',
|
605 |
'name',
|
606 |
'slug',
|
607 |
+
'term_group',
|
608 |
+
'term_id',
|
609 |
+
'description',
|
610 |
+
'count',
|
611 |
),
|
612 |
);
|
613 |
+
$query_params['hide_empty'] = array(
|
614 |
+
'description' => __( 'Whether to hide terms not assigned to any posts.' ),
|
615 |
+
'type' => 'boolean',
|
616 |
+
'default' => false,
|
617 |
+
);
|
618 |
$taxonomy = get_taxonomy( $this->taxonomy );
|
619 |
if ( $taxonomy->hierarchical ) {
|
620 |
$query_params['parent'] = array(
|
621 |
+
'description' => __( 'Limit result set to terms assigned to a specific parent term.' ),
|
622 |
'type' => 'integer',
|
623 |
'sanitize_callback' => 'absint',
|
624 |
);
|
632 |
* @param string
|
633 |
* @return bool|WP_Error
|
634 |
*/
|
635 |
+
protected function check_is_taxonomy_allowed( $taxonomy ) {
|
636 |
+
$taxonomy_obj = get_taxonomy( $taxonomy );
|
637 |
+
if ( $taxonomy_obj && ! empty( $taxonomy_obj->show_in_rest ) ) {
|
638 |
return true;
|
639 |
}
|
640 |
+
return false;
|
|
|
641 |
}
|
642 |
}
|
lib/endpoints/class-wp-rest-users-controller.php
CHANGED
@@ -10,24 +10,24 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
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 |
-
'
|
19 |
-
'args' => $query_params,
|
20 |
),
|
21 |
array(
|
22 |
'methods' => WP_REST_Server::CREATABLE,
|
23 |
'callback' => array( $this, 'create_item' ),
|
24 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
25 |
-
'args' => array_merge( $this->get_endpoint_args_for_item_schema(
|
26 |
'password' => array(
|
27 |
'required' => true,
|
28 |
),
|
29 |
) ),
|
30 |
),
|
|
|
|
|
31 |
) );
|
32 |
register_rest_route( 'wp/v2', '/users/(?P<id>[\d]+)', array(
|
33 |
array(
|
@@ -35,16 +35,14 @@ 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' => array(
|
39 |
-
'default' => 'embed',
|
40 |
-
),
|
41 |
),
|
42 |
),
|
43 |
array(
|
44 |
'methods' => WP_REST_Server::EDITABLE,
|
45 |
'callback' => array( $this, 'update_item' ),
|
46 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
47 |
-
'args' => array_merge( $this->get_endpoint_args_for_item_schema(
|
48 |
'password' => array(),
|
49 |
) ),
|
50 |
),
|
@@ -53,9 +51,14 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
53 |
'callback' => array( $this, 'delete_item' ),
|
54 |
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
55 |
'args' => array(
|
|
|
|
|
|
|
56 |
'reassign' => array(),
|
57 |
),
|
58 |
),
|
|
|
|
|
59 |
) );
|
60 |
|
61 |
register_rest_route( 'wp/v2', '/users/me', array(
|
@@ -64,12 +67,8 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
64 |
'args' => array(
|
65 |
'context' => array(),
|
66 |
),
|
|
|
67 |
));
|
68 |
-
|
69 |
-
register_rest_route( 'wp/v2', '/users/schema', array(
|
70 |
-
'methods' => WP_REST_Server::READABLE,
|
71 |
-
'callback' => array( $this, 'get_public_item_schema' ),
|
72 |
-
) );
|
73 |
}
|
74 |
|
75 |
/**
|
@@ -81,17 +80,38 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
81 |
public function get_items( $request ) {
|
82 |
|
83 |
$prepared_args = array();
|
|
|
84 |
$prepared_args['order'] = $request['order'];
|
85 |
$prepared_args['number'] = $request['per_page'];
|
86 |
$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
|
87 |
$orderby_possibles = array(
|
88 |
'id' => 'ID',
|
|
|
89 |
'name' => 'display_name',
|
90 |
'registered_date' => 'registered',
|
91 |
-
|
92 |
$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
|
93 |
$prepared_args['search'] = $request['search'];
|
94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
$prepared_args = apply_filters( 'rest_user_query', $prepared_args, $request );
|
96 |
|
97 |
$query = new WP_User_Query( $prepared_args );
|
@@ -106,25 +126,32 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
106 |
}
|
107 |
|
108 |
$response = rest_ensure_response( $users );
|
|
|
|
|
|
|
|
|
109 |
unset( $prepared_args['number'] );
|
110 |
unset( $prepared_args['offset'] );
|
|
|
|
|
|
|
111 |
$count_query = new WP_User_Query( $prepared_args );
|
112 |
$total_users = $count_query->get_total();
|
113 |
$response->header( 'X-WP-Total', (int) $total_users );
|
114 |
-
$max_pages = ceil( $total_users / $
|
115 |
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
116 |
|
117 |
$base = add_query_arg( $request->get_query_params(), rest_url( '/wp/v2/users' ) );
|
118 |
-
if ( $
|
119 |
-
$prev_page = $
|
120 |
if ( $prev_page > $max_pages ) {
|
121 |
$prev_page = $max_pages;
|
122 |
}
|
123 |
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
124 |
$response->link_header( 'prev', $prev_link );
|
125 |
}
|
126 |
-
if ( $max_pages > $
|
127 |
-
$next_page = $
|
128 |
$next_link = add_query_arg( 'page', $next_page, $base );
|
129 |
$response->link_header( 'next', $next_link );
|
130 |
}
|
@@ -143,7 +170,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
143 |
$user = get_userdata( $id );
|
144 |
|
145 |
if ( empty( $id ) || empty( $user->ID ) ) {
|
146 |
-
return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user
|
147 |
}
|
148 |
|
149 |
$user = $this->prepare_item_for_response( $user, $request );
|
@@ -164,10 +191,10 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
164 |
return new WP_Error( 'rest_not_logged_in', __( 'You are not currently logged in.' ), array( 'status' => 401 ) );
|
165 |
}
|
166 |
|
167 |
-
$
|
168 |
-
|
169 |
-
|
170 |
-
)
|
171 |
if ( is_wp_error( $response ) ) {
|
172 |
return $response;
|
173 |
}
|
@@ -192,10 +219,6 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
192 |
return new WP_Error( 'rest_user_exists', __( 'Cannot create existing user.' ), array( 'status' => 400 ) );
|
193 |
}
|
194 |
|
195 |
-
if ( ! empty( $request['role'] ) && ! isset( $wp_roles->role_objects[ $request['role'] ] ) ) {
|
196 |
-
return new WP_Error( 'rest_user_invalid_role', __( 'Role is invalid.' ), array( 'status' => 400 ) );
|
197 |
-
}
|
198 |
-
|
199 |
$user = $this->prepare_item_for_database( $request );
|
200 |
|
201 |
if ( is_multisite() ) {
|
@@ -225,12 +248,19 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
225 |
|
226 |
$this->update_additional_fields_for_object( $user, $request );
|
227 |
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
234 |
$response = rest_ensure_response( $response );
|
235 |
$response->set_status( 201 );
|
236 |
$response->header( 'Location', rest_url( '/wp/v2/users/' . $user_id ) );
|
@@ -249,7 +279,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
249 |
|
250 |
$user = get_userdata( $id );
|
251 |
if ( ! $user ) {
|
252 |
-
return new WP_Error( 'rest_user_invalid_id', __( 'User
|
253 |
}
|
254 |
|
255 |
if ( email_exists( $request['email'] ) && $request['email'] !== $user->user_email ) {
|
@@ -283,16 +313,15 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
283 |
|
284 |
$this->update_additional_fields_for_object( $user, $request );
|
285 |
|
|
|
286 |
do_action( 'rest_insert_user', $user, $request, false );
|
287 |
|
288 |
-
$
|
289 |
-
|
290 |
-
|
291 |
-
)
|
292 |
-
$response = rest_ensure_response( $response );
|
293 |
-
$response->header( 'Location', rest_url( '/wp/v2/users/' . $user_id ) );
|
294 |
|
295 |
-
return $response;
|
296 |
}
|
297 |
|
298 |
/**
|
@@ -308,46 +337,50 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
308 |
|
309 |
// We don't support trashing for this type, error out
|
310 |
if ( ! $force ) {
|
311 |
-
return new WP_Error( 'rest_trash_not_supported', __( '
|
312 |
}
|
313 |
|
314 |
$user = get_userdata( $id );
|
315 |
if ( ! $user ) {
|
316 |
-
return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user
|
317 |
}
|
318 |
|
319 |
if ( ! empty( $reassign ) ) {
|
320 |
if ( $reassign === $id || ! get_userdata( $reassign ) ) {
|
321 |
-
return new WP_Error( 'rest_user_invalid_reassign', __( 'Invalid user
|
322 |
}
|
323 |
}
|
324 |
|
325 |
-
$get_request = new WP_REST_Request
|
|
|
326 |
$get_request->set_param( 'context', 'edit' );
|
327 |
$orig_user = $this->prepare_item_for_response( $user, $get_request );
|
328 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
329 |
$result = wp_delete_user( $id, $reassign );
|
330 |
|
331 |
if ( ! $result ) {
|
332 |
return new WP_Error( 'rest_cannot_delete', __( 'The user cannot be deleted.' ), array( 'status' => 500 ) );
|
333 |
}
|
334 |
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
* @return bool
|
343 |
-
*/
|
344 |
-
public function get_items_permissions_check( $request ) {
|
345 |
|
346 |
-
|
347 |
-
return false;
|
348 |
-
}
|
349 |
-
|
350 |
-
return true;
|
351 |
}
|
352 |
|
353 |
/**
|
@@ -362,7 +395,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
362 |
$user = get_userdata( $id );
|
363 |
|
364 |
if ( empty( $id ) || empty( $user->ID ) ) {
|
365 |
-
return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user
|
366 |
}
|
367 |
|
368 |
if ( get_current_user_id() === $id ) {
|
@@ -372,11 +405,11 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
372 |
$context = ! empty( $request['context'] ) && in_array( $request['context'], array( 'edit', 'view', 'embed' ) ) ? $request['context'] : 'embed';
|
373 |
|
374 |
if ( 'edit' === $context && ! current_user_can( 'edit_user', $id ) ) {
|
375 |
-
return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user with edit context' ), array( 'status' =>
|
376 |
} else if ( 'view' === $context && ! current_user_can( 'list_users' ) ) {
|
377 |
-
return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user with view context' ), array( 'status' =>
|
378 |
} else if ( 'embed' === $context && ! count_user_posts( $id ) && ! current_user_can( 'edit_user', $id ) && ! current_user_can( 'list_users' ) ) {
|
379 |
-
return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user' ), array( 'status' =>
|
380 |
}
|
381 |
|
382 |
return true;
|
@@ -391,7 +424,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
391 |
public function create_item_permissions_check( $request ) {
|
392 |
|
393 |
if ( ! current_user_can( 'create_users' ) ) {
|
394 |
-
return new WP_Error( 'rest_cannot_create_user', __( 'Sorry, you are not allowed to create users.' ), array( 'status' =>
|
395 |
}
|
396 |
|
397 |
return true;
|
@@ -408,11 +441,11 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
408 |
$id = (int) $request['id'];
|
409 |
|
410 |
if ( ! current_user_can( 'edit_user', $id ) ) {
|
411 |
-
return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit users.' ), array( 'status' =>
|
412 |
}
|
413 |
|
414 |
if ( ! empty( $request['role'] ) && ! current_user_can( 'edit_users' ) ) {
|
415 |
-
return new WP_Error( 'rest_cannot_edit_roles', __( 'Sorry, you are not allowed to edit roles of users.' ), array( 'status' =>
|
416 |
}
|
417 |
|
418 |
return true;
|
@@ -430,7 +463,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
430 |
$reassign = isset( $request['reassign'] ) ? absint( $request['reassign'] ) : null;
|
431 |
|
432 |
if ( ! current_user_can( 'delete_user', $id ) ) {
|
433 |
-
return new WP_Error( 'rest_user_cannot_delete', __( 'Sorry, you are not allowed to delete this user.' ), array( 'status' =>
|
434 |
}
|
435 |
|
436 |
return true;
|
@@ -441,26 +474,26 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
441 |
*
|
442 |
* @param object $user User object.
|
443 |
* @param WP_REST_Request $request Request object.
|
444 |
-
* @return
|
445 |
*/
|
446 |
public function prepare_item_for_response( $user, $request ) {
|
447 |
$data = array(
|
448 |
-
'avatar_urls' => rest_get_avatar_urls( $user->user_email ),
|
449 |
-
'capabilities' => $user->allcaps,
|
450 |
-
'description' => $user->description,
|
451 |
-
'email' => $user->user_email,
|
452 |
-
'extra_capabilities' => $user->caps,
|
453 |
-
'first_name' => $user->first_name,
|
454 |
'id' => $user->ID,
|
|
|
|
|
|
|
455 |
'last_name' => $user->last_name,
|
|
|
|
|
|
|
456 |
'link' => get_author_posts_url( $user->ID ),
|
457 |
-
'
|
458 |
'nickname' => $user->nickname,
|
|
|
459 |
'registered_date' => date( 'c', strtotime( $user->user_registered ) ),
|
460 |
'roles' => $user->roles,
|
461 |
-
'
|
462 |
-
'
|
463 |
-
'username' => $user->user_login,
|
464 |
);
|
465 |
|
466 |
$context = ! empty( $request['context'] ) ? $request['context'] : 'embed';
|
@@ -469,11 +502,18 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
469 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
470 |
|
471 |
// Wrap the data in a response object
|
472 |
-
$
|
473 |
-
|
474 |
-
$
|
475 |
-
|
476 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
477 |
}
|
478 |
|
479 |
/**
|
@@ -538,12 +578,18 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
538 |
$prepared_user->description = $request['description'];
|
539 |
}
|
540 |
if ( isset( $request['role'] ) ) {
|
541 |
-
$prepared_user->role =
|
542 |
}
|
543 |
if ( isset( $request['url'] ) ) {
|
544 |
$prepared_user->user_url = $request['url'];
|
545 |
}
|
546 |
|
|
|
|
|
|
|
|
|
|
|
|
|
547 |
return apply_filters( 'rest_pre_insert_user', $prepared_user, $request );
|
548 |
}
|
549 |
|
@@ -557,16 +603,16 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
557 |
protected function check_role_update( $user_id, $role ) {
|
558 |
global $wp_roles;
|
559 |
|
560 |
-
if ( ! isset( $wp_roles->role_objects[ $role ] ) ) {
|
561 |
-
return new WP_Error( 'rest_user_invalid_role', __( 'Role is invalid.' ), array( 'status' => 400 ) );
|
562 |
-
}
|
563 |
-
|
564 |
$potential_role = $wp_roles->role_objects[ $role ];
|
565 |
|
566 |
// Don't let anyone with 'edit_users' (admins) edit their own role to something without it.
|
567 |
// Multisite super admins can freely edit their blog roles -- they possess all caps.
|
568 |
if ( ( is_multisite() && current_user_can( 'manage_sites' ) ) || get_current_user_id() !== $user_id || $potential_role->has_cap( 'edit_users' ) ) {
|
569 |
// The new role must be editable by the logged-in user.
|
|
|
|
|
|
|
|
|
570 |
$editable_roles = get_editable_roles();
|
571 |
if ( empty( $editable_roles[ $role ] ) ) {
|
572 |
return new WP_Error( 'rest_user_invalid_role', __( 'You cannot give users that role.' ), array( 'status' => 403 ) );
|
@@ -575,7 +621,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
575 |
return true;
|
576 |
}
|
577 |
|
578 |
-
return new WP_Error( 'rest_user_invalid_role', __( 'You cannot give users that role.' ), array( 'status' =>
|
579 |
}
|
580 |
|
581 |
/**
|
@@ -589,129 +635,138 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
589 |
$avatar_sizes = rest_get_avatar_sizes();
|
590 |
foreach ( $avatar_sizes as $size ) {
|
591 |
$avatar_properties[ $size ] = array(
|
592 |
-
'description' => 'Avatar URL with image size of
|
593 |
-
'type' => '
|
|
|
594 |
'context' => array( 'embed', 'view', 'edit' ),
|
595 |
);
|
596 |
}
|
597 |
|
|
|
|
|
598 |
$schema = array(
|
599 |
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
600 |
'title' => 'user',
|
601 |
'type' => 'object',
|
602 |
'properties' => array(
|
603 |
-
'
|
604 |
-
'description' => '
|
605 |
-
'type' => '
|
606 |
'context' => array( 'embed', 'view', 'edit' ),
|
607 |
'readonly' => true,
|
608 |
-
'properties' => $avatar_properties,
|
609 |
),
|
610 |
-
'
|
611 |
-
'description' => '
|
612 |
-
'type' => 'object',
|
613 |
-
'context' => array( 'view', 'edit' ),
|
614 |
-
),
|
615 |
-
'description' => array(
|
616 |
-
'description' => 'Description of the object.',
|
617 |
'type' => 'string',
|
618 |
-
'context' => array( '
|
|
|
619 |
'arg_options' => array(
|
620 |
-
'sanitize_callback' => '
|
621 |
),
|
622 |
),
|
623 |
-
'
|
624 |
-
'description' => '
|
625 |
'type' => 'string',
|
626 |
-
'
|
627 |
-
'
|
628 |
-
|
629 |
-
),
|
630 |
-
'extra_capabilities' => array(
|
631 |
-
'description' => 'Any extra capabilities assigned to the user.',
|
632 |
-
'type' => 'object',
|
633 |
-
'context' => array( 'edit' ),
|
634 |
-
'readonly' => true,
|
635 |
),
|
|
|
636 |
'first_name' => array(
|
637 |
-
'description' => 'First name for the object.',
|
638 |
'type' => 'string',
|
639 |
'context' => array( 'view', 'edit' ),
|
640 |
'arg_options' => array(
|
641 |
'sanitize_callback' => 'sanitize_text_field',
|
642 |
),
|
643 |
),
|
644 |
-
'id' => array(
|
645 |
-
'description' => 'Unique identifier for the object.',
|
646 |
-
'type' => 'integer',
|
647 |
-
'context' => array( 'embed', 'view', 'edit' ),
|
648 |
-
'readonly' => true,
|
649 |
-
),
|
650 |
'last_name' => array(
|
651 |
-
'description' => 'Last name for the object.',
|
652 |
'type' => 'string',
|
653 |
'context' => array( 'view', 'edit' ),
|
654 |
'arg_options' => array(
|
655 |
'sanitize_callback' => 'sanitize_text_field',
|
656 |
),
|
657 |
),
|
658 |
-
'
|
659 |
-
'description' => '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
660 |
'type' => 'string',
|
661 |
'format' => 'uri',
|
662 |
'context' => array( 'embed', 'view', 'edit' ),
|
663 |
'readonly' => true,
|
664 |
),
|
665 |
-
'
|
666 |
-
'description' => '
|
667 |
'type' => 'string',
|
668 |
'context' => array( 'embed', 'view', 'edit' ),
|
669 |
'arg_options' => array(
|
670 |
-
'sanitize_callback' => '
|
671 |
),
|
672 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
673 |
'nickname' => array(
|
674 |
-
'description' => 'The nickname for the object.',
|
675 |
'type' => 'string',
|
676 |
'context' => array( 'view', 'edit' ),
|
677 |
'arg_options' => array(
|
678 |
'sanitize_callback' => 'sanitize_text_field',
|
679 |
),
|
680 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
681 |
'registered_date' => array(
|
682 |
-
'description' => 'Registration date for the user.',
|
683 |
'type' => 'date-time',
|
684 |
'context' => array( 'view', 'edit' ),
|
685 |
'readonly' => true,
|
686 |
),
|
687 |
'roles' => array(
|
688 |
-
'description' => 'Roles assigned to the user.',
|
689 |
'type' => 'array',
|
690 |
'context' => array( 'view', 'edit' ),
|
|
|
691 |
),
|
692 |
-
'
|
693 |
-
'description' => '
|
694 |
'type' => 'string',
|
695 |
-
'
|
696 |
-
'arg_options' => array(
|
697 |
-
'sanitize_callback' => 'sanitize_title',
|
698 |
-
),
|
699 |
),
|
700 |
-
'
|
701 |
-
'description' => '
|
702 |
-
'type' => '
|
703 |
-
'
|
704 |
-
'context' => array( 'embed', 'view', 'edit' ),
|
705 |
-
'readonly' => true,
|
706 |
),
|
707 |
-
'
|
708 |
-
'description' => '
|
709 |
-
'type' => '
|
710 |
'context' => array( 'edit' ),
|
711 |
-
'
|
712 |
-
'arg_options' => array(
|
713 |
-
'sanitize_callback' => 'sanitize_user',
|
714 |
-
),
|
715 |
),
|
716 |
),
|
717 |
);
|
@@ -725,24 +780,31 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
|
|
725 |
*/
|
726 |
public function get_collection_params() {
|
727 |
$query_params = parent::get_collection_params();
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
'
|
733 |
-
'type' => '
|
|
|
|
|
734 |
);
|
735 |
$query_params['order'] = array(
|
736 |
'default' => 'asc',
|
737 |
-
'description' => 'Order sort attribute ascending or descending.',
|
738 |
'enum' => array( 'asc', 'desc' ),
|
739 |
'sanitize_callback' => 'sanitize_key',
|
740 |
'type' => 'string',
|
741 |
);
|
742 |
$query_params['orderby'] = array(
|
743 |
'default' => 'name',
|
744 |
-
'description' => 'Sort collection by object attribute.',
|
745 |
-
'enum' => array(
|
|
|
|
|
|
|
|
|
|
|
746 |
'sanitize_callback' => 'sanitize_key',
|
747 |
'type' => 'string',
|
748 |
);
|
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,
|
21 |
'callback' => array( $this, 'create_item' ),
|
22 |
'permission_callback' => array( $this, 'create_item_permissions_check' ),
|
23 |
+
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
|
24 |
'password' => array(
|
25 |
'required' => true,
|
26 |
),
|
27 |
) ),
|
28 |
),
|
29 |
+
|
30 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
31 |
) );
|
32 |
register_rest_route( 'wp/v2', '/users/(?P<id>[\d]+)', array(
|
33 |
array(
|
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(
|
42 |
'methods' => WP_REST_Server::EDITABLE,
|
43 |
'callback' => array( $this, 'update_item' ),
|
44 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
45 |
+
'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), array(
|
46 |
'password' => array(),
|
47 |
) ),
|
48 |
),
|
51 |
'callback' => array( $this, 'delete_item' ),
|
52 |
'permission_callback' => array( $this, 'delete_item_permissions_check' ),
|
53 |
'args' => array(
|
54 |
+
'force' => array(
|
55 |
+
'default' => false,
|
56 |
+
),
|
57 |
'reassign' => array(),
|
58 |
),
|
59 |
),
|
60 |
+
|
61 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
62 |
) );
|
63 |
|
64 |
register_rest_route( 'wp/v2', '/users/me', array(
|
67 |
'args' => array(
|
68 |
'context' => array(),
|
69 |
),
|
70 |
+
'schema' => array( $this, 'get_public_item_schema' ),
|
71 |
));
|
|
|
|
|
|
|
|
|
|
|
72 |
}
|
73 |
|
74 |
/**
|
80 |
public function get_items( $request ) {
|
81 |
|
82 |
$prepared_args = array();
|
83 |
+
$prepared_args['include'] = $request['include'];
|
84 |
$prepared_args['order'] = $request['order'];
|
85 |
$prepared_args['number'] = $request['per_page'];
|
86 |
$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
|
87 |
$orderby_possibles = array(
|
88 |
'id' => 'ID',
|
89 |
+
'include' => 'include',
|
90 |
'name' => 'display_name',
|
91 |
'registered_date' => 'registered',
|
92 |
+
);
|
93 |
$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
|
94 |
$prepared_args['search'] = $request['search'];
|
95 |
|
96 |
+
if ( ! current_user_can( 'list_users' ) ) {
|
97 |
+
$prepared_args['has_published_posts'] = true;
|
98 |
+
|
99 |
+
// Only display a public subset of information
|
100 |
+
$request['context'] = 'embed';
|
101 |
+
}
|
102 |
+
|
103 |
+
if ( '' !== $prepared_args['search'] ) {
|
104 |
+
$prepared_args['search'] = '*' . $prepared_args['search'] . '*';
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Filter arguments, before passing to WP_User_Query, when querying users via the REST API.
|
109 |
+
*
|
110 |
+
* @see https://codex.wordpress.org/Class_Reference/WP_User_Query
|
111 |
+
*
|
112 |
+
* @param array $prepared_args Array of arguments for WP_User_Query.
|
113 |
+
* @param WP_REST_Request $request The current request.
|
114 |
+
*/
|
115 |
$prepared_args = apply_filters( 'rest_user_query', $prepared_args, $request );
|
116 |
|
117 |
$query = new WP_User_Query( $prepared_args );
|
126 |
}
|
127 |
|
128 |
$response = rest_ensure_response( $users );
|
129 |
+
|
130 |
+
// Store pagation values for headers then unset for count query.
|
131 |
+
$per_page = (int) $prepared_args['number'];
|
132 |
+
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
|
133 |
unset( $prepared_args['number'] );
|
134 |
unset( $prepared_args['offset'] );
|
135 |
+
|
136 |
+
$prepared_args['fields'] = 'ID';
|
137 |
+
|
138 |
$count_query = new WP_User_Query( $prepared_args );
|
139 |
$total_users = $count_query->get_total();
|
140 |
$response->header( 'X-WP-Total', (int) $total_users );
|
141 |
+
$max_pages = ceil( $total_users / $per_page );
|
142 |
$response->header( 'X-WP-TotalPages', (int) $max_pages );
|
143 |
|
144 |
$base = add_query_arg( $request->get_query_params(), rest_url( '/wp/v2/users' ) );
|
145 |
+
if ( $page > 1 ) {
|
146 |
+
$prev_page = $page - 1;
|
147 |
if ( $prev_page > $max_pages ) {
|
148 |
$prev_page = $max_pages;
|
149 |
}
|
150 |
$prev_link = add_query_arg( 'page', $prev_page, $base );
|
151 |
$response->link_header( 'prev', $prev_link );
|
152 |
}
|
153 |
+
if ( $max_pages > $page ) {
|
154 |
+
$next_page = $page + 1;
|
155 |
$next_link = add_query_arg( 'page', $next_page, $base );
|
156 |
$response->link_header( 'next', $next_link );
|
157 |
}
|
170 |
$user = get_userdata( $id );
|
171 |
|
172 |
if ( empty( $id ) || empty( $user->ID ) ) {
|
173 |
+
return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user id.' ), array( 'status' => 404 ) );
|
174 |
}
|
175 |
|
176 |
$user = $this->prepare_item_for_response( $user, $request );
|
191 |
return new WP_Error( 'rest_not_logged_in', __( 'You are not currently logged in.' ), array( 'status' => 401 ) );
|
192 |
}
|
193 |
|
194 |
+
$get_request = new WP_REST_Request;
|
195 |
+
$get_request->set_param( 'id', $current_user_id );
|
196 |
+
$get_request->set_param( 'context', $request['context'] );
|
197 |
+
$response = $this->get_item( $get_request );
|
198 |
if ( is_wp_error( $response ) ) {
|
199 |
return $response;
|
200 |
}
|
219 |
return new WP_Error( 'rest_user_exists', __( 'Cannot create existing user.' ), array( 'status' => 400 ) );
|
220 |
}
|
221 |
|
|
|
|
|
|
|
|
|
222 |
$user = $this->prepare_item_for_database( $request );
|
223 |
|
224 |
if ( is_multisite() ) {
|
248 |
|
249 |
$this->update_additional_fields_for_object( $user, $request );
|
250 |
|
251 |
+
/**
|
252 |
+
* Fires after a user is created or updated via the REST API.
|
253 |
+
*
|
254 |
+
* @param object $user Data used to create the user (not a WP_User object).
|
255 |
+
* @param WP_REST_Request $request Request object.
|
256 |
+
* @param bool $creating True when creating user, false when updating user.
|
257 |
+
*/
|
258 |
+
do_action( 'rest_insert_user', $user, $request, true );
|
259 |
+
|
260 |
+
$get_request = new WP_REST_Request;
|
261 |
+
$get_request->set_param( 'id', $user_id );
|
262 |
+
$get_request->set_param( 'context', 'edit' );
|
263 |
+
$response = $this->get_item( $get_request );
|
264 |
$response = rest_ensure_response( $response );
|
265 |
$response->set_status( 201 );
|
266 |
$response->header( 'Location', rest_url( '/wp/v2/users/' . $user_id ) );
|
279 |
|
280 |
$user = get_userdata( $id );
|
281 |
if ( ! $user ) {
|
282 |
+
return new WP_Error( 'rest_user_invalid_id', __( 'User id is invalid.' ), array( 'status' => 400 ) );
|
283 |
}
|
284 |
|
285 |
if ( email_exists( $request['email'] ) && $request['email'] !== $user->user_email ) {
|
313 |
|
314 |
$this->update_additional_fields_for_object( $user, $request );
|
315 |
|
316 |
+
/* This action is documented in lib/endpoints/class-wp-rest-users-controller.php */
|
317 |
do_action( 'rest_insert_user', $user, $request, false );
|
318 |
|
319 |
+
$get_request = new WP_REST_Request;
|
320 |
+
$get_request->set_param( 'id', $user_id );
|
321 |
+
$get_request->set_param( 'context', 'edit' );
|
322 |
+
$response = $this->get_item( $get_request );
|
|
|
|
|
323 |
|
324 |
+
return rest_ensure_response( $response );
|
325 |
}
|
326 |
|
327 |
/**
|
337 |
|
338 |
// We don't support trashing for this type, error out
|
339 |
if ( ! $force ) {
|
340 |
+
return new WP_Error( 'rest_trash_not_supported', __( 'Users do not support trashing.' ), array( 'status' => 501 ) );
|
341 |
}
|
342 |
|
343 |
$user = get_userdata( $id );
|
344 |
if ( ! $user ) {
|
345 |
+
return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user id.' ), array( 'status' => 400 ) );
|
346 |
}
|
347 |
|
348 |
if ( ! empty( $reassign ) ) {
|
349 |
if ( $reassign === $id || ! get_userdata( $reassign ) ) {
|
350 |
+
return new WP_Error( 'rest_user_invalid_reassign', __( 'Invalid user id.' ), array( 'status' => 400 ) );
|
351 |
}
|
352 |
}
|
353 |
|
354 |
+
$get_request = new WP_REST_Request;
|
355 |
+
$get_request->set_param( 'id', $id );
|
356 |
$get_request->set_param( 'context', 'edit' );
|
357 |
$orig_user = $this->prepare_item_for_response( $user, $get_request );
|
358 |
|
359 |
+
$data = $orig_user->get_data();
|
360 |
+
$data = array(
|
361 |
+
'data' => $data,
|
362 |
+
'deleted' => true,
|
363 |
+
);
|
364 |
+
$orig_user->set_data( $data );
|
365 |
+
|
366 |
+
/** Include admin user functions to get access to wp_delete_user() */
|
367 |
+
require_once ABSPATH . 'wp-admin/includes/user.php';
|
368 |
+
|
369 |
$result = wp_delete_user( $id, $reassign );
|
370 |
|
371 |
if ( ! $result ) {
|
372 |
return new WP_Error( 'rest_cannot_delete', __( 'The user cannot be deleted.' ), array( 'status' => 500 ) );
|
373 |
}
|
374 |
|
375 |
+
/**
|
376 |
+
* Fires after a user is deleted via the REST API.
|
377 |
+
*
|
378 |
+
* @param WP_User $user The user data.
|
379 |
+
* @param WP_REST_Request $request The request sent to the API.
|
380 |
+
*/
|
381 |
+
do_action( 'rest_delete_user', $user, $data, $request );
|
|
|
|
|
|
|
382 |
|
383 |
+
return $orig_user;
|
|
|
|
|
|
|
|
|
384 |
}
|
385 |
|
386 |
/**
|
395 |
$user = get_userdata( $id );
|
396 |
|
397 |
if ( empty( $id ) || empty( $user->ID ) ) {
|
398 |
+
return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user id.' ), array( 'status' => 404 ) );
|
399 |
}
|
400 |
|
401 |
if ( get_current_user_id() === $id ) {
|
405 |
$context = ! empty( $request['context'] ) && in_array( $request['context'], array( 'edit', 'view', 'embed' ) ) ? $request['context'] : 'embed';
|
406 |
|
407 |
if ( 'edit' === $context && ! current_user_can( 'edit_user', $id ) ) {
|
408 |
+
return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user with edit context' ), array( 'status' => rest_authorization_required_code() ) );
|
409 |
} else if ( 'view' === $context && ! current_user_can( 'list_users' ) ) {
|
410 |
+
return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user with view context' ), array( 'status' => rest_authorization_required_code() ) );
|
411 |
} else if ( 'embed' === $context && ! count_user_posts( $id ) && ! current_user_can( 'edit_user', $id ) && ! current_user_can( 'list_users' ) ) {
|
412 |
+
return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this user' ), array( 'status' => rest_authorization_required_code() ) );
|
413 |
}
|
414 |
|
415 |
return true;
|
424 |
public function create_item_permissions_check( $request ) {
|
425 |
|
426 |
if ( ! current_user_can( 'create_users' ) ) {
|
427 |
+
return new WP_Error( 'rest_cannot_create_user', __( 'Sorry, you are not allowed to create users.' ), array( 'status' => rest_authorization_required_code() ) );
|
428 |
}
|
429 |
|
430 |
return true;
|
441 |
$id = (int) $request['id'];
|
442 |
|
443 |
if ( ! current_user_can( 'edit_user', $id ) ) {
|
444 |
+
return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit users.' ), array( 'status' => rest_authorization_required_code() ) );
|
445 |
}
|
446 |
|
447 |
if ( ! empty( $request['role'] ) && ! current_user_can( 'edit_users' ) ) {
|
448 |
+
return new WP_Error( 'rest_cannot_edit_roles', __( 'Sorry, you are not allowed to edit roles of users.' ), array( 'status' => rest_authorization_required_code() ) );
|
449 |
}
|
450 |
|
451 |
return true;
|
463 |
$reassign = isset( $request['reassign'] ) ? absint( $request['reassign'] ) : null;
|
464 |
|
465 |
if ( ! current_user_can( 'delete_user', $id ) ) {
|
466 |
+
return new WP_Error( 'rest_user_cannot_delete', __( 'Sorry, you are not allowed to delete this user.' ), array( 'status' => rest_authorization_required_code() ) );
|
467 |
}
|
468 |
|
469 |
return true;
|
474 |
*
|
475 |
* @param object $user User object.
|
476 |
* @param WP_REST_Request $request Request object.
|
477 |
+
* @return WP_REST_Response Response data.
|
478 |
*/
|
479 |
public function prepare_item_for_response( $user, $request ) {
|
480 |
$data = array(
|
|
|
|
|
|
|
|
|
|
|
|
|
481 |
'id' => $user->ID,
|
482 |
+
'username' => $user->user_login,
|
483 |
+
'name' => $user->display_name,
|
484 |
+
'first_name' => $user->first_name,
|
485 |
'last_name' => $user->last_name,
|
486 |
+
'email' => $user->user_email,
|
487 |
+
'url' => $user->user_url,
|
488 |
+
'description' => $user->description,
|
489 |
'link' => get_author_posts_url( $user->ID ),
|
490 |
+
'avatar_urls' => rest_get_avatar_urls( $user->user_email ),
|
491 |
'nickname' => $user->nickname,
|
492 |
+
'slug' => $user->user_nicename,
|
493 |
'registered_date' => date( 'c', strtotime( $user->user_registered ) ),
|
494 |
'roles' => $user->roles,
|
495 |
+
'capabilities' => $user->allcaps,
|
496 |
+
'extra_capabilities' => $user->caps,
|
|
|
497 |
);
|
498 |
|
499 |
$context = ! empty( $request['context'] ) ? $request['context'] : 'embed';
|
502 |
$data = $this->add_additional_fields_to_object( $data, $request );
|
503 |
|
504 |
// Wrap the data in a response object
|
505 |
+
$response = rest_ensure_response( $data );
|
506 |
+
|
507 |
+
$response->add_links( $this->prepare_links( $user ) );
|
508 |
+
|
509 |
+
/**
|
510 |
+
* Filter user data returned from the REST API.
|
511 |
+
*
|
512 |
+
* @param WP_REST_Response $response The response object.
|
513 |
+
* @param object $user User object used to create response.
|
514 |
+
* @param WP_REST_Request $request Request object.
|
515 |
+
*/
|
516 |
+
return apply_filters( 'rest_prepare_user', $response, $user, $request );
|
517 |
}
|
518 |
|
519 |
/**
|
578 |
$prepared_user->description = $request['description'];
|
579 |
}
|
580 |
if ( isset( $request['role'] ) ) {
|
581 |
+
$prepared_user->role = $request['role'];
|
582 |
}
|
583 |
if ( isset( $request['url'] ) ) {
|
584 |
$prepared_user->user_url = $request['url'];
|
585 |
}
|
586 |
|
587 |
+
/**
|
588 |
+
* Filter user data before inserting user via the REST API.
|
589 |
+
*
|
590 |
+
* @param object $prepared_user User object.
|
591 |
+
* @param WP_REST_Request $request Request object.
|
592 |
+
*/
|
593 |
return apply_filters( 'rest_pre_insert_user', $prepared_user, $request );
|
594 |
}
|
595 |
|
603 |
protected function check_role_update( $user_id, $role ) {
|
604 |
global $wp_roles;
|
605 |
|
|
|
|
|
|
|
|
|
606 |
$potential_role = $wp_roles->role_objects[ $role ];
|
607 |
|
608 |
// Don't let anyone with 'edit_users' (admins) edit their own role to something without it.
|
609 |
// Multisite super admins can freely edit their blog roles -- they possess all caps.
|
610 |
if ( ( is_multisite() && current_user_can( 'manage_sites' ) ) || get_current_user_id() !== $user_id || $potential_role->has_cap( 'edit_users' ) ) {
|
611 |
// The new role must be editable by the logged-in user.
|
612 |
+
|
613 |
+
/** Include admin functions to get access to get_editable_roles() */
|
614 |
+
require_once ABSPATH . 'wp-admin/includes/admin.php';
|
615 |
+
|
616 |
$editable_roles = get_editable_roles();
|
617 |
if ( empty( $editable_roles[ $role ] ) ) {
|
618 |
return new WP_Error( 'rest_user_invalid_role', __( 'You cannot give users that role.' ), array( 'status' => 403 ) );
|
621 |
return true;
|
622 |
}
|
623 |
|
624 |
+
return new WP_Error( 'rest_user_invalid_role', __( 'You cannot give users that role.' ), array( 'status' => rest_authorization_required_code() ) );
|
625 |
}
|
626 |
|
627 |
/**
|
635 |
$avatar_sizes = rest_get_avatar_sizes();
|
636 |
foreach ( $avatar_sizes as $size ) {
|
637 |
$avatar_properties[ $size ] = array(
|
638 |
+
'description' => sprintf( __( 'Avatar URL with image size of %d pixels.' ), $size ),
|
639 |
+
'type' => 'string',
|
640 |
+
'format' => 'uri',
|
641 |
'context' => array( 'embed', 'view', 'edit' ),
|
642 |
);
|
643 |
}
|
644 |
|
645 |
+
global $wp_roles;
|
646 |
+
|
647 |
$schema = array(
|
648 |
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
649 |
'title' => 'user',
|
650 |
'type' => 'object',
|
651 |
'properties' => array(
|
652 |
+
'id' => array(
|
653 |
+
'description' => __( 'Unique identifier for the object.' ),
|
654 |
+
'type' => 'integer',
|
655 |
'context' => array( 'embed', 'view', 'edit' ),
|
656 |
'readonly' => true,
|
|
|
657 |
),
|
658 |
+
'username' => array(
|
659 |
+
'description' => __( 'Login name for the user.' ),
|
|
|
|
|
|
|
|
|
|
|
660 |
'type' => 'string',
|
661 |
+
'context' => array( 'edit' ),
|
662 |
+
'required' => true,
|
663 |
'arg_options' => array(
|
664 |
+
'sanitize_callback' => 'sanitize_user',
|
665 |
),
|
666 |
),
|
667 |
+
'name' => array(
|
668 |
+
'description' => __( 'Display name for the object.' ),
|
669 |
'type' => 'string',
|
670 |
+
'context' => array( 'embed', 'view', 'edit' ),
|
671 |
+
'arg_options' => array(
|
672 |
+
'sanitize_callback' => 'sanitize_text_field',
|
|
|
|
|
|
|
|
|
|
|
|
|
673 |
),
|
674 |
+
),
|
675 |
'first_name' => array(
|
676 |
+
'description' => __( 'First name for the object.' ),
|
677 |
'type' => 'string',
|
678 |
'context' => array( 'view', 'edit' ),
|
679 |
'arg_options' => array(
|
680 |
'sanitize_callback' => 'sanitize_text_field',
|
681 |
),
|
682 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
683 |
'last_name' => array(
|
684 |
+
'description' => __( 'Last name for the object.' ),
|
685 |
'type' => 'string',
|
686 |
'context' => array( 'view', 'edit' ),
|
687 |
'arg_options' => array(
|
688 |
'sanitize_callback' => 'sanitize_text_field',
|
689 |
),
|
690 |
),
|
691 |
+
'email' => array(
|
692 |
+
'description' => __( 'The email address for the object.' ),
|
693 |
+
'type' => 'string',
|
694 |
+
'format' => 'email',
|
695 |
+
'context' => array( 'view', 'edit' ),
|
696 |
+
'required' => true,
|
697 |
+
),
|
698 |
+
'url' => array(
|
699 |
+
'description' => __( 'URL of the object.' ),
|
700 |
'type' => 'string',
|
701 |
'format' => 'uri',
|
702 |
'context' => array( 'embed', 'view', 'edit' ),
|
703 |
'readonly' => true,
|
704 |
),
|
705 |
+
'description' => array(
|
706 |
+
'description' => __( 'Description of the object.' ),
|
707 |
'type' => 'string',
|
708 |
'context' => array( 'embed', 'view', 'edit' ),
|
709 |
'arg_options' => array(
|
710 |
+
'sanitize_callback' => 'wp_filter_post_kses',
|
711 |
),
|
712 |
),
|
713 |
+
'link' => array(
|
714 |
+
'description' => __( 'Author URL to the object.' ),
|
715 |
+
'type' => 'string',
|
716 |
+
'format' => 'uri',
|
717 |
+
'context' => array( 'embed', 'view', 'edit' ),
|
718 |
+
'readonly' => true,
|
719 |
+
),
|
720 |
+
'avatar_urls' => array(
|
721 |
+
'description' => __( 'Avatar URLs for the object.' ),
|
722 |
+
'type' => 'object',
|
723 |
+
'context' => array( 'embed', 'view', 'edit' ),
|
724 |
+
'readonly' => true,
|
725 |
+
'properties' => $avatar_properties,
|
726 |
+
),
|
727 |
'nickname' => array(
|
728 |
+
'description' => __( 'The nickname for the object.' ),
|
729 |
'type' => 'string',
|
730 |
'context' => array( 'view', 'edit' ),
|
731 |
'arg_options' => array(
|
732 |
'sanitize_callback' => 'sanitize_text_field',
|
733 |
),
|
734 |
),
|
735 |
+
'slug' => array(
|
736 |
+
'description' => __( 'An alphanumeric identifier for the object unique to its type.' ),
|
737 |
+
'type' => 'string',
|
738 |
+
'context' => array( 'embed', 'view', 'edit' ),
|
739 |
+
'arg_options' => array(
|
740 |
+
'sanitize_callback' => 'sanitize_title',
|
741 |
+
),
|
742 |
+
),
|
743 |
'registered_date' => array(
|
744 |
+
'description' => __( 'Registration date for the user.' ),
|
745 |
'type' => 'date-time',
|
746 |
'context' => array( 'view', 'edit' ),
|
747 |
'readonly' => true,
|
748 |
),
|
749 |
'roles' => array(
|
750 |
+
'description' => __( 'Roles assigned to the user.' ),
|
751 |
'type' => 'array',
|
752 |
'context' => array( 'view', 'edit' ),
|
753 |
+
'readonly' => true,
|
754 |
),
|
755 |
+
'role' => array(
|
756 |
+
'description' => __( 'Role assigned to the user.' ),
|
757 |
'type' => 'string',
|
758 |
+
'enum' => array_keys( $wp_roles->role_objects ),
|
|
|
|
|
|
|
759 |
),
|
760 |
+
'capabilities' => array(
|
761 |
+
'description' => __( 'All capabilities assigned to the user.' ),
|
762 |
+
'type' => 'object',
|
763 |
+
'context' => array( 'view', 'edit' ),
|
|
|
|
|
764 |
),
|
765 |
+
'extra_capabilities' => array(
|
766 |
+
'description' => __( 'Any extra capabilities assigned to the user.' ),
|
767 |
+
'type' => 'object',
|
768 |
'context' => array( 'edit' ),
|
769 |
+
'readonly' => true,
|
|
|
|
|
|
|
770 |
),
|
771 |
),
|
772 |
);
|
780 |
*/
|
781 |
public function get_collection_params() {
|
782 |
$query_params = parent::get_collection_params();
|
783 |
+
|
784 |
+
$query_params['context']['default'] = 'view';
|
785 |
+
|
786 |
+
$query_params['include'] = array(
|
787 |
+
'description' => __( 'Limit result set to specific ids.' ),
|
788 |
+
'type' => 'array',
|
789 |
+
'default' => array(),
|
790 |
+
'sanitize_callback' => 'wp_parse_id_list',
|
791 |
);
|
792 |
$query_params['order'] = array(
|
793 |
'default' => 'asc',
|
794 |
+
'description' => __( 'Order sort attribute ascending or descending.' ),
|
795 |
'enum' => array( 'asc', 'desc' ),
|
796 |
'sanitize_callback' => 'sanitize_key',
|
797 |
'type' => 'string',
|
798 |
);
|
799 |
$query_params['orderby'] = array(
|
800 |
'default' => 'name',
|
801 |
+
'description' => __( 'Sort collection by object attribute.' ),
|
802 |
+
'enum' => array(
|
803 |
+
'id',
|
804 |
+
'include',
|
805 |
+
'name',
|
806 |
+
'registered_date',
|
807 |
+
),
|
808 |
'sanitize_callback' => 'sanitize_key',
|
809 |
'type' => 'string',
|
810 |
);
|
lib/infrastructure/class-jsonserializable.php
DELETED
@@ -1,18 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Compatibility shim for PHP <5.4
|
4 |
-
*
|
5 |
-
* @link http://php.net/jsonserializable
|
6 |
-
*
|
7 |
-
* @package WordPress
|
8 |
-
* @subpackage JSON API
|
9 |
-
*/
|
10 |
-
|
11 |
-
if ( ! interface_exists( 'JsonSerializable' ) ) {
|
12 |
-
define( 'WP_JSON_SERIALIZE_COMPATIBLE', true );
|
13 |
-
// @codingStandardsIgnoreStart
|
14 |
-
interface JsonSerializable {
|
15 |
-
public function jsonSerialize();
|
16 |
-
}
|
17 |
-
// @codingStandardsIgnoreEnd
|
18 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/infrastructure/class-wp-http-response.php
DELETED
@@ -1,112 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class WP_HTTP_Response implements WP_HTTP_ResponseInterface {
|
4 |
-
/**
|
5 |
-
* @var mixed
|
6 |
-
*/
|
7 |
-
public $data;
|
8 |
-
/**
|
9 |
-
* @var integer
|
10 |
-
*/
|
11 |
-
public $headers;
|
12 |
-
/**
|
13 |
-
* @var array
|
14 |
-
*/
|
15 |
-
public $status;
|
16 |
-
/**
|
17 |
-
* Constructor
|
18 |
-
*
|
19 |
-
* @param mixed $data Response data
|
20 |
-
* @param integer $status HTTP status code
|
21 |
-
* @param array $headers HTTP header map
|
22 |
-
*/
|
23 |
-
public function __construct( $data = null, $status = 200, $headers = array() ) {
|
24 |
-
$this->data = $data;
|
25 |
-
$this->set_status( $status );
|
26 |
-
$this->set_headers( $headers );
|
27 |
-
}
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Get headers associated with the response
|
31 |
-
*
|
32 |
-
* @return array Map of header name to header value
|
33 |
-
*/
|
34 |
-
public function get_headers() {
|
35 |
-
return $this->headers;
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Set all header values
|
40 |
-
*
|
41 |
-
* @param array $headers Map of header name to header value
|
42 |
-
*/
|
43 |
-
public function set_headers( $headers ) {
|
44 |
-
$this->headers = $headers;
|
45 |
-
}
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Set a single HTTP header
|
49 |
-
*
|
50 |
-
* @param string $key Header name
|
51 |
-
* @param string $value Header value
|
52 |
-
* @param boolean $replace Replace an existing header of the same name?
|
53 |
-
*/
|
54 |
-
public function header( $key, $value, $replace = true ) {
|
55 |
-
if ( $replace || ! isset( $this->headers[ $key ] ) ) {
|
56 |
-
$this->headers[ $key ] = $value;
|
57 |
-
} else {
|
58 |
-
$this->headers[ $key ] .= ', ' . $value;
|
59 |
-
}
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Get the HTTP return code for the response
|
64 |
-
*
|
65 |
-
* @return integer 3-digit HTTP status code
|
66 |
-
*/
|
67 |
-
public function get_status() {
|
68 |
-
return $this->status;
|
69 |
-
}
|
70 |
-
|
71 |
-
/**
|
72 |
-
* Set the HTTP status code
|
73 |
-
*
|
74 |
-
* @param int $code HTTP status
|
75 |
-
*/
|
76 |
-
public function set_status( $code ) {
|
77 |
-
$this->status = absint( $code );
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Get the response data
|
82 |
-
*
|
83 |
-
* @return mixed
|
84 |
-
*/
|
85 |
-
public function get_data() {
|
86 |
-
return $this->data;
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* Set the response data
|
91 |
-
*
|
92 |
-
* @param mixed $data
|
93 |
-
*/
|
94 |
-
public function set_data( $data ) {
|
95 |
-
$this->data = $data;
|
96 |
-
}
|
97 |
-
|
98 |
-
/**
|
99 |
-
* Get the response data for JSON serialization
|
100 |
-
*
|
101 |
-
* It is expected that in most implementations, this will return the same as
|
102 |
-
* {@see get_data()}, however this may be different if you want to do custom
|
103 |
-
* JSON data handling.
|
104 |
-
*
|
105 |
-
* @return mixed Any JSON-serializable value
|
106 |
-
*/
|
107 |
-
// @codingStandardsIgnoreStart
|
108 |
-
public function jsonSerialize() {
|
109 |
-
// @codingStandardsIgnoreEnd
|
110 |
-
return $this->get_data();
|
111 |
-
}
|
112 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/infrastructure/class-wp-http-responseinterface.php
DELETED
@@ -1,35 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
interface WP_HTTP_ResponseInterface extends JsonSerializable {
|
4 |
-
/**
|
5 |
-
* Get headers associated with the response
|
6 |
-
*
|
7 |
-
* @return array Map of header name to header value
|
8 |
-
*/
|
9 |
-
public function get_headers();
|
10 |
-
|
11 |
-
/**
|
12 |
-
* Get the HTTP return code for the response
|
13 |
-
*
|
14 |
-
* @return integer 3-digit HTTP status code
|
15 |
-
*/
|
16 |
-
public function get_status();
|
17 |
-
|
18 |
-
/**
|
19 |
-
* Get the response data
|
20 |
-
*
|
21 |
-
* @return mixed
|
22 |
-
*/
|
23 |
-
public function get_data();
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Get the response data for JSON serialization
|
27 |
-
*
|
28 |
-
* It is expected that in most implementations, this will return the same as
|
29 |
-
* {@see get_data()}, however this may be different if you want to do custom
|
30 |
-
* JSON data handling.
|
31 |
-
*
|
32 |
-
* @return mixed Any JSON-serializable value
|
33 |
-
*/
|
34 |
-
// public function jsonSerialize();
|
35 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/infrastructure/class-wp-rest-request.php
DELETED
@@ -1,764 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Request object
|
5 |
-
*
|
6 |
-
* Contains data from the request, to be passed to the callback.
|
7 |
-
*
|
8 |
-
* Note: This implements ArrayAccess, and acts as an array of parameters when
|
9 |
-
* used in that manner. It does not use ArrayObject (as we cannot rely on SPL),
|
10 |
-
* so be aware it may have non-array behaviour in some cases.
|
11 |
-
*
|
12 |
-
* @package WordPress
|
13 |
-
*/
|
14 |
-
class WP_REST_Request implements ArrayAccess {
|
15 |
-
/**
|
16 |
-
* HTTP method
|
17 |
-
*
|
18 |
-
* @var string
|
19 |
-
*/
|
20 |
-
protected $method = '';
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Parameters passed to the request
|
24 |
-
*
|
25 |
-
* These typically come from the `$_GET`, `$_POST` and `$_FILES`
|
26 |
-
* superglobals when being created from the global scope.
|
27 |
-
*
|
28 |
-
* @var array Contains GET, POST and FILES keys mapping to arrays of data
|
29 |
-
*/
|
30 |
-
protected $params;
|
31 |
-
|
32 |
-
/**
|
33 |
-
* HTTP headers for the request
|
34 |
-
*
|
35 |
-
* @var array Map of key to value. Key is always lowercase, as per HTTP specification
|
36 |
-
*/
|
37 |
-
protected $headers = array();
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Body data
|
41 |
-
*
|
42 |
-
* @var string Binary data from the request
|
43 |
-
*/
|
44 |
-
protected $body = null;
|
45 |
-
|
46 |
-
/**
|
47 |
-
* Route matched for the request
|
48 |
-
*
|
49 |
-
* @var string
|
50 |
-
*/
|
51 |
-
protected $route;
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Attributes (options) for the route that was matched
|
55 |
-
*
|
56 |
-
* This is the options array used when the route was registered, typically
|
57 |
-
* containing the callback as well as the valid methods for the route.
|
58 |
-
*
|
59 |
-
* @return array Attributes for the request
|
60 |
-
*/
|
61 |
-
protected $attributes = array();
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Have we parsed the JSON data yet?
|
65 |
-
*
|
66 |
-
* Allows lazy-parsing of JSON data where possible.
|
67 |
-
*
|
68 |
-
* @var boolean
|
69 |
-
*/
|
70 |
-
protected $parsed_json = false;
|
71 |
-
|
72 |
-
/**
|
73 |
-
* Have we parsed body data yet?
|
74 |
-
*
|
75 |
-
* @var boolean
|
76 |
-
*/
|
77 |
-
protected $parsed_body = false;
|
78 |
-
|
79 |
-
/**
|
80 |
-
* Constructor
|
81 |
-
*/
|
82 |
-
public function __construct( $method = '', $route = '', $attributes = array() ) {
|
83 |
-
$this->params = array(
|
84 |
-
'URL' => array(),
|
85 |
-
'GET' => array(),
|
86 |
-
'POST' => array(),
|
87 |
-
'FILES' => array(),
|
88 |
-
|
89 |
-
// See parse_json_params
|
90 |
-
'JSON' => null,
|
91 |
-
|
92 |
-
'defaults' => array(),
|
93 |
-
);
|
94 |
-
|
95 |
-
$this->set_method( $method );
|
96 |
-
$this->set_route( $route );
|
97 |
-
$this->set_attributes( $attributes );
|
98 |
-
}
|
99 |
-
|
100 |
-
/**
|
101 |
-
* Get HTTP method for the request
|
102 |
-
*
|
103 |
-
* @return string HTTP method
|
104 |
-
*/
|
105 |
-
public function get_method() {
|
106 |
-
return $this->method;
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* Set HTTP method for the request
|
111 |
-
*
|
112 |
-
* @param string $method HTTP method
|
113 |
-
*/
|
114 |
-
public function set_method( $method ) {
|
115 |
-
$this->method = strtoupper( $method );
|
116 |
-
}
|
117 |
-
|
118 |
-
/**
|
119 |
-
* Get all headers from the request
|
120 |
-
*
|
121 |
-
* @return array Map of key to value. Key is always lowercase, as per HTTP specification
|
122 |
-
*/
|
123 |
-
public function get_headers() {
|
124 |
-
return $this->headers;
|
125 |
-
}
|
126 |
-
|
127 |
-
/**
|
128 |
-
* Canonicalize header name
|
129 |
-
*
|
130 |
-
* Ensures that header names are always treated the same regardless of
|
131 |
-
* source. Header names are always case insensitive.
|
132 |
-
*
|
133 |
-
* Note that we treat `-` (dashes) and `_` (underscores) as the same
|
134 |
-
* character, as per header parsing rules in both Apache and nginx.
|
135 |
-
*
|
136 |
-
* @link http://stackoverflow.com/q/18185366
|
137 |
-
* @link http://wiki.nginx.org/Pitfalls#Missing_.28disappearing.29_HTTP_headers
|
138 |
-
* @link http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers
|
139 |
-
*
|
140 |
-
* @param string $key Header name
|
141 |
-
* @return string Canonicalized name
|
142 |
-
*/
|
143 |
-
public static function canonicalize_header_name( $key ) {
|
144 |
-
$key = strtolower( $key );
|
145 |
-
$key = str_replace( '-', '_', $key );
|
146 |
-
|
147 |
-
return $key;
|
148 |
-
}
|
149 |
-
|
150 |
-
/**
|
151 |
-
* Get header from request
|
152 |
-
*
|
153 |
-
* If the header has multiple values, they will be concatenated with a comma
|
154 |
-
* as per the HTTP specification. Be aware that some non-compliant headers
|
155 |
-
* (notably cookie headers) cannot be joined this way.
|
156 |
-
*
|
157 |
-
* @param string $key Header name, will be canonicalized to lowercase
|
158 |
-
* @return string|null String value if set, null otherwise
|
159 |
-
*/
|
160 |
-
public function get_header( $key ) {
|
161 |
-
$key = $this->canonicalize_header_name( $key );
|
162 |
-
|
163 |
-
if ( ! isset( $this->headers[ $key ] ) ) {
|
164 |
-
return null;
|
165 |
-
}
|
166 |
-
|
167 |
-
return implode( ',', $this->headers[ $key ] );
|
168 |
-
}
|
169 |
-
|
170 |
-
/**
|
171 |
-
* Get header values from request
|
172 |
-
*
|
173 |
-
* @param string $key Header name, will be canonicalized to lowercase
|
174 |
-
* @return array|null List of string values if set, null otherwise
|
175 |
-
*/
|
176 |
-
public function get_header_as_array( $key ) {
|
177 |
-
$key = $this->canonicalize_header_name( $key );
|
178 |
-
|
179 |
-
if ( ! isset( $this->headers[ $key ] ) ) {
|
180 |
-
return null;
|
181 |
-
}
|
182 |
-
|
183 |
-
return $this->headers[ $key ];
|
184 |
-
}
|
185 |
-
|
186 |
-
/**
|
187 |
-
* Set header on request
|
188 |
-
*
|
189 |
-
* @param string $key Header name
|
190 |
-
* @param string|string[] $value Header value, or list of values
|
191 |
-
*/
|
192 |
-
public function set_header( $key, $value ) {
|
193 |
-
$key = $this->canonicalize_header_name( $key );
|
194 |
-
$value = (array) $value;
|
195 |
-
|
196 |
-
$this->headers[ $key ] = $value;
|
197 |
-
}
|
198 |
-
|
199 |
-
/**
|
200 |
-
* Append a header value for the given header
|
201 |
-
*
|
202 |
-
* @param string $key Header name
|
203 |
-
* @param string|string[] $value Header value, or list of values
|
204 |
-
*/
|
205 |
-
public function add_header( $key, $value ) {
|
206 |
-
$key = $this->canonicalize_header_name( $key );
|
207 |
-
$value = (array) $value;
|
208 |
-
|
209 |
-
if ( ! isset( $this->headers[ $key ] ) ) {
|
210 |
-
$this->headers[ $key ] = array();
|
211 |
-
}
|
212 |
-
|
213 |
-
$this->headers[ $key ] = array_merge( $this->headers[ $key ], $value );
|
214 |
-
}
|
215 |
-
|
216 |
-
/**
|
217 |
-
* Remove all values for a header
|
218 |
-
*
|
219 |
-
* @param string $key Header name
|
220 |
-
*/
|
221 |
-
public function remove_header( $key ) {
|
222 |
-
unset( $this->headers[ $key ] );
|
223 |
-
}
|
224 |
-
|
225 |
-
/**
|
226 |
-
* Set headers on the request
|
227 |
-
*
|
228 |
-
* @param array $headers Map of header name to value
|
229 |
-
* @param boolean $override If true, replace the request's headers. Otherwise, merge with existing.
|
230 |
-
*/
|
231 |
-
public function set_headers( $headers, $override = true ) {
|
232 |
-
if ( true === $override ) {
|
233 |
-
$this->headers = array();
|
234 |
-
}
|
235 |
-
|
236 |
-
foreach ( $headers as $key => $value ) {
|
237 |
-
$this->set_header( $key, $value );
|
238 |
-
}
|
239 |
-
}
|
240 |
-
|
241 |
-
/**
|
242 |
-
* Get the content-type of the request
|
243 |
-
*
|
244 |
-
* @return array Map containing 'value' and 'parameters' keys
|
245 |
-
*/
|
246 |
-
public function get_content_type() {
|
247 |
-
$value = $this->get_header( 'content-type' );
|
248 |
-
if ( empty( $value ) ) {
|
249 |
-
return null;
|
250 |
-
}
|
251 |
-
|
252 |
-
$parameters = '';
|
253 |
-
if ( strpos( $value, ';' ) ) {
|
254 |
-
list( $value, $parameters ) = explode( ';', $value, 2 );
|
255 |
-
}
|
256 |
-
|
257 |
-
$value = strtolower( $value );
|
258 |
-
if ( strpos( $value, '/' ) === false ) {
|
259 |
-
return null;
|
260 |
-
}
|
261 |
-
|
262 |
-
// Parse type and subtype out
|
263 |
-
list( $type, $subtype ) = explode( '/', $value, 2 );
|
264 |
-
|
265 |
-
$data = compact( 'value', 'type', 'subtype', 'parameters' );
|
266 |
-
$data = array_map( 'trim', $data );
|
267 |
-
|
268 |
-
return $data;
|
269 |
-
}
|
270 |
-
|
271 |
-
/**
|
272 |
-
* Get the parameter priority order
|
273 |
-
*
|
274 |
-
* Used when checking parameters in {@see get_param}.
|
275 |
-
*
|
276 |
-
* @return string[] List of types to check, in order of priority
|
277 |
-
*/
|
278 |
-
protected function get_parameter_order() {
|
279 |
-
$order = array();
|
280 |
-
$order[] = 'JSON';
|
281 |
-
|
282 |
-
$this->parse_json_params();
|
283 |
-
|
284 |
-
// Ensure we parse the body data
|
285 |
-
$body = $this->get_body();
|
286 |
-
if ( $this->method !== 'POST' && ! empty( $body ) ) {
|
287 |
-
$this->parse_body_params();
|
288 |
-
}
|
289 |
-
|
290 |
-
$accepts_body_data = array( 'POST', 'PUT', 'PATCH' );
|
291 |
-
if ( in_array( $this->method, $accepts_body_data ) ) {
|
292 |
-
$order[] = 'POST';
|
293 |
-
}
|
294 |
-
|
295 |
-
$order[] = 'GET';
|
296 |
-
$order[] = 'URL';
|
297 |
-
$order[] = 'defaults';
|
298 |
-
|
299 |
-
/**
|
300 |
-
* Alter the parameter checking order
|
301 |
-
*
|
302 |
-
* The order affects which parameters are checked when using
|
303 |
-
* {@see get_param} and family. This acts similarly to PHP's
|
304 |
-
* `request_order` setting.
|
305 |
-
*
|
306 |
-
* @param string[] $order List of types to check, in order of priority
|
307 |
-
* @param WP_REST_Request $this Request object
|
308 |
-
*/
|
309 |
-
return apply_filters( 'rest_request_parameter_order', $order, $this );
|
310 |
-
}
|
311 |
-
|
312 |
-
/**
|
313 |
-
* Get a parameter from the request
|
314 |
-
*
|
315 |
-
* @param string $key Parameter name
|
316 |
-
* @return mixed|null Value if set, null otherwise
|
317 |
-
*/
|
318 |
-
public function get_param( $key ) {
|
319 |
-
$order = $this->get_parameter_order();
|
320 |
-
|
321 |
-
foreach ( $order as $type ) {
|
322 |
-
// Do we have the parameter for this type?
|
323 |
-
if ( isset( $this->params[ $type ][ $key ] ) ) {
|
324 |
-
return $this->params[ $type ][ $key ];
|
325 |
-
}
|
326 |
-
}
|
327 |
-
|
328 |
-
return null;
|
329 |
-
}
|
330 |
-
|
331 |
-
/**
|
332 |
-
* Set a parameter on the request
|
333 |
-
*
|
334 |
-
* @param string $key Parameter name
|
335 |
-
* @param mixed $value Parameter value
|
336 |
-
*/
|
337 |
-
public function set_param( $key, $value ) {
|
338 |
-
switch ( $this->method ) {
|
339 |
-
case 'POST':
|
340 |
-
$this->params['POST'][ $key ] = $value;
|
341 |
-
break;
|
342 |
-
|
343 |
-
default:
|
344 |
-
$this->params['GET'][ $key ] = $value;
|
345 |
-
break;
|
346 |
-
}
|
347 |
-
}
|
348 |
-
|
349 |
-
/**
|
350 |
-
* Get merged parameters from the request
|
351 |
-
*
|
352 |
-
* The equivalent of {@see get_param}, but returns all parameters for the
|
353 |
-
* request. Handles merging all the available values into a single array.
|
354 |
-
*
|
355 |
-
* @return array Map of key to value
|
356 |
-
*/
|
357 |
-
public function get_params() {
|
358 |
-
$order = $this->get_parameter_order();
|
359 |
-
$order = array_reverse( $order, true );
|
360 |
-
|
361 |
-
$params = array();
|
362 |
-
foreach ( $order as $type ) {
|
363 |
-
$params = array_merge( $params, (array) $this->params[ $type ] );
|
364 |
-
}
|
365 |
-
|
366 |
-
return $params;
|
367 |
-
}
|
368 |
-
|
369 |
-
/**
|
370 |
-
* Get parameters from the route itself
|
371 |
-
*
|
372 |
-
* These are parsed from the URL using the regex.
|
373 |
-
*
|
374 |
-
* @return array Parameter map of key to value
|
375 |
-
*/
|
376 |
-
public function get_url_params() {
|
377 |
-
return $this->params['URL'];
|
378 |
-
}
|
379 |
-
|
380 |
-
/**
|
381 |
-
* Set parameters from the route
|
382 |
-
*
|
383 |
-
* Typically, this is set after parsing the URL.
|
384 |
-
*
|
385 |
-
* @param array $params Parameter map of key to value
|
386 |
-
*/
|
387 |
-
public function set_url_params( $params ) {
|
388 |
-
$this->params['URL'] = $params;
|
389 |
-
}
|
390 |
-
|
391 |
-
/**
|
392 |
-
* Get parameters from the query string
|
393 |
-
*
|
394 |
-
* These are the parameters you'd typically find in `$_GET`
|
395 |
-
*
|
396 |
-
* @return array Parameter map of key to value
|
397 |
-
*/
|
398 |
-
public function get_query_params() {
|
399 |
-
return $this->params['GET'];
|
400 |
-
}
|
401 |
-
|
402 |
-
/**
|
403 |
-
* Set parameters from the query string
|
404 |
-
*
|
405 |
-
* Typically, this is set from `$_GET`
|
406 |
-
*
|
407 |
-
* @param array $params Parameter map of key to value
|
408 |
-
*/
|
409 |
-
public function set_query_params( $params ) {
|
410 |
-
$this->params['GET'] = $params;
|
411 |
-
}
|
412 |
-
|
413 |
-
/**
|
414 |
-
* Get parameters from the body
|
415 |
-
*
|
416 |
-
* These are the parameters you'd typically find in `$_POST`
|
417 |
-
*
|
418 |
-
* @return array Parameter map of key to value
|
419 |
-
*/
|
420 |
-
public function get_body_params() {
|
421 |
-
return $this->params['POST'];
|
422 |
-
}
|
423 |
-
|
424 |
-
/**
|
425 |
-
* Set parameters from the body
|
426 |
-
*
|
427 |
-
* Typically, this is set from `$_POST`
|
428 |
-
*
|
429 |
-
* @param array $params Parameter map of key to value
|
430 |
-
*/
|
431 |
-
public function set_body_params( $params ) {
|
432 |
-
$this->params['POST'] = $params;
|
433 |
-
}
|
434 |
-
|
435 |
-
/**
|
436 |
-
* Get multipart file parameters from the body
|
437 |
-
*
|
438 |
-
* These are the parameters you'd typically find in `$_FILES`
|
439 |
-
*
|
440 |
-
* @return array Parameter map of key to value
|
441 |
-
*/
|
442 |
-
public function get_file_params() {
|
443 |
-
return $this->params['FILES'];
|
444 |
-
}
|
445 |
-
|
446 |
-
/**
|
447 |
-
* Set multipart file parameters from the body
|
448 |
-
*
|
449 |
-
* Typically, this is set from `$_FILES`
|
450 |
-
*
|
451 |
-
* @param array $params Parameter map of key to value
|
452 |
-
*/
|
453 |
-
public function set_file_params( $params ) {
|
454 |
-
$this->params['FILES'] = $params;
|
455 |
-
}
|
456 |
-
|
457 |
-
/**
|
458 |
-
* Get default parameters
|
459 |
-
*
|
460 |
-
* These are the parameters set in the route registration
|
461 |
-
*
|
462 |
-
* @return array Parameter map of key to value
|
463 |
-
*/
|
464 |
-
public function get_default_params() {
|
465 |
-
return $this->params['defaults'];
|
466 |
-
}
|
467 |
-
|
468 |
-
/**
|
469 |
-
* Set default parameters
|
470 |
-
*
|
471 |
-
* These are the parameters set in the route registration
|
472 |
-
*
|
473 |
-
* @param array $params Parameter map of key to value
|
474 |
-
*/
|
475 |
-
public function set_default_params( $params ) {
|
476 |
-
$this->params['defaults'] = $params;
|
477 |
-
}
|
478 |
-
|
479 |
-
/**
|
480 |
-
* Get body content
|
481 |
-
*
|
482 |
-
* @return string Binary data from the request body
|
483 |
-
*/
|
484 |
-
public function get_body() {
|
485 |
-
return $this->body;
|
486 |
-
}
|
487 |
-
|
488 |
-
/**
|
489 |
-
* Set body content
|
490 |
-
*
|
491 |
-
* @param string $data Binary data from the request body
|
492 |
-
*/
|
493 |
-
public function set_body( $data ) {
|
494 |
-
$this->body = $data;
|
495 |
-
|
496 |
-
// Enable lazy parsing
|
497 |
-
$this->parsed_json = false;
|
498 |
-
$this->parsed_body = false;
|
499 |
-
$this->params['JSON'] = null;
|
500 |
-
}
|
501 |
-
|
502 |
-
/**
|
503 |
-
* Get parameters from a JSON-formatted body
|
504 |
-
*
|
505 |
-
* @return array Parameter map of key to value
|
506 |
-
*/
|
507 |
-
public function get_json_params() {
|
508 |
-
// Ensure the parameters have been parsed out
|
509 |
-
$this->parse_json_params();
|
510 |
-
|
511 |
-
return $this->params['JSON'];
|
512 |
-
}
|
513 |
-
|
514 |
-
/**
|
515 |
-
* Parse the JSON parameters
|
516 |
-
*
|
517 |
-
* Avoids parsing the JSON data until we need to access it.
|
518 |
-
*/
|
519 |
-
protected function parse_json_params() {
|
520 |
-
if ( $this->parsed_json ) {
|
521 |
-
return;
|
522 |
-
}
|
523 |
-
$this->parsed_json = true;
|
524 |
-
|
525 |
-
// Check that we actually got JSON
|
526 |
-
$content_type = $this->get_content_type();
|
527 |
-
if ( empty( $content_type ) || 'application/json' !== $content_type['value'] ) {
|
528 |
-
return;
|
529 |
-
}
|
530 |
-
|
531 |
-
$params = json_decode( $this->get_body(), true );
|
532 |
-
|
533 |
-
// Check for a parsing error
|
534 |
-
//
|
535 |
-
// Note that due to WP's JSON compatibility functions, json_last_error
|
536 |
-
// might not be defined: https://core.trac.wordpress.org/ticket/27799
|
537 |
-
if ( null === $params && ( ! function_exists( 'json_last_error' ) || JSON_ERROR_NONE !== json_last_error() ) ) {
|
538 |
-
return;
|
539 |
-
}
|
540 |
-
|
541 |
-
$this->params['JSON'] = $params;
|
542 |
-
}
|
543 |
-
|
544 |
-
/**
|
545 |
-
* Parse body parameters.
|
546 |
-
*
|
547 |
-
* Parses out URL-encoded bodies for request methods that aren't supported
|
548 |
-
* natively by PHP. In PHP 5.x, only POST has these parsed automatically.
|
549 |
-
*/
|
550 |
-
protected function parse_body_params() {
|
551 |
-
if ( $this->parsed_body ) {
|
552 |
-
return;
|
553 |
-
}
|
554 |
-
$this->parsed_body = true;
|
555 |
-
|
556 |
-
// Check that we got URL-encoded. Treat a missing content-type as
|
557 |
-
// URL-encoded for maximum compatibility
|
558 |
-
$content_type = $this->get_content_type();
|
559 |
-
if ( ! empty( $content_type ) && 'application/x-www-form-urlencoded' !== $content_type['value'] ) {
|
560 |
-
return;
|
561 |
-
}
|
562 |
-
|
563 |
-
parse_str( $this->get_body(), $params );
|
564 |
-
|
565 |
-
// Amazingly, parse_str follows magic quote rules. Sigh.
|
566 |
-
// NOTE: Do not refactor to use `wp_unslash`.
|
567 |
-
// @codeCoverageIgnoreStart
|
568 |
-
if ( get_magic_quotes_gpc() ) {
|
569 |
-
$params = stripslashes_deep( $params );
|
570 |
-
}
|
571 |
-
// @codeCoverageIgnoreEnd
|
572 |
-
|
573 |
-
// Add to the POST parameters stored internally. If a user has already
|
574 |
-
// set these manually (via `set_body_params`), don't override them.
|
575 |
-
$this->params['POST'] = array_merge( $params, $this->params['POST'] );
|
576 |
-
}
|
577 |
-
|
578 |
-
/**
|
579 |
-
* Get route that matched the request
|
580 |
-
*
|
581 |
-
* @return string Route matching regex
|
582 |
-
*/
|
583 |
-
public function get_route() {
|
584 |
-
return $this->route;
|
585 |
-
}
|
586 |
-
|
587 |
-
/**
|
588 |
-
* Set route that matched the request
|
589 |
-
*
|
590 |
-
* @param string $route Route matching regex
|
591 |
-
*/
|
592 |
-
public function set_route( $route ) {
|
593 |
-
$this->route = $route;
|
594 |
-
}
|
595 |
-
|
596 |
-
/**
|
597 |
-
* Get attributes for the request
|
598 |
-
*
|
599 |
-
* These are the options for the route that was matched.
|
600 |
-
*
|
601 |
-
* @return array Attributes for the request
|
602 |
-
*/
|
603 |
-
public function get_attributes() {
|
604 |
-
return $this->attributes;
|
605 |
-
}
|
606 |
-
|
607 |
-
/**
|
608 |
-
* Set attributes for the request
|
609 |
-
*
|
610 |
-
* @param array $attributes Attributes for the request
|
611 |
-
*/
|
612 |
-
public function set_attributes( $attributes ) {
|
613 |
-
$this->attributes = $attributes;
|
614 |
-
}
|
615 |
-
|
616 |
-
/**
|
617 |
-
* Sanitize (where possible) the params on the request.
|
618 |
-
*
|
619 |
-
* This is primarily based off the sanitize_callback param on each registered
|
620 |
-
* argument.
|
621 |
-
*
|
622 |
-
* @return null
|
623 |
-
*/
|
624 |
-
public function sanitize_params() {
|
625 |
-
|
626 |
-
$attributes = $this->get_attributes();
|
627 |
-
|
628 |
-
// No arguments set, skip sanitizing
|
629 |
-
if ( empty( $attributes['args'] ) ) {
|
630 |
-
return true;
|
631 |
-
}
|
632 |
-
|
633 |
-
$order = $this->get_parameter_order();
|
634 |
-
|
635 |
-
foreach ( $order as $type ) {
|
636 |
-
if ( empty( $this->params[ $type ] ) ) {
|
637 |
-
continue;
|
638 |
-
}
|
639 |
-
foreach ( $this->params[ $type ] as $key => $value ) {
|
640 |
-
// check if this param has a sanitize_callback added
|
641 |
-
if ( isset( $attributes['args'][ $key ] ) && ! empty( $attributes['args'][ $key ]['sanitize_callback'] ) ) {
|
642 |
-
$this->params[ $type ][ $key ] = call_user_func( $attributes['args'][ $key ]['sanitize_callback'], $value, $this, $key );
|
643 |
-
}
|
644 |
-
}
|
645 |
-
}
|
646 |
-
}
|
647 |
-
|
648 |
-
/**
|
649 |
-
* Check whether this request is valid according to its attributes
|
650 |
-
*
|
651 |
-
* @return bool|WP_Error
|
652 |
-
*/
|
653 |
-
public function has_valid_params() {
|
654 |
-
|
655 |
-
$attributes = $this->get_attributes();
|
656 |
-
$required = array();
|
657 |
-
|
658 |
-
// No arguments set, skip validation
|
659 |
-
if ( empty( $attributes['args'] ) ) {
|
660 |
-
return true;
|
661 |
-
}
|
662 |
-
|
663 |
-
foreach ( $attributes['args'] as $key => $arg ) {
|
664 |
-
|
665 |
-
$param = $this->get_param( $key );
|
666 |
-
if ( isset( $arg['required'] ) && true === $arg['required'] && null === $param ) {
|
667 |
-
$required[] = $key;
|
668 |
-
}
|
669 |
-
}
|
670 |
-
|
671 |
-
if ( ! empty( $required ) ) {
|
672 |
-
return new WP_Error( 'rest_missing_callback_param', sprintf( __( 'Missing parameter(s): %s' ), implode( ', ', $required ) ), array( 'status' => 400, 'params' => $required ) );
|
673 |
-
}
|
674 |
-
|
675 |
-
// check the validation callbacks for each registered arg.
|
676 |
-
// This is done after required checking as required checking is cheaper.
|
677 |
-
$invalid_params = array();
|
678 |
-
|
679 |
-
foreach ( $attributes['args'] as $key => $arg ) {
|
680 |
-
|
681 |
-
$param = $this->get_param( $key );
|
682 |
-
|
683 |
-
if ( null !== $param && ! empty( $arg['validate_callback']) ) {
|
684 |
-
$valid_check = call_user_func( $arg['validate_callback'], $param, $this, $key );
|
685 |
-
|
686 |
-
if ( false === $valid_check ) {
|
687 |
-
$invalid_params[ $key ] = __( 'Invalid param.' );
|
688 |
-
}
|
689 |
-
|
690 |
-
if ( is_wp_error( $valid_check ) ) {
|
691 |
-
$invalid_params[] = sprintf( '%s (%s)', $key, $valid_check->get_error_message() );
|
692 |
-
}
|
693 |
-
}
|
694 |
-
}
|
695 |
-
|
696 |
-
if ( $invalid_params ) {
|
697 |
-
return new WP_Error( 'rest_invalid_param', sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', $invalid_params ) ), array( 'status' => 400, 'params' => $invalid_params ) );
|
698 |
-
}
|
699 |
-
|
700 |
-
return true;
|
701 |
-
|
702 |
-
}
|
703 |
-
|
704 |
-
/**
|
705 |
-
* Check if a parameter is set
|
706 |
-
*
|
707 |
-
* @param string $key Parameter name
|
708 |
-
* @return boolean
|
709 |
-
*/
|
710 |
-
// @codingStandardsIgnoreStart
|
711 |
-
public function offsetExists( $offset ) {
|
712 |
-
// @codingStandardsIgnoreEnd
|
713 |
-
$order = $this->get_parameter_order();
|
714 |
-
|
715 |
-
foreach ( $order as $type ) {
|
716 |
-
if ( isset( $this->params[ $type ][ $offset ] ) ) {
|
717 |
-
return true;
|
718 |
-
}
|
719 |
-
}
|
720 |
-
|
721 |
-
return false;
|
722 |
-
}
|
723 |
-
|
724 |
-
/**
|
725 |
-
* Get a parameter from the request
|
726 |
-
*
|
727 |
-
* @param string $key Parameter name
|
728 |
-
* @return mixed|null Value if set, null otherwise
|
729 |
-
*/
|
730 |
-
// @codingStandardsIgnoreStart
|
731 |
-
public function offsetGet( $offset ) {
|
732 |
-
// @codingStandardsIgnoreEnd
|
733 |
-
return $this->get_param( $offset );
|
734 |
-
}
|
735 |
-
|
736 |
-
/**
|
737 |
-
* Set a parameter on the request
|
738 |
-
*
|
739 |
-
* @param string $key Parameter name
|
740 |
-
* @param mixed $value Parameter value
|
741 |
-
*/
|
742 |
-
// @codingStandardsIgnoreStart
|
743 |
-
public function offsetSet( $offset, $value ) {
|
744 |
-
// @codingStandardsIgnoreEnd
|
745 |
-
return $this->set_param( $offset, $value );
|
746 |
-
}
|
747 |
-
|
748 |
-
/**
|
749 |
-
* Remove a parameter from the request
|
750 |
-
*
|
751 |
-
* @param string $key Parameter name
|
752 |
-
* @param mixed $value Parameter value
|
753 |
-
*/
|
754 |
-
// @codingStandardsIgnoreStart
|
755 |
-
public function offsetUnset( $offset ) {
|
756 |
-
// @codingStandardsIgnoreEnd
|
757 |
-
$order = $this->get_parameter_order();
|
758 |
-
|
759 |
-
// Remove the offset from every group
|
760 |
-
foreach ( $order as $type ) {
|
761 |
-
unset( $this->params[ $type ][ $offset ] );
|
762 |
-
}
|
763 |
-
}
|
764 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/infrastructure/class-wp-rest-response.php
DELETED
@@ -1,176 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class WP_REST_Response extends WP_HTTP_Response {
|
4 |
-
/**
|
5 |
-
* Links related to the response
|
6 |
-
*
|
7 |
-
* @var array
|
8 |
-
*/
|
9 |
-
protected $links = array();
|
10 |
-
|
11 |
-
/**
|
12 |
-
* The route that was to create the response
|
13 |
-
*
|
14 |
-
* @var string
|
15 |
-
*/
|
16 |
-
protected $matched_route = '';
|
17 |
-
|
18 |
-
/**
|
19 |
-
* The handler that was used to create the response
|
20 |
-
*
|
21 |
-
* @var null|array
|
22 |
-
*/
|
23 |
-
protected $matched_handler = null;
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Add a link to the response
|
27 |
-
*
|
28 |
-
* @internal The $rel parameter is first, as this looks nicer when sending multiple
|
29 |
-
*
|
30 |
-
* @link http://tools.ietf.org/html/rfc5988
|
31 |
-
* @link http://www.iana.org/assignments/link-relations/link-relations.xml
|
32 |
-
*
|
33 |
-
* @param string $rel Link relation. Either an IANA registered type, or an absolute URL
|
34 |
-
* @param string $link Target IRI for the link
|
35 |
-
* @param array $attributes Link parameters to send along with the URL
|
36 |
-
*/
|
37 |
-
public function add_link( $rel, $href, $attributes = array() ) {
|
38 |
-
if ( empty( $this->links[ $rel ] ) ) {
|
39 |
-
$this->links[ $rel ] = array();
|
40 |
-
}
|
41 |
-
|
42 |
-
if ( isset( $attributes['href'] ) ) {
|
43 |
-
// Remove the href attribute, as it's used for the main URL
|
44 |
-
unset( $attributes['href'] );
|
45 |
-
}
|
46 |
-
|
47 |
-
$this->links[ $rel ][] = array(
|
48 |
-
'href' => $href,
|
49 |
-
'attributes' => $attributes,
|
50 |
-
);
|
51 |
-
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Add multiple links to the response.
|
55 |
-
*
|
56 |
-
* Link data should be an associative array with link relation as the key.
|
57 |
-
* The value can either be an associative array of link attributes
|
58 |
-
* (including `href` with the URL for the response), or a list of these
|
59 |
-
* associative arrays.
|
60 |
-
*
|
61 |
-
* @param array $links Map of link relation to list of links.
|
62 |
-
*/
|
63 |
-
public function add_links( $links ) {
|
64 |
-
foreach ( $links as $rel => $set ) {
|
65 |
-
// If it's a single link, wrap with an array for consistent handling
|
66 |
-
if ( isset( $set['href'] ) ) {
|
67 |
-
$set = array( $set );
|
68 |
-
}
|
69 |
-
|
70 |
-
foreach ( $set as $attributes ) {
|
71 |
-
$this->add_link( $rel, $attributes['href'], $attributes );
|
72 |
-
}
|
73 |
-
}
|
74 |
-
}
|
75 |
-
|
76 |
-
/**
|
77 |
-
* Get links for the response
|
78 |
-
*
|
79 |
-
* @return array
|
80 |
-
*/
|
81 |
-
public function get_links() {
|
82 |
-
return $this->links;
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Set a single link header
|
87 |
-
*
|
88 |
-
* @internal The $rel parameter is first, as this looks nicer when sending multiple
|
89 |
-
*
|
90 |
-
* @link http://tools.ietf.org/html/rfc5988
|
91 |
-
* @link http://www.iana.org/assignments/link-relations/link-relations.xml
|
92 |
-
*
|
93 |
-
* @param string $rel Link relation. Either an IANA registered type, or an absolute URL
|
94 |
-
* @param string $link Target IRI for the link
|
95 |
-
* @param array $other Other parameters to send, as an assocative array
|
96 |
-
*/
|
97 |
-
public function link_header( $rel, $link, $other = array() ) {
|
98 |
-
$header = '<' . $link . '>; rel="' . $rel . '"';
|
99 |
-
|
100 |
-
foreach ( $other as $key => $value ) {
|
101 |
-
if ( 'title' === $key ) {
|
102 |
-
$value = '"' . $value . '"';
|
103 |
-
}
|
104 |
-
$header .= '; ' . $key . '=' . $value;
|
105 |
-
}
|
106 |
-
return $this->header( 'Link', $header, false );
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* Get the route that was used to
|
111 |
-
*
|
112 |
-
* @return string
|
113 |
-
*/
|
114 |
-
public function get_matched_route() {
|
115 |
-
return $this->matched_route;
|
116 |
-
}
|
117 |
-
|
118 |
-
/**
|
119 |
-
* Set the route (regex for path) that caused the response
|
120 |
-
*
|
121 |
-
* @param string $route
|
122 |
-
*/
|
123 |
-
public function set_matched_route( $route ) {
|
124 |
-
$this->matched_route = $route;
|
125 |
-
}
|
126 |
-
|
127 |
-
/**
|
128 |
-
* Get the handler that was used to generate the response
|
129 |
-
*
|
130 |
-
* @return null|array
|
131 |
-
*/
|
132 |
-
public function get_matched_handler() {
|
133 |
-
return $this->matched_handler;
|
134 |
-
}
|
135 |
-
|
136 |
-
/**
|
137 |
-
* Get the handler that was responsible for generting the response
|
138 |
-
*
|
139 |
-
* @param array $handler
|
140 |
-
*/
|
141 |
-
public function set_matched_handler( $handler ) {
|
142 |
-
$this->matched_handler = $handler;
|
143 |
-
}
|
144 |
-
|
145 |
-
/**
|
146 |
-
* Check if the response is an error, i.e. >= 400 response code
|
147 |
-
*
|
148 |
-
* @return boolean
|
149 |
-
*/
|
150 |
-
public function is_error() {
|
151 |
-
return $this->get_status() >= 400;
|
152 |
-
}
|
153 |
-
|
154 |
-
/**
|
155 |
-
* Get a WP_Error object from the response's
|
156 |
-
*
|
157 |
-
* @return WP_Error|null on not an errored response
|
158 |
-
*/
|
159 |
-
public function as_error() {
|
160 |
-
if ( ! $this->is_error() ) {
|
161 |
-
return null;
|
162 |
-
}
|
163 |
-
|
164 |
-
$error = new WP_Error;
|
165 |
-
|
166 |
-
if ( is_array( $this->get_data() ) ) {
|
167 |
-
foreach ( $this->get_data() as $err ) {
|
168 |
-
$error->add( $err['code'], $err['message'], $err['data'] );
|
169 |
-
}
|
170 |
-
} else {
|
171 |
-
$error->add( $this->get_status(), '', array( 'status' => $this->get_status() ) );
|
172 |
-
}
|
173 |
-
|
174 |
-
return $error;
|
175 |
-
}
|
176 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/infrastructure/class-wp-rest-server.php
DELETED
@@ -1,962 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* WordPress REST API
|
4 |
-
*
|
5 |
-
* Contains the WP_REST_Server class.
|
6 |
-
*
|
7 |
-
* @package WordPress
|
8 |
-
*/
|
9 |
-
|
10 |
-
require_once ( ABSPATH . 'wp-admin/includes/admin.php' );
|
11 |
-
|
12 |
-
/**
|
13 |
-
* WordPress REST API server handler
|
14 |
-
*
|
15 |
-
* @package WordPress
|
16 |
-
*/
|
17 |
-
class WP_REST_Server {
|
18 |
-
const METHOD_GET = 'GET';
|
19 |
-
const METHOD_POST = 'POST';
|
20 |
-
const METHOD_PUT = 'PUT';
|
21 |
-
const METHOD_PATCH = 'PATCH';
|
22 |
-
const METHOD_DELETE = 'DELETE';
|
23 |
-
|
24 |
-
const READABLE = 'GET';
|
25 |
-
const CREATABLE = 'POST';
|
26 |
-
const EDITABLE = 'POST, PUT, PATCH';
|
27 |
-
const DELETABLE = 'DELETE';
|
28 |
-
const ALLMETHODS = 'GET, POST, PUT, PATCH, DELETE';
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Does the endpoint accept raw JSON entities?
|
32 |
-
*/
|
33 |
-
const ACCEPT_RAW = 64;
|
34 |
-
const ACCEPT_JSON = 128;
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Should we hide this endpoint from the index?
|
38 |
-
*/
|
39 |
-
const HIDDEN_ENDPOINT = 256;
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Map of HTTP verbs to constants
|
43 |
-
* @var array
|
44 |
-
*/
|
45 |
-
public static $method_map = array(
|
46 |
-
'HEAD' => self::METHOD_GET,
|
47 |
-
'GET' => self::METHOD_GET,
|
48 |
-
'POST' => self::METHOD_POST,
|
49 |
-
'PUT' => self::METHOD_PUT,
|
50 |
-
'PATCH' => self::METHOD_PATCH,
|
51 |
-
'DELETE' => self::METHOD_DELETE,
|
52 |
-
);
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Namespaces registered to the server
|
56 |
-
*
|
57 |
-
* @var array
|
58 |
-
*/
|
59 |
-
protected $namespaces = array();
|
60 |
-
|
61 |
-
/**
|
62 |
-
* Endpoints registered to the server
|
63 |
-
*
|
64 |
-
* @var array
|
65 |
-
*/
|
66 |
-
protected $endpoints = array();
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Options defined for the routes
|
70 |
-
*
|
71 |
-
* @var array
|
72 |
-
*/
|
73 |
-
protected $route_options = array();
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Instantiate the server
|
77 |
-
*/
|
78 |
-
public function __construct() {
|
79 |
-
$this->endpoints = array(
|
80 |
-
// Meta endpoints
|
81 |
-
'/' => array(
|
82 |
-
'callback' => array( $this, 'get_index' ),
|
83 |
-
'methods' => 'GET',
|
84 |
-
),
|
85 |
-
);
|
86 |
-
}
|
87 |
-
|
88 |
-
|
89 |
-
/**
|
90 |
-
* Check the authentication headers if supplied
|
91 |
-
*
|
92 |
-
* @return WP_Error|null WP_Error indicates unsuccessful login, null indicates successful or no authentication provided
|
93 |
-
*/
|
94 |
-
public function check_authentication() {
|
95 |
-
/**
|
96 |
-
* Pass an authentication error to the API
|
97 |
-
*
|
98 |
-
* This is used to pass a {@see WP_Error} from an authentication method
|
99 |
-
* back to the API.
|
100 |
-
*
|
101 |
-
* Authentication methods should check first if they're being used, as
|
102 |
-
* multiple authentication methods can be enabled on a site (cookies,
|
103 |
-
* HTTP basic auth, OAuth). If the authentication method hooked in is
|
104 |
-
* not actually being attempted, null should be returned to indicate
|
105 |
-
* another authentication method should check instead. Similarly,
|
106 |
-
* callbacks should ensure the value is `null` before checking for
|
107 |
-
* errors.
|
108 |
-
*
|
109 |
-
* A {@see WP_Error} instance can be returned if an error occurs, and
|
110 |
-
* this should match the format used by API methods internally (that is,
|
111 |
-
* the `status` data should be used). A callback can return `true` to
|
112 |
-
* indicate that the authentication method was used, and it succeeded.
|
113 |
-
*
|
114 |
-
* @param WP_Error|null|boolean WP_Error if authentication error, null if authentication method wasn't used, true if authentication succeeded
|
115 |
-
*/
|
116 |
-
return apply_filters( 'rest_authentication_errors', null );
|
117 |
-
}
|
118 |
-
|
119 |
-
/**
|
120 |
-
* Convert an error to a response object
|
121 |
-
*
|
122 |
-
* This iterates over all error codes and messages to change it into a flat
|
123 |
-
* array. This enables simpler client behaviour, as it is represented as a
|
124 |
-
* list in JSON rather than an object/map
|
125 |
-
*
|
126 |
-
* @param WP_Error $error
|
127 |
-
* @return array List of associative arrays with code and message keys
|
128 |
-
*/
|
129 |
-
protected function error_to_response( $error ) {
|
130 |
-
$error_data = $error->get_error_data();
|
131 |
-
if ( is_array( $error_data ) && isset( $error_data['status'] ) ) {
|
132 |
-
$status = $error_data['status'];
|
133 |
-
} else {
|
134 |
-
$status = 500;
|
135 |
-
}
|
136 |
-
|
137 |
-
$data = array();
|
138 |
-
foreach ( (array) $error->errors as $code => $messages ) {
|
139 |
-
foreach ( (array) $messages as $message ) {
|
140 |
-
$data[] = array( 'code' => $code, 'message' => $message, 'data' => $error->get_error_data( $code ) );
|
141 |
-
}
|
142 |
-
}
|
143 |
-
$response = new WP_REST_Response( $data, $status );
|
144 |
-
|
145 |
-
return $response;
|
146 |
-
}
|
147 |
-
|
148 |
-
/**
|
149 |
-
* Get an appropriate error representation in JSON
|
150 |
-
*
|
151 |
-
* Note: This should only be used in {@see WP_REST_Server::serve_request()},
|
152 |
-
* as it cannot handle WP_Error internally. All callbacks and other internal
|
153 |
-
* methods should instead return a WP_Error with the data set to an array
|
154 |
-
* that includes a 'status' key, with the value being the HTTP status to
|
155 |
-
* send.
|
156 |
-
*
|
157 |
-
* @param string $code WP_Error-style code
|
158 |
-
* @param string $message Human-readable message
|
159 |
-
* @param int $status HTTP status code to send
|
160 |
-
* @return string JSON representation of the error
|
161 |
-
*/
|
162 |
-
protected function json_error( $code, $message, $status = null ) {
|
163 |
-
if ( $status ) {
|
164 |
-
$this->set_status( $status );
|
165 |
-
}
|
166 |
-
$error = compact( 'code', 'message' );
|
167 |
-
|
168 |
-
return json_encode( array( $error ) );
|
169 |
-
}
|
170 |
-
|
171 |
-
/**
|
172 |
-
* Handle serving an API request
|
173 |
-
*
|
174 |
-
* Matches the current server URI to a route and runs the first matching
|
175 |
-
* callback then outputs a JSON representation of the returned value.
|
176 |
-
*
|
177 |
-
* @uses WP_REST_Server::dispatch()
|
178 |
-
*/
|
179 |
-
public function serve_request( $path = null ) {
|
180 |
-
$content_type = isset( $_GET['_jsonp'] ) ? 'application/javascript' : 'application/json';
|
181 |
-
$this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) );
|
182 |
-
|
183 |
-
// Mitigate possible JSONP Flash attacks
|
184 |
-
// http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
|
185 |
-
$this->send_header( 'X-Content-Type-Options', 'nosniff' );
|
186 |
-
|
187 |
-
// Proper filter for turning off the JSON API. It is on by default.
|
188 |
-
$enabled = apply_filters( 'rest_enabled', true );
|
189 |
-
|
190 |
-
$jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true );
|
191 |
-
|
192 |
-
if ( ! $enabled ) {
|
193 |
-
echo $this->json_error( 'rest_disabled', __( 'The REST API is disabled on this site.' ), 404 );
|
194 |
-
return false;
|
195 |
-
}
|
196 |
-
if ( isset( $_GET['_jsonp'] ) ) {
|
197 |
-
if ( ! $jsonp_enabled ) {
|
198 |
-
echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 );
|
199 |
-
return false;
|
200 |
-
}
|
201 |
-
|
202 |
-
// Check for invalid characters (only alphanumeric allowed)
|
203 |
-
if ( ! is_string( $_GET['_jsonp'] ) || preg_match( '/\W\./', $_GET['_jsonp'] ) ) {
|
204 |
-
echo $this->json_error( 'rest_callback_invalid', __( 'The JSONP callback function is invalid.' ), 400 );
|
205 |
-
return false;
|
206 |
-
}
|
207 |
-
}
|
208 |
-
|
209 |
-
if ( empty( $path ) ) {
|
210 |
-
if ( isset( $_SERVER['PATH_INFO'] ) ) {
|
211 |
-
$path = $_SERVER['PATH_INFO'];
|
212 |
-
} else {
|
213 |
-
$path = '/';
|
214 |
-
}
|
215 |
-
}
|
216 |
-
|
217 |
-
$request = new WP_REST_Request( $_SERVER['REQUEST_METHOD'], $path );
|
218 |
-
$request->set_query_params( $_GET );
|
219 |
-
$request->set_body_params( $_POST );
|
220 |
-
$request->set_file_params( $_FILES );
|
221 |
-
$request->set_headers( $this->get_headers( $_SERVER ) );
|
222 |
-
$request->set_body( $this->get_raw_data() );
|
223 |
-
|
224 |
-
/**
|
225 |
-
* HTTP method override for clients that can't use PUT/PATCH/DELETE. First, we check
|
226 |
-
* $_GET['_method']. If that is not set, we check for the HTTP_X_HTTP_METHOD_OVERRIDE
|
227 |
-
* header.
|
228 |
-
*/
|
229 |
-
if ( isset( $_GET['_method'] ) ) {
|
230 |
-
$request->set_method( $_GET['_method'] );
|
231 |
-
} elseif ( isset( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ) {
|
232 |
-
$request->set_method( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] );
|
233 |
-
}
|
234 |
-
|
235 |
-
$result = $this->check_authentication();
|
236 |
-
|
237 |
-
if ( ! is_wp_error( $result ) ) {
|
238 |
-
/**
|
239 |
-
* Allow hijacking the request before dispatching
|
240 |
-
*
|
241 |
-
* If `$result` is non-empty, this value will be used to serve the
|
242 |
-
* request instead.
|
243 |
-
*
|
244 |
-
* @param mixed $result Response to replace the requested version with. Can be anything a normal endpoint can return, or null to not hijack the request.
|
245 |
-
* @param WP_REST_Server $this Server instance
|
246 |
-
* @param WP_REST_Request $request Request used to generate the response
|
247 |
-
*/
|
248 |
-
$result = apply_filters( 'rest_pre_dispatch', null, $this, $request );
|
249 |
-
}
|
250 |
-
|
251 |
-
if ( empty( $result ) ) {
|
252 |
-
$result = $this->dispatch( $request );
|
253 |
-
}
|
254 |
-
|
255 |
-
// Normalize to either WP_Error or WP_REST_Response...
|
256 |
-
$result = rest_ensure_response( $result );
|
257 |
-
|
258 |
-
// ...then convert WP_Error across
|
259 |
-
if ( is_wp_error( $result ) ) {
|
260 |
-
$result = $this->error_to_response( $result );
|
261 |
-
}
|
262 |
-
|
263 |
-
/**
|
264 |
-
* Allow modifying the response before returning
|
265 |
-
*
|
266 |
-
* @param WP_HTTP_ResponseInterface $result Result to send to the client. Usually a WP_REST_Response
|
267 |
-
* @param WP_REST_Server $this Server instance
|
268 |
-
* @param WP_REST_Request $request Request used to generate the response
|
269 |
-
*/
|
270 |
-
$result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $request );
|
271 |
-
|
272 |
-
// Wrap the response in an envelope if asked for
|
273 |
-
if ( isset( $_GET['_envelope'] ) ) {
|
274 |
-
$result = $this->envelope_response( $result, isset( $_GET['_embed'] ) );
|
275 |
-
}
|
276 |
-
|
277 |
-
// Send extra data from response objects
|
278 |
-
$headers = $result->get_headers();
|
279 |
-
$this->send_headers( $headers );
|
280 |
-
|
281 |
-
$code = $result->get_status();
|
282 |
-
$this->set_status( $code );
|
283 |
-
|
284 |
-
/**
|
285 |
-
* Allow sending the request manually
|
286 |
-
*
|
287 |
-
* If `$served` is true, the result will not be sent to the client.
|
288 |
-
*
|
289 |
-
* This is a filter rather than an action, since this is designed to be
|
290 |
-
* re-entrant if needed.
|
291 |
-
*
|
292 |
-
* @param bool $served Whether the request has already been served
|
293 |
-
* @param WP_HTTP_ResponseInterface $result Result to send to the client. Usually a WP_REST_Response
|
294 |
-
* @param WP_REST_Request $request Request used to generate the response
|
295 |
-
* @param WP_REST_Server $this Server instance
|
296 |
-
*/
|
297 |
-
$served = apply_filters( 'rest_pre_serve_request', false, $result, $request, $this );
|
298 |
-
|
299 |
-
if ( ! $served ) {
|
300 |
-
if ( 'HEAD' === $request->get_method() ) {
|
301 |
-
return;
|
302 |
-
}
|
303 |
-
|
304 |
-
// Embed links inside the request
|
305 |
-
$result = $this->response_to_data( $result, isset( $_GET['_embed'] ) );
|
306 |
-
|
307 |
-
$result = json_encode( $result );
|
308 |
-
|
309 |
-
$json_error_message = $this->get_json_last_error();
|
310 |
-
if ( $json_error_message ) {
|
311 |
-
$json_error_obj = new WP_Error( 'rest_encode_error', $json_error_message, array( 'status' => 500 ) );
|
312 |
-
$result = $this->error_to_response( $json_error_obj );
|
313 |
-
$result = json_encode( $result->data[0] );
|
314 |
-
}
|
315 |
-
|
316 |
-
if ( isset( $_GET['_jsonp'] ) ) {
|
317 |
-
// Prepend '/**/' to mitigate possible JSONP Flash attacks
|
318 |
-
// http://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
|
319 |
-
echo '/**/' . $_GET['_jsonp'] . '(' . $result . ')';
|
320 |
-
} else {
|
321 |
-
echo $result;
|
322 |
-
}
|
323 |
-
}
|
324 |
-
}
|
325 |
-
|
326 |
-
/**
|
327 |
-
* Convert a response to data to send
|
328 |
-
*
|
329 |
-
* @param WP_REST_Response $response Response object
|
330 |
-
* @param boolean $embed Should we embed links?
|
331 |
-
* @return array
|
332 |
-
*/
|
333 |
-
public function response_to_data( $response, $embed ) {
|
334 |
-
$data = $this->prepare_response( $response->get_data() );
|
335 |
-
$links = $this->get_response_links( $response );
|
336 |
-
|
337 |
-
if ( ! empty( $links ) ) {
|
338 |
-
// Convert links to part of the data
|
339 |
-
$data['_links'] = $links;
|
340 |
-
|
341 |
-
if ( $embed ) {
|
342 |
-
$data = $this->embed_links( $data );
|
343 |
-
}
|
344 |
-
}
|
345 |
-
|
346 |
-
return $data;
|
347 |
-
}
|
348 |
-
|
349 |
-
/**
|
350 |
-
* Get links from a response.
|
351 |
-
*
|
352 |
-
* Extracts the links from a reponse into a structured hash, suitable for
|
353 |
-
* direct output.
|
354 |
-
*
|
355 |
-
* @param WP_REST_Response $response Response to extract links from.
|
356 |
-
* @return array Map of link relation to list of link hashes.
|
357 |
-
*/
|
358 |
-
public static function get_response_links( $response ) {
|
359 |
-
$links = $response->get_links();
|
360 |
-
|
361 |
-
if ( empty( $links ) ) {
|
362 |
-
return array();
|
363 |
-
}
|
364 |
-
|
365 |
-
// Convert links to part of the data
|
366 |
-
$data = array();
|
367 |
-
foreach ( $links as $rel => $items ) {
|
368 |
-
$data[ $rel ] = array();
|
369 |
-
|
370 |
-
foreach ( $items as $item ) {
|
371 |
-
$attributes = $item['attributes'];
|
372 |
-
$attributes['href'] = $item['href'];
|
373 |
-
$data[ $rel ][] = $attributes;
|
374 |
-
}
|
375 |
-
}
|
376 |
-
|
377 |
-
return $data;
|
378 |
-
}
|
379 |
-
|
380 |
-
/**
|
381 |
-
* Embed the links from the data into the request
|
382 |
-
*
|
383 |
-
* @param array $data Data from the request
|
384 |
-
* @return array Data with sub-requests embedded
|
385 |
-
*/
|
386 |
-
protected function embed_links( $data ) {
|
387 |
-
if ( empty( $data['_links'] ) ) {
|
388 |
-
return $data;
|
389 |
-
}
|
390 |
-
|
391 |
-
$embedded = array();
|
392 |
-
$api_root = rest_url();
|
393 |
-
foreach ( $data['_links'] as $rel => $links ) {
|
394 |
-
// Ignore links to self, for obvious reasons
|
395 |
-
if ( 'self' === $rel ) {
|
396 |
-
continue;
|
397 |
-
}
|
398 |
-
|
399 |
-
$embeds = array();
|
400 |
-
|
401 |
-
foreach ( $links as $item ) {
|
402 |
-
// Is the link embeddable?
|
403 |
-
if ( empty( $item['embeddable'] ) || strpos( $item['href'], $api_root ) !== 0 ) {
|
404 |
-
// Ensure we keep the same order
|
405 |
-
$embeds[] = array();
|
406 |
-
continue;
|
407 |
-
}
|
408 |
-
|
409 |
-
// Run through our internal routing and serve
|
410 |
-
$route = substr( $item['href'], strlen( untrailingslashit( $api_root ) ) );
|
411 |
-
$query_params = array();
|
412 |
-
|
413 |
-
// Parse out URL query parameters
|
414 |
-
$parsed = parse_url( $route );
|
415 |
-
if ( empty( $parsed['path'] ) ) {
|
416 |
-
$embeds[] = array();
|
417 |
-
continue;
|
418 |
-
}
|
419 |
-
|
420 |
-
if ( ! empty( $parsed['query'] ) ) {
|
421 |
-
parse_str( $parsed['query'], $query_params );
|
422 |
-
|
423 |
-
// Ensure magic quotes are stripped
|
424 |
-
// @codeCoverageIgnoreStart
|
425 |
-
if ( get_magic_quotes_gpc() ) {
|
426 |
-
$query_params = stripslashes_deep( $query_params );
|
427 |
-
}
|
428 |
-
// @codeCoverageIgnoreEnd
|
429 |
-
}
|
430 |
-
|
431 |
-
// Embedded resources get passed context=embed
|
432 |
-
$query_params['context'] = 'embed';
|
433 |
-
|
434 |
-
$request = new WP_REST_Request( 'GET', $parsed['path'] );
|
435 |
-
$request->set_query_params( $query_params );
|
436 |
-
$response = $this->dispatch( $request );
|
437 |
-
|
438 |
-
$embeds[] = $response;
|
439 |
-
}
|
440 |
-
|
441 |
-
// Did we get any real links?
|
442 |
-
$has_links = count( array_filter( $embeds ) );
|
443 |
-
if ( $has_links ) {
|
444 |
-
$embedded[ $rel ] = $embeds;
|
445 |
-
}
|
446 |
-
}
|
447 |
-
|
448 |
-
if ( ! empty( $embedded ) ) {
|
449 |
-
$data['_embedded'] = $embedded;
|
450 |
-
}
|
451 |
-
|
452 |
-
return $data;
|
453 |
-
}
|
454 |
-
|
455 |
-
/**
|
456 |
-
* Wrap the response in an envelope
|
457 |
-
*
|
458 |
-
* The enveloping technique is used to work around browser/client
|
459 |
-
* compatibility issues. Essentially, it converts the full HTTP response to
|
460 |
-
* data instead.
|
461 |
-
*
|
462 |
-
* @param WP_REST_Response $response Response object
|
463 |
-
* @param boolean $embed Should we embed links?
|
464 |
-
* @return WP_REST_Response New reponse with wrapped data
|
465 |
-
*/
|
466 |
-
public function envelope_response( $response, $embed ) {
|
467 |
-
$envelope = array(
|
468 |
-
'body' => $this->response_to_data( $response, $embed ),
|
469 |
-
'status' => $response->get_status(),
|
470 |
-
'headers' => $response->get_headers(),
|
471 |
-
);
|
472 |
-
|
473 |
-
/**
|
474 |
-
* Alter the enveloped form of a response
|
475 |
-
*
|
476 |
-
* @param array $envelope Envelope data
|
477 |
-
* @param WP_REST_Response $response Original response data
|
478 |
-
*/
|
479 |
-
$envelope = apply_filters( 'rest_envelope_response', $envelope, $response );
|
480 |
-
|
481 |
-
// Ensure it's still a response
|
482 |
-
return rest_ensure_response( $envelope );
|
483 |
-
}
|
484 |
-
|
485 |
-
/**
|
486 |
-
* Register a route to the server
|
487 |
-
*
|
488 |
-
* @param string $route
|
489 |
-
* @param array $route_args
|
490 |
-
* @param boolean $override If the route already exists, should we override it? True overrides, false merges (with newer overriding if duplicate keys exist)
|
491 |
-
*/
|
492 |
-
public function register_route( $namespace, $route, $route_args, $override = false ) {
|
493 |
-
if ( ! isset( $this->namespaces[ $namespace ] ) ) {
|
494 |
-
$this->namespaces[ $namespace ] = array();
|
495 |
-
|
496 |
-
$this->register_route( $namespace, '/' . $namespace, array(
|
497 |
-
array(
|
498 |
-
'methods' => self::READABLE,
|
499 |
-
'callback' => array( $this, 'get_namespace_index' ),
|
500 |
-
'args' => array(
|
501 |
-
'namespace' => array(
|
502 |
-
'default' => $namespace,
|
503 |
-
),
|
504 |
-
),
|
505 |
-
),
|
506 |
-
) );
|
507 |
-
}
|
508 |
-
|
509 |
-
// Associative to avoid double-registration
|
510 |
-
$this->namespaces[ $namespace ][ $route ] = true;
|
511 |
-
$route_args['namespace'] = $namespace;
|
512 |
-
|
513 |
-
if ( $override || empty( $this->endpoints[ $route ] ) ) {
|
514 |
-
$this->endpoints[ $route ] = $route_args;
|
515 |
-
} else {
|
516 |
-
$this->endpoints[ $route ] = array_merge( $this->endpoints[ $route ], $route_args );
|
517 |
-
}
|
518 |
-
}
|
519 |
-
|
520 |
-
/**
|
521 |
-
* Retrieve the route map
|
522 |
-
*
|
523 |
-
* The route map is an associative array with path regexes as the keys. The
|
524 |
-
* value is an indexed array with the callback function/method as the first
|
525 |
-
* item, and a bitmask of HTTP methods as the second item (see the class
|
526 |
-
* constants).
|
527 |
-
*
|
528 |
-
* Each route can be mapped to more than one callback by using an array of
|
529 |
-
* the indexed arrays. This allows mapping e.g. GET requests to one callback
|
530 |
-
* and POST requests to another.
|
531 |
-
*
|
532 |
-
* Note that the path regexes (array keys) must have @ escaped, as this is
|
533 |
-
* used as the delimiter with preg_match()
|
534 |
-
*
|
535 |
-
* @return array `'/path/regex' => array( $callback, $bitmask )` or `'/path/regex' => array( array( $callback, $bitmask ), ...)`
|
536 |
-
*/
|
537 |
-
public function get_routes() {
|
538 |
-
|
539 |
-
$endpoints = apply_filters( 'rest_endpoints', $this->endpoints );
|
540 |
-
|
541 |
-
// Normalise the endpoints
|
542 |
-
$defaults = array(
|
543 |
-
'methods' => '',
|
544 |
-
'accept_json' => false,
|
545 |
-
'accept_raw' => false,
|
546 |
-
'show_in_index' => true,
|
547 |
-
'args' => array(),
|
548 |
-
);
|
549 |
-
foreach ( $endpoints as $route => &$handlers ) {
|
550 |
-
if ( isset( $handlers['callback'] ) ) {
|
551 |
-
// Single endpoint, add one deeper
|
552 |
-
$handlers = array( $handlers );
|
553 |
-
}
|
554 |
-
if ( ! isset( $this->route_options[ $route ] ) ) {
|
555 |
-
$this->route_options[ $route ] = array();
|
556 |
-
}
|
557 |
-
|
558 |
-
foreach ( $handlers as $key => &$handler ) {
|
559 |
-
if ( ! is_numeric( $key ) ) {
|
560 |
-
// Route option, move it to the options
|
561 |
-
$this->route_options[ $route ][ $key ] = $handler;
|
562 |
-
unset( $handlers[ $key ] );
|
563 |
-
continue;
|
564 |
-
}
|
565 |
-
$handler = wp_parse_args( $handler, $defaults );
|
566 |
-
|
567 |
-
// Allow comma-separated HTTP methods
|
568 |
-
if ( is_string( $handler['methods'] ) ) {
|
569 |
-
$methods = explode( ',', $handler['methods'] );
|
570 |
-
} else if ( is_array( $handler['methods'] ) ) {
|
571 |
-
$methods = $handler['methods'];
|
572 |
-
}
|
573 |
-
|
574 |
-
$handler['methods'] = array();
|
575 |
-
foreach ( $methods as $method ) {
|
576 |
-
$method = strtoupper( trim( $method ) );
|
577 |
-
$handler['methods'][ $method ] = true;
|
578 |
-
}
|
579 |
-
}
|
580 |
-
}
|
581 |
-
return $endpoints;
|
582 |
-
}
|
583 |
-
|
584 |
-
/**
|
585 |
-
* Get namespaces registered on the server.
|
586 |
-
*
|
587 |
-
* @return array List of registered namespaces.
|
588 |
-
*/
|
589 |
-
public function get_namespaces() {
|
590 |
-
return array_keys( $this->namespaces );
|
591 |
-
}
|
592 |
-
|
593 |
-
/**
|
594 |
-
* Match the request to a callback and call it
|
595 |
-
*
|
596 |
-
* @param WP_REST_Request $request Request to attempt dispatching
|
597 |
-
* @return WP_REST_Response Response returned by the callback
|
598 |
-
*/
|
599 |
-
public function dispatch( $request ) {
|
600 |
-
$method = $request->get_method();
|
601 |
-
$path = $request->get_route();
|
602 |
-
|
603 |
-
foreach ( $this->get_routes() as $route => $handlers ) {
|
604 |
-
foreach ( $handlers as $handler ) {
|
605 |
-
$callback = $handler['callback'];
|
606 |
-
$supported = $handler['methods'];
|
607 |
-
$response = null;
|
608 |
-
|
609 |
-
if ( empty( $handler['methods'][ $method ] ) ) {
|
610 |
-
continue;
|
611 |
-
}
|
612 |
-
|
613 |
-
$match = preg_match( '@^' . $route . '$@i', $path, $args );
|
614 |
-
|
615 |
-
if ( ! $match ) {
|
616 |
-
continue;
|
617 |
-
}
|
618 |
-
|
619 |
-
if ( ! is_callable( $callback ) ) {
|
620 |
-
$response = new WP_Error( 'rest_invalid_handler', __( 'The handler for the route is invalid' ), array( 'status' => 500 ) );
|
621 |
-
}
|
622 |
-
|
623 |
-
if ( ! is_wp_error( $response ) ) {
|
624 |
-
|
625 |
-
$request->set_url_params( $args );
|
626 |
-
$request->set_attributes( $handler );
|
627 |
-
|
628 |
-
$request->sanitize_params();
|
629 |
-
|
630 |
-
$defaults = array();
|
631 |
-
|
632 |
-
foreach ( $handler['args'] as $arg => $options ) {
|
633 |
-
if ( isset( $options['default'] ) ) {
|
634 |
-
$defaults[ $arg ] = $options['default'];
|
635 |
-
}
|
636 |
-
}
|
637 |
-
|
638 |
-
$request->set_default_params( $defaults );
|
639 |
-
|
640 |
-
$check_required = $request->has_valid_params();
|
641 |
-
if ( is_wp_error( $check_required ) ) {
|
642 |
-
$response = $check_required;
|
643 |
-
}
|
644 |
-
}
|
645 |
-
|
646 |
-
if ( ! is_wp_error( $response ) ) {
|
647 |
-
// check permission specified on the route.
|
648 |
-
if ( ! empty( $handler['permission_callback'] ) ) {
|
649 |
-
$permission = call_user_func( $handler['permission_callback'], $request );
|
650 |
-
|
651 |
-
if ( is_wp_error( $permission ) ) {
|
652 |
-
$response = $permission;
|
653 |
-
} else if ( false === $permission || null === $permission ) {
|
654 |
-
$response = new WP_Error( 'rest_forbidden', __( "You don't have permission to do this." ), array( 'status' => 403 ) );
|
655 |
-
}
|
656 |
-
}
|
657 |
-
}
|
658 |
-
|
659 |
-
if ( ! is_wp_error( $response ) ) {
|
660 |
-
/**
|
661 |
-
* Allow plugins to override dispatching the request
|
662 |
-
*
|
663 |
-
* @param boolean $dispatch_result Dispatch result, will be used if not empty
|
664 |
-
* @param WP_REST_Request $request
|
665 |
-
*/
|
666 |
-
$dispatch_result = apply_filters( 'rest_dispatch_request', null, $request );
|
667 |
-
|
668 |
-
// Allow plugins to halt the request via this filter
|
669 |
-
if ( null !== $dispatch_result ) {
|
670 |
-
$response = $dispatch_result;
|
671 |
-
} else {
|
672 |
-
$response = call_user_func( $callback, $request );
|
673 |
-
}
|
674 |
-
}
|
675 |
-
|
676 |
-
if ( is_wp_error( $response ) ) {
|
677 |
-
$response = $this->error_to_response( $response );
|
678 |
-
} else {
|
679 |
-
$response = rest_ensure_response( $response );
|
680 |
-
}
|
681 |
-
|
682 |
-
$response->set_matched_route( $route );
|
683 |
-
$response->set_matched_handler( $handler );
|
684 |
-
|
685 |
-
return $response;
|
686 |
-
}
|
687 |
-
}
|
688 |
-
|
689 |
-
return $this->error_to_response( new WP_Error( 'rest_no_route', __( 'No route was found matching the URL and request method' ), array( 'status' => 404 ) ) );
|
690 |
-
}
|
691 |
-
|
692 |
-
/**
|
693 |
-
* Returns if an error occurred during most recent JSON encode/decode
|
694 |
-
* Strings to be translated will be in format like "Encoding error: Maximum stack depth exceeded"
|
695 |
-
*
|
696 |
-
* @return boolean|string Boolean false or string error message
|
697 |
-
*/
|
698 |
-
protected function get_json_last_error( ) {
|
699 |
-
// see https://core.trac.wordpress.org/ticket/27799
|
700 |
-
if ( ! function_exists( 'json_last_error' ) ) {
|
701 |
-
return false;
|
702 |
-
}
|
703 |
-
|
704 |
-
$last_error_code = json_last_error();
|
705 |
-
if ( ( defined( 'JSON_ERROR_NONE' ) && JSON_ERROR_NONE === $last_error_code ) || empty( $last_error_code ) ) {
|
706 |
-
return false;
|
707 |
-
}
|
708 |
-
|
709 |
-
return json_last_error_msg();
|
710 |
-
}
|
711 |
-
|
712 |
-
/**
|
713 |
-
* Get the site index.
|
714 |
-
*
|
715 |
-
* This endpoint describes the capabilities of the site.
|
716 |
-
*
|
717 |
-
* @todo Should we generate text documentation too based on PHPDoc?
|
718 |
-
*
|
719 |
-
* @return array Index entity
|
720 |
-
*/
|
721 |
-
public function get_index() {
|
722 |
-
// General site data
|
723 |
-
$available = array(
|
724 |
-
'name' => get_option( 'blogname' ),
|
725 |
-
'description' => get_option( 'blogdescription' ),
|
726 |
-
'url' => get_option( 'siteurl' ),
|
727 |
-
'namespaces' => array_keys( $this->namespaces ),
|
728 |
-
'authentication' => array(),
|
729 |
-
'routes' => $this->get_route_data( $this->get_routes() ),
|
730 |
-
);
|
731 |
-
|
732 |
-
$response = new WP_REST_Response( $available );
|
733 |
-
$response->add_link( 'help', 'http://v2.wp-api.org/' );
|
734 |
-
|
735 |
-
/**
|
736 |
-
* Filter the API root index data.
|
737 |
-
*
|
738 |
-
* This contains the data describing the API. This includes information
|
739 |
-
* about supported authentication schemes, supported namespaces, routes
|
740 |
-
* available on the API, and a small amount of data about the site.
|
741 |
-
*
|
742 |
-
* @param WP_REST_Response $response Response data.
|
743 |
-
*/
|
744 |
-
return apply_filters( 'rest_index', $response );
|
745 |
-
}
|
746 |
-
|
747 |
-
/**
|
748 |
-
* Get the index for a namespace.
|
749 |
-
*
|
750 |
-
* @param WP_REST_Request $request
|
751 |
-
* @return array|WP_REST_Response
|
752 |
-
*/
|
753 |
-
public function get_namespace_index( $request ) {
|
754 |
-
$namespace = $request['namespace'];
|
755 |
-
|
756 |
-
if ( ! isset( $this->namespaces[ $namespace ] ) ) {
|
757 |
-
return new WP_Error( 'rest_invalid_namespace', __( 'The specified namespace could not be found.' ), array( 'status' => 404 ) );
|
758 |
-
}
|
759 |
-
|
760 |
-
$routes = $this->namespaces[ $namespace ];
|
761 |
-
$endpoints = array_intersect_key( $this->get_routes(), $routes );
|
762 |
-
|
763 |
-
$data = array(
|
764 |
-
'namespace' => $namespace,
|
765 |
-
'routes' => $this->get_route_data( $endpoints ),
|
766 |
-
);
|
767 |
-
$response = rest_ensure_response( $data );
|
768 |
-
|
769 |
-
// Link to the root index
|
770 |
-
$response->add_link( 'up', rest_url( '/' ) );
|
771 |
-
|
772 |
-
/**
|
773 |
-
* Filter the namespace index data.
|
774 |
-
*
|
775 |
-
* This typically is just the route data for the namespace, but you can
|
776 |
-
* add any data you'd like here.
|
777 |
-
*
|
778 |
-
* @param WP_REST_Response $response Response data.
|
779 |
-
* @param WP_REST_Request $request Request data. The namespace is passed as the 'namespace' parameter.
|
780 |
-
*/
|
781 |
-
return apply_filters( 'rest_namespace_index', $response, $request );
|
782 |
-
}
|
783 |
-
|
784 |
-
/**
|
785 |
-
* Get the publicly-visible data for routes.
|
786 |
-
*
|
787 |
-
* @param array $routes Routes to get data for
|
788 |
-
* @return array Route data to expose in indexes.
|
789 |
-
*/
|
790 |
-
protected function get_route_data( $routes ) {
|
791 |
-
$available = array();
|
792 |
-
// Find the available routes
|
793 |
-
foreach ( $routes as $route => $callbacks ) {
|
794 |
-
$data = array(
|
795 |
-
'namespace' => '',
|
796 |
-
'methods' => array(),
|
797 |
-
);
|
798 |
-
if ( isset( $this->route_options[ $route ] ) ) {
|
799 |
-
$options = $this->route_options[ $route ];
|
800 |
-
if ( isset( $options['namespace'] ) ) {
|
801 |
-
$data['namespace'] = $options['namespace'];
|
802 |
-
}
|
803 |
-
}
|
804 |
-
|
805 |
-
$route = preg_replace( '#\(\?P<(\w+?)>.*?\)#', '{$1}', $route );
|
806 |
-
|
807 |
-
foreach ( $callbacks as $callback ) {
|
808 |
-
// Skip to the next route if any callback is hidden
|
809 |
-
if ( empty( $callback['show_in_index'] ) ) {
|
810 |
-
continue;
|
811 |
-
}
|
812 |
-
|
813 |
-
$data['methods'] = array_merge( $data['methods'], array_keys( $callback['methods'] ) );
|
814 |
-
|
815 |
-
// For non-variable routes, generate links
|
816 |
-
if ( strpos( $route, '{' ) === false ) {
|
817 |
-
$data['_links'] = array(
|
818 |
-
'self' => rest_url( $route ),
|
819 |
-
);
|
820 |
-
}
|
821 |
-
}
|
822 |
-
|
823 |
-
if ( empty( $data['methods'] ) ) {
|
824 |
-
// No methods supported, hide the route
|
825 |
-
continue;
|
826 |
-
}
|
827 |
-
|
828 |
-
$available[ $route ] = apply_filters( 'rest_endpoints_description', $data );
|
829 |
-
}
|
830 |
-
|
831 |
-
/**
|
832 |
-
* Filter the publicly-visible data for routes.
|
833 |
-
*
|
834 |
-
* This data is exposed on indexes and can be used by clients or
|
835 |
-
* developers to investigate the site and find out how to use it. It
|
836 |
-
* acts as a form of self-documentation.
|
837 |
-
*
|
838 |
-
* @param array $available Map of route to route data.
|
839 |
-
* @param array $routes Internal route data as an associative array.
|
840 |
-
*/
|
841 |
-
return apply_filters( 'rest_route_data', $available, $routes );
|
842 |
-
}
|
843 |
-
|
844 |
-
/**
|
845 |
-
* Send a HTTP status code
|
846 |
-
*
|
847 |
-
* @param int $code HTTP status
|
848 |
-
*/
|
849 |
-
protected function set_status( $code ) {
|
850 |
-
status_header( $code );
|
851 |
-
}
|
852 |
-
|
853 |
-
/**
|
854 |
-
* Send a HTTP header
|
855 |
-
*
|
856 |
-
* @param string $key Header key
|
857 |
-
* @param string $value Header value
|
858 |
-
*/
|
859 |
-
public function send_header( $key, $value ) {
|
860 |
-
// Sanitize as per RFC2616 (Section 4.2):
|
861 |
-
// Any LWS that occurs between field-content MAY be replaced with a
|
862 |
-
// single SP before interpreting the field value or forwarding the
|
863 |
-
// message downstream.
|
864 |
-
$value = preg_replace( '/\s+/', ' ', $value );
|
865 |
-
header( sprintf( '%s: %s', $key, $value ) );
|
866 |
-
}
|
867 |
-
|
868 |
-
/**
|
869 |
-
* Send multiple HTTP headers
|
870 |
-
*
|
871 |
-
* @param array Map of header name to header value
|
872 |
-
*/
|
873 |
-
public function send_headers( $headers ) {
|
874 |
-
foreach ( $headers as $key => $value ) {
|
875 |
-
$this->send_header( $key, $value );
|
876 |
-
}
|
877 |
-
}
|
878 |
-
|
879 |
-
/**
|
880 |
-
* Retrieve the raw request entity (body)
|
881 |
-
*
|
882 |
-
* @return string
|
883 |
-
*/
|
884 |
-
public function get_raw_data() {
|
885 |
-
global $HTTP_RAW_POST_DATA;
|
886 |
-
|
887 |
-
// A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
|
888 |
-
// but we can do it ourself.
|
889 |
-
if ( ! isset( $HTTP_RAW_POST_DATA ) ) {
|
890 |
-
$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
|
891 |
-
}
|
892 |
-
|
893 |
-
return $HTTP_RAW_POST_DATA;
|
894 |
-
}
|
895 |
-
|
896 |
-
/**
|
897 |
-
* Prepares response data to be serialized to JSON
|
898 |
-
*
|
899 |
-
* This supports the JsonSerializable interface for PHP 5.2-5.3 as well.
|
900 |
-
*
|
901 |
-
* @codeCoverageIgnore This is a compatibility shim.
|
902 |
-
*
|
903 |
-
* @param mixed $data Native representation
|
904 |
-
* @return array|string Data ready for `json_encode()`
|
905 |
-
*/
|
906 |
-
public function prepare_response( $data ) {
|
907 |
-
if ( ! defined( 'WP_REST_SERIALIZE_COMPATIBLE' ) || WP_REST_SERIALIZE_COMPATIBLE === false ) {
|
908 |
-
return $data;
|
909 |
-
}
|
910 |
-
|
911 |
-
switch ( gettype( $data ) ) {
|
912 |
-
case 'boolean':
|
913 |
-
case 'integer':
|
914 |
-
case 'double':
|
915 |
-
case 'string':
|
916 |
-
case 'NULL':
|
917 |
-
// These values can be passed through
|
918 |
-
return $data;
|
919 |
-
|
920 |
-
case 'array':
|
921 |
-
// Arrays must be mapped in case they also return objects
|
922 |
-
return array_map( array( $this, 'prepare_response' ), $data );
|
923 |
-
|
924 |
-
case 'object':
|
925 |
-
if ( $data instanceof JsonSerializable ) {
|
926 |
-
$data = $data->jsonSerialize();
|
927 |
-
} else {
|
928 |
-
$data = get_object_vars( $data );
|
929 |
-
}
|
930 |
-
|
931 |
-
// Now, pass the array (or whatever was returned from
|
932 |
-
// jsonSerialize through.)
|
933 |
-
return $this->prepare_response( $data );
|
934 |
-
|
935 |
-
default:
|
936 |
-
return null;
|
937 |
-
}
|
938 |
-
}
|
939 |
-
|
940 |
-
/**
|
941 |
-
* Extract headers from a PHP-style $_SERVER array
|
942 |
-
*
|
943 |
-
* @param array $server Associative array similar to $_SERVER
|
944 |
-
* @return array Headers extracted from the input
|
945 |
-
*/
|
946 |
-
public function get_headers( $server ) {
|
947 |
-
$headers = array();
|
948 |
-
|
949 |
-
// CONTENT_* headers are not prefixed with HTTP_
|
950 |
-
$additional = array( 'CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true );
|
951 |
-
|
952 |
-
foreach ( $server as $key => $value ) {
|
953 |
-
if ( strpos( $key, 'HTTP_' ) === 0 ) {
|
954 |
-
$headers[ substr( $key, 5 ) ] = $value;
|
955 |
-
} elseif ( isset( $additional[ $key ] ) ) {
|
956 |
-
$headers[ $key ] = $value;
|
957 |
-
}
|
958 |
-
}
|
959 |
-
|
960 |
-
return $headers;
|
961 |
-
}
|
962 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plugin.php
CHANGED
@@ -4,147 +4,145 @@
|
|
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-
|
8 |
* Plugin URI: https://github.com/WP-API/WP-API
|
9 |
* License: GPL2+
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
-
*
|
14 |
-
*
|
15 |
-
* @var string
|
16 |
*/
|
17 |
-
|
18 |
|
19 |
/**
|
20 |
-
*
|
21 |
*/
|
22 |
-
include_once( dirname( __FILE__ ) . '/compatibility-v1.php' );
|
23 |
-
include_once( dirname( __FILE__ ) . '/lib/infrastructure/class-jsonserializable.php' );
|
24 |
-
|
25 |
-
include_once( dirname( __FILE__ ) . '/lib/infrastructure/class-wp-rest-server.php' );
|
26 |
-
|
27 |
-
include_once( dirname( __FILE__ ) . '/lib/infrastructure/class-wp-http-responseinterface.php' );
|
28 |
-
include_once( dirname( __FILE__ ) . '/lib/infrastructure/class-wp-http-response.php' );
|
29 |
-
include_once( dirname( __FILE__ ) . '/lib/infrastructure/class-wp-rest-response.php' );
|
30 |
-
require_once( dirname( __FILE__ ) . '/lib/infrastructure/class-wp-rest-request.php' );
|
31 |
-
|
32 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-controller.php';
|
33 |
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-posts-controller.php';
|
34 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-attachments-controller.php';
|
35 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-post-types-controller.php';
|
36 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-post-statuses-controller.php';
|
37 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-revisions-controller.php';
|
38 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-taxonomies-controller.php';
|
39 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-terms-controller.php';
|
40 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-users-controller.php';
|
41 |
-
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-comments-controller.php';
|
42 |
-
include_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-meta-controller.php';
|
43 |
-
include_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-meta-posts-controller.php';
|
44 |
-
include_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-posts-terms-controller.php';
|
45 |
|
46 |
-
|
|
|
|
|
|
|
47 |
|
|
|
|
|
|
|
|
|
48 |
|
49 |
/**
|
50 |
-
*
|
51 |
-
*
|
52 |
-
* @param string $namespace The first URL segment after core prefix. Should be unique to your package/plugin.
|
53 |
-
* @param string $route The base URL for route you are adding.
|
54 |
-
* @param array $args Either an array of options for the endpoint, or an array of arrays for multiple methods
|
55 |
-
* @param boolean $override If the route already exists, should we override it? True overrides, false merges (with newer overriding if duplicate keys exist)
|
56 |
*/
|
57 |
-
|
58 |
|
59 |
-
|
60 |
-
|
|
|
|
|
61 |
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
);
|
72 |
-
foreach ( $args as &$arg_group ) {
|
73 |
-
$arg_group = array_merge( $defaults, $arg_group );
|
74 |
-
}
|
75 |
|
76 |
-
|
77 |
-
|
78 |
-
|
|
|
79 |
|
80 |
/**
|
81 |
-
*
|
82 |
-
*
|
83 |
-
* @param string|array $object_type "post"|"term"|"comment" etc
|
84 |
-
* @param string $attribute The attribute name
|
85 |
-
* @param array $args
|
86 |
-
* @return bool|wp_error
|
87 |
*/
|
88 |
-
|
89 |
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
);
|
95 |
|
96 |
-
|
|
|
|
|
|
|
97 |
|
98 |
-
|
|
|
|
|
|
|
99 |
|
100 |
-
|
|
|
|
|
|
|
101 |
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
}
|
106 |
|
107 |
/**
|
108 |
-
*
|
|
|
109 |
* These attributes will eventually be committed to core.
|
|
|
|
|
|
|
|
|
110 |
*/
|
111 |
function _add_extra_api_post_type_arguments() {
|
112 |
global $wp_post_types;
|
113 |
|
114 |
-
$wp_post_types['post']
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
$wp_post_types['page']->rest_base = 'pages';
|
120 |
-
$wp_post_types['page']->rest_controller_class = 'WP_REST_Posts_Controller';
|
121 |
|
122 |
-
$wp_post_types['
|
123 |
-
|
124 |
-
|
|
|
|
|
125 |
|
|
|
|
|
|
|
|
|
|
|
126 |
}
|
127 |
-
add_action( 'init', '_add_extra_api_post_type_arguments', 11 );
|
128 |
|
129 |
/**
|
130 |
-
*
|
|
|
131 |
* These attributes will eventually be committed to core.
|
|
|
|
|
|
|
|
|
132 |
*/
|
133 |
function _add_extra_api_taxonomy_arguments() {
|
134 |
global $wp_taxonomies;
|
135 |
|
136 |
-
$wp_taxonomies['category']
|
137 |
-
|
138 |
-
|
|
|
|
|
139 |
|
140 |
-
$wp_taxonomies['post_tag']
|
141 |
-
|
142 |
-
|
|
|
|
|
143 |
}
|
144 |
-
add_action( 'init', '_add_extra_api_taxonomy_arguments', 11 );
|
145 |
|
146 |
/**
|
147 |
-
*
|
|
|
|
|
148 |
*/
|
149 |
function create_initial_rest_routes() {
|
150 |
|
@@ -181,27 +179,19 @@ function create_initial_rest_routes() {
|
|
181 |
}
|
182 |
}
|
183 |
|
184 |
-
|
185 |
-
* Post types
|
186 |
-
*/
|
187 |
$controller = new WP_REST_Post_Types_Controller;
|
188 |
$controller->register_routes();
|
189 |
|
190 |
-
|
191 |
-
* Post statuses
|
192 |
-
*/
|
193 |
$controller = new WP_REST_Post_Statuses_Controller;
|
194 |
$controller->register_routes();
|
195 |
|
196 |
-
|
197 |
-
* Taxonomies
|
198 |
-
*/
|
199 |
$controller = new WP_REST_Taxonomies_Controller;
|
200 |
$controller->register_routes();
|
201 |
|
202 |
-
|
203 |
-
* Terms
|
204 |
-
*/
|
205 |
foreach ( get_taxonomies( array( 'show_in_rest' => true ), 'object' ) as $taxonomy ) {
|
206 |
$class = ! empty( $taxonomy->rest_controller_class ) ? $taxonomy->rest_controller_class : 'WP_REST_Terms_Controller';
|
207 |
|
@@ -216,492 +206,74 @@ function create_initial_rest_routes() {
|
|
216 |
$controller->register_routes();
|
217 |
}
|
218 |
|
219 |
-
|
220 |
-
* Users
|
221 |
-
*/
|
222 |
$controller = new WP_REST_Users_Controller;
|
223 |
$controller->register_routes();
|
224 |
|
225 |
-
|
226 |
-
* Comments
|
227 |
-
*/
|
228 |
$controller = new WP_REST_Comments_Controller;
|
229 |
$controller->register_routes();
|
230 |
-
|
231 |
-
}
|
232 |
-
add_action( 'rest_api_init', 'create_initial_rest_routes', 0 );
|
233 |
-
|
234 |
-
/**
|
235 |
-
* Register rewrite rules for the API.
|
236 |
-
*
|
237 |
-
* @global WP $wp Current WordPress environment instance.
|
238 |
-
*/
|
239 |
-
function rest_api_init() {
|
240 |
-
rest_api_register_rewrites();
|
241 |
-
|
242 |
-
global $wp;
|
243 |
-
$wp->add_query_var( 'rest_route' );
|
244 |
-
}
|
245 |
-
add_action( 'init', 'rest_api_init' );
|
246 |
-
|
247 |
-
/**
|
248 |
-
* Add rewrite rules.
|
249 |
-
*/
|
250 |
-
function rest_api_register_rewrites() {
|
251 |
-
add_rewrite_rule( '^' . rest_get_url_prefix() . '/?$','index.php?rest_route=/','top' );
|
252 |
-
add_rewrite_rule( '^' . rest_get_url_prefix() . '(.*)?','index.php?rest_route=$matches[1]','top' );
|
253 |
-
}
|
254 |
-
|
255 |
-
/**
|
256 |
-
* Determine if the rewrite rules should be flushed.
|
257 |
-
*/
|
258 |
-
function rest_api_maybe_flush_rewrites() {
|
259 |
-
$version = get_option( 'rest_api_plugin_version', null );
|
260 |
-
|
261 |
-
if ( empty( $version ) || REST_API_VERSION !== $version ) {
|
262 |
-
flush_rewrite_rules();
|
263 |
-
update_option( 'rest_api_plugin_version', REST_API_VERSION );
|
264 |
-
}
|
265 |
-
|
266 |
-
}
|
267 |
-
add_action( 'init', 'rest_api_maybe_flush_rewrites', 999 );
|
268 |
-
|
269 |
-
/**
|
270 |
-
* Register the default REST API filters.
|
271 |
-
*
|
272 |
-
* @internal This will live in default-filters.php
|
273 |
-
*
|
274 |
-
* @global WP_REST_Posts $WP_REST_posts
|
275 |
-
* @global WP_REST_Pages $WP_REST_pages
|
276 |
-
* @global WP_REST_Media $WP_REST_media
|
277 |
-
* @global WP_REST_Taxonomies $WP_REST_taxonomies
|
278 |
-
*
|
279 |
-
* @param WP_REST_Server $server Server object.
|
280 |
-
*/
|
281 |
-
function rest_api_default_filters( $server ) {
|
282 |
-
// Deprecated reporting.
|
283 |
-
add_action( 'deprecated_function_run', 'rest_handle_deprecated_function', 10, 3 );
|
284 |
-
add_filter( 'deprecated_function_trigger_error', '__return_false' );
|
285 |
-
add_action( 'deprecated_argument_run', 'rest_handle_deprecated_argument', 10, 3 );
|
286 |
-
add_filter( 'deprecated_argument_trigger_error', '__return_false' );
|
287 |
-
|
288 |
-
// Default serving
|
289 |
-
add_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );
|
290 |
-
add_filter( 'rest_post_dispatch', 'rest_send_allow_header', 10, 3 );
|
291 |
-
|
292 |
-
add_filter( 'rest_pre_dispatch', 'rest_handle_options_request', 10, 3 );
|
293 |
-
|
294 |
}
|
295 |
-
add_action( 'rest_api_init', 'rest_api_default_filters', 10, 1 );
|
296 |
-
|
297 |
-
/**
|
298 |
-
* Load the REST API.
|
299 |
-
*
|
300 |
-
* @todo Extract code that should be unit tested into isolated methods such as
|
301 |
-
* the wp_rest_server_class filter and serving requests. This would also
|
302 |
-
* help for code re-use by `wp-json` endpoint. Note that we can't unit
|
303 |
-
* test any method that calls die().
|
304 |
-
*/
|
305 |
-
function rest_api_loaded() {
|
306 |
-
if ( empty( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
|
307 |
-
return;
|
308 |
-
}
|
309 |
|
|
|
310 |
/**
|
311 |
-
*
|
312 |
*
|
313 |
-
* @
|
314 |
-
* @todo Remove me in favour of REST_REQUEST
|
315 |
*/
|
316 |
-
|
317 |
-
|
318 |
-
/**
|
319 |
-
* Whether this is a REST Request.
|
320 |
-
*
|
321 |
-
* @var bool
|
322 |
-
*/
|
323 |
-
define( 'REST_REQUEST', true );
|
324 |
-
|
325 |
-
/** @var WP_REST_Server $wp_rest_server */
|
326 |
-
global $wp_rest_server;
|
327 |
-
|
328 |
-
// Allow for a plugin to insert a different class to handle requests.
|
329 |
-
$wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' );
|
330 |
-
$wp_rest_server = new $wp_rest_server_class;
|
331 |
-
|
332 |
-
/**
|
333 |
-
* Fires when preparing to serve an API request.
|
334 |
-
*
|
335 |
-
* Endpoint objects should be created and register their hooks on this
|
336 |
-
* action rather than another action to ensure they're only loaded when
|
337 |
-
* needed.
|
338 |
-
*
|
339 |
-
* @param WP_REST_Server $wp_rest_server Server object.
|
340 |
-
*/
|
341 |
-
do_action( 'rest_api_init', $wp_rest_server );
|
342 |
-
|
343 |
-
// Fire off the request.
|
344 |
-
$wp_rest_server->serve_request( $GLOBALS['wp']->query_vars['rest_route'] );
|
345 |
-
|
346 |
-
// We're done.
|
347 |
-
die();
|
348 |
-
}
|
349 |
-
add_action( 'parse_request', 'rest_api_loaded' );
|
350 |
-
|
351 |
-
/**
|
352 |
-
* Register routes and flush the rewrite rules on activation.
|
353 |
-
*
|
354 |
-
* @param bool $network_wide ?
|
355 |
-
*/
|
356 |
-
function rest_api_activation( $network_wide ) {
|
357 |
-
if ( function_exists( 'is_multisite' ) && is_multisite() && $network_wide ) {
|
358 |
-
$mu_blogs = wp_get_sites();
|
359 |
-
|
360 |
-
foreach ( $mu_blogs as $mu_blog ) {
|
361 |
-
switch_to_blog( $mu_blog['blog_id'] );
|
362 |
-
|
363 |
-
rest_api_register_rewrites();
|
364 |
-
update_option( 'rest_api_plugin_version', null );
|
365 |
-
}
|
366 |
-
|
367 |
-
restore_current_blog();
|
368 |
-
} else {
|
369 |
-
rest_api_register_rewrites();
|
370 |
-
update_option( 'rest_api_plugin_version', null );
|
371 |
}
|
372 |
}
|
373 |
-
register_activation_hook( __FILE__, 'rest_api_activation' );
|
374 |
|
375 |
-
|
376 |
-
* Flush the rewrite rules on deactivation.
|
377 |
-
*
|
378 |
-
* @param bool $network_wide ?
|
379 |
-
*/
|
380 |
-
function rest_api_deactivation( $network_wide ) {
|
381 |
-
if ( function_exists( 'is_multisite' ) && is_multisite() && $network_wide ) {
|
382 |
-
|
383 |
-
$mu_blogs = wp_get_sites();
|
384 |
-
|
385 |
-
foreach ( $mu_blogs as $mu_blog ) {
|
386 |
-
switch_to_blog( $mu_blog['blog_id'] );
|
387 |
-
delete_option( 'rest_api_plugin_version' );
|
388 |
-
}
|
389 |
-
|
390 |
-
restore_current_blog();
|
391 |
-
} else {
|
392 |
-
delete_option( 'rest_api_plugin_version' );
|
393 |
-
}
|
394 |
-
}
|
395 |
-
register_deactivation_hook( __FILE__, 'rest_api_deactivation' );
|
396 |
-
|
397 |
-
/**
|
398 |
-
* Get the URL prefix for any API resource.
|
399 |
-
*
|
400 |
-
* @return string Prefix.
|
401 |
-
*/
|
402 |
-
function rest_get_url_prefix() {
|
403 |
/**
|
404 |
-
*
|
405 |
*
|
406 |
-
* @
|
|
|
407 |
*
|
408 |
-
* @param string $
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
/**
|
414 |
-
* Get URL to a REST endpoint on a site.
|
415 |
-
*
|
416 |
-
* @todo Check if this is even necessary
|
417 |
-
*
|
418 |
-
* @param int $blog_id Blog ID. Optional. The ID of the multisite blog to get URL for. Default null of null returns URL for current blog.
|
419 |
-
* @param string $path Optional. REST route. Default empty.
|
420 |
-
* @param string $scheme Optional. Sanitization scheme. Default 'json'.
|
421 |
-
* @return string Full URL to the endpoint.
|
422 |
-
*/
|
423 |
-
function get_rest_url( $blog_id = null, $path = '', $scheme = 'json' ) {
|
424 |
-
if ( get_option( 'permalink_structure' ) ) {
|
425 |
-
$url = get_home_url( $blog_id, rest_get_url_prefix(), $scheme );
|
426 |
-
|
427 |
-
if ( ! empty( $path ) && is_string( $path ) && strpos( $path, '..' ) === false ) {
|
428 |
-
$url .= '/' . ltrim( $path, '/' );
|
429 |
-
}
|
430 |
-
} else {
|
431 |
-
$url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
|
432 |
-
|
433 |
-
if ( empty( $path ) ) {
|
434 |
-
$path = '/';
|
435 |
-
} else {
|
436 |
-
$path = '/' . ltrim( $path, '/' );
|
437 |
-
}
|
438 |
-
|
439 |
-
$url = add_query_arg( 'rest_route', $path, $url );
|
440 |
-
}
|
441 |
-
|
442 |
-
/**
|
443 |
-
* Filter the REST URL.
|
444 |
*
|
445 |
-
*
|
446 |
-
*
|
447 |
-
*
|
448 |
-
*
|
449 |
-
*
|
450 |
-
*
|
|
|
|
|
|
|
451 |
*/
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
* @param string $path Optional. REST route. Default empty.
|
459 |
-
* @param string $scheme Optional. Sanitization scheme. Default 'json'.
|
460 |
-
* @return string Full URL to the endpoint.
|
461 |
-
*/
|
462 |
-
function rest_url( $path = '', $scheme = 'json' ) {
|
463 |
-
return get_rest_url( null, $path, $scheme );
|
464 |
-
}
|
465 |
-
|
466 |
-
/**
|
467 |
-
* Do a REST request.
|
468 |
-
* Used primarily to route internal requests through WP_REST_Server
|
469 |
-
*
|
470 |
-
* @param WP_REST_Request|string $request
|
471 |
-
* @return WP_REST_Response
|
472 |
-
*/
|
473 |
-
function rest_do_request( $request ) {
|
474 |
-
global $wp_rest_server;
|
475 |
-
$request = rest_ensure_request( $request );
|
476 |
-
return $wp_rest_server->dispatch( $request );
|
477 |
-
}
|
478 |
-
|
479 |
-
/**
|
480 |
-
* Ensure request arguments are a request object.
|
481 |
-
*
|
482 |
-
* This ensures that the request is consistent.
|
483 |
-
*
|
484 |
-
* @param array|WP_REST_Request $request Request to check.
|
485 |
-
* @return WP_REST_Request
|
486 |
-
*/
|
487 |
-
function rest_ensure_request( $request ) {
|
488 |
-
if ( $request instanceof WP_REST_Request ) {
|
489 |
-
return $request;
|
490 |
-
}
|
491 |
-
|
492 |
-
return new WP_REST_Request( 'GET', '', $request );
|
493 |
-
}
|
494 |
-
|
495 |
-
/**
|
496 |
-
* Ensure a REST response is a response object.
|
497 |
-
*
|
498 |
-
* This ensures that the response is consistent, and implements
|
499 |
-
* {@see WP_HTTP_ResponseInterface}, allowing usage of
|
500 |
-
* `set_status`/`header`/etc without needing to double-check the object. Will
|
501 |
-
* also allow {@see WP_Error} to indicate error responses, so users should
|
502 |
-
* immediately check for this value.
|
503 |
-
*
|
504 |
-
* @param WP_Error|WP_HTTP_ResponseInterface|mixed $response Response to check.
|
505 |
-
* @return mixed WP_Error if present, WP_HTTP_ResponseInterface if instance,
|
506 |
-
* otherwise WP_REST_Response.
|
507 |
-
*/
|
508 |
-
function rest_ensure_response( $response ) {
|
509 |
-
if ( is_wp_error( $response ) ) {
|
510 |
-
return $response;
|
511 |
-
}
|
512 |
-
|
513 |
-
if ( $response instanceof WP_HTTP_ResponseInterface ) {
|
514 |
-
return $response;
|
515 |
-
}
|
516 |
-
|
517 |
-
return new WP_REST_Response( $response );
|
518 |
-
}
|
519 |
-
|
520 |
-
/**
|
521 |
-
* Handle {@see _deprecated_function()} errors.
|
522 |
-
*
|
523 |
-
* @param string $function Function name.
|
524 |
-
* @param string $replacement Replacement function name.
|
525 |
-
* @param string $version Version.
|
526 |
-
*/
|
527 |
-
function rest_handle_deprecated_function( $function, $replacement, $version ) {
|
528 |
-
if ( ! empty( $replacement ) ) {
|
529 |
-
$string = sprintf( __( '%1$s (since %2$s; use %3$s instead)' ), $function, $version, $replacement );
|
530 |
-
} else {
|
531 |
-
$string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
|
532 |
-
}
|
533 |
|
534 |
-
|
535 |
-
}
|
536 |
-
|
537 |
-
/**
|
538 |
-
* Handle {@see _deprecated_function} errors.
|
539 |
-
*
|
540 |
-
* @param string $function Function name.
|
541 |
-
* @param string $replacement Replacement function name.
|
542 |
-
* @param string $version Version.
|
543 |
-
*/
|
544 |
-
function rest_handle_deprecated_argument( $function, $replacement, $version ) {
|
545 |
-
if ( ! empty( $replacement ) ) {
|
546 |
-
$string = sprintf( __( '%1$s (since %2$s; %3$s)' ), $function, $version, $replacement );
|
547 |
-
} else {
|
548 |
-
$string = sprintf( __( '%1$s (since %2$s; no alternative available)' ), $function, $version );
|
549 |
-
}
|
550 |
-
|
551 |
-
header( sprintf( 'X-WP-DeprecatedParam: %s', $string ) );
|
552 |
-
}
|
553 |
-
|
554 |
-
/**
|
555 |
-
* Send Cross-Origin Resource Sharing headers with API requests
|
556 |
-
*
|
557 |
-
* @param mixed $value Response data
|
558 |
-
* @return mixed Response data
|
559 |
-
*/
|
560 |
-
function rest_send_cors_headers( $value ) {
|
561 |
-
$origin = get_http_origin();
|
562 |
-
|
563 |
-
if ( $origin ) {
|
564 |
-
header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
|
565 |
-
header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' );
|
566 |
-
header( 'Access-Control-Allow-Credentials: true' );
|
567 |
-
}
|
568 |
-
|
569 |
-
return $value;
|
570 |
-
}
|
571 |
-
|
572 |
-
/**
|
573 |
-
* Handle OPTIONS requests for the server
|
574 |
-
*
|
575 |
-
* This is handled outside of the server code, as it doesn't obey normal route
|
576 |
-
* mapping.
|
577 |
-
*
|
578 |
-
* @param mixed $response Current response, either response or `null` to indicate pass-through.
|
579 |
-
* @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server).
|
580 |
-
* @param WP_REST_Request $request The request that was used to make current response.
|
581 |
-
* @return WP_REST_Response $response Modified response, either response or `null` to indicate pass-through.
|
582 |
-
*/
|
583 |
-
function rest_handle_options_request( $response, $handler, $request ) {
|
584 |
-
if ( ! empty( $response ) || $request->get_method() !== 'OPTIONS' ) {
|
585 |
-
return $response;
|
586 |
-
}
|
587 |
-
|
588 |
-
$response = new WP_REST_Response();
|
589 |
-
|
590 |
-
$accept = array();
|
591 |
-
|
592 |
-
foreach ( $handler->get_routes() as $route => $endpoints ) {
|
593 |
-
$match = preg_match( '@^' . $route . '$@i', $request->get_route(), $args );
|
594 |
-
|
595 |
-
if ( ! $match ) {
|
596 |
-
continue;
|
597 |
-
}
|
598 |
-
|
599 |
-
foreach ( $endpoints as $endpoint ) {
|
600 |
-
$accept = array_merge( $accept, $endpoint['methods'] );
|
601 |
-
}
|
602 |
-
break;
|
603 |
-
}
|
604 |
-
$accept = array_keys( $accept );
|
605 |
-
|
606 |
-
$response->header( 'Accept', implode( ', ', $accept ) );
|
607 |
-
|
608 |
-
return $response;
|
609 |
-
}
|
610 |
-
|
611 |
-
/**
|
612 |
-
* Send the "Allow" header to state all methods that can be sen
|
613 |
-
* to the current route
|
614 |
-
*
|
615 |
-
* @param WP_REST_Response $response Current response being served.
|
616 |
-
* @param WP_REST_Server $server ResponseHandler instance (usually WP_REST_Server)
|
617 |
-
* @param WP_REST_Request $request The request that was used to make current response.
|
618 |
-
*/
|
619 |
-
function rest_send_allow_header( $response, $server, $request ) {
|
620 |
-
|
621 |
-
$matched_route = $response->get_matched_route();
|
622 |
-
|
623 |
-
if ( ! $matched_route ) {
|
624 |
-
return $response;
|
625 |
-
}
|
626 |
-
|
627 |
-
$routes = $server->get_routes();
|
628 |
|
629 |
-
|
630 |
|
631 |
-
|
632 |
-
foreach ( $routes[ $matched_route ] as $_handler ) {
|
633 |
-
foreach ( $_handler['methods'] as $handler_method => $value ) {
|
634 |
|
635 |
-
|
636 |
-
|
637 |
-
$permission = call_user_func( $_handler['permission_callback'], $request );
|
638 |
-
|
639 |
-
$allowed_methods[ $handler_method ] = true === $permission;
|
640 |
-
} else {
|
641 |
-
$allowed_methods[ $handler_method ] = true;
|
642 |
-
}
|
643 |
}
|
644 |
}
|
645 |
-
|
646 |
-
// strip out all the methods that are not allowed (false values)
|
647 |
-
$allowed_methods = array_filter( $allowed_methods );
|
648 |
-
|
649 |
-
if ( $allowed_methods ) {
|
650 |
-
$response->header( 'Allow', implode( ', ', array_map( 'strtoupper', array_keys( $allowed_methods ) ) ) );
|
651 |
-
}
|
652 |
-
|
653 |
-
return $response;
|
654 |
}
|
655 |
|
656 |
-
if ( ! function_exists( '
|
657 |
/**
|
658 |
-
*
|
659 |
-
*
|
660 |
-
* @internal This is a compatibility function for PHP <5.5
|
661 |
-
*
|
662 |
-
* @return boolean|string Returns the error message on success, "No Error" if no error has occurred, or FALSE on failure.
|
663 |
*/
|
664 |
-
function
|
665 |
-
|
666 |
-
|
667 |
-
return false;
|
668 |
-
}
|
669 |
-
|
670 |
-
$last_error_code = json_last_error();
|
671 |
-
|
672 |
-
// just in case JSON_ERROR_NONE is not defined
|
673 |
-
$error_code_none = defined( 'JSON_ERROR_NONE' ) ? JSON_ERROR_NONE : 0;
|
674 |
-
|
675 |
-
switch ( true ) {
|
676 |
-
case $last_error_code === $error_code_none:
|
677 |
-
return 'No error';
|
678 |
-
|
679 |
-
case defined( 'JSON_ERROR_DEPTH' ) && JSON_ERROR_DEPTH === $last_error_code:
|
680 |
-
return 'Maximum stack depth exceeded';
|
681 |
-
|
682 |
-
case defined( 'JSON_ERROR_STATE_MISMATCH' ) && JSON_ERROR_STATE_MISMATCH === $last_error_code:
|
683 |
-
return 'State mismatch (invalid or malformed JSON)';
|
684 |
-
|
685 |
-
case defined( 'JSON_ERROR_CTRL_CHAR' ) && JSON_ERROR_CTRL_CHAR === $last_error_code:
|
686 |
-
return 'Control character error, possibly incorrectly encoded';
|
687 |
-
|
688 |
-
case defined( 'JSON_ERROR_SYNTAX' ) && JSON_ERROR_SYNTAX === $last_error_code:
|
689 |
-
return 'Syntax error';
|
690 |
-
|
691 |
-
case defined( 'JSON_ERROR_UTF8' ) && JSON_ERROR_UTF8 === $last_error_code:
|
692 |
-
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
693 |
-
|
694 |
-
case defined( 'JSON_ERROR_RECURSION' ) && JSON_ERROR_RECURSION === $last_error_code:
|
695 |
-
return 'Recursion detected';
|
696 |
-
|
697 |
-
case defined( 'JSON_ERROR_INF_OR_NAN' ) && JSON_ERROR_INF_OR_NAN === $last_error_code:
|
698 |
-
return 'Inf and NaN cannot be JSON encoded';
|
699 |
-
|
700 |
-
case defined( 'JSON_ERROR_UNSUPPORTED_TYPE' ) && JSON_ERROR_UNSUPPORTED_TYPE === $last_error_code:
|
701 |
-
return 'Type is not supported';
|
702 |
-
|
703 |
-
default:
|
704 |
-
return 'An unknown error occurred';
|
705 |
-
}
|
706 |
}
|
707 |
-
|
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-beta10
|
8 |
* Plugin URI: https://github.com/WP-API/WP-API
|
9 |
* License: GPL2+
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
+
* WP_REST_Controller class.
|
|
|
|
|
14 |
*/
|
15 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-controller.php';
|
16 |
|
17 |
/**
|
18 |
+
* WP_REST_Posts_Controller class.
|
19 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-posts-controller.php';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
+
/**
|
23 |
+
* WP_REST_Attachments_Controller class.
|
24 |
+
*/
|
25 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-attachments-controller.php';
|
26 |
|
27 |
+
/**
|
28 |
+
* WP_REST_Post_Types_Controller class.
|
29 |
+
*/
|
30 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-post-types-controller.php';
|
31 |
|
32 |
/**
|
33 |
+
* WP_REST_Post_Statuses_Controller class.
|
|
|
|
|
|
|
|
|
|
|
34 |
*/
|
35 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-post-statuses-controller.php';
|
36 |
|
37 |
+
/**
|
38 |
+
* WP_REST_Revisions_Controller class.
|
39 |
+
*/
|
40 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-revisions-controller.php';
|
41 |
|
42 |
+
/**
|
43 |
+
* WP_REST_Taxonomies_Controller class.
|
44 |
+
*/
|
45 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-taxonomies-controller.php';
|
46 |
|
47 |
+
/**
|
48 |
+
* WP_REST_Terms_Controller class.
|
49 |
+
*/
|
50 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-terms-controller.php';
|
|
|
|
|
|
|
|
|
51 |
|
52 |
+
/**
|
53 |
+
* WP_REST_Users_Controller class.
|
54 |
+
*/
|
55 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-users-controller.php';
|
56 |
|
57 |
/**
|
58 |
+
* WP_REST_Comments_Controller class.
|
|
|
|
|
|
|
|
|
|
|
59 |
*/
|
60 |
+
require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-comments-controller.php';
|
61 |
|
62 |
+
/**
|
63 |
+
* WP_REST_Meta_Controller class.
|
64 |
+
*/
|
65 |
+
include_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-meta-controller.php';
|
|
|
66 |
|
67 |
+
/**
|
68 |
+
* WP_REST_Meta_Posts_Controller class.
|
69 |
+
*/
|
70 |
+
include_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-meta-posts-controller.php';
|
71 |
|
72 |
+
/**
|
73 |
+
* WP_REST_Posts_Terms_Controller class.
|
74 |
+
*/
|
75 |
+
include_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-posts-terms-controller.php';
|
76 |
|
77 |
+
/**
|
78 |
+
* REST extras.
|
79 |
+
*/
|
80 |
+
include_once( dirname( __FILE__ ) . '/extras.php' );
|
81 |
|
82 |
+
add_filter( 'init', '_add_extra_api_post_type_arguments', 11 );
|
83 |
+
add_action( 'init', '_add_extra_api_taxonomy_arguments', 11 );
|
84 |
+
add_action( 'rest_api_init', 'create_initial_rest_routes', 0 );
|
|
|
85 |
|
86 |
/**
|
87 |
+
* Adds extra post type registration arguments.
|
88 |
+
*
|
89 |
* These attributes will eventually be committed to core.
|
90 |
+
*
|
91 |
+
* @since 4.4.0
|
92 |
+
*
|
93 |
+
* @global array $wp_taxonomies Registered taxonomies.
|
94 |
*/
|
95 |
function _add_extra_api_post_type_arguments() {
|
96 |
global $wp_post_types;
|
97 |
|
98 |
+
if ( isset( $wp_post_types['post'] ) ) {
|
99 |
+
$wp_post_types['post']->show_in_rest = true;
|
100 |
+
$wp_post_types['post']->rest_base = 'posts';
|
101 |
+
$wp_post_types['post']->rest_controller_class = 'WP_REST_Posts_Controller';
|
102 |
+
}
|
|
|
|
|
103 |
|
104 |
+
if ( isset( $wp_post_types['page'] ) ) {
|
105 |
+
$wp_post_types['page']->show_in_rest = true;
|
106 |
+
$wp_post_types['page']->rest_base = 'pages';
|
107 |
+
$wp_post_types['page']->rest_controller_class = 'WP_REST_Posts_Controller';
|
108 |
+
}
|
109 |
|
110 |
+
if ( isset( $wp_post_types['attachment'] ) ) {
|
111 |
+
$wp_post_types['attachment']->show_in_rest = true;
|
112 |
+
$wp_post_types['attachment']->rest_base = 'media';
|
113 |
+
$wp_post_types['attachment']->rest_controller_class = 'WP_REST_Attachments_Controller';
|
114 |
+
}
|
115 |
}
|
|
|
116 |
|
117 |
/**
|
118 |
+
* Adds extra taxonomy registration arguments.
|
119 |
+
*
|
120 |
* These attributes will eventually be committed to core.
|
121 |
+
*
|
122 |
+
* @since 4.4.0
|
123 |
+
*
|
124 |
+
* @global array $wp_taxonomies Registered taxonomies.
|
125 |
*/
|
126 |
function _add_extra_api_taxonomy_arguments() {
|
127 |
global $wp_taxonomies;
|
128 |
|
129 |
+
if ( isset( $wp_taxonomies['category'] ) ) {
|
130 |
+
$wp_taxonomies['category']->show_in_rest = true;
|
131 |
+
$wp_taxonomies['category']->rest_base = 'categories';
|
132 |
+
$wp_taxonomies['category']->rest_controller_class = 'WP_REST_Terms_Controller';
|
133 |
+
}
|
134 |
|
135 |
+
if ( isset( $wp_taxonomies['post_tag'] ) ) {
|
136 |
+
$wp_taxonomies['post_tag']->show_in_rest = true;
|
137 |
+
$wp_taxonomies['post_tag']->rest_base = 'tags';
|
138 |
+
$wp_taxonomies['post_tag']->rest_controller_class = 'WP_REST_Terms_Controller';
|
139 |
+
}
|
140 |
}
|
|
|
141 |
|
142 |
/**
|
143 |
+
* Registers default REST API routes.
|
144 |
+
*
|
145 |
+
* @since 4.4.0
|
146 |
*/
|
147 |
function create_initial_rest_routes() {
|
148 |
|
179 |
}
|
180 |
}
|
181 |
|
182 |
+
// Post types.
|
|
|
|
|
183 |
$controller = new WP_REST_Post_Types_Controller;
|
184 |
$controller->register_routes();
|
185 |
|
186 |
+
// Post statuses.
|
|
|
|
|
187 |
$controller = new WP_REST_Post_Statuses_Controller;
|
188 |
$controller->register_routes();
|
189 |
|
190 |
+
// Taxonomies.
|
|
|
|
|
191 |
$controller = new WP_REST_Taxonomies_Controller;
|
192 |
$controller->register_routes();
|
193 |
|
194 |
+
// Terms.
|
|
|
|
|
195 |
foreach ( get_taxonomies( array( 'show_in_rest' => true ), 'object' ) as $taxonomy ) {
|
196 |
$class = ! empty( $taxonomy->rest_controller_class ) ? $taxonomy->rest_controller_class : 'WP_REST_Terms_Controller';
|
197 |
|
206 |
$controller->register_routes();
|
207 |
}
|
208 |
|
209 |
+
// Users.
|
|
|
|
|
210 |
$controller = new WP_REST_Users_Controller;
|
211 |
$controller->register_routes();
|
212 |
|
213 |
+
// Comments.
|
|
|
|
|
214 |
$controller = new WP_REST_Comments_Controller;
|
215 |
$controller->register_routes();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
216 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
|
218 |
+
if ( ! function_exists( 'rest_authorization_required_code' ) ) {
|
219 |
/**
|
220 |
+
* Returns a contextual HTTP error code for authorization failure.
|
221 |
*
|
222 |
+
* @return integer
|
|
|
223 |
*/
|
224 |
+
function rest_authorization_required_code() {
|
225 |
+
return is_user_logged_in() ? 403 : 401;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
226 |
}
|
227 |
}
|
|
|
228 |
|
229 |
+
if ( ! function_exists( 'register_rest_field' ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
/**
|
231 |
+
* Registers a new field on an existing WordPress object type.
|
232 |
*
|
233 |
+
* @global array $wp_rest_additional_fields Holds registered fields, organized
|
234 |
+
* by object type.
|
235 |
*
|
236 |
+
* @param string|array $object_type Object(s) the field is being registered
|
237 |
+
* to, "post"|"term"|"comment" etc.
|
238 |
+
* @param string $attribute The attribute name.
|
239 |
+
* @param array $args {
|
240 |
+
* Optional. An array of arguments used to handle the registered field.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
*
|
242 |
+
* @type string|array|null $get_callback Optional. The callback function used to retrieve the field
|
243 |
+
* value. Default is 'null', the field will not be returned in
|
244 |
+
* the response.
|
245 |
+
* @type string|array|null $update_callback Optional. The callback function used to set and update the
|
246 |
+
* field value. Default is 'null', the value cannot be set or
|
247 |
+
* updated.
|
248 |
+
* @type string|array|null $schema Optional. The callback function used to create the schema for
|
249 |
+
* this field. Default is 'null', no schema entry will be returned.
|
250 |
+
* }
|
251 |
*/
|
252 |
+
function register_rest_field( $object_type, $attribute, $args = array() ) {
|
253 |
+
$defaults = array(
|
254 |
+
'get_callback' => null,
|
255 |
+
'update_callback' => null,
|
256 |
+
'schema' => null,
|
257 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
|
259 |
+
$args = wp_parse_args( $args, $defaults );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
260 |
|
261 |
+
global $wp_rest_additional_fields;
|
262 |
|
263 |
+
$object_types = (array) $object_type;
|
|
|
|
|
264 |
|
265 |
+
foreach ( $object_types as $object_type ) {
|
266 |
+
$wp_rest_additional_fields[ $object_type ][ $attribute ] = $args;
|
|
|
|
|
|
|
|
|
|
|
|
|
267 |
}
|
268 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
269 |
}
|
270 |
|
271 |
+
if ( ! function_exists( 'register_api_field' ) ) {
|
272 |
/**
|
273 |
+
* Backwards compat shim
|
|
|
|
|
|
|
|
|
274 |
*/
|
275 |
+
function register_api_field( $object_type, $attributes, $args = array() ) {
|
276 |
+
_deprecated_function( 'register_api_field', 'WPAPI-2.0', 'register_rest_field' );
|
277 |
+
register_rest_field( $object_type, $attributes, $args );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
}
|
279 |
+
}
|
readme.txt
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
=== WordPress REST API (Version 2) ===
|
2 |
-
Contributors: rmccue, rachelbaker
|
3 |
Tags: json, rest, api, rest-api
|
4 |
-
Requires at least: 4.
|
5 |
-
Tested up to: 4.
|
6 |
-
Stable tag: 2.0-
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
@@ -36,13 +36,968 @@ For full-flavoured API support, you'll need to be using pretty permalinks to use
|
|
36 |
|
37 |
== Changelog ==
|
38 |
|
39 |
-
= 2.0 Beta
|
40 |
|
41 |
-
* Ensure media of private posts are private too.
|
42 |
|
43 |
Reported by @danielbachhuber on 2016-01-08.
|
44 |
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
Partial rewrite and evolution of the REST API to prepare for core integration.
|
48 |
|
1 |
=== WordPress REST API (Version 2) ===
|
2 |
+
Contributors: rmccue, rachelbaker, danielbachhuber, joehoyle
|
3 |
Tags: json, rest, api, rest-api
|
4 |
+
Requires at least: 4.4
|
5 |
+
Tested up to: 4.5-alpha
|
6 |
+
Stable tag: 2.0-beta10
|
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 10.0 =
|
40 |
|
41 |
+
* SECURITY: Ensure media of private posts are private too.
|
42 |
|
43 |
Reported by @danielbachhuber on 2016-01-08.
|
44 |
|
45 |
+
* BREAKING CHANGE: Removes compatibility repo for WordPress 4.3.
|
46 |
+
|
47 |
+
WordPress 4.4 is now the minimum supported WordPress version.
|
48 |
+
|
49 |
+
(props @danielbachhuber, [#1848](https://github.com/WP-API/WP-API/pull/1848))
|
50 |
+
|
51 |
+
* BREAKING CHANGE: Changes link relation for types and taxonomies.
|
52 |
+
|
53 |
+
In Beta 9, this link relation was introduced as `item`, which isn't correct. The relation has been changed to `https://api.w.org/items`.
|
54 |
+
|
55 |
+
(props @danielbachhuber, [#1853](https://github.com/WP-API/WP-API/pull/1853))
|
56 |
+
|
57 |
+
* BREAKING CHANGE: Introduces `edit` context for `wp/v2/types` and `wp/v2/taxonomies`.
|
58 |
+
|
59 |
+
Some fields have moved into this context, which require `edit_posts` and `manage_terms`, respectively.
|
60 |
+
|
61 |
+
(props @danielbachhuber, [#1894](https://github.com/WP-API/WP-API/pull/1894), [#1864](https://github.com/WP-API/WP-API/pull/1864))
|
62 |
+
|
63 |
+
* BREAKING CHANGE: Removes `post_format` as a term `_link` for Posts.
|
64 |
+
|
65 |
+
Post formats aren't a custom taxonomy in the eyes of the REST API.
|
66 |
+
|
67 |
+
(props @danielbachhuber, [#1854](https://github.com/WP-API/WP-API/pull/1854))
|
68 |
+
|
69 |
+
* Declares `parent` query param for Pages.
|
70 |
+
|
71 |
+
(props @danielbachhuber, [#1975](https://github.com/WP-API/WP-API/pull/1975))
|
72 |
+
|
73 |
+
* Permits logged-in users to query for media.
|
74 |
+
|
75 |
+
(props @danielbachhuber, [#1973](https://github.com/WP-API/WP-API/pull/1973))
|
76 |
+
|
77 |
+
* Removes duplicated query params from Terms controller.
|
78 |
+
|
79 |
+
(props @danielbachhuber, [#1963](https://github.com/WP-API/WP-API/pull/1963))
|
80 |
+
|
81 |
+
* Adds `include` param to `/wp/v2/posts`, `/wp/v2/users`, `/wp/v2/<taxonomy>` and `/wp/v2/comments`.
|
82 |
+
|
83 |
+
(props @danielbachhuber, [#1961](https://github.com/WP-API/WP-API/pull/1961), [#1964](https://github.com/WP-API/WP-API/pull/1964), [#1968](https://github.com/WP-API/WP-API/pull/1968), [#1971](https://github.com/WP-API/WP-API/pull/1971))
|
84 |
+
|
85 |
+
* Ensures `GET /wp/v2/posts` respects `order` and `orderby` params.
|
86 |
+
|
87 |
+
(props @danielbachhuber, [#1962](https://github.com/WP-API/WP-API/pull/1962))
|
88 |
+
|
89 |
+
* Fixes fatal by loading `wp-admin/includes/user.php` to expose `wp_delete_user()`.
|
90 |
+
|
91 |
+
(props @danielbachhuber, [#1958](https://github.com/WP-API/WP-API/pull/1958))
|
92 |
+
|
93 |
+
* Permits making a post sticky when also supplying an empty password.
|
94 |
+
|
95 |
+
(props @westonruter, [#1949](https://github.com/WP-API/WP-API/pull/1949))
|
96 |
+
|
97 |
+
* Uses `WP_REST_Request` internally across controllers.
|
98 |
+
|
99 |
+
(props @danielbachhuber, [#1933](https://github.com/WP-API/WP-API/pull/1933), [#1939](https://github.com/WP-API/WP-API/pull/1939), [#1934](https://github.com/WP-API/WP-API/pull/1934), [#1938](https://github.com/WP-API/WP-API/pull/1938))
|
100 |
+
|
101 |
+
* Cleans up permissions checks in `WP_REST_Terms_Controller`.
|
102 |
+
|
103 |
+
(props @danielbachhuber, [#1941](https://github.com/WP-API/WP-API/pull/1941))
|
104 |
+
|
105 |
+
* Uses `show_in_rest` to determine publicness for post types.
|
106 |
+
|
107 |
+
(props @danielbachhuber, [#1942](https://github.com/WP-API/WP-API/pull/1942))
|
108 |
+
|
109 |
+
* Makes `description` strings available for translation.
|
110 |
+
|
111 |
+
(props @danielbachhuber, [#1944](https://github.com/WP-API/WP-API/pull/1944))
|
112 |
+
|
113 |
+
* Checks `assign_terms` cap for taxonomy when managing post terms.
|
114 |
+
|
115 |
+
(props @danielbachhuber, [#1940](https://github.com/WP-API/WP-API/pull/1940))
|
116 |
+
|
117 |
+
* Defer to `edit_posts` of the custom post type when accessing private query vars.
|
118 |
+
|
119 |
+
(props @danielbachhuber, [#1886](https://github.com/WP-API/WP-API/pull/1886))
|
120 |
+
|
121 |
+
* Allows Terms collection params to be filtered.
|
122 |
+
|
123 |
+
(props @rachelbaker, [#1882](https://github.com/WP-API/WP-API/pull/1882))
|
124 |
+
|
125 |
+
* Renames post terms create/delete permissions callback.
|
126 |
+
|
127 |
+
(props @wpsmith, [#1923](https://github.com/WP-API/WP-API/pull/1923))
|
128 |
+
|
129 |
+
* Fixes invalid use of 'uri' as schema `type`.
|
130 |
+
|
131 |
+
(props @wpsmith, [#1913](https://github.com/WP-API/WP-API/pull/1913))
|
132 |
+
|
133 |
+
* Casts integer with (int) over intval for speed.
|
134 |
+
|
135 |
+
(props @wpsmith, [#1907](https://github.com/WP-API/WP-API/pull/1907))
|
136 |
+
|
137 |
+
* Fixes PHP Doc typo for `validate_schema_property` and `sanitize_schema_property`.
|
138 |
+
|
139 |
+
(props @wpsmith, @danielbachhuber, [#1909](https://github.com/WP-API/WP-API/pull/1909), [#1910](https://github.com/WP-API/WP-API/pull/1910))
|
140 |
+
|
141 |
+
* Adds a helpful description to the `filter` argument.
|
142 |
+
|
143 |
+
(props @danielbachhuber, [#1885](https://github.com/WP-API/WP-API/pull/1885))
|
144 |
+
|
145 |
+
* Changes order of Users response to match schema order.
|
146 |
+
|
147 |
+
(props @rachelbaker, [#1879](https://github.com/WP-API/WP-API/pull/1879))
|
148 |
+
|
149 |
+
* Adjusts Posts pagination headers for `filter` params.
|
150 |
+
|
151 |
+
(props @rachelbaker, [#1878](https://github.com/WP-API/WP-API/pull/1878))
|
152 |
+
|
153 |
+
* Uses proper status code when failing to get comments of private post.
|
154 |
+
|
155 |
+
(props @danielbachhuber, [#1866](https://github.com/WP-API/WP-API/pull/1867))
|
156 |
+
|
157 |
+
* Fixes invalid capability for comments get items permissions callback.
|
158 |
+
|
159 |
+
`manage_comments` doesn't exist; `moderate_comments` does.
|
160 |
+
|
161 |
+
(props @danielbachhuber, [#1866](https://github.com/WP-API/WP-API/pull/1866))
|
162 |
+
|
163 |
+
* Permits creating comments without an assigned post.
|
164 |
+
|
165 |
+
(props @danielbachhuber, [#1857](https://github.com/WP-API/WP-API/pull/1857))
|
166 |
+
|
167 |
+
* Prevents error notice when `show_in_rest` isn't set for a post type.
|
168 |
+
|
169 |
+
(props @danielbachhuber, [#1852](https://github.com/WP-API/WP-API/pull/1852))
|
170 |
+
|
171 |
+
= 2.0 Beta 9.0 =
|
172 |
+
|
173 |
+
* BREAKING CHANGE: Move tags and categories to top-level endpoints.
|
174 |
+
|
175 |
+
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`.
|
176 |
+
|
177 |
+
(props @danielbachhuber, [#1802](https://github.com/WP-API/WP-API/pull/1802))
|
178 |
+
|
179 |
+
* BREAKING CHANGE: Return object for requests to `/wp/v2/taxonomies`.
|
180 |
+
|
181 |
+
This is consistent with `/wp/v2/types` and `/wp/v2/statuses`.
|
182 |
+
|
183 |
+
(props @danielbachhuber, [#1825](https://github.com/WP-API/WP-API/pull/1825))
|
184 |
+
|
185 |
+
* BREAKING CHANGE: Remove `rest_get_timezone()`.
|
186 |
+
|
187 |
+
`json_get_timezone()` was only ever used in v1. This function causes fatals, and shouldn't be used.
|
188 |
+
|
189 |
+
(props @danielbachhuber, [#1823](https://github.com/WP-API/WP-API/pull/1823))
|
190 |
+
|
191 |
+
* BREAKING CHANGE: Rename `register_api_field()` to `register_rest_field()`.
|
192 |
+
|
193 |
+
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.
|
194 |
+
|
195 |
+
(props @danielbachhuber, [#1824](https://github.com/WP-API/WP-API/pull/1824))
|
196 |
+
|
197 |
+
* BREAKING CHANGE: Change taxonomies' `post_type` argument to `type`.
|
198 |
+
|
199 |
+
It's consistent with how we're exposing post types in the API.
|
200 |
+
|
201 |
+
(props @danielbachhuber, [#1824](https://github.com/WP-API/WP-API/pull/1824))
|
202 |
+
|
203 |
+
* Sync infrastructure with shipped in WordPress 4.4.
|
204 |
+
|
205 |
+
* `wp-includes/rest-api/rest-functions.php` is removed, and its functions moved into `wp-includes/rest-api.php`.
|
206 |
+
* Send nocache headers for REST requests. [#34832](https://core.trac.wordpress.org/ticket/34832)
|
207 |
+
* Fix handling of HEAD requests. [#34837](https://core.trac.wordpress.org/ticket/34837)
|
208 |
+
* Mark `WP_REST_Server::get_raw_data()` as static. [#34768](https://core.trac.wordpress.org/ticket/34768)
|
209 |
+
* Unabbreviate error string. [#34818](https://core.trac.wordpress.org/ticket/34818)
|
210 |
+
|
211 |
+
* Change terms endpoints to use `term_id` not `tt_id`.
|
212 |
+
|
213 |
+
(props @joehoyle, [#1837](https://github.com/WP-API/WP-API/pull/1837))
|
214 |
+
|
215 |
+
* Standardize declaration of `context` param for `GET` requests across controllers.
|
216 |
+
|
217 |
+
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.
|
218 |
+
|
219 |
+
(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))
|
220 |
+
|
221 |
+
* Link types / taxonomies to their collections, and vice versa.
|
222 |
+
|
223 |
+
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.
|
224 |
+
|
225 |
+
(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))
|
226 |
+
|
227 |
+
* Add missing 'wp/v2' in Location Response header when creating new Post Meta.
|
228 |
+
|
229 |
+
(props @johanmynhardt, [#1790](https://github.com/WP-API/WP-API/pull/1790))
|
230 |
+
|
231 |
+
* Expose Post collection query params, including `author`, `order`, `orderby` and `status`.
|
232 |
+
|
233 |
+
(props @danielbachhuber, [#1793](https://github.com/WP-API/WP-API/pull/1793))
|
234 |
+
|
235 |
+
* Ignore sticky posts by default.
|
236 |
+
|
237 |
+
(props @danielbachhuber, [#1801](https://github.com/WP-API/WP-API/pull/1801))
|
238 |
+
|
239 |
+
* Include `full` image size in attachment `sizes` attribute.
|
240 |
+
|
241 |
+
(props @danielbachhuber, [#1806](https://github.com/WP-API/WP-API/pull/1806))
|
242 |
+
|
243 |
+
* In text strings, use `id` instead of `ID`.
|
244 |
+
|
245 |
+
`ID` is an implementation artifact. Our Resources use `id`.
|
246 |
+
|
247 |
+
(props @danielbachhuber, [#1803](https://github.com/WP-API/WP-API/pull/1803))
|
248 |
+
|
249 |
+
* Ensure `attachment.sizes[]` use `mime_type` instead of `mime-type`.
|
250 |
+
|
251 |
+
(props @danielbachhuber, [#1809](https://github.com/WP-API/WP-API/pull/1809))
|
252 |
+
|
253 |
+
* Introduce `rest_authorization_required_code()`.
|
254 |
+
|
255 |
+
Many controllers returned incorrect HTTP codes, which this also fixes.
|
256 |
+
|
257 |
+
(props @danielbachhuber, [#1808](https://github.com/WP-API/WP-API/pull/1808))
|
258 |
+
|
259 |
+
* Respect core's `comment_registration` setting.
|
260 |
+
|
261 |
+
If it's enabled, require users to be logged in to comment.
|
262 |
+
|
263 |
+
(props @danielbachhuber, [#1826](https://github.com/WP-API/WP-API/pull/1826))
|
264 |
+
|
265 |
+
* Default to wildcard when searching users.
|
266 |
+
|
267 |
+
(props @danielbachhuber, [#1827](https://github.com/WP-API/WP-API/pull/1827))
|
268 |
+
|
269 |
+
* Bring the wp-api.js library up to date for v2 of the REST API.
|
270 |
+
|
271 |
+
(props @adamsilverstein, [#1828](https://github.com/WP-API/WP-API/pull/1828))
|
272 |
+
|
273 |
+
* Add `rest_prepare_status` filter.
|
274 |
+
|
275 |
+
(props @danielbachhuber, [#1830](https://github.com/WP-API/WP-API/pull/1830))
|
276 |
+
|
277 |
+
* Make `prepare_*` filters more consistent.
|
278 |
+
|
279 |
+
(props @danielbachhuber, [#1831](https://github.com/WP-API/WP-API/pull/1831))
|
280 |
+
|
281 |
+
* Add `rest_prepare_post_type` filter for post types.
|
282 |
+
|
283 |
+
(props @danielbachhuber, [#1833](https://github.com/WP-API/WP-API/pull/1833))
|
284 |
+
|
285 |
+
= 2.0 Beta 8.0 =
|
286 |
+
|
287 |
+
* Prevent fatals when uploading attachment by including admin utilities.
|
288 |
+
|
289 |
+
(props @danielbachhuber, [#1756](https://github.com/WP-API/WP-API/pull/1756))
|
290 |
+
|
291 |
+
* Return 201 status code when creating a term.
|
292 |
+
|
293 |
+
(props @danielbachhuber, [#1753](https://github.com/WP-API/WP-API/pull/1753))
|
294 |
+
|
295 |
+
* Don't permit requesting terms cross routes.
|
296 |
+
|
297 |
+
Clients should only be able to request categories from the category route, and tags from the tag route.
|
298 |
+
|
299 |
+
(props @danielbachhuber, [#1764](https://github.com/WP-API/WP-API/pull/1764))
|
300 |
+
|
301 |
+
* Set `fields=>id` when using `WP_User_Query` to fix large memory usage
|
302 |
+
|
303 |
+
(props @joehoyle, [#1770](https://github.com/WP-API/WP-API/pull/1770))
|
304 |
+
|
305 |
+
* Fix Post `_link` to attached attachments.
|
306 |
+
|
307 |
+
(props @danielbachhuber, [#1777](https://github.com/WP-API/WP-API/pull/1777))
|
308 |
+
|
309 |
+
* Add support for getting a post with a custom public status.
|
310 |
+
|
311 |
+
(props @danielbachhuber, [#1765](https://github.com/WP-API/WP-API/pull/1765))
|
312 |
+
|
313 |
+
* Ensure post content doesn't get double-slashed on update.
|
314 |
+
|
315 |
+
(props @joehoyle, [#1772](https://github.com/WP-API/WP-API/pull/1772))
|
316 |
+
|
317 |
+
* Change 'int' to 'integer' for `WP_REST_Controller::validate_schema_property()`
|
318 |
+
|
319 |
+
(props @wpsmith, [#1759](https://github.com/WP-API/WP-API/pull/1759))
|
320 |
+
|
321 |
+
= 2.0 Beta 7.0 =
|
322 |
+
|
323 |
+
* Sync infrastructure from WordPress core as of r35691.
|
324 |
+
|
325 |
+
* Remove `register_api_field()` because it's conceptually tied to `WP_REST_Controller` [#34730](https://core.trac.wordpress.org/ticket/34730)
|
326 |
+
* Update the REST API header links to use api.w.org [#34303](https://core.trac.wordpress.org/ticket/34303)
|
327 |
+
* Require the `$namespace` argument in `register_rest_route()` [#34416](https://core.trac.wordpress.org/ticket/34416)
|
328 |
+
* Include `enum` and `description` in help data [#34543](https://core.trac.wordpress.org/ticket/34543)
|
329 |
+
* Save `preg_match` iterations in `WP_REST_Server` [#34488](https://core.trac.wordpress.org/ticket/34488)
|
330 |
+
* Don't return route URL in `WP_REST_Request:get_params()` [#34647](https://core.trac.wordpress.org/ticket/34647)
|
331 |
+
|
332 |
+
* Restore `register_api_field()` within the plugin.
|
333 |
+
|
334 |
+
(props @danielbachhuber, [#1748](https://github.com/WP-API/WP-API/pull/1748))
|
335 |
+
|
336 |
+
* Require admin functions for use of `wp_handle_upload()`, fixing fatal.
|
337 |
+
|
338 |
+
(props @joehoyle, [#1746](https://github.com/WP-API/WP-API/pull/1746))
|
339 |
+
|
340 |
+
* Properly handle requesting terms where `parent=0` and `0` is a string.
|
341 |
+
|
342 |
+
(props @danielbachhuber, [#1739](https://github.com/WP-API/WP-API/pull/1739))
|
343 |
+
|
344 |
+
* Prevent PHP error notice when `&filter` isn't an array.
|
345 |
+
|
346 |
+
(props @danielbachhuber, [#1734](https://github.com/WP-API/WP-API/pull/1734))
|
347 |
+
|
348 |
+
* Change link relations to use api.w.org.
|
349 |
+
|
350 |
+
(props @danielbachhuber, [#1726](https://github.com/WP-API/WP-API/pull/1726))
|
351 |
+
|
352 |
+
= 2.0 Beta 6.0 =
|
353 |
+
|
354 |
+
* Remove global inclusion of wp-admin/includes/admin.php
|
355 |
+
|
356 |
+
For a long time, the REST API loaded wp-admin/includes/admin.php to make use of specific admin utilities. Now, it only loads those admin utilities when it needs them.
|
357 |
+
|
358 |
+
If your custom endpoints make use of admin utilities, you'll need to make sure to load wp-admin/includes/admin.php before you use them.
|
359 |
+
|
360 |
+
(props @joehoyle, [#1696](https://github.com/WP-API/WP-API/pull/1696))
|
361 |
+
|
362 |
+
* Link directly to the featured image in a Post's links.
|
363 |
+
|
364 |
+
(props @rmccue, [#1563](https://github.com/WP-API/WP-API/pull/1563), [#1711](https://github.com/WP-API/WP-API/pull/1711))
|
365 |
+
|
366 |
+
* Provide object type as callback argument for custom API fields.
|
367 |
+
|
368 |
+
(props @jtsternberg, [#1714](https://github.com/WP-API/WP-API/pull/1714))
|
369 |
+
|
370 |
+
* Change users schema order to be order of importance instead of alpha.
|
371 |
+
|
372 |
+
(props @rachelbaker, [#1708](https://github.com/WP-API/WP-API/pull/1708))
|
373 |
+
|
374 |
+
* Clarify documentation for `date` and `modified` attributes.
|
375 |
+
|
376 |
+
(props @danielbachhuber, [#1715](https://github.com/WP-API/WP-API/pull/1715))
|
377 |
+
|
378 |
+
* Update the wp-api.js client from the client-js repo.
|
379 |
+
|
380 |
+
(props @rachelbaker, [#1709](https://github.com/WP-API/WP-API/pull/1709))
|
381 |
+
|
382 |
+
* Fix the `format` enum to be an array of strings.
|
383 |
+
|
384 |
+
(props @joehoyle, [#1707](https://github.com/WP-API/WP-API/pull/1707))
|
385 |
+
|
386 |
+
* Run revisions for collection through `prepare_response_for_collection()`.
|
387 |
+
|
388 |
+
(props @danielbachhuber, @rachelbaker, [#1671](https://github.com/WP-API/WP-API/pull/1671))
|
389 |
+
|
390 |
+
* Expose `date_gmt` for `view` context of Posts and Comments.
|
391 |
+
|
392 |
+
(props @danielbachhuber, [#1690](https://github.com/WP-API/WP-API/pull/1690))
|
393 |
+
|
394 |
+
* Fix PHP and JS docblock formatting.
|
395 |
+
|
396 |
+
(props @ahmadawais, [#1699](https://github.com/WP-API/WP-API/pull/1698), [#1699](https://github.com/WP-API/WP-API/pull/1699), [#1701](https://github.com/WP-API/WP-API/pull/1701), [#1700](https://github.com/WP-API/WP-API/pull/1700), [#1702](https://github.com/WP-API/WP-API/pull/1702), [#1703](https://github.com/WP-API/WP-API/pull/1703))
|
397 |
+
|
398 |
+
* Include `media_details` attribute for attachments in embed context.
|
399 |
+
|
400 |
+
For image attachments, media_details includes a sizes array of image sizes, which is useful for templating.
|
401 |
+
|
402 |
+
(props @danielbachhuber, [#1667](https://github.com/WP-API/WP-API/pull/1667))
|
403 |
+
|
404 |
+
* Make `WP_REST_Controller` error messages more helpful by specifying method to subclass.
|
405 |
+
|
406 |
+
(props @danielbachhuber, [#1670](https://github.com/WP-API/WP-API/pull/1670))
|
407 |
+
|
408 |
+
* Expose `slug` in `embed` context for Users.
|
409 |
+
|
410 |
+
`user_nicename` is a public attribute, used in user URLs, so this is safe data to present.
|
411 |
+
|
412 |
+
(props @danielbachhuber, [#1666](https://github.com/WP-API/WP-API/pull/1666))
|
413 |
+
|
414 |
+
* Handle falsy value from `wp_count_terms()`, fixing fatal.
|
415 |
+
|
416 |
+
(props @joehoyle, [#1641](https://github.com/WP-API/WP-API/pull/1641))
|
417 |
+
|
418 |
+
* Correct methods in `WP_REST_SERVER::EDITABLE` description.
|
419 |
+
|
420 |
+
(props @rachelbaker, [#1601](https://github.com/WP-API/WP-API/pull/1601))
|
421 |
+
|
422 |
+
* Add the embed context to Users collection query params.
|
423 |
+
|
424 |
+
(props @rachelbaker, [#1591](https://github.com/WP-API/WP-API/pull/1591))
|
425 |
+
|
426 |
+
* Add Terms Controller collection args details.
|
427 |
+
|
428 |
+
(props @rachelbaker, [#1603](https://github.com/WP-API/WP-API/pull/1603))
|
429 |
+
|
430 |
+
* Set comment author details from current user.
|
431 |
+
|
432 |
+
(props @rmccue, [#1580](https://github.com/WP-API/WP-API/pull/1580))
|
433 |
+
|
434 |
+
* More hook documentation.
|
435 |
+
|
436 |
+
(props @adamsilverstein, [#1556](https://github.com/WP-API/WP-API/pull/1556), [#1560](https://github.com/WP-API/WP-API/pull/1560))
|
437 |
+
|
438 |
+
* Return the trashed status of deleted posts/comments.
|
439 |
+
|
440 |
+
When a post or a comment is deleted, returns a flag to say whether it's been trashed or properly deleted.
|
441 |
+
|
442 |
+
(props @pento, [#1499](https://github.com/WP-API/WP-API/pull/1499))
|
443 |
+
|
444 |
+
* In `WP_REST_Posts_Controller::update_item()`, check the post ID based on the proper post type.
|
445 |
+
|
446 |
+
(props @rachelbaker, [#1497](https://github.com/WP-API/WP-API/pull/1497))
|
447 |
+
|
448 |
+
= 2.0 Beta 5.0 =
|
449 |
+
|
450 |
+
* Load api-core as a compatibility library
|
451 |
+
|
452 |
+
Now api-core has been merged into WordPress trunk (for 4.4) we should no longer load the infrastructure code when it's already available. This also fixes a fatal error for users who were on trunk.
|
453 |
+
|
454 |
+
(props @rmccue)
|
455 |
+
|
456 |
+
* Switch to new mysql_to_rfc3339
|
457 |
+
|
458 |
+
(props @rmccue)
|
459 |
+
|
460 |
+
* Double-check term taxonomy
|
461 |
+
|
462 |
+
(props @rmccue)
|
463 |
+
|
464 |
+
* Load admin functions
|
465 |
+
|
466 |
+
This was removed from the latest beta of WordPress in the REST API infrastructure, a more long term fix is planned.
|
467 |
+
|
468 |
+
(props @joehoyle)
|
469 |
+
|
470 |
+
* Add Add compat shim for renamed `rest_mysql_to_rfc3339()`
|
471 |
+
|
472 |
+
(props @danielbachhuber)
|
473 |
+
|
474 |
+
* Compat shim for `wp_is_numeric_array()`
|
475 |
+
|
476 |
+
(props @danielbachhuber)
|
477 |
+
|
478 |
+
* Revert Switch to register_post_type_args filter
|
479 |
+
|
480 |
+
(props @joehoyle)
|
481 |
+
|
482 |
+
= 2.0 Beta 4.0 =
|
483 |
+
|
484 |
+
* Show public user information through the user controller.
|
485 |
+
|
486 |
+
In WordPress as of [r32683](https://core.trac.wordpress.org/changeset/32683) (scheduled for 4.3), `WP_User_Query` now has support for getting users with published posts.
|
487 |
+
|
488 |
+
To match current behaviour in WordPress themes and feeds, we now expose this public user information. This includes the avatar, description, user ID, custom URL, display name, and URL, for users who have published at least one post on the site. This information is available to all clients; other fields and data for all users are still only available when authenticated.
|
489 |
+
|
490 |
+
(props @joehoyle, @rmccue, @Shelob9, [#1397][gh-1397], [#839][gh-839], [#1435][gh-1435])
|
491 |
+
|
492 |
+
* Send schema in OPTIONS requests and index.
|
493 |
+
|
494 |
+
Rather than using separate `/schema` endpoints, the schema for items is now available through an OPTIONS request to the route. This means that full documentation is now available for endpoints through an OPTIONS request; this includes available methods, what data you can pass to the endpoint, and the data you'll get back.
|
495 |
+
|
496 |
+
This data is now also available in the main index and namespace indexes. Simply request the index with `context=help` to get full schema data. Warning: this response will be huge. The schema for single endpoints is also available in the collection's OPTIONS response.
|
497 |
+
|
498 |
+
**⚠️ This breaks backwards compatibility** for clients relying on schemas being at their own routes. These clients should instead send `OPTIONS` requests.
|
499 |
+
|
500 |
+
Custom endpoints can register their own schema via the `schema` option on the route. This option should live side-by-side with the endpoints (similar to `relation` in WP's meta queries), so your registration call will look something like:
|
501 |
+
|
502 |
+
```php
|
503 |
+
register_rest_route( 'test-ns', '/test', array(
|
504 |
+
array(
|
505 |
+
'methods' => 'GET',
|
506 |
+
'callback' => 'my_test_callback',
|
507 |
+
),
|
508 |
+
|
509 |
+
'schema' => 'my_schema_callback',
|
510 |
+
) );
|
511 |
+
```
|
512 |
+
|
513 |
+
(props @rmccue, [#1415][gh-1415], [#1222][gh-1222], [#1305][gh-1305])
|
514 |
+
|
515 |
+
* Update JavaScript API for version 2.
|
516 |
+
|
517 |
+
Our fantastic JavaScript API from version 1 is now available for version 2, refreshed with the latest and greatest changes.
|
518 |
+
|
519 |
+
As a refresher: if you want to use it, simply make your script depend on `wp-api` when you enqueue it. If you want to enqueue the script manually, add `wp_enqueue_script( 'wp-api' )` to a callback on `wp_enqueue_scripts`.
|
520 |
+
|
521 |
+
(props @tlovett1, @kadamwhite, @nathanrice, [#1374][gh-1374], [#1320][gh-1320])
|
522 |
+
|
523 |
+
* Embed links inside items in a collection.
|
524 |
+
|
525 |
+
Previously when fetching a collection of items, you only received the items themselves. To fetch the links as well via embedding, you needed to make a request to the single item with `_embed` set.
|
526 |
+
|
527 |
+
No longer! You can now request a collection with embeds enabled (try `/wp/v2/posts?_embed`). This will embed links inside each item, allowing you to build interface items much easier (for example, post archive pages can get featured image data at the same time).
|
528 |
+
|
529 |
+
This also applies to custom endpoints. Any endpoint that returns a list of objects will automatically have the embedding applied to objects inside the list.
|
530 |
+
|
531 |
+
(props @rmccue, [#1459][gh-1459], [#865][gh-865])
|
532 |
+
|
533 |
+
* Fix potential XSS vulnerability.
|
534 |
+
|
535 |
+
Requests from other origins could potentially run code on the API domain, allowing cross-origin access to authentication cookies or similar.
|
536 |
+
|
537 |
+
Reported by @xknown on 2015-07-23.
|
538 |
+
|
539 |
+
* Move `/posts` `WP_Query` vars back to `filter` param.
|
540 |
+
|
541 |
+
In version 1, we had internal `WP_Query` vars available via `filter` (e.g. `filter[s]=search+term`). For our first betas of version 2, we tried something different and exposed these directly on the endpoint. The experiment has now concluded; we didn't like this that much, so `filter` is back.
|
542 |
+
|
543 |
+
We plan on adding nicer looking arguments to collections in future releases, with a view towards being consistent across different collections. We also plan on opening up the underlying query vars via `filter` for users, comments, and terms as well.
|
544 |
+
|
545 |
+
**⚠️ This breaks backwards compatibility** for users using WP Query vars. Simply change your `x=y` parameter to `filter[x]=y`.
|
546 |
+
|
547 |
+
(props @WP-API, [#1420][gh-1420])
|
548 |
+
|
549 |
+
* Respect `rest_base` for taxonomies.
|
550 |
+
|
551 |
+
**⚠️ This breaks backwards compatibility** by changing the `/wp/v2/posts/{id}/terms/post_tag` endpoint to `/wp/v2/posts/{id}/tag`.
|
552 |
+
|
553 |
+
(props @joehoyle, [#1466][gh-1466])
|
554 |
+
|
555 |
+
* Add permission check for retrieving the posts collection in edit context.
|
556 |
+
|
557 |
+
By extension of the fact that getting any individual post yields a forbidden context error when the `context=edit` and the user is not authorized, the user should also not be permitted to list any post items when unauthorized.
|
558 |
+
|
559 |
+
(props @danielpunkass, [#1412][gh-1412])
|
560 |
+
|
561 |
+
* Ensure the REST API URL always has a trailing slash.
|
562 |
+
|
563 |
+
Previously, when pretty permalinks were enabled, the API URL during autodiscovery looked like `/wp-json`, whereas the non-pretty permalink URL looked like `?rest_route=/`. These are now consistent, and always end with a slash character to simplify client URL building.
|
564 |
+
|
565 |
+
(props @danielpunkass, @rmccue, [#1426][gh-1426], [#1442][gh-1442], [#1455][gh-1455], [#1467][gh-1467])
|
566 |
+
|
567 |
+
* Use `wp_json_encode` instead of `json_encode`
|
568 |
+
|
569 |
+
Since WordPress 4.1, `wp_json_encode` has been available to ensure encoded values are sane, and that non-UTF8 encodings are supported. We now use this function rather than doing the encode ourselves.
|
570 |
+
|
571 |
+
(props @rmccue, @pento, [#1417][gh-1417])
|
572 |
+
|
573 |
+
* Add `role` to schema for users.
|
574 |
+
|
575 |
+
The available roles you can assign to a user are now available in the schema as an `enum`.
|
576 |
+
|
577 |
+
(props @joehoyle, [#1400][gh-1400])
|
578 |
+
|
579 |
+
* Use the schema for validation inside the comments controller.
|
580 |
+
|
581 |
+
Previously, the schema was merely a decorative element for documentation inside the comments controller. To bring it inline with our other controllers, the schema is now used internally for validation.
|
582 |
+
|
583 |
+
(props @joehoyle, [#1422][gh-1422])
|
584 |
+
|
585 |
+
* Don't set the Location header in update responses.
|
586 |
+
|
587 |
+
Previously, the Location header was sent when updating resources due to some inadvertent copypasta. This header should only be sent when creating to direct clients to the new resource, and isn't required when you're already on the correct resource.
|
588 |
+
|
589 |
+
(props @rachelbaker, [#1441][gh-1441])
|
590 |
+
|
591 |
+
* Re-enable the `rest_insert_post` action hook for `WP_REST_Posts_Controller`
|
592 |
+
|
593 |
+
This was disabled during 2.0 development to avoid breaking lots of plugins on the `json_insert_post` action. Now that we've changed namespaces and are Mostly Stable (tm), we can re-enable the action.
|
594 |
+
|
595 |
+
(props @jaredcobb, [#1427][gh-1427], [#1424][gh-1424])
|
596 |
+
|
597 |
+
* Fix post taxonomy terms link URLs.
|
598 |
+
|
599 |
+
When moving the routes in a previous beta, we forgot to correct the links on post objects to the new correct route. Sorry!
|
600 |
+
|
601 |
+
(props @rachelbaker, @joehoyle, [#1447][gh-1447], [#1383][gh-1383])
|
602 |
+
|
603 |
+
* Use `wp_get_attachment_image_src()` on the image sizes in attachments.
|
604 |
+
|
605 |
+
Since the first versions of the API, we've been building attachment URLs via `str_replace`. Who knows why we were doing this, but it caused problems with custom attachment URLs (such as CDN-hosted images). This now correctly uses the internal functions and filters.
|
606 |
+
|
607 |
+
(props @joehoyle, [#1462][gh-1462])
|
608 |
+
|
609 |
+
* Make the embed context a default, not forced.
|
610 |
+
|
611 |
+
If you want embeds to bring in full data rather than with `context=edit`, you can now change the link to specify `context=view` explicitly.
|
612 |
+
|
613 |
+
(props @rmccue, [#1464][gh-1464])
|
614 |
+
|
615 |
+
* Ensure we always use the `term_taxonomy_id` and never expose `term_id` publicly.
|
616 |
+
|
617 |
+
Previously, `term_id` was inadvertently exposed in some error responses.
|
618 |
+
|
619 |
+
(props @jdolan, [#1430][gh-1430])
|
620 |
+
|
621 |
+
* Fix adding alt text to attachments on creation.
|
622 |
+
|
623 |
+
Previously, this could only be set when updating an attachment, not when creating one.
|
624 |
+
|
625 |
+
(props @joehoyle, [#1398][gh-1398])
|
626 |
+
|
627 |
+
* Throw an error when registering routes without a namespace.
|
628 |
+
|
629 |
+
Namespaces should **always** be provided when registering routes. We now throw a `doing_it_wrong` error when attempting to register one. (Previously, this caused a warning, or an invalid internal route.)
|
630 |
+
|
631 |
+
If you *really* need to register namespaceless routes (e.g. to replicate an existing API), call `WP_REST_Server::register_route` directly rather than using the convenience function.
|
632 |
+
|
633 |
+
(props @joehoyle, @rmccue, [#1355][gh-1355])
|
634 |
+
|
635 |
+
* Show links on embeds.
|
636 |
+
|
637 |
+
Previously, links were accidentally stripped from embedded response data.
|
638 |
+
|
639 |
+
(props @rmccue, [#1472][gh-1472])
|
640 |
+
|
641 |
+
* Clarify insufficient permisssion error when editing posts.
|
642 |
+
|
643 |
+
(props @danielpunkass, [#1411][gh-1411])
|
644 |
+
|
645 |
+
* Improve @return inline docs for rest_ensure_response()
|
646 |
+
|
647 |
+
(props @Shelob9, [#1328][gh-1328])
|
648 |
+
|
649 |
+
* Check taxonomies exist before trying to set properties.
|
650 |
+
|
651 |
+
(props @joehoyle, @rachelbaker, [#1354][gh-1354])
|
652 |
+
|
653 |
+
* Update controllers to ensure we use `sanitize_callback` wherever possible.
|
654 |
+
|
655 |
+
(props @joehoyle, [#1399][gh-1399])
|
656 |
+
|
657 |
+
* Add more phpDoc documentation, and correct existing documentation.
|
658 |
+
|
659 |
+
(props @Shelob9, @rmccue, [#1432][gh-1432], [#1433][gh-1433], [#1465][gh-1465])
|
660 |
+
|
661 |
+
* Update testing infrastructure.
|
662 |
+
|
663 |
+
Travis now runs our coding standards tests in parallel, and now uses the new, faster container-based testing infrastructure.
|
664 |
+
|
665 |
+
(props @ntwb, @frozzare, [#1449][gh-1449], [#1457][gh-1457])
|
666 |
+
|
667 |
+
[View all changes](https://github.com/WP-API/WP-API/compare/2.0-beta3...2.0-beta4)
|
668 |
+
|
669 |
+
[gh-839]: https://github.com/WP-API/WP-API/issues/839
|
670 |
+
[gh-865]: https://github.com/WP-API/WP-API/issues/865
|
671 |
+
[gh-1222]: https://github.com/WP-API/WP-API/issues/1222
|
672 |
+
[gh-1305]: https://github.com/WP-API/WP-API/issues/1305
|
673 |
+
[gh-1310]: https://github.com/WP-API/WP-API/issues/1310
|
674 |
+
[gh-1320]: https://github.com/WP-API/WP-API/issues/1320
|
675 |
+
[gh-1328]: https://github.com/WP-API/WP-API/issues/1328
|
676 |
+
[gh-1354]: https://github.com/WP-API/WP-API/issues/1354
|
677 |
+
[gh-1355]: https://github.com/WP-API/WP-API/issues/1355
|
678 |
+
[gh-1372]: https://github.com/WP-API/WP-API/issues/1372
|
679 |
+
[gh-1374]: https://github.com/WP-API/WP-API/issues/1374
|
680 |
+
[gh-1383]: https://github.com/WP-API/WP-API/issues/1383
|
681 |
+
[gh-1397]: https://github.com/WP-API/WP-API/issues/1397
|
682 |
+
[gh-1398]: https://github.com/WP-API/WP-API/issues/1398
|
683 |
+
[gh-1399]: https://github.com/WP-API/WP-API/issues/1399
|
684 |
+
[gh-1400]: https://github.com/WP-API/WP-API/issues/1400
|
685 |
+
[gh-1402]: https://github.com/WP-API/WP-API/issues/1402
|
686 |
+
[gh-1411]: https://github.com/WP-API/WP-API/issues/1411
|
687 |
+
[gh-1412]: https://github.com/WP-API/WP-API/issues/1412
|
688 |
+
[gh-1413]: https://github.com/WP-API/WP-API/issues/1413
|
689 |
+
[gh-1415]: https://github.com/WP-API/WP-API/issues/1415
|
690 |
+
[gh-1417]: https://github.com/WP-API/WP-API/issues/1417
|
691 |
+
[gh-1420]: https://github.com/WP-API/WP-API/issues/1420
|
692 |
+
[gh-1422]: https://github.com/WP-API/WP-API/issues/1422
|
693 |
+
[gh-1424]: https://github.com/WP-API/WP-API/issues/1424
|
694 |
+
[gh-1426]: https://github.com/WP-API/WP-API/issues/1426
|
695 |
+
[gh-1427]: https://github.com/WP-API/WP-API/issues/1427
|
696 |
+
[gh-1430]: https://github.com/WP-API/WP-API/issues/1430
|
697 |
+
[gh-1432]: https://github.com/WP-API/WP-API/issues/1432
|
698 |
+
[gh-1433]: https://github.com/WP-API/WP-API/issues/1433
|
699 |
+
[gh-1435]: https://github.com/WP-API/WP-API/issues/1435
|
700 |
+
[gh-1441]: https://github.com/WP-API/WP-API/issues/1441
|
701 |
+
[gh-1442]: https://github.com/WP-API/WP-API/issues/1442
|
702 |
+
[gh-1447]: https://github.com/WP-API/WP-API/issues/1447
|
703 |
+
[gh-1449]: https://github.com/WP-API/WP-API/issues/1449
|
704 |
+
[gh-1455]: https://github.com/WP-API/WP-API/issues/1455
|
705 |
+
[gh-1455]: https://github.com/WP-API/WP-API/issues/1455
|
706 |
+
[gh-1457]: https://github.com/WP-API/WP-API/issues/1457
|
707 |
+
[gh-1459]: https://github.com/WP-API/WP-API/issues/1459
|
708 |
+
[gh-1462]: https://github.com/WP-API/WP-API/issues/1462
|
709 |
+
[gh-1464]: https://github.com/WP-API/WP-API/issues/1464
|
710 |
+
[gh-1465]: https://github.com/WP-API/WP-API/issues/1465
|
711 |
+
[gh-1466]: https://github.com/WP-API/WP-API/issues/1466
|
712 |
+
[gh-1467]: https://github.com/WP-API/WP-API/issues/1467
|
713 |
+
[gh-1472]: https://github.com/WP-API/WP-API/issues/1472
|
714 |
+
|
715 |
+
= 2.0 Beta 3.0 =
|
716 |
+
|
717 |
+
* Add ability to declare sanitization and default options for schema fields.
|
718 |
+
|
719 |
+
The `arg_options` array can be used to declare the sanitization callback,
|
720 |
+
default value, or requirement of a field.
|
721 |
+
|
722 |
+
(props @joehoyle, [#1345][gh-1345])
|
723 |
+
(props @joehoyle, [#1346][gh-1346])
|
724 |
+
|
725 |
+
* Expand supported parameters for creating and updating Comments.
|
726 |
+
|
727 |
+
(props @rachelbaker, [#1245][gh-1245])
|
728 |
+
|
729 |
+
* Declare collection parameters for Terms of a Post.
|
730 |
+
|
731 |
+
Define the available collection parameters in `get_collection_params()` and
|
732 |
+
allow Terms of a Post to be queried by term order.
|
733 |
+
|
734 |
+
(props @danielbachhuber, [#1332][gh-1332])
|
735 |
+
|
736 |
+
* Improve the Attachment error message for an invalid Content-Disposition
|
737 |
+
|
738 |
+
(props @danielbachhuber, [#1317][gh-1317])
|
739 |
+
|
740 |
+
* Return 200 status when updating Attachments, Comments, and Users.
|
741 |
+
|
742 |
+
(props @rachelbaker, [#1348][gh-1348])
|
743 |
+
|
744 |
+
* Remove unnecessary `handle_format_param()` method.
|
745 |
+
|
746 |
+
(props @danielbachhuber, [#1331][gh-1331])
|
747 |
+
|
748 |
+
* Add `author_avatar_url` field to the Comment response and schema.
|
749 |
+
|
750 |
+
(props @rachelbaker [#1327][gh-1327])
|
751 |
+
|
752 |
+
* Introduce `rest_do_request()` for making REST requests internally.
|
753 |
+
|
754 |
+
(props @danielbachhuber, [#1333][gh-1333])
|
755 |
+
|
756 |
+
* Remove unused DateTime class.
|
757 |
+
|
758 |
+
(props @rmccue, [#1314][gh-1314])
|
759 |
+
|
760 |
+
* Add inline documentation for `$wp_rest_server` global.
|
761 |
+
|
762 |
+
(props @Shelob9, [#1324][gh-1324])
|
763 |
+
|
764 |
+
[View all changes](https://github.com/WP-API/WP-API/compare/2.0-beta2...2.0-beta3)
|
765 |
+
[gh-1245]: https://github.com/WP-API/WP-API/issues/1245
|
766 |
+
[gh-1314]: https://github.com/WP-API/WP-API/issues/1314
|
767 |
+
[gh-1317]: https://github.com/WP-API/WP-API/issues/1317
|
768 |
+
[gh-1318]: https://github.com/WP-API/WP-API/issues/1318
|
769 |
+
[gh-1324]: https://github.com/WP-API/WP-API/issues/1324
|
770 |
+
[gh-1326]: https://github.com/WP-API/WP-API/issues/1326
|
771 |
+
[gh-1327]: https://github.com/WP-API/WP-API/issues/1327
|
772 |
+
[gh-1331]: https://github.com/WP-API/WP-API/issues/1331
|
773 |
+
[gh-1332]: https://github.com/WP-API/WP-API/issues/1332
|
774 |
+
[gh-1333]: https://github.com/WP-API/WP-API/issues/1333
|
775 |
+
[gh-1345]: https://github.com/WP-API/WP-API/issues/1345
|
776 |
+
[gh-1346]: https://github.com/WP-API/WP-API/issues/1346
|
777 |
+
[gh-1347]: https://github.com/WP-API/WP-API/issues/1347
|
778 |
+
[gh-1348]: https://github.com/WP-API/WP-API/issues/1348
|
779 |
+
|
780 |
+
= 2.0 Beta 2.0 =
|
781 |
+
|
782 |
+
* Load the WP REST API before the main query runs.
|
783 |
+
|
784 |
+
The `rest_api_loaded` function now hooks into the `parse_request` action.
|
785 |
+
This change prevents the main query from being run on every request and
|
786 |
+
allows sites to set `WP_USE_THEMES` to `false`. Previously, the main query
|
787 |
+
was always being run (`SELECT * FROM wp_posts LIMIT 10`), even though the
|
788 |
+
result was never used and couldn't be cached.
|
789 |
+
|
790 |
+
(props @rmccue, [#1270][gh-1270])
|
791 |
+
|
792 |
+
* Register a new field on an existing WordPress object type.
|
793 |
+
|
794 |
+
Introduces `register_api_field()` to add a field to an object and
|
795 |
+
its schema.
|
796 |
+
|
797 |
+
(props @joehoyle, @rachelbaker, [#927][gh-927])
|
798 |
+
(props @joehoyle, [#1207][gh-1207])
|
799 |
+
(props @joehoyle, [#1243][gh-1243])
|
800 |
+
|
801 |
+
* Add endpoints for viewing, creating, updating, and deleting Terms for a Post.
|
802 |
+
|
803 |
+
The new `WP_REST_Posts_Terms_Controller` class controller supports routes for
|
804 |
+
Terms that belong to a Post.
|
805 |
+
|
806 |
+
(props @joehoyle, @danielbachhuber, [#1216][gh-1216])
|
807 |
+
|
808 |
+
* Add pagination headers for collection queries.
|
809 |
+
|
810 |
+
The `X-WP-Total` and `X-WP-TotalPages` are now present in terms, comments,
|
811 |
+
and users collection responses.
|
812 |
+
|
813 |
+
(props @danielbachhuber, [#1182][gh-1182])
|
814 |
+
(props @danielbachhuber, [#1191][gh-1191])
|
815 |
+
(props @danielbachhuber, @joehoyle, [#1197][gh-1197])
|
816 |
+
|
817 |
+
* List registered namespaces in the index for feature detection.
|
818 |
+
|
819 |
+
The index (`/wp-json` by default) now contains a list of the available
|
820 |
+
namespaces. This allows for simple feature detection. You can grab the index
|
821 |
+
and check namespaces for `wp/v3` or `pluginname/v2`, which indicate the
|
822 |
+
supported endpoints on the site.
|
823 |
+
|
824 |
+
(props @rmccue,, [#1283][gh-1283])
|
825 |
+
|
826 |
+
* Standardize link property relations and support embedding for all resources.
|
827 |
+
|
828 |
+
Change link properties to use IANA-registered relations. Also adds embedding
|
829 |
+
support to Attachments, Comments and Terms.
|
830 |
+
|
831 |
+
(props @rmccue, @rachelbaker, [#1284][gh-1284])
|
832 |
+
|
833 |
+
* Add support for Composer dependency management.
|
834 |
+
|
835 |
+
Allows you to recursively install/update the WP REST API inside of WordPress
|
836 |
+
plugins or themes.
|
837 |
+
|
838 |
+
(props @QWp6t, [#1157][gh-1157])
|
839 |
+
|
840 |
+
* Return full objects in the delete response.
|
841 |
+
|
842 |
+
Instead of returning a random message when deleting a Post, Comment, Term, or
|
843 |
+
User provide the original resource data.
|
844 |
+
|
845 |
+
(props @danielbachhuber, [#1253][gh-1253])
|
846 |
+
(props @danielbachhuber, [#1254][gh-1254])
|
847 |
+
(props @danielbachhuber, [#1255][gh-1255])
|
848 |
+
(props @danielbachhuber, [#1256][gh-1256])
|
849 |
+
|
850 |
+
* Return programmatically readable error messages for invalid or missing
|
851 |
+
required parameters.
|
852 |
+
|
853 |
+
(props @joehoyle, [#1175][gh-1175])
|
854 |
+
|
855 |
+
* Declare supported arguments for Comment and User collection queries.
|
856 |
+
|
857 |
+
(props @danielbachhuber, [#1211][gh-1211])
|
858 |
+
(props @danielbachhuber, [#1217][gh-1217])
|
859 |
+
|
860 |
+
* Automatically validate parameters based on Schema data.
|
861 |
+
|
862 |
+
(props @joehoyle, [#1128][gh-1128])
|
863 |
+
|
864 |
+
* Use the `show_in_rest` attributes for exposing Taxonomies.
|
865 |
+
|
866 |
+
(props @joehoyle, [#1279][gh-1279])
|
867 |
+
|
868 |
+
* Handle `parent` when creating or updating a Term.
|
869 |
+
|
870 |
+
(props @joehoyle, [#1221][gh-1221])
|
871 |
+
|
872 |
+
* Limit fields returned in `embed` context User responses.
|
873 |
+
|
874 |
+
(props @rachelbaker, [#1251][gh-1251])
|
875 |
+
|
876 |
+
* Only include `parent` in term response when tax is hierarchical.
|
877 |
+
|
878 |
+
(props @danielbachhuber, [#1189][gh-1189])
|
879 |
+
|
880 |
+
* Fix bug in creating comments if `type` was not set.
|
881 |
+
|
882 |
+
(props @rachelbaker, [#1244][gh-1244])
|
883 |
+
|
884 |
+
* Rename `post_name` field to `post_slug`.
|
885 |
+
|
886 |
+
(props @danielbachhuber, [#1235][gh-1235])
|
887 |
+
|
888 |
+
* Add check when creating a user to verify the provided role is valid.
|
889 |
+
|
890 |
+
(props @rachelbaker, [#1267][gh-1267])
|
891 |
+
|
892 |
+
* Add link properties to the Post Status response.
|
893 |
+
|
894 |
+
(props @joehoyle, [#1243][gh-1243])
|
895 |
+
|
896 |
+
* Return `0` for `parent` in Post response instead of `null`.
|
897 |
+
|
898 |
+
(props @danielbachhuber, [#1269][gh-1269])
|
899 |
+
|
900 |
+
* Only link `author` when there's a valid author
|
901 |
+
|
902 |
+
(props @danielbachhuber, [#1203][gh-1203])
|
903 |
+
|
904 |
+
* Only permit querying by parent term when tax is hierarchical.
|
905 |
+
|
906 |
+
(props @danielbachhuber, [#1219][gh-1219])
|
907 |
+
|
908 |
+
* Only permit deleting posts of the proper type
|
909 |
+
|
910 |
+
(props @danielbachhuber, [#1257][gh-1257])
|
911 |
+
|
912 |
+
* Set pagination headers even when no found posts.
|
913 |
+
|
914 |
+
(props @danielbachhuber, [#1209][gh-1209])
|
915 |
+
|
916 |
+
* Correct prefix in `rest_request_parameter_order` filter.
|
917 |
+
|
918 |
+
(props @quasel, [#1158][gh-1158])
|
919 |
+
|
920 |
+
* Retool `WP_REST_Terms_Controller` to follow Posts controller pattern.
|
921 |
+
|
922 |
+
(props @danielbachhuber, [#1170][gh-1170])
|
923 |
+
|
924 |
+
* Remove unused `accept_json argument` from the `register_routes` method.
|
925 |
+
|
926 |
+
(props @quasel, [#1160][gh-1160])
|
927 |
+
|
928 |
+
* Fix typo in `sanitize_params` inline documentation.
|
929 |
+
|
930 |
+
(props @Shelob9, [#1226][gh-1226])
|
931 |
+
|
932 |
+
* Remove commented out code in dispatch method.
|
933 |
+
|
934 |
+
(props @rachelbaker, [#1162][gh-1162])
|
935 |
+
|
936 |
+
|
937 |
+
[View all changes](https://github.com/WP-API/WP-API/compare/2.0-beta1.1...2.0-beta2)
|
938 |
+
[gh-927]: https://github.com/WP-API/WP-API/issues/927
|
939 |
+
[gh-1128]: https://github.com/WP-API/WP-API/issues/1128
|
940 |
+
[gh-1157]: https://github.com/WP-API/WP-API/issues/1157
|
941 |
+
[gh-1158]: https://github.com/WP-API/WP-API/issues/1158
|
942 |
+
[gh-1160]: https://github.com/WP-API/WP-API/issues/1160
|
943 |
+
[gh-1162]: https://github.com/WP-API/WP-API/issues/1162
|
944 |
+
[gh-1168]: https://github.com/WP-API/WP-API/issues/1168
|
945 |
+
[gh-1170]: https://github.com/WP-API/WP-API/issues/1170
|
946 |
+
[gh-1171]: https://github.com/WP-API/WP-API/issues/1171
|
947 |
+
[gh-1175]: https://github.com/WP-API/WP-API/issues/1175
|
948 |
+
[gh-1176]: https://github.com/WP-API/WP-API/issues/1176
|
949 |
+
[gh-1177]: https://github.com/WP-API/WP-API/issues/1177
|
950 |
+
[gh-1181]: https://github.com/WP-API/WP-API/issues/1181
|
951 |
+
[gh-1182]: https://github.com/WP-API/WP-API/issues/1182
|
952 |
+
[gh-1188]: https://github.com/WP-API/WP-API/issues/1188
|
953 |
+
[gh-1189]: https://github.com/WP-API/WP-API/issues/1189
|
954 |
+
[gh-1191]: https://github.com/WP-API/WP-API/issues/1191
|
955 |
+
[gh-1197]: https://github.com/WP-API/WP-API/issues/1197
|
956 |
+
[gh-1200]: https://github.com/WP-API/WP-API/issues/1200
|
957 |
+
[gh-1203]: https://github.com/WP-API/WP-API/issues/1203
|
958 |
+
[gh-1207]: https://github.com/WP-API/WP-API/issues/1207
|
959 |
+
[gh-1209]: https://github.com/WP-API/WP-API/issues/1209
|
960 |
+
[gh-1210]: https://github.com/WP-API/WP-API/issues/1210
|
961 |
+
[gh-1211]: https://github.com/WP-API/WP-API/issues/1211
|
962 |
+
[gh-1216]: https://github.com/WP-API/WP-API/issues/1216
|
963 |
+
[gh-1217]: https://github.com/WP-API/WP-API/issues/1217
|
964 |
+
[gh-1219]: https://github.com/WP-API/WP-API/issues/1219
|
965 |
+
[gh-1221]: https://github.com/WP-API/WP-API/issues/1221
|
966 |
+
[gh-1226]: https://github.com/WP-API/WP-API/issues/1226
|
967 |
+
[gh-1235]: https://github.com/WP-API/WP-API/issues/1235
|
968 |
+
[gh-1243]: https://github.com/WP-API/WP-API/issues/1243
|
969 |
+
[gh-1244]: https://github.com/WP-API/WP-API/issues/1244
|
970 |
+
[gh-1249]: https://github.com/WP-API/WP-API/issues/1249
|
971 |
+
[gh-1251]: https://github.com/WP-API/WP-API/issues/1251
|
972 |
+
[gh-1253]: https://github.com/WP-API/WP-API/issues/1253
|
973 |
+
[gh-1254]: https://github.com/WP-API/WP-API/issues/1254
|
974 |
+
[gh-1255]: https://github.com/WP-API/WP-API/issues/1255
|
975 |
+
[gh-1256]: https://github.com/WP-API/WP-API/issues/1256
|
976 |
+
[gh-1257]: https://github.com/WP-API/WP-API/issues/1257
|
977 |
+
[gh-1259]: https://github.com/WP-API/WP-API/issues/1259
|
978 |
+
[gh-1267]: https://github.com/WP-API/WP-API/issues/1267
|
979 |
+
[gh-1268]: https://github.com/WP-API/WP-API/issues/1268
|
980 |
+
[gh-1269]: https://github.com/WP-API/WP-API/issues/1269
|
981 |
+
[gh-1270]: https://github.com/WP-API/WP-API/issues/1270
|
982 |
+
[gh-1276]: https://github.com/WP-API/WP-API/issues/1276
|
983 |
+
[gh-1277]: https://github.com/WP-API/WP-API/issues/1277
|
984 |
+
[gh-1279]: https://github.com/WP-API/WP-API/issues/1279
|
985 |
+
[gh-1283]: https://github.com/WP-API/WP-API/issues/1283
|
986 |
+
[gh-1284]: https://github.com/WP-API/WP-API/issues/1284
|
987 |
+
[gh-1295]: https://github.com/WP-API/WP-API/issues/1295
|
988 |
+
[gh-1301]: https://github.com/WP-API/WP-API/issues/1301
|
989 |
+
|
990 |
+
|
991 |
+
= 2.0 Beta 1.1 =
|
992 |
+
|
993 |
+
* Fix user access security vulnerability.
|
994 |
+
|
995 |
+
Authenticated users were able to escalate their privileges bypassing the
|
996 |
+
expected capabilities check.
|
997 |
+
|
998 |
+
Reported by @kacperszurek on 2015-05-16.
|
999 |
+
|
1000 |
+
= 2.0 Beta 1 =
|
1001 |
|
1002 |
Partial rewrite and evolution of the REST API to prepare for core integration.
|
1003 |
|
wp-api.js
CHANGED
@@ -13,18 +13,25 @@
|
|
13 |
|
14 |
})( window );
|
15 |
|
16 |
-
(function(
|
|
|
|
|
17 |
|
18 |
-
|
|
|
|
|
19 |
|
20 |
-
|
21 |
-
|
|
|
|
|
22 |
if ( ! Date.prototype.toISOString ) {
|
23 |
var pad = function( number ) {
|
24 |
var r = String( number );
|
25 |
if ( r.length === 1 ) {
|
26 |
r = '0' + r;
|
27 |
}
|
|
|
28 |
return r;
|
29 |
};
|
30 |
|
@@ -35,88 +42,83 @@
|
|
35 |
'T' + pad( this.getUTCHours() ) +
|
36 |
':' + pad( this.getUTCMinutes() ) +
|
37 |
':' + pad( this.getUTCSeconds() ) +
|
38 |
-
'.' + String( ( this.getUTCMilliseconds()/1000 ).toFixed( 3 ) ).slice( 2, 5 ) +
|
39 |
'Z';
|
40 |
};
|
41 |
}
|
42 |
|
43 |
-
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
// ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
|
53 |
-
// before falling back to any implementation-specific date parsing, so that’s what we do, even if native
|
54 |
-
// implementations could be faster
|
55 |
-
// 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
|
56 |
-
if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
|
57 |
-
// avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
|
58 |
-
for ( i = 0; ( k = numericKeys[i] ); ++i) {
|
59 |
-
struct[k] = +struct[k] || 0;
|
60 |
-
}
|
61 |
-
|
62 |
-
// allow undefined days and months
|
63 |
-
struct[2] = ( +struct[2] || 1 ) - 1;
|
64 |
-
struct[3] = +struct[3] || 1;
|
65 |
|
66 |
-
|
67 |
-
|
68 |
|
69 |
-
|
70 |
-
|
71 |
-
}
|
72 |
}
|
73 |
-
|
74 |
-
timestamp = Date.UTC( struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7] );
|
75 |
-
}
|
76 |
-
else {
|
77 |
-
timestamp = origParse ? origParse( date ) : NaN;
|
78 |
}
|
79 |
|
80 |
-
|
81 |
-
}
|
82 |
-
|
|
|
83 |
|
84 |
-
|
85 |
-
|
86 |
-
wp.api.utils = wp.api.utils || new WP_API_Utils();
|
87 |
|
88 |
-
})(
|
89 |
|
90 |
/* global WP_API_Settings:false */
|
91 |
// Suppress warning about parse function's unused "options" argument:
|
92 |
/* jshint unused:false */
|
93 |
-
(function( wp, WP_API_Settings, Backbone,
|
94 |
|
95 |
'use strict';
|
96 |
|
97 |
/**
|
98 |
-
* Array of parseable dates
|
99 |
*
|
100 |
-
* @type {string[]}
|
101 |
*/
|
102 |
var parseable_dates = [ 'date', 'modified', 'date_gmt', 'modified_gmt' ];
|
103 |
|
104 |
/**
|
105 |
-
* Mixin for all content that is time stamped
|
106 |
*
|
107 |
-
* @type {{toJSON: toJSON, parse: parse}}
|
108 |
*/
|
109 |
var TimeStampedMixin = {
|
110 |
/**
|
111 |
-
* Serialize the entity pre-sync
|
112 |
*
|
113 |
-
* @returns {*}
|
114 |
*/
|
115 |
toJSON: function() {
|
116 |
var attributes = _.clone( this.attributes );
|
117 |
|
118 |
-
// Serialize Date objects back into 8601 strings
|
119 |
-
_.each( parseable_dates, function
|
120 |
if ( key in attributes ) {
|
121 |
attributes[key] = attributes[key].toISOString();
|
122 |
}
|
@@ -126,13 +128,14 @@
|
|
126 |
},
|
127 |
|
128 |
/**
|
129 |
-
* Unserialize the fetched response
|
130 |
*
|
131 |
-
* @param {*} response
|
132 |
-
* @returns {*}
|
133 |
*/
|
134 |
parse: function( response ) {
|
135 |
-
|
|
|
136 |
_.each( parseable_dates, function ( key ) {
|
137 |
if ( ! ( key in response ) ) {
|
138 |
return;
|
@@ -142,8 +145,8 @@
|
|
142 |
response[key] = new Date( timestamp );
|
143 |
});
|
144 |
|
145 |
-
// Parse the author into a User object
|
146 |
-
if ( response.author
|
147 |
response.author = new wp.api.models.User( response.author );
|
148 |
}
|
149 |
|
@@ -152,13 +155,13 @@
|
|
152 |
};
|
153 |
|
154 |
/**
|
155 |
-
* Mixin for all hierarchical content types such as posts
|
156 |
*
|
157 |
-
* @type {{parent: parent}}
|
158 |
*/
|
159 |
var HierarchicalMixin = {
|
160 |
/**
|
161 |
-
* Get parent object
|
162 |
*
|
163 |
* @returns {Backbone.Model}
|
164 |
*/
|
@@ -166,14 +169,14 @@
|
|
166 |
|
167 |
var object, parent = this.get( 'parent' );
|
168 |
|
169 |
-
// Return null if we don't have a parent
|
170 |
if ( parent === 0 ) {
|
171 |
return null;
|
172 |
}
|
173 |
|
174 |
var parentModel = this;
|
175 |
|
176 |
-
if ( typeof this.parentModel
|
177 |
/**
|
178 |
* Probably a better way to do this. Perhaps grab a cached version of the
|
179 |
* instantiated model?
|
@@ -185,36 +188,38 @@
|
|
185 |
if ( parentModel.collection ) {
|
186 |
return parentModel.collection.get( parent );
|
187 |
} else {
|
188 |
-
|
|
|
189 |
object = new parentModel.constructor( {
|
190 |
-
|
191 |
});
|
192 |
|
193 |
-
// Note that this acts asynchronously
|
194 |
object.fetch();
|
|
|
195 |
return object;
|
196 |
}
|
197 |
}
|
198 |
};
|
199 |
|
200 |
/**
|
201 |
-
* Private Backbone base model for all models
|
202 |
*/
|
203 |
-
var
|
204 |
-
/** @lends
|
205 |
{
|
206 |
/**
|
207 |
-
* Set nonce header before every Backbone sync
|
208 |
*
|
209 |
-
* @param {string} method
|
210 |
-
* @param {Backbone.Model} model
|
211 |
-
* @param {{beforeSend}, *} options
|
212 |
-
* @returns {*}
|
213 |
*/
|
214 |
sync: function( method, model, options ) {
|
215 |
options = options || {};
|
216 |
|
217 |
-
if ( typeof WP_API_Settings.nonce
|
218 |
var beforeSend = options.beforeSend;
|
219 |
|
220 |
options.beforeSend = function( xhr ) {
|
@@ -232,387 +237,344 @@
|
|
232 |
);
|
233 |
|
234 |
/**
|
235 |
-
* Backbone model for single
|
|
|
|
|
|
|
|
|
236 |
*/
|
237 |
-
wp.api.models.User =
|
238 |
/** @lends User.prototype */
|
239 |
{
|
240 |
-
idAttribute: '
|
241 |
|
242 |
-
urlRoot: WP_API_Settings.root + '/users',
|
243 |
|
244 |
defaults: {
|
245 |
-
|
246 |
-
|
|
|
|
|
247 |
email: '',
|
248 |
-
|
249 |
-
name: '',
|
250 |
first_name: '',
|
251 |
last_name: '',
|
|
|
|
|
252 |
nickname: '',
|
|
|
|
|
253 |
slug: '',
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
links: {}
|
258 |
-
}
|
259 |
-
},
|
260 |
-
|
261 |
-
/**
|
262 |
-
* Return avatar URL
|
263 |
-
*
|
264 |
-
* @param {number} size
|
265 |
-
* @returns {string}
|
266 |
-
*/
|
267 |
-
avatar: function( size ) {
|
268 |
-
return this.get( 'avatar' ) + '&s=' + size;
|
269 |
}
|
270 |
}
|
271 |
);
|
272 |
|
273 |
/**
|
274 |
-
* Model for
|
|
|
|
|
|
|
275 |
*/
|
276 |
-
wp.api.models.Taxonomy =
|
277 |
/** @lends Taxonomy.prototype */
|
278 |
{
|
279 |
idAttribute: 'slug',
|
280 |
|
281 |
-
urlRoot: WP_API_Settings.root + '/taxonomies',
|
282 |
|
283 |
defaults: {
|
284 |
name: '',
|
285 |
slug: null,
|
|
|
286 |
labels: {},
|
287 |
-
types:
|
288 |
show_cloud: false,
|
289 |
-
hierarchical: false
|
290 |
-
meta: {
|
291 |
-
links: {}
|
292 |
-
}
|
293 |
}
|
294 |
}
|
295 |
);
|
296 |
|
297 |
/**
|
298 |
-
* Backbone model for term
|
|
|
|
|
|
|
299 |
*/
|
300 |
-
wp.api.models.Term =
|
301 |
/** @lends Term.prototype */
|
302 |
{
|
303 |
-
idAttribute: '
|
304 |
-
|
305 |
-
taxonomy: 'category',
|
306 |
-
|
307 |
-
/**
|
308 |
-
* @class Represent a term
|
309 |
-
* @augments Backbone.Model
|
310 |
-
* @constructs
|
311 |
-
*/
|
312 |
-
initialize: function( attributes, options ) {
|
313 |
-
if ( typeof options !== 'undefined' ) {
|
314 |
-
if ( options.taxonomy ) {
|
315 |
-
this.taxonomy = options.taxonomy;
|
316 |
-
}
|
317 |
-
}
|
318 |
-
},
|
319 |
-
|
320 |
-
/**
|
321 |
-
* Return URL for the model
|
322 |
-
*
|
323 |
-
* @returns {string}
|
324 |
-
*/
|
325 |
-
url: function() {
|
326 |
-
var id = this.get( 'ID' );
|
327 |
-
id = id || '';
|
328 |
|
329 |
-
|
330 |
-
},
|
331 |
|
332 |
defaults: {
|
333 |
-
|
334 |
name: '',
|
335 |
slug: '',
|
336 |
description: '',
|
337 |
parent: null,
|
338 |
count: 0,
|
339 |
link: '',
|
340 |
-
|
341 |
-
|
342 |
-
}
|
343 |
}
|
344 |
|
345 |
-
}
|
346 |
);
|
347 |
|
348 |
/**
|
349 |
-
* Backbone model for single
|
|
|
|
|
|
|
350 |
*/
|
351 |
-
wp.api.models.Post =
|
352 |
/** @lends Post.prototype */
|
353 |
{
|
354 |
-
idAttribute: '
|
355 |
|
356 |
-
urlRoot: WP_API_Settings.root + '/posts',
|
357 |
|
358 |
defaults: {
|
359 |
-
|
360 |
-
title: '',
|
361 |
-
status: 'draft',
|
362 |
-
type: 'post',
|
363 |
-
author: new wp.api.models.User(),
|
364 |
-
content: '',
|
365 |
-
link: '',
|
366 |
-
'parent': 0,
|
367 |
date: new Date(),
|
368 |
date_gmt: new Date(),
|
|
|
|
|
369 |
modified: new Date(),
|
370 |
modified_gmt: new Date(),
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
|
|
|
|
|
|
376 |
comment_status: 'open',
|
377 |
ping_status: 'open',
|
378 |
sticky: false,
|
379 |
-
|
380 |
-
|
381 |
-
featured_image: null,
|
382 |
-
terms: {},
|
383 |
-
post_meta: {},
|
384 |
-
meta: {
|
385 |
-
links: {}
|
386 |
-
}
|
387 |
}
|
388 |
}, TimeStampedMixin, HierarchicalMixin )
|
389 |
);
|
390 |
|
391 |
/**
|
392 |
-
* Backbone model for
|
|
|
|
|
|
|
393 |
*/
|
394 |
-
wp.api.models.Page =
|
395 |
/** @lends Page.prototype */
|
396 |
{
|
397 |
-
idAttribute: '
|
398 |
|
399 |
-
urlRoot: WP_API_Settings.root + '/pages',
|
400 |
|
401 |
defaults: {
|
402 |
-
|
403 |
-
title: '',
|
404 |
-
status: 'draft',
|
405 |
-
type: 'page',
|
406 |
-
author: new wp.api.models.User(),
|
407 |
-
content: '',
|
408 |
-
parent: 0,
|
409 |
-
link: '',
|
410 |
date: new Date(),
|
411 |
-
modified: new Date(),
|
412 |
date_gmt: new Date(),
|
|
|
|
|
|
|
413 |
modified_gmt: new Date(),
|
414 |
-
date_tz: 'Etc/UTC',
|
415 |
-
modified_tz: 'Etc/UTC',
|
416 |
-
format: 'standard',
|
417 |
-
slug: '',
|
418 |
-
guid: '',
|
419 |
-
excerpt: '',
|
420 |
-
menu_order: 0,
|
421 |
-
comment_status: 'closed',
|
422 |
-
ping_status: 'open',
|
423 |
-
sticky: false,
|
424 |
password: '',
|
425 |
-
|
426 |
-
|
427 |
-
|
|
|
|
|
|
|
|
|
428 |
featured_image: null,
|
429 |
-
|
|
|
|
|
|
|
|
|
430 |
}
|
431 |
}, TimeStampedMixin, HierarchicalMixin )
|
432 |
);
|
433 |
|
434 |
/**
|
435 |
-
* Backbone model for
|
|
|
|
|
|
|
|
|
436 |
*/
|
437 |
-
wp.api.models.
|
438 |
-
/** @lends
|
439 |
{
|
440 |
-
|
441 |
-
* Return URL for model
|
442 |
-
*
|
443 |
-
* @returns {string}
|
444 |
-
*/
|
445 |
-
url: function() {
|
446 |
-
var parent_id = this.get( 'parent' );
|
447 |
-
parent_id = parent_id || '';
|
448 |
-
|
449 |
-
var id = this.get( 'ID' );
|
450 |
-
id = id || '';
|
451 |
|
452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
453 |
},
|
454 |
|
455 |
/**
|
456 |
-
*
|
457 |
-
*
|
458 |
-
* @
|
459 |
*/
|
460 |
-
|
461 |
-
|
462 |
-
|
|
|
|
|
463 |
}
|
464 |
-
|
|
|
465 |
);
|
466 |
|
467 |
/**
|
468 |
-
* Backbone model for media
|
|
|
|
|
|
|
469 |
*/
|
470 |
-
wp.api.models.Media =
|
471 |
/** @lends Media.prototype */
|
472 |
{
|
473 |
-
idAttribute: '
|
474 |
|
475 |
-
urlRoot: WP_API_Settings.root + '/media',
|
476 |
|
477 |
defaults: {
|
478 |
-
|
479 |
-
title: '',
|
480 |
-
status: 'inherit',
|
481 |
-
type: 'attachment',
|
482 |
-
author: new wp.api.models.User(),
|
483 |
-
content: '',
|
484 |
-
parent: 0,
|
485 |
-
link: '',
|
486 |
date: new Date(),
|
|
|
|
|
|
|
487 |
modified: new Date(),
|
488 |
-
|
|
|
489 |
slug: '',
|
490 |
-
|
491 |
-
|
492 |
-
|
|
|
493 |
comment_status: 'open',
|
494 |
ping_status: 'open',
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
}
|
503 |
-
terms: [],
|
504 |
-
source: '',
|
505 |
-
is_image: true,
|
506 |
-
attachment_meta: {},
|
507 |
-
image_meta: {}
|
508 |
-
},
|
509 |
-
|
510 |
-
/**
|
511 |
-
* @class Represent a media item
|
512 |
-
* @augments Backbone.Model
|
513 |
-
* @constructs
|
514 |
-
*/
|
515 |
-
initialize: function() {
|
516 |
-
// Todo: what of the parent model is a page?
|
517 |
-
this.parentModel = wp.api.models.Post;
|
518 |
}
|
519 |
-
|
|
|
520 |
);
|
521 |
|
522 |
/**
|
523 |
-
* Backbone model for
|
|
|
|
|
|
|
524 |
*/
|
525 |
-
wp.api.models.Comment =
|
526 |
/** @lends Comment.prototype */
|
527 |
{
|
528 |
-
idAttribute: '
|
|
|
|
|
529 |
|
530 |
defaults: {
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
|
|
538 |
date: new Date(),
|
539 |
date_gmt: new Date(),
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
/**
|
547 |
-
* Return URL for model
|
548 |
-
*
|
549 |
-
* @returns {string}
|
550 |
-
*/
|
551 |
-
url: function() {
|
552 |
-
var post_id = this.get( 'post' );
|
553 |
-
post_id = post_id || '';
|
554 |
-
|
555 |
-
var id = this.get( 'ID' );
|
556 |
-
id = id || '';
|
557 |
-
|
558 |
-
return WP_API_Settings.root + '/posts/' + post_id + '/comments/' + id;
|
559 |
}
|
|
|
560 |
}, TimeStampedMixin, HierarchicalMixin )
|
561 |
);
|
562 |
|
563 |
/**
|
564 |
-
* Backbone model for single post
|
|
|
|
|
|
|
565 |
*/
|
566 |
-
wp.api.models.PostType =
|
567 |
/** @lends PostType.prototype */
|
568 |
{
|
569 |
idAttribute: 'slug',
|
570 |
|
571 |
-
urlRoot: WP_API_Settings.root + '/
|
572 |
|
573 |
defaults: {
|
574 |
slug: null,
|
575 |
name: '',
|
576 |
description: '',
|
577 |
labels: {},
|
578 |
-
|
579 |
-
searchable: false,
|
580 |
-
hierarchical: false,
|
581 |
-
meta: {
|
582 |
-
links: {}
|
583 |
-
},
|
584 |
-
taxonomies: []
|
585 |
},
|
586 |
|
587 |
/**
|
588 |
-
* Prevent model from being saved
|
589 |
*
|
590 |
-
* @returns {boolean}
|
591 |
*/
|
592 |
-
save: function
|
593 |
return false;
|
594 |
},
|
595 |
|
596 |
/**
|
597 |
-
* Prevent model from being deleted
|
598 |
*
|
599 |
-
* @returns {boolean}
|
600 |
*/
|
601 |
-
|
602 |
return false;
|
603 |
}
|
604 |
}
|
605 |
);
|
606 |
|
607 |
/**
|
608 |
-
* Backbone model for a post status
|
|
|
|
|
|
|
609 |
*/
|
610 |
-
wp.api.models.PostStatus =
|
611 |
/** @lends PostStatus.prototype */
|
612 |
{
|
613 |
idAttribute: 'slug',
|
614 |
|
615 |
-
urlRoot: WP_API_Settings.root + '/
|
616 |
|
617 |
defaults: {
|
618 |
slug: null,
|
@@ -622,44 +584,80 @@
|
|
622 |
'private': false,
|
623 |
queryable: true,
|
624 |
show_in_list: true,
|
625 |
-
|
626 |
-
links: {}
|
627 |
-
}
|
628 |
},
|
629 |
|
630 |
/**
|
631 |
-
* Prevent model from being saved
|
632 |
*
|
633 |
-
* @returns {boolean}
|
634 |
*/
|
635 |
save: function() {
|
636 |
return false;
|
637 |
},
|
638 |
|
639 |
/**
|
640 |
-
* Prevent model from being deleted
|
641 |
*
|
642 |
-
* @returns {boolean}
|
643 |
*/
|
644 |
-
|
645 |
return false;
|
646 |
}
|
647 |
}
|
648 |
);
|
649 |
|
650 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
651 |
|
652 |
/* global WP_API_Settings:false */
|
653 |
(function( wp, WP_API_Settings, Backbone, _, window, undefined ) {
|
654 |
|
655 |
'use strict';
|
656 |
|
|
|
|
|
|
|
657 |
var BaseCollection = Backbone.Collection.extend(
|
658 |
/** @lends BaseCollection.prototype */
|
659 |
{
|
660 |
|
661 |
/**
|
662 |
-
* Setup default state
|
663 |
*/
|
664 |
initialize: function() {
|
665 |
this.state = {
|
@@ -675,53 +673,52 @@
|
|
675 |
*
|
676 |
* Set nonce header before every Backbone sync.
|
677 |
*
|
678 |
-
* @param {string} method
|
679 |
-
* @param {Backbone.Model} model
|
680 |
-
* @param {{success}, *} options
|
681 |
-
* @returns {*}
|
682 |
*/
|
683 |
sync: function( method, model, options ) {
|
684 |
options = options || {};
|
685 |
-
var beforeSend = options.beforeSend
|
|
|
686 |
|
687 |
-
if ( typeof WP_API_Settings.nonce
|
688 |
options.beforeSend = function( xhr ) {
|
689 |
xhr.setRequestHeader( 'X-WP-Nonce', WP_API_Settings.nonce );
|
690 |
|
691 |
if ( beforeSend ) {
|
692 |
-
return beforeSend.apply(
|
693 |
}
|
694 |
};
|
695 |
}
|
696 |
|
697 |
if ( 'read' === method ) {
|
698 |
-
var SELF = this;
|
699 |
-
|
700 |
if ( options.data ) {
|
701 |
-
|
702 |
|
703 |
-
delete
|
704 |
} else {
|
705 |
-
|
706 |
}
|
707 |
|
708 |
-
if ( typeof options.data.page
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
} else {
|
713 |
-
|
714 |
}
|
715 |
|
716 |
var success = options.success;
|
717 |
options.success = function( data, textStatus, request ) {
|
718 |
-
|
719 |
-
|
720 |
|
721 |
-
if (
|
722 |
-
|
723 |
} else {
|
724 |
-
|
725 |
}
|
726 |
|
727 |
if ( success ) {
|
@@ -734,10 +731,10 @@
|
|
734 |
},
|
735 |
|
736 |
/**
|
737 |
-
* Fetches the next page of objects if a new page exists
|
738 |
*
|
739 |
-
* @param {data: {page}} options
|
740 |
-
* @returns {*}
|
741 |
*/
|
742 |
more: function( options ) {
|
743 |
options = options || {};
|
@@ -745,7 +742,7 @@
|
|
745 |
|
746 |
_.extend( options.data, this.state.data );
|
747 |
|
748 |
-
if ( typeof options.data.page
|
749 |
if ( ! this.hasMore() ) {
|
750 |
return false;
|
751 |
}
|
@@ -761,9 +758,9 @@
|
|
761 |
},
|
762 |
|
763 |
/**
|
764 |
-
* Returns true if there are more pages of objects available
|
765 |
*
|
766 |
-
* @returns null|boolean
|
767 |
*/
|
768 |
hasMore: function() {
|
769 |
if ( this.state.totalPages === null ||
|
@@ -778,180 +775,170 @@
|
|
778 |
);
|
779 |
|
780 |
/**
|
781 |
-
* Backbone collection for posts
|
782 |
*/
|
783 |
wp.api.collections.Posts = BaseCollection.extend(
|
784 |
/** @lends Posts.prototype */
|
785 |
{
|
786 |
-
url: WP_API_Settings.root + '/posts',
|
787 |
|
788 |
model: wp.api.models.Post
|
789 |
}
|
790 |
);
|
791 |
|
792 |
/**
|
793 |
-
* Backbone collection for pages
|
794 |
*/
|
795 |
wp.api.collections.Pages = BaseCollection.extend(
|
796 |
/** @lends Pages.prototype */
|
797 |
{
|
798 |
-
url: WP_API_Settings.root + '/pages',
|
799 |
|
800 |
model: wp.api.models.Page
|
801 |
}
|
802 |
);
|
803 |
|
804 |
/**
|
805 |
-
* Backbone users collection
|
806 |
*/
|
807 |
wp.api.collections.Users = BaseCollection.extend(
|
808 |
/** @lends Users.prototype */
|
809 |
{
|
810 |
-
url: WP_API_Settings.root + '/users',
|
811 |
|
812 |
model: wp.api.models.User
|
813 |
}
|
814 |
);
|
815 |
|
816 |
/**
|
817 |
-
* Backbone post statuses collection
|
818 |
*/
|
819 |
wp.api.collections.PostStatuses = BaseCollection.extend(
|
820 |
/** @lends PostStatuses.prototype */
|
821 |
{
|
822 |
-
url: WP_API_Settings.root + '/
|
|
|
|
|
823 |
|
824 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
825 |
|
|
|
|
|
826 |
}
|
827 |
);
|
828 |
|
829 |
/**
|
830 |
-
* Backbone media library collection
|
831 |
*/
|
832 |
wp.api.collections.MediaLibrary = BaseCollection.extend(
|
833 |
/** @lends MediaLibrary.prototype */
|
834 |
{
|
835 |
-
url: WP_API_Settings.root + '/media',
|
836 |
|
837 |
model: wp.api.models.Media
|
838 |
}
|
839 |
);
|
840 |
|
841 |
/**
|
842 |
-
* Backbone taxonomy collection
|
843 |
*/
|
844 |
wp.api.collections.Taxonomies = BaseCollection.extend(
|
845 |
/** @lends Taxonomies.prototype */
|
846 |
{
|
847 |
model: wp.api.models.Taxonomy,
|
848 |
|
849 |
-
url: WP_API_Settings.root + '/taxonomies'
|
850 |
}
|
851 |
);
|
852 |
|
853 |
/**
|
854 |
-
* Backbone comment collection
|
855 |
*/
|
856 |
wp.api.collections.Comments = BaseCollection.extend(
|
857 |
/** @lends Comments.prototype */
|
858 |
{
|
859 |
model: wp.api.models.Comment,
|
860 |
|
861 |
-
post: null,
|
862 |
-
|
863 |
-
/**
|
864 |
-
* @class Represent an array of comments
|
865 |
-
* @augments Backbone.Collection
|
866 |
-
* @constructs
|
867 |
-
*/
|
868 |
-
initialize: function( models, options ) {
|
869 |
-
BaseCollection.prototype.initialize.apply( this, arguments );
|
870 |
-
|
871 |
-
if ( options && options.post ) {
|
872 |
-
this.post = options.post;
|
873 |
-
}
|
874 |
-
},
|
875 |
-
|
876 |
/**
|
877 |
-
* Return URL for collection
|
878 |
*
|
879 |
-
* @returns {string}
|
880 |
*/
|
881 |
-
url:
|
882 |
-
return WP_API_Settings.root + '/posts/' + this.post + '/comments';
|
883 |
-
}
|
884 |
}
|
885 |
);
|
886 |
|
887 |
/**
|
888 |
-
* Backbone post type collection
|
889 |
*/
|
890 |
wp.api.collections.PostTypes = BaseCollection.extend(
|
891 |
/** @lends PostTypes.prototype */
|
892 |
{
|
893 |
model: wp.api.models.PostType,
|
894 |
|
895 |
-
url: WP_API_Settings.root + '/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
896 |
}
|
897 |
);
|
898 |
|
899 |
/**
|
900 |
-
* Backbone terms collection
|
|
|
|
|
901 |
*/
|
902 |
wp.api.collections.Terms = BaseCollection.extend(
|
903 |
/** @lends Terms.prototype */
|
904 |
{
|
905 |
model: wp.api.models.Term,
|
906 |
|
907 |
-
type: 'post',
|
908 |
-
|
909 |
taxonomy: 'category',
|
910 |
|
911 |
/**
|
912 |
-
* @class Represent an array of terms
|
913 |
-
* @augments Backbone.Collection
|
914 |
* @constructs
|
915 |
*/
|
916 |
initialize: function( models, options ) {
|
917 |
-
|
918 |
-
|
919 |
-
if ( typeof options !== 'undefined' ) {
|
920 |
-
if ( options.type ) {
|
921 |
-
this.type = options.type;
|
922 |
-
}
|
923 |
-
|
924 |
-
if ( options.taxonomy ) {
|
925 |
-
this.taxonomy = options.taxonomy;
|
926 |
-
}
|
927 |
}
|
928 |
|
929 |
-
|
930 |
-
},
|
931 |
-
|
932 |
-
/**
|
933 |
-
* We need to set the type and taxonomy for each model
|
934 |
-
*
|
935 |
-
* @param {Backbone.model} model
|
936 |
-
*/
|
937 |
-
addModel: function( model ) {
|
938 |
-
model.type = this.type;
|
939 |
-
model.taxonomy = this.taxonomy;
|
940 |
},
|
941 |
|
942 |
/**
|
943 |
-
* Return URL for collection
|
944 |
*
|
945 |
-
* @returns {string}
|
946 |
*/
|
947 |
url: function() {
|
948 |
-
return WP_API_Settings.root + '/
|
949 |
}
|
950 |
}
|
951 |
);
|
952 |
|
953 |
/**
|
954 |
-
* Backbone revisions collection
|
|
|
|
|
955 |
*/
|
956 |
wp.api.collections.Revisions = BaseCollection.extend(
|
957 |
/** @lends Revisions.prototype */
|
@@ -961,8 +948,8 @@
|
|
961 |
parent: null,
|
962 |
|
963 |
/**
|
964 |
-
* @class Represent an array of revisions
|
965 |
-
* @augments Backbone.Collection
|
966 |
* @constructs
|
967 |
*/
|
968 |
initialize: function( models, options ) {
|
@@ -974,14 +961,22 @@
|
|
974 |
},
|
975 |
|
976 |
/**
|
977 |
-
* return URL for collection
|
978 |
*
|
979 |
-
* @returns {string}
|
980 |
*/
|
981 |
url: function() {
|
982 |
-
return WP_API_Settings.root + '/posts/' + this.parent + '/revisions';
|
983 |
}
|
984 |
}
|
985 |
);
|
986 |
|
987 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
26 |
+
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
|
27 |
+
*/
|
28 |
if ( ! Date.prototype.toISOString ) {
|
29 |
var pad = function( number ) {
|
30 |
var r = String( number );
|
31 |
if ( r.length === 1 ) {
|
32 |
r = '0' + r;
|
33 |
}
|
34 |
+
|
35 |
return r;
|
36 |
};
|
37 |
|
42 |
'T' + pad( this.getUTCHours() ) +
|
43 |
':' + pad( this.getUTCMinutes() ) +
|
44 |
':' + pad( this.getUTCSeconds() ) +
|
45 |
+
'.' + String( ( this.getUTCMilliseconds() / 1000 ).toFixed( 3 ) ).slice( 2, 5 ) +
|
46 |
'Z';
|
47 |
};
|
48 |
}
|
49 |
|
50 |
+
/**
|
51 |
+
* Parse date into ISO8601 format.
|
52 |
+
*
|
53 |
+
* @param {Date} date.
|
54 |
+
*/
|
55 |
+
wp.api.utils.parseISO8601 = function( date ) {
|
56 |
+
var timestamp, struct, i, k,
|
57 |
+
minutesOffset = 0,
|
58 |
numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
|
59 |
|
60 |
+
// ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
|
61 |
+
// before falling back to any implementation-specific date parsing, so that’s what we do, even if native
|
62 |
+
// implementations could be faster.
|
63 |
+
// 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
|
64 |
+
if ( ( struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec( date ) ) ) {
|
65 |
+
// Avoid NaN timestamps caused by “undefined” values being passed to Date.UTC.
|
66 |
+
for ( i = 0; ( k = numericKeys[i] ); ++i ) {
|
67 |
+
struct[k] = +struct[k] || 0;
|
68 |
+
}
|
69 |
|
70 |
+
// Allow undefined days and months.
|
71 |
+
struct[2] = ( +struct[2] || 1 ) - 1;
|
72 |
+
struct[3] = +struct[3] || 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
+
if ( struct[8] !== 'Z' && struct[9] !== undefined ) {
|
75 |
+
minutesOffset = struct[10] * 60 + struct[11];
|
76 |
|
77 |
+
if ( struct[9] === '+' ) {
|
78 |
+
minutesOffset = 0 - minutesOffset;
|
|
|
79 |
}
|
|
|
|
|
|
|
|
|
|
|
80 |
}
|
81 |
|
82 |
+
timestamp = Date.UTC( struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7] );
|
83 |
+
} else {
|
84 |
+
timestamp = Date.parse ? Date.parse( date ) : NaN;
|
85 |
+
}
|
86 |
|
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 |
*
|
102 |
+
* @type {string[]}.
|
103 |
*/
|
104 |
var parseable_dates = [ 'date', 'modified', 'date_gmt', 'modified_gmt' ];
|
105 |
|
106 |
/**
|
107 |
+
* Mixin for all content that is time stamped.
|
108 |
*
|
109 |
+
* @type {{toJSON: toJSON, parse: parse}}.
|
110 |
*/
|
111 |
var TimeStampedMixin = {
|
112 |
/**
|
113 |
+
* Serialize the entity pre-sync.
|
114 |
*
|
115 |
+
* @returns {*}.
|
116 |
*/
|
117 |
toJSON: function() {
|
118 |
var attributes = _.clone( this.attributes );
|
119 |
|
120 |
+
// Serialize Date objects back into 8601 strings.
|
121 |
+
_.each( parseable_dates, function( key ) {
|
122 |
if ( key in attributes ) {
|
123 |
attributes[key] = attributes[key].toISOString();
|
124 |
}
|
128 |
},
|
129 |
|
130 |
/**
|
131 |
+
* Unserialize the fetched response.
|
132 |
*
|
133 |
+
* @param {*} response.
|
134 |
+
* @returns {*}.
|
135 |
*/
|
136 |
parse: function( response ) {
|
137 |
+
|
138 |
+
// Parse dates into native Date objects.
|
139 |
_.each( parseable_dates, function ( key ) {
|
140 |
if ( ! ( key in response ) ) {
|
141 |
return;
|
145 |
response[key] = new Date( timestamp );
|
146 |
});
|
147 |
|
148 |
+
// Parse the author into a User object.
|
149 |
+
if ( 'undefined' !== typeof response.author ) {
|
150 |
response.author = new wp.api.models.User( response.author );
|
151 |
}
|
152 |
|
155 |
};
|
156 |
|
157 |
/**
|
158 |
+
* Mixin for all hierarchical content types such as posts.
|
159 |
*
|
160 |
+
* @type {{parent: parent}}.
|
161 |
*/
|
162 |
var HierarchicalMixin = {
|
163 |
/**
|
164 |
+
* Get parent object.
|
165 |
*
|
166 |
* @returns {Backbone.Model}
|
167 |
*/
|
169 |
|
170 |
var object, parent = this.get( 'parent' );
|
171 |
|
172 |
+
// Return null if we don't have a parent.
|
173 |
if ( parent === 0 ) {
|
174 |
return null;
|
175 |
}
|
176 |
|
177 |
var parentModel = this;
|
178 |
|
179 |
+
if ( 'undefined' !== typeof this.parentModel ) {
|
180 |
/**
|
181 |
* Probably a better way to do this. Perhaps grab a cached version of the
|
182 |
* instantiated model?
|
188 |
if ( parentModel.collection ) {
|
189 |
return parentModel.collection.get( parent );
|
190 |
} else {
|
191 |
+
|
192 |
+
// Otherwise, get the object directly.
|
193 |
object = new parentModel.constructor( {
|
194 |
+
id: parent
|
195 |
});
|
196 |
|
197 |
+
// Note that this acts asynchronously.
|
198 |
object.fetch();
|
199 |
+
|
200 |
return object;
|
201 |
}
|
202 |
}
|
203 |
};
|
204 |
|
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.
|
213 |
*
|
214 |
+
* @param {string} method.
|
215 |
+
* @param {Backbone.Model} model.
|
216 |
+
* @param {{beforeSend}, *} options.
|
217 |
+
* @returns {*}.
|
218 |
*/
|
219 |
sync: function( method, model, options ) {
|
220 |
options = options || {};
|
221 |
|
222 |
+
if ( 'undefined' !== typeof WP_API_Settings.nonce ) {
|
223 |
var beforeSend = options.beforeSend;
|
224 |
|
225 |
options.beforeSend = function( xhr ) {
|
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',
|
250 |
|
251 |
+
urlRoot: WP_API_Settings.root + 'wp/v2/users',
|
252 |
|
253 |
defaults: {
|
254 |
+
id: 'me',
|
255 |
+
avatar_url: {},
|
256 |
+
capabilities: {},
|
257 |
+
description: '',
|
258 |
email: '',
|
259 |
+
extra_capabilities: {},
|
|
|
260 |
first_name: '',
|
261 |
last_name: '',
|
262 |
+
link: '',
|
263 |
+
name: '',
|
264 |
nickname: '',
|
265 |
+
registered_date: new Date(),
|
266 |
+
roles: [],
|
267 |
slug: '',
|
268 |
+
url: '',
|
269 |
+
username: '',
|
270 |
+
_links: {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
}
|
272 |
}
|
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',
|
285 |
|
286 |
+
urlRoot: WP_API_Settings.root + 'wp/v2/taxonomies',
|
287 |
|
288 |
defaults: {
|
289 |
name: '',
|
290 |
slug: null,
|
291 |
+
description: '',
|
292 |
labels: {},
|
293 |
+
types: [],
|
294 |
show_cloud: false,
|
295 |
+
hierarchical: false
|
|
|
|
|
|
|
296 |
}
|
297 |
}
|
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,
|
315 |
name: '',
|
316 |
slug: '',
|
317 |
description: '',
|
318 |
parent: null,
|
319 |
count: 0,
|
320 |
link: '',
|
321 |
+
taxonomy: '',
|
322 |
+
_links: {}
|
|
|
323 |
}
|
324 |
|
325 |
+
}
|
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',
|
338 |
|
339 |
+
urlRoot: WP_API_Settings.root + 'wp/v2/posts',
|
340 |
|
341 |
defaults: {
|
342 |
+
id: null,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
343 |
date: new Date(),
|
344 |
date_gmt: new Date(),
|
345 |
+
guid: {},
|
346 |
+
link: '',
|
347 |
modified: new Date(),
|
348 |
modified_gmt: new Date(),
|
349 |
+
password: '',
|
350 |
+
status: 'draft',
|
351 |
+
type: 'post',
|
352 |
+
title: {},
|
353 |
+
content: {},
|
354 |
+
author: null,
|
355 |
+
excerpt: {},
|
356 |
+
featured_image: null,
|
357 |
comment_status: 'open',
|
358 |
ping_status: 'open',
|
359 |
sticky: false,
|
360 |
+
format: 'standard',
|
361 |
+
_links: {}
|
|
|
|
|
|
|
|
|
|
|
|
|
362 |
}
|
363 |
}, TimeStampedMixin, HierarchicalMixin )
|
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',
|
376 |
|
377 |
+
urlRoot: WP_API_Settings.root + 'wp/v2/pages',
|
378 |
|
379 |
defaults: {
|
380 |
+
id: null,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
381 |
date: new Date(),
|
|
|
382 |
date_gmt: new Date(),
|
383 |
+
guid: {},
|
384 |
+
link: '',
|
385 |
+
modified: new Date(),
|
386 |
modified_gmt: new Date(),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
387 |
password: '',
|
388 |
+
slug: '',
|
389 |
+
status: 'draft',
|
390 |
+
type: 'page',
|
391 |
+
title: {},
|
392 |
+
content: {},
|
393 |
+
author: null,
|
394 |
+
excerpt: {},
|
395 |
featured_image: null,
|
396 |
+
comment_status: 'closed',
|
397 |
+
ping_status: 'closed',
|
398 |
+
menu_order: null,
|
399 |
+
template: '',
|
400 |
+
_links: {}
|
401 |
}
|
402 |
}, TimeStampedMixin, HierarchicalMixin )
|
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,
|
420 |
+
date: new Date(),
|
421 |
+
date_gmt: new Date(),
|
422 |
+
guid: {},
|
423 |
+
modified: new Date(),
|
424 |
+
modified_gmt: new Date(),
|
425 |
+
parent: 0,
|
426 |
+
slug: '',
|
427 |
+
title: {},
|
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',
|
458 |
|
459 |
+
urlRoot: WP_API_Settings.root + 'wp/v2/media',
|
460 |
|
461 |
defaults: {
|
462 |
+
id: null,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
date: new Date(),
|
464 |
+
date_gmt: new Date(),
|
465 |
+
guid: {},
|
466 |
+
link: '',
|
467 |
modified: new Date(),
|
468 |
+
modified_gmt: new Date(),
|
469 |
+
password: '',
|
470 |
slug: '',
|
471 |
+
status: 'draft',
|
472 |
+
type: 'attachment',
|
473 |
+
title: {},
|
474 |
+
author: null,
|
475 |
comment_status: 'open',
|
476 |
ping_status: 'open',
|
477 |
+
alt_text: '',
|
478 |
+
caption: '',
|
479 |
+
description: '',
|
480 |
+
media_type: '',
|
481 |
+
media_details: {},
|
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,
|
506 |
+
author_email: '',
|
507 |
+
author_ip: '',
|
508 |
+
author_name: '',
|
509 |
+
author_url: '',
|
510 |
+
author_user_agent: '',
|
511 |
+
content: {},
|
512 |
date: new Date(),
|
513 |
date_gmt: new Date(),
|
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,
|
540 |
name: '',
|
541 |
description: '',
|
542 |
labels: {},
|
543 |
+
hierarchical: false
|
|
|
|
|
|
|
|
|
|
|
|
|
544 |
},
|
545 |
|
546 |
/**
|
547 |
+
* Prevent model from being saved.
|
548 |
*
|
549 |
+
* @returns {boolean}.
|
550 |
*/
|
551 |
+
save: function() {
|
552 |
return false;
|
553 |
},
|
554 |
|
555 |
/**
|
556 |
+
* Prevent model from being deleted.
|
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,
|
584 |
'private': false,
|
585 |
queryable: true,
|
586 |
show_in_list: true,
|
587 |
+
_links: {}
|
|
|
|
|
588 |
},
|
589 |
|
590 |
/**
|
591 |
+
* Prevent model from being saved.
|
592 |
*
|
593 |
+
* @returns {boolean}.
|
594 |
*/
|
595 |
save: function() {
|
596 |
return false;
|
597 |
},
|
598 |
|
599 |
/**
|
600 |
+
* Prevent model from being deleted.
|
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 |
+
*/
|
655 |
var BaseCollection = Backbone.Collection.extend(
|
656 |
/** @lends BaseCollection.prototype */
|
657 |
{
|
658 |
|
659 |
/**
|
660 |
+
* Setup default state.
|
661 |
*/
|
662 |
initialize: function() {
|
663 |
this.state = {
|
673 |
*
|
674 |
* Set nonce header before every Backbone sync.
|
675 |
*
|
676 |
+
* @param {string} method.
|
677 |
+
* @param {Backbone.Model} model.
|
678 |
+
* @param {{success}, *} options.
|
679 |
+
* @returns {*}.
|
680 |
*/
|
681 |
sync: function( method, model, options ) {
|
682 |
options = options || {};
|
683 |
+
var beforeSend = options.beforeSend,
|
684 |
+
self = this;
|
685 |
|
686 |
+
if ( 'undefined' !== typeof WP_API_Settings.nonce ) {
|
687 |
options.beforeSend = function( xhr ) {
|
688 |
xhr.setRequestHeader( 'X-WP-Nonce', WP_API_Settings.nonce );
|
689 |
|
690 |
if ( beforeSend ) {
|
691 |
+
return beforeSend.apply( self, arguments );
|
692 |
}
|
693 |
};
|
694 |
}
|
695 |
|
696 |
if ( 'read' === method ) {
|
|
|
|
|
697 |
if ( options.data ) {
|
698 |
+
self.state.data = _.clone( options.data );
|
699 |
|
700 |
+
delete self.state.data.page;
|
701 |
} else {
|
702 |
+
self.state.data = options.data = {};
|
703 |
}
|
704 |
|
705 |
+
if ( 'undefined' === typeof options.data.page ) {
|
706 |
+
self.state.currentPage = null;
|
707 |
+
self.state.totalPages = null;
|
708 |
+
self.state.totalObjects = null;
|
709 |
} else {
|
710 |
+
self.state.currentPage = options.data.page - 1;
|
711 |
}
|
712 |
|
713 |
var success = options.success;
|
714 |
options.success = function( data, textStatus, request ) {
|
715 |
+
self.state.totalPages = parseInt( request.getResponseHeader( 'x-wp-totalpages' ), 10 );
|
716 |
+
self.state.totalObjects = parseInt( request.getResponseHeader( 'x-wp-total' ), 10 );
|
717 |
|
718 |
+
if ( self.state.currentPage === null ) {
|
719 |
+
self.state.currentPage = 1;
|
720 |
} else {
|
721 |
+
self.state.currentPage++;
|
722 |
}
|
723 |
|
724 |
if ( success ) {
|
731 |
},
|
732 |
|
733 |
/**
|
734 |
+
* Fetches the next page of objects if a new page exists.
|
735 |
*
|
736 |
+
* @param {data: {page}} options.
|
737 |
+
* @returns {*}.
|
738 |
*/
|
739 |
more: function( options ) {
|
740 |
options = options || {};
|
742 |
|
743 |
_.extend( options.data, this.state.data );
|
744 |
|
745 |
+
if ( 'undefined' === typeof options.data.page ) {
|
746 |
if ( ! this.hasMore() ) {
|
747 |
return false;
|
748 |
}
|
758 |
},
|
759 |
|
760 |
/**
|
761 |
+
* Returns true if there are more pages of objects available.
|
762 |
*
|
763 |
+
* @returns null|boolean.
|
764 |
*/
|
765 |
hasMore: function() {
|
766 |
if ( this.state.totalPages === null ||
|
775 |
);
|
776 |
|
777 |
/**
|
778 |
+
* Backbone collection for posts.
|
779 |
*/
|
780 |
wp.api.collections.Posts = BaseCollection.extend(
|
781 |
/** @lends Posts.prototype */
|
782 |
{
|
783 |
+
url: WP_API_Settings.root + 'wp/v2/posts',
|
784 |
|
785 |
model: wp.api.models.Post
|
786 |
}
|
787 |
);
|
788 |
|
789 |
/**
|
790 |
+
* Backbone collection for pages.
|
791 |
*/
|
792 |
wp.api.collections.Pages = BaseCollection.extend(
|
793 |
/** @lends Pages.prototype */
|
794 |
{
|
795 |
+
url: WP_API_Settings.root + 'wp/v2/pages',
|
796 |
|
797 |
model: wp.api.models.Page
|
798 |
}
|
799 |
);
|
800 |
|
801 |
/**
|
802 |
+
* Backbone users collection.
|
803 |
*/
|
804 |
wp.api.collections.Users = BaseCollection.extend(
|
805 |
/** @lends Users.prototype */
|
806 |
{
|
807 |
+
url: WP_API_Settings.root + 'wp/v2/users',
|
808 |
|
809 |
model: wp.api.models.User
|
810 |
}
|
811 |
);
|
812 |
|
813 |
/**
|
814 |
+
* Backbone post statuses collection.
|
815 |
*/
|
816 |
wp.api.collections.PostStatuses = BaseCollection.extend(
|
817 |
/** @lends PostStatuses.prototype */
|
818 |
{
|
819 |
+
url: WP_API_Settings.root + 'wp/v2/statuses',
|
820 |
+
|
821 |
+
model: wp.api.models.PostStatus,
|
822 |
|
823 |
+
parse: function( response ) {
|
824 |
+
var responseArray = [];
|
825 |
+
|
826 |
+
for ( var property in response ) {
|
827 |
+
if ( response.hasOwnProperty( property ) ) {
|
828 |
+
responseArray.push( response[property] );
|
829 |
+
}
|
830 |
+
}
|
831 |
|
832 |
+
return this.constructor.__super__.parse.call( this, responseArray );
|
833 |
+
}
|
834 |
}
|
835 |
);
|
836 |
|
837 |
/**
|
838 |
+
* Backbone media library collection.
|
839 |
*/
|
840 |
wp.api.collections.MediaLibrary = BaseCollection.extend(
|
841 |
/** @lends MediaLibrary.prototype */
|
842 |
{
|
843 |
+
url: WP_API_Settings.root + 'wp/v2/media',
|
844 |
|
845 |
model: wp.api.models.Media
|
846 |
}
|
847 |
);
|
848 |
|
849 |
/**
|
850 |
+
* Backbone taxonomy collection.
|
851 |
*/
|
852 |
wp.api.collections.Taxonomies = BaseCollection.extend(
|
853 |
/** @lends Taxonomies.prototype */
|
854 |
{
|
855 |
model: wp.api.models.Taxonomy,
|
856 |
|
857 |
+
url: WP_API_Settings.root + 'wp/v2/taxonomies'
|
858 |
}
|
859 |
);
|
860 |
|
861 |
/**
|
862 |
+
* Backbone comment collection.
|
863 |
*/
|
864 |
wp.api.collections.Comments = BaseCollection.extend(
|
865 |
/** @lends Comments.prototype */
|
866 |
{
|
867 |
model: wp.api.models.Comment,
|
868 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
869 |
/**
|
870 |
+
* Return URL for collection.
|
871 |
*
|
872 |
+
* @returns {string}.
|
873 |
*/
|
874 |
+
url: WP_API_Settings.root + 'wp/v2/comments'
|
|
|
|
|
875 |
}
|
876 |
);
|
877 |
|
878 |
/**
|
879 |
+
* Backbone post type collection.
|
880 |
*/
|
881 |
wp.api.collections.PostTypes = BaseCollection.extend(
|
882 |
/** @lends PostTypes.prototype */
|
883 |
{
|
884 |
model: wp.api.models.PostType,
|
885 |
|
886 |
+
url: WP_API_Settings.root + 'wp/v2/types',
|
887 |
+
|
888 |
+
parse: function( response ) {
|
889 |
+
var responseArray = [];
|
890 |
+
|
891 |
+
for ( var property in response ) {
|
892 |
+
if ( response.hasOwnProperty( property ) ) {
|
893 |
+
responseArray.push( response[property] );
|
894 |
+
}
|
895 |
+
}
|
896 |
+
|
897 |
+
return this.constructor.__super__.parse.call( this, responseArray );
|
898 |
+
}
|
899 |
}
|
900 |
);
|
901 |
|
902 |
/**
|
903 |
+
* Backbone terms collection.
|
904 |
+
*
|
905 |
+
* Usage: new wp.api.collections.Terms( {}, { taxonomy: 'taxonomy-slug' } )
|
906 |
*/
|
907 |
wp.api.collections.Terms = BaseCollection.extend(
|
908 |
/** @lends Terms.prototype */
|
909 |
{
|
910 |
model: wp.api.models.Term,
|
911 |
|
|
|
|
|
912 |
taxonomy: 'category',
|
913 |
|
914 |
/**
|
915 |
+
* @class Represent an array of terms.
|
916 |
+
* @augments Backbone.Collection.
|
917 |
* @constructs
|
918 |
*/
|
919 |
initialize: function( models, options ) {
|
920 |
+
if ( 'undefined' !== typeof options && options.taxonomy ) {
|
921 |
+
this.taxonomy = options.taxonomy;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
922 |
}
|
923 |
|
924 |
+
BaseCollection.prototype.initialize.apply( this, arguments );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
925 |
},
|
926 |
|
927 |
/**
|
928 |
+
* Return URL for collection.
|
929 |
*
|
930 |
+
* @returns {string}.
|
931 |
*/
|
932 |
url: function() {
|
933 |
+
return WP_API_Settings.root + 'wp/v2/terms/' + this.taxonomy;
|
934 |
}
|
935 |
}
|
936 |
);
|
937 |
|
938 |
/**
|
939 |
+
* Backbone revisions collection.
|
940 |
+
*
|
941 |
+
* Usage: new wp.api.collections.Revisions( {}, { parent: POST_ID } ).
|
942 |
*/
|
943 |
wp.api.collections.Revisions = BaseCollection.extend(
|
944 |
/** @lends Revisions.prototype */
|
948 |
parent: null,
|
949 |
|
950 |
/**
|
951 |
+
* @class Represent an array of revisions.
|
952 |
+
* @augments Backbone.Collection.
|
953 |
* @constructs
|
954 |
*/
|
955 |
initialize: function( models, options ) {
|
961 |
},
|
962 |
|
963 |
/**
|
964 |
+
* return URL for collection.
|
965 |
*
|
966 |
+
* @returns {string}.
|
967 |
*/
|
968 |
url: function() {
|
969 |
+
return WP_API_Settings.root + 'wp/v2/posts/' + this.parent + '/revisions';
|
970 |
}
|
971 |
}
|
972 |
);
|
973 |
|
974 |
+
/**
|
975 |
+
* Todo: Handle schema endpoints.
|
976 |
+
*/
|
977 |
+
|
978 |
+
/**
|
979 |
+
* Todo: Handle post meta.
|
980 |
+
*/
|
981 |
+
|
982 |
+
})( wp, WP_API_Settings, Backbone, _, window );
|