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