PrivacyExportPersonalData.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <?php
  2. /**
  3. * Testing Ajax handler for exporting personal data.
  4. *
  5. * @package WordPress\UnitTests
  6. * @since 5.2.0
  7. */
  8. /**
  9. * Tests_Ajax_PrivacyExportPersonalData class.
  10. *
  11. * @since 5.2.0
  12. *
  13. * @group ajax
  14. * @group privacy
  15. *
  16. * @covers ::wp_ajax_wp_privacy_export_personal_data
  17. */
  18. class Tests_Ajax_PrivacyExportPersonalData extends WP_Ajax_UnitTestCase {
  19. /**
  20. * User Request ID.
  21. *
  22. * @since 5.2.0
  23. *
  24. * @var int $request_id
  25. */
  26. protected static $request_id;
  27. /**
  28. * User Request Email.
  29. *
  30. * @since 5.2.0
  31. *
  32. * @var string $request_email
  33. */
  34. protected static $request_email;
  35. /**
  36. * Ajax Action.
  37. *
  38. * @since 5.2.0
  39. *
  40. * @var string $action
  41. */
  42. protected static $action;
  43. /**
  44. * Exporter Index.
  45. *
  46. * @since 5.2.0
  47. *
  48. * @var int $exporter
  49. */
  50. protected static $exporter;
  51. /**
  52. * Exporter Key.
  53. *
  54. * @since 5.2.0
  55. *
  56. * @var string $exporter_key
  57. */
  58. protected static $exporter_key;
  59. /**
  60. * Exporter Friendly Name.
  61. *
  62. * @since 5.2.0
  63. *
  64. * @var string $exporter_friendly_name
  65. */
  66. protected static $exporter_friendly_name;
  67. /**
  68. * Page Index.
  69. *
  70. * @since 5.2.0
  71. *
  72. * @var int $page
  73. */
  74. protected static $page;
  75. /**
  76. * Send As Email.
  77. *
  78. * @since 5.2.0
  79. *
  80. * @var bool $send_as_email
  81. */
  82. protected static $send_as_email;
  83. /**
  84. * Last response parsed.
  85. *
  86. * @since 5.2.0
  87. *
  88. * @var array $_last_response_parsed
  89. */
  90. protected $_last_response_parsed;
  91. /**
  92. * An array key in the test exporter to unset.
  93. *
  94. * @since 5.2.0
  95. *
  96. * @var string $key_to_unset
  97. */
  98. protected $key_to_unset;
  99. /**
  100. * A value to change the test exporter callback to.
  101. *
  102. * @since 5.2.0
  103. *
  104. * @var string $new_callback_value
  105. */
  106. protected $new_callback_value;
  107. /**
  108. * Create user export request fixtures.
  109. *
  110. * @since 5.2.0
  111. *
  112. * @param WP_UnitTest_Factory $factory Factory.
  113. */
  114. public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
  115. self::$request_email = 'requester@example.com';
  116. self::$request_id = wp_create_user_request( self::$request_email, 'export_personal_data' );
  117. self::$action = 'wp-privacy-export-personal-data';
  118. self::$exporter = 1;
  119. self::$exporter_key = 'custom-exporter';
  120. self::$exporter_friendly_name = 'Custom Exporter';
  121. self::$page = 1;
  122. self::$send_as_email = false;
  123. }
  124. /**
  125. * Setup before each test method.
  126. *
  127. * @since 5.2.0
  128. */
  129. public function setUp() {
  130. parent::setUp();
  131. $this->key_to_unset = '';
  132. $this->new_callback_value = '';
  133. // Make sure the exporter response is not modified and avoid e.g. writing export file to disk.
  134. remove_all_filters( 'wp_privacy_personal_data_export_page' );
  135. // Only use our custom privacy personal data exporter.
  136. remove_all_filters( 'wp_privacy_personal_data_exporters' );
  137. add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
  138. $this->_setRole( 'administrator' );
  139. // `export_others_personal_data` meta cap in Multisite installation is only granted to those with `manage_network` capability.
  140. if ( is_multisite() ) {
  141. grant_super_admin( get_current_user_id() );
  142. }
  143. }
  144. /**
  145. * Clean up after each test method.
  146. */
  147. public function tearDown() {
  148. remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
  149. if ( is_multisite() ) {
  150. revoke_super_admin( get_current_user_id() );
  151. }
  152. parent::tearDown();
  153. }
  154. /**
  155. * Helper method for changing the test exporter's callback function.
  156. *
  157. * @param string|array $callback New test exporter callback function.
  158. */
  159. protected function _set_exporter_callback( $callback ) {
  160. $this->new_callback_value = $callback;
  161. add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_value' ), 20 );
  162. }
  163. /**
  164. * Change the test exporter callback to a specified value.
  165. *
  166. * @since 5.2.0
  167. *
  168. * @param array $exporters List of data exporters.
  169. * @return array List of data exporters.
  170. */
  171. public function filter_exporter_callback_value( $exporters ) {
  172. $exporters[ self::$exporter_key ]['callback'] = $this->new_callback_value;
  173. return $exporters;
  174. }
  175. /**
  176. * Helper method for unsetting an array index in the test exporter.
  177. *
  178. * @param string $key Test exporter key to unset.
  179. */
  180. protected function _unset_exporter_key( $key ) {
  181. $this->key_to_unset = $key;
  182. add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_key' ), 20 );
  183. }
  184. /**
  185. * Unset a specified key in the test exporter array.
  186. *
  187. * @param array $exporters List of data exporters.
  188. *
  189. * @return array List of data exporters.
  190. */
  191. public function filter_unset_exporter_key( $exporters ) {
  192. if ( false === $this->key_to_unset ) {
  193. $exporters[ self::$exporter_key ] = false;
  194. } elseif ( ! empty( $this->key_to_unset ) ) {
  195. unset( $exporters[ self::$exporter_key ][ $this->key_to_unset ] );
  196. }
  197. return $exporters;
  198. }
  199. /**
  200. * The function should send an error when the request ID is missing.
  201. *
  202. * @since 5.2.0
  203. */
  204. public function test_error_when_missing_request_id() {
  205. $this->_make_ajax_call(
  206. array(
  207. 'id' => null, // Missing request ID.
  208. )
  209. );
  210. $this->assertFalse( $this->_last_response_parsed['success'] );
  211. $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
  212. }
  213. /**
  214. * The function should send an error when the request ID is less than 1.
  215. *
  216. * @since 5.2.0
  217. */
  218. public function test_error_when_invalid_id() {
  219. $this->_make_ajax_call(
  220. array(
  221. 'id' => -1, // Invalid request ID, less than 1.
  222. )
  223. );
  224. $this->assertFalse( $this->_last_response_parsed['success'] );
  225. $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
  226. }
  227. /**
  228. * The function should send an error when the current user is missing the required capability.
  229. *
  230. * @since 5.2.0
  231. */
  232. public function test_error_when_current_user_missing_required_capability() {
  233. $this->_setRole( 'author' );
  234. $this->_make_ajax_call();
  235. $this->assertFalse( $this->_last_response_parsed['success'] );
  236. $this->assertFalse( current_user_can( 'export_others_personal_data' ) );
  237. $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
  238. }
  239. /**
  240. * Test requests do not succeed on multisite when the current user is not a network admin.
  241. *
  242. * @ticket 43438
  243. * @group multisite
  244. * @group ms-required
  245. */
  246. public function test_error_when_current_user_missing_required_capability_multisite() {
  247. revoke_super_admin( get_current_user_id() );
  248. $this->_make_ajax_call();
  249. $this->assertFalse( $this->_last_response_parsed['success'] );
  250. $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
  251. }
  252. /**
  253. * The function should send an error when the nonce does not validate.
  254. *
  255. * @since 5.2.0
  256. */
  257. public function test_failure_with_invalid_nonce() {
  258. $this->expectException( 'WPAjaxDieStopException' );
  259. $this->expectExceptionMessage( '-1' );
  260. $this->_make_ajax_call(
  261. array(
  262. 'security' => 'invalid-nonce',
  263. )
  264. );
  265. }
  266. /**
  267. * The function should send an error when the request type is incorrect.
  268. *
  269. * @since 5.2.0
  270. */
  271. public function test_error_when_incorrect_request_type() {
  272. $request_id = wp_create_user_request(
  273. 'erase-request@example.com',
  274. 'remove_personal_data' // Incorrect request type, expects 'export_personal_data'.
  275. );
  276. $this->_make_ajax_call(
  277. array(
  278. 'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ),
  279. 'id' => $request_id,
  280. )
  281. );
  282. $this->assertFalse( $this->_last_response_parsed['success'] );
  283. $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] );
  284. }
  285. /**
  286. * The function should send an error when the requester's email address is invalid.
  287. *
  288. * @since 5.2.0
  289. */
  290. public function test_error_when_invalid_email_address() {
  291. wp_update_post(
  292. array(
  293. 'ID' => self::$request_id,
  294. 'post_title' => '', // Invalid requester's email address.
  295. )
  296. );
  297. $this->_make_ajax_call();
  298. $this->assertFalse( $this->_last_response_parsed['success'] );
  299. $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] );
  300. }
  301. /**
  302. * The function should send an error when the exporter index is missing.
  303. *
  304. * @since 5.2.0
  305. */
  306. public function test_error_when_missing_exporter_index() {
  307. $this->_make_ajax_call(
  308. array(
  309. 'exporter' => null, // Missing exporter index.
  310. )
  311. );
  312. $this->assertFalse( $this->_last_response_parsed['success'] );
  313. $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] );
  314. }
  315. /**
  316. * The function should send an error when the page index is missing.
  317. *
  318. * @since 5.2.0
  319. */
  320. public function test_error_when_missing_page_index() {
  321. $this->_make_ajax_call(
  322. array(
  323. 'page' => null, // Missing page index.
  324. )
  325. );
  326. $this->assertFalse( $this->_last_response_parsed['success'] );
  327. $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
  328. }
  329. /**
  330. * The function should send an error when an exporter has improperly used the `wp_privacy_personal_data_exporters` filter.
  331. *
  332. * @since 5.2.0
  333. */
  334. public function test_error_when_exporter_has_improperly_used_exporters_filter() {
  335. // Improper filter usage: returns false instead of an expected array.
  336. add_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 );
  337. $this->_make_ajax_call();
  338. $this->assertFalse( $this->_last_response_parsed['success'] );
  339. $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] );
  340. }
  341. /**
  342. * The function should send an error when the exporter index is negative.
  343. *
  344. * @since 5.2.0
  345. */
  346. public function test_error_when_negative_exporter_index() {
  347. $this->_make_ajax_call(
  348. array(
  349. 'exporter' => -1, // Negative exporter index.
  350. )
  351. );
  352. $this->assertFalse( $this->_last_response_parsed['success'] );
  353. $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] );
  354. }
  355. /**
  356. * The function should send an error when the exporter index is out of range.
  357. *
  358. * @since 5.2.0
  359. */
  360. public function test_error_when_exporter_index_out_of_range() {
  361. $this->_make_ajax_call(
  362. array(
  363. 'exporter' => PHP_INT_MAX, // Out of range exporter index.
  364. )
  365. );
  366. $this->assertFalse( $this->_last_response_parsed['success'] );
  367. $this->assertSame( 'Exporter index is out of range.', $this->_last_response_parsed['data'] );
  368. }
  369. /**
  370. * The function should send an error when the page index is less than one.
  371. *
  372. * @since 5.2.0
  373. */
  374. public function test_error_when_page_index_less_than_one() {
  375. $this->_make_ajax_call(
  376. array(
  377. 'page' => 0, // Page index less than one.
  378. )
  379. );
  380. $this->assertFalse( $this->_last_response_parsed['success'] );
  381. $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
  382. }
  383. /**
  384. * The function should send an error when an exporter is not an array.
  385. *
  386. * @since 5.2.0
  387. */
  388. public function test_error_when_exporter_not_array() {
  389. $this->_unset_exporter_key( false );
  390. $this->_make_ajax_call();
  391. $this->assertFalse( $this->_last_response_parsed['success'] );
  392. $this->assertSame(
  393. sprintf(
  394. 'Expected an array describing the exporter at index %s.',
  395. self::$exporter_key
  396. ),
  397. $this->_last_response_parsed['data']
  398. );
  399. }
  400. /**
  401. * The function should send an error when an exporter is missing a friendly name.
  402. *
  403. * @since 5.2.0
  404. */
  405. public function test_error_when_exporter_missing_friendly_name() {
  406. $this->_unset_exporter_key( 'exporter_friendly_name' );
  407. $this->_make_ajax_call();
  408. $this->assertFalse( $this->_last_response_parsed['success'] );
  409. $this->assertSame(
  410. sprintf(
  411. 'Exporter array at index %s does not include a friendly name.',
  412. self::$exporter_key
  413. ),
  414. $this->_last_response_parsed['data']
  415. );
  416. }
  417. /**
  418. * The function should send an error when an exporter is missing a callback.
  419. *
  420. * @since 5.2.0
  421. */
  422. public function test_error_when_exporter_missing_callback() {
  423. $this->_unset_exporter_key( 'callback' );
  424. $this->_make_ajax_call();
  425. $this->assertFalse( $this->_last_response_parsed['success'] );
  426. $this->assertSame(
  427. sprintf(
  428. 'Exporter does not include a callback: %s.',
  429. self::$exporter_friendly_name
  430. ),
  431. $this->_last_response_parsed['data']
  432. );
  433. }
  434. /**
  435. * The function should send an error when an exporter, at a given index, has an invalid callback.
  436. *
  437. * @since 5.2.0
  438. */
  439. public function test_error_when_exporter_index_invalid_callback() {
  440. $this->_set_exporter_callback( false );
  441. $this->_make_ajax_call();
  442. $this->assertFalse( $this->_last_response_parsed['success'] );
  443. $this->assertSame(
  444. sprintf(
  445. 'Exporter callback is not a valid callback: %s.',
  446. self::$exporter_friendly_name
  447. ),
  448. $this->_last_response_parsed['data']
  449. );
  450. }
  451. /**
  452. * When an exporter callback returns a WP_Error, it should be passed as the error.
  453. *
  454. * @since 5.2.0
  455. */
  456. public function test_error_when_exporter_callback_returns_wp_error() {
  457. $this->_set_exporter_callback( array( $this, 'callback_return_wp_error' ) );
  458. $this->_make_ajax_call();
  459. $this->assertFalse( $this->_last_response_parsed['success'] );
  460. $this->assertSame( 'passed_message', $this->_last_response_parsed['data'][0]['code'] );
  461. $this->assertSame( 'This is a WP_Error message.', $this->_last_response_parsed['data'][0]['message'] );
  462. }
  463. /**
  464. * Callback for exporter's response.
  465. *
  466. * @since 5.2.0
  467. *
  468. * @param string $email_address The requester's email address.
  469. * @param int $page Page number.
  470. * @return WP_Error WP_Error instance.
  471. */
  472. public function callback_return_wp_error( $email_address, $page = 1 ) {
  473. return new WP_Error( 'passed_message', 'This is a WP_Error message.' );
  474. }
  475. /**
  476. * The function should send an error when an exporter, at a given index, is missing an array response.
  477. *
  478. * @since 5.2.0
  479. */
  480. public function test_error_when_exporter_index_invalid_response() {
  481. $this->_set_exporter_callback( '__return_null' );
  482. $this->_make_ajax_call();
  483. $this->assertFalse( $this->_last_response_parsed['success'] );
  484. $this->assertSame(
  485. sprintf(
  486. 'Expected response as an array from exporter: %s.',
  487. self::$exporter_friendly_name
  488. ),
  489. $this->_last_response_parsed['data']
  490. );
  491. }
  492. /**
  493. * The function should send an error when an exporter is missing data in array response.
  494. *
  495. * @since 5.2.0
  496. */
  497. public function test_error_when_exporter_missing_data_response() {
  498. $this->_set_exporter_callback( array( $this, 'callback_missing_data_response' ) );
  499. $this->_make_ajax_call();
  500. $this->assertFalse( $this->_last_response_parsed['success'] );
  501. $this->assertSame(
  502. sprintf(
  503. 'Expected data in response array from exporter: %s.',
  504. self::$exporter_friendly_name
  505. ),
  506. $this->_last_response_parsed['data']
  507. );
  508. }
  509. /**
  510. * Callback for exporter's response.
  511. *
  512. * @since 5.2.0
  513. *
  514. * @param string $email_address The requester's email address.
  515. * @param int $page Page number.
  516. *
  517. * @return array Export data.
  518. */
  519. public function callback_missing_data_response( $email_address, $page = 1 ) {
  520. $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
  521. unset( $response['data'] ); // Missing data part of response.
  522. return $response;
  523. }
  524. /**
  525. * The function should send an error when an exporter is missing 'data' array in array response.
  526. *
  527. * @since 5.2.0
  528. */
  529. public function test_function_should_error_when_exporter_missing_data_array_response() {
  530. $this->_set_exporter_callback( array( $this, 'callback_missing_data_array_response' ) );
  531. $this->_make_ajax_call();
  532. $this->assertFalse( $this->_last_response_parsed['success'] );
  533. $this->assertSame(
  534. sprintf(
  535. 'Expected data array in response array from exporter: %s.',
  536. self::$exporter_friendly_name
  537. ),
  538. $this->_last_response_parsed['data']
  539. );
  540. }
  541. /**
  542. * Callback for exporter's response.
  543. *
  544. * @since 5.2.0
  545. *
  546. * @param string $email_address The requester's email address.
  547. * @param int $page Page number.
  548. *
  549. * @return array Export data.
  550. */
  551. public function callback_missing_data_array_response( $email_address, $page = 1 ) {
  552. $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
  553. $response['data'] = false; // Not an array.
  554. return $response;
  555. }
  556. /**
  557. * The function should send an error when an exporter is missing 'done' in array response.
  558. *
  559. * @since 5.2.0
  560. */
  561. public function test_error_when_exporter_missing_done_response() {
  562. $this->_set_exporter_callback( array( $this, 'callback_missing_done_response' ) );
  563. $this->_make_ajax_call();
  564. $this->assertFalse( $this->_last_response_parsed['success'] );
  565. $this->assertSame(
  566. sprintf(
  567. 'Expected done (boolean) in response array from exporter: %s.',
  568. self::$exporter_friendly_name
  569. ),
  570. $this->_last_response_parsed['data']
  571. );
  572. }
  573. /**
  574. * Remove the response's done flag.
  575. *
  576. * @since 5.2.0
  577. *
  578. * @param string $email_address The requester's email address.
  579. * @param int $page Page number.
  580. *
  581. * @return array Export data.
  582. */
  583. public function callback_missing_done_response( $email_address, $page = 1 ) {
  584. $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
  585. unset( $response['done'] );
  586. return $response;
  587. }
  588. /**
  589. * The function should successfully send exporter data response when the current user has the required capability.
  590. *
  591. * @since 5.2.0
  592. */
  593. public function test_succeeds_when_current_user_has_required_capability() {
  594. $this->assertTrue( current_user_can( 'export_others_personal_data' ) );
  595. $this->_make_ajax_call();
  596. $this->assertTrue( $this->_last_response_parsed['success'] );
  597. $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] );
  598. $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] );
  599. $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] );
  600. }
  601. /**
  602. * The function should successfully send exporter data response when no items to export.
  603. *
  604. * @since 5.2.0
  605. */
  606. public function test_success_when_no_items_to_export() {
  607. $this->_make_ajax_call( array( 'page' => 2 ) );
  608. $this->assertTrue( $this->_last_response_parsed['success'] );
  609. $this->assertEmpty( $this->_last_response_parsed['data']['data'] );
  610. $this->assertTrue( $this->_last_response_parsed['data']['done'] );
  611. }
  612. /**
  613. * The function's output should be filterable with the `wp_privacy_personal_data_export_page` filter.
  614. *
  615. * @since 5.2.0
  616. */
  617. public function test_output_should_be_filterable() {
  618. add_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20, 7 );
  619. $this->_make_ajax_call();
  620. $expected_group_label = sprintf(
  621. '%s-%s-%s-%s-%s-%s',
  622. self::$exporter,
  623. self::$page,
  624. self::$request_email,
  625. self::$request_id,
  626. self::$send_as_email,
  627. self::$exporter_key
  628. );
  629. $this->assertTrue( $this->_last_response_parsed['success'] );
  630. $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] );
  631. $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] );
  632. $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] );
  633. $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] );
  634. $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] );
  635. }
  636. /**
  637. * Filter exporter's data response.
  638. *
  639. * @since 5.2.0
  640. *
  641. * @param array $response The personal data for the given exporter and page.
  642. * @param int $exporter_index The index of the exporter that provided this data.
  643. * @param string $email_address The email address associated with this personal data.
  644. * @param int $page The page for this response.
  645. * @param int $request_id The privacy request post ID associated with this request.
  646. * @param bool $send_as_email Whether the final results of the export should be emailed to the user.
  647. * @param string $exporter_key The key (slug) of the exporter that provided this data.
  648. *
  649. * @return array The personal data for the given exporter and page.
  650. */
  651. public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) {
  652. $group_label = sprintf(
  653. '%s-%s-%s-%s-%s-%s',
  654. $exporter_index,
  655. $page,
  656. $email_address,
  657. $request_id,
  658. $send_as_email,
  659. $exporter_key
  660. );
  661. $response['group_label'] = $group_label;
  662. $response['group_id'] = 'filtered_group_id';
  663. $response['item_id'] = 'filtered_item_id';
  664. $response['data'][0]['name'] = 'filtered_name';
  665. $response['data'][0]['value'] = 'filtered_value';
  666. return $response;
  667. }
  668. /**
  669. * Filter to register a custom personal data exporter.
  670. *
  671. * @since 5.2.0
  672. *
  673. * @param array $exporters An array of personal data exporters.
  674. *
  675. * @return array An array of personal data exporters.
  676. */
  677. public function filter_register_custom_personal_data_exporter( $exporters ) {
  678. $exporters[ self::$exporter_key ] = array(
  679. 'exporter_friendly_name' => self::$exporter_friendly_name,
  680. 'callback' => array( $this, 'callback_custom_personal_data_exporter' ),
  681. );
  682. return $exporters;
  683. }
  684. /**
  685. * Callback for a custom personal data exporter.
  686. *
  687. * @since 5.2.0
  688. *
  689. * @param string $email_address The requester's email address.
  690. * @param int $page Page number.
  691. *
  692. * @return array Export data response.
  693. */
  694. public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) {
  695. $data_to_export = array();
  696. if ( 1 === $page ) {
  697. $data_to_export = array(
  698. 'group_id' => self::$exporter_key . '-group-id',
  699. 'group_label' => self::$exporter_key . '-group-label',
  700. 'item_id' => self::$exporter_key . '-item-id',
  701. 'data' => array(
  702. array(
  703. 'name' => 'Email',
  704. 'value' => $email_address,
  705. ),
  706. ),
  707. );
  708. }
  709. return array(
  710. 'data' => $data_to_export,
  711. 'done' => true,
  712. );
  713. }
  714. /**
  715. * Helper function for Ajax handler.
  716. *
  717. * @since 5.2.0
  718. *
  719. * @param array $args Ajax request arguments.
  720. */
  721. protected function _make_ajax_call( $args = array() ) {
  722. $this->_last_response_parsed = null;
  723. $this->_last_response = '';
  724. $defaults = array(
  725. 'action' => self::$action,
  726. 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ),
  727. 'exporter' => self::$exporter,
  728. 'page' => self::$page,
  729. 'sendAsEmail' => self::$send_as_email,
  730. 'id' => self::$request_id,
  731. );
  732. $_POST = wp_parse_args( $args, $defaults );
  733. try {
  734. $this->_handleAjax( self::$action );
  735. } catch ( WPAjaxDieContinueException $e ) {
  736. unset( $e );
  737. }
  738. if ( $this->_last_response ) {
  739. $this->_last_response_parsed = json_decode( $this->_last_response, true );
  740. }
  741. }
  742. }