rest-tags-controller.php 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454
  1. <?php
  2. /**
  3. * Unit tests covering WP_REST_Terms_Controller functionality, used for Tags.
  4. *
  5. * @package WordPress
  6. * @subpackage REST API
  7. */
  8. /**
  9. * @group restapi
  10. */
  11. class WP_Test_REST_Tags_Controller extends WP_Test_REST_Controller_Testcase {
  12. protected static $superadmin;
  13. protected static $administrator;
  14. protected static $editor;
  15. protected static $contributor;
  16. protected static $subscriber;
  17. protected static $tag_ids = array();
  18. protected static $total_tags = 30;
  19. protected static $per_page = 50;
  20. public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
  21. self::$superadmin = $factory->user->create(
  22. array(
  23. 'role' => 'administrator',
  24. 'user_login' => 'superadmin',
  25. )
  26. );
  27. self::$administrator = $factory->user->create(
  28. array(
  29. 'role' => 'administrator',
  30. )
  31. );
  32. self::$editor = $factory->user->create(
  33. array(
  34. 'role' => 'editor',
  35. )
  36. );
  37. self::$contributor = $factory->user->create(
  38. array(
  39. 'role' => 'contributor',
  40. )
  41. );
  42. self::$subscriber = $factory->user->create(
  43. array(
  44. 'role' => 'subscriber',
  45. )
  46. );
  47. if ( is_multisite() ) {
  48. update_site_option( 'site_admins', array( 'superadmin' ) );
  49. }
  50. // Set up tags for pagination tests.
  51. for ( $i = 0; $i < self::$total_tags; $i++ ) {
  52. $tag_ids[] = $factory->tag->create(
  53. array(
  54. 'name' => "Tag {$i}",
  55. )
  56. );
  57. }
  58. }
  59. public static function wpTearDownAfterClass() {
  60. self::delete_user( self::$superadmin );
  61. self::delete_user( self::$administrator );
  62. self::delete_user( self::$editor );
  63. self::delete_user( self::$subscriber );
  64. // Remove tags for pagination tests.
  65. foreach ( self::$tag_ids as $tag_id ) {
  66. wp_delete_term( $tag_id, 'post_tag' );
  67. }
  68. }
  69. public function setUp() {
  70. parent::setUp();
  71. register_meta(
  72. 'term',
  73. 'test_single',
  74. array(
  75. 'show_in_rest' => true,
  76. 'single' => true,
  77. 'type' => 'string',
  78. )
  79. );
  80. register_meta(
  81. 'term',
  82. 'test_multi',
  83. array(
  84. 'show_in_rest' => true,
  85. 'single' => false,
  86. 'type' => 'string',
  87. )
  88. );
  89. register_term_meta(
  90. 'post_tag',
  91. 'test_tag_single',
  92. array(
  93. 'show_in_rest' => true,
  94. 'single' => true,
  95. 'type' => 'string',
  96. )
  97. );
  98. register_term_meta(
  99. 'post_tag',
  100. 'test_tag_multi',
  101. array(
  102. 'show_in_rest' => true,
  103. 'single' => false,
  104. 'type' => 'string',
  105. )
  106. );
  107. register_term_meta(
  108. 'category',
  109. 'test_cat_meta',
  110. array(
  111. 'show_in_rest' => true,
  112. 'single' => true,
  113. 'type' => 'string',
  114. )
  115. );
  116. }
  117. public function test_register_routes() {
  118. $routes = rest_get_server()->get_routes();
  119. $this->assertArrayHasKey( '/wp/v2/tags', $routes );
  120. $this->assertArrayHasKey( '/wp/v2/tags/(?P<id>[\d]+)', $routes );
  121. }
  122. public function test_context_param() {
  123. // Collection.
  124. $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/tags' );
  125. $response = rest_get_server()->dispatch( $request );
  126. $data = $response->get_data();
  127. $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
  128. $this->assertSameSets( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
  129. // Single.
  130. $tag1 = $this->factory->tag->create( array( 'name' => 'Season 5' ) );
  131. $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/tags/' . $tag1 );
  132. $response = rest_get_server()->dispatch( $request );
  133. $data = $response->get_data();
  134. $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] );
  135. $this->assertSameSets( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
  136. }
  137. public function test_registered_query_params() {
  138. $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/tags' );
  139. $response = rest_get_server()->dispatch( $request );
  140. $data = $response->get_data();
  141. $keys = array_keys( $data['endpoints'][0]['args'] );
  142. sort( $keys );
  143. $this->assertSame(
  144. array(
  145. 'context',
  146. 'exclude',
  147. 'hide_empty',
  148. 'include',
  149. 'offset',
  150. 'order',
  151. 'orderby',
  152. 'page',
  153. 'per_page',
  154. 'post',
  155. 'search',
  156. 'slug',
  157. ),
  158. $keys
  159. );
  160. }
  161. public function test_get_items() {
  162. $this->factory->tag->create();
  163. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  164. $request->set_param( 'per_page', self::$per_page );
  165. $response = rest_get_server()->dispatch( $request );
  166. $this->check_get_taxonomy_terms_response( $response );
  167. }
  168. public function test_get_items_invalid_permission_for_context() {
  169. wp_set_current_user( 0 );
  170. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  171. $request->set_param( 'context', 'edit' );
  172. $response = rest_get_server()->dispatch( $request );
  173. $this->assertErrorResponse( 'rest_forbidden_context', $response, 401 );
  174. }
  175. public function test_get_items_hide_empty_arg() {
  176. $post_id = $this->factory->post->create();
  177. $tag1 = $this->factory->tag->create( array( 'name' => 'Season 5' ) );
  178. $tag2 = $this->factory->tag->create( array( 'name' => 'The Be Sharps' ) );
  179. wp_set_object_terms( $post_id, array( $tag1, $tag2 ), 'post_tag' );
  180. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  181. $request->set_param( 'hide_empty', true );
  182. $response = rest_get_server()->dispatch( $request );
  183. $data = $response->get_data();
  184. $this->assertSame( 2, count( $data ) );
  185. $this->assertSame( 'Season 5', $data[0]['name'] );
  186. $this->assertSame( 'The Be Sharps', $data[1]['name'] );
  187. // Invalid 'hide_empty' should error.
  188. $request->set_param( 'hide_empty', 'nothanks' );
  189. $response = rest_get_server()->dispatch( $request );
  190. $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
  191. }
  192. public function test_get_items_include_query() {
  193. $id1 = $this->factory->tag->create();
  194. $id2 = $this->factory->tag->create();
  195. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  196. // 'orderby' => 'asc'.
  197. $request->set_param( 'include', array( $id2, $id1 ) );
  198. $response = rest_get_server()->dispatch( $request );
  199. $data = $response->get_data();
  200. $this->assertSame( 2, count( $data ) );
  201. $this->assertSame( $id1, $data[0]['id'] );
  202. // 'orderby' => 'include'.
  203. $request->set_param( 'orderby', 'include' );
  204. $response = rest_get_server()->dispatch( $request );
  205. $data = $response->get_data();
  206. $this->assertSame( 2, count( $data ) );
  207. $this->assertSame( $id2, $data[0]['id'] );
  208. // Invalid 'include' should error.
  209. $request->set_param( 'include', array( 'myterm' ) );
  210. $response = rest_get_server()->dispatch( $request );
  211. $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
  212. }
  213. public function test_get_items_exclude_query() {
  214. $id1 = $this->factory->tag->create();
  215. $id2 = $this->factory->tag->create();
  216. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  217. $request->set_param( 'per_page', self::$per_page );
  218. $response = rest_get_server()->dispatch( $request );
  219. $data = $response->get_data();
  220. $ids = wp_list_pluck( $data, 'id' );
  221. $this->assertTrue( in_array( $id1, $ids, true ) );
  222. $this->assertTrue( in_array( $id2, $ids, true ) );
  223. $request->set_param( 'exclude', array( $id2 ) );
  224. $response = rest_get_server()->dispatch( $request );
  225. $data = $response->get_data();
  226. $ids = wp_list_pluck( $data, 'id' );
  227. $this->assertTrue( in_array( $id1, $ids, true ) );
  228. $this->assertFalse( in_array( $id2, $ids, true ) );
  229. // Invalid 'exclude' should error.
  230. $request->set_param( 'exclude', array( 'invalid' ) );
  231. $response = rest_get_server()->dispatch( $request );
  232. $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
  233. }
  234. public function test_get_items_offset_query() {
  235. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  236. $request->set_param( 'per_page', self::$per_page );
  237. $request->set_param( 'offset', 1 );
  238. $response = rest_get_server()->dispatch( $request );
  239. $this->assertCount( self::$total_tags - 1, $response->get_data() );
  240. // 'offset' works with 'per_page'.
  241. $request->set_param( 'per_page', 2 );
  242. $response = rest_get_server()->dispatch( $request );
  243. $this->assertCount( 2, $response->get_data() );
  244. // 'offset' takes priority over 'page'.
  245. $request->set_param( 'page', 3 );
  246. $response = rest_get_server()->dispatch( $request );
  247. $this->assertCount( 2, $response->get_data() );
  248. // Invalid 'offset' should error.
  249. $request->set_param( 'offset', 'moreplease' );
  250. $response = rest_get_server()->dispatch( $request );
  251. $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
  252. }
  253. public function test_get_items_orderby_args() {
  254. $tag1 = $this->factory->tag->create( array( 'name' => 'Apple' ) );
  255. $tag2 = $this->factory->tag->create( array( 'name' => 'Zucchini' ) );
  256. /*
  257. * Tests:
  258. * - orderby
  259. * - order
  260. * - per_page
  261. */
  262. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  263. $request->set_param( 'orderby', 'name' );
  264. $request->set_param( 'order', 'desc' );
  265. $request->set_param( 'per_page', 1 );
  266. $response = rest_get_server()->dispatch( $request );
  267. $this->assertSame( 200, $response->get_status() );
  268. $data = $response->get_data();
  269. $this->assertSame( 1, count( $data ) );
  270. $this->assertSame( 'Zucchini', $data[0]['name'] );
  271. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  272. $request->set_param( 'orderby', 'name' );
  273. $request->set_param( 'order', 'asc' );
  274. $request->set_param( 'per_page', 2 );
  275. $response = rest_get_server()->dispatch( $request );
  276. $this->assertSame( 200, $response->get_status() );
  277. $data = $response->get_data();
  278. $this->assertSame( 2, count( $data ) );
  279. $this->assertSame( 'Apple', $data[0]['name'] );
  280. // Invalid 'orderby' should error.
  281. $request->set_param( 'orderby', 'invalid' );
  282. $response = rest_get_server()->dispatch( $request );
  283. $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
  284. }
  285. public function test_get_items_orderby_id() {
  286. $tag0 = $this->factory->tag->create( array( 'name' => 'Cantaloupe' ) );
  287. $tag1 = $this->factory->tag->create( array( 'name' => 'Apple' ) );
  288. $tag2 = $this->factory->tag->create( array( 'name' => 'Banana' ) );
  289. // Defaults to 'orderby' => 'name', 'order' => 'asc'.
  290. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  291. $response = rest_get_server()->dispatch( $request );
  292. $this->assertSame( 200, $response->get_status() );
  293. $data = $response->get_data();
  294. $this->assertSame( 'Apple', $data[0]['name'] );
  295. $this->assertSame( 'Banana', $data[1]['name'] );
  296. $this->assertSame( 'Cantaloupe', $data[2]['name'] );
  297. // 'orderby' => 'id', with default 'order' => 'asc'.
  298. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  299. $request->set_param( 'orderby', 'id' );
  300. $response = rest_get_server()->dispatch( $request );
  301. $this->assertSame( 200, $response->get_status() );
  302. $data = $response->get_data();
  303. $this->assertSame( 'Tag 0', $data[0]['name'] );
  304. $this->assertSame( 'Tag 1', $data[1]['name'] );
  305. $this->assertSame( 'Tag 2', $data[2]['name'] );
  306. // 'orderby' => 'id', 'order' => 'desc'.
  307. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  308. $request->set_param( 'orderby', 'id' );
  309. $request->set_param( 'order', 'desc' );
  310. $response = rest_get_server()->dispatch( $request );
  311. $data = $response->get_data();
  312. $this->assertSame( 200, $response->get_status() );
  313. $this->assertSame( 'Banana', $data[0]['name'] );
  314. $this->assertSame( 'Apple', $data[1]['name'] );
  315. $this->assertSame( 'Cantaloupe', $data[2]['name'] );
  316. }
  317. public function test_get_items_orderby_slugs() {
  318. $this->factory->tag->create( array( 'name' => 'Burrito' ) );
  319. $this->factory->tag->create( array( 'name' => 'Taco' ) );
  320. $this->factory->tag->create( array( 'name' => 'Chalupa' ) );
  321. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  322. $request->set_param( 'orderby', 'include_slugs' );
  323. $request->set_param( 'slug', array( 'taco', 'burrito', 'chalupa' ) );
  324. $response = rest_get_server()->dispatch( $request );
  325. $data = $response->get_data();
  326. $this->assertSame( 200, $response->get_status() );
  327. $this->assertSame( 'taco', $data[0]['slug'] );
  328. $this->assertSame( 'burrito', $data[1]['slug'] );
  329. $this->assertSame( 'chalupa', $data[2]['slug'] );
  330. }
  331. public function test_get_items_post_args() {
  332. $post_id = $this->factory->post->create();
  333. $tag1 = $this->factory->tag->create( array( 'name' => 'DC' ) );
  334. $tag2 = $this->factory->tag->create( array( 'name' => 'Marvel' ) );
  335. $this->factory->tag->create( array( 'name' => 'Dark Horse' ) );
  336. wp_set_object_terms( $post_id, array( $tag1, $tag2 ), 'post_tag' );
  337. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  338. $request->set_param( 'post', $post_id );
  339. $response = rest_get_server()->dispatch( $request );
  340. $this->assertSame( 200, $response->get_status() );
  341. $data = $response->get_data();
  342. $this->assertSame( 2, count( $data ) );
  343. $this->assertSame( 'DC', $data[0]['name'] );
  344. // Invalid 'post' should error.
  345. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  346. $request->set_param( 'post', 'invalid-post' );
  347. $response = rest_get_server()->dispatch( $request );
  348. $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
  349. }
  350. public function test_get_terms_post_args_paging() {
  351. $post_id = $this->factory->post->create();
  352. wp_set_object_terms( $post_id, self::$tag_ids, 'post_tag' );
  353. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  354. $request->set_param( 'post', $post_id );
  355. $request->set_param( 'page', 1 );
  356. $request->set_param( 'per_page', 15 );
  357. $request->set_param( 'orderby', 'id' );
  358. $response = rest_get_server()->dispatch( $request );
  359. $tags = $response->get_data();
  360. $i = 0;
  361. foreach ( $tags as $tag ) {
  362. $this->assertSame( $tag['name'], "Tag {$i}" );
  363. $i++;
  364. }
  365. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  366. $request->set_param( 'post', $post_id );
  367. $request->set_param( 'page', 2 );
  368. $request->set_param( 'per_page', 15 );
  369. $request->set_param( 'orderby', 'id' );
  370. $response = rest_get_server()->dispatch( $request );
  371. $tags = $response->get_data();
  372. foreach ( $tags as $tag ) {
  373. $this->assertSame( $tag['name'], "Tag {$i}" );
  374. $i++;
  375. }
  376. }
  377. public function test_get_items_post_empty() {
  378. $post_id = $this->factory->post->create();
  379. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  380. $request->set_param( 'post', $post_id );
  381. $response = rest_get_server()->dispatch( $request );
  382. $this->assertSame( 200, $response->get_status() );
  383. $data = $response->get_data();
  384. $this->assertCount( 0, $data );
  385. }
  386. public function test_get_items_custom_tax_post_args() {
  387. register_taxonomy( 'batman', 'post', array( 'show_in_rest' => true ) );
  388. $controller = new WP_REST_Terms_Controller( 'batman' );
  389. $controller->register_routes();
  390. $term1 = $this->factory->term->create(
  391. array(
  392. 'name' => 'Cape',
  393. 'taxonomy' => 'batman',
  394. )
  395. );
  396. $term2 = $this->factory->term->create(
  397. array(
  398. 'name' => 'Mask',
  399. 'taxonomy' => 'batman',
  400. )
  401. );
  402. $this->factory->term->create(
  403. array(
  404. 'name' => 'Car',
  405. 'taxonomy' => 'batman',
  406. )
  407. );
  408. $post_id = $this->factory->post->create();
  409. wp_set_object_terms( $post_id, array( $term1, $term2 ), 'batman' );
  410. $request = new WP_REST_Request( 'GET', '/wp/v2/batman' );
  411. $request->set_param( 'post', $post_id );
  412. $response = rest_get_server()->dispatch( $request );
  413. $this->assertSame( 200, $response->get_status() );
  414. $data = $response->get_data();
  415. $this->assertSame( 2, count( $data ) );
  416. $this->assertSame( 'Cape', $data[0]['name'] );
  417. }
  418. public function test_get_items_search_args() {
  419. $tag1 = $this->factory->tag->create( array( 'name' => 'Apple' ) );
  420. $tag2 = $this->factory->tag->create( array( 'name' => 'Banana' ) );
  421. /*
  422. * Tests:
  423. * - search
  424. */
  425. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  426. $request->set_param( 'search', 'App' );
  427. $response = rest_get_server()->dispatch( $request );
  428. $this->assertSame( 200, $response->get_status() );
  429. $data = $response->get_data();
  430. $this->assertSame( 1, count( $data ) );
  431. $this->assertSame( 'Apple', $data[0]['name'] );
  432. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  433. $request->set_param( 'search', 'Garbage' );
  434. $response = rest_get_server()->dispatch( $request );
  435. $this->assertSame( 200, $response->get_status() );
  436. $data = $response->get_data();
  437. $this->assertSame( 0, count( $data ) );
  438. }
  439. public function test_get_items_slug_arg() {
  440. $tag1 = $this->factory->tag->create( array( 'name' => 'Apple' ) );
  441. $tag2 = $this->factory->tag->create( array( 'name' => 'Banana' ) );
  442. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  443. $request->set_param( 'slug', 'apple' );
  444. $response = rest_get_server()->dispatch( $request );
  445. $this->assertSame( 200, $response->get_status() );
  446. $data = $response->get_data();
  447. $this->assertSame( 1, count( $data ) );
  448. $this->assertSame( 'Apple', $data[0]['name'] );
  449. }
  450. public function test_get_items_slug_array_arg() {
  451. $id1 = $this->factory->tag->create( array( 'name' => 'Taco' ) );
  452. $id2 = $this->factory->tag->create( array( 'name' => 'Enchilada' ) );
  453. $id3 = $this->factory->tag->create( array( 'name' => 'Burrito' ) );
  454. $this->factory->tag->create( array( 'name' => 'Pizza' ) );
  455. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  456. $request->set_param(
  457. 'slug',
  458. array(
  459. 'taco',
  460. 'burrito',
  461. 'enchilada',
  462. )
  463. );
  464. $response = rest_get_server()->dispatch( $request );
  465. $this->assertSame( 200, $response->get_status() );
  466. $data = $response->get_data();
  467. $names = wp_list_pluck( $data, 'name' );
  468. sort( $names );
  469. $this->assertSame( array( 'Burrito', 'Enchilada', 'Taco' ), $names );
  470. }
  471. public function test_get_items_slug_csv_arg() {
  472. $id1 = $this->factory->tag->create( array( 'name' => 'Taco' ) );
  473. $id2 = $this->factory->tag->create( array( 'name' => 'Enchilada' ) );
  474. $id3 = $this->factory->tag->create( array( 'name' => 'Burrito' ) );
  475. $this->factory->tag->create( array( 'name' => 'Pizza' ) );
  476. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  477. $request->set_param( 'slug', 'taco,burrito, enchilada' );
  478. $response = rest_get_server()->dispatch( $request );
  479. $this->assertSame( 200, $response->get_status() );
  480. $data = $response->get_data();
  481. $names = wp_list_pluck( $data, 'name' );
  482. sort( $names );
  483. $this->assertSame( array( 'Burrito', 'Enchilada', 'Taco' ), $names );
  484. }
  485. public function test_get_terms_private_taxonomy() {
  486. register_taxonomy( 'robin', 'post', array( 'public' => false ) );
  487. $term1 = $this->factory->term->create(
  488. array(
  489. 'name' => 'Cape',
  490. 'taxonomy' => 'robin',
  491. )
  492. );
  493. $term2 = $this->factory->term->create(
  494. array(
  495. 'name' => 'Mask',
  496. 'taxonomy' => 'robin',
  497. )
  498. );
  499. $request = new WP_REST_Request( 'GET', '/wp/v2/terms/robin' );
  500. $response = rest_get_server()->dispatch( $request );
  501. $this->assertErrorResponse( 'rest_no_route', $response, 404 );
  502. }
  503. public function test_get_terms_pagination_headers() {
  504. $total_tags = self::$total_tags;
  505. $total_pages = (int) ceil( $total_tags / 10 );
  506. // Start of the index.
  507. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  508. $response = rest_get_server()->dispatch( $request );
  509. $headers = $response->get_headers();
  510. $this->assertSame( $total_tags, $headers['X-WP-Total'] );
  511. $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
  512. $next_link = add_query_arg(
  513. array(
  514. 'page' => 2,
  515. ),
  516. rest_url( 'wp/v2/tags' )
  517. );
  518. $this->assertFalse( stripos( $headers['Link'], 'rel="prev"' ) );
  519. $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] );
  520. // 3rd page.
  521. $this->factory->tag->create();
  522. $total_tags++;
  523. $total_pages++;
  524. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  525. $request->set_param( 'page', 3 );
  526. $response = rest_get_server()->dispatch( $request );
  527. $headers = $response->get_headers();
  528. $this->assertSame( $total_tags, $headers['X-WP-Total'] );
  529. $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
  530. $prev_link = add_query_arg(
  531. array(
  532. 'page' => 2,
  533. ),
  534. rest_url( 'wp/v2/tags' )
  535. );
  536. $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] );
  537. $next_link = add_query_arg(
  538. array(
  539. 'page' => 4,
  540. ),
  541. rest_url( 'wp/v2/tags' )
  542. );
  543. $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] );
  544. // Last page.
  545. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  546. $request->set_param( 'page', $total_pages );
  547. $response = rest_get_server()->dispatch( $request );
  548. $headers = $response->get_headers();
  549. $this->assertSame( $total_tags, $headers['X-WP-Total'] );
  550. $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
  551. $prev_link = add_query_arg(
  552. array(
  553. 'page' => $total_pages - 1,
  554. ),
  555. rest_url( 'wp/v2/tags' )
  556. );
  557. $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] );
  558. $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) );
  559. // Out of bounds.
  560. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  561. $request->set_param( 'page', 100 );
  562. $response = rest_get_server()->dispatch( $request );
  563. $headers = $response->get_headers();
  564. $this->assertSame( $total_tags, $headers['X-WP-Total'] );
  565. $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
  566. $prev_link = add_query_arg(
  567. array(
  568. 'page' => $total_pages,
  569. ),
  570. rest_url( 'wp/v2/tags' )
  571. );
  572. $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] );
  573. $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) );
  574. }
  575. public function test_get_items_invalid_context() {
  576. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  577. $request->set_param( 'context', 'banana' );
  578. $response = rest_get_server()->dispatch( $request );
  579. $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
  580. }
  581. public function test_get_item() {
  582. $id = $this->factory->tag->create();
  583. $request = new WP_REST_Request( 'GET', '/wp/v2/tags/' . $id );
  584. $response = rest_get_server()->dispatch( $request );
  585. $this->check_get_taxonomy_term_response( $response, $id );
  586. }
  587. /**
  588. * @ticket 39122
  589. */
  590. public function test_get_item_meta() {
  591. $id = $this->factory->tag->create();
  592. $request = new WP_REST_Request( 'GET', '/wp/v2/tags/' . $id );
  593. $response = rest_get_server()->dispatch( $request );
  594. $data = $response->get_data();
  595. $this->assertArrayHasKey( 'meta', $data );
  596. $meta = (array) $data['meta'];
  597. $this->assertArrayHasKey( 'test_single', $meta );
  598. $this->assertSame( $meta['test_single'], '' );
  599. $this->assertArrayHasKey( 'test_multi', $meta );
  600. $this->assertSame( $meta['test_multi'], array() );
  601. $this->assertArrayHasKey( 'test_tag_single', $meta );
  602. $this->assertSame( $meta['test_tag_single'], '' );
  603. $this->assertArrayHasKey( 'test_tag_multi', $meta );
  604. $this->assertSame( $meta['test_tag_multi'], array() );
  605. }
  606. /**
  607. * @ticket 39122
  608. */
  609. public function test_get_item_meta_registered_for_different_taxonomy() {
  610. $id = $this->factory->tag->create();
  611. $request = new WP_REST_Request( 'GET', '/wp/v2/tags/' . $id );
  612. $response = rest_get_server()->dispatch( $request );
  613. $data = $response->get_data();
  614. $this->assertArrayHasKey( 'meta', $data );
  615. $meta = (array) $data['meta'];
  616. $this->assertFalse( isset( $meta['test_cat_meta'] ) );
  617. }
  618. public function test_get_term_invalid_term() {
  619. $request = new WP_REST_Request( 'GET', '/wp/v2/tags/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
  620. $response = rest_get_server()->dispatch( $request );
  621. $this->assertErrorResponse( 'rest_term_invalid', $response, 404 );
  622. }
  623. public function test_get_item_invalid_permission_for_context() {
  624. $id = $this->factory->tag->create();
  625. wp_set_current_user( 0 );
  626. $request = new WP_REST_Request( 'GET', '/wp/v2/tags/' . $id );
  627. $request->set_param( 'context', 'edit' );
  628. $response = rest_get_server()->dispatch( $request );
  629. $this->assertErrorResponse( 'rest_forbidden_context', $response, 401 );
  630. }
  631. public function test_get_term_private_taxonomy() {
  632. register_taxonomy( 'robin', 'post', array( 'public' => false ) );
  633. $term1 = $this->factory->term->create(
  634. array(
  635. 'name' => 'Cape',
  636. 'taxonomy' => 'robin',
  637. )
  638. );
  639. $request = new WP_REST_Request( 'GET', '/wp/v2/terms/robin/' . $term1 );
  640. $response = rest_get_server()->dispatch( $request );
  641. $this->assertErrorResponse( 'rest_no_route', $response, 404 );
  642. }
  643. public function test_get_item_incorrect_taxonomy() {
  644. register_taxonomy( 'robin', 'post' );
  645. $term1 = $this->factory->term->create(
  646. array(
  647. 'name' => 'Cape',
  648. 'taxonomy' => 'robin',
  649. )
  650. );
  651. $request = new WP_REST_Request( 'GET', '/wp/v2/tags/' . $term1 );
  652. $response = rest_get_server()->dispatch( $request );
  653. $this->assertErrorResponse( 'rest_term_invalid', $response, 404 );
  654. }
  655. public function test_create_item() {
  656. wp_set_current_user( self::$administrator );
  657. $request = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  658. $request->set_param( 'name', 'My Awesome Term' );
  659. $request->set_param( 'description', 'This term is so awesome.' );
  660. $request->set_param( 'slug', 'so-awesome' );
  661. $response = rest_get_server()->dispatch( $request );
  662. $this->assertSame( 201, $response->get_status() );
  663. $headers = $response->get_headers();
  664. $data = $response->get_data();
  665. $this->assertContains( '/wp/v2/tags/' . $data['id'], $headers['Location'] );
  666. $this->assertSame( 'My Awesome Term', $data['name'] );
  667. $this->assertSame( 'This term is so awesome.', $data['description'] );
  668. $this->assertSame( 'so-awesome', $data['slug'] );
  669. }
  670. public function test_create_item_contributor() {
  671. wp_set_current_user( self::$contributor );
  672. $request = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  673. $request->set_param( 'name', 'My Awesome Term' );
  674. $request->set_param( 'description', 'This term is so awesome.' );
  675. $request->set_param( 'slug', 'so-awesome' );
  676. $response = rest_get_server()->dispatch( $request );
  677. $this->assertSame( 201, $response->get_status() );
  678. $headers = $response->get_headers();
  679. $data = $response->get_data();
  680. $this->assertContains( '/wp/v2/tags/' . $data['id'], $headers['Location'] );
  681. $this->assertSame( 'My Awesome Term', $data['name'] );
  682. $this->assertSame( 'This term is so awesome.', $data['description'] );
  683. $this->assertSame( 'so-awesome', $data['slug'] );
  684. }
  685. public function test_create_item_incorrect_permissions() {
  686. wp_set_current_user( self::$subscriber );
  687. $request = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  688. $request->set_param( 'name', 'Incorrect permissions' );
  689. $response = rest_get_server()->dispatch( $request );
  690. $this->assertErrorResponse( 'rest_cannot_create', $response, 403 );
  691. }
  692. public function test_create_item_missing_arguments() {
  693. wp_set_current_user( self::$administrator );
  694. $request = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  695. $response = rest_get_server()->dispatch( $request );
  696. $this->assertErrorResponse( 'rest_missing_callback_param', $response, 400 );
  697. }
  698. public function test_create_item_parent_non_hierarchical_taxonomy() {
  699. wp_set_current_user( self::$administrator );
  700. $request = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  701. $request->set_param( 'name', 'My Awesome Term' );
  702. $request->set_param( 'parent', REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
  703. $response = rest_get_server()->dispatch( $request );
  704. $this->assertErrorResponse( 'rest_taxonomy_not_hierarchical', $response, 400 );
  705. }
  706. public function test_create_item_with_meta() {
  707. wp_set_current_user( self::$administrator );
  708. $request = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  709. $request->set_param( 'name', 'My Awesome Term' );
  710. $request->set_param( 'meta', array( 'test_tag_single' => 'hello' ) );
  711. $response = rest_get_server()->dispatch( $request );
  712. $this->assertSame( 201, $response->get_status() );
  713. $headers = $response->get_headers();
  714. $data = $response->get_data();
  715. $this->assertContains( '/wp/v2/tags/' . $data['id'], $headers['Location'] );
  716. $this->assertSame( 'My Awesome Term', $data['name'] );
  717. $this->assertSame( 'hello', get_term_meta( $data['id'], 'test_tag_single', true ) );
  718. }
  719. public function test_create_item_with_meta_wrong_id() {
  720. wp_set_current_user( self::$administrator );
  721. $existing_tag_id = $this->factory->tag->create( array( 'name' => 'My Not So Awesome Term' ) );
  722. $request = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  723. $request->set_param( 'name', 'My Awesome Term' );
  724. $request->set_param( 'meta', array( 'test_tag_single' => 'hello' ) );
  725. $request->set_param( 'id', $existing_tag_id );
  726. $response = rest_get_server()->dispatch( $request );
  727. $this->assertSame( 201, $response->get_status() );
  728. $headers = $response->get_headers();
  729. $data = $response->get_data();
  730. $this->assertContains( '/wp/v2/tags/' . $data['id'], $headers['Location'] );
  731. $this->assertSame( 'My Awesome Term', $data['name'] );
  732. $this->assertSame( '', get_term_meta( $existing_tag_id, 'test_tag_single', true ) );
  733. $this->assertSame( 'hello', get_term_meta( $data['id'], 'test_tag_single', true ) );
  734. }
  735. public function test_update_item() {
  736. wp_set_current_user( self::$administrator );
  737. $orig_args = array(
  738. 'name' => 'Original Name',
  739. 'description' => 'Original Description',
  740. 'slug' => 'original-slug',
  741. );
  742. $term = get_term_by( 'id', $this->factory->tag->create( $orig_args ), 'post_tag' );
  743. $request = new WP_REST_Request( 'POST', '/wp/v2/tags/' . $term->term_id );
  744. $request->set_param( 'name', 'New Name' );
  745. $request->set_param( 'description', 'New Description' );
  746. $request->set_param( 'slug', 'new-slug' );
  747. $request->set_param(
  748. 'meta',
  749. array(
  750. 'test_single' => 'just meta',
  751. 'test_tag_single' => 'tag-specific meta',
  752. 'test_cat_meta' => 'category-specific meta',
  753. )
  754. );
  755. $response = rest_get_server()->dispatch( $request );
  756. $this->assertSame( 200, $response->get_status() );
  757. $data = $response->get_data();
  758. $this->assertSame( 'New Name', $data['name'] );
  759. $this->assertSame( 'New Description', $data['description'] );
  760. $this->assertSame( 'new-slug', $data['slug'] );
  761. $this->assertSame( 'just meta', $data['meta']['test_single'] );
  762. $this->assertSame( 'tag-specific meta', $data['meta']['test_tag_single'] );
  763. $this->assertFalse( isset( $data['meta']['test_cat_meta'] ) );
  764. }
  765. public function test_update_item_no_change() {
  766. wp_set_current_user( self::$administrator );
  767. $term = get_term_by( 'id', $this->factory->tag->create(), 'post_tag' );
  768. $request = new WP_REST_Request( 'PUT', '/wp/v2/tags/' . $term->term_id );
  769. $response = rest_get_server()->dispatch( $request );
  770. $this->assertSame( 200, $response->get_status() );
  771. $request->set_param( 'slug', $term->slug );
  772. // Run twice to make sure that the update still succeeds
  773. // even if no DB rows are updated.
  774. $response = rest_get_server()->dispatch( $request );
  775. $this->assertSame( 200, $response->get_status() );
  776. $response = rest_get_server()->dispatch( $request );
  777. $this->assertSame( 200, $response->get_status() );
  778. }
  779. public function test_update_item_invalid_term() {
  780. wp_set_current_user( self::$administrator );
  781. $request = new WP_REST_Request( 'POST', '/wp/v2/tags/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
  782. $request->set_param( 'name', 'Invalid Term' );
  783. $response = rest_get_server()->dispatch( $request );
  784. $this->assertErrorResponse( 'rest_term_invalid', $response, 404 );
  785. }
  786. public function test_update_item_incorrect_permissions() {
  787. wp_set_current_user( self::$subscriber );
  788. $term = get_term_by( 'id', $this->factory->tag->create(), 'post_tag' );
  789. $request = new WP_REST_Request( 'POST', '/wp/v2/tags/' . $term->term_id );
  790. $request->set_param( 'name', 'Incorrect permissions' );
  791. $response = rest_get_server()->dispatch( $request );
  792. $this->assertErrorResponse( 'rest_cannot_update', $response, 403 );
  793. }
  794. /**
  795. * @ticket 38505
  796. */
  797. public function test_update_item_with_edit_term_cap_granted() {
  798. wp_set_current_user( self::$subscriber );
  799. $term = $this->factory->tag->create_and_get();
  800. $request = new WP_REST_Request( 'POST', '/wp/v2/tags/' . $term->term_id );
  801. $request->set_param( 'name', 'New Name' );
  802. add_filter( 'map_meta_cap', array( $this, 'grant_edit_term' ), 10, 2 );
  803. $response = rest_get_server()->dispatch( $request );
  804. remove_filter( 'user_has_cap', array( $this, 'grant_edit_term' ), 10, 2 );
  805. $this->assertSame( 200, $response->get_status() );
  806. $data = $response->get_data();
  807. $this->assertSame( 'New Name', $data['name'] );
  808. }
  809. public function grant_edit_term( $caps, $cap ) {
  810. if ( 'edit_term' === $cap ) {
  811. $caps = array( 'read' );
  812. }
  813. return $caps;
  814. }
  815. /**
  816. * @ticket 38505
  817. */
  818. public function test_update_item_with_edit_term_cap_revoked() {
  819. wp_set_current_user( self::$administrator );
  820. $term = $this->factory->tag->create_and_get();
  821. $request = new WP_REST_Request( 'POST', '/wp/v2/tags/' . $term->term_id );
  822. $request->set_param( 'name', 'New Name' );
  823. add_filter( 'map_meta_cap', array( $this, 'revoke_edit_term' ), 10, 2 );
  824. $response = rest_get_server()->dispatch( $request );
  825. remove_filter( 'user_has_cap', array( $this, 'revoke_edit_term' ), 10, 2 );
  826. $this->assertErrorResponse( 'rest_cannot_update', $response, 403 );
  827. }
  828. public function revoke_edit_term( $caps, $cap ) {
  829. if ( 'edit_term' === $cap ) {
  830. $caps = array( 'do_not_allow' );
  831. }
  832. return $caps;
  833. }
  834. public function test_update_item_parent_non_hierarchical_taxonomy() {
  835. wp_set_current_user( self::$administrator );
  836. $term = get_term_by( 'id', $this->factory->tag->create(), 'post_tag' );
  837. $request = new WP_REST_Request( 'POST', '/wp/v2/tags/' . $term->term_id );
  838. $request->set_param( 'parent', REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
  839. $response = rest_get_server()->dispatch( $request );
  840. $this->assertErrorResponse( 'rest_taxonomy_not_hierarchical', $response, 400 );
  841. }
  842. public function verify_tag_roundtrip( $input = array(), $expected_output = array() ) {
  843. // Create the tag.
  844. $request = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  845. foreach ( $input as $name => $value ) {
  846. $request->set_param( $name, $value );
  847. }
  848. $response = rest_get_server()->dispatch( $request );
  849. $this->assertSame( 201, $response->get_status() );
  850. $actual_output = $response->get_data();
  851. // Compare expected API output to actual API output.
  852. $this->assertSame( $expected_output['name'], $actual_output['name'] );
  853. $this->assertSame( $expected_output['description'], $actual_output['description'] );
  854. // Compare expected API output to WP internal values.
  855. $tag = get_term_by( 'id', $actual_output['id'], 'post_tag' );
  856. $this->assertSame( $expected_output['name'], $tag->name );
  857. $this->assertSame( $expected_output['description'], $tag->description );
  858. // Update the tag.
  859. $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/tags/%d', $actual_output['id'] ) );
  860. foreach ( $input as $name => $value ) {
  861. $request->set_param( $name, $value );
  862. }
  863. $response = rest_get_server()->dispatch( $request );
  864. $this->assertSame( 200, $response->get_status() );
  865. $actual_output = $response->get_data();
  866. // Compare expected API output to actual API output.
  867. $this->assertSame( $expected_output['name'], $actual_output['name'] );
  868. $this->assertSame( $expected_output['description'], $actual_output['description'] );
  869. // Compare expected API output to WP internal values.
  870. $tag = get_term_by( 'id', $actual_output['id'], 'post_tag' );
  871. $this->assertSame( $expected_output['name'], $tag->name );
  872. $this->assertSame( $expected_output['description'], $tag->description );
  873. }
  874. public function test_tag_roundtrip_as_editor() {
  875. wp_set_current_user( self::$editor );
  876. $this->assertSame( ! is_multisite(), current_user_can( 'unfiltered_html' ) );
  877. $this->verify_tag_roundtrip(
  878. array(
  879. 'name' => '\o/ ¯\_(ツ)_/¯',
  880. 'description' => '\o/ ¯\_(ツ)_/¯',
  881. ),
  882. array(
  883. 'name' => '\o/ ¯\_(ツ)_/¯',
  884. 'description' => '\o/ ¯\_(ツ)_/¯',
  885. )
  886. );
  887. }
  888. public function test_tag_roundtrip_as_editor_html() {
  889. wp_set_current_user( self::$editor );
  890. if ( is_multisite() ) {
  891. $this->assertFalse( current_user_can( 'unfiltered_html' ) );
  892. $this->verify_tag_roundtrip(
  893. array(
  894. 'name' => '<div>div</div> <strong>strong</strong> <script>oh noes</script>',
  895. 'description' => '<div>div</div> <strong>strong</strong> <script>oh noes</script>',
  896. ),
  897. array(
  898. 'name' => 'div strong',
  899. 'description' => 'div <strong>strong</strong> oh noes',
  900. )
  901. );
  902. } else {
  903. $this->assertTrue( current_user_can( 'unfiltered_html' ) );
  904. $this->verify_tag_roundtrip(
  905. array(
  906. 'name' => '<div>div</div> <strong>strong</strong> <script>oh noes</script>',
  907. 'description' => '<div>div</div> <strong>strong</strong> <script>oh noes</script>',
  908. ),
  909. array(
  910. 'name' => 'div strong',
  911. 'description' => 'div <strong>strong</strong> oh noes',
  912. )
  913. );
  914. }
  915. }
  916. public function test_tag_roundtrip_as_superadmin() {
  917. wp_set_current_user( self::$superadmin );
  918. $this->assertTrue( current_user_can( 'unfiltered_html' ) );
  919. $this->verify_tag_roundtrip(
  920. array(
  921. 'name' => '\\\&\\\ &amp; &invalid; < &lt; &amp;lt;',
  922. 'description' => '\\\&\\\ &amp; &invalid; < &lt; &amp;lt;',
  923. ),
  924. array(
  925. 'name' => '\\\&amp;\\\ &amp; &amp;invalid; &lt; &lt; &amp;lt;',
  926. 'description' => '\\\&amp;\\\ &amp; &amp;invalid; &lt; &lt; &amp;lt;',
  927. )
  928. );
  929. }
  930. public function test_tag_roundtrip_as_superadmin_html() {
  931. wp_set_current_user( self::$superadmin );
  932. $this->assertTrue( current_user_can( 'unfiltered_html' ) );
  933. $this->verify_tag_roundtrip(
  934. array(
  935. 'name' => '<div>div</div> <strong>strong</strong> <script>oh noes</script>',
  936. 'description' => '<div>div</div> <strong>strong</strong> <script>oh noes</script>',
  937. ),
  938. array(
  939. 'name' => 'div strong',
  940. 'description' => 'div <strong>strong</strong> oh noes',
  941. )
  942. );
  943. }
  944. public function test_delete_item() {
  945. wp_set_current_user( self::$administrator );
  946. $term = get_term_by( 'id', $this->factory->tag->create( array( 'name' => 'Deleted Tag' ) ), 'post_tag' );
  947. $request = new WP_REST_Request( 'DELETE', '/wp/v2/tags/' . $term->term_id );
  948. $request->set_param( 'force', true );
  949. $response = rest_get_server()->dispatch( $request );
  950. $this->assertSame( 200, $response->get_status() );
  951. $data = $response->get_data();
  952. $this->assertTrue( $data['deleted'] );
  953. $this->assertSame( 'Deleted Tag', $data['previous']['name'] );
  954. }
  955. public function test_delete_item_no_trash() {
  956. wp_set_current_user( self::$administrator );
  957. $term = get_term_by( 'id', $this->factory->tag->create( array( 'name' => 'Deleted Tag' ) ), 'post_tag' );
  958. $request = new WP_REST_Request( 'DELETE', '/wp/v2/tags/' . $term->term_id );
  959. $response = rest_get_server()->dispatch( $request );
  960. $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
  961. $request->set_param( 'force', 'false' );
  962. $response = rest_get_server()->dispatch( $request );
  963. $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );
  964. }
  965. public function test_delete_item_invalid_term() {
  966. wp_set_current_user( self::$administrator );
  967. $request = new WP_REST_Request( 'DELETE', '/wp/v2/tags/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
  968. $response = rest_get_server()->dispatch( $request );
  969. $this->assertErrorResponse( 'rest_term_invalid', $response, 404 );
  970. }
  971. public function test_delete_item_incorrect_permissions() {
  972. wp_set_current_user( self::$subscriber );
  973. $term = get_term_by( 'id', $this->factory->tag->create(), 'post_tag' );
  974. $request = new WP_REST_Request( 'DELETE', '/wp/v2/tags/' . $term->term_id );
  975. $response = rest_get_server()->dispatch( $request );
  976. $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
  977. }
  978. /**
  979. * @ticket 38505
  980. */
  981. public function test_delete_item_with_delete_term_cap_granted() {
  982. wp_set_current_user( self::$subscriber );
  983. $term = get_term_by( 'id', $this->factory->tag->create( array( 'name' => 'Deleted Tag' ) ), 'post_tag' );
  984. $request = new WP_REST_Request( 'DELETE', '/wp/v2/tags/' . $term->term_id );
  985. $request->set_param( 'force', true );
  986. add_filter( 'map_meta_cap', array( $this, 'grant_delete_term' ), 10, 2 );
  987. $response = rest_get_server()->dispatch( $request );
  988. remove_filter( 'map_meta_cap', array( $this, 'grant_delete_term' ), 10, 2 );
  989. $this->assertSame( 200, $response->get_status() );
  990. $data = $response->get_data();
  991. $this->assertTrue( $data['deleted'] );
  992. $this->assertSame( 'Deleted Tag', $data['previous']['name'] );
  993. }
  994. public function grant_delete_term( $caps, $cap ) {
  995. if ( 'delete_term' === $cap ) {
  996. $caps = array( 'read' );
  997. }
  998. return $caps;
  999. }
  1000. /**
  1001. * @ticket 38505
  1002. */
  1003. public function test_delete_item_with_delete_term_cap_revoked() {
  1004. wp_set_current_user( self::$administrator );
  1005. $term = get_term_by( 'id', $this->factory->tag->create( array( 'name' => 'Deleted Tag' ) ), 'post_tag' );
  1006. $request = new WP_REST_Request( 'DELETE', '/wp/v2/tags/' . $term->term_id );
  1007. $request->set_param( 'force', true );
  1008. add_filter( 'map_meta_cap', array( $this, 'revoke_delete_term' ), 10, 2 );
  1009. $response = rest_get_server()->dispatch( $request );
  1010. remove_filter( 'map_meta_cap', array( $this, 'revoke_delete_term' ), 10, 2 );
  1011. $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
  1012. }
  1013. public function revoke_delete_term( $caps, $cap ) {
  1014. if ( 'delete_term' === $cap ) {
  1015. $caps = array( 'do_not_allow' );
  1016. }
  1017. return $caps;
  1018. }
  1019. public function test_prepare_item() {
  1020. $term = get_term_by( 'id', $this->factory->tag->create(), 'post_tag' );
  1021. $request = new WP_REST_Request( 'GET', '/wp/v2/tags/' . $term->term_id );
  1022. $response = rest_get_server()->dispatch( $request );
  1023. $data = $response->get_data();
  1024. $this->check_taxonomy_term( $term, $data, $response->get_links() );
  1025. }
  1026. public function test_prepare_item_limit_fields() {
  1027. $request = new WP_REST_Request;
  1028. $endpoint = new WP_REST_Terms_Controller( 'post_tag' );
  1029. $request->set_param( '_fields', 'id,name' );
  1030. $term = get_term_by( 'id', $this->factory->tag->create(), 'post_tag' );
  1031. $response = $endpoint->prepare_item_for_response( $term, $request );
  1032. $this->assertSame(
  1033. array(
  1034. 'id',
  1035. 'name',
  1036. ),
  1037. array_keys( $response->get_data() )
  1038. );
  1039. }
  1040. public function test_get_item_schema() {
  1041. $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/tags' );
  1042. $response = rest_get_server()->dispatch( $request );
  1043. $data = $response->get_data();
  1044. $properties = $data['schema']['properties'];
  1045. $this->assertSame( 8, count( $properties ) );
  1046. $this->assertArrayHasKey( 'id', $properties );
  1047. $this->assertArrayHasKey( 'count', $properties );
  1048. $this->assertArrayHasKey( 'description', $properties );
  1049. $this->assertArrayHasKey( 'link', $properties );
  1050. $this->assertArrayHasKey( 'meta', $properties );
  1051. $this->assertArrayHasKey( 'name', $properties );
  1052. $this->assertArrayHasKey( 'slug', $properties );
  1053. $this->assertArrayHasKey( 'taxonomy', $properties );
  1054. $this->assertSame( array( 'post_tag' ), $properties['taxonomy']['enum'] );
  1055. }
  1056. public function test_get_item_schema_non_hierarchical() {
  1057. $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/tags' );
  1058. $response = rest_get_server()->dispatch( $request );
  1059. $data = $response->get_data();
  1060. $properties = $data['schema']['properties'];
  1061. $this->assertArrayHasKey( 'id', $properties );
  1062. $this->assertFalse( isset( $properties['parent'] ) );
  1063. }
  1064. public function test_get_additional_field_registration() {
  1065. $schema = array(
  1066. 'type' => 'integer',
  1067. 'description' => 'Some integer of mine',
  1068. 'enum' => array( 1, 2, 3, 4 ),
  1069. 'context' => array( 'view', 'edit' ),
  1070. );
  1071. register_rest_field(
  1072. 'tag',
  1073. 'my_custom_int',
  1074. array(
  1075. 'schema' => $schema,
  1076. 'get_callback' => array( $this, 'additional_field_get_callback' ),
  1077. )
  1078. );
  1079. $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/tags' );
  1080. $response = rest_get_server()->dispatch( $request );
  1081. $data = $response->get_data();
  1082. $this->assertArrayHasKey( 'my_custom_int', $data['schema']['properties'] );
  1083. $this->assertSame( $schema, $data['schema']['properties']['my_custom_int'] );
  1084. $tag_id = $this->factory->tag->create();
  1085. $request = new WP_REST_Request( 'GET', '/wp/v2/tags/' . $tag_id );
  1086. $response = rest_get_server()->dispatch( $request );
  1087. $this->assertArrayHasKey( 'my_custom_int', $response->data );
  1088. global $wp_rest_additional_fields;
  1089. $wp_rest_additional_fields = array();
  1090. }
  1091. public function test_additional_field_update_errors() {
  1092. $schema = array(
  1093. 'type' => 'integer',
  1094. 'description' => 'Some integer of mine',
  1095. 'enum' => array( 1, 2, 3, 4 ),
  1096. 'context' => array( 'view', 'edit' ),
  1097. );
  1098. register_rest_field(
  1099. 'tag',
  1100. 'my_custom_int',
  1101. array(
  1102. 'schema' => $schema,
  1103. 'get_callback' => array( $this, 'additional_field_get_callback' ),
  1104. 'update_callback' => array( $this, 'additional_field_update_callback' ),
  1105. )
  1106. );
  1107. wp_set_current_user( self::$administrator );
  1108. $tag_id = $this->factory->tag->create();
  1109. // Check for error on update.
  1110. $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/tags/%d', $tag_id ) );
  1111. $request->set_body_params(
  1112. array(
  1113. 'my_custom_int' => 'returnError',
  1114. )
  1115. );
  1116. $response = rest_get_server()->dispatch( $request );
  1117. $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
  1118. global $wp_rest_additional_fields;
  1119. $wp_rest_additional_fields = array();
  1120. }
  1121. /**
  1122. * @ticket 38504
  1123. */
  1124. public function test_object_term_queries_are_cached() {
  1125. global $wpdb;
  1126. $tags = $this->factory->tag->create_many( 2 );
  1127. $p = $this->factory->post->create();
  1128. wp_set_object_terms( $p, $tags[0], 'post_tag' );
  1129. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  1130. $request->set_param( 'post', $p );
  1131. $response = rest_get_server()->dispatch( $request );
  1132. $found_1 = wp_list_pluck( $response->data, 'id' );
  1133. unset( $request, $response );
  1134. $num_queries = $wpdb->num_queries;
  1135. $request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
  1136. $request->set_param( 'post', $p );
  1137. $response = rest_get_server()->dispatch( $request );
  1138. $found_2 = wp_list_pluck( $response->data, 'id' );
  1139. $this->assertSameSets( $found_1, $found_2 );
  1140. $this->assertSame( $num_queries, $wpdb->num_queries );
  1141. }
  1142. /**
  1143. * @ticket 41411
  1144. */
  1145. public function test_editable_response_uses_edit_context() {
  1146. wp_set_current_user( self::$administrator );
  1147. $view_field = 'view_only_field';
  1148. $edit_field = 'edit_only_field';
  1149. register_rest_field(
  1150. 'tag',
  1151. $view_field,
  1152. array(
  1153. 'context' => array( 'view' ),
  1154. 'get_callback' => '__return_empty_string',
  1155. )
  1156. );
  1157. register_rest_field(
  1158. 'tag',
  1159. $edit_field,
  1160. array(
  1161. 'context' => array( 'edit' ),
  1162. 'get_callback' => '__return_empty_string',
  1163. )
  1164. );
  1165. $create = new WP_REST_Request( 'POST', '/wp/v2/tags' );
  1166. $create->set_param( 'name', 'My New Term' );
  1167. $response = rest_get_server()->dispatch( $create );
  1168. $this->assertSame( 201, $response->get_status() );
  1169. $data = $response->get_data();
  1170. $this->assertArrayHasKey( $edit_field, $data );
  1171. $this->assertArrayNotHasKey( $view_field, $data );
  1172. $update = new WP_REST_Request( 'PUT', '/wp/v2/tags/' . $data['id'] );
  1173. $update->set_param( 'name', 'My Awesome New Term' );
  1174. $response = rest_get_server()->dispatch( $update );
  1175. $this->assertSame( 200, $response->get_status() );
  1176. $data = $response->get_data();
  1177. $this->assertArrayHasKey( $edit_field, $data );
  1178. $this->assertArrayNotHasKey( $view_field, $data );
  1179. }
  1180. public function additional_field_get_callback( $object, $request ) {
  1181. return 123;
  1182. }
  1183. public function additional_field_update_callback( $value, $tag ) {
  1184. if ( 'returnError' === $value ) {
  1185. return new WP_Error( 'rest_invalid_param', 'Testing an error.', array( 'status' => 400 ) );
  1186. }
  1187. }
  1188. protected function check_get_taxonomy_terms_response( $response ) {
  1189. $this->assertSame( 200, $response->get_status() );
  1190. $data = $response->get_data();
  1191. $args = array(
  1192. 'hide_empty' => false,
  1193. );
  1194. $tags = get_terms( 'post_tag', $args );
  1195. $this->assertSame( count( $tags ), count( $data ) );
  1196. $this->assertSame( $tags[0]->term_id, $data[0]['id'] );
  1197. $this->assertSame( $tags[0]->name, $data[0]['name'] );
  1198. $this->assertSame( $tags[0]->slug, $data[0]['slug'] );
  1199. $this->assertSame( $tags[0]->taxonomy, $data[0]['taxonomy'] );
  1200. $this->assertSame( $tags[0]->description, $data[0]['description'] );
  1201. $this->assertSame( $tags[0]->count, $data[0]['count'] );
  1202. }
  1203. protected function check_taxonomy_term( $term, $data, $links ) {
  1204. $this->assertSame( $term->term_id, $data['id'] );
  1205. $this->assertSame( $term->name, $data['name'] );
  1206. $this->assertSame( $term->slug, $data['slug'] );
  1207. $this->assertSame( $term->description, $data['description'] );
  1208. $this->assertSame( get_term_link( $term ), $data['link'] );
  1209. $this->assertSame( $term->count, $data['count'] );
  1210. $taxonomy = get_taxonomy( $term->taxonomy );
  1211. if ( $taxonomy->hierarchical ) {
  1212. $this->assertSame( $term->parent, $data['parent'] );
  1213. } else {
  1214. $this->assertFalse( isset( $data['parent'] ) );
  1215. }
  1216. $expected_links = array(
  1217. 'self',
  1218. 'collection',
  1219. 'about',
  1220. 'https://api.w.org/post_type',
  1221. );
  1222. if ( $taxonomy->hierarchical && $term->parent ) {
  1223. $expected_links[] = 'up';
  1224. }
  1225. $this->assertSameSets( $expected_links, array_keys( $links ) );
  1226. $this->assertContains( 'wp/v2/taxonomies/' . $term->taxonomy, $links['about'][0]['href'] );
  1227. $this->assertSame( add_query_arg( 'tags', $term->term_id, rest_url( 'wp/v2/posts' ) ), $links['https://api.w.org/post_type'][0]['href'] );
  1228. }
  1229. protected function check_get_taxonomy_term_response( $response, $id ) {
  1230. $this->assertSame( 200, $response->get_status() );
  1231. $data = $response->get_data();
  1232. $tag = get_term( $id, 'post_tag' );
  1233. $this->check_taxonomy_term( $tag, $data, $response->get_links() );
  1234. }
  1235. }