PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.8-beta
Jetpack – WP Security, Backup, Speed, & Growth v15.8-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-list-posts-v1-2-endpoint.php
jetpack / json-endpoints Last commit date
jetpack 4 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 4 months 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 6 months ago class.wpcom-json-api-update-post-v1-1-endpoint.php 6 months ago class.wpcom-json-api-update-post-v1-2-endpoint.php 6 months 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-list-posts-v1-2-endpoint.php
505 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit( 0 );
5 }
6
7 /**
8 * List posts v1_2 endpoint.
9 */
10 new WPCOM_JSON_API_List_Posts_v1_2_Endpoint(
11 array(
12 'description' => 'Get a list of matching posts.',
13 'min_version' => '1.2',
14 'max_version' => '1.2',
15
16 'group' => 'posts',
17 'stat' => 'posts',
18
19 'method' => 'GET',
20 'path' => '/sites/%s/posts/',
21 'path_labels' => array(
22 '$site' => '(int|string) Site ID or domain',
23 ),
24 'rest_route' => '/posts',
25 'rest_min_jp_version' => '14.5-a.2',
26
27 'allow_fallback_to_jetpack_blog_token' => true,
28
29 'query_parameters' => array(
30 'number' => '(int=20) The number of posts to return. Limit: 100.',
31 'offset' => '(int=0) 0-indexed offset.',
32 'page' => '(int) Return the Nth 1-indexed page of posts. Takes precedence over the <code>offset</code> parameter.',
33 'page_handle' => '(string) A page handle, returned from a previous API call as a <code>meta.next_page</code> property. This is the most efficient way to fetch the next page of results.',
34 'order' => array(
35 'DESC' => 'Return posts in descending order. For dates, that means newest to oldest.',
36 'ASC' => 'Return posts in ascending order. For dates, that means oldest to newest.',
37 ),
38 'order_by' => array(
39 'date' => 'Order by the created time of each post.',
40 'modified' => 'Order by the modified time of each post.',
41 'title' => "Order lexicographically by the posts' titles.",
42 'comment_count' => 'Order by the number of comments for each post.',
43 'ID' => 'Order by post ID.',
44 ),
45 'after' => '(ISO 8601 datetime) Return posts dated after the specified datetime.',
46 'before' => '(ISO 8601 datetime) Return posts dated before the specified datetime.',
47 'modified_after' => '(ISO 8601 datetime) Return posts modified after the specified datetime.',
48 'modified_before' => '(ISO 8601 datetime) Return posts modified before the specified datetime.',
49 'tag' => '(string) Specify the tag name or slug.',
50 'category' => '(string) Specify the category name or slug.',
51 'term' => '(object:string) Specify comma-separated term slugs to search within, indexed by taxonomy slug.',
52 'type' => "(string) Specify the post type. Defaults to 'post', use 'any' to query for both posts and pages. Post types besides post and page need to be whitelisted using the <code>rest_api_allowed_post_types</code> filter.",
53 'exclude_private_types' => '(bool=false) Use this flag together with `type=any` to get only publicly accessible posts.',
54 'parent_id' => '(int) Returns only posts which are children of the specified post. Applies only to hierarchical post types.',
55 'include' => '(array:int|int) Includes the specified post ID(s) in the response',
56 'exclude' => '(array:int|int) Excludes the specified post ID(s) from the response',
57 'exclude_tree' => '(int) Excludes the specified post and all of its descendants from the response. Applies only to hierarchical post types.',
58 'status' => '(string) Comma-separated list of statuses for which to query, including any of: "publish", "private", "draft", "pending", "future", and "trash", or simply "any". Defaults to "publish"',
59 'sticky' => array(
60 'include' => 'Sticky posts are not excluded from the list.',
61 'exclude' => 'Sticky posts are excluded from the list.',
62 'require' => 'Only include sticky posts',
63 ),
64 'author' => "(int) Author's user ID",
65 'search' => '(string) Search query',
66 'meta_key' => '(string) Metadata key that the post should contain',
67 'meta_value' => '(string) Metadata value that the post should contain. Will only be applied if a `meta_key` is also given',
68 ),
69
70 'example_request' => 'https://public-api.wordpress.com/rest/v1.2/sites/en.blog.wordpress.com/posts/?number=2',
71 )
72 );
73
74 /**
75 * List posts v1_2 endpoint.
76 *
77 * /sites/%s/posts/ -> $blog_id
78 *
79 * @phan-constructor-used-for-side-effects
80 */
81 class WPCOM_JSON_API_List_Posts_v1_2_Endpoint extends WPCOM_JSON_API_List_Posts_v1_1_Endpoint { // phpcs:ignore
82
83 /**
84 * API callback.
85 *
86 * @param string $path - the path.
87 * @param string $blog_id - the blog ID.
88 */
89 public function callback( $path = '', $blog_id = 0 ) {
90 $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
91 if ( is_wp_error( $blog_id ) ) {
92 return $blog_id;
93 }
94
95 $args = $this->query_args();
96 $is_eligible_for_page_handle = true;
97 $site = $this->get_platform()->get_site( $blog_id );
98
99 if ( $args['number'] < 1 ) {
100 $args['number'] = 20;
101 } elseif ( 100 < $args['number'] ) {
102 return new WP_Error( 'invalid_number', 'The NUMBER parameter must be less than or equal to 100.', 400 );
103 }
104
105 if ( isset( $args['type'] ) ) {
106 // load all types on WPCOM, unless only built-in ones are requested.
107 if ( defined( 'IS_WPCOM' ) && IS_WPCOM && ! in_array( $args['type'], array( 'post', 'revision', 'page' ), true ) ) {
108 $this->load_theme_functions();
109 }
110
111 if ( ! $site->is_post_type_allowed( $args['type'] ) ) {
112 return new WP_Error( 'unknown_post_type', 'Unknown post type', 404 );
113 }
114
115 // Normalize post_type.
116 if ( 'any' === $args['type'] ) {
117 $whitelisted_post_types = $site->get_whitelisted_post_types();
118
119 if ( ! empty( $args['exclude_private_types'] ) ) {
120 $public_post_types = get_post_types( array( 'public' => true ) );
121 $args['type'] = array_intersect( $public_post_types, $whitelisted_post_types );
122 } else {
123 $args['type'] = $whitelisted_post_types;
124 }
125 }
126 } else {
127 // let's be explicit about defaulting to 'post'.
128 $args['type'] = 'post';
129 }
130
131 // make sure the user can read or edit the requested post type(s).
132 if ( is_array( $args['type'] ) ) {
133 $allowed_types = array();
134 foreach ( $args['type'] as $post_type ) {
135 if ( $site->current_user_can_access_post_type( $post_type, $args['context'] ) ) {
136 $allowed_types[] = $post_type;
137 }
138 }
139
140 if ( empty( $allowed_types ) ) {
141 return array(
142 'found' => 0,
143 'posts' => array(),
144 );
145 }
146 $args['type'] = $allowed_types;
147 } elseif ( ! $site->current_user_can_access_post_type( $args['type'], $args['context'] ) ) {
148 return array(
149 'found' => 0,
150 'posts' => array(),
151 );
152 }
153
154 // determine statuses.
155 $status = ( ! empty( $args['status'] ) ) ? explode( ',', $args['status'] ) : array( 'publish' );
156 if ( is_user_logged_in() ) {
157 $statuses_whitelist = array(
158 'publish',
159 'pending',
160 'draft',
161 'future',
162 'private',
163 'trash',
164 'any',
165 );
166 $status = array_intersect( $status, $statuses_whitelist );
167 } else {
168 // logged-out users can see only published posts.
169 $statuses_whitelist = array( 'publish', 'any' );
170 $status = array_intersect( $status, $statuses_whitelist );
171
172 if ( empty( $status ) ) {
173 // requested only protected statuses? nothing for you here.
174 return array(
175 'found' => 0,
176 'posts' => array(),
177 );
178 }
179 // clear it (AKA published only) because "any" includes protected.
180 $status = array();
181 }
182
183 $query = array(
184 'posts_per_page' => $args['number'],
185 'order' => $args['order'],
186 'orderby' => $args['order_by'],
187 'post_type' => $args['type'],
188 'post_status' => $status,
189 'post_parent' => isset( $args['parent_id'] ) ? $args['parent_id'] : null,
190 'author' => isset( $args['author'] ) && 0 < $args['author'] ? $args['author'] : null,
191 's' => isset( $args['search'] ) && '' !== $args['search'] ? $args['search'] : null,
192 'fields' => 'ids',
193 );
194
195 if ( ! is_user_logged_in() ) {
196 $query['has_password'] = false;
197 }
198
199 if ( isset( $args['include'] ) ) {
200 $query['post__in'] = is_array( $args['include'] ) ? $args['include'] : array( (int) $args['include'] );
201 }
202
203 if ( isset( $args['meta_key'] ) ) {
204 $show = false;
205 if ( WPCOM_JSON_API_Metadata::is_public( $args['meta_key'] ) ) {
206 $show = true;
207 }
208 if ( current_user_can( 'edit_post_meta', $query['post_type'], $args['meta_key'] ) ) {
209 $show = true;
210 }
211
212 if ( is_protected_meta( $args['meta_key'], 'post' ) && ! $show ) {
213 return new WP_Error( 'invalid_meta_key', 'Invalid meta key', 404 );
214 }
215
216 $meta = array( 'key' => $args['meta_key'] );
217 if ( isset( $args['meta_value'] ) ) {
218 $meta['value'] = $args['meta_value'];
219 }
220
221 $query['meta_query'] = array( $meta );
222 }
223
224 if ( 'include' === $args['sticky'] ) {
225 $query['ignore_sticky_posts'] = 1;
226 } elseif ( 'exclude' === $args['sticky'] ) {
227 $sticky = get_option( 'sticky_posts' );
228 if ( is_array( $sticky ) ) {
229 $query['post__not_in'] = $sticky;
230 }
231 } elseif ( 'require' === $args['sticky'] ) {
232 $sticky = get_option( 'sticky_posts' );
233 if ( is_array( $sticky ) && ! empty( $sticky ) ) {
234 $query['post__in'] = isset( $args['include'] ) ? array_merge( $query['post__in'], $sticky ) : $sticky;
235 } else {
236 // no sticky posts exist.
237 return array(
238 'found' => 0,
239 'posts' => array(),
240 );
241 }
242 }
243
244 if ( isset( $args['exclude'] ) ) {
245 $excluded_ids = (array) $args['exclude'];
246 $query['post__not_in'] = isset( $query['post__not_in'] ) ? array_merge( $query['post__not_in'], $excluded_ids ) : $excluded_ids;
247 }
248
249 if ( isset( $args['exclude_tree'] ) && is_post_type_hierarchical( $args['type'] ) ) {
250 // get_page_children is a misnomer; it supports all hierarchical post types.
251 $page_args = array(
252 'child_of' => $args['exclude_tree'],
253 'post_type' => $args['type'],
254 // since we're looking for things to exclude, be aggressive.
255 'post_status' => 'publish,draft,pending,private,future,trash',
256 );
257 $post_descendants = get_pages( $page_args );
258
259 $exclude_tree = array( $args['exclude_tree'] );
260 foreach ( $post_descendants as $child ) {
261 $exclude_tree[] = $child->ID;
262 }
263
264 $query['post__not_in'] = isset( $query['post__not_in'] ) ? array_merge( $query['post__not_in'], $exclude_tree ) : $exclude_tree;
265 }
266
267 if ( isset( $args['category'] ) ) {
268 $category = get_term_by( 'slug', $args['category'], 'category' );
269 if ( false === $category ) {
270 $query['category_name'] = $args['category'];
271 } else {
272 $query['cat'] = $category->term_id;
273 }
274 }
275
276 if ( isset( $args['tag'] ) ) {
277 $query['tag'] = $args['tag'];
278 }
279
280 if ( ! empty( $args['term'] ) ) {
281 $query['tax_query'] = array();
282 foreach ( $args['term'] as $taxonomy => $slug ) {
283 $taxonomy_object = get_taxonomy( $taxonomy );
284 if ( false === $taxonomy_object || ( ! $taxonomy_object->public &&
285 ! current_user_can( $taxonomy_object->cap->assign_terms ) ) ) {
286 continue;
287 }
288
289 $query['tax_query'][] = array(
290 'taxonomy' => $taxonomy,
291 'field' => 'slug',
292 'terms' => explode( ',', $slug ),
293 );
294 }
295 }
296
297 if ( isset( $args['page'] ) ) {
298 if ( $args['page'] < 1 ) {
299 $args['page'] = 1;
300 }
301
302 $query['paged'] = $args['page'];
303 if ( 1 !== $query['paged'] ) {
304 $is_eligible_for_page_handle = false;
305 }
306 } else {
307 if ( $args['offset'] < 0 ) {
308 $args['offset'] = 0;
309 }
310
311 $query['offset'] = $args['offset'];
312 if ( 0 !== $query['offset'] ) {
313 $is_eligible_for_page_handle = false;
314 }
315 }
316
317 if ( isset( $args['before'] ) ) {
318 $this->date_range['before'] = $args['before'];
319 }
320 if ( isset( $args['after'] ) ) {
321 $this->date_range['after'] = $args['after'];
322 }
323
324 if ( isset( $args['modified_before_gmt'] ) ) {
325 $this->modified_range['before'] = $args['modified_before_gmt'];
326 }
327 if ( isset( $args['modified_after_gmt'] ) ) {
328 $this->modified_range['after'] = $args['modified_after_gmt'];
329 }
330
331 if ( $this->date_range ) {
332 add_filter( 'posts_where', array( $this, 'handle_date_range' ) );
333 }
334
335 if ( $this->modified_range ) {
336 add_filter( 'posts_where', array( $this, 'handle_modified_range' ) );
337 }
338
339 if ( isset( $args['page_handle'] ) ) {
340 $page_handle = wp_parse_args( $args['page_handle'] );
341 if ( isset( $page_handle['value'] ) && isset( $page_handle['id'] ) ) {
342 // we have a valid looking page handle.
343 $this->page_handle = $page_handle;
344 add_filter( 'posts_where', array( $this, 'handle_where_for_page_handle' ) );
345 }
346 }
347
348 /**
349 * 'column' necessary for the me/posts endpoint (which extends sites/$site/posts).
350 * Would need to be added to the sites/$site/posts definition if we ever want to
351 * use it there.
352 */
353 $column_whitelist = array( 'post_modified_gmt' );
354 if ( isset( $args['column'] ) && in_array( $args['column'], $column_whitelist, true ) ) {
355 $query['column'] = $args['column'];
356 }
357
358 $this->performed_query = $query;
359 add_filter( 'posts_orderby', array( $this, 'handle_orderby_for_page_handle' ) );
360
361 $wp_query = new WP_Query( $query );
362
363 remove_filter( 'posts_orderby', array( $this, 'handle_orderby_for_page_handle' ) );
364
365 if ( $this->date_range ) {
366 remove_filter( 'posts_where', array( $this, 'handle_date_range' ) );
367 $this->date_range = array();
368 }
369
370 if ( $this->modified_range ) {
371 remove_filter( 'posts_where', array( $this, 'handle_modified_range' ) );
372 $this->modified_range = array();
373 }
374
375 if ( $this->page_handle ) {
376 remove_filter( 'posts_where', array( $this, 'handle_where_for_page_handle' ) );
377
378 }
379
380 $return = array();
381 $excluded_count = 0;
382 foreach ( array_keys( $this->response_format ) as $key ) {
383 switch ( $key ) {
384 case 'found':
385 $return[ $key ] = (int) $wp_query->found_posts;
386 break;
387 case 'posts':
388 $posts = array();
389 foreach ( $wp_query->posts as $post_ID ) {
390 $the_post = $this->get_post_by( 'ID', $post_ID, $args['context'] );
391 if ( $the_post && ! is_wp_error( $the_post ) ) {
392 $posts[] = $the_post;
393 } else {
394 ++$excluded_count;
395 }
396 }
397
398 if ( $posts ) {
399 /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
400 do_action( 'wpcom_json_api_objects', 'posts', count( $posts ) );
401 }
402
403 $return[ $key ] = $posts;
404 break;
405
406 case 'meta':
407 if ( ! is_array( $args['type'] ) ) {
408 $return[ $key ] = (object) array(
409 'links' => (object) array(
410 'counts' => (string) $this->links->get_site_link( $blog_id, 'post-counts/' . $args['type'] ),
411 ),
412 );
413 }
414
415 if ( $is_eligible_for_page_handle && $return['posts'] && is_array( $return['posts'] ) ) {
416 $last_post = end( $return['posts'] );
417 reset( $return['posts'] );
418 $post_count = is_countable( $return['posts'] ) ? count( $return['posts'] ) : 0;
419 if ( ( $return['found'] > $post_count ) && $last_post ) {
420 if ( ! isset( $return[ $key ] ) ) {
421 $return[ $key ] = (object) array();
422 }
423
424 $handle = $this->build_page_handle( $last_post, $query );
425 if ( $handle !== null ) {
426 $return[ $key ]->next_page = $handle;
427 }
428 }
429 }
430
431 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
432 if ( ! isset( $return[ $key ] ) ) {
433 $return[ $key ] = new stdClass();
434 }
435 $return[ $key ]->wpcom = true;
436 }
437
438 break;
439 }
440 }
441
442 $return['found'] -= $excluded_count;
443
444 return $return;
445 }
446
447 /**
448 * Build page handle.
449 *
450 * @param array $post - the post.
451 * @param array $query - the query.
452 */
453 public function build_page_handle( $post, $query ) {
454 $column = $query['orderby'];
455 if ( ! $column ) {
456 $column = 'date';
457 }
458 if ( ! isset( $post['ID'] ) || ! isset( $post[ $column ] ) ) {
459 return null;
460 }
461 return build_query(
462 array(
463 'value' => rawurlencode( $post[ $column ] ),
464 'id' => $post['ID'],
465 )
466 );
467 }
468
469 /**
470 * Build the date range query.
471 *
472 * @param string $column - the database column.
473 * @param array $range - the date range.
474 * @param string $where - sql where clause.
475 */
476 public function build_date_range_query( $column, $range, $where ) {
477 global $wpdb;
478
479 switch ( count( $range ) ) {
480 case 2:
481 $where .= $wpdb->prepare(
482 " AND `$wpdb->posts`.$column >= CAST( %s AS DATETIME ) AND `$wpdb->posts`.$column < CAST( %s AS DATETIME ) ", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
483 $range['after'],
484 $range['before']
485 );
486 break;
487 case 1:
488 if ( isset( $range['before'] ) ) {
489 $where .= $wpdb->prepare(
490 " AND `$wpdb->posts`.$column < CAST( %s AS DATETIME ) ", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
491 $range['before']
492 );
493 } else {
494 $where .= $wpdb->prepare(
495 " AND `$wpdb->posts`.$column > CAST( %s AS DATETIME ) ", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
496 $range['after']
497 );
498 }
499 break;
500 }
501
502 return $where;
503 }
504 }
505