PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.5.1
Jetpack – WP Security, Backup, Speed, & Growth v14.5.1
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 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 1 year ago class.wpcom-json-api-add-widget-endpoint.php 2 years ago class.wpcom-json-api-autosave-post-v1-1-endpoint.php 5 years ago class.wpcom-json-api-bulk-delete-post-endpoint.php 4 years ago class.wpcom-json-api-bulk-restore-post-endpoint.php 2 years ago class.wpcom-json-api-bulk-update-comments-endpoint.php 3 years ago class.wpcom-json-api-comment-endpoint.php 1 year ago class.wpcom-json-api-delete-media-endpoint.php 4 years ago class.wpcom-json-api-delete-media-v1-1-endpoint.php 4 years ago class.wpcom-json-api-edit-media-v1-2-endpoint.php 2 years ago class.wpcom-json-api-get-autosave-v1-1-endpoint.php 5 years ago class.wpcom-json-api-get-comment-counts-endpoint.php 1 year ago class.wpcom-json-api-get-comment-endpoint.php 4 years ago class.wpcom-json-api-get-comment-history-endpoint.php 1 year ago class.wpcom-json-api-get-comments-tree-endpoint.php 4 years ago class.wpcom-json-api-get-comments-tree-v1-1-endpoint.php 4 years ago class.wpcom-json-api-get-comments-tree-v1-2-endpoint.php 4 years ago class.wpcom-json-api-get-customcss.php 2 years ago class.wpcom-json-api-get-media-endpoint.php 4 years ago class.wpcom-json-api-get-media-v1-1-endpoint.php 4 years ago class.wpcom-json-api-get-media-v1-2-endpoint.php 2 years ago class.wpcom-json-api-get-post-counts-v1-1-endpoint.php 2 years ago class.wpcom-json-api-get-post-endpoint.php 2 years ago class.wpcom-json-api-get-post-v1-1-endpoint.php 2 years ago class.wpcom-json-api-get-site-endpoint.php 1 year ago class.wpcom-json-api-get-site-v1-2-endpoint.php 1 year ago class.wpcom-json-api-get-taxonomies-endpoint.php 2 years ago class.wpcom-json-api-get-taxonomy-endpoint.php 4 years ago class.wpcom-json-api-get-term-endpoint.php 4 years ago class.wpcom-json-api-list-comments-endpoint.php 1 year ago class.wpcom-json-api-list-dropdown-pages-endpoint.php 3 years ago class.wpcom-json-api-list-embeds-endpoint.php 4 years ago class.wpcom-json-api-list-media-endpoint.php 2 years ago class.wpcom-json-api-list-media-v1-1-endpoint.php 2 years ago class.wpcom-json-api-list-media-v1-2-endpoint.php 2 years ago class.wpcom-json-api-list-post-type-taxonomies-endpoint.php 4 years ago class.wpcom-json-api-list-post-types-endpoint.php 3 years ago class.wpcom-json-api-list-posts-endpoint.php 1 year ago class.wpcom-json-api-list-posts-v1-1-endpoint.php 1 year ago class.wpcom-json-api-list-posts-v1-2-endpoint.php 1 year ago class.wpcom-json-api-list-roles-endpoint.php 1 year ago class.wpcom-json-api-list-shortcodes-endpoint.php 4 years ago class.wpcom-json-api-list-terms-endpoint.php 2 years ago class.wpcom-json-api-list-users-endpoint.php 1 year ago class.wpcom-json-api-menus-v1-1-endpoint.php 1 year ago class.wpcom-json-api-post-endpoint.php 2 years ago class.wpcom-json-api-post-v1-1-endpoint.php 2 years ago class.wpcom-json-api-render-embed-endpoint.php 2 years ago class.wpcom-json-api-render-embed-reversal-endpoint.php 2 years ago class.wpcom-json-api-render-endpoint.php 3 years ago class.wpcom-json-api-render-shortcode-endpoint.php 3 years ago class.wpcom-json-api-sharing-buttons-endpoint.php 2 years ago class.wpcom-json-api-site-settings-endpoint.php 1 year ago class.wpcom-json-api-site-settings-v1-2-endpoint.php 2 years ago class.wpcom-json-api-site-settings-v1-3-endpoint.php 2 years ago class.wpcom-json-api-site-settings-v1-4-endpoint.php 1 year ago class.wpcom-json-api-site-user-endpoint.php 1 year ago class.wpcom-json-api-taxonomy-endpoint.php 4 years ago class.wpcom-json-api-update-comment-endpoint.php 2 years ago class.wpcom-json-api-update-customcss.php 2 years ago class.wpcom-json-api-update-media-endpoint.php 4 years ago class.wpcom-json-api-update-media-v1-1-endpoint.php 2 years ago class.wpcom-json-api-update-post-endpoint.php 1 year ago class.wpcom-json-api-update-post-v1-1-endpoint.php 1 year ago class.wpcom-json-api-update-post-v1-2-endpoint.php 1 year ago class.wpcom-json-api-update-site-homepage-endpoint.php 4 years ago class.wpcom-json-api-update-site-logo-endpoint.php 2 years ago class.wpcom-json-api-update-taxonomy-endpoint.php 4 years ago class.wpcom-json-api-update-term-endpoint.php 4 years ago class.wpcom-json-api-update-user-endpoint.php 3 years ago class.wpcom-json-api-upload-media-endpoint.php 3 years ago class.wpcom-json-api-upload-media-v1-1-endpoint.php 1 year ago
class.wpcom-json-api-post-endpoint.php
648 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 /**
4 * Post Endpoint class.
5 */
6 abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
7 /**
8 * Post object format.
9 *
10 * @var array
11 */
12 public $post_object_format = array(
13 // explicitly document and cast all output
14 'ID' => '(int) The post ID.',
15 'site_ID' => '(int) The site ID.',
16 'author' => '(object>author) The author of the post.',
17 'date' => "(ISO 8601 datetime) The post's creation time.",
18 'modified' => "(ISO 8601 datetime) The post's most recent update time.",
19 'title' => '(HTML) <code>context</code> dependent.',
20 'URL' => '(URL) The full permalink URL to the post.',
21 'short_URL' => '(URL) The wp.me short URL.',
22 'content' => '(HTML) <code>context</code> dependent.',
23 'excerpt' => '(HTML) <code>context</code> dependent.',
24 'slug' => '(string) The name (slug) for the post, used in URLs.',
25 'guid' => '(string) The GUID for the post.',
26 'status' => array(
27 'publish' => 'The post is published.',
28 'draft' => 'The post is saved as a draft.',
29 'pending' => 'The post is pending editorial approval.',
30 'private' => 'The post is published privately',
31 'future' => 'The post is scheduled for future publishing.',
32 'trash' => 'The post is in the trash.',
33 'auto-draft' => 'The post is a placeholder for a new post.',
34 ),
35 'sticky' => '(bool) Is the post sticky?',
36 'password' => '(string) The plaintext password protecting the post, or, more likely, the empty string if the post is not password protected.',
37 'parent' => "(object>post_reference|false) A reference to the post's parent, if it has one.",
38 '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.",
39 'comments_open' => '(bool) Is the post open for comments?',
40 'pings_open' => '(bool) Is the post open for pingbacks, trackbacks?',
41 'likes_enabled' => '(bool) Is the post open to likes?',
42 'sharing_enabled' => '(bool) Should sharing buttons show on this post?',
43 'comment_count' => '(int) The number of comments for this post.',
44 'like_count' => '(int) The number of likes for this post.',
45 'i_like' => '(bool) Does the current user like this post?',
46 'is_reblogged' => '(bool) Did the current user reblog this post?',
47 'is_following' => '(bool) Is the current user following this blog?',
48 'global_ID' => '(string) A unique WordPress.com-wide representation of a post.',
49 'featured_image' => '(URL) The URL to the featured image for this post if it has one.',
50 'post_thumbnail' => '(object>attachment) The attachment object for the featured image if it has one.',
51 'format' => array(), // see constructor
52 'geo' => '(object>geo|false)',
53 'menu_order' => '(int) (Pages Only) The order pages should appear in.',
54 'publicize_URLs' => '(array:URL) Array of Facebook URLs published by this post.',
55 'tags' => '(object:tag) Hash of tags (keyed by tag name) applied to the post.',
56 'categories' => '(object:category) Hash of categories (keyed by category name) applied to the post.',
57 'attachments' => '(object:attachment) Hash of post attachments (keyed by attachment ID).',
58 '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.',
59 'meta' => '(object) API result meta data',
60 'current_user_can' => '(object) List of permissions. Note, deprecated in favor of `capabilities`',
61 'capabilities' => '(object) List of post-specific permissions for the user; publish_post, edit_post, delete_post',
62 );
63
64 /**
65 * Constructor function.
66 *
67 * @param string|array|object $args — Args.
68 */
69 public function __construct( $args ) {
70 if ( is_array( $this->post_object_format ) && isset( $this->post_object_format['format'] ) ) {
71 $this->post_object_format['format'] = get_post_format_strings();
72 }
73 if ( ! $this->response_format ) {
74 $this->response_format =& $this->post_object_format;
75 }
76 parent::__construct( $args );
77 }
78
79 /**
80 * Filter to replace the password form with a simple message that the post is protected.
81 *
82 * @return string
83 */
84 public function the_password_form() {
85 return __( 'This post is password protected.', 'jetpack' );
86 }
87
88 /**
89 * Get a post by a specified field and value
90 *
91 * @param string $field - the field.
92 * @param string $field_value - the field value.
93 * @param string $context Post use context (e.g. 'display').
94 * @return array|bool|WP_Error Post
95 **/
96 public function get_post_by( $field, $field_value, $context = 'display' ) {
97 global $blog_id;
98
99 /** This filter is documented in class.json-api-endpoints.php */
100 $is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
101
102 if ( defined( 'GEO_LOCATION__CLASS' ) && class_exists( GEO_LOCATION__CLASS ) ) {
103 $geo = call_user_func( array( GEO_LOCATION__CLASS, 'init' ) );
104 } else {
105 $geo = false;
106 }
107
108 if ( 'display' === $context ) {
109 $args = $this->query_args();
110 if ( isset( $args['content_width'] ) && $args['content_width'] ) {
111 $GLOBALS['content_width'] = (int) $args['content_width'];
112 }
113 }
114
115 switch ( $field ) {
116 case 'name':
117 $post_id = $this->get_post_id_by_name( $field_value );
118 if ( is_wp_error( $post_id ) ) {
119 return $post_id;
120 }
121 break;
122 default:
123 $post_id = (int) $field_value;
124 break;
125 }
126
127 $post = get_post( $post_id, OBJECT, $context );
128
129 if ( ! $post || is_wp_error( $post ) ) {
130 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
131 }
132
133 if ( ! $this->is_post_type_allowed( $post->post_type ) && ( ! function_exists( 'is_post_freshly_pressed' ) || ! is_post_freshly_pressed( $post->ID ) ) ) {
134 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
135 }
136
137 // Permissions
138 $capabilities = $this->get_current_user_capabilities( $post );
139
140 switch ( $context ) {
141 case 'edit':
142 if ( ! $capabilities['edit_post'] ) {
143 return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
144 }
145 break;
146 case 'display':
147 break;
148 default:
149 return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 );
150 }
151
152 $can_view = $this->user_can_view_post( $post->ID );
153 if ( ! $can_view || is_wp_error( $can_view ) ) {
154 return $can_view;
155 }
156
157 $GLOBALS['post'] = $post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
158
159 if ( 'display' === $context ) {
160 setup_postdata( $post );
161 }
162
163 $response = array();
164
165 $fields = null;
166 if ( 'display' === $context && ! empty( $this->api->query['fields'] ) ) {
167 $fields = array_fill_keys( array_map( 'trim', explode( ',', $this->api->query['fields'] ) ), true );
168 }
169
170 foreach ( array_keys( $this->post_object_format ) as $key ) {
171 if ( $fields !== null && ! isset( $fields[ $key ] ) ) {
172 continue;
173 }
174 switch ( $key ) {
175 case 'ID':
176 // explicitly cast all output
177 $response[ $key ] = (int) $post->ID;
178 break;
179 case 'site_ID':
180 $response[ $key ] = (int) $this->api->get_blog_id_for_output();
181 break;
182 case 'author':
183 $response[ $key ] = (object) $this->get_author( $post, 'edit' === $context && $capabilities['edit_post'] );
184 break;
185 case 'date':
186 $response[ $key ] = (string) $this->format_date( $post->post_date_gmt, $post->post_date );
187 break;
188 case 'modified':
189 $response[ $key ] = (string) $this->format_date( $post->post_modified_gmt, $post->post_modified );
190 break;
191 case 'title':
192 if ( 'display' === $context ) {
193 $response[ $key ] = (string) get_the_title( $post->ID );
194 } else {
195 $response[ $key ] = (string) htmlspecialchars_decode( $post->post_title, ENT_QUOTES );
196 }
197 break;
198 case 'URL':
199 if ( 'revision' === $post->post_type ) {
200 $response[ $key ] = (string) esc_url_raw( get_permalink( $post->post_parent ) );
201 } else {
202 $response[ $key ] = (string) esc_url_raw( get_permalink( $post->ID ) );
203 }
204 break;
205 case 'short_URL':
206 $response[ $key ] = (string) esc_url_raw( wp_get_shortlink( $post->ID ) );
207 break;
208 case 'content':
209 if ( 'display' === $context ) {
210 add_filter( 'the_password_form', array( $this, 'the_password_form' ) );
211 $response[ $key ] = (string) $this->get_the_post_content_for_display();
212 remove_filter( 'the_password_form', array( $this, 'the_password_form' ) );
213 } else {
214 $response[ $key ] = (string) $post->post_content;
215 }
216 break;
217 case 'excerpt':
218 if ( 'display' === $context ) {
219 add_filter( 'the_password_form', array( $this, 'the_password_form' ) );
220 ob_start();
221 the_excerpt();
222 $response[ $key ] = (string) ob_get_clean();
223 remove_filter( 'the_password_form', array( $this, 'the_password_form' ) );
224 } else {
225 $response[ $key ] = htmlspecialchars_decode( (string) $post->post_excerpt, ENT_QUOTES );
226 }
227 break;
228 case 'status':
229 $response[ $key ] = (string) get_post_status( $post->ID );
230 break;
231 case 'sticky':
232 $response[ $key ] = (bool) is_sticky( $post->ID );
233 break;
234 case 'slug':
235 $response[ $key ] = (string) $post->post_name;
236 break;
237 case 'guid':
238 $response[ $key ] = (string) $post->guid;
239 break;
240 case 'password':
241 $response[ $key ] = (string) $post->post_password;
242 if ( 'edit' === $context ) {
243 $response[ $key ] = htmlspecialchars_decode( (string) $response[ $key ], ENT_QUOTES );
244 }
245 break;
246 /** (object|false) */
247 case 'parent':
248 if ( $post->post_parent ) {
249 $parent = get_post( $post->post_parent );
250 if ( 'display' === $context ) {
251 $parent_title = (string) get_the_title( $parent->ID );
252 } else {
253 $parent_title = (string) htmlspecialchars_decode( $post->post_title, ENT_QUOTES );
254 }
255 $response[ $key ] = (object) array(
256 'ID' => (int) $parent->ID,
257 'type' => (string) $parent->post_type,
258 'link' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $parent->ID ),
259 'title' => $parent_title,
260 );
261 } else {
262 $response[ $key ] = false;
263 }
264 break;
265 case 'type':
266 $response[ $key ] = (string) $post->post_type;
267 break;
268 case 'comments_open':
269 $response[ $key ] = (bool) comments_open( $post->ID );
270 break;
271 case 'pings_open':
272 $response[ $key ] = (bool) pings_open( $post->ID );
273 break;
274 case 'likes_enabled':
275 /** This filter is documented in modules/likes.php */
276 $sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
277 $post_likes_switched = get_post_meta( $post->ID, 'switch_like_status', true );
278 $post_likes_enabled = $post_likes_switched || ( $sitewide_likes_enabled && $post_likes_switched !== '0' );
279 $response[ $key ] = (bool) $post_likes_enabled;
280 break;
281 case 'sharing_enabled':
282 $show = true;
283 /** This filter is documented in modules/sharedaddy/sharing-service.php */
284 $show = apply_filters( 'sharing_show', $show, $post );
285
286 $switched_status = get_post_meta( $post->ID, 'sharing_disabled', false );
287
288 if ( ! empty( $switched_status ) ) {
289 $show = false;
290 }
291 $response[ $key ] = (bool) $show;
292 break;
293 case 'comment_count':
294 $response[ $key ] = (int) $post->comment_count;
295 break;
296 case 'like_count':
297 $response[ $key ] = (int) $this->api->post_like_count( $blog_id, $post->ID );
298 break;
299 case 'i_like':
300 $response[ $key ] = (bool) $this->api->is_liked( $blog_id, $post->ID );
301 break;
302 case 'is_reblogged':
303 $response[ $key ] = (bool) $this->api->is_reblogged( $blog_id, $post->ID );
304 break;
305 case 'is_following':
306 $response[ $key ] = (bool) $this->api->is_following( $blog_id );
307 break;
308 case 'global_ID':
309 $response[ $key ] = (string) $this->api->add_global_ID( $blog_id, $post->ID );
310 break;
311 case 'featured_image':
312 if ( $is_jetpack && ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
313 $response[ $key ] = get_post_meta( $post->ID, '_jetpack_featured_image', true );
314 } else {
315 $image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' );
316 if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
317 $response[ $key ] = (string) $image_attributes[0];
318 } else {
319 $response[ $key ] = '';
320 }
321 }
322 break;
323 case 'post_thumbnail':
324 $response[ $key ] = null;
325
326 $thumb_id = get_post_thumbnail_id( $post->ID );
327 if ( ! empty( $thumb_id ) ) {
328 $attachment = get_post( $thumb_id );
329 if ( ! empty( $attachment ) ) {
330 $featured_image_object = $this->get_attachment( $attachment );
331 }
332
333 if ( ! empty( $featured_image_object ) ) {
334 $response[ $key ] = (object) $featured_image_object;
335 }
336 }
337 break;
338 case 'format':
339 $response[ $key ] = (string) get_post_format( $post->ID );
340 if ( ! $response[ $key ] ) {
341 $response[ $key ] = 'standard';
342 }
343 break;
344 /** (object|false) */
345 case 'geo':
346 if ( ! $geo ) {
347 $response[ $key ] = false;
348 } else {
349 $geo_data = $geo->get_geo( 'post', $post->ID );
350 $response[ $key ] = false;
351 if ( $geo_data ) {
352 $geo_data = array_intersect_key(
353 $geo_data,
354 array(
355 'latitude' => true,
356 'longitude' => true,
357 'address' => true,
358 'public' => true,
359 )
360 );
361 if ( $geo_data ) {
362 $response[ $key ] = (object) array(
363 'latitude' => isset( $geo_data['latitude'] ) ? (float) $geo_data['latitude'] : 0,
364 'longitude' => isset( $geo_data['longitude'] ) ? (float) $geo_data['longitude'] : 0,
365 'address' => isset( $geo_data['address'] ) ? (string) $geo_data['address'] : '',
366 );
367 } else {
368 $response[ $key ] = false;
369 }
370 // Private
371 if ( ! isset( $geo_data['public'] ) || ! $geo_data['public'] ) {
372 if ( 'edit' !== $context || ! $capabilities['edit_post'] ) {
373 // user can't access
374 $response[ $key ] = false;
375 }
376 }
377 }
378 }
379 break;
380 case 'menu_order':
381 $response[ $key ] = (int) $post->menu_order;
382 break;
383 case 'publicize_URLs':
384 $publicize_urls = array();
385 $publicize = get_post_meta( $post->ID, 'publicize_results', true );
386 if ( $publicize ) {
387 foreach ( $publicize as $service => $data ) {
388 switch ( $service ) {
389 // @todo Explore removing once Twitter has been removed from Publicize.
390 case 'twitter':
391 foreach ( $data as $datum ) {
392 $publicize_urls[] = esc_url_raw( "https://twitter.com/{$datum['user_id']}/status/{$datum['post_id']}" );
393 }
394 break;
395 case 'fb':
396 foreach ( $data as $datum ) {
397 $publicize_urls[] = esc_url_raw( "https://www.facebook.com/permalink.php?story_fbid={$datum['post_id']}&id={$datum['user_id']}" );
398 }
399 break;
400 }
401 }
402 }
403 $response[ $key ] = (array) $publicize_urls;
404 break;
405 case 'tags':
406 $response[ $key ] = array();
407 $terms = wp_get_post_tags( $post->ID );
408 foreach ( $terms as $term ) {
409 if ( ! empty( $term->name ) ) {
410 $response[ $key ][ $term->name ] = $this->format_taxonomy( $term, 'post_tag', 'display' );
411 }
412 }
413 $response[ $key ] = (object) $response[ $key ];
414 break;
415 case 'categories':
416 $response[ $key ] = array();
417 $terms = wp_get_object_terms( $post->ID, 'category', array( 'fields' => 'all' ) );
418 foreach ( $terms as $term ) {
419 if ( ! empty( $term->name ) ) {
420 $response[ $key ][ $term->name ] = $this->format_taxonomy( $term, 'category', 'display' );
421 }
422 }
423 $response[ $key ] = (object) $response[ $key ];
424 break;
425 case 'attachments':
426 $response[ $key ] = array();
427 $_attachments = get_posts(
428 array(
429 'post_parent' => $post->ID,
430 'post_status' => 'inherit',
431 'post_type' => 'attachment',
432 'posts_per_page' => 100,
433 )
434 );
435 foreach ( $_attachments as $attachment ) {
436 $response[ $key ][ $attachment->ID ] = $this->get_attachment( $attachment );
437 }
438 $response[ $key ] = (object) $response[ $key ];
439 break;
440 /** (array|false) */
441 case 'metadata':
442 $metadata = array();
443 foreach ( (array) has_meta( $post_id ) as $meta ) {
444 // Don't expose protected fields.
445 $show = false;
446 if ( WPCOM_JSON_API_Metadata::is_public( $meta['meta_key'] ) ) {
447 $show = true;
448 }
449 if ( current_user_can( 'edit_post_meta', $post_id, $meta['meta_key'] ) ) {
450 $show = true;
451 }
452
453 if (
454 in_array( $meta['meta_key'], Jetpack_SEO_Posts::POST_META_KEYS_ARRAY, true ) &&
455 ! Jetpack_SEO_Utils::is_enabled_jetpack_seo()
456 ) {
457 $show = false;
458 }
459
460 if ( ! $show ) {
461 continue;
462 }
463
464 $metadata[] = array(
465 'id' => $meta['meta_id'],
466 'key' => $meta['meta_key'],
467 'value' => maybe_unserialize( $meta['meta_value'] ),
468 );
469 }
470
471 if ( ! empty( $metadata ) ) {
472 $response[ $key ] = $metadata;
473 } else {
474 $response[ $key ] = false;
475 }
476 break;
477 case 'meta':
478 $response[ $key ] = (object) array(
479 'links' => (object) array(
480 'self' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $post->ID ),
481 'help' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $post->ID, 'help' ),
482 'site' => (string) $this->links->get_site_link( $this->api->get_blog_id_for_output() ),
483 'replies' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $post->ID, 'replies/' ),
484 'likes' => (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $post->ID, 'likes/' ),
485 ),
486 );
487 break;
488 case 'current_user_can':
489 $response[ $key ] = $capabilities;
490 break;
491 case 'capabilities':
492 $response[ $key ] = $capabilities;
493 break;
494
495 }
496 }
497
498 unset( $GLOBALS['post'] );
499 return $response;
500 }
501
502 /**
503 *
504 * Get the post content for display.
505 *
506 * No Blog ID parameter. No Post ID parameter. Depends on globals.
507 * Expects setup_postdata() to already have been run.
508 *
509 * @return string|false
510 */
511 public function get_the_post_content_for_display() {
512 global $pages, $page;
513
514 $old_pages = $pages;
515 $old_page = $page;
516
517 $content = implode( "\n\n", $pages );
518 $content = preg_replace( '/<!--more(.*?)?-->/', '', $content );
519 $pages = array( $content ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
520 $page = 1; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
521
522 ob_start();
523 the_content();
524 $return = ob_get_clean();
525
526 $pages = $old_pages; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
527 $page = $old_page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
528
529 return $return;
530 }
531
532 /**
533 * Gets the blog post.
534 *
535 * @param int $blog_id - the blog ID.
536 * @param int $post_id - the post ID.
537 * @param string $context - the context.
538 * @return array|bool|WP_Error Post
539 */
540 public function get_blog_post( $blog_id, $post_id, $context = 'display' ) {
541 $blog_id = $this->api->get_blog_id( $blog_id );
542 if ( ! $blog_id || is_wp_error( $blog_id ) ) {
543 return $blog_id;
544 }
545 switch_to_blog( $blog_id );
546 $post = $this->get_post_by( 'ID', $post_id, $context );
547 restore_current_blog();
548 return $post;
549 }
550
551 /**
552 * Supporting featured media in post endpoints. Currently on for wpcom blogs
553 * since it's calling WPCOM_JSON_API_Read_Endpoint methods which presently
554 * rely on wpcom specific functionality.
555 *
556 * @param WP_Post $post - the WP Post object.
557 * @return object list of featured media
558 */
559 public static function find_featured_media( &$post ) {
560
561 if ( class_exists( 'WPCOM_JSON_API_Read_Endpoint' ) ) {
562 return WPCOM_JSON_API_Read_Endpoint::find_featured_worthy_media( (array) $post );
563 } else {
564 return (object) array();
565 }
566 }
567
568 /**
569 * Returns attachment object.
570 *
571 * @param object $attachment attachment row.
572 *
573 * @return object
574 */
575 public function get_attachment( $attachment ) {
576 $metadata = wp_get_attachment_metadata( $attachment->ID );
577
578 $result = array(
579 'ID' => (int) $attachment->ID,
580 'URL' => (string) wp_get_attachment_url( $attachment->ID ),
581 'guid' => (string) $attachment->guid,
582 'mime_type' => (string) $attachment->post_mime_type,
583 'width' => (int) isset( $metadata['width'] ) ? $metadata['width'] : 0,
584 'height' => (int) isset( $metadata['height'] ) ? $metadata['height'] : 0,
585 );
586
587 if ( isset( $metadata['duration'] ) ) {
588 $result['duration'] = (int) $metadata['duration'];
589 }
590
591 return (object) apply_filters( 'get_attachment', $result );
592 }
593
594 /**
595 * Get post-specific user capabilities
596 *
597 * @param WP_Post $post - the WP_Post object.
598 *
599 * @return array - array of post-level permissions; 'publish_post', 'delete_post', 'edit_post'
600 */
601 public function get_current_user_capabilities( $post ) {
602 return array(
603 'publish_post' => current_user_can( 'publish_post', $post->ID ),
604 'delete_post' => current_user_can( 'delete_post', $post->ID ),
605 'edit_post' => current_user_can( 'edit_post', $post->ID ),
606 );
607 }
608
609 /**
610 * Get post ID by name
611 *
612 * Attempts to match name on post title and page path
613 *
614 * @param string $name - the name of the post.
615 *
616 * @return int|object Post ID on success, WP_Error object on failure
617 **/
618 protected function get_post_id_by_name( $name ) {
619 $name = sanitize_title( $name );
620
621 if ( ! $name ) {
622 return new WP_Error( 'invalid_post', 'Invalid post', 400 );
623 }
624
625 $posts = get_posts(
626 array(
627 'name' => $name,
628 'numberposts' => 1,
629 'post_type' => $this->_get_whitelisted_post_types(),
630 )
631 );
632
633 if ( ! $posts || ! isset( $posts[0]->ID ) || ! $posts[0]->ID ) {
634 $page = get_page_by_path( $name );
635
636 if ( ! $page ) {
637 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
638 }
639
640 $post_id = $page->ID;
641 } else {
642 $post_id = (int) $posts[0]->ID;
643 }
644
645 return $post_id;
646 }
647 }
648