WPCOM_JSON_API_List_Posts_Endpoint

The Jetpack by WordPress.com WPCOM JSON API List Posts Endpoint class.

Defined (1)

The class is defined in the following location(s).

/json-endpoints/class.wpcom-json-api-list-posts-endpoint.php  
  1. class WPCOM_JSON_API_List_Posts_Endpoint extends WPCOM_JSON_API_Post_Endpoint { 
  2. public $date_range = array(); 
  3.  
  4. public $response_format = array( 
  5. 'found' => '(int) The total number of posts found that match the request (ignoring limits, offsets, and pagination).',  
  6. 'posts' => '(array:post) An array of post objects.',  
  7. ); 
  8.  
  9. // /sites/%s/posts/ -> $blog_id 
  10. function callback( $path = '', $blog_id = 0 ) { 
  11. $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) ); 
  12. if ( is_wp_error( $blog_id ) ) { 
  13. return $blog_id; 
  14.  
  15. $args = $this->query_args(); 
  16.  
  17. if ( $args['number'] < 1 ) { 
  18. $args['number'] = 20; 
  19. } elseif ( 100 < $args['number'] ) { 
  20. return new WP_Error( 'invalid_number', 'The NUMBER parameter must be less than or equal to 100.', 400 ); 
  21.  
  22. if ( isset( $args['type'] ) && ! $this->is_post_type_allowed( $args['type'] ) ) { 
  23. return new WP_Error( 'unknown_post_type', 'Unknown post type', 404 ); 
  24.  
  25. // Normalize post_type 
  26. if ( isset( $args['type'] ) && 'any' == $args['type'] ) { 
  27. if ( version_compare( $this->api->version, '1.1', '<' ) ) { 
  28. $args['type'] = array( 'post', 'page' ); 
  29. } else { // 1.1+ 
  30. $args['type'] = $this->_get_whitelisted_post_types(); 
  31.  
  32. // determine statuses 
  33. $status = $args['status']; 
  34. $status = ( $status ) ? explode( ', ', $status ) : array( 'publish' ); 
  35. if ( is_user_logged_in() ) { 
  36. $statuses_whitelist = array( 
  37. 'publish',  
  38. 'pending',  
  39. 'draft',  
  40. 'future',  
  41. 'private',  
  42. 'trash',  
  43. 'any',  
  44. ); 
  45. $status = array_intersect( $status, $statuses_whitelist ); 
  46. } else { 
  47. // logged-out users can see only published posts 
  48. $statuses_whitelist = array( 'publish', 'any' ); 
  49. $status = array_intersect( $status, $statuses_whitelist ); 
  50.  
  51. if ( empty( $status ) ) { 
  52. // requested only protected statuses? nothing for you here 
  53. return array( 'found' => 0, 'posts' => array() ); 
  54. // clear it (AKA published only) because "any" includes protected 
  55. $status = array(); 
  56.  
  57. // let's be explicit about defaulting to 'post' 
  58. $args['type'] = isset( $args['type'] ) ? $args['type'] : 'post'; 
  59.  
  60. // make sure the user can read or edit the requested post type(s) 
  61. if ( is_array( $args['type'] ) ) { 
  62. $allowed_types = array(); 
  63. foreach ( $args['type'] as $post_type ) { 
  64. if ( $this->current_user_can_access_post_type( $post_type, $args['context'] ) ) { 
  65. $allowed_types[] = $post_type; 
  66.  
  67. if ( empty( $allowed_types ) ) { 
  68. return array( 'found' => 0, 'posts' => array() ); 
  69. $args['type'] = $allowed_types; 
  70. else { 
  71. if ( ! $this->current_user_can_access_post_type( $args['type'], $args['context'] ) ) { 
  72. return array( 'found' => 0, 'posts' => array() ); 
  73.  
  74. $query = array( 
  75. 'posts_per_page' => $args['number'],  
  76. 'order' => $args['order'],  
  77. 'orderby' => $args['order_by'],  
  78. 'post_type' => $args['type'],  
  79. 'post_status' => $status,  
  80. 'post_parent' => isset( $args['parent_id'] ) ? $args['parent_id'] : null,  
  81. 'author' => isset( $args['author'] ) && 0 < $args['author'] ? $args['author'] : null,  
  82. 's' => isset( $args['search'] ) ? $args['search'] : null,  
  83. 'fields' => 'ids',  
  84. ); 
  85.  
  86. if ( ! is_user_logged_in () ) { 
  87. $query['has_password'] = false; 
  88.  
  89. if ( isset( $args['meta_key'] ) ) { 
  90. $show = false; 
  91. if ( $this->is_metadata_public( $args['meta_key'] ) ) 
  92. $show = true; 
  93. if ( current_user_can( 'edit_post_meta', $query['post_type'], $args['meta_key'] ) ) 
  94. $show = true; 
  95.  
  96. if ( is_protected_meta( $args['meta_key'], 'post' ) && ! $show ) 
  97. return new WP_Error( 'invalid_meta_key', 'Invalid meta key', 404 ); 
  98.  
  99. $meta = array( 'key' => $args['meta_key'] ); 
  100. if ( isset( $args['meta_value'] ) ) 
  101. $meta['value'] = $args['meta_value']; 
  102.  
  103. $query['meta_query'] = array( $meta ); 
  104.  
  105. if ( 
  106. isset( $args['sticky'] ) 
  107. && 
  108. ( $sticky = get_option( 'sticky_posts' ) ) 
  109. && 
  110. is_array( $sticky ) 
  111. ) { 
  112. if ( $args['sticky'] ) { 
  113. $query['post__in'] = $sticky; 
  114. } else { 
  115. $query['post__not_in'] = $sticky; 
  116. $query['ignore_sticky_posts'] = 1; 
  117. } else { 
  118. $query['post__not_in'] = $sticky; 
  119. $query['ignore_sticky_posts'] = 1; 
  120.  
  121. if ( isset( $args['exclude'] ) ) { 
  122. $query['post__not_in'] = array_merge( $query['post__not_in'], (array) $args['exclude'] ); 
  123.  
  124. if ( isset( $args['exclude_tree'] ) && is_post_type_hierarchical( $args['type'] ) ) { 
  125. // get_page_children is a misnomer; it supports all hierarchical post types 
  126. $page_args = array( 
  127. 'child_of' => $args['exclude_tree'],  
  128. 'post_type' => $args['type'],  
  129. // since we're looking for things to exclude, be aggressive 
  130. 'post_status' => 'publish, draft, pending, private, future, trash',  
  131. ); 
  132. $post_descendants = get_pages( $page_args ); 
  133.  
  134. $exclude_tree = array( $args['exclude_tree'] ); 
  135. foreach ( $post_descendants as $child ) { 
  136. $exclude_tree[] = $child->ID; 
  137.  
  138. $query['post__not_in'] = isset( $query['post__not_in'] ) ? array_merge( $query['post__not_in'], $exclude_tree ) : $exclude_tree; 
  139.  
  140. if ( isset( $args['category'] ) ) { 
  141. $category = get_term_by( 'slug', $args['category'], 'category' ); 
  142. if ( $category === false) { 
  143. $query['category_name'] = $args['category']; 
  144. } else { 
  145. $query['cat'] = $category->term_id; 
  146.  
  147. if ( isset( $args['tag'] ) ) { 
  148. $query['tag'] = $args['tag']; 
  149.  
  150. if ( isset( $args['page'] ) ) { 
  151. if ( $args['page'] < 1 ) { 
  152. $args['page'] = 1; 
  153.  
  154. $query['paged'] = $args['page']; 
  155. } else { 
  156. if ( $args['offset'] < 0 ) { 
  157. $args['offset'] = 0; 
  158.  
  159. $query['offset'] = $args['offset']; 
  160.  
  161. if ( isset( $args['before'] ) ) { 
  162. $this->date_range['before'] = $args['before']; 
  163. if ( isset( $args['after'] ) ) { 
  164. $this->date_range['after'] = $args['after']; 
  165.  
  166. if ( $this->date_range ) { 
  167. add_filter( 'posts_where', array( $this, 'handle_date_range' ) ); 
  168.  
  169. /** 
  170. * 'column' necessary for the me/posts endpoint (which extends sites/$site/posts). 
  171. * Would need to be added to the sites/$site/posts definition if we ever want to 
  172. * use it there. 
  173. */ 
  174. $column_whitelist = array( 'post_modified_gmt' ); 
  175. if ( isset( $args['column'] ) && in_array( $args['column'], $column_whitelist ) ) { 
  176. $query['column'] = $args['column']; 
  177.  
  178. $wp_query = new WP_Query( $query ); 
  179. if ( $this->date_range ) { 
  180. remove_filter( 'posts_where', array( $this, 'handle_date_range' ) ); 
  181. $this->date_range = array(); 
  182.  
  183. $return = array(); 
  184. $excluded_count = 0; 
  185. foreach ( array_keys( $this->response_format ) as $key ) { 
  186. switch ( $key ) { 
  187. case 'found' : 
  188. $return[$key] = (int) $wp_query->found_posts; 
  189. break; 
  190. case 'posts' : 
  191. $posts = array(); 
  192. foreach ( $wp_query->posts as $post_ID ) { 
  193. $the_post = $this->get_post_by( 'ID', $post_ID, $args['context'] ); 
  194. if ( $the_post && ! is_wp_error( $the_post ) ) { 
  195. $posts[] = $the_post; 
  196. } else { 
  197. $excluded_count++; 
  198.  
  199. if ( $posts ) { 
  200. /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */ 
  201. do_action( 'wpcom_json_api_objects', 'posts', count( $posts ) ); 
  202.  
  203. $return[$key] = $posts; 
  204. break; 
  205.  
  206. $return['found'] -= $excluded_count; 
  207.  
  208. return $return; 
  209.  
  210. function handle_date_range( $where ) { 
  211. global $wpdb; 
  212.  
  213. switch ( count( $this->date_range ) ) { 
  214. case 2 : 
  215. $where .= $wpdb->prepare( 
  216. " AND `$wpdb->posts`.post_date BETWEEN CAST( %s AS DATETIME ) AND CAST( %s AS DATETIME ) ",  
  217. $this->date_range['after'],  
  218. $this->date_range['before'] 
  219. ); 
  220. break; 
  221. case 1 : 
  222. if ( isset( $this->date_range['before'] ) ) { 
  223. $where .= $wpdb->prepare( 
  224. " AND `$wpdb->posts`.post_date <= CAST( %s AS DATETIME ) ",  
  225. $this->date_range['before'] 
  226. ); 
  227. } else { 
  228. $where .= $wpdb->prepare( 
  229. " AND `$wpdb->posts`.post_date >= CAST( %s AS DATETIME ) ",  
  230. $this->date_range['after'] 
  231. ); 
  232. break; 
  233.  
  234. return $where;