testcase-rest-post-type-controller.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <?php
  2. abstract class WP_Test_REST_Post_Type_Controller_Testcase extends WP_Test_REST_Controller_Testcase {
  3. protected function check_post_data( $post, $data, $context, $links ) {
  4. $post_type_obj = get_post_type_object( $post->post_type );
  5. // Standard fields.
  6. $this->assertSame( $post->ID, $data['id'] );
  7. $this->assertSame( $post->post_name, $data['slug'] );
  8. $this->assertSame( get_permalink( $post->ID ), $data['link'] );
  9. if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) {
  10. $post_date_gmt = gmdate( 'Y-m-d H:i:s', strtotime( $post->post_date ) - ( get_option( 'gmt_offset' ) * 3600 ) );
  11. $this->assertSame( mysql_to_rfc3339( $post_date_gmt ), $data['date_gmt'] );
  12. } else {
  13. $this->assertSame( mysql_to_rfc3339( $post->post_date_gmt ), $data['date_gmt'] );
  14. }
  15. $this->assertSame( mysql_to_rfc3339( $post->post_date ), $data['date'] );
  16. if ( '0000-00-00 00:00:00' === $post->post_modified_gmt ) {
  17. $post_modified_gmt = gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) - ( get_option( 'gmt_offset' ) * 3600 ) );
  18. $this->assertSame( mysql_to_rfc3339( $post_modified_gmt ), $data['modified_gmt'] );
  19. } else {
  20. $this->assertSame( mysql_to_rfc3339( $post->post_modified_gmt ), $data['modified_gmt'] );
  21. }
  22. $this->assertSame( mysql_to_rfc3339( $post->post_modified ), $data['modified'] );
  23. // Author.
  24. if ( post_type_supports( $post->post_type, 'author' ) ) {
  25. $this->assertEquals( $post->post_author, $data['author'] );
  26. } else {
  27. $this->assertEmpty( $data['author'] );
  28. }
  29. // Post parent.
  30. if ( $post_type_obj->hierarchical ) {
  31. $this->assertArrayHasKey( 'parent', $data );
  32. if ( $post->post_parent ) {
  33. if ( is_int( $data['parent'] ) ) {
  34. $this->assertSame( $post->post_parent, $data['parent'] );
  35. } else {
  36. $this->assertSame( $post->post_parent, $data['parent']['id'] );
  37. $this->check_get_post_response( $data['parent'], get_post( $data['parent']['id'] ), 'view-parent' );
  38. }
  39. } else {
  40. $this->assertEmpty( $data['parent'] );
  41. }
  42. } else {
  43. $this->assertFalse( isset( $data['parent'] ) );
  44. }
  45. // Page attributes.
  46. if ( $post_type_obj->hierarchical && post_type_supports( $post->post_type, 'page-attributes' ) ) {
  47. $this->assertSame( $post->menu_order, $data['menu_order'] );
  48. } else {
  49. $this->assertFalse( isset( $data['menu_order'] ) );
  50. }
  51. // Comments.
  52. if ( post_type_supports( $post->post_type, 'comments' ) ) {
  53. $this->assertSame( $post->comment_status, $data['comment_status'] );
  54. $this->assertSame( $post->ping_status, $data['ping_status'] );
  55. } else {
  56. $this->assertFalse( isset( $data['comment_status'] ) );
  57. $this->assertFalse( isset( $data['ping_status'] ) );
  58. }
  59. if ( 'post' === $post->post_type ) {
  60. $this->assertSame( is_sticky( $post->ID ), $data['sticky'] );
  61. }
  62. if ( 'post' === $post->post_type && 'edit' === $context ) {
  63. $this->assertSame( $post->post_password, $data['password'] );
  64. }
  65. if ( 'page' === $post->post_type ) {
  66. $this->assertSame( get_page_template_slug( $post->ID ), $data['template'] );
  67. }
  68. if ( post_type_supports( $post->post_type, 'thumbnail' ) ) {
  69. $this->assertSame( (int) get_post_thumbnail_id( $post->ID ), $data['featured_media'] );
  70. } else {
  71. $this->assertFalse( isset( $data['featured_media'] ) );
  72. }
  73. // Check post format.
  74. if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
  75. $post_format = get_post_format( $post->ID );
  76. if ( empty( $post_format ) ) {
  77. $this->assertSame( 'standard', $data['format'] );
  78. } else {
  79. $this->assertSame( get_post_format( $post->ID ), $data['format'] );
  80. }
  81. } else {
  82. $this->assertFalse( isset( $data['format'] ) );
  83. }
  84. // Check filtered values.
  85. if ( post_type_supports( $post->post_type, 'title' ) ) {
  86. add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
  87. $this->assertSame( get_the_title( $post->ID ), $data['title']['rendered'] );
  88. remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
  89. if ( 'edit' === $context ) {
  90. $this->assertSame( $post->post_title, $data['title']['raw'] );
  91. } else {
  92. $this->assertFalse( isset( $data['title']['raw'] ) );
  93. }
  94. } else {
  95. $this->assertFalse( isset( $data['title'] ) );
  96. }
  97. if ( post_type_supports( $post->post_type, 'editor' ) ) {
  98. // TODO: Apply content filter for more accurate testing.
  99. if ( ! $post->post_password ) {
  100. $this->assertSame( wpautop( $post->post_content ), $data['content']['rendered'] );
  101. }
  102. if ( 'edit' === $context ) {
  103. $this->assertSame( $post->post_content, $data['content']['raw'] );
  104. } else {
  105. $this->assertFalse( isset( $data['content']['raw'] ) );
  106. }
  107. } else {
  108. $this->assertFalse( isset( $data['content'] ) );
  109. }
  110. if ( post_type_supports( $post->post_type, 'excerpt' ) ) {
  111. if ( empty( $post->post_password ) ) {
  112. // TODO: Apply excerpt filter for more accurate testing.
  113. $this->assertSame( wpautop( $post->post_excerpt ), $data['excerpt']['rendered'] );
  114. } else {
  115. // TODO: Better testing for excerpts for password protected posts.
  116. }
  117. if ( 'edit' === $context ) {
  118. $this->assertSame( $post->post_excerpt, $data['excerpt']['raw'] );
  119. } else {
  120. $this->assertFalse( isset( $data['excerpt']['raw'] ) );
  121. }
  122. } else {
  123. $this->assertFalse( isset( $data['excerpt'] ) );
  124. }
  125. $this->assertSame( $post->post_status, $data['status'] );
  126. $this->assertSame( $post->guid, $data['guid']['rendered'] );
  127. if ( 'edit' === $context ) {
  128. $this->assertSame( $post->guid, $data['guid']['raw'] );
  129. }
  130. $taxonomies = wp_list_filter( get_object_taxonomies( $post->post_type, 'objects' ), array( 'show_in_rest' => true ) );
  131. foreach ( $taxonomies as $taxonomy ) {
  132. $this->assertTrue( isset( $data[ $taxonomy->rest_base ] ) );
  133. $terms = wp_get_object_terms( $post->ID, $taxonomy->name, array( 'fields' => 'ids' ) );
  134. sort( $terms );
  135. sort( $data[ $taxonomy->rest_base ] );
  136. $this->assertSame( $terms, $data[ $taxonomy->rest_base ] );
  137. }
  138. // Test links.
  139. if ( $links ) {
  140. $links = test_rest_expand_compact_links( $links );
  141. $post_type = get_post_type_object( $data['type'] );
  142. $this->assertSame( $links['self'][0]['href'], rest_url( 'wp/v2/' . $post_type->rest_base . '/' . $data['id'] ) );
  143. $this->assertSame( $links['collection'][0]['href'], rest_url( 'wp/v2/' . $post_type->rest_base ) );
  144. $this->assertSame( $links['about'][0]['href'], rest_url( 'wp/v2/types/' . $data['type'] ) );
  145. if ( post_type_supports( $post->post_type, 'author' ) && $data['author'] ) {
  146. $this->assertSame( $links['author'][0]['href'], rest_url( 'wp/v2/users/' . $data['author'] ) );
  147. }
  148. if ( post_type_supports( $post->post_type, 'comments' ) ) {
  149. $this->assertSame( $links['replies'][0]['href'], add_query_arg( 'post', $data['id'], rest_url( 'wp/v2/comments' ) ) );
  150. }
  151. if ( post_type_supports( $post->post_type, 'revisions' ) ) {
  152. $this->assertSame( $links['version-history'][0]['href'], rest_url( 'wp/v2/' . $post_type->rest_base . '/' . $data['id'] . '/revisions' ) );
  153. }
  154. if ( $post_type->hierarchical && ! empty( $data['parent'] ) ) {
  155. $this->assertSame( $links['up'][0]['href'], rest_url( 'wp/v2/' . $post_type->rest_base . '/' . $data['parent'] ) );
  156. }
  157. if ( ! in_array( $data['type'], array( 'attachment', 'nav_menu_item', 'revision' ), true ) ) {
  158. $this->assertSame( $links['https://api.w.org/attachment'][0]['href'], add_query_arg( 'parent', $data['id'], rest_url( 'wp/v2/media' ) ) );
  159. }
  160. if ( ! empty( $data['featured_media'] ) ) {
  161. $this->assertSame( $links['https://api.w.org/featuredmedia'][0]['href'], rest_url( 'wp/v2/media/' . $data['featured_media'] ) );
  162. }
  163. $num = 0;
  164. foreach ( $taxonomies as $key => $taxonomy ) {
  165. $this->assertSame( $taxonomy->name, $links['https://api.w.org/term'][ $num ]['attributes']['taxonomy'] );
  166. $this->assertSame( add_query_arg( 'post', $data['id'], rest_url( 'wp/v2/' . $taxonomy->rest_base ) ), $links['https://api.w.org/term'][ $num ]['href'] );
  167. $num++;
  168. }
  169. }
  170. }
  171. protected function check_get_posts_response( $response, $context = 'view' ) {
  172. $this->assertNotWPError( $response );
  173. $response = rest_ensure_response( $response );
  174. $this->assertSame( 200, $response->get_status() );
  175. $headers = $response->get_headers();
  176. $this->assertArrayHasKey( 'X-WP-Total', $headers );
  177. $this->assertArrayHasKey( 'X-WP-TotalPages', $headers );
  178. $all_data = $response->get_data();
  179. foreach ( $all_data as $data ) {
  180. $post = get_post( $data['id'] );
  181. // As the links for the post are "response_links" format in the data array,
  182. // we have to pull them out and parse them.
  183. $links = $data['_links'];
  184. foreach ( $links as &$links_array ) {
  185. foreach ( $links_array as &$link ) {
  186. $attributes = array_diff_key(
  187. $link,
  188. array(
  189. 'href' => 1,
  190. 'name' => 1,
  191. )
  192. );
  193. $link = array_diff_key( $link, $attributes );
  194. $link['attributes'] = $attributes;
  195. }
  196. }
  197. $this->check_post_data( $post, $data, $context, $links );
  198. }
  199. }
  200. protected function check_get_post_response( $response, $context = 'view' ) {
  201. $this->assertNotWPError( $response );
  202. $response = rest_ensure_response( $response );
  203. $this->assertSame( 200, $response->get_status() );
  204. $data = $response->get_data();
  205. $post = get_post( $data['id'] );
  206. $this->check_post_data( $post, $data, $context, $response->get_links() );
  207. }
  208. protected function check_create_post_response( $response ) {
  209. $this->assertNotWPError( $response );
  210. $response = rest_ensure_response( $response );
  211. $this->assertSame( 201, $response->get_status() );
  212. $headers = $response->get_headers();
  213. $this->assertArrayHasKey( 'Location', $headers );
  214. $data = $response->get_data();
  215. $post = get_post( $data['id'] );
  216. $this->check_post_data( $post, $data, 'edit', $response->get_links() );
  217. }
  218. protected function check_update_post_response( $response ) {
  219. $this->assertNotWPError( $response );
  220. $response = rest_ensure_response( $response );
  221. $this->assertSame( 200, $response->get_status() );
  222. $headers = $response->get_headers();
  223. $this->assertArrayNotHasKey( 'Location', $headers );
  224. $data = $response->get_data();
  225. $post = get_post( $data['id'] );
  226. $this->check_post_data( $post, $data, 'edit', $response->get_links() );
  227. }
  228. protected function set_post_data( $args = array() ) {
  229. $defaults = array(
  230. 'title' => 'Post Title',
  231. 'content' => 'Post content',
  232. 'excerpt' => 'Post excerpt',
  233. 'name' => 'test',
  234. 'status' => 'publish',
  235. 'author' => get_current_user_id(),
  236. 'type' => 'post',
  237. );
  238. return wp_parse_args( $args, $defaults );
  239. }
  240. protected function set_raw_post_data( $args = array() ) {
  241. return wp_parse_args(
  242. $args,
  243. $this->set_post_data(
  244. array(
  245. 'title' => array(
  246. 'raw' => 'Post Title',
  247. ),
  248. 'content' => array(
  249. 'raw' => 'Post content',
  250. ),
  251. 'excerpt' => array(
  252. 'raw' => 'Post excerpt',
  253. ),
  254. )
  255. )
  256. );
  257. }
  258. /**
  259. * Overwrite the default protected title format.
  260. *
  261. * By default WordPress will show password protected posts with a title of
  262. * "Protected: %s", as the REST API communicates the protected status of a post
  263. * in a machine readable format, we remove the "Protected: " prefix.
  264. *
  265. * @return string
  266. */
  267. public function protected_title_format() {
  268. return '%s';
  269. }
  270. }