|
1 <?php |
|
2 /** |
|
3 * REST API: WP_REST_Template_Revisions_Controller class |
|
4 * |
|
5 * @package WordPress |
|
6 * @subpackage REST_API |
|
7 * @since 6.4.0 |
|
8 */ |
|
9 |
|
10 /** |
|
11 * Core class used to access template revisions via the REST API. |
|
12 * |
|
13 * @since 6.4.0 |
|
14 * |
|
15 * @see WP_REST_Controller |
|
16 */ |
|
17 class WP_REST_Template_Revisions_Controller extends WP_REST_Revisions_Controller { |
|
18 /** |
|
19 * Parent post type. |
|
20 * |
|
21 * @since 6.4.0 |
|
22 * @var string |
|
23 */ |
|
24 private $parent_post_type; |
|
25 |
|
26 /** |
|
27 * Parent controller. |
|
28 * |
|
29 * @since 6.4.0 |
|
30 * @var WP_REST_Controller |
|
31 */ |
|
32 private $parent_controller; |
|
33 |
|
34 /** |
|
35 * The base of the parent controller's route. |
|
36 * |
|
37 * @since 6.4.0 |
|
38 * @var string |
|
39 */ |
|
40 private $parent_base; |
|
41 |
|
42 /** |
|
43 * Constructor. |
|
44 * |
|
45 * @since 6.4.0 |
|
46 * |
|
47 * @param string $parent_post_type Post type of the parent. |
|
48 */ |
|
49 public function __construct( $parent_post_type ) { |
|
50 parent::__construct( $parent_post_type ); |
|
51 $this->parent_post_type = $parent_post_type; |
|
52 $post_type_object = get_post_type_object( $parent_post_type ); |
|
53 $parent_controller = $post_type_object->get_rest_controller(); |
|
54 |
|
55 if ( ! $parent_controller ) { |
|
56 $parent_controller = new WP_REST_Templates_Controller( $parent_post_type ); |
|
57 } |
|
58 |
|
59 $this->parent_controller = $parent_controller; |
|
60 $this->rest_base = 'revisions'; |
|
61 $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; |
|
62 $this->namespace = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2'; |
|
63 } |
|
64 |
|
65 /** |
|
66 * Registers the routes for revisions based on post types supporting revisions. |
|
67 * |
|
68 * @since 6.4.0 |
|
69 * |
|
70 * @see register_rest_route() |
|
71 */ |
|
72 public function register_routes() { |
|
73 |
|
74 register_rest_route( |
|
75 $this->namespace, |
|
76 sprintf( |
|
77 '/%s/(?P<parent>%s%s)/%s', |
|
78 $this->parent_base, |
|
79 /* |
|
80 * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`. |
|
81 * Excludes invalid directory name characters: `/:<>*?"|`. |
|
82 */ |
|
83 '([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)', |
|
84 // Matches the template name. |
|
85 '[\/\w%-]+', |
|
86 $this->rest_base |
|
87 ), |
|
88 array( |
|
89 'args' => array( |
|
90 'parent' => array( |
|
91 'description' => __( 'The id of a template' ), |
|
92 'type' => 'string', |
|
93 'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ), |
|
94 ), |
|
95 ), |
|
96 array( |
|
97 'methods' => WP_REST_Server::READABLE, |
|
98 'callback' => array( $this, 'get_items' ), |
|
99 'permission_callback' => array( $this, 'get_items_permissions_check' ), |
|
100 'args' => $this->get_collection_params(), |
|
101 ), |
|
102 'schema' => array( $this, 'get_public_item_schema' ), |
|
103 ) |
|
104 ); |
|
105 |
|
106 register_rest_route( |
|
107 $this->namespace, |
|
108 sprintf( |
|
109 '/%s/(?P<parent>%s%s)/%s/%s', |
|
110 $this->parent_base, |
|
111 /* |
|
112 * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`. |
|
113 * Excludes invalid directory name characters: `/:<>*?"|`. |
|
114 */ |
|
115 '([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)', |
|
116 // Matches the template name. |
|
117 '[\/\w%-]+', |
|
118 $this->rest_base, |
|
119 '(?P<id>[\d]+)' |
|
120 ), |
|
121 array( |
|
122 'args' => array( |
|
123 'parent' => array( |
|
124 'description' => __( 'The id of a template' ), |
|
125 'type' => 'string', |
|
126 'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ), |
|
127 ), |
|
128 'id' => array( |
|
129 'description' => __( 'Unique identifier for the revision.' ), |
|
130 'type' => 'integer', |
|
131 ), |
|
132 ), |
|
133 array( |
|
134 'methods' => WP_REST_Server::READABLE, |
|
135 'callback' => array( $this, 'get_item' ), |
|
136 'permission_callback' => array( $this, 'get_item_permissions_check' ), |
|
137 'args' => array( |
|
138 'context' => $this->get_context_param( array( 'default' => 'view' ) ), |
|
139 ), |
|
140 ), |
|
141 array( |
|
142 'methods' => WP_REST_Server::DELETABLE, |
|
143 'callback' => array( $this, 'delete_item' ), |
|
144 'permission_callback' => array( $this, 'delete_item_permissions_check' ), |
|
145 'args' => array( |
|
146 'force' => array( |
|
147 'type' => 'boolean', |
|
148 'default' => false, |
|
149 'description' => __( 'Required to be true, as revisions do not support trashing.' ), |
|
150 ), |
|
151 ), |
|
152 ), |
|
153 'schema' => array( $this, 'get_public_item_schema' ), |
|
154 ) |
|
155 ); |
|
156 } |
|
157 |
|
158 /** |
|
159 * Gets the parent post, if the template ID is valid. |
|
160 * |
|
161 * @since 6.4.0 |
|
162 * |
|
163 * @param string $parent_template_id Supplied ID. |
|
164 * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise. |
|
165 */ |
|
166 protected function get_parent( $parent_template_id ) { |
|
167 $template = get_block_template( $parent_template_id, $this->parent_post_type ); |
|
168 |
|
169 if ( ! $template ) { |
|
170 return new WP_Error( |
|
171 'rest_post_invalid_parent', |
|
172 __( 'Invalid template parent ID.' ), |
|
173 array( 'status' => 404 ) |
|
174 ); |
|
175 } |
|
176 |
|
177 return get_post( $template->wp_id ); |
|
178 } |
|
179 |
|
180 /** |
|
181 * Prepares the item for the REST response. |
|
182 * |
|
183 * @since 6.4.0 |
|
184 * |
|
185 * @param WP_Post $item Post revision object. |
|
186 * @param WP_REST_Request $request Request object. |
|
187 * @return WP_REST_Response Response object. |
|
188 */ |
|
189 public function prepare_item_for_response( $item, $request ) { |
|
190 $template = _build_block_template_result_from_post( $item ); |
|
191 $response = $this->parent_controller->prepare_item_for_response( $template, $request ); |
|
192 |
|
193 $fields = $this->get_fields_for_response( $request ); |
|
194 $data = $response->get_data(); |
|
195 |
|
196 if ( in_array( 'parent', $fields, true ) ) { |
|
197 $data['parent'] = (int) $item->post_parent; |
|
198 } |
|
199 |
|
200 $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; |
|
201 $data = $this->filter_response_by_context( $data, $context ); |
|
202 |
|
203 // Wrap the data in a response object. |
|
204 $response = new WP_REST_Response( $data ); |
|
205 |
|
206 if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { |
|
207 $links = $this->prepare_links( $template ); |
|
208 $response->add_links( $links ); |
|
209 } |
|
210 |
|
211 return $response; |
|
212 } |
|
213 |
|
214 /** |
|
215 * Checks if a given request has access to delete a revision. |
|
216 * |
|
217 * @since 6.4.0 |
|
218 * |
|
219 * @param WP_REST_Request $request Full details about the request. |
|
220 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise. |
|
221 */ |
|
222 public function delete_item_permissions_check( $request ) { |
|
223 $parent = $this->get_parent( $request['parent'] ); |
|
224 if ( is_wp_error( $parent ) ) { |
|
225 return $parent; |
|
226 } |
|
227 |
|
228 if ( ! current_user_can( 'delete_post', $parent->ID ) ) { |
|
229 return new WP_Error( |
|
230 'rest_cannot_delete', |
|
231 __( 'Sorry, you are not allowed to delete revisions of this post.' ), |
|
232 array( 'status' => rest_authorization_required_code() ) |
|
233 ); |
|
234 } |
|
235 |
|
236 $revision = $this->get_revision( $request['id'] ); |
|
237 if ( is_wp_error( $revision ) ) { |
|
238 return $revision; |
|
239 } |
|
240 |
|
241 if ( ! current_user_can( 'edit_theme_options' ) ) { |
|
242 return new WP_Error( |
|
243 'rest_cannot_delete', |
|
244 __( 'Sorry, you are not allowed to delete this revision.' ), |
|
245 array( 'status' => rest_authorization_required_code() ) |
|
246 ); |
|
247 } |
|
248 |
|
249 return true; |
|
250 } |
|
251 |
|
252 /** |
|
253 * Prepares links for the request. |
|
254 * |
|
255 * @since 6.4.0 |
|
256 * |
|
257 * @param WP_Block_Template $template Template. |
|
258 * @return array Links for the given post. |
|
259 */ |
|
260 protected function prepare_links( $template ) { |
|
261 $links = array( |
|
262 'self' => array( |
|
263 'href' => rest_url( sprintf( '/%s/%s/%s/%s/%d', $this->namespace, $this->parent_base, $template->id, $this->rest_base, $template->wp_id ) ), |
|
264 ), |
|
265 'parent' => array( |
|
266 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->parent_base, $template->id ) ), |
|
267 ), |
|
268 ); |
|
269 |
|
270 return $links; |
|
271 } |
|
272 |
|
273 /** |
|
274 * Retrieves the item's schema, conforming to JSON Schema. |
|
275 * |
|
276 * @since 6.4.0 |
|
277 * |
|
278 * @return array Item schema data. |
|
279 */ |
|
280 public function get_item_schema() { |
|
281 if ( $this->schema ) { |
|
282 return $this->add_additional_fields_schema( $this->schema ); |
|
283 } |
|
284 |
|
285 $schema = $this->parent_controller->get_item_schema(); |
|
286 |
|
287 $schema['properties']['parent'] = array( |
|
288 'description' => __( 'The ID for the parent of the revision.' ), |
|
289 'type' => 'integer', |
|
290 'context' => array( 'view', 'edit', 'embed' ), |
|
291 ); |
|
292 |
|
293 $this->schema = $schema; |
|
294 |
|
295 return $this->add_additional_fields_schema( $this->schema ); |
|
296 } |
|
297 } |