Ticket #43438: 43438.19.diff
File 43438.19.diff, 48.1 KB (added by , 6 years ago) |
---|
-
src/wp-admin/includes/ajax-actions.php
diff --git src/wp-admin/includes/ajax-actions.php src/wp-admin/includes/ajax-actions.php index f3c5428..05c49af 100644
function wp_ajax_wp_privacy_export_personal_data() { 4429 4429 } 4430 4430 4431 4431 if ( $exporter_index > count( $exporters ) ) { 4432 wp_send_json_error( __( 'Exporter index out of range.' ) );4432 wp_send_json_error( __( 'Exporter index is out of range.' ) ); 4433 4433 } 4434 4434 4435 4435 if ( $page < 1 ) { … … function wp_ajax_wp_privacy_export_personal_data() { 4452 4452 sprintf( __( 'Exporter array at index %s does not include a friendly name.' ), $exporter_key ) 4453 4453 ); 4454 4454 } 4455 4456 $exporter_friendly_name = $exporter['exporter_friendly_name']; 4457 4455 4458 if ( ! array_key_exists( 'callback', $exporter ) ) { 4456 4459 wp_send_json_error( 4457 4460 /* translators: %s: exporter friendly name */ 4458 sprintf( __( 'Exporter does not include a callback: %s.' ), esc_html( $exporter ['exporter_friendly_name']) )4461 sprintf( __( 'Exporter does not include a callback: %s.' ), esc_html( $exporter_friendly_name ) ) 4459 4462 ); 4460 4463 } 4461 4464 if ( ! is_callable( $exporter['callback'] ) ) { 4462 4465 wp_send_json_error( 4463 4466 /* translators: %s: exporter friendly name */ 4464 sprintf( __( 'Exporter callback is not a valid callback: %s.' ), esc_html( $exporter ['exporter_friendly_name']) )4467 sprintf( __( 'Exporter callback is not a valid callback: %s.' ), esc_html( $exporter_friendly_name ) ) 4465 4468 ); 4466 4469 } 4467 4470 4468 $callback = $exporter['callback']; 4469 $exporter_friendly_name = $exporter['exporter_friendly_name']; 4470 4471 $callback = $exporter['callback']; 4471 4472 $response = call_user_func( $callback, $email_address, $page ); 4473 4472 4474 if ( is_wp_error( $response ) ) { 4473 4475 wp_send_json_error( $response ); 4474 4476 } … … function wp_ajax_wp_privacy_erase_personal_data() { 4559 4561 $request = wp_get_user_request_data( $request_id ); 4560 4562 4561 4563 if ( ! $request || 'remove_personal_data' !== $request->action_name ) { 4562 wp_send_json_error( __( 'Invalid request ID.' ) );4564 wp_send_json_error( __( 'Invalid request type.' ) ); 4563 4565 } 4564 4566 4565 4567 $email_address = $request->email; … … function wp_ajax_wp_privacy_erase_personal_data() { 4626 4628 wp_send_json_error( sprintf( __( 'Expected an array describing the eraser at index %d.' ), $eraser_index ) ); 4627 4629 } 4628 4630 4629 if ( ! array_key_exists( 'callback', $eraser ) ) {4630 /* translators: %d: array index */4631 wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a callback.' ), $eraser_index ) );4632 }4633 4634 if ( ! is_callable( $eraser['callback'] ) ) {4635 /* translators: %d: array index */4636 wp_send_json_error( sprintf( __( 'Eraser callback at index %d is not a valid callback.' ), $eraser_index ) );4637 }4638 4639 4631 if ( ! array_key_exists( 'eraser_friendly_name', $eraser ) ) { 4640 4632 /* translators: %d: array index */ 4641 4633 wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a friendly name.' ), $eraser_index ) ); 4642 4634 } 4643 4635 4644 $callback = $eraser['callback'];4645 4636 $eraser_friendly_name = $eraser['eraser_friendly_name']; 4646 4637 4638 if ( ! array_key_exists( 'callback', $eraser ) ) { 4639 wp_send_json_error( 4640 sprintf( 4641 /* translators: %d: array index */ 4642 __( 'Eraser does not include a callback: %s.' ), 4643 esc_html( $eraser_friendly_name ) 4644 ) 4645 ); 4646 } 4647 4648 if ( ! is_callable( $eraser['callback'] ) ) { 4649 wp_send_json_error( 4650 sprintf( 4651 /* translators: %d: array index */ 4652 __( 'Eraser callback is not valid: %s.' ), 4653 esc_html( $eraser_friendly_name ) 4654 ) 4655 ); 4656 } 4657 4658 $callback = $eraser['callback']; 4647 4659 $response = call_user_func( $callback, $email_address, $page ); 4648 4660 4649 4661 if ( is_wp_error( $response ) ) { -
tests/phpunit/includes/testcase-ajax.php
diff --git tests/phpunit/includes/testcase-ajax.php tests/phpunit/includes/testcase-ajax.php index bf5a5bc..736d362 100644
abstract class WP_Ajax_UnitTestCase extends WP_UnitTestCase { 119 119 'delete-theme', 120 120 'install-theme', 121 121 'get-post-thumbnail-html', 122 'wp-privacy-export-personal-data', 123 'wp-privacy-erase-personal-data', 122 124 ); 123 125 124 126 public static function setUpBeforeClass() { -
new file tests/phpunit/tests/ajax/PrivacyErasePersonalData.php
diff --git tests/phpunit/tests/ajax/PrivacyErasePersonalData.php tests/phpunit/tests/ajax/PrivacyErasePersonalData.php new file mode 100644 index 0000000..82aefc1
- + 1 <?php 2 /** 3 * Testing Ajax handler for erasing personal data. 4 * 5 * @package WordPress\UnitTests 6 * 7 * @since 4.9.9 8 */ 9 10 /** 11 * Tests_Ajax_PrivacyExportPersonalData class. 12 * 13 * @since 4.9.9 14 * 15 * @group ajax 16 * @group privacy 17 * @covers wp_ajax_wp_privacy_erase_personal_data 18 */ 19 class Tests_Ajax_PrivacyErasePersonalData extends WP_Ajax_UnitTestCase { 20 /** 21 * User Request ID. 22 * 23 * @since 4.9.9 24 * 25 * @var int $request_id 26 */ 27 protected static $request_id; 28 29 /** 30 * User Request Email. 31 * 32 * @since 4.9.9 33 * 34 * @var string $request_email 35 */ 36 protected static $request_email; 37 38 /** 39 * Ajax Action. 40 * 41 * @since 4.9.9 42 * 43 * @var string $action 44 */ 45 protected static $action; 46 47 /** 48 * Eraser Index. 49 * 50 * @since 4.9.9 51 * 52 * @var int $eraser 53 */ 54 protected static $eraser; 55 56 /** 57 * Eraser Key. 58 * 59 * @since 4.9.9 60 * 61 * @var string $eraser_key 62 */ 63 protected static $eraser_key; 64 65 /** 66 * Eraser Friendly Name. 67 * 68 * @since 4.9.9 69 * 70 * @var string $eraser_friendly_name 71 */ 72 protected static $eraser_friendly_name; 73 74 /** 75 * Page Index. 76 * 77 * @since 4.9.9 78 * 79 * @var int $page 80 */ 81 protected static $page; 82 83 /** 84 * Last response parsed. 85 * 86 * @since 4.9.9 87 * 88 * @var array $_last_response_parsed 89 */ 90 protected $_last_response_parsed; 91 92 /** 93 * An array key in the test eraser to unset. 94 * 95 * @since 4.9.9 96 * 97 * @var string $key_to_unset 98 */ 99 protected $key_to_unset; 100 101 /** 102 * A value to change the test eraser callback to. 103 * 104 * @since 4.9.9 105 * 106 * @var string $new_callback_value 107 */ 108 protected $new_callback_value; 109 110 /** 111 * Create user erase request fixtures. 112 * 113 * @param WP_UnitTest_Factory $factory Factory. 114 */ 115 public static function wpSetUpBeforeClass( $factory ) { 116 self::$request_email = 'requester@example.com'; 117 self::$request_id = wp_create_user_request( self::$request_email, 'remove_personal_data' ); 118 self::$action = 'wp-privacy-erase-personal-data'; 119 self::$eraser = 1; 120 self::$eraser_key = 'custom-eraser'; 121 self::$eraser_friendly_name = 'Custom Eraser'; 122 self::$page = 1; 123 } 124 125 /** 126 * Register a custom personal data eraser. 127 */ 128 public function setUp() { 129 parent::setUp(); 130 131 $this->key_to_unset = ''; 132 133 // Make sure the erasers response is not modified and avoid sending emails. 134 remove_all_filters( 'wp_privacy_personal_data_erasure_page' ); 135 remove_all_actions( 'wp_privacy_personal_data_erased' ); 136 137 // Only use our custom privacy personal data eraser. 138 remove_all_filters( 'wp_privacy_personal_data_erasers' ); 139 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) ); 140 141 $this->_setRole( 'administrator' ); 142 } 143 144 /** 145 * Clean up after each test method. 146 */ 147 public function tearDown() { 148 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) ); 149 $this->new_callback_value = ''; 150 151 parent::tearDown(); 152 } 153 154 /** 155 * Helper method for changing the test eraser's callback function. 156 * 157 * @param string|array $callback New test eraser callback index value. 158 */ 159 protected function _set_eraser_callback( $callback ) { 160 $this->new_callback_value = $callback; 161 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 ); 162 } 163 164 /** 165 * Change the test eraser callback to a specified value. 166 * 167 * @since 4.9.9 168 * 169 * @param array $erasers List of data erasers. 170 * @return array $erasersList of data erasers. 171 */ 172 public function filter_eraser_callback_value( $erasers ) { 173 $erasers[ self::$eraser_key ]['callback'] = $this->new_callback_value; 174 175 return $erasers; 176 } 177 178 /** 179 * Helper method for unsetting an array index in the test eraser. 180 * 181 * @param string|bool $key Test eraser key to unset. 182 */ 183 protected function _unset_eraser_key( $key ) { 184 $this->key_to_unset = $key; 185 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 186 } 187 188 /** 189 * Unsets an array key in the test eraser. 190 * 191 * If the key is false, the eraser is set to false. 192 * 193 * @since 4.9.9 194 * 195 * @param array $erasers Erasers. 196 * @return array $erasers Erasers. 197 */ 198 public function filter_unset_eraser_index( $erasers ) { 199 if ( false === $this->key_to_unset ) { 200 $erasers[ self::$eraser_key ] = false; 201 } elseif ( ! empty( $this->key_to_unset ) ) { 202 unset( $erasers[ self::$eraser_key ][ $this->key_to_unset ] ); 203 } 204 205 return $erasers; 206 } 207 208 /** 209 * Helper method for erasing a key from the eraser response. 210 * 211 * @since 4.9.9 212 * 213 * @param array $key Response key to unset. 214 */ 215 protected function _unset_response_key( $key ) { 216 $this->key_to_unset = $key; 217 $this->_set_eraser_callback( array( $this, 'filter_unset_response_index' ) ); 218 } 219 220 /** 221 * Unsets an array index in a response. 222 * 223 * @since 4.9.9 224 * 225 * @param string $email_address The requester's email address. 226 * @param int $page Page number. 227 * @return array $return Export data. 228 */ 229 public function filter_unset_response_index( $email_address, $page = 1 ) { 230 $response = $this->callback_personal_data_eraser( $email_address, $page ); 231 232 if ( ! empty( $this->key_to_unset ) ) { 233 unset( $response[ $this->key_to_unset ] ); 234 } 235 236 return $response; 237 } 238 239 /** 240 * The function should send an error when the request ID is missing. 241 * 242 * @since 4.9.9 243 * 244 * @ticket 43438 245 */ 246 public function test_error_when_missing_request_id() { 247 $this->assertNotWPError( self::$request_id ); 248 249 // Set up a request. 250 $this->_make_ajax_call( 251 array( 252 'id' => null, // Missing request ID. 253 ) 254 ); 255 256 $this->assertFalse( $this->_last_response_parsed['success'] ); 257 $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] ); 258 } 259 260 /** 261 * The function should send an error when the request ID is less than 1. 262 * 263 * @since 4.9.9 264 * 265 * @ticket 43438 266 */ 267 public function test_error_when_request_id_invalid() { 268 $this->assertNotWPError( self::$request_id ); 269 270 // Set up a request. 271 $this->_make_ajax_call( 272 array( 273 'id' => -1, // Invalid request ID. 274 ) 275 ); 276 277 $this->assertFalse( $this->_last_response_parsed['success'] ); 278 $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] ); 279 } 280 281 /** 282 * The function should send an error when the current user is missing required capabilities. 283 * 284 * @since 4.9.9 285 * 286 * @ticket 43438 287 */ 288 public function test_error_when_current_user_missing_required_capabilities() { 289 $this->_setRole( 'author' ); 290 291 $this->assertFalse( current_user_can( 'erase_others_personal_data' ) ); 292 $this->assertFalse( current_user_can( 'delete_users' ) ); 293 294 $this->_make_ajax_call(); 295 296 $this->assertFalse( $this->_last_response_parsed['success'] ); 297 $this->assertSame( 'Invalid request.', $this->_last_response_parsed['data'] ); 298 } 299 300 /** 301 * The function should send an error when the nonce does not validate. 302 * 303 * @since 4.9.9 304 */ 305 public function test_failure_with_invalid_nonce() { 306 $this->setExpectedException( 'WPAjaxDieStopException', '-1' ); 307 308 $this->_make_ajax_call( 309 array( 310 'security' => 'invalid-nonce', 311 ) 312 ); 313 } 314 315 /** 316 * The function should send an error when the request type is incorrect. 317 * 318 * @since 4.9.9 319 */ 320 public function test_error_when_incorrect_request_type() { 321 $request_id = wp_create_user_request( 322 'export-request@example.com', 323 'export_personal_data' // Incorrect request type, expects 'remove_personal_data'. 324 ); 325 326 $this->_make_ajax_call( 327 array( 328 'security' => wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ), 329 'id' => $request_id, 330 ) 331 ); 332 333 $this->assertFalse( $this->_last_response_parsed['success'] ); 334 $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] ); 335 } 336 337 /** 338 * The function should send an error when the request email is invalid. 339 * 340 * @since 4.9.9 341 */ 342 public function test_error_when_invalid_email() { 343 wp_update_post( 344 array( 345 'ID' => self::$request_id, 346 'post_title' => '', // Invalid requester's email address. 347 ) 348 ); 349 350 $this->_make_ajax_call(); 351 352 $this->assertFalse( $this->_last_response_parsed['success'] ); 353 $this->assertSame( 'Invalid email address in request.', $this->_last_response_parsed['data'] ); 354 } 355 356 /** 357 * The function should send an error when the eraser index is missing. 358 * 359 * @since 4.9.9 360 */ 361 public function test_error_when_missing_eraser_index() { 362 $this->_make_ajax_call( 363 array( 364 'eraser' => null, // Missing eraser index. 365 ) 366 ); 367 368 $this->assertFalse( $this->_last_response_parsed['success'] ); 369 $this->assertSame( 'Missing eraser index.', $this->_last_response_parsed['data'] ); 370 } 371 372 /** 373 * The function should send an error when the page index is missing. 374 * 375 * @since 4.9.9 376 */ 377 public function test_error_when_missing_page_index() { 378 $this->_make_ajax_call( 379 array( 380 'page' => null, // Missing page index. 381 ) 382 ); 383 384 $this->assertFalse( $this->_last_response_parsed['success'] ); 385 $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] ); 386 } 387 388 /** 389 * The function should send an error when the eraser index is negative. 390 * 391 * @since 4.9.9 392 */ 393 public function test_error_when_negative_eraser_index() { 394 $this->_make_ajax_call( 395 array( 396 'eraser' => -1, // Negative eraser index. 397 ) 398 ); 399 400 $this->assertFalse( $this->_last_response_parsed['success'] ); 401 $this->assertSame( 'Eraser index cannot be less than one.', $this->_last_response_parsed['data'] ); 402 } 403 404 /** 405 * The function should send an error when the eraser index is out of range. 406 * 407 * @since 4.9.9 408 */ 409 public function test_error_when_eraser_index_out_of_range() { 410 $this->_make_ajax_call( 411 array( 412 'eraser' => PHP_INT_MAX, // Out of range eraser index. 413 ) 414 ); 415 416 $this->assertFalse( $this->_last_response_parsed['success'] ); 417 $this->assertSame( 'Eraser index is out of range.', $this->_last_response_parsed['data'] ); 418 } 419 420 /** 421 * The function should send an error when the page index is less than one. 422 * 423 * @since 4.9.9 424 */ 425 public function test_error_when_page_index_less_than_one() { 426 $this->_make_ajax_call( 427 array( 428 'page' => 0, // Page index less than one. 429 ) 430 ); 431 432 $this->assertFalse( $this->_last_response_parsed['success'] ); 433 $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] ); 434 } 435 436 /** 437 * The function should send an error when an eraser is not an array. 438 * 439 * @since 4.9.9 440 */ 441 public function test_error_when_eraser_not_array() { 442 $this->_unset_eraser_key( false ); 443 $this->_make_ajax_call(); 444 445 $this->assertFalse( $this->_last_response_parsed['success'] ); 446 $this->assertSame( 447 sprintf( 448 'Expected an array describing the eraser at index %s.', 449 self::$eraser 450 ), 451 $this->_last_response_parsed['data'] 452 ); 453 } 454 455 /** 456 * The function should send an error when an eraser is missing a friendly name. 457 * 458 * @since 4.9.9 459 */ 460 public function test_error_when_eraser_missing_friendly_name() { 461 $this->_unset_eraser_key( 'eraser_friendly_name' ); 462 $this->_make_ajax_call(); 463 464 $this->assertFalse( $this->_last_response_parsed['success'] ); 465 $this->assertSame( 466 sprintf( 467 'Eraser array at index %s does not include a friendly name.', 468 self::$eraser 469 ), 470 $this->_last_response_parsed['data'] 471 ); 472 } 473 474 /** 475 * The function should send an error when an eraser is missing a callback. 476 * 477 * @since 4.9.9 478 */ 479 public function test_error_when_eraser_missing_callback() { 480 $this->_unset_eraser_key( 'callback' ); 481 $this->_make_ajax_call(); 482 483 $this->assertFalse( $this->_last_response_parsed['success'] ); 484 $this->assertSame( 485 sprintf( 486 'Eraser does not include a callback: %s.', 487 self::$eraser_friendly_name 488 ), 489 $this->_last_response_parsed['data'] 490 ); 491 } 492 493 /** 494 * The function should send an error when an eraser, at a given index, has an invalid callback. 495 * 496 * @since 4.9.9 497 */ 498 public function test_error_when_eraser_index_invalid_callback() { 499 $this->_set_eraser_callback( false ); 500 $this->_make_ajax_call(); 501 502 $this->assertFalse( $this->_last_response_parsed['success'] ); 503 $this->assertSame( 504 sprintf( 505 'Eraser callback is not valid: %s.', 506 self::$eraser_friendly_name 507 ), 508 $this->_last_response_parsed['data'] 509 ); 510 } 511 512 /** 513 * The function should send an error when an eraser, at a given index, is missing an array response. 514 * 515 * @since 4.9.9 516 */ 517 public function test_error_when_eraser_index_invalid_response() { 518 $this->_set_eraser_callback( '__return_null' ); 519 $this->_make_ajax_call(); 520 521 $this->assertFalse( $this->_last_response_parsed['success'] ); 522 $this->assertSame( 523 sprintf( 524 'Did not receive array from %1$s eraser (index %2$d).', 525 self::$eraser_friendly_name, 526 self::$eraser 527 ), 528 $this->_last_response_parsed['data'] 529 ); 530 } 531 532 /** 533 * The function should send an error when missing an items_removed index. 534 * 535 * @since 4.9.9 536 */ 537 public function test_error_when_eraser_items_removed_missing() { 538 $this->_unset_response_key( 'items_removed' ); 539 $this->_make_ajax_call(); 540 541 $this->assertFalse( $this->_last_response_parsed['success'] ); 542 $this->assertSame( 543 sprintf( 544 'Expected items_removed key in response array from %1$s eraser (index %2$d).', 545 self::$eraser_friendly_name, 546 self::$eraser 547 ), 548 $this->_last_response_parsed['data'] 549 ); 550 } 551 552 /** 553 * The function should send an error when missing an items_retained index. 554 * 555 * @since 4.9.9 556 */ 557 public function test_error_when_eraser_items_retained_missing() { 558 $this->_unset_response_key( 'items_retained' ); 559 $this->_make_ajax_call(); 560 561 $this->assertFalse( $this->_last_response_parsed['success'] ); 562 $this->assertSame( 563 sprintf( 564 'Expected items_retained key in response array from %1$s eraser (index %2$d).', 565 self::$eraser_friendly_name, 566 self::$eraser 567 ), 568 $this->_last_response_parsed['data'] 569 ); 570 } 571 572 /** 573 * The function should send an error when missing a messages index. 574 * 575 * @since 4.9.9 576 */ 577 public function test_error_when_eraser_messages_missing() { 578 $this->_unset_response_key( 'messages' ); 579 $this->_make_ajax_call(); 580 581 $this->assertFalse( $this->_last_response_parsed['success'] ); 582 $this->assertSame( 583 sprintf( 584 'Expected messages key in response array from %1$s eraser (index %2$d).', 585 self::$eraser_friendly_name, 586 self::$eraser 587 ), 588 $this->_last_response_parsed['data'] 589 ); 590 } 591 592 /** 593 * The function should send an error when the messages index is not an array. 594 * 595 * @since 4.9.9 596 */ 597 public function test_error_when_eraser_messages_not_array() { 598 $this->_set_eraser_callback( array( $this, 'filter_response_messages_invalid' ) ); 599 $this->_make_ajax_call(); 600 601 $this->assertFalse( $this->_last_response_parsed['success'] ); 602 $this->assertSame( 603 sprintf( 604 'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).', 605 self::$eraser_friendly_name, 606 self::$eraser 607 ), 608 $this->_last_response_parsed['data'] 609 ); 610 } 611 612 /** 613 * Change the messages index to an invalid value (not an array). 614 * 615 * @since 4.9.9 616 * 617 * @param string $email_address The requester's email address. 618 * @param int $page Page number. 619 * @return array $return Export data. 620 */ 621 public function filter_response_messages_invalid( $email_address, $page = 1 ) { 622 $response = $this->callback_personal_data_eraser( $email_address, $page ); 623 $response['messages'] = true; 624 625 return $response; 626 } 627 628 /** 629 * The function should send an error when an eraser is missing 'done' in array response. 630 * 631 * @since 4.9.9 632 */ 633 public function test_error_when_eraser_missing_done_response() { 634 $this->_unset_response_key( 'done' ); 635 $this->_make_ajax_call(); 636 637 $this->assertFalse( $this->_last_response_parsed['success'] ); 638 $this->assertSame( 639 sprintf( 640 'Expected done flag in response array from %1$s eraser (index %2$d).', 641 self::$eraser_friendly_name, 642 self::$eraser 643 ), 644 $this->_last_response_parsed['data'] 645 ); 646 } 647 648 /** 649 * The function should successfully send erasers response data when the current user has the required 650 * capabilities. 651 * 652 * @since 4.9.9 653 * 654 * @ticket 43438 655 */ 656 public function test_success_when_current_user_has_required_capabilities() { 657 $this->assertTrue( current_user_can( 'erase_others_personal_data' ) ); 658 $this->assertTrue( current_user_can( 'delete_users' ) ); 659 660 $this->_make_ajax_call(); 661 662 $this->assertSame( 'A message regarding retained data for requester@example.com.', $this->_last_response_parsed['data']['messages'][0] ); 663 $this->assertTrue( $this->_last_response_parsed['success'] ); 664 $this->assertTrue( $this->_last_response_parsed['data']['items_removed'] ); 665 $this->assertTrue( $this->_last_response_parsed['data']['items_retained'] ); 666 $this->assertTrue( $this->_last_response_parsed['data']['done'] ); 667 } 668 669 /** 670 * Test that the function's output should be filterable with the `wp_privacy_personal_data_erasure_page` filter. 671 * 672 * @since 4.9.9 673 */ 674 public function test_output_should_be_filterable() { 675 add_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_eraser_data_response' ), 20, 6 ); 676 $this->_make_ajax_call(); 677 678 $expected_new_index = self::$request_email . '-' . self::$request_id . '-' . self::$eraser_key; 679 680 $this->assertTrue( $this->_last_response_parsed['success'] ); 681 $this->assertSame( 'filtered removed', $this->_last_response_parsed['data']['items_removed'] ); 682 $this->assertSame( 'filtered retained', $this->_last_response_parsed['data']['items_retained'] ); 683 $this->assertSame( array( 'filtered messages' ), $this->_last_response_parsed['data']['messages'] ); 684 $this->assertSame( 'filtered done', $this->_last_response_parsed['data']['done'] ); 685 $this->assertSame( $expected_new_index, $this->_last_response_parsed['data']['new_index'] ); 686 } 687 688 /** 689 * Filters the eraser response. 690 * 691 * @since 4.9.9 692 * 693 * @param array $response The personal data for the given eraser and page. 694 * @param int $eraser_index The index of the eraser that provided this data. 695 * @param string $email_address The email address associated with this personal data. 696 * @param int $page The page for this response. 697 * @param int $request_id The privacy request post ID associated with this request. 698 * @param string $eraser_key The key (slug) of the eraser that provided this data. 699 * @return array Filtered erase response. 700 */ 701 public function filter_eraser_data_response( $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ) { 702 $response['items_removed'] = 'filtered removed'; 703 $response['items_retained'] = 'filtered retained'; 704 $response['messages'] = array( 'filtered messages' ); 705 $response['done'] = 'filtered done'; 706 $response['new_index'] = $email_address . '-' . $request_id . '-' . $eraser_key; 707 708 return $response; 709 } 710 711 /** 712 * Register handler for a custom personal data eraser. 713 * 714 * @since 4.9.9 715 * 716 * @param array $erasers An array of personal data erasers. 717 * @return array $erasers An array of personal data erasers. 718 */ 719 public function register_custom_personal_data_eraser( $erasers ) { 720 $erasers[ self::$eraser_key ] = array( 721 'eraser_friendly_name' => self::$eraser_friendly_name, 722 'callback' => array( $this, 'callback_personal_data_eraser' ), 723 ); 724 return $erasers; 725 } 726 727 /** 728 * Custom Personal Data Eraser. 729 * 730 * @since 4.9.9 731 * 732 * @param string $email_address The comment author email address. 733 * @param int $page Page number. 734 * @return array $return Erase data. 735 */ 736 public function callback_personal_data_eraser( $email_address, $page = 1 ) { 737 if ( 1 === $page ) { 738 return array( 739 'items_removed' => true, 740 'items_retained' => true, 741 'messages' => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ), 742 'done' => true, 743 ); 744 } 745 746 return array( 747 'items_removed' => false, 748 'items_retained' => false, 749 'messages' => array(), 750 'done' => true, 751 ); 752 } 753 754 /** 755 * Helper function for ajax handler. 756 * 757 * @since 4.9.9 758 * 759 * @param array $args Ajax request arguments. 760 */ 761 protected function _make_ajax_call( $args = array() ) { 762 $this->_last_response_parsed = null; 763 $this->_last_response = ''; 764 765 $defaults = array( 766 'action' => self::$action, 767 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ), 768 'page' => self::$page, 769 'id' => self::$request_id, 770 'eraser' => self::$eraser, 771 ); 772 773 $_POST = wp_parse_args( $args, $defaults ); 774 775 try { 776 $this->_handleAjax( self::$action ); 777 } catch ( WPAjaxDieContinueException $e ) { 778 unset( $e ); 779 } 780 781 if ( $this->_last_response ) { 782 $this->_last_response_parsed = json_decode( $this->_last_response, true ); 783 } 784 } 785 } -
new file tests/phpunit/tests/ajax/PrivacyExportPersonalData.php
diff --git tests/phpunit/tests/ajax/PrivacyExportPersonalData.php tests/phpunit/tests/ajax/PrivacyExportPersonalData.php new file mode 100644 index 0000000..1acfd8d
- + 1 <?php 2 /** 3 * Testing Ajax handler for exporting personal data. 4 * 5 * @package WordPress\UnitTests 6 * 7 * @since 4.9.9 8 */ 9 10 /** 11 * Tests_Ajax_PrivacyExportPersonalData class. 12 * 13 * @since 4.9.9 14 * 15 * @group ajax 16 * @group privacy 17 * @covers wp_ajax_wp_privacy_export_personal_data 18 */ 19 class Tests_Ajax_PrivacyExportPersonalData extends WP_Ajax_UnitTestCase { 20 /** 21 * User Request ID. 22 * 23 * @since 4.9.9 24 * 25 * @var int $request_id 26 */ 27 protected static $request_id; 28 29 /** 30 * User Request Email. 31 * 32 * @since 4.9.9 33 * 34 * @var string $request_email 35 */ 36 protected static $request_email; 37 38 /** 39 * Ajax Action. 40 * 41 * @since 4.9.9 42 * 43 * @var string $action 44 */ 45 protected static $action; 46 47 /** 48 * Exporter Index. 49 * 50 * @since 4.9.9 51 * 52 * @var int $exporter 53 */ 54 protected static $exporter; 55 56 /** 57 * Exporter Key. 58 * 59 * @since 4.9.9 60 * 61 * @var string $exporter_key 62 */ 63 protected static $exporter_key; 64 65 /** 66 * Exporter Friendly Name. 67 * 68 * @since 4.9.9 69 * 70 * @var string $exporter_friendly_name 71 */ 72 protected static $exporter_friendly_name; 73 74 /** 75 * Page Index. 76 * 77 * @since 4.9.9 78 * 79 * @var int $page 80 */ 81 protected static $page; 82 83 /** 84 * Send As Email. 85 * 86 * @since 4.9.9 87 * 88 * @var bool $send_as_email 89 */ 90 protected static $send_as_email; 91 92 /** 93 * Last response parsed. 94 * 95 * @since 4.9.9 96 * 97 * @var array $_last_response_parsed 98 */ 99 protected $_last_response_parsed; 100 101 /** 102 * An array key in the test exporter to unset. 103 * 104 * @since 4.9.9 105 * 106 * @var string $key_to_unset 107 */ 108 protected $key_to_unset; 109 110 /** 111 * A value to change the test exporter callback to. 112 * 113 * @since 4.9.9 114 * 115 * @var string $new_callback_value 116 */ 117 protected $new_callback_value; 118 119 /** 120 * Create user export request fixtures. 121 * 122 * @since 4.9.9 123 * 124 * @param WP_UnitTest_Factory $factory Factory. 125 */ 126 public static function wpSetUpBeforeClass( $factory ) { 127 self::$request_email = 'requester@example.com'; 128 self::$request_id = wp_create_user_request( self::$request_email, 'export_personal_data' ); 129 self::$action = 'wp-privacy-export-personal-data'; 130 self::$exporter = 1; 131 self::$exporter_key = 'custom-exporter'; 132 self::$exporter_friendly_name = 'Custom Exporter'; 133 self::$page = 1; 134 self::$send_as_email = false; 135 } 136 137 /** 138 * Setup before each test method. 139 * 140 * @since 4.9.9 141 */ 142 public function setUp() { 143 parent::setUp(); 144 145 $this->key_to_unset = ''; 146 $this->new_callback_value = ''; 147 148 // Make sure the exporter response is not modified and avoid e.g. writing export file to disk. 149 remove_all_filters( 'wp_privacy_personal_data_export_page' ); 150 151 // Only use our custom privacy personal data exporter. 152 remove_all_filters( 'wp_privacy_personal_data_exporters' ); 153 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) ); 154 155 $this->_setRole( 'administrator' ); 156 } 157 158 /** 159 * Clean up after each test method. 160 */ 161 public function tearDown() { 162 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) ); 163 164 parent::tearDown(); 165 } 166 167 /** 168 * Helper method for changing the test exporter's callback function. 169 * 170 * @param string|array $callback New test exporter callback function. 171 */ 172 protected function _set_exporter_callback( $callback ) { 173 $this->new_callback_value = $callback; 174 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_value' ), 20 ); 175 } 176 177 /** 178 * Change the test exporter callback to a specified value. 179 * 180 * @since 4.9.9 181 * 182 * @param array $exporters List of data exporters. 183 * @return array $exporters List of data exporters. 184 */ 185 public function filter_exporter_callback_value( $exporters ) { 186 $exporters[ self::$exporter_key ]['callback'] = $this->new_callback_value; 187 188 return $exporters; 189 } 190 191 /** 192 * Helper method for unsetting an array index in the test exporter. 193 * 194 * @param string $key Test exporter key to unset. 195 */ 196 protected function _unset_exporter_key( $key ) { 197 $this->key_to_unset = $key; 198 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_key' ), 20 ); 199 } 200 201 /** 202 * Unset a specified key in the test exporter array. 203 * 204 * @param array $exporters List of data exporters. 205 * @return array $exporters List of data exporters. 206 */ 207 public function filter_unset_exporter_key( $exporters ) { 208 if ( false === $this->key_to_unset ) { 209 $exporters[ self::$exporter_key ] = false; 210 } elseif ( ! empty( $this->key_to_unset ) ) { 211 unset( $exporters[ self::$exporter_key ][ $this->key_to_unset ] ); 212 } 213 214 return $exporters; 215 } 216 217 /** 218 * The function should send an error when the request ID is missing. 219 * 220 * @since 4.9.9 221 */ 222 public function test_error_when_missing_request_id() { 223 $this->_make_ajax_call( 224 array( 225 'id' => null, // Missing request ID. 226 ) 227 ); 228 229 $this->assertFalse( $this->_last_response_parsed['success'] ); 230 $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] ); 231 } 232 233 /** 234 * The function should send an error when the request ID is less than 1. 235 * 236 * @since 4.9.9 237 */ 238 public function test_error_when_invalid_id() { 239 $this->_make_ajax_call( 240 array( 241 'id' => -1, // Invalid request ID, less than 1. 242 ) 243 ); 244 245 $this->assertFalse( $this->_last_response_parsed['success'] ); 246 $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] ); 247 } 248 249 /** 250 * The function should send an error when the current user is missing the required capability. 251 * 252 * @since 4.9.9 253 */ 254 public function test_error_when_current_user_missing_required_capability() { 255 $this->_setRole( 'author' ); 256 257 $this->_make_ajax_call(); 258 259 $this->assertFalse( $this->_last_response_parsed['success'] ); 260 $this->assertFalse( current_user_can( 'export_others_personal_data' ) ); 261 $this->assertSame( 'Invalid request.', $this->_last_response_parsed['data'] ); 262 } 263 264 /** 265 * The function should send an error when the nonce does not validate. 266 * 267 * @since 4.9.9 268 */ 269 public function test_failure_with_invalid_nonce() { 270 $this->setExpectedException( 'WPAjaxDieStopException', '-1' ); 271 272 $this->_make_ajax_call( 273 array( 274 'security' => 'invalid-nonce', 275 ) 276 ); 277 } 278 279 /** 280 * The function should send an error when the request type is incorrect. 281 * 282 * @since 4.9.9 283 */ 284 public function test_error_when_incorrect_request_type() { 285 $request_id = wp_create_user_request( 286 'erase-request@example.com', 287 'remove_personal_data' // Incorrect request type, expects 'export_personal_data'. 288 ); 289 290 $this->_make_ajax_call( 291 array( 292 'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ), 293 'id' => $request_id, 294 ) 295 ); 296 297 $this->assertFalse( $this->_last_response_parsed['success'] ); 298 $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] ); 299 } 300 301 /** 302 * The function should send an error when the requester's email address is invalid. 303 * 304 * @since 4.9.9 305 */ 306 public function test_error_when_invalid_email_address() { 307 wp_update_post( 308 array( 309 'ID' => self::$request_id, 310 'post_title' => '', // Invalid requester's email address. 311 ) 312 ); 313 314 $this->_make_ajax_call(); 315 316 $this->assertFalse( $this->_last_response_parsed['success'] ); 317 $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] ); 318 } 319 320 /** 321 * The function should send an error when the exporter index is missing. 322 * 323 * @since 4.9.9 324 */ 325 public function test_error_when_missing_exporter_index() { 326 $this->_make_ajax_call( 327 array( 328 'exporter' => null, // Missing exporter index. 329 ) 330 ); 331 332 $this->assertFalse( $this->_last_response_parsed['success'] ); 333 $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] ); 334 } 335 336 /** 337 * The function should send an error when the page index is missing. 338 * 339 * @since 4.9.9 340 */ 341 public function test_error_when_missing_page_index() { 342 $this->_make_ajax_call( 343 array( 344 'page' => null, // Missing page index. 345 ) 346 ); 347 348 $this->assertFalse( $this->_last_response_parsed['success'] ); 349 $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] ); 350 } 351 352 /** 353 * The function should send an error when an exporter has improperly used the `wp_privacy_personal_data_exporters` filter. 354 * 355 * @since 4.9.9 356 */ 357 public function test_error_when_exporter_has_improperly_used_exporters_filter() { 358 // Improper filter usage: returns false instead of an expected array. 359 add_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 ); 360 $this->_make_ajax_call(); 361 362 $this->assertFalse( $this->_last_response_parsed['success'] ); 363 $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] ); 364 } 365 366 /** 367 * The function should send an error when the exporter index is negative. 368 * 369 * @since 4.9.9 370 */ 371 public function test_error_when_negative_exporter_index() { 372 $this->_make_ajax_call( 373 array( 374 'exporter' => -1, // Negative exporter index. 375 ) 376 ); 377 378 $this->assertFalse( $this->_last_response_parsed['success'] ); 379 $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] ); 380 } 381 382 /** 383 * The function should send an error when the exporter index is out of range. 384 * 385 * @since 4.9.9 386 */ 387 public function test_error_when_exporter_index_out_of_range() { 388 $this->_make_ajax_call( 389 array( 390 'exporter' => PHP_INT_MAX, // Out of range exporter index. 391 ) 392 ); 393 394 $this->assertFalse( $this->_last_response_parsed['success'] ); 395 $this->assertSame( 'Exporter index is out of range.', $this->_last_response_parsed['data'] ); 396 } 397 398 /** 399 * The function should send an error when the page index is less than one. 400 * 401 * @since 4.9.9 402 */ 403 public function test_error_when_page_index_less_than_one() { 404 $this->_make_ajax_call( 405 array( 406 'page' => 0, // Page index less than one. 407 ) 408 ); 409 410 $this->assertFalse( $this->_last_response_parsed['success'] ); 411 $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] ); 412 } 413 414 /** 415 * The function should send an error when an exporter is not an array. 416 * 417 * @since 4.9.9 418 */ 419 public function test_error_when_exporter_not_array() { 420 $this->_unset_exporter_key( false ); 421 $this->_make_ajax_call(); 422 423 $this->assertFalse( $this->_last_response_parsed['success'] ); 424 $this->assertSame( 425 sprintf( 426 'Expected an array describing the exporter at index %s.', 427 self::$exporter_key 428 ), 429 $this->_last_response_parsed['data'] 430 ); 431 } 432 433 /** 434 * The function should send an error when an exporter is missing a friendly name. 435 * 436 * @since 4.9.9 437 */ 438 public function test_error_when_exporter_missing_friendly_name() { 439 $this->_unset_exporter_key( 'exporter_friendly_name' ); 440 $this->_make_ajax_call(); 441 442 $this->assertFalse( $this->_last_response_parsed['success'] ); 443 $this->assertSame( 444 sprintf( 445 'Exporter array at index %s does not include a friendly name.', 446 self::$exporter_key 447 ), 448 $this->_last_response_parsed['data'] 449 ); 450 } 451 452 /** 453 * The function should send an error when an exporter is missing a callback. 454 * 455 * @since 4.9.9 456 */ 457 public function test_error_when_exporter_missing_callback() { 458 $this->_unset_exporter_key( 'callback' ); 459 $this->_make_ajax_call(); 460 461 $this->assertFalse( $this->_last_response_parsed['success'] ); 462 $this->assertSame( 463 sprintf( 464 'Exporter does not include a callback: %s.', 465 self::$exporter_friendly_name 466 ), 467 $this->_last_response_parsed['data'] 468 ); 469 } 470 471 /** 472 * The function should send an error when an exporter, at a given index, has an invalid callback. 473 * 474 * @since 4.9.9 475 */ 476 public function test_error_when_exporter_index_invalid_callback() { 477 $this->_set_exporter_callback( false ); 478 $this->_make_ajax_call(); 479 480 $this->assertFalse( $this->_last_response_parsed['success'] ); 481 $this->assertSame( 482 sprintf( 483 'Exporter callback is not a valid callback: %s.', 484 self::$exporter_friendly_name 485 ), 486 $this->_last_response_parsed['data'] 487 ); 488 } 489 490 /** 491 * When an exporter callback returns a WP_Error, it should be passed as the error. 492 * 493 * @since 4.9.9 494 */ 495 public function test_error_when_exporter_callback_returns_wp_error() { 496 $this->_set_exporter_callback( array( $this, 'callback_return_wp_error' ) ); 497 $this->_make_ajax_call(); 498 499 $this->assertFalse( $this->_last_response_parsed['success'] ); 500 $this->assertSame( 'passed_message', $this->_last_response_parsed['data'][0]['code'] ); 501 $this->assertSame( 'This is a WP_Error message.', $this->_last_response_parsed['data'][0]['message'] ); 502 } 503 504 /** 505 * Callback for exporter's response. 506 * 507 * @since 4.9.9 508 * 509 * @param string $email_address The requester's email address. 510 * @param int $page Page number. 511 * @return WP_Error WP_Error instance. 512 */ 513 public function callback_return_wp_error( $email_address, $page = 1 ) { 514 return new WP_Error( 'passed_message', 'This is a WP_Error message.' ); 515 } 516 517 /** 518 * The function should send an error when an exporter, at a given index, is missing an array response. 519 * 520 * @since 4.9.9 521 */ 522 public function test_error_when_exporter_index_invalid_response() { 523 $this->_set_exporter_callback( '__return_null' ); 524 $this->_make_ajax_call(); 525 526 $this->assertFalse( $this->_last_response_parsed['success'] ); 527 $this->assertSame( 528 sprintf( 529 'Expected response as an array from exporter: %s.', 530 self::$exporter_friendly_name 531 ), 532 $this->_last_response_parsed['data'] 533 ); 534 } 535 536 /** 537 * The function should send an error when an exporter is missing data in array response. 538 * 539 * @since 4.9.9 540 */ 541 public function test_error_when_exporter_missing_data_response() { 542 $this->_set_exporter_callback( array( $this, 'callback_missing_data_response' ) ); 543 $this->_make_ajax_call(); 544 545 $this->assertFalse( $this->_last_response_parsed['success'] ); 546 $this->assertSame( 547 sprintf( 548 'Expected data in response array from exporter: %s.', 549 self::$exporter_friendly_name 550 ), 551 $this->_last_response_parsed['data'] 552 ); 553 } 554 555 /** 556 * Callback for exporter's response. 557 * 558 * @since 4.9.9 559 * 560 * @param string $email_address The requester's email address. 561 * @param int $page Page number. 562 * @return array $return Export data. 563 */ 564 public function callback_missing_data_response( $email_address, $page = 1 ) { 565 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 566 unset( $response['data'] ); // Missing data part of response. 567 568 return $response; 569 } 570 571 /** 572 * The function should send an error when an exporter is missing 'data' array in array response. 573 * 574 * @since 4.9.9 575 */ 576 public function test_function_should_error_when_exporter_missing_data_array_response() { 577 $this->_set_exporter_callback( array( $this, 'callback_missing_data_array_response' ) ); 578 $this->_make_ajax_call(); 579 580 $this->assertFalse( $this->_last_response_parsed['success'] ); 581 $this->assertSame( 582 sprintf( 583 'Expected data array in response array from exporter: %s.', 584 self::$exporter_friendly_name 585 ), 586 $this->_last_response_parsed['data'] 587 ); 588 } 589 590 /** 591 * Callback for exporter's response. 592 * 593 * @since 4.9.9 594 * 595 * @param string $email_address The requester's email address. 596 * @param int $page Page number. 597 * 598 * @return array $return Export data. 599 */ 600 public function callback_missing_data_array_response( $email_address, $page = 1 ) { 601 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 602 $response['data'] = false; // Not an array. 603 return $response; 604 } 605 606 /** 607 * The function should send an error when an exporter is missing 'done' in array response. 608 * 609 * @since 4.9.9 610 */ 611 public function test_error_when_exporter_missing_done_response() { 612 $this->_set_exporter_callback( array( $this, 'callback_missing_done_response' ) ); 613 $this->_make_ajax_call(); 614 615 $this->assertFalse( $this->_last_response_parsed['success'] ); 616 $this->assertSame( 617 sprintf( 618 'Expected done (boolean) in response array from exporter: %s.', 619 self::$exporter_friendly_name 620 ), 621 $this->_last_response_parsed['data'] 622 ); 623 } 624 625 /** 626 * Remove the response's done flag. 627 * 628 * @since 4.9.9 629 * 630 * @param string $email_address The requester's email address. 631 * @param int $page Page number. 632 * @return array $return Export data. 633 */ 634 public function callback_missing_done_response( $email_address, $page = 1 ) { 635 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 636 unset( $response['done'] ); 637 638 return $response; 639 } 640 641 /** 642 * The function should successfully send exporter data response when the current user has the required capability. 643 * 644 * @since 4.9.9 645 */ 646 public function test_succeeds_when_current_user_has_required_capability() { 647 $this->_make_ajax_call(); 648 649 $this->assertTrue( $this->_last_response_parsed['success'] ); 650 $this->assertTrue( current_user_can( 'export_others_personal_data' ) ); 651 $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] ); 652 $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] ); 653 $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] ); 654 } 655 656 /** 657 * The function's output should be filterable with the `wp_privacy_personal_data_export_page` filter. 658 * 659 * @since 4.9.9 660 */ 661 public function test_output_should_be_filterable() { 662 add_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20, 7 ); 663 $this->_make_ajax_call(); 664 665 $expected_group_label = sprintf( 666 '%s-%s-%s-%s-%s-%s', 667 self::$exporter, 668 self::$page, 669 self::$request_email, 670 self::$request_id, 671 self::$send_as_email, 672 self::$exporter_key 673 ); 674 675 $this->assertTrue( $this->_last_response_parsed['success'] ); 676 $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] ); 677 $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] ); 678 $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] ); 679 $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] ); 680 $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] ); 681 } 682 683 /** 684 * Filter exporter's data response. 685 * 686 * @since 4.9.9 687 * 688 * @param array $response The personal data for the given exporter and page. 689 * @param int $exporter_index The index of the exporter that provided this data. 690 * @param string $email_address The email address associated with this personal data. 691 * @param int $page The page for this response. 692 * @param int $request_id The privacy request post ID associated with this request. 693 * @param bool $send_as_email Whether the final results of the export should be emailed to the user. 694 * @param string $exporter_key The key (slug) of the exporter that provided this data. 695 * 696 * @return array $response The personal data for the given exporter and page. 697 */ 698 public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) { 699 $group_label = sprintf( 700 '%s-%s-%s-%s-%s-%s', 701 $exporter_index, 702 $page, 703 $email_address, 704 $request_id, 705 $send_as_email, 706 $exporter_key 707 ); 708 $response['group_label'] = $group_label; 709 $response['group_id'] = 'filtered_group_id'; 710 $response['item_id'] = 'filtered_item_id'; 711 $response['data'][0]['name'] = 'filtered_name'; 712 $response['data'][0]['value'] = 'filtered_value'; 713 714 return $response; 715 } 716 717 /** 718 * Filter to register a custom personal data exporter. 719 * 720 * @since 4.9.9 721 * 722 * @param array $exporters An array of personal data exporters. 723 * @return array $exporters An array of personal data exporters. 724 */ 725 public function filter_register_custom_personal_data_exporter( $exporters ) { 726 $exporters[ self::$exporter_key ] = array( 727 'exporter_friendly_name' => self::$exporter_friendly_name, 728 'callback' => array( $this, 'callback_custom_personal_data_exporter' ), 729 ); 730 return $exporters; 731 } 732 733 /** 734 * Callback for a custom personal data exporter. 735 * 736 * @since 4.9.9 737 * 738 * @param string $email_address The requester's email address. 739 * @param int $page Page number. 740 * 741 * @return array $response Export data response. 742 */ 743 public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) { 744 $data_to_export = array(); 745 746 if ( 1 === $page ) { 747 $data_to_export = array( 748 'group_id' => self::$exporter_key . '-group-id', 749 'group_label' => self::$exporter_key . '-group-label', 750 'item_id' => self::$exporter_key . '-item-id', 751 'data' => array( 752 array( 753 'name' => 'Email', 754 'value' => $email_address, 755 ), 756 ), 757 ); 758 } 759 760 return array( 761 'data' => $data_to_export, 762 'done' => true, 763 ); 764 } 765 766 /** 767 * Helper function for ajax handler. 768 * 769 * @since 4.9.9 770 * 771 * @param array $args Ajax request arguments. 772 */ 773 protected function _make_ajax_call( $args = array() ) { 774 $this->_last_response_parsed = null; 775 $this->_last_response = ''; 776 777 $defaults = array( 778 'action' => self::$action, 779 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ), 780 'exporter' => self::$exporter, 781 'page' => self::$page, 782 'sendAsEmail' => self::$send_as_email, 783 'id' => self::$request_id, 784 ); 785 786 $_POST = wp_parse_args( $args, $defaults ); 787 788 try { 789 $this->_handleAjax( self::$action ); 790 } catch ( WPAjaxDieContinueException $e ) { 791 unset( $e ); 792 } 793 794 if ( $this->_last_response ) { 795 $this->_last_response_parsed = json_decode( $this->_last_response, true ); 796 } 797 } 798 }