CustomizeManager.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. <?php
  2. /**
  3. * Testing Ajax customize manager functionality.
  4. *
  5. * @package WordPress
  6. * @subpackage UnitTests
  7. * @since 4.3.0
  8. * @group ajax
  9. */
  10. class Tests_Ajax_CustomizeManager extends WP_Ajax_UnitTestCase {
  11. /**
  12. * Instance of WP_Customize_Manager which is reset for each test.
  13. *
  14. * @var WP_Customize_Manager
  15. */
  16. public $wp_customize;
  17. /**
  18. * Admin user ID.
  19. *
  20. * @var int
  21. */
  22. protected static $admin_user_id;
  23. /**
  24. * Subscriber user ID.
  25. *
  26. * @var int
  27. */
  28. protected static $subscriber_user_id;
  29. /**
  30. * Last response parsed.
  31. *
  32. * @var array|null
  33. */
  34. protected $_last_response_parsed;
  35. /**
  36. * Set up before class.
  37. *
  38. * @param WP_UnitTest_Factory $factory Factory.
  39. */
  40. public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
  41. self::$subscriber_user_id = $factory->user->create( array( 'role' => 'subscriber' ) );
  42. self::$admin_user_id = $factory->user->create( array( 'role' => 'administrator' ) );
  43. }
  44. /**
  45. * Set up the test fixture.
  46. */
  47. public function setUp() {
  48. parent::setUp();
  49. require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
  50. }
  51. /**
  52. * Tear down.
  53. */
  54. public function tearDown() {
  55. $_REQUEST = array();
  56. parent::tearDown();
  57. }
  58. /**
  59. * Helper to keep it DRY
  60. *
  61. * @param string $action Action.
  62. */
  63. protected function make_ajax_call( $action ) {
  64. $this->_last_response_parsed = null;
  65. $this->_last_response = '';
  66. try {
  67. $this->_handleAjax( $action );
  68. } catch ( WPAjaxDieContinueException $e ) {
  69. unset( $e );
  70. }
  71. if ( $this->_last_response ) {
  72. $this->_last_response_parsed = json_decode( $this->_last_response, true );
  73. }
  74. }
  75. /**
  76. * Overridden caps for user_has_cap.
  77. *
  78. * @var array
  79. */
  80. protected $overridden_caps = array();
  81. /**
  82. * Dynamically filter a user's capabilities.
  83. *
  84. * @param array $allcaps An array of all the user's capabilities.
  85. * @return array All caps.
  86. */
  87. function filter_user_has_cap( $allcaps ) {
  88. $allcaps = array_merge( $allcaps, $this->overridden_caps );
  89. return $allcaps;
  90. }
  91. /**
  92. * Test WP_Customize_Manager::save().
  93. *
  94. * @ticket 30937
  95. * @covers WP_Customize_Manager::save
  96. */
  97. function test_save_failures() {
  98. global $wp_customize;
  99. $wp_customize = new WP_Customize_Manager();
  100. $wp_customize->register_controls();
  101. add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ) );
  102. // Unauthenticated.
  103. wp_set_current_user( 0 );
  104. $this->make_ajax_call( 'customize_save' );
  105. $this->assertFalse( $this->_last_response_parsed['success'] );
  106. $this->assertSame( 'unauthenticated', $this->_last_response_parsed['data'] );
  107. // Unauthorized.
  108. wp_set_current_user( self::$subscriber_user_id );
  109. $nonce = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
  110. $_POST['nonce'] = $nonce;
  111. $_GET['nonce'] = $nonce;
  112. $_REQUEST['nonce'] = $nonce;
  113. $exception = null;
  114. try {
  115. ob_start();
  116. $wp_customize->setup_theme();
  117. } catch ( WPAjaxDieContinueException $e ) {
  118. $exception = $e;
  119. }
  120. $this->assertNotEmpty( $e );
  121. $this->assertEquals( -1, $e->getMessage() );
  122. // Not called setup_theme.
  123. wp_set_current_user( self::$admin_user_id );
  124. $nonce = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
  125. $_POST['nonce'] = $nonce;
  126. $_GET['nonce'] = $nonce;
  127. $_REQUEST['nonce'] = $nonce;
  128. $this->make_ajax_call( 'customize_save' );
  129. $this->assertFalse( $this->_last_response_parsed['success'] );
  130. $this->assertSame( 'not_preview', $this->_last_response_parsed['data'] );
  131. // Bad nonce.
  132. $_POST['nonce'] = 'bad';
  133. $_GET['nonce'] = 'bad';
  134. $_REQUEST['nonce'] = 'bad';
  135. $wp_customize->setup_theme();
  136. $this->make_ajax_call( 'customize_save' );
  137. $this->assertFalse( $this->_last_response_parsed['success'] );
  138. $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data'] );
  139. // User cannot create.
  140. $nonce = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
  141. $_POST['nonce'] = $nonce;
  142. $_GET['nonce'] = $nonce;
  143. $_REQUEST['nonce'] = $nonce;
  144. $post_type_obj = get_post_type_object( 'customize_changeset' );
  145. $post_type_obj->cap->create_posts = 'create_customize_changesets';
  146. $this->make_ajax_call( 'customize_save' );
  147. $this->assertFalse( $this->_last_response_parsed['success'] );
  148. $this->assertSame( 'cannot_create_changeset_post', $this->_last_response_parsed['data'] );
  149. $this->overridden_caps[ $post_type_obj->cap->create_posts ] = true;
  150. $this->make_ajax_call( 'customize_save' );
  151. $this->assertTrue( $this->_last_response_parsed['success'] );
  152. $post_type_obj->cap->create_posts = 'customize'; // Restore.
  153. // Changeset already published.
  154. $wp_customize->set_post_value( 'blogname', 'Hello' );
  155. $wp_customize->save_changeset_post( array( 'status' => 'publish' ) );
  156. $this->make_ajax_call( 'customize_save' );
  157. $this->assertFalse( $this->_last_response_parsed['success'] );
  158. $this->assertSame( 'changeset_already_published', $this->_last_response_parsed['data']['code'] );
  159. wp_update_post(
  160. array(
  161. 'ID' => $wp_customize->changeset_post_id(),
  162. 'post_status' => 'auto-draft',
  163. )
  164. );
  165. // User cannot edit.
  166. $post_type_obj = get_post_type_object( 'customize_changeset' );
  167. $post_type_obj->cap->edit_post = 'edit_customize_changesets';
  168. $this->make_ajax_call( 'customize_save' );
  169. $this->assertFalse( $this->_last_response_parsed['success'] );
  170. $this->assertSame( 'cannot_edit_changeset_post', $this->_last_response_parsed['data'] );
  171. $this->overridden_caps[ $post_type_obj->cap->edit_post ] = true;
  172. $this->make_ajax_call( 'customize_save' );
  173. $this->assertTrue( $this->_last_response_parsed['success'] );
  174. $post_type_obj->cap->edit_post = 'customize'; // Restore.
  175. // Bad customize_changeset_data.
  176. $_POST['customize_changeset_data'] = '[MALFORMED]';
  177. $this->make_ajax_call( 'customize_save' );
  178. $this->assertFalse( $this->_last_response_parsed['success'] );
  179. $this->assertSame( 'invalid_customize_changeset_data', $this->_last_response_parsed['data'] );
  180. // Bad customize_changeset_status.
  181. $_POST['customize_changeset_data'] = '{}';
  182. $_POST['customize_changeset_status'] = 'unrecognized';
  183. $this->make_ajax_call( 'customize_save' );
  184. $this->assertFalse( $this->_last_response_parsed['success'] );
  185. $this->assertSame( 'bad_customize_changeset_status', $this->_last_response_parsed['data'] );
  186. // Disallowed publish posts if not allowed.
  187. $post_type_obj = get_post_type_object( 'customize_changeset' );
  188. $post_type_obj->cap->publish_posts = 'publish_customize_changesets';
  189. $_POST['customize_changeset_status'] = 'publish';
  190. $this->make_ajax_call( 'customize_save' );
  191. $this->assertFalse( $this->_last_response_parsed['success'] );
  192. $this->assertSame( 'changeset_publish_unauthorized', $this->_last_response_parsed['data'] );
  193. $_POST['customize_changeset_status'] = 'future';
  194. $this->make_ajax_call( 'customize_save' );
  195. $this->assertFalse( $this->_last_response_parsed['success'] );
  196. $this->assertSame( 'changeset_publish_unauthorized', $this->_last_response_parsed['data'] );
  197. $post_type_obj->cap->publish_posts = 'customize'; // Restore.
  198. // Validate date.
  199. $_POST['customize_changeset_status'] = 'draft';
  200. $_POST['customize_changeset_date'] = 'BAD DATE';
  201. $this->make_ajax_call( 'customize_save' );
  202. $this->assertFalse( $this->_last_response_parsed['success'] );
  203. $this->assertSame( 'bad_customize_changeset_date', $this->_last_response_parsed['data'] );
  204. $_POST['customize_changeset_date'] = '2010-01-01 00:00:00';
  205. $this->make_ajax_call( 'customize_save' );
  206. $this->assertFalse( $this->_last_response_parsed['success'] );
  207. $this->assertSame( 'not_future_date', $this->_last_response_parsed['data']['code'] );
  208. $_POST['customize_changeset_date'] = ( gmdate( 'Y' ) + 1 ) . '-01-01 00:00:00';
  209. $this->make_ajax_call( 'customize_save' );
  210. $this->assertTrue( $this->_last_response_parsed['success'] );
  211. $_POST['customize_changeset_status'] = 'future';
  212. $_POST['customize_changeset_date'] = '+10 minutes';
  213. $this->make_ajax_call( 'customize_save' );
  214. $this->assertTrue( $this->_last_response_parsed['success'] );
  215. $this->assertSame( 'future', get_post_status( $wp_customize->changeset_post_id() ) );
  216. wp_update_post(
  217. array(
  218. 'ID' => $wp_customize->changeset_post_id(),
  219. 'post_status' => 'auto-draft',
  220. )
  221. );
  222. }
  223. /**
  224. * Set up valid user state.
  225. *
  226. * @param string $uuid Changeset UUID.
  227. * @return WP_Customize_Manager
  228. */
  229. protected function set_up_valid_state( $uuid = null ) {
  230. global $wp_customize;
  231. wp_set_current_user( self::$admin_user_id );
  232. $wp_customize = new WP_Customize_Manager(
  233. array(
  234. 'changeset_uuid' => $uuid,
  235. )
  236. );
  237. $wp_customize->register_controls();
  238. $nonce = wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() );
  239. $_POST['nonce'] = $nonce;
  240. $_GET['nonce'] = $nonce;
  241. $_REQUEST['nonce'] = $nonce;
  242. $wp_customize->setup_theme();
  243. return $wp_customize;
  244. }
  245. /**
  246. * Test WP_Customize_Manager::save().
  247. *
  248. * @ticket 30937
  249. * @covers WP_Customize_Manager::save
  250. */
  251. function test_save_success_publish_create() {
  252. $wp_customize = $this->set_up_valid_state();
  253. $_POST['customize_changeset_status'] = 'publish';
  254. $_POST['customize_changeset_title'] = 'Success Changeset';
  255. $_POST['customize_changeset_data'] = wp_json_encode(
  256. array(
  257. 'blogname' => array(
  258. 'value' => 'Successful Site Title',
  259. ),
  260. )
  261. );
  262. $this->make_ajax_call( 'customize_save' );
  263. $this->assertTrue( $this->_last_response_parsed['success'] );
  264. $this->assertInternalType( 'array', $this->_last_response_parsed['data'] );
  265. $this->assertSame( 'publish', $this->_last_response_parsed['data']['changeset_status'] );
  266. $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
  267. $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
  268. $this->assertSame( 'Success Changeset', get_post( $wp_customize->changeset_post_id() )->post_title );
  269. $this->assertSame( 'Successful Site Title', get_option( 'blogname' ) );
  270. }
  271. /**
  272. * Test WP_Customize_Manager::save().
  273. *
  274. * @ticket 30937
  275. * @covers WP_Customize_Manager::save
  276. */
  277. function test_save_success_publish_edit() {
  278. $uuid = wp_generate_uuid4();
  279. $post_id = $this->factory()->post->create(
  280. array(
  281. 'post_name' => $uuid,
  282. 'post_title' => 'Original',
  283. 'post_type' => 'customize_changeset',
  284. 'post_status' => 'auto-draft',
  285. 'post_content' => wp_json_encode(
  286. array(
  287. 'blogname' => array(
  288. 'value' => 'New Site Title',
  289. ),
  290. )
  291. ),
  292. )
  293. );
  294. $wp_customize = $this->set_up_valid_state( $uuid );
  295. $_POST['customize_changeset_status'] = 'publish';
  296. $_POST['customize_changeset_title'] = 'Published';
  297. $this->make_ajax_call( 'customize_save' );
  298. $this->assertTrue( $this->_last_response_parsed['success'] );
  299. $this->assertInternalType( 'array', $this->_last_response_parsed['data'] );
  300. $this->assertSame( 'publish', $this->_last_response_parsed['data']['changeset_status'] );
  301. $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
  302. $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
  303. $this->assertSame( 'New Site Title', get_option( 'blogname' ) );
  304. $this->assertSame( 'Published', get_post( $post_id )->post_title );
  305. }
  306. /**
  307. * Test WP_Customize_Manager::save().
  308. *
  309. * @ticket 38943
  310. * @covers WP_Customize_Manager::save
  311. */
  312. function test_success_save_post_date() {
  313. $uuid = wp_generate_uuid4();
  314. $post_id = $this->factory()->post->create(
  315. array(
  316. 'post_name' => $uuid,
  317. 'post_title' => 'Original',
  318. 'post_type' => 'customize_changeset',
  319. 'post_status' => 'auto-draft',
  320. 'post_content' => wp_json_encode(
  321. array(
  322. 'blogname' => array(
  323. 'value' => 'New Site Title',
  324. ),
  325. )
  326. ),
  327. )
  328. );
  329. $wp_customize = $this->set_up_valid_state( $uuid );
  330. // Success future schedule date.
  331. $future_date = ( gmdate( 'Y' ) + 1 ) . '-01-01 00:00:00';
  332. $_POST['customize_changeset_status'] = 'future';
  333. $_POST['customize_changeset_title'] = 'Future date';
  334. $_POST['customize_changeset_date'] = $future_date;
  335. $this->make_ajax_call( 'customize_save' );
  336. $this->assertTrue( $this->_last_response_parsed['success'] );
  337. $this->assertArrayHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
  338. $changeset_post_schedule = get_post( $post_id );
  339. $this->assertSame( $future_date, $changeset_post_schedule->post_date );
  340. // Success future changeset change to draft keeping existing date.
  341. unset( $_POST['customize_changeset_date'] );
  342. $_POST['customize_changeset_status'] = 'draft';
  343. $this->make_ajax_call( 'customize_save' );
  344. $this->assertTrue( $this->_last_response_parsed['success'] );
  345. $this->assertArrayNotHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
  346. $changeset_post_draft = get_post( $post_id );
  347. $this->assertSame( $future_date, $changeset_post_draft->post_date );
  348. // Success if date is not passed with schedule changeset and stored changeset have future date.
  349. $_POST['customize_changeset_status'] = 'future';
  350. $this->make_ajax_call( 'customize_save' );
  351. $this->assertTrue( $this->_last_response_parsed['success'] );
  352. $this->assertArrayHasKey( 'changeset_date', $this->_last_response_parsed['data'] );
  353. $changeset_post_schedule = get_post( $post_id );
  354. $this->assertSame( $future_date, $changeset_post_schedule->post_date );
  355. // Success if draft with past date.
  356. $now = current_time( 'mysql' );
  357. wp_update_post(
  358. array(
  359. 'ID' => $post_id,
  360. 'post_status' => 'draft',
  361. 'post_date' => $now,
  362. 'post_date_gmt' => get_gmt_from_date( $now ),
  363. )
  364. );
  365. // Fail if future request and existing date is past.
  366. $_POST['customize_changeset_status'] = 'future';
  367. unset( $_POST['customize_changeset_date'] );
  368. $this->make_ajax_call( 'customize_save' );
  369. $this->assertFalse( $this->_last_response_parsed['success'] );
  370. $this->assertSame( 'not_future_date', $this->_last_response_parsed['data']['code'] );
  371. // Success publish changeset reset date to current.
  372. wp_update_post(
  373. array(
  374. 'ID' => $post_id,
  375. 'post_status' => 'future',
  376. 'post_date' => $future_date,
  377. 'post_date_gmt' => get_gmt_from_date( $future_date ),
  378. )
  379. );
  380. unset( $_POST['customize_changeset_date'] );
  381. $_POST['customize_changeset_status'] = 'publish';
  382. $this->make_ajax_call( 'customize_save' );
  383. $this->assertTrue( $this->_last_response_parsed['success'] );
  384. $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
  385. $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
  386. $changeset_post_publish = get_post( $post_id );
  387. $this->assertNotEquals( $future_date, $changeset_post_publish->post_date );
  388. // Check response when trying to update an already-published post.
  389. $this->assertSame( 'trash', get_post_status( $post_id ) );
  390. $_POST['customize_changeset_status'] = 'publish';
  391. $this->make_ajax_call( 'customize_save' );
  392. $this->assertFalse( $this->_last_response_parsed['success'] );
  393. $this->assertSame( 'changeset_already_published', $this->_last_response_parsed['data']['code'] );
  394. $this->assertArrayHasKey( 'next_changeset_uuid', $this->_last_response_parsed['data'] );
  395. $this->assertTrue( wp_is_uuid( $this->_last_response_parsed['data']['next_changeset_uuid'], 4 ) );
  396. }
  397. /**
  398. * Test WP_Customize_Manager::save().
  399. *
  400. * @ticket 39896
  401. * @covers WP_Customize_Manager::save
  402. */
  403. public function test_save_autosave() {
  404. $uuid = wp_generate_uuid4();
  405. $post_id = $this->factory()->post->create(
  406. array(
  407. 'post_name' => $uuid,
  408. 'post_type' => 'customize_changeset',
  409. 'post_status' => 'draft',
  410. 'post_content' => wp_json_encode(
  411. array(
  412. 'blogname' => array(
  413. 'value' => 'New Site Title',
  414. ),
  415. )
  416. ),
  417. )
  418. );
  419. $this->set_up_valid_state( $uuid );
  420. $this->assertFalse( wp_get_post_autosave( $post_id ) );
  421. $_POST['customize_changeset_data'] = wp_json_encode(
  422. array(
  423. 'blogname' => array(
  424. 'value' => 'Autosaved Site Title',
  425. ),
  426. )
  427. );
  428. $_POST['customize_changeset_autosave'] = 'on';
  429. $this->make_ajax_call( 'customize_save' );
  430. $this->assertTrue( $this->_last_response_parsed['success'] );
  431. $this->assertSame( 'draft', $this->_last_response_parsed['data']['changeset_status'] );
  432. $autosave_revision = wp_get_post_autosave( $post_id );
  433. $this->assertInstanceOf( 'WP_Post', $autosave_revision );
  434. $this->assertContains( 'New Site Title', get_post( $post_id )->post_content );
  435. $this->assertContains( 'Autosaved Site Title', $autosave_revision->post_content );
  436. }
  437. /**
  438. * Test request for trashing a changeset.
  439. *
  440. * @ticket 39896
  441. * @covers WP_Customize_Manager::handle_changeset_trash_request
  442. */
  443. public function test_handle_changeset_trash_request() {
  444. $uuid = wp_generate_uuid4();
  445. $wp_customize = $this->set_up_valid_state( $uuid );
  446. $this->make_ajax_call( 'customize_trash' );
  447. $this->assertFalse( $this->_last_response_parsed['success'] );
  448. $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data']['code'] );
  449. $nonce = wp_create_nonce( 'trash_customize_changeset' );
  450. $_POST['nonce'] = $nonce;
  451. $_GET['nonce'] = $nonce;
  452. $_REQUEST['nonce'] = $nonce;
  453. $this->make_ajax_call( 'customize_trash' );
  454. $this->assertFalse( $this->_last_response_parsed['success'] );
  455. $this->assertSame( 'non_existent_changeset', $this->_last_response_parsed['data']['code'] );
  456. $wp_customize->register_controls(); // And settings too.
  457. $wp_customize->set_post_value( 'blogname', 'HELLO' );
  458. $wp_customize->save_changeset_post(
  459. array(
  460. 'status' => 'save',
  461. )
  462. );
  463. add_filter( 'map_meta_cap', array( $this, 'return_do_not_allow' ) );
  464. $this->make_ajax_call( 'customize_trash' );
  465. $this->assertFalse( $this->_last_response_parsed['success'] );
  466. $this->assertSame( 'changeset_trash_unauthorized', $this->_last_response_parsed['data']['code'] );
  467. remove_filter( 'map_meta_cap', array( $this, 'return_do_not_allow' ) );
  468. $lock_user_id = static::factory()->user->create( array( 'role' => 'administrator' ) );
  469. $previous_user = get_current_user_id();
  470. wp_set_current_user( $lock_user_id );
  471. $wp_customize->set_changeset_lock( $wp_customize->changeset_post_id() );
  472. wp_set_current_user( $previous_user );
  473. $this->make_ajax_call( 'customize_trash' );
  474. $this->assertFalse( $this->_last_response_parsed['success'] );
  475. $this->assertSame( 'changeset_locked', $this->_last_response_parsed['data']['code'] );
  476. delete_post_meta( $wp_customize->changeset_post_id(), '_edit_lock' );
  477. wp_update_post(
  478. array(
  479. 'ID' => $wp_customize->changeset_post_id(),
  480. 'post_status' => 'trash',
  481. )
  482. );
  483. $this->make_ajax_call( 'customize_trash' );
  484. $this->assertFalse( $this->_last_response_parsed['success'] );
  485. $this->assertSame( 'changeset_already_trashed', $this->_last_response_parsed['data']['code'] );
  486. wp_update_post(
  487. array(
  488. 'ID' => $wp_customize->changeset_post_id(),
  489. 'post_status' => 'draft',
  490. )
  491. );
  492. $wp_trash_post_count = did_action( 'wp_trash_post' );
  493. add_filter( 'pre_trash_post', '__return_false' );
  494. $this->make_ajax_call( 'customize_trash' );
  495. $this->assertFalse( $this->_last_response_parsed['success'] );
  496. $this->assertSame( 'changeset_trash_failure', $this->_last_response_parsed['data']['code'] );
  497. remove_filter( 'pre_trash_post', '__return_false' );
  498. $this->assertSame( $wp_trash_post_count, did_action( 'wp_trash_post' ) );
  499. $wp_trash_post_count = did_action( 'wp_trash_post' );
  500. $this->assertSame( 'draft', get_post_status( $wp_customize->changeset_post_id() ) );
  501. $this->make_ajax_call( 'customize_trash' );
  502. $this->assertTrue( $this->_last_response_parsed['success'] );
  503. $this->assertSame( 'trash', get_post_status( $wp_customize->changeset_post_id() ) );
  504. $this->assertSame( $wp_trash_post_count + 1, did_action( 'wp_trash_post' ) );
  505. }
  506. /**
  507. * Return caps array containing 'do_not_allow'.
  508. *
  509. * @return array Caps.
  510. */
  511. public function return_do_not_allow() {
  512. return array( 'do_not_allow' );
  513. }
  514. /**
  515. * Test request for dismissing autosave changesets.
  516. *
  517. * @ticket 39896
  518. * @covers WP_Customize_Manager::handle_dismiss_autosave_or_lock_request
  519. * @covers WP_Customize_Manager::dismiss_user_auto_draft_changesets
  520. */
  521. public function test_handle_dismiss_autosave_or_lock_request() {
  522. $uuid = wp_generate_uuid4();
  523. $wp_customize = $this->set_up_valid_state( $uuid );
  524. $valid_user_id = get_current_user_id();
  525. // Temporarily remove user to test requirement that user is logged in. See #42450.
  526. wp_set_current_user( 0 );
  527. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  528. $this->assertFalse( $this->_last_response_parsed['success'] );
  529. $this->assertSame( 'unauthenticated', $this->_last_response_parsed['data'] );
  530. wp_set_current_user( $valid_user_id );
  531. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  532. $this->assertFalse( $this->_last_response_parsed['success'] );
  533. $this->assertSame( 'invalid_nonce', $this->_last_response_parsed['data'] );
  534. $nonce = wp_create_nonce( 'customize_dismiss_autosave_or_lock' );
  535. $_POST['nonce'] = $nonce;
  536. $_GET['nonce'] = $nonce;
  537. $_REQUEST['nonce'] = $nonce;
  538. $_POST['dismiss_lock'] = true;
  539. $_GET['dismiss_lock'] = true;
  540. $_REQUEST['dismiss_lock'] = true;
  541. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  542. $this->assertFalse( $this->_last_response_parsed['success'] );
  543. $this->assertSame( 'no_changeset_to_dismiss_lock', $this->_last_response_parsed['data'] );
  544. $_POST['dismiss_autosave'] = true;
  545. $_GET['dismiss_autosave'] = true;
  546. $_REQUEST['dismiss_autosave'] = true;
  547. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  548. $this->assertFalse( $this->_last_response_parsed['success'] );
  549. $this->assertSame( 'no_auto_draft_to_delete', $this->_last_response_parsed['data'] );
  550. $other_user_id = $this->factory()->user->create();
  551. // Create auto-drafts.
  552. $user_auto_draft_ids = array();
  553. for ( $i = 0; $i < 3; $i++ ) {
  554. $user_auto_draft_ids[] = $this->factory()->post->create(
  555. array(
  556. 'post_name' => wp_generate_uuid4(),
  557. 'post_type' => 'customize_changeset',
  558. 'post_status' => 'auto-draft',
  559. 'post_author' => self::$admin_user_id,
  560. 'post_content' => wp_json_encode( array() ),
  561. )
  562. );
  563. }
  564. $other_user_auto_draft_ids = array();
  565. for ( $i = 0; $i < 3; $i++ ) {
  566. $other_user_auto_draft_ids[] = $this->factory()->post->create(
  567. array(
  568. 'post_name' => wp_generate_uuid4(),
  569. 'post_type' => 'customize_changeset',
  570. 'post_status' => 'auto-draft',
  571. 'post_author' => $other_user_id,
  572. 'post_content' => wp_json_encode( array() ),
  573. )
  574. );
  575. }
  576. foreach ( array_merge( $user_auto_draft_ids, $other_user_auto_draft_ids ) as $post_id ) {
  577. $this->assertFalse( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
  578. }
  579. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  580. $this->assertTrue( $this->_last_response_parsed['success'] );
  581. $this->assertSame( 'auto_draft_dismissed', $this->_last_response_parsed['data'] );
  582. foreach ( $user_auto_draft_ids as $post_id ) {
  583. $this->assertSame( 'auto-draft', get_post_status( $post_id ) );
  584. $this->assertTrue( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
  585. }
  586. foreach ( $other_user_auto_draft_ids as $post_id ) {
  587. $this->assertSame( 'auto-draft', get_post_status( $post_id ) );
  588. $this->assertFalse( (bool) get_post_meta( $post_id, '_customize_restore_dismissed', true ) );
  589. }
  590. // Subsequent test results in none dismissed.
  591. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  592. $this->assertFalse( $this->_last_response_parsed['success'] );
  593. $this->assertSame( 'no_auto_draft_to_delete', $this->_last_response_parsed['data'] );
  594. // Save a changeset as a draft.
  595. $r = $wp_customize->save_changeset_post(
  596. array(
  597. 'data' => array(
  598. 'blogname' => array(
  599. 'value' => 'Foo',
  600. ),
  601. ),
  602. 'status' => 'draft',
  603. )
  604. );
  605. $_POST['dismiss_autosave'] = false;
  606. $_GET['dismiss_autosave'] = false;
  607. $_REQUEST['dismiss_autosave'] = false;
  608. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  609. $this->assertTrue( $this->_last_response_parsed['success'] );
  610. $this->assertSame( 'changeset_lock_dismissed', $this->_last_response_parsed['data'] );
  611. $_POST['dismiss_autosave'] = true;
  612. $_GET['dismiss_autosave'] = true;
  613. $_REQUEST['dismiss_autosave'] = true;
  614. $this->assertNotWPError( $r );
  615. $this->assertFalse( wp_get_post_autosave( $wp_customize->changeset_post_id() ) );
  616. $this->assertContains( 'Foo', get_post( $wp_customize->changeset_post_id() )->post_content );
  617. // Since no autosave yet, confirm no action.
  618. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  619. $this->assertFalse( $this->_last_response_parsed['success'] );
  620. $this->assertSame( 'no_autosave_revision_to_delete', $this->_last_response_parsed['data'] );
  621. // Add the autosave revision.
  622. $r = $wp_customize->save_changeset_post(
  623. array(
  624. 'data' => array(
  625. 'blogname' => array(
  626. 'value' => 'Bar',
  627. ),
  628. ),
  629. 'autosave' => true,
  630. )
  631. );
  632. $this->assertNotWPError( $r );
  633. $autosave_revision = wp_get_post_autosave( $wp_customize->changeset_post_id() );
  634. $this->assertInstanceOf( 'WP_Post', $autosave_revision );
  635. $this->assertContains( 'Foo', get_post( $wp_customize->changeset_post_id() )->post_content );
  636. $this->assertContains( 'Bar', $autosave_revision->post_content );
  637. // Confirm autosave gets deleted.
  638. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  639. $this->assertTrue( $this->_last_response_parsed['success'] );
  640. $this->assertSame( 'autosave_revision_deleted', $this->_last_response_parsed['data'] );
  641. $this->assertFalse( wp_get_post_autosave( $wp_customize->changeset_post_id() ) );
  642. // Since no autosave yet, confirm no action.
  643. $this->make_ajax_call( 'customize_dismiss_autosave_or_lock' );
  644. $this->assertFalse( $this->_last_response_parsed['success'] );
  645. $this->assertSame( 'no_autosave_revision_to_delete', $this->_last_response_parsed['data'] );
  646. }
  647. }