PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.9-a.3
Jetpack – WP Security, Backup, Speed, & Growth v15.9-a.3
15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / json-endpoints / class.wpcom-json-api-post-endpoint.php
jetpack / json-endpoints Last commit date
jetpack 2 weeks ago class.wpcom-json-api-add-widget-endpoint.php 6 months ago class.wpcom-json-api-autosave-post-v1-1-endpoint.php 6 months ago class.wpcom-json-api-bulk-delete-post-endpoint.php 6 months ago class.wpcom-json-api-bulk-restore-post-endpoint.php 6 months ago class.wpcom-json-api-bulk-update-comments-endpoint.php 6 months ago class.wpcom-json-api-comment-endpoint.php 6 months ago class.wpcom-json-api-delete-media-endpoint.php 6 months ago class.wpcom-json-api-delete-media-v1-1-endpoint.php 6 months ago class.wpcom-json-api-edit-media-v1-2-endpoint.php 6 months ago class.wpcom-json-api-get-autosave-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-comment-counts-endpoint.php 6 months ago class.wpcom-json-api-get-comment-endpoint.php 6 months ago class.wpcom-json-api-get-comment-history-endpoint.php 6 months ago class.wpcom-json-api-get-comments-tree-endpoint.php 6 months ago class.wpcom-json-api-get-comments-tree-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-comments-tree-v1-2-endpoint.php 6 months ago class.wpcom-json-api-get-customcss.php 6 months ago class.wpcom-json-api-get-media-endpoint.php 6 months ago class.wpcom-json-api-get-media-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-media-v1-2-endpoint.php 6 months ago class.wpcom-json-api-get-post-counts-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-post-endpoint.php 6 months ago class.wpcom-json-api-get-post-v1-1-endpoint.php 6 months ago class.wpcom-json-api-get-site-endpoint.php 4 weeks ago class.wpcom-json-api-get-site-v1-2-endpoint.php 3 months ago class.wpcom-json-api-get-taxonomies-endpoint.php 1 month ago class.wpcom-json-api-get-taxonomy-endpoint.php 6 months ago class.wpcom-json-api-get-term-endpoint.php 6 months ago class.wpcom-json-api-list-comments-endpoint.php 6 months ago class.wpcom-json-api-list-dropdown-pages-endpoint.php 6 months ago class.wpcom-json-api-list-embeds-endpoint.php 6 months ago class.wpcom-json-api-list-media-endpoint.php 6 months ago class.wpcom-json-api-list-media-v1-1-endpoint.php 6 months ago class.wpcom-json-api-list-media-v1-2-endpoint.php 6 months ago class.wpcom-json-api-list-post-type-taxonomies-endpoint.php 6 months ago class.wpcom-json-api-list-post-types-endpoint.php 6 months ago class.wpcom-json-api-list-posts-endpoint.php 6 months ago class.wpcom-json-api-list-posts-v1-1-endpoint.php 6 months ago class.wpcom-json-api-list-posts-v1-2-endpoint.php 6 months ago class.wpcom-json-api-list-roles-endpoint.php 6 months ago class.wpcom-json-api-list-shortcodes-endpoint.php 6 months ago class.wpcom-json-api-list-terms-endpoint.php 6 months ago class.wpcom-json-api-list-users-endpoint.php 6 months ago class.wpcom-json-api-menus-v1-1-endpoint.php 6 months ago class.wpcom-json-api-post-endpoint.php 6 months ago class.wpcom-json-api-post-v1-1-endpoint.php 1 month ago class.wpcom-json-api-render-embed-endpoint.php 6 months ago class.wpcom-json-api-render-embed-reversal-endpoint.php 6 months ago class.wpcom-json-api-render-endpoint.php 2 weeks ago class.wpcom-json-api-render-shortcode-endpoint.php 6 months ago class.wpcom-json-api-sharing-buttons-endpoint.php 6 months ago class.wpcom-json-api-site-settings-endpoint.php 2 months ago class.wpcom-json-api-site-settings-v1-2-endpoint.php 6 months ago class.wpcom-json-api-site-settings-v1-3-endpoint.php 6 months ago class.wpcom-json-api-site-settings-v1-4-endpoint.php 2 months ago class.wpcom-json-api-site-user-endpoint.php 6 months ago class.wpcom-json-api-taxonomy-endpoint.php 6 months ago class.wpcom-json-api-update-comment-endpoint.php 4 months ago class.wpcom-json-api-update-customcss.php 6 months ago class.wpcom-json-api-update-media-endpoint.php 6 months ago class.wpcom-json-api-update-media-v1-1-endpoint.php 6 months ago class.wpcom-json-api-update-post-endpoint.php 2 weeks ago class.wpcom-json-api-update-post-v1-1-endpoint.php 2 weeks ago class.wpcom-json-api-update-post-v1-2-endpoint.php 2 weeks ago class.wpcom-json-api-update-site-homepage-endpoint.php 6 months ago class.wpcom-json-api-update-site-logo-endpoint.php 6 months ago class.wpcom-json-api-update-taxonomy-endpoint.php 5 months ago class.wpcom-json-api-update-term-endpoint.php 6 months ago class.wpcom-json-api-update-user-endpoint.php 6 months ago class.wpcom-json-api-upload-media-endpoint.php 6 months ago class.wpcom-json-api-upload-media-v1-1-endpoint.php 6 months ago
class.wpcom-json-api-post-endpoint.php
652 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit( 0 );
5 }
6
7 /**
8 * Post Endpoint class.
9 */
10 abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
11 /**
12 * Post object format.
13 *
14 * @var array
15 */
16 public $post_object_format = array(
17 // explicitly document and cast all output
18 'ID' => '(int) The post ID.',
19 'site_ID' => '(int) The site ID.',
20 'author' => '(object>author) The author of the post.',
21 'date' => "(ISO 8601 datetime) The post's creation time.",
22 'modified' => "(ISO 8601 datetime) The post's most recent update time.",
23 'title' => '(HTML) <code>context</code> dependent.',
24 'URL' => '(URL) The full permalink URL to the post.',
25 'short_URL' => '(URL) The wp.me short URL.',
26 'content' => '(HTML) <code>context</code> dependent.',
27 'excerpt' => '(HTML) <code>context</code> dependent.',
28 'slug' => '(string) The name (slug) for the post, used in URLs.',
29 'guid' => '(string) The GUID for the post.',
30 'status' => array(
31 'publish' => 'The post is published.',
32 'draft' => 'The post is saved as a draft.',
33 'pending' => 'The post is pending editorial approval.',
34 'private' => 'The post is published privately',
35 'future' => 'The post is scheduled for future publishing.',
36 'trash' => 'The post is in the trash.',
37 'auto-draft' => 'The post is a placeholder for a new post.',
38 ),
39 'sticky' => '(bool) Is the post sticky?',
40 'password' => '(string) The plaintext password protecting the post, or, more likely, the empty string if the post is not password protected.',
41 'parent' => "(object>post_reference|false) A reference to the post's parent, if it has one.",
42 'type' => "(string) The post's post_type. Post types besides post, page and revision need to be whitelisted using the <code>rest_api_allowed_post_types</code> filter.",
43 'comments_open' => '(bool) Is the post open for comments?',
44 'pings_open' => '(bool) Is the post open for pingbacks, trackbacks?',
45 'likes_enabled' => '(bool) Is the post open to likes?',
46 'sharing_enabled' => '(bool) Should sharing buttons show on this post?',
47 'comment_count' => '(int) The number of comments for this post.',
48 'like_count' => '(int) The number of likes for this post.',
49 'i_like' => '(bool) Does the current user like this post?',
50 'is_reblogged' => '(bool) Did the current user reblog this post?',
51 'is_following' => '(bool) Is the current user following this blog?',
52 'global_ID' => '(string) A unique WordPress.com-wide representation of a post.',
53 'featured_image' => '(URL) The URL to the featured image for this post if it has one.',
54 'post_thumbnail' => '(object>attachment) The attachment object for the featured image if it has one.',
55 'format' => array(), // see constructor
56 'geo' => '(object>geo|false)',
57 'menu_order' => '(int) (Pages Only) The order pages should appear in.',
58 'publicize_URLs' => '(array:URL) Array of Facebook URLs published by this post.',
59 'tags' => '(object:tag) Hash of tags (keyed by tag name) applied to the post.',
60 'categories' => '(object:category) Hash of categories (keyed by category name) applied to the post.',
61 'attachments' => '(object:attachment) Hash of post attachments (keyed by attachment ID).',
62 'metadata' => '(array) Array of post metadata keys and values. All unprotected meta keys are available by default for read requests. Both unprotected and protected meta keys are available for authenticated requests with access. Protected meta keys can be made available with the <code>rest_api_allowed_public_metadata</code> filter.',
63 'meta' => '(object) API result meta data',
64 'current_user_can' => '(object) List of permissions. Note, deprecated in favor of `capabilities`',
65 'capabilities' => '(object) List of post-specific permissions for the user; publish_post, edit_post, delete_post',
66 );
67
68 /**
69 * Constructor function.
70 *
71 * @param string|array|object $args — Args.
72 */
73 public function __construct( $args ) {
74 if ( is_array( $this->post_object_format ) && isset( $this->post_object_format['format'] ) ) {
75 $this->post_object_format['format'] = get_post_format_strings();
76 }
77 if ( ! $this->response_format ) {
78 $this->response_format =& $this->post_object_format;
79 }
80 parent::__construct( $args );
81 }
82
83 /**
84 * Filter to replace the password form with a simple message that the post is protected.
85 *
86 * @return string
87 */
88 public function the_password_form() {
89 return __( 'This post is password protected.', 'jetpack' );
90 }
91
92 /**
93 * Get a post by a specified field and value
94 *
95 * @param string $field - the field.
96 * @param string $field_value - the field value.
97 * @param string $context Post use context (e.g. 'display').
98 * @return array|bool|WP_Error Post
99 **/
100 public function get_post_by( $field, $field_value, $context = 'display' ) {
101 global $blog_id;
102
103 /** This filter is documented in class.json-api-endpoints.php */
104 $is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
105
106 if ( defined( 'GEO_LOCATION__CLASS' ) && class_exists( GEO_LOCATION__CLASS ) ) {
107 $geo = call_user_func( array( GEO_LOCATION__CLASS, 'init' ) );
108 } else {
109 $geo = false;
110 }
111
112 if ( 'display' === $context ) {
113 $args = $this->query_args();
114 if ( isset( $args['content_width'] ) && $args['content_width'] ) {
115 $GLOBALS['content_width'] = (int) $args['content_width'];
116 }
117 }
118
119 switch ( $field ) {
120 case 'name':
121 $post_id = $this->get_post_id_by_name( $field_value );
122 if ( is_wp_error( $post_id ) ) {
123 return $post_id;
124 }
125 break;
126 default:
127 $post_id = (int) $field_value;
128 break;
129 }
130
131 $post = get_post( $post_id, OBJECT, $context );
132
133 if ( ! $post || is_wp_error( $post ) ) {
134 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
135 }
136
137 if ( ! $this->is_post_type_allowed( $post->post_type ) && ( ! function_exists( 'is_post_freshly_pressed' ) || ! is_post_freshly_pressed( $post->ID ) ) ) {
138 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
139 }
140
141 // Permissions
142 $capabilities = $this->get_current_user_capabilities( $post );
143
144 switch ( $context ) {
145 case 'edit':
146 if ( ! $capabilities['edit_post'] ) {
147 return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
148 }
149 break;
150 case 'display':
151 break;
152 default:
153 return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 );
154 }
155
156 $can_view = $this->user_can_view_post( $post->ID );
157 if ( ! $can_view || is_wp_error( $can_view ) ) {
158 return $can_view;
159 }
160
161 $GLOBALS['post'] = $post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
162
163 if ( 'display' === $context ) {
164 setup_postdata( $post );
165 }
166
167 $response = array();
168
169 $fields = null;
170 if ( 'display' === $context && ! empty( $this->api->query['fields'] ) ) {
171 $fields = array_fill_keys( array_map( 'trim', explode( ',', $this->api->query['fields'] ) ), true );
172 }
173
174 foreach ( array_keys( $this->post_object_format ) as $key ) {
175 if ( $fields !== null && ! isset( $fields[ $key ] ) ) {
176 continue;
177 }
178 switch ( $key ) {
179 case 'ID':
180 // explicitly cast all output
181 $response[ $key ] = (int) $post->ID;
182 break;
183 case 'site_ID':
184 $response[ $key ] = (int) $this->api->get_blog_id_for_output();
185 break;
186 case 'author':
187 $response[ $key ] = (object) $this->get_author( $post, 'edit' === $context && $capabilities['edit_post'] );
188 break;
189 case 'date':
190 $response[ $key ] = (string) $this->format_date( $post->post_date_gmt, $post->post_date );
191 break;
192 case 'modified':
193 $response[ $key ] = (string) $this->format_date( $post->post_modified_gmt, $post->post_modified );
194 break;
195 case 'title':
196 if ( 'display' === $context ) {
197 $response[ $key ] = (string) get_the_title( $post->ID );
198 } else {
199 $response[ $key ] = htmlspecialchars_decode( $post->post_title, ENT_QUOTES );
200 }
201 break;
202 case 'URL':
203 if ( 'revision' === $post->post_type ) {
204 $response[ $key ] = (string) esc_url_raw( get_permalink( $post->post_parent ) );
205 } else {
206 $response[ $key ] = (string) esc_url_raw( get_permalink( $post->ID ) );
207 }
208 break;
209 case 'short_URL':
210 $response[ $key ] = (string) esc_url_raw( wp_get_shortlink( $post->ID ) );
211 break;
212 case 'content':
213 if ( 'display' === $context ) {
214 add_filter( 'the_password_form', array( $this, 'the_password_form' ) );
215 $response[ $key ] = (string) $this->get_the_post_content_for_display();
216 remove_filter( 'the_password_form', array( $this, 'the_password_form' ) );
217 } else {
218 $response[ $key ] = (string) $post->post_content;
219 }
220 break;
221 case 'excerpt':
222 if ( 'display' === $context ) {
223 add_filter( 'the_password_form', array( $this, 'the_password_form' ) );
224 ob_start();
225 the_excerpt();
226 $response[ $key ] = (string) ob_get_clean();
227 remove_filter( 'the_password_form', array( $this, 'the_password_form' ) );
228 } else {
229 $response[ $key ] = htmlspecialchars_decode( (string) $post->post_excerpt, ENT_QUOTES );
230 }
231 break;
232 case 'status':
233 $response[ $key ] = (string) get_post_status( $post->ID );
234 break;
235 case 'sticky':
236 $response[ $key ] = (bool) is_sticky( $post->ID );
237 break;
238 case 'slug':
239 $response[ $key ] = (string) $post->post_name;
240 break;
241 case 'guid':
242 $response[ $key ] = (string) $post->guid;
243 break;
244 case 'password':
245 $response[ $key ] = (string) $post->post_password;
246 if ( 'edit' === $context ) {
247 $response[ $key ] = htmlspecialchars_decode( $response[ $key ], ENT_QUOTES );
248 }
249 break;
250 /** (object|false) */
251 case 'parent':
252 if ( $post->post_parent ) {
253 $parent = get_post( $post->post_parent );
254 if ( 'display' === $context ) {
255 $parent_title = (string) get_the_title( $parent->ID );
256 } else {
257 $parent_title = htmlspecialchars_decode( $post->post_title, ENT_QUOTES );
258 }
259 $response[ $key ] = (object) array(
260 'ID' => (int) $parent->ID,
261 'type' => (string) $parent->post_type,
262 'link' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $parent->ID ),
263 'title' => $parent_title,
264 );
265 } else {
266 $response[ $key ] = false;
267 }
268 break;
269 case 'type':
270 $response[ $key ] = (string) $post->post_type;
271 break;
272 case 'comments_open':
273 $response[ $key ] = (bool) comments_open( $post->ID );
274 break;
275 case 'pings_open':
276 $response[ $key ] = (bool) pings_open( $post->ID );
277 break;
278 case 'likes_enabled':
279 /** This filter is documented in modules/likes.php */
280 $sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
281 $post_likes_switched = get_post_meta( $post->ID, 'switch_like_status', true );
282 $post_likes_enabled = $post_likes_switched || ( $sitewide_likes_enabled && $post_likes_switched !== '0' );
283 $response[ $key ] = $post_likes_enabled;
284 break;
285 case 'sharing_enabled':
286 $show = true;
287 /** This filter is documented in modules/sharedaddy/sharing-service.php */
288 $show = apply_filters( 'sharing_show', $show, $post );
289
290 $switched_status = get_post_meta( $post->ID, 'sharing_disabled', false );
291
292 if ( ! empty( $switched_status ) ) {
293 $show = false;
294 }
295 $response[ $key ] = (bool) $show;
296 break;
297 case 'comment_count':
298 $response[ $key ] = (int) $post->comment_count;
299 break;
300 case 'like_count':
301 $response[ $key ] = (int) $this->api->post_like_count( $blog_id, $post->ID );
302 break;
303 case 'i_like':
304 $response[ $key ] = (bool) $this->api->is_liked( $blog_id, $post->ID );
305 break;
306 case 'is_reblogged':
307 $response[ $key ] = (bool) $this->api->is_reblogged( $blog_id, $post->ID );
308 break;
309 case 'is_following':
310 $response[ $key ] = (bool) $this->api->is_following( $blog_id );
311 break;
312 case 'global_ID':
313 $response[ $key ] = (string) $this->api->add_global_ID( $blog_id, $post->ID );
314 break;
315 case 'featured_image':
316 if ( $is_jetpack && ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
317 $response[ $key ] = get_post_meta( $post->ID, '_jetpack_featured_image', true );
318 } else {
319 $image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' );
320 if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
321 $response[ $key ] = (string) $image_attributes[0];
322 } else {
323 $response[ $key ] = '';
324 }
325 }
326 break;
327 case 'post_thumbnail':
328 $response[ $key ] = null;
329
330 $thumb_id = get_post_thumbnail_id( $post->ID );
331 if ( ! empty( $thumb_id ) ) {
332 $attachment = get_post( $thumb_id );
333 if ( ! empty( $attachment ) ) {
334 $featured_image_object = $this->get_attachment( $attachment );
335 }
336
337 if ( ! empty( $featured_image_object ) ) {
338 $response[ $key ] = (object) $featured_image_object;
339 }
340 }
341 break;
342 case 'format':
343 $response[ $key ] = (string) get_post_format( $post->ID );
344 if ( ! $response[ $key ] ) {
345 $response[ $key ] = 'standard';
346 }
347 break;
348 /** (object|false) */
349 case 'geo':
350 if ( ! $geo ) {
351 $response[ $key ] = false;
352 } else {
353 $geo_data = $geo->get_geo( 'post', $post->ID );
354 $response[ $key ] = false;
355 if ( $geo_data ) {
356 $geo_data = array_intersect_key(
357 $geo_data,
358 array(
359 'latitude' => true,
360 'longitude' => true,
361 'address' => true,
362 'public' => true,
363 )
364 );
365 if ( $geo_data ) {
366 $response[ $key ] = (object) array(
367 'latitude' => isset( $geo_data['latitude'] ) ? (float) $geo_data['latitude'] : 0,
368 'longitude' => isset( $geo_data['longitude'] ) ? (float) $geo_data['longitude'] : 0,
369 'address' => isset( $geo_data['address'] ) ? (string) $geo_data['address'] : '',
370 );
371 } else {
372 $response[ $key ] = false;
373 }
374 // Private
375 if ( ! isset( $geo_data['public'] ) || ! $geo_data['public'] ) {
376 if ( 'edit' !== $context || ! $capabilities['edit_post'] ) {
377 // user can't access
378 $response[ $key ] = false;
379 }
380 }
381 }
382 }
383 break;
384 case 'menu_order':
385 $response[ $key ] = (int) $post->menu_order;
386 break;
387 case 'publicize_URLs':
388 $publicize_urls = array();
389 $publicize = get_post_meta( $post->ID, 'publicize_results', true );
390 if ( $publicize ) {
391 foreach ( $publicize as $service => $data ) {
392 switch ( $service ) {
393 // @todo Explore removing once Twitter has been removed from Publicize.
394 case 'twitter':
395 foreach ( $data as $datum ) {
396 $publicize_urls[] = esc_url_raw( "https://twitter.com/{$datum['user_id']}/status/{$datum['post_id']}" );
397 }
398 break;
399 case 'fb':
400 foreach ( $data as $datum ) {
401 $publicize_urls[] = esc_url_raw( "https://www.facebook.com/permalink.php?story_fbid={$datum['post_id']}&id={$datum['user_id']}" );
402 }
403 break;
404 }
405 }
406 }
407 $response[ $key ] = $publicize_urls;
408 break;
409 case 'tags':
410 $response[ $key ] = array();
411 $terms = wp_get_post_tags( $post->ID );
412 foreach ( $terms as $term ) {
413 if ( ! empty( $term->name ) ) {
414 $response[ $key ][ $term->name ] = $this->format_taxonomy( $term, 'post_tag', 'display' );
415 }
416 }
417 $response[ $key ] = (object) $response[ $key ];
418 break;
419 case 'categories':
420 $response[ $key ] = array();
421 $terms = wp_get_object_terms( $post->ID, 'category', array( 'fields' => 'all' ) );
422 foreach ( $terms as $term ) {
423 if ( ! empty( $term->name ) ) {
424 $response[ $key ][ $term->name ] = $this->format_taxonomy( $term, 'category', 'display' );
425 }
426 }
427 $response[ $key ] = (object) $response[ $key ];
428 break;
429 case 'attachments':
430 $response[ $key ] = array();
431 $_attachments = get_posts(
432 array(
433 'post_parent' => $post->ID,
434 'post_status' => 'inherit',
435 'post_type' => 'attachment',
436 'posts_per_page' => 100,
437 )
438 );
439 foreach ( $_attachments as $attachment ) {
440 $response[ $key ][ $attachment->ID ] = $this->get_attachment( $attachment );
441 }
442 $response[ $key ] = (object) $response[ $key ];
443 break;
444 /** (array|false) */
445 case 'metadata':
446 $metadata = array();
447 foreach ( (array) has_meta( $post_id ) as $meta ) {
448 // Don't expose protected fields.
449 $show = false;
450 if ( WPCOM_JSON_API_Metadata::is_public( $meta['meta_key'] ) ) {
451 $show = true;
452 }
453 if ( current_user_can( 'edit_post_meta', $post_id, $meta['meta_key'] ) ) {
454 $show = true;
455 }
456
457 if (
458 in_array( $meta['meta_key'], Jetpack_SEO_Posts::POST_META_KEYS_ARRAY, true ) &&
459 ! Jetpack_SEO_Utils::is_enabled_jetpack_seo()
460 ) {
461 $show = false;
462 }
463
464 if ( ! $show ) {
465 continue;
466 }
467
468 $metadata[] = array(
469 'id' => $meta['meta_id'],
470 'key' => $meta['meta_key'],
471 'value' => maybe_unserialize( $meta['meta_value'] ),
472 );
473 }
474
475 if ( ! empty( $metadata ) ) {
476 $response[ $key ] = $metadata;
477 } else {
478 $response[ $key ] = false;
479 }
480 break;
481 case 'meta':
482 $response[ $key ] = (object) array(
483 'links' => (object) array(
484 'self' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $post->ID ),
485 'help' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $post->ID, 'help' ),
486 'site' => (string) $this->links->get_site_link( $this->api->get_blog_id_for_output() ),
487 'replies' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $post->ID, 'replies/' ),
488 'likes' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $post->ID, 'likes/' ),
489 ),
490 );
491 break;
492 case 'current_user_can':
493 $response[ $key ] = $capabilities;
494 break;
495 case 'capabilities':
496 $response[ $key ] = $capabilities;
497 break;
498
499 }
500 }
501
502 unset( $GLOBALS['post'] );
503 return $response;
504 }
505
506 /**
507 *
508 * Get the post content for display.
509 *
510 * No Blog ID parameter. No Post ID parameter. Depends on globals.
511 * Expects setup_postdata() to already have been run.
512 *
513 * @return string|false
514 */
515 public function get_the_post_content_for_display() {
516 global $pages, $page;
517
518 $old_pages = $pages;
519 $old_page = $page;
520
521 $content = implode( "\n\n", $pages );
522 $content = preg_replace( '/<!--more(.*?)?-->/', '', $content );
523 $pages = array( $content ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
524 $page = 1; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
525
526 ob_start();
527 the_content();
528 $return = ob_get_clean();
529
530 $pages = $old_pages; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
531 $page = $old_page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
532
533 return $return;
534 }
535
536 /**
537 * Gets the blog post.
538 *
539 * @param int $blog_id - the blog ID.
540 * @param int $post_id - the post ID.
541 * @param string $context - the context.
542 * @return array|bool|WP_Error Post
543 */
544 public function get_blog_post( $blog_id, $post_id, $context = 'display' ) {
545 $blog_id = $this->api->get_blog_id( $blog_id );
546 if ( ! $blog_id || is_wp_error( $blog_id ) ) {
547 return $blog_id;
548 }
549 switch_to_blog( $blog_id );
550 $post = $this->get_post_by( 'ID', $post_id, $context );
551 restore_current_blog();
552 return $post;
553 }
554
555 /**
556 * Supporting featured media in post endpoints. Currently on for wpcom blogs
557 * since it's calling WPCOM_JSON_API_Read_Endpoint methods which presently
558 * rely on wpcom specific functionality.
559 *
560 * @param WP_Post $post - the WP Post object.
561 * @return object list of featured media
562 */
563 public static function find_featured_media( &$post ) {
564
565 if ( class_exists( 'WPCOM_JSON_API_Read_Endpoint' ) ) {
566 return WPCOM_JSON_API_Read_Endpoint::find_featured_worthy_media( (array) $post );
567 } else {
568 return (object) array();
569 }
570 }
571
572 /**
573 * Returns attachment object.
574 *
575 * @param object $attachment attachment row.
576 *
577 * @return object
578 */
579 public function get_attachment( $attachment ) {
580 $metadata = wp_get_attachment_metadata( $attachment->ID );
581
582 $result = array(
583 'ID' => (int) $attachment->ID,
584 'URL' => (string) wp_get_attachment_url( $attachment->ID ),
585 'guid' => (string) $attachment->guid,
586 'mime_type' => (string) $attachment->post_mime_type,
587 'width' => (int) isset( $metadata['width'] ) ? $metadata['width'] : 0,
588 'height' => (int) isset( $metadata['height'] ) ? $metadata['height'] : 0,
589 );
590
591 if ( isset( $metadata['duration'] ) ) {
592 $result['duration'] = (int) $metadata['duration'];
593 }
594
595 return (object) apply_filters( 'get_attachment', $result );
596 }
597
598 /**
599 * Get post-specific user capabilities
600 *
601 * @param WP_Post $post - the WP_Post object.
602 *
603 * @return array - array of post-level permissions; 'publish_post', 'delete_post', 'edit_post'
604 */
605 public function get_current_user_capabilities( $post ) {
606 return array(
607 'publish_post' => current_user_can( 'publish_post', $post->ID ),
608 'delete_post' => current_user_can( 'delete_post', $post->ID ),
609 'edit_post' => current_user_can( 'edit_post', $post->ID ),
610 );
611 }
612
613 /**
614 * Get post ID by name
615 *
616 * Attempts to match name on post title and page path
617 *
618 * @param string $name - the name of the post.
619 *
620 * @return int|object Post ID on success, WP_Error object on failure
621 **/
622 protected function get_post_id_by_name( $name ) {
623 $name = sanitize_title( $name );
624
625 if ( ! $name ) {
626 return new WP_Error( 'invalid_post', 'Invalid post', 400 );
627 }
628
629 $posts = get_posts(
630 array(
631 'name' => $name,
632 'numberposts' => 1,
633 'post_type' => $this->_get_whitelisted_post_types(),
634 )
635 );
636
637 if ( ! $posts || ! isset( $posts[0]->ID ) || ! $posts[0]->ID ) {
638 $page = get_page_by_path( $name );
639
640 if ( ! $page ) {
641 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
642 }
643
644 $post_id = $page->ID;
645 } else {
646 $post_id = (int) $posts[0]->ID;
647 }
648
649 return $post_id;
650 }
651 }
652