Ticket #43438: 43438.16.diff
File 43438.16.diff, 49.0 KB (added by , 7 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..3533fc2
- + 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 * A response index 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 exporter 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 * Unsets an array index in the test eraser. 156 * 157 * @since 4.9.9 158 * 159 * @param array $erasers Erasers. 160 * @return array $erasers Erasers. 161 */ 162 public function filter_unset_eraser_index( $erasers ) { 163 if ( ! empty( $this->key_to_unset ) ) { 164 unset( $erasers[ self::$eraser_key ][ $this->key_to_unset ] ); 165 } 166 167 return $erasers; 168 } 169 170 /** 171 * Change the test eraser callback to a specified value. 172 * 173 * @since 4.9.9 174 * 175 * @param array $erasers List of data erasers. 176 * @return array $erasersList of data erasers. 177 */ 178 public function filter_eraser_callback_value( $erasers ) { 179 $erasers[ self::$eraser_key ]['callback'] = $this->new_callback_value; 180 181 return $erasers; 182 } 183 184 /** 185 * The function should send an error when the request ID is missing. 186 * 187 * @since 4.9.9 188 * 189 * @ticket 43438 190 */ 191 public function test_error_when_missing_request_id() { 192 $this->assertNotWPError( self::$request_id ); 193 194 // Set up a request. 195 $this->_make_ajax_call( 196 array( 197 'id' => null, // Missing request ID. 198 ) 199 ); 200 201 $this->assertFalse( $this->_last_response_parsed['success'] ); 202 $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] ); 203 } 204 205 /** 206 * The function should send an error when the request ID is less than 1. 207 * 208 * @since 4.9.9 209 * 210 * @ticket 43438 211 */ 212 public function test_error_when_request_id_invalid() { 213 $this->assertNotWPError( self::$request_id ); 214 215 // Set up a request. 216 $this->_make_ajax_call( 217 array( 218 'id' => -1, // Invalid request ID. 219 ) 220 ); 221 222 $this->assertFalse( $this->_last_response_parsed['success'] ); 223 $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] ); 224 } 225 226 /** 227 * The function should send an error when the current user is missing required capabilities. 228 * 229 * @since 4.9.9 230 * 231 * @ticket 43438 232 */ 233 public function test_error_when_current_user_missing_required_capabilities() { 234 $this->_setRole( 'author' ); 235 236 $this->assertFalse( current_user_can( 'erase_others_personal_data' ) ); 237 $this->assertFalse( current_user_can( 'delete_users' ) ); 238 239 $this->_make_ajax_call(); 240 241 $this->assertFalse( $this->_last_response_parsed['success'] ); 242 $this->assertSame( 'Invalid request.', $this->_last_response_parsed['data'] ); 243 } 244 245 /** 246 * The function should send an error when the nonce does not validate. 247 * 248 * @since 4.9.9 249 */ 250 public function test_failure_with_invalid_nonce() { 251 $this->setExpectedException( 'WPAjaxDieStopException', '-1' ); 252 253 $this->_make_ajax_call( 254 array( 255 'security' => 'invalid-nonce', 256 ) 257 ); 258 } 259 260 /** 261 * The function should send an error when the request type is incorrect. 262 * 263 * @since 4.9.9 264 */ 265 public function test_error_when_incorrect_request_type() { 266 $request_id = wp_create_user_request( 267 'export-request@example.com', 268 'export_personal_data' // Incorrect request type, expects 'remove_personal_data'. 269 ); 270 271 $this->_make_ajax_call( 272 array( 273 'security' => wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ), 274 'id' => $request_id, 275 ) 276 ); 277 278 $this->assertFalse( $this->_last_response_parsed['success'] ); 279 $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] ); 280 } 281 282 /** 283 * The function should send an error when the request email is invalid. 284 * 285 * @since 4.9.9 286 */ 287 public function test_error_when_invalid_email() { 288 wp_update_post( 289 array( 290 'ID' => self::$request_id, 291 'post_title' => '', // Invalid requester's email address. 292 ) 293 ); 294 295 $this->_make_ajax_call(); 296 297 $this->assertFalse( $this->_last_response_parsed['success'] ); 298 $this->assertSame( 'Invalid email address in request.', $this->_last_response_parsed['data'] ); 299 } 300 301 /** 302 * The function should send an error when the eraser index is missing. 303 * 304 * @since 4.9.9 305 */ 306 public function test_error_when_missing_eraser_index() { 307 $this->_make_ajax_call( 308 array( 309 'eraser' => null, // Missing eraser index. 310 ) 311 ); 312 313 $this->assertFalse( $this->_last_response_parsed['success'] ); 314 $this->assertSame( 'Missing eraser index.', $this->_last_response_parsed['data'] ); 315 } 316 317 /** 318 * The function should send an error when the page index is missing. 319 * 320 * @since 4.9.9 321 */ 322 public function test_error_when_missing_page_index() { 323 $this->_make_ajax_call( 324 array( 325 'page' => null, // Missing page index. 326 ) 327 ); 328 329 $this->assertFalse( $this->_last_response_parsed['success'] ); 330 $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] ); 331 } 332 333 /** 334 * The function should send an error when the eraser index is negative. 335 * 336 * @since 4.9.9 337 */ 338 public function test_error_when_negative_eraser_index() { 339 $this->_make_ajax_call( 340 array( 341 'eraser' => -1, // Negative eraser index. 342 ) 343 ); 344 345 $this->assertFalse( $this->_last_response_parsed['success'] ); 346 $this->assertSame( 'Eraser index cannot be less than one.', $this->_last_response_parsed['data'] ); 347 } 348 349 /** 350 * The function should send an error when the eraser index is out of range. 351 * 352 * @since 4.9.9 353 */ 354 public function test_error_when_eraser_index_out_of_range() { 355 $this->_make_ajax_call( 356 array( 357 'eraser' => PHP_INT_MAX, // Out of range eraser index. 358 ) 359 ); 360 361 $this->assertFalse( $this->_last_response_parsed['success'] ); 362 $this->assertSame( 'Eraser index is out of range.', $this->_last_response_parsed['data'] ); 363 } 364 365 /** 366 * The function should send an error when the page index is less than one. 367 * 368 * @since 4.9.9 369 */ 370 public function test_error_when_page_index_less_than_one() { 371 $this->_make_ajax_call( 372 array( 373 'page' => 0, // Page index less than one. 374 ) 375 ); 376 377 $this->assertFalse( $this->_last_response_parsed['success'] ); 378 $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] ); 379 } 380 381 /** 382 * The function should send an error when an eraser is not an array. 383 * 384 * @since 4.9.9 385 */ 386 public function test_error_when_eraser_not_array() { 387 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_not_array' ), 20 ); 388 $this->_make_ajax_call(); 389 390 $this->assertFalse( $this->_last_response_parsed['success'] ); 391 $this->assertSame( 392 sprintf( 393 'Expected an array describing the eraser at index %s.', 394 self::$eraser 395 ), 396 $this->_last_response_parsed['data'] 397 ); 398 } 399 400 /** 401 * Change the eraser information to an invalid value. 402 * 403 * @since 4.9.9 404 * 405 * @param array $erasers Erasers. 406 * @return array $erasers Erasers. 407 */ 408 public function filter_eraser_not_array( $erasers ) { 409 $erasers[ self::$eraser_key ] = false; 410 411 return $erasers; 412 } 413 414 /** 415 * The function should send an error when an eraser is missing a friendly name. 416 * 417 * @since 4.9.9 418 */ 419 public function test_error_when_eraser_missing_friendly_name() { 420 $this->key_to_unset = 'eraser_friendly_name'; 421 422 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 423 $this->_make_ajax_call(); 424 425 $this->assertFalse( $this->_last_response_parsed['success'] ); 426 $this->assertSame( 427 sprintf( 428 'Eraser array at index %s does not include a friendly name.', 429 self::$eraser 430 ), 431 $this->_last_response_parsed['data'] 432 ); 433 } 434 435 /** 436 * The function should send an error when an eraser is missing a callback. 437 * 438 * @since 4.9.9 439 */ 440 public function test_error_when_eraser_missing_callback() { 441 $this->key_to_unset = 'callback'; 442 443 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 444 $this->_make_ajax_call(); 445 446 $this->assertFalse( $this->_last_response_parsed['success'] ); 447 $this->assertSame( 448 sprintf( 449 'Eraser does not include a callback: %s.', 450 self::$eraser_friendly_name 451 ), 452 $this->_last_response_parsed['data'] 453 ); 454 } 455 456 /** 457 * The function should send an error when an eraser, at a given index, has an invalid callback. 458 * 459 * @since 4.9.9 460 */ 461 public function test_error_when_eraser_index_invalid_callback() { 462 $this->new_callback_value = false; 463 464 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 ); 465 $this->_make_ajax_call(); 466 467 $this->assertFalse( $this->_last_response_parsed['success'] ); 468 $this->assertSame( 469 sprintf( 470 'Eraser callback is not valid: %s.', 471 self::$eraser_friendly_name 472 ), 473 $this->_last_response_parsed['data'] 474 ); 475 } 476 477 /** 478 * The function should send an error when an eraser, at a given index, is missing an array response. 479 * 480 * @since 4.9.9 481 */ 482 public function test_error_when_eraser_index_invalid_response() { 483 $this->new_callback_value = '__return_null'; 484 485 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 ); 486 $this->_make_ajax_call(); 487 488 $this->assertFalse( $this->_last_response_parsed['success'] ); 489 $this->assertSame( 490 sprintf( 491 'Did not receive array from %1$s eraser (index %2$d).', 492 self::$eraser_friendly_name, 493 self::$eraser 494 ), 495 $this->_last_response_parsed['data'] 496 ); 497 } 498 499 /** 500 * Change the test eraser's callback to one that unsets an index in the response. 501 * 502 * @since 4.9.9 503 * 504 * @param array $erasers Erasers. 505 * @return array $erasers Erasers. 506 */ 507 public function filter_callback_unset_index( $erasers ) { 508 $erasers[ self::$eraser_key ]['callback'] = array( $this, 'filter_unset_response_index' ); 509 510 return $erasers; 511 } 512 513 /** 514 * Unsets an array index in a response. 515 * 516 * @since 4.9.9 517 * 518 * @param string $email_address The requester's email address. 519 * @param int $page Page number. 520 * @return array $return Export data. 521 */ 522 public function filter_unset_response_index( $email_address, $page = 1 ) { 523 $response = $this->callback_personal_data_eraser( $email_address, $page ); 524 525 if ( ! empty( $this->key_to_unset ) ) { 526 unset( $response[ $this->key_to_unset ] ); 527 } 528 529 return $response; 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->key_to_unset = 'items_removed'; 539 540 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 541 $this->_make_ajax_call(); 542 543 $this->assertFalse( $this->_last_response_parsed['success'] ); 544 $this->assertSame( 545 sprintf( 546 'Expected items_removed key in response array from %1$s eraser (index %2$d).', 547 self::$eraser_friendly_name, 548 self::$eraser 549 ), 550 $this->_last_response_parsed['data'] 551 ); 552 } 553 554 /** 555 * The function should send an error when missing an items_retained index. 556 * 557 * @since 4.9.9 558 */ 559 public function test_error_when_eraser_items_retained_missing() { 560 $this->key_to_unset = 'items_retained'; 561 562 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 563 $this->_make_ajax_call(); 564 565 $this->assertFalse( $this->_last_response_parsed['success'] ); 566 $this->assertSame( 567 sprintf( 568 'Expected items_retained key in response array from %1$s eraser (index %2$d).', 569 self::$eraser_friendly_name, 570 self::$eraser 571 ), 572 $this->_last_response_parsed['data'] 573 ); 574 } 575 576 /** 577 * The function should send an error when missing a messages index. 578 * 579 * @since 4.9.9 580 */ 581 public function test_error_when_eraser_messages_missing() { 582 $this->key_to_unset = 'messages'; 583 584 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 585 $this->_make_ajax_call(); 586 587 $this->assertFalse( $this->_last_response_parsed['success'] ); 588 $this->assertSame( 589 sprintf( 590 'Expected messages key in response array from %1$s eraser (index %2$d).', 591 self::$eraser_friendly_name, 592 self::$eraser 593 ), 594 $this->_last_response_parsed['data'] 595 ); 596 } 597 598 /** 599 * The function should send an error when the messages index is not an array. 600 * 601 * @since 4.9.9 602 */ 603 public function test_error_when_eraser_messages_not_array() { 604 $this->new_callback_value = array( $this, 'filter_response_messages_invalid' ); 605 606 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 ); 607 $this->_make_ajax_call(); 608 609 $this->assertFalse( $this->_last_response_parsed['success'] ); 610 $this->assertSame( 611 sprintf( 612 'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).', 613 self::$eraser_friendly_name, 614 self::$eraser 615 ), 616 $this->_last_response_parsed['data'] 617 ); 618 } 619 620 /** 621 * Change the messages index to an invalid value (not an array). 622 * 623 * @since 4.9.9 624 * 625 * @param string $email_address The requester's email address. 626 * @param int $page Page number. 627 * @return array $return Export data. 628 */ 629 public function filter_response_messages_invalid( $email_address, $page = 1 ) { 630 $response = $this->callback_personal_data_eraser( $email_address, $page ); 631 $response['messages'] = true; 632 633 return $response; 634 } 635 636 /** 637 * The function should send an error when an eraser is missing 'done' in array response. 638 * 639 * @since 4.9.9 640 */ 641 public function test_error_when_eraser_missing_done_response() { 642 $this->key_to_unset = 'done'; 643 644 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 645 $this->_make_ajax_call(); 646 647 $this->assertFalse( $this->_last_response_parsed['success'] ); 648 $this->assertSame( 649 sprintf( 650 'Expected done flag in response array from %1$s eraser (index %2$d).', 651 self::$eraser_friendly_name, 652 self::$eraser 653 ), 654 $this->_last_response_parsed['data'] 655 ); 656 } 657 658 /** 659 * The function should successfully send erasers response data when the current user has the required 660 * capabilities. 661 * 662 * @since 4.9.9 663 * 664 * @ticket 43438 665 */ 666 public function test_success_when_current_user_has_required_capabilities() { 667 $this->assertTrue( current_user_can( 'erase_others_personal_data' ) ); 668 $this->assertTrue( current_user_can( 'delete_users' ) ); 669 670 $this->_make_ajax_call(); 671 672 $this->assertSame( 'A message regarding retained data for requester@example.com.', $this->_last_response_parsed['data']['messages'][0] ); 673 $this->assertTrue( $this->_last_response_parsed['success'] ); 674 $this->assertTrue( $this->_last_response_parsed['data']['items_removed'] ); 675 $this->assertTrue( $this->_last_response_parsed['data']['items_retained'] ); 676 $this->assertTrue( $this->_last_response_parsed['data']['done'] ); 677 } 678 679 /** 680 * Test that the function's output should be filterable with the `wp_privacy_personal_data_erasure_page` filter. 681 * 682 * @since 4.9.9 683 */ 684 public function test_output_should_be_filterable() { 685 add_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_exporter_data_response' ), 20, 6 ); 686 $this->_make_ajax_call(); 687 688 $expected_new_index = self::$request_email . '-' . self::$request_id . '-' . self::$eraser_key; 689 690 $this->assertTrue( $this->_last_response_parsed['success'] ); 691 $this->assertSame( 'filtered removed', $this->_last_response_parsed['data']['items_removed'] ); 692 $this->assertSame( 'filtered retained', $this->_last_response_parsed['data']['items_retained'] ); 693 $this->assertSame( array( 'filtered messages' ), $this->_last_response_parsed['data']['messages'] ); 694 $this->assertSame( 'filtered done', $this->_last_response_parsed['data']['done'] ); 695 $this->assertSame( $expected_new_index, $this->_last_response_parsed['data']['new_index'] ); 696 } 697 698 /** 699 * Filters the exporter response. 700 * 701 * @since 4.9.9 702 * 703 * @param array $response The personal data for the given exporter and page. 704 * @param int $eraser_index The index of the eraser that provided this data. 705 * @param string $email_address The email address associated with this personal data. 706 * @param int $page The page for this response. 707 * @param int $request_id The privacy request post ID associated with this request. 708 * @param string $eraser_key The key (slug) of the eraser that provided this data. 709 * @return array Filtered export response. 710 */ 711 public function filter_exporter_data_response( $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ) { 712 $response['items_removed'] = 'filtered removed'; 713 $response['items_retained'] = 'filtered retained'; 714 $response['messages'] = array( 'filtered messages' ); 715 $response['done'] = 'filtered done'; 716 $response['new_index'] = $email_address . '-' . $request_id . '-' . $eraser_key; 717 718 return $response; 719 } 720 721 /** 722 * Register handler for a custom personal data eraser. 723 * 724 * @since 4.9.9 725 * 726 * @param array $erasers An array of personal data erasers. 727 * @return array $erasers An array of personal data erasers. 728 */ 729 public function register_custom_personal_data_eraser( $erasers ) { 730 $erasers[ self::$eraser_key ] = array( 731 'eraser_friendly_name' => self::$eraser_friendly_name, 732 'callback' => array( $this, 'callback_personal_data_eraser' ), 733 ); 734 return $erasers; 735 } 736 737 /** 738 * Custom Personal Data Eraser. 739 * 740 * @since 4.9.9 741 * 742 * @param string $email_address The comment author email address. 743 * @param int $page Page number. 744 * @return array $return Erase data. 745 */ 746 public function callback_personal_data_eraser( $email_address, $page = 1 ) { 747 if ( 1 === $page ) { 748 return array( 749 'items_removed' => true, 750 'items_retained' => true, 751 'messages' => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ), 752 'done' => true, 753 ); 754 } 755 756 return array( 757 'items_removed' => false, 758 'items_retained' => false, 759 'messages' => array(), 760 'done' => true, 761 ); 762 } 763 764 /** 765 * Helper function for ajax handler. 766 * 767 * @since 4.9.9 768 * 769 * @param array $args Ajax request arguments. 770 */ 771 protected function _make_ajax_call( $args = array() ) { 772 $this->_last_response_parsed = null; 773 $this->_last_response = ''; 774 775 $defaults = array( 776 'action' => self::$action, 777 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ), 778 'page' => self::$page, 779 'id' => self::$request_id, 780 'eraser' => self::$eraser, 781 ); 782 783 $_POST = wp_parse_args( $args, $defaults ); 784 785 try { 786 $this->_handleAjax( self::$action ); 787 } catch ( WPAjaxDieContinueException $e ) { 788 unset( $e ); 789 } 790 791 if ( $this->_last_response ) { 792 $this->_last_response_parsed = json_decode( $this->_last_response, true ); 793 } 794 } 795 } -
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..98c63b2
- + 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 ajax1 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 // Make sure the exporter response is not modified and avoid e.g. writing export file to disk. 146 remove_all_filters( 'wp_privacy_personal_data_export_page' ); 147 148 // Only use our custom privacy personal data exporter. 149 remove_all_filters( 'wp_privacy_personal_data_exporters' ); 150 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) ); 151 152 $this->_setRole( 'administrator' ); 153 } 154 155 /** 156 * Clean up after each test method. 157 */ 158 public function tearDown() { 159 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) ); 160 $this->key_to_unset = ''; 161 $this->new_callback_value = ''; 162 163 parent::tearDown(); 164 } 165 166 /** 167 * Unset a specified key in the test exporter array. 168 * 169 * @param array $exporters List of data exporters. 170 * @return array $exporters List of data exporters. 171 */ 172 public function filter_unset_exporter_value( $exporters ) { 173 if ( ! empty( $this->key_to_unset ) ) { 174 unset( $exporters[ self::$exporter_key ][ $this->key_to_unset ] ); 175 } 176 177 return $exporters; 178 } 179 180 /** 181 * Change the test exporter callback to a specified value. 182 * 183 * @since 4.9.9 184 * 185 * @param array $exporters List of data exporters. 186 * @return array $exporters List of data exporters. 187 */ 188 public function filter_exporter_callback_index_value( $exporters ) { 189 $exporters[ self::$exporter_key ]['callback'] = $this->new_callback_value; 190 191 return $exporters; 192 } 193 194 /** 195 * The function should send an error when the request ID is missing. 196 * 197 * @since 4.9.9 198 */ 199 public function test_error_when_missing_request_id() { 200 $this->_make_ajax_call( 201 array( 202 'id' => null, // Missing request ID. 203 ) 204 ); 205 206 $this->assertFalse( $this->_last_response_parsed['success'] ); 207 $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] ); 208 } 209 210 /** 211 * The function should send an error when the request ID is less than 1. 212 * 213 * @since 4.9.9 214 */ 215 public function test_error_when_invalid_id() { 216 $this->_make_ajax_call( 217 array( 218 'id' => -1, // Invalid request ID, less than 1. 219 ) 220 ); 221 222 $this->assertFalse( $this->_last_response_parsed['success'] ); 223 $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] ); 224 } 225 226 /** 227 * The function should send an error when the current user is missing the required capability. 228 * 229 * @since 4.9.9 230 */ 231 public function test_error_when_current_user_missing_required_capability() { 232 $this->_setRole( 'author' ); 233 234 $this->_make_ajax_call(); 235 236 $this->assertFalse( $this->_last_response_parsed['success'] ); 237 $this->assertFalse( current_user_can( 'export_others_personal_data' ) ); 238 $this->assertSame( 'Invalid request.', $this->_last_response_parsed['data'] ); 239 } 240 241 /** 242 * The function should send an error when the nonce does not validate. 243 * 244 * @since 4.9.9 245 */ 246 public function test_failure_with_invalid_nonce() { 247 $this->setExpectedException( 'WPAjaxDieStopException', '-1' ); 248 249 $this->_make_ajax_call( 250 array( 251 'security' => 'invalid-nonce', 252 ) 253 ); 254 } 255 256 /** 257 * The function should send an error when the request type is incorrect. 258 * 259 * @since 4.9.9 260 */ 261 public function test_error_when_incorrect_request_type() { 262 $request_id = wp_create_user_request( 263 'erase-request@example.com', 264 'remove_personal_data' // Incorrect request type, expects 'export_personal_data'. 265 ); 266 267 $this->_make_ajax_call( 268 array( 269 'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ), 270 'id' => $request_id, 271 ) 272 ); 273 274 $this->assertFalse( $this->_last_response_parsed['success'] ); 275 $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] ); 276 } 277 278 /** 279 * The function should send an error when the requester's email address is invalid. 280 * 281 * @since 4.9.9 282 */ 283 public function test_error_when_invalid_email_address() { 284 wp_update_post( 285 array( 286 'ID' => self::$request_id, 287 'post_title' => '', // Invalid requester's email address. 288 ) 289 ); 290 291 $this->_make_ajax_call(); 292 293 $this->assertFalse( $this->_last_response_parsed['success'] ); 294 $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] ); 295 } 296 297 /** 298 * The function should send an error when the exporter index is missing. 299 * 300 * @since 4.9.9 301 */ 302 public function test_error_when_missing_exporter_index() { 303 $this->_make_ajax_call( 304 array( 305 'exporter' => null, // Missing exporter index. 306 ) 307 ); 308 309 $this->assertFalse( $this->_last_response_parsed['success'] ); 310 $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] ); 311 } 312 313 /** 314 * The function should send an error when the page index is missing. 315 * 316 * @since 4.9.9 317 */ 318 public function test_error_when_missing_page_index() { 319 $this->_make_ajax_call( 320 array( 321 'page' => null, // Missing page index. 322 ) 323 ); 324 325 $this->assertFalse( $this->_last_response_parsed['success'] ); 326 $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] ); 327 } 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 4.9.9 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 339 $this->assertFalse( $this->_last_response_parsed['success'] ); 340 $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] ); 341 } 342 343 /** 344 * The function should send an error when the exporter index is negative. 345 * 346 * @since 4.9.9 347 */ 348 public function test_error_when_negative_exporter_index() { 349 $this->_make_ajax_call( 350 array( 351 'exporter' => -1, // Negative exporter index. 352 ) 353 ); 354 355 $this->assertFalse( $this->_last_response_parsed['success'] ); 356 $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] ); 357 } 358 359 /** 360 * The function should send an error when the exporter index is out of range. 361 * 362 * @since 4.9.9 363 */ 364 public function test_error_when_exporter_index_out_of_range() { 365 $this->_make_ajax_call( 366 array( 367 'exporter' => PHP_INT_MAX, // Out of range exporter index. 368 ) 369 ); 370 371 $this->assertFalse( $this->_last_response_parsed['success'] ); 372 $this->assertSame( 'Exporter index is out of range.', $this->_last_response_parsed['data'] ); 373 } 374 375 /** 376 * The function should send an error when the page index is less than one. 377 * 378 * @since 4.9.9 379 */ 380 public function test_error_when_page_index_less_than_one() { 381 $this->_make_ajax_call( 382 array( 383 'page' => 0, // Page index less than one. 384 ) 385 ); 386 387 $this->assertFalse( $this->_last_response_parsed['success'] ); 388 $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] ); 389 } 390 391 /** 392 * The function should send an error when an exporter is not an array. 393 * 394 * @since 4.9.9 395 */ 396 public function test_error_when_exporter_not_array() { 397 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_not_array' ), 20 ); 398 $this->_make_ajax_call(); 399 400 $this->assertFalse( $this->_last_response_parsed['success'] ); 401 $this->assertSame( 402 sprintf( 403 'Expected an array describing the exporter at index %s.', 404 self::$exporter_key 405 ), 406 $this->_last_response_parsed['data'] 407 ); 408 } 409 410 /** 411 * Change the exporter information to an invalid value. 412 * 413 * @since 4.9.9 414 * 415 * @param array $exporters Exporters. 416 * @return array $exporters Exporters. 417 */ 418 public function filter_exporter_not_array( $exporters ) { 419 $exporters[ self::$exporter_key ] = false; 420 421 return $exporters; 422 } 423 424 /** 425 * The function should send an error when an exporter is missing a friendly name. 426 * 427 * @since 4.9.9 428 */ 429 public function test_error_when_exporter_missing_friendly_name() { 430 $this->key_to_unset = 'exporter_friendly_name'; 431 432 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_value' ), 20 ); 433 $this->_make_ajax_call(); 434 435 $this->assertFalse( $this->_last_response_parsed['success'] ); 436 $this->assertSame( 437 sprintf( 438 'Exporter array at index %s does not include a friendly name.', 439 self::$exporter_key 440 ), 441 $this->_last_response_parsed['data'] 442 ); 443 } 444 445 /** 446 * The function should send an error when an exporter is missing a callback. 447 * 448 * @since 4.9.9 449 */ 450 public function test_error_when_exporter_missing_callback() { 451 $this->key_to_unset = 'callback'; 452 453 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_value' ), 20 ); 454 $this->_make_ajax_call(); 455 456 $this->assertFalse( $this->_last_response_parsed['success'] ); 457 $this->assertSame( 458 sprintf( 459 'Exporter does not include a callback: %s.', 460 self::$exporter_friendly_name 461 ), 462 $this->_last_response_parsed['data'] 463 ); 464 } 465 466 /** 467 * The function should send an error when an exporter, at a given index, has an invalid callback. 468 * 469 * @since 4.9.9 470 */ 471 public function test_error_when_exporter_index_invalid_callback() { 472 $this->new_callback_value = false; 473 474 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 475 $this->_make_ajax_call(); 476 477 $this->assertFalse( $this->_last_response_parsed['success'] ); 478 $this->assertSame( 479 sprintf( 480 'Exporter callback is not a valid callback: %s.', 481 self::$exporter_friendly_name 482 ), 483 $this->_last_response_parsed['data'] 484 ); 485 } 486 487 /** 488 * When an exporter callback returns a WP_Error, it should be passed as the error. 489 * 490 * @since 4.9.9 491 */ 492 public function test_error_when_exporter_callback_returns_wp_error() { 493 $this->new_callback_value = array( $this, 'callback_return_wp_error' ); 494 495 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 496 $this->_make_ajax_call(); 497 498 $this->assertFalse( $this->_last_response_parsed['success'] ); 499 $this->assertSame( 'passed_message', $this->_last_response_parsed['data'][0]['code'] ); 500 $this->assertSame( 'This is a WP_Error message.', $this->_last_response_parsed['data'][0]['message'] ); 501 } 502 503 /** 504 * Callback for exporter's response. 505 * 506 * @since 4.9.9 507 * 508 * @param string $email_address The requester's email address. 509 * @param int $page Page number. 510 * @return WP_Error WP_Error instance. 511 */ 512 public function callback_return_wp_error( $email_address, $page = 1 ) { 513 return new WP_Error( 'passed_message', 'This is a WP_Error message.' ); 514 } 515 516 /** 517 * The function should send an error when an exporter, at a given index, is missing an array response. 518 * 519 * @since 4.9.9 520 */ 521 public function test_error_when_exporter_index_invalid_response() { 522 $this->new_callback_value = '__return_null'; 523 524 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 525 $this->_make_ajax_call(); 526 527 $this->assertFalse( $this->_last_response_parsed['success'] ); 528 $this->assertSame( 529 sprintf( 530 'Expected response as an array from exporter: %s.', 531 self::$exporter_friendly_name 532 ), 533 $this->_last_response_parsed['data'] 534 ); 535 } 536 537 /** 538 * The function should send an error when an exporter is missing data in array response. 539 * 540 * @since 4.9.9 541 */ 542 public function test_error_when_exporter_missing_data_response() { 543 $this->new_callback_value = array( $this, 'callback_missing_data_response' ); 544 545 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 546 $this->_make_ajax_call(); 547 548 $this->assertFalse( $this->_last_response_parsed['success'] ); 549 $this->assertSame( 550 sprintf( 551 'Expected data in response array from exporter: %s.', 552 self::$exporter_friendly_name 553 ), 554 $this->_last_response_parsed['data'] 555 ); 556 } 557 558 /** 559 * Callback for exporter's response. 560 * 561 * @since 4.9.9 562 * 563 * @param string $email_address The requester's email address. 564 * @param int $page Page number. 565 * @return array $return Export data. 566 */ 567 public function callback_missing_data_response( $email_address, $page = 1 ) { 568 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 569 unset( $response['data'] ); // Missing data part of response. 570 571 return $response; 572 } 573 574 /** 575 * The function should send an error when an exporter is missing 'data' array in array response. 576 * 577 * @since 4.9.9 578 */ 579 public function test_function_should_error_when_exporter_missing_data_array_response() { 580 $this->new_callback_value = array( $this, 'callback_missing_data_array_response' ); 581 582 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 583 $this->_make_ajax_call(); 584 585 $this->assertFalse( $this->_last_response_parsed['success'] ); 586 $this->assertSame( 587 sprintf( 588 'Expected data array in response array from exporter: %s.', 589 self::$exporter_friendly_name 590 ), 591 $this->_last_response_parsed['data'] 592 ); 593 } 594 595 /** 596 * Callback for exporter's response. 597 * 598 * @since 4.9.9 599 * 600 * @param string $email_address The requester's email address. 601 * @param int $page Page number. 602 * 603 * @return array $return Export data. 604 */ 605 public function callback_missing_data_array_response( $email_address, $page = 1 ) { 606 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 607 $response['data'] = false; // Not an array. 608 return $response; 609 } 610 611 /** 612 * The function should send an error when an exporter is missing 'done' in array response. 613 * 614 * @since 4.9.9 615 */ 616 public function test_error_when_exporter_missing_done_response() { 617 $this->new_callback_value = array( $this, 'callback_missing_done_response' ); 618 619 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 620 $this->_make_ajax_call(); 621 622 $this->assertFalse( $this->_last_response_parsed['success'] ); 623 $this->assertSame( 624 sprintf( 625 'Expected done (boolean) in response array from exporter: %s.', 626 self::$exporter_friendly_name 627 ), 628 $this->_last_response_parsed['data'] 629 ); 630 } 631 632 /** 633 * Remove the response's done flag. 634 * 635 * @since 4.9.9 636 * 637 * @param string $email_address The requester's email address. 638 * @param int $page Page number. 639 * @return array $return Export data. 640 */ 641 public function callback_missing_done_response( $email_address, $page = 1 ) { 642 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 643 unset( $response['done'] ); 644 645 return $response; 646 } 647 648 /** 649 * The function should successfully send exporter data response when the current user has the required capability. 650 * 651 * @since 4.9.9 652 */ 653 public function test_succeeds_when_current_user_has_required_capability() { 654 $this->_make_ajax_call(); 655 656 $this->assertTrue( $this->_last_response_parsed['success'] ); 657 $this->assertTrue( current_user_can( 'export_others_personal_data' ) ); 658 $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] ); 659 $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] ); 660 $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] ); 661 } 662 663 /** 664 * The function's output should be filterable with the `wp_privacy_personal_data_export_page` filter. 665 * 666 * @since 4.9.9 667 */ 668 public function test_output_should_be_filterable() { 669 add_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20, 7 ); 670 $this->_make_ajax_call(); 671 672 $expected_group_label = sprintf( 673 '%s-%s-%s-%s-%s-%s', 674 self::$exporter, 675 self::$page, 676 self::$request_email, 677 self::$request_id, 678 self::$send_as_email, 679 self::$exporter_key 680 ); 681 682 $this->assertTrue( $this->_last_response_parsed['success'] ); 683 $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] ); 684 $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] ); 685 $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] ); 686 $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] ); 687 $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] ); 688 } 689 690 /** 691 * Filter exporter's data response. 692 * 693 * @since 4.9.9 694 * 695 * @param array $response The personal data for the given exporter and page. 696 * @param int $exporter_index The index of the exporter that provided this data. 697 * @param string $email_address The email address associated with this personal data. 698 * @param int $page The page for this response. 699 * @param int $request_id The privacy request post ID associated with this request. 700 * @param bool $send_as_email Whether the final results of the export should be emailed to the user. 701 * @param string $exporter_key The key (slug) of the exporter that provided this data. 702 * 703 * @return array $response The personal data for the given exporter and page. 704 */ 705 public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) { 706 $group_label = sprintf( 707 '%s-%s-%s-%s-%s-%s', 708 $exporter_index, 709 $page, 710 $email_address, 711 $request_id, 712 $send_as_email, 713 $exporter_key 714 ); 715 $response['group_label'] = $group_label; 716 $response['group_id'] = 'filtered_group_id'; 717 $response['item_id'] = 'filtered_item_id'; 718 $response['data'][0]['name'] = 'filtered_name'; 719 $response['data'][0]['value'] = 'filtered_value'; 720 721 return $response; 722 } 723 724 /** 725 * Filter to register a custom personal data exporter. 726 * 727 * @since 4.9.9 728 * 729 * @param array $exporters An array of personal data exporters. 730 * @return array $exporters An array of personal data exporters. 731 */ 732 public function filter_register_custom_personal_data_exporter( $exporters ) { 733 $exporters[ self::$exporter_key ] = array( 734 'exporter_friendly_name' => self::$exporter_friendly_name, 735 'callback' => array( $this, 'callback_custom_personal_data_exporter' ), 736 ); 737 return $exporters; 738 } 739 740 /** 741 * Callback for a custom personal data exporter. 742 * 743 * @since 4.9.9 744 * 745 * @param string $email_address The requester's email address. 746 * @param int $page Page number. 747 * 748 * @return array $response Export data response. 749 */ 750 public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) { 751 $data_to_export = array(); 752 753 if ( 1 === $page ) { 754 $data_to_export = array( 755 'group_id' => self::$exporter_key . '-group-id', 756 'group_label' => self::$exporter_key . '-group-label', 757 'item_id' => self::$exporter_key . '-item-id', 758 'data' => array( 759 array( 760 'name' => 'Email', 761 'value' => $email_address, 762 ), 763 ), 764 ); 765 } 766 767 return array( 768 'data' => $data_to_export, 769 'done' => true, 770 ); 771 } 772 773 /** 774 * Helper function for ajax handler. 775 * 776 * @since 4.9.9 777 * 778 * @param array $args Ajax request arguments. 779 */ 780 protected function _make_ajax_call( $args = array() ) { 781 $this->_last_response_parsed = null; 782 $this->_last_response = ''; 783 784 $defaults = array( 785 'action' => self::$action, 786 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ), 787 'exporter' => self::$exporter, 788 'page' => self::$page, 789 'sendAsEmail' => self::$send_as_email, 790 'id' => self::$request_id, 791 ); 792 793 $_POST = wp_parse_args( $args, $defaults ); 794 795 try { 796 $this->_handleAjax( self::$action ); 797 } catch ( WPAjaxDieContinueException $e ) { 798 unset( $e ); 799 } 800 801 if ( $this->_last_response ) { 802 $this->_last_response_parsed = json_decode( $this->_last_response, true ); 803 } 804 } 805 }