PluginProbe
UpdraftCentral Dashboard / trunk
UpdraftCentral Dashboard vtrunk
0.8.33 0.7.2 0.7.3 0.7.4 0.8.0 0.8.1 0.8.10 0.8.11 0.8.12 0.8.13 0.8.14 0.8.15 0.8.16 0.8.17 0.8.18 0.8.19 0.8.2 0.8.20 0.8.21 0.8.22 0.8.23 0.8.24 0.8.25 0.8.26 0.8.27 All 51 releases
updraftcentral / classes / class-rest-posts-controller.php

class-rest-posts-controller.php in UpdraftCentral Dashboard trunk, at classes/class-rest-posts-controller.php

438 lines 15.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) die('Access denied.');
3
4 if (!class_exists('WP_REST_Posts_Controller')) {
5 include_once ABSPATH . 'wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php';
6 }
7
8 /**
9 * UpdraftCentral_REST_Posts_Controller class. Used to override some basic response
10 * details to integrate the remote post information.
11 *
12 * Originally copied and edited from WP_REST_Posts_Controller (WordPress 5.0.3). Updated
13 * to support higher versions up to WordPress 5.4.
14 *
15 * @see WP_REST_Posts_Controller
16 */
17 class UpdraftCentral_REST_Posts_Controller extends WP_REST_Posts_Controller {
18
19 public function __construct($post_type) {
20 parent::__construct($post_type);
21 }
22
23 /**
24 * Prepares a single post output for response.
25 *
26 * @since 4.7.0
27 *
28 * @param object $post_obj A simple (stdClass) object containing basic (public) properties of a remote post object.
29 * @param WP_REST_Request $request Request object.
30 * @param array $misc Array containing remote post data.
31 * @return WP_REST_Response Response object.
32 */
33 public function prepare_remote_item_for_response($post_obj, $request, $misc) {
34 // Make sure that we have a WP_Post object before proceeding
35 // with the rest of the process below.
36 $post = new WP_Post($post_obj);
37
38 $fields = $this->get_fields_for_response($request);
39
40 // Base fields for every post.
41 $data = array();
42
43 if ((function_exists('rest_is_field_included') && rest_is_field_included('id', $fields)) || in_array('id', $fields, true)) {
44 $data['id'] = (int) $post->ID;
45 }
46
47 if ((function_exists('rest_is_field_included') && rest_is_field_included('date', $fields)) || in_array('date', $fields, true)) {
48 $data['date'] = $this->prepare_date_response($post->post_date_gmt, $post->post_date);
49 }
50
51 if ((function_exists('rest_is_field_included') && rest_is_field_included('date_gmt', $fields)) || in_array('date_gmt', $fields, true)) {
52 /*
53 * For drafts, `post_date_gmt` may not be set, indicating that the date
54 * of the draft should be updated each time it is saved (see #38883).
55 * In this case, shim the value based on the `post_date` field
56 * with the site's timezone offset applied.
57 */
58 if ('0000-00-00 00:00:00' === $post->post_date_gmt) {
59 $post_date_gmt = get_gmt_from_date($post->post_date);
60 } else {
61 $post_date_gmt = $post->post_date_gmt;
62 }
63 $data['date_gmt'] = $this->prepare_date_response($post_date_gmt);
64 }
65
66 if ((function_exists('rest_is_field_included') && rest_is_field_included('guid', $fields)) || in_array('guid', $fields, true)) {
67 $data['guid'] = array(
68 /** This filter is documented in wp-includes/post-template.php */
69 'rendered' => $misc['guid_rendered'],
70 'raw' => $post->guid,
71 );
72 }
73
74 if ((function_exists('rest_is_field_included') && rest_is_field_included('modified', $fields)) || in_array('modified', $fields, true)) {
75 $data['modified'] = $this->prepare_date_response($post->post_modified_gmt, $post->post_modified);
76 }
77
78 if ((function_exists('rest_is_field_included') && rest_is_field_included('modified_gmt', $fields)) || in_array('modified_gmt', $fields, true)) {
79 /*
80 * For drafts, `post_modified_gmt` may not be set (see `post_date_gmt` comments
81 * above). In this case, shim the value based on the `post_modified` field
82 * with the site's timezone offset applied.
83 */
84 if ('0000-00-00 00:00:00' === $post->post_modified_gmt) {
85 $post_modified_gmt = gmdate('Y-m-d H:i:s', strtotime($post->post_modified) - (get_option('gmt_offset') * 3600));
86 } else {
87 $post_modified_gmt = $post->post_modified_gmt;
88 }
89 $data['modified_gmt'] = $this->prepare_date_response($post_modified_gmt);
90 }
91
92 if ((function_exists('rest_is_field_included') && rest_is_field_included('password', $fields)) || in_array('password', $fields, true)) {
93 $data['password'] = $post->post_password;
94 }
95
96 if ((function_exists('rest_is_field_included') && rest_is_field_included('slug', $fields)) || in_array('slug', $fields, true)) {
97 $data['slug'] = $post->post_name;
98 }
99
100 if ((function_exists('rest_is_field_included') && rest_is_field_included('status', $fields)) || in_array('status', $fields, true)) {
101 $data['status'] = $post->post_status;
102 }
103
104 if ((function_exists('rest_is_field_included') && rest_is_field_included('type', $fields)) || in_array('type', $fields, true)) {
105 $data['type'] = $post->post_type;
106 }
107
108 if ((function_exists('rest_is_field_included') && rest_is_field_included('link', $fields)) || in_array('link', $fields, true)) {
109 $data['link'] = $misc['link'];
110 }
111
112 if ((function_exists('rest_is_field_included') && rest_is_field_included('title', $fields)) || in_array('title', $fields, true)) {
113 $data['title'] = array(
114 'raw' => $post->post_title,
115 'rendered' => $misc['title_rendered'],
116 );
117 }
118
119 if ((function_exists('rest_is_field_included') && rest_is_field_included('content', $fields)) || in_array('content', $fields, true)) {
120 $data['content'] = array(
121 'raw' => $post->post_content,
122 /** This filter is documented in wp-includes/post-template.php */
123 'rendered' => $misc['post_password_required'] ? '' : $misc['content_rendered'],
124 'protected' => (bool) $post->post_password,
125 'block_version' => block_version($post->post_content),
126 );
127 }
128
129 if ((function_exists('rest_is_field_included') && rest_is_field_included('excerpt', $fields)) || in_array('excerpt', $fields, true)) {
130 /** This filter is documented in wp-includes/post-template.php */
131 $excerpt = $misc['excerpt'];
132 $data['excerpt'] = array(
133 'raw' => $post->post_excerpt,
134 'rendered' => $misc['post_password_required'] ? '' : $excerpt,
135 'protected' => (bool) $post->post_password,
136 );
137 }
138
139 if ((function_exists('rest_is_field_included') && rest_is_field_included('author', $fields)) || in_array('author', $fields, true)) {
140 $data['author'] = (int) $post->post_author;
141 }
142
143 if ((function_exists('rest_is_field_included') && rest_is_field_included('featured_media', $fields)) || in_array('featured_media', $fields, true)) {
144 $data['featured_media'] = (int) $misc['featured_media'];
145 }
146
147 if ((function_exists('rest_is_field_included') && rest_is_field_included('parent', $fields)) || in_array('parent', $fields, true)) {
148 $data['parent'] = (int) $post->post_parent;
149 }
150
151 if ((function_exists('rest_is_field_included') && rest_is_field_included('menu_order', $fields)) || in_array('menu_order', $fields, true)) {
152 $data['menu_order'] = (int) $post->menu_order;
153 }
154
155 if ((function_exists('rest_is_field_included') && rest_is_field_included('comment_status', $fields)) || in_array('comment_status', $fields, true)) {
156 $data['comment_status'] = $post->comment_status;
157 }
158
159 if ((function_exists('rest_is_field_included') && rest_is_field_included('ping_status', $fields)) || in_array('ping_status', $fields, true)) {
160 $data['ping_status'] = $post->ping_status;
161 }
162
163 if ((function_exists('rest_is_field_included') && rest_is_field_included('sticky', $fields)) || in_array('sticky', $fields, true)) {
164 $data['sticky'] = (bool) $misc['sticky'];
165 }
166
167 if ((function_exists('rest_is_field_included') && rest_is_field_included('template', $fields)) || in_array('template', $fields, true)) {
168 $template = $misc['template'];
169 if ($template) {
170 $data['template'] = $template;
171 } else {
172 $data['template'] = 'default';
173 }
174 }
175
176 if ((function_exists('rest_is_field_included') && rest_is_field_included('format', $fields)) || in_array('format', $fields, true)) {
177 $data['format'] = $misc['format'];
178 if ('false' == $data['format']) $data['format'] = (bool) $data['format'];
179
180 // Fill in blank post format.
181 if (empty($data['format'])) {
182 $data['format'] = 'standard';
183 }
184 }
185
186 if ((function_exists('rest_is_field_included') && rest_is_field_included('meta', $fields)) || in_array('meta', $fields, true)) {
187 $data['meta'] = $this->meta->get_value($post->ID, $request);
188 }
189
190 // We need categories and tags to show up in all WP version starting from WP 3.4, thus,
191 // we're going pass a "show_ui = true" filter to the wp_list_filter function. Filtering other
192 // taxonomy that are not meant to be shown in any UI elements or section.
193 if (isset($misc['taxonomy_objects'])) {
194 $taxonomies = wp_list_filter($misc['taxonomy_objects'], array('show_ui' => 'true'));
195
196 foreach ($taxonomies as $taxonomy) {
197 $tax_name = $taxonomy['name'];
198 $base = !empty($taxonomy['rest_base']) ? $taxonomy['rest_base'] : $tax_name;
199
200 if ((function_exists('rest_is_field_included') && rest_is_field_included($base, $fields)) || in_array($base, $fields, true)) {
201 $terms = $misc['taxonomy_terms'][$tax_name];
202
203 // Making sure that the terms collection is not empty before we add its data to the response.
204 if (is_array($terms) && !empty($terms)) {
205 // Get the first term element and validate if it's an array, it's not empty and it is the one we expected
206 $term_0 = reset($terms);
207 if (is_array($term_0) && !empty($term_0) && isset($term_0['term_id'])) {
208 $terms = array_values(wp_list_pluck($terms, 'term_id'));
209 foreach ($terms as $key => $value) {
210 if (!empty($value)) {
211 $terms[$key] = (int) $value;
212 } else {
213 // If value is empty, then we remove the empty entry
214 // from the terms collection before returning.
215 unset($terms[$key]);
216 }
217 }
218 }
219 }
220 $data[$base] = $terms ? $terms : array();
221 }
222 }
223 }
224
225 if ((bool) $misc['post_type_viewable'] && (bool) $misc['post_type_public']) {
226 $sample_permalink = $misc['sample_permalink'];
227 if ((function_exists('rest_is_field_included') && rest_is_field_included('permalink_template', $fields)) || in_array('permalink_template', $fields, true)) {
228 $data['permalink_template'] = $sample_permalink[0];
229 }
230 if ((function_exists('rest_is_field_included') && rest_is_field_included('generated_slug', $fields)) || in_array('generated_slug', $fields, true)) {
231 $data['generated_slug'] = $sample_permalink[1];
232 }
233 }
234
235 $context = !empty($request['context']) ? $request['context'] : 'view';
236 $data = $this->add_additional_fields_to_object($data, $request);
237 $data = $this->filter_response_by_context($data, $context);
238
239 // Wrap the data in a response object.
240 $response = rest_ensure_response($data);
241
242 $links = $this->prepare_remote_links($post, $misc);
243 $response->add_links($links);
244
245 if (!empty($links['self']['href'])) {
246 $actions = $this->get_available_remote_actions($post, $request, $misc);
247
248 $self = $links['self']['href'];
249 foreach ($actions as $rel) {
250 $response->add_link($rel, $self);
251 }
252 }
253
254 return $response;
255 }
256
257 /**
258 * Prepares remote links for the request.
259 *
260 * @param WP_Post $post Post object.
261 * @param array $misc Miscellaneous data for the Post object
262 * @return array Links for the given post.
263 */
264 private function prepare_remote_links($post, $misc) {
265 $base = sprintf('%s/%s', $this->namespace, $this->rest_base);
266
267 // Entity meta.
268 $links = array(
269 'self' => array(
270 'href' => rest_url(trailingslashit($base) . $post->ID),
271 ),
272 'collection' => array(
273 'href' => rest_url($base),
274 ),
275 'about' => array(
276 'href' => rest_url('wp/v2/types/' . $this->post_type),
277 ),
278 );
279
280 if ((in_array($post->post_type, array('post', 'page'), true) || (bool) $misc['post_type_supports_authors'])
281 && !empty($post->post_author)) {// phpcs:ignore PEAR.ControlStructures.MultiLineCondition.CloseBracketNewLine
282 $links['author'] = array(
283 'href' => rest_url('wp/v2/users/' . $post->post_author),
284 'embeddable' => true,
285 );
286 }
287
288 if (in_array($post->post_type, array('post', 'page'), true) || (bool) $misc['post_type_supports_comments']) {
289 $replies_url = rest_url('wp/v2/comments');
290 $replies_url = add_query_arg('post', $post->ID, $replies_url);
291
292 $links['replies'] = array(
293 'href' => $replies_url,
294 'embeddable' => true,
295 );
296 }
297
298 if (in_array($post->post_type, array('post', 'page'), true) || (bool) $misc['post_type_supports_revisions']) {
299 if (isset($misc['post_revisions']) && is_array($misc['post_revisions'])) {
300 $revisions = $misc['post_revisions'];
301 $revisions_count = count($revisions);
302
303 $links['version-history'] = array(
304 'href' => rest_url(trailingslashit($base) . $post->ID . '/revisions'),
305 'count' => $revisions_count,
306 );
307
308 if ($revisions_count > 0) {
309 $last_revision = array_shift($revisions);
310
311 $links['predecessor-version'] = array(
312 'href' => rest_url(trailingslashit($base) . $post->ID . '/revisions/' . $last_revision),
313 'id' => $last_revision,
314 );
315 }
316 }
317 }
318
319 if ((bool) $misc['post_type_hierarchical'] && !empty($post->post_parent)) {
320 $links['up'] = array(
321 'href' => rest_url(trailingslashit($base) . (int) $post->post_parent),
322 'embeddable' => true,
323 );
324 }
325
326 // If we have a featured media, add that.
327 if ($featured_media = $misc['post_thumbnail_id']) {
328 $image_url = rest_url('wp/v2/media/' . $featured_media);
329
330 $links['https://api.w.org/featuredmedia'] = array(
331 'href' => $image_url,
332 'embeddable' => true,
333 );
334 }
335
336 if (!in_array($post->post_type, array('attachment', 'nav_menu_item', 'revision'), true)) {
337 $attachments_url = rest_url('wp/v2/media');
338 $attachments_url = add_query_arg('parent', $post->ID, $attachments_url);
339
340 $links['https://api.w.org/attachment'] = array(
341 'href' => $attachments_url,
342 );
343 }
344
345 if (isset($misc['taxonomy_names'])) {
346 $taxonomies = $misc['taxonomy_names'];
347 if (!empty($taxonomies)) {
348 $links['https://api.w.org/term'] = array();
349
350 foreach ($taxonomies as $tax) {
351 if (!empty($misc['taxonomy_objects']) && isset($misc['taxonomy_objects'][$tax])) {
352 $taxonomy_obj = $misc['taxonomy_objects'][$tax];
353
354 // Skip taxonomies that are not public.
355 if (empty($taxonomy_obj['show_in_rest']) || !json_decode($taxonomy_obj['show_in_rest'])) {
356 continue;
357 }
358
359 $tax_base = !empty($taxonomy_obj['rest_base']) ? $taxonomy_obj['rest_base'] : $tax;
360 $terms_url = add_query_arg(
361 'post',
362 $post->ID,
363 rest_url('wp/v2/' . $tax_base)
364 );
365
366 $links['https://api.w.org/term'][] = array(
367 'href' => $terms_url,
368 'taxonomy' => $tax,
369 'embeddable' => true,
370 );
371 }
372 }
373 }
374 }
375
376 return $links;
377 }
378
379 /**
380 * Get available remote actions
381 *
382 * @param WP_Post $post Post object.
383 * @param WP_REST_Request $request Request object.
384 * @param array $misc Miscellaneous data for the Post object
385 * @return array Action links for the given post.
386 */
387 private function get_available_remote_actions($post, $request, $misc) {
388 if ('edit' !== $request['context']) {
389 return array();
390 }
391
392 $rels = array();
393
394 if ('attachment' !== $this->post_type && (bool) $misc['can_publish_posts']) {
395 $rels[] = 'https://api.w.org/action-publish';
396 }
397
398 if ((bool) $misc['can_unfiltered_html']) {
399 $rels[] = 'https://api.w.org/action-unfiltered-html';
400 }
401
402 if ('post' === $misc['post_type_name']) {
403 if ((bool) $misc['can_edit_others_posts'] && (bool) $misc['can_publish_posts']) {
404 $rels[] = 'https://api.w.org/action-sticky';
405 }
406 }
407
408 if ((bool) $misc['post_type_supports_authors']) {
409 if ((bool) $misc['can_edit_others_posts']) {
410 $rels[] = 'https://api.w.org/action-assign-author';
411 }
412 }
413
414 if (isset($misc['taxonomy_objects'])) {
415 $taxonomies = wp_list_filter($misc['taxonomy_objects'], array('show_in_rest' => 'true'));
416 foreach ($taxonomies as $tax) {
417 $tax_name = $tax['name'];
418 $tax_base = !empty($tax['rest_base']) ? $tax['rest_base'] : $tax_name;
419
420 if (!empty($misc['taxonomy_caps']) && isset($misc['taxonomy_caps'][$tax_name])) {
421 $tax_caps = $misc['taxonomy_caps'][$tax_name];
422 $create_cap = (bool) $tax_caps['hierarchical'] ? (bool) $tax_caps['edit_terms'] : (bool) $tax_caps['assign_terms'];
423
424 if ($create_cap) {
425 $rels[] = 'https://api.w.org/action-create-' . $tax_base;
426 }
427
428 if ((bool) $tax_caps['assign_terms']) {
429 $rels[] = 'https://api.w.org/action-assign-' . $tax_base;
430 }
431 }
432 }
433 }
434
435 return $rels;
436 }
437 }
438