Ticket #43438: 43438.15.2.diff
File 43438.15.2.diff, 48.9 KB (added by , 7 years ago) |
---|
-
src/wp-admin/includes/ajax-actions.php
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 ) { … … 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 4469 $ exporter_friendly_name = $exporter['exporter_friendly_name'];4471 $callback = $exporter['callback']; 4472 $response = call_user_func( $callback, $email_address, $page ); 4470 4473 4471 $response = call_user_func( $callback, $email_address, $page );4472 4474 if ( is_wp_error( $response ) ) { 4473 4475 wp_send_json_error( $response ); 4474 4476 } … … 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; … … 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 ) ) {4631 if ( ! array_key_exists( 'eraser_friendly_name', $eraser ) ) { 4630 4632 /* translators: %d: array index */ 4631 wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a callback.' ), $eraser_index ) );4633 wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a friendly name.' ), $eraser_index ) ); 4632 4634 } 4633 4635 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 ) ); 4636 $eraser_friendly_name = $eraser['eraser_friendly_name']; 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 ); 4637 4646 } 4638 4647 4639 if ( ! array_key_exists( 'eraser_friendly_name', $eraser ) ) { 4640 /* translators: %d: array index */ 4641 wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a friendly name.' ), $eraser_index ) ); 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 ); 4642 4656 } 4643 4657 4644 $callback = $eraser['callback']; 4645 $eraser_friendly_name = $eraser['eraser_friendly_name']; 4646 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
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() { -
tests/phpunit/tests/ajax/PrivacyErasePersonalData.php
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 * An value to change the test exporter callback to. 103 * 104 * @since 4.9.9 105 * 106 * @var string $key_to_unset 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, 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, 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' => 'not-a-valid-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( 'export-request@example.com', 'export_personal_data' ); 267 268 $this->_make_ajax_call( 269 array( 270 'security' => wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ), 271 'id' => $request_id, 272 ) 273 ); 274 275 $this->assertFalse( $this->_last_response_parsed['success'] ); 276 $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] ); 277 } 278 279 /** 280 * The function should send an error when the request email is invalid. 281 * 282 * @since 4.9.9 283 */ 284 public function test_error_when_invalid_email() { 285 wp_update_post( 286 array( 287 'ID' => self::$request_id, 288 'post_title' => '', 289 ) 290 ); 291 292 $this->_make_ajax_call(); 293 294 $this->assertFalse( $this->_last_response_parsed['success'] ); 295 $this->assertSame( 'Invalid email address in request.', $this->_last_response_parsed['data'] ); 296 } 297 298 /** 299 * The function should send an error when the eraser index is missing. 300 * 301 * @since 4.9.9 302 */ 303 public function test_error_when_missing_eraser_index() { 304 $this->_make_ajax_call( 305 array( 306 'eraser' => null, 307 ) 308 ); 309 310 $this->assertFalse( $this->_last_response_parsed['success'] ); 311 $this->assertSame( 'Missing eraser index.', $this->_last_response_parsed['data'] ); 312 } 313 314 /** 315 * The function should send an error when the page index is missing. 316 * 317 * @since 4.9.9 318 */ 319 public function test_error_when_missing_page_index() { 320 $this->_make_ajax_call( 321 array( 322 'page' => null, 323 ) 324 ); 325 326 $this->assertFalse( $this->_last_response_parsed['success'] ); 327 $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] ); 328 } 329 330 /** 331 * The function should send an error when the eraser index is negative. 332 * 333 * @since 4.9.9 334 */ 335 public function test_error_when_negative_eraser_index() { 336 $this->_make_ajax_call( 337 array( 338 'eraser' => -1, // Negative eraser index. 339 ) 340 ); 341 342 $this->assertFalse( $this->_last_response_parsed['success'] ); 343 $this->assertSame( 'Eraser index cannot be less than one.', $this->_last_response_parsed['data'] ); 344 } 345 346 /** 347 * The function should send an error when the eraser index is out of range. 348 * 349 * @since 4.9.9 350 */ 351 public function test_error_when_eraser_index_out_of_range() { 352 $this->_make_ajax_call( 353 array( 354 'eraser' => PHP_INT_MAX, // Out of range eraser index. 355 ) 356 ); 357 358 $this->assertFalse( $this->_last_response_parsed['success'] ); 359 $this->assertSame( 'Eraser index is out of range.', $this->_last_response_parsed['data'] ); 360 } 361 362 /** 363 * The function should send an error when the page index is less than one. 364 * 365 * @since 4.9.9 366 */ 367 public function test_error_when_page_index_less_than_one() { 368 $this->_make_ajax_call( 369 array( 370 'page' => 0, // Page index less than one. 371 ) 372 ); 373 374 $this->assertFalse( $this->_last_response_parsed['success'] ); 375 $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] ); 376 } 377 378 /** 379 * The function should send an error when an eraser is not an array. 380 * 381 * @since 4.9.9 382 */ 383 public function test_error_when_eraser_not_array() { 384 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_not_array' ), 20 ); 385 $this->_make_ajax_call(); 386 387 $this->assertFalse( $this->_last_response_parsed['success'] ); 388 $this->assertSame( 389 sprintf( 390 'Expected an array describing the eraser at index %s.', 391 self::$eraser 392 ), 393 $this->_last_response_parsed['data'] 394 ); 395 } 396 397 /** 398 * Change the eraser information to an invalid value. 399 * 400 * @since 4.9.9 401 * 402 * @param array $erasers Erasers. 403 * @return array $erasers Erasers. 404 */ 405 public function filter_eraser_not_array( $erasers ) { 406 $erasers[ self::$eraser_key ] = false; 407 408 return $erasers; 409 } 410 411 /** 412 * The function should send an error when an eraser is missing a friendly name. 413 * 414 * @since 4.9.9 415 */ 416 public function test_error_when_eraser_missing_friendly_name() { 417 $this->key_to_unset = 'eraser_friendly_name'; 418 419 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 420 $this->_make_ajax_call(); 421 422 $this->assertFalse( $this->_last_response_parsed['success'] ); 423 $this->assertSame( 424 sprintf( 425 'Eraser array at index %s does not include a friendly name.', 426 self::$eraser 427 ), 428 $this->_last_response_parsed['data'] 429 ); 430 } 431 432 /** 433 * The function should send an error when an eraser is missing a callback. 434 * 435 * @since 4.9.9 436 */ 437 public function test_error_when_eraser_missing_callback() { 438 $this->key_to_unset = 'callback'; 439 440 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 441 $this->_make_ajax_call(); 442 443 $this->assertFalse( $this->_last_response_parsed['success'] ); 444 $this->assertSame( 445 sprintf( 446 'Eraser does not include a callback: %s.', 447 self::$eraser_friendly_name 448 ), 449 $this->_last_response_parsed['data'] 450 ); 451 } 452 453 /** 454 * The function should send an error when an eraser, at a given index, has an invalid callback. 455 * 456 * @since 4.9.9 457 */ 458 public function test_error_when_eraser_index_invalid_callback() { 459 $this->new_callback_value = false; 460 461 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 ); 462 $this->_make_ajax_call(); 463 464 $this->assertFalse( $this->_last_response_parsed['success'] ); 465 $this->assertSame( 466 sprintf( 467 'Eraser callback is not valid: %s.', 468 self::$eraser_friendly_name 469 ), 470 $this->_last_response_parsed['data'] 471 ); 472 } 473 474 /** 475 * The function should send an error when an eraser, at a given index, is missing an array response. 476 * 477 * @since 4.9.9 478 */ 479 public function test_error_when_eraser_index_invalid_response() { 480 $this->new_callback_value = '__return_null'; 481 482 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 ); 483 $this->_make_ajax_call(); 484 485 $this->assertFalse( $this->_last_response_parsed['success'] ); 486 $this->assertSame( 487 sprintf( 488 'Did not receive array from %1$s eraser (index %2$d).', 489 self::$eraser_friendly_name, 490 self::$eraser 491 ), 492 $this->_last_response_parsed['data'] 493 ); 494 } 495 496 /** 497 * Change the test eraser's callback to one that unsets an index in the response. 498 * 499 * @since 4.9.9 500 * 501 * @param array $erasers Erasers. 502 * @return array $erasers Erasers. 503 */ 504 public function filter_callback_unset_index( $erasers ) { 505 $erasers[ self::$eraser_key ]['callback'] = array( $this, 'filter_unset_response_index' ); 506 507 return $erasers; 508 } 509 510 /** 511 * Unsets an array index in a response. 512 * 513 * @since 4.9.9 514 * 515 * @param string $email_address The requester's email address. 516 * @param int $page Page number. 517 * @return array $return Export data. 518 */ 519 public function filter_unset_response_index( $email_address, $page = 1 ) { 520 $response = $this->callback_personal_data_eraser( $email_address, $page ); 521 522 if ( ! empty( $this->key_to_unset ) ) { 523 unset( $response[ $this->key_to_unset ] ); 524 } 525 526 return $response; 527 } 528 529 /** 530 * The function should send an error when missing an items_removed index. 531 * 532 * @since 4.9.9 533 */ 534 public function test_error_when_eraser_items_removed_missing() { 535 $this->key_to_unset = 'items_removed'; 536 537 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 538 $this->_make_ajax_call(); 539 540 $this->assertFalse( $this->_last_response_parsed['success'] ); 541 $this->assertSame( 542 sprintf( 543 'Expected items_removed key in response array from %1$s eraser (index %2$d).', 544 self::$eraser_friendly_name, 545 self::$eraser 546 ), 547 $this->_last_response_parsed['data'] 548 ); 549 } 550 551 /** 552 * The function should send an error when missing an items_retained index. 553 * 554 * @since 4.9.9 555 */ 556 public function test_error_when_eraser_items_retained_missing() { 557 $this->key_to_unset = 'items_retained'; 558 559 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 560 $this->_make_ajax_call(); 561 562 $this->assertFalse( $this->_last_response_parsed['success'] ); 563 $this->assertSame( 564 sprintf( 565 'Expected items_retained key in response array from %1$s eraser (index %2$d).', 566 self::$eraser_friendly_name, 567 self::$eraser 568 ), 569 $this->_last_response_parsed['data'] 570 ); 571 } 572 573 /** 574 * The function should send an error when missing a messages index. 575 * 576 * @since 4.9.9 577 */ 578 public function test_error_when_eraser_messages_missing() { 579 $this->key_to_unset = 'messages'; 580 581 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 582 $this->_make_ajax_call(); 583 584 $this->assertFalse( $this->_last_response_parsed['success'] ); 585 $this->assertSame( 586 sprintf( 587 'Expected messages key in response array from %1$s eraser (index %2$d).', 588 self::$eraser_friendly_name, 589 self::$eraser 590 ), 591 $this->_last_response_parsed['data'] 592 ); 593 } 594 595 /** 596 * The function should send an error when the messages index is not an array. 597 * 598 * @since 4.9.9 599 */ 600 public function test_error_when_eraser_messages_not_array() { 601 $this->new_callback_value = array( $this, 'filter_response_messages_invalid' ); 602 603 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 ); 604 $this->_make_ajax_call(); 605 606 $this->assertFalse( $this->_last_response_parsed['success'] ); 607 $this->assertSame( 608 sprintf( 609 'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).', 610 self::$eraser_friendly_name, 611 self::$eraser 612 ), 613 $this->_last_response_parsed['data'] 614 ); 615 } 616 617 /** 618 * Change the messages index to an invalid value (not an array). 619 * 620 * @since 4.9.9 621 * 622 * @param string $email_address The requester's email address. 623 * @param int $page Page number. 624 * @return array $return Export data. 625 */ 626 public function filter_response_messages_invalid( $email_address, $page = 1 ) { 627 $response = $this->callback_personal_data_eraser( $email_address, $page ); 628 $response['messages'] = true; 629 630 return $response; 631 } 632 633 /** 634 * The function should send an error when an eraser is missing 'done' in array response. 635 * 636 * @since 4.9.9 637 */ 638 public function test_error_when_eraser_missing_done_response() { 639 $this->key_to_unset = 'done'; 640 641 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 642 $this->_make_ajax_call(); 643 644 $this->assertFalse( $this->_last_response_parsed['success'] ); 645 $this->assertSame( 646 sprintf( 647 'Expected done flag in response array from %1$s eraser (index %2$d).', 648 self::$eraser_friendly_name, 649 self::$eraser 650 ), 651 $this->_last_response_parsed['data'] 652 ); 653 } 654 655 /** 656 * The function should successfully send erasers response data when the current user has the required 657 * capabilities. 658 * 659 * @since 4.9.9 660 * 661 * @ticket 43438 662 */ 663 public function test_success_when_current_user_has_required_capabilities() { 664 $this->assertTrue( current_user_can( 'erase_others_personal_data' ) ); 665 $this->assertTrue( current_user_can( 'delete_users' ) ); 666 667 $this->_make_ajax_call(); 668 669 $this->assertSame( 'A message regarding retained data for requester@example.com.', $this->_last_response_parsed['data']['messages'][0] ); 670 $this->assertTrue( $this->_last_response_parsed['success'] ); 671 $this->assertTrue( $this->_last_response_parsed['data']['items_removed'] ); 672 $this->assertTrue( $this->_last_response_parsed['data']['items_retained'] ); 673 $this->assertTrue( $this->_last_response_parsed['data']['done'] ); 674 } 675 676 /** 677 * Test that the function's output should be filterable with the `wp_privacy_personal_data_erasure_page` filter. 678 * 679 * @since 4.9.9 680 */ 681 public function test_output_should_be_filterable() { 682 add_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_exporter_data_response' ), 20, 6 ); 683 $this->_make_ajax_call(); 684 685 $expected_new_index = self::$request_email . '-' . self::$request_id . '-' . self::$eraser_key; 686 687 $this->assertTrue( $this->_last_response_parsed['success'] ); 688 $this->assertSame( 'filtered removed', $this->_last_response_parsed['data']['items_removed'] ); 689 $this->assertSame( 'filtered retained', $this->_last_response_parsed['data']['items_retained'] ); 690 $this->assertSame( array( 'filtered messages' ), $this->_last_response_parsed['data']['messages'] ); 691 $this->assertSame( 'filtered done', $this->_last_response_parsed['data']['done'] ); 692 $this->assertSame( $expected_new_index, $this->_last_response_parsed['data']['new_index'] ); 693 } 694 695 /** 696 * Filters the exporter response. 697 * 698 * @since 4.9.9 699 * 700 * @param array $response The personal data for the given exporter and page. 701 * @param int $eraser_index The index of the eraser that provided this data. 702 * @param string $email_address The email address associated with this personal data. 703 * @param int $page The page for this response. 704 * @param int $request_id The privacy request post ID associated with this request. 705 * @param string $eraser_key The key (slug) of the eraser that provided this data. 706 * @return array Filtered export response. 707 */ 708 public function filter_exporter_data_response( $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ) { 709 $response['items_removed'] = 'filtered removed'; 710 $response['items_retained'] = 'filtered retained'; 711 $response['messages'] = array( 'filtered messages' ); 712 $response['done'] = 'filtered done'; 713 $response['new_index'] = $email_address . '-' . $request_id . '-' . $eraser_key; 714 715 return $response; 716 } 717 718 /** 719 * Register handler for a custom personal data eraser. 720 * 721 * @since 4.9.9 722 * 723 * @param array $erasers An array of personal data erasers. 724 * @return array $erasers An array of personal data erasers. 725 */ 726 public function register_custom_personal_data_eraser( $erasers ) { 727 $erasers[ self::$eraser_key ] = array( 728 'eraser_friendly_name' => self::$eraser_friendly_name, 729 'callback' => array( $this, 'callback_personal_data_eraser' ), 730 ); 731 return $erasers; 732 } 733 734 /** 735 * Custom Personal Data Eraser. 736 * 737 * @since 4.9.9 738 * 739 * @param string $email_address The comment author email address. 740 * @param int $page Page number. 741 * @return array $return Erase data. 742 */ 743 public function callback_personal_data_eraser( $email_address, $page = 1 ) { 744 if ( 1 === $page ) { 745 return array( 746 'items_removed' => true, 747 'items_retained' => true, 748 'messages' => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ), 749 'done' => true, 750 ); 751 } 752 753 return array( 754 'items_removed' => false, 755 'items_retained' => false, 756 'messages' => array(), 757 'done' => true, 758 ); 759 } 760 761 /** 762 * Helper function for ajax handler. 763 * 764 * @since 4.9.9 765 * 766 * @param array $args Ajax request arguments. 767 */ 768 protected function _make_ajax_call( $args = array() ) { 769 $this->_last_response_parsed = null; 770 $this->_last_response = ''; 771 772 $defaults = array( 773 'action' => self::$action, 774 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ), 775 'page' => self::$page, 776 'id' => self::$request_id, 777 'eraser' => self::$eraser, 778 ); 779 780 $_POST = wp_parse_args( $args, $defaults ); 781 782 try { 783 $this->_handleAjax( self::$action ); 784 } catch ( WPAjaxDieContinueException $e ) { 785 unset( $e ); 786 } 787 788 if ( $this->_last_response ) { 789 $this->_last_response_parsed = json_decode( $this->_last_response, true ); 790 } 791 } 792 } -
tests/phpunit/tests/ajax/PrivacyExportPersonalData.php
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 * An value to change the test exporter callback to. 112 * 113 * @since 4.9.9 114 * 115 * @var string $key_to_unset 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 invalid. 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. 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' => 'not-a-valid-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( 'erase-request@example.com', 'erase_personal_data' ); 263 264 $this->_make_ajax_call( 265 array( 266 'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ), 267 'id' => $request_id, 268 ) 269 ); 270 271 $this->assertFalse( $this->_last_response_parsed['success'] ); 272 $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] ); 273 } 274 275 /** 276 * The function should send an error when the requester's email address is invalid. 277 * 278 * @since 4.9.9 279 */ 280 public function test_error_when_invalid_email_address() { 281 wp_update_post( 282 array( 283 'ID' => self::$request_id, 284 'post_title' => '', // Invalid requester's email address. 285 ) 286 ); 287 288 $this->_make_ajax_call(); 289 290 $this->assertFalse( $this->_last_response_parsed['success'] ); 291 $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] ); 292 } 293 294 /** 295 * The function should send an error when the exporter index is missing. 296 * 297 * @since 4.9.9 298 */ 299 public function test_error_when_missing_exporter_index() { 300 $this->_make_ajax_call( 301 array( 302 'exporter' => null, // Missing exporter index. 303 ) 304 ); 305 306 $this->assertFalse( $this->_last_response_parsed['success'] ); 307 $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] ); 308 } 309 310 /** 311 * The function should send an error when the page index is missing. 312 * 313 * @since 4.9.9 314 */ 315 public function test_error_when_missing_page_index() { 316 $this->_make_ajax_call( 317 array( 318 'page' => null, // Missing page index. 319 ) 320 ); 321 322 $this->assertFalse( $this->_last_response_parsed['success'] ); 323 $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] ); 324 } 325 326 /** 327 * The function should send an error when an exporter has improperly used the `wp_privacy_personal_data_exporters` filter. 328 * 329 * @since 4.9.9 330 */ 331 public function test_error_when_exporter_has_improperly_used_exporters_filter() { 332 // Improper filter usage: returns false instead of an expected array. 333 add_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 ); 334 $this->_make_ajax_call(); 335 336 $this->assertFalse( $this->_last_response_parsed['success'] ); 337 $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] ); 338 } 339 340 /** 341 * The function should send an error when the exporter index is negative. 342 * 343 * @since 4.9.9 344 */ 345 public function test_error_when_negative_exporter_index() { 346 $this->_make_ajax_call( 347 array( 348 'exporter' => -1, // Negative exporter index. 349 ) 350 ); 351 352 $this->assertFalse( $this->_last_response_parsed['success'] ); 353 $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] ); 354 } 355 356 /** 357 * The function should send an error when the exporter index is out of range. 358 * 359 * @since 4.9.9 360 */ 361 public function test_error_when_exporter_index_out_of_range() { 362 $this->_make_ajax_call( 363 array( 364 'exporter' => PHP_INT_MAX, // Out of range exporter index. 365 ) 366 ); 367 368 $this->assertFalse( $this->_last_response_parsed['success'] ); 369 $this->assertSame( 'Exporter index is out of range.', $this->_last_response_parsed['data'] ); 370 } 371 372 /** 373 * The function should send an error when the page index is less than one. 374 * 375 * @since 4.9.9 376 */ 377 public function test_error_when_page_index_less_than_one() { 378 $this->_make_ajax_call( 379 array( 380 'page' => 0, // Page index less than one. 381 ) 382 ); 383 384 $this->assertFalse( $this->_last_response_parsed['success'] ); 385 $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] ); 386 } 387 388 /** 389 * The function should send an error when an exporter is not an array. 390 * 391 * @since 4.9.9 392 */ 393 public function test_error_when_exporter_not_array() { 394 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_not_array' ), 20 ); 395 $this->_make_ajax_call(); 396 397 $this->assertFalse( $this->_last_response_parsed['success'] ); 398 $this->assertSame( 399 sprintf( 400 'Expected an array describing the exporter at index %s.', 401 self::$exporter_key 402 ), 403 $this->_last_response_parsed['data'] 404 ); 405 } 406 407 /** 408 * Change the exporter information to an invalid value. 409 * 410 * @since 4.9.9 411 * 412 * @param array $exporters Exporters. 413 * @return array $exporters Exporters. 414 */ 415 public function filter_exporter_not_array( $exporters ) { 416 $exporters[ self::$exporter_key ] = false; 417 418 return $exporters; 419 } 420 421 /** 422 * The function should send an error when an exporter is missing a friendly name. 423 * 424 * @since 4.9.9 425 */ 426 public function test_error_when_exporter_missing_friendly_name() { 427 $this->key_to_unset = 'exporter_friendly_name'; 428 429 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_value' ), 20 ); 430 $this->_make_ajax_call(); 431 432 $this->assertFalse( $this->_last_response_parsed['success'] ); 433 $this->assertSame( 434 sprintf( 435 'Exporter array at index %s does not include a friendly name.', 436 self::$exporter_key 437 ), 438 $this->_last_response_parsed['data'] 439 ); 440 } 441 442 /** 443 * The function should send an error when an exporter is missing a callback. 444 * 445 * @since 4.9.9 446 */ 447 public function test_error_when_exporter_missing_callback() { 448 $this->key_to_unset = 'callback'; 449 450 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_value' ), 20 ); 451 $this->_make_ajax_call(); 452 453 $this->assertFalse( $this->_last_response_parsed['success'] ); 454 $this->assertSame( 455 sprintf( 456 'Exporter does not include a callback: %s.', 457 self::$exporter_friendly_name 458 ), 459 $this->_last_response_parsed['data'] 460 ); 461 } 462 463 /** 464 * The function should send an error when an exporter, at a given index, has an invalid callback. 465 * 466 * @since 4.9.9 467 */ 468 public function test_error_when_exporter_index_invalid_callback() { 469 $this->new_callback_value = false; 470 471 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 472 $this->_make_ajax_call(); 473 474 $this->assertFalse( $this->_last_response_parsed['success'] ); 475 $this->assertSame( 476 sprintf( 477 'Exporter callback is not a valid callback: %s.', 478 self::$exporter_friendly_name 479 ), 480 $this->_last_response_parsed['data'] 481 ); 482 } 483 484 /** 485 * When an exporter callback returns a WP_Error, it should be passed as the error. 486 * 487 * @since 4.9.9 488 */ 489 public function test_error_when_exporter_callback_returns_wp_error() { 490 $this->new_callback_value = array( $this, 'callback_return_wp_error' ); 491 492 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 493 $this->_make_ajax_call(); 494 495 $this->assertFalse( $this->_last_response_parsed['success'] ); 496 $this->assertSame( 'passed_message', $this->_last_response_parsed['data'][0]['code'] ); 497 $this->assertSame( 'This is a WP_Error message.', $this->_last_response_parsed['data'][0]['message'] ); 498 } 499 500 /** 501 * Callback for exporter's response. 502 * 503 * @since 4.9.9 504 * 505 * @param string $email_address The requester's email address. 506 * @param int $page Page number. 507 * @return WP_Error WP_Error instance. 508 */ 509 public function callback_return_wp_error( $email_address, $page = 1 ) { 510 return new WP_Error( 'passed_message', 'This is a WP_Error message.' ); 511 } 512 513 /** 514 * The function should send an error when an exporter, at a given index, is missing an array response. 515 * 516 * @since 4.9.9 517 */ 518 public function test_error_when_exporter_index_invalid_response() { 519 $this->new_callback_value = '__return_null'; 520 521 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 522 $this->_make_ajax_call(); 523 524 $this->assertFalse( $this->_last_response_parsed['success'] ); 525 $this->assertSame( 526 sprintf( 527 'Expected response as an array from exporter: %s.', 528 self::$exporter_friendly_name 529 ), 530 $this->_last_response_parsed['data'] 531 ); 532 } 533 534 /** 535 * The function should send an error when an exporter is missing data in array response. 536 * 537 * @since 4.9.9 538 */ 539 public function test_error_when_exporter_missing_data_response() { 540 $this->new_callback_value = array( $this, 'callback_missing_data_response' ); 541 542 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 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->new_callback_value = array( $this, 'callback_missing_data_array_response' ); 578 579 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 580 $this->_make_ajax_call(); 581 582 $this->assertFalse( $this->_last_response_parsed['success'] ); 583 $this->assertSame( 584 sprintf( 585 'Expected data array in response array from exporter: %s.', 586 self::$exporter_friendly_name 587 ), 588 $this->_last_response_parsed['data'] 589 ); 590 } 591 592 /** 593 * Callback for exporter's response. 594 * 595 * @since 4.9.9 596 * 597 * @param string $email_address The requester's email address. 598 * @param int $page Page number. 599 * 600 * @return array $return Export data. 601 */ 602 public function callback_missing_data_array_response( $email_address, $page = 1 ) { 603 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 604 $response['data'] = false; // Not an array. 605 return $response; 606 } 607 608 /** 609 * The function should send an error when an exporter is missing 'done' in array response. 610 * 611 * @since 4.9.9 612 */ 613 public function test_error_when_exporter_missing_done_response() { 614 $this->new_callback_value = array( $this, 'callback_missing_done_response' ); 615 616 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_index_value' ), 20 ); 617 $this->_make_ajax_call(); 618 619 $this->assertFalse( $this->_last_response_parsed['success'] ); 620 $this->assertSame( 621 sprintf( 622 'Expected done (boolean) in response array from exporter: %s.', 623 self::$exporter_friendly_name 624 ), 625 $this->_last_response_parsed['data'] 626 ); 627 } 628 629 /** 630 * Remove the response's done flag. 631 * 632 * @since 4.9.9 633 * 634 * @param string $email_address The requester's email address. 635 * @param int $page Page number. 636 * @return array $return Export data. 637 */ 638 public function callback_missing_done_response( $email_address, $page = 1 ) { 639 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 640 unset( $response['done'] ); 641 642 return $response; 643 } 644 645 /** 646 * The function should successfully send exporter data response when the current user has the required capability. 647 * 648 * @since 4.9.9 649 */ 650 public function test_succeeds_when_current_user_has_required_capability() { 651 $this->_make_ajax_call(); 652 653 $this->assertTrue( $this->_last_response_parsed['success'] ); 654 $this->assertTrue( current_user_can( 'export_others_personal_data' ) ); 655 $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] ); 656 $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] ); 657 $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] ); 658 } 659 660 /** 661 * The function's output should be filterable with the `wp_privacy_personal_data_export_page` filter. 662 * 663 * @since 4.9.9 664 */ 665 public function test_output_should_be_filterable() { 666 add_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20, 7 ); 667 $this->_make_ajax_call(); 668 669 $expected_group_label = sprintf( 670 '%s-%s-%s-%s-%s-%s', 671 self::$exporter, 672 self::$page, 673 self::$request_email, 674 self::$request_id, 675 self::$send_as_email, 676 self::$exporter_key 677 ); 678 679 $this->assertTrue( $this->_last_response_parsed['success'] ); 680 $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] ); 681 $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] ); 682 $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] ); 683 $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] ); 684 $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] ); 685 } 686 687 /** 688 * Filter exporter's data response. 689 * 690 * @since 4.9.9 691 * 692 * @param array $response The personal data for the given exporter and page. 693 * @param int $exporter_index The index of the exporter that provided this data. 694 * @param string $email_address The email address associated with this personal data. 695 * @param int $page The page for this response. 696 * @param int $request_id The privacy request post ID associated with this request. 697 * @param bool $send_as_email Whether the final results of the export should be emailed to the user. 698 * @param string $exporter_key The key (slug) of the exporter that provided this data. 699 * 700 * @return array $response The personal data for the given exporter and page. 701 */ 702 public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) { 703 $group_label = sprintf( 704 '%s-%s-%s-%s-%s-%s', 705 $exporter_index, 706 $page, 707 $email_address, 708 $request_id, 709 $send_as_email, 710 $exporter_key 711 ); 712 $response['group_label'] = $group_label; 713 $response['group_id'] = 'filtered_group_id'; 714 $response['item_id'] = 'filtered_item_id'; 715 $response['data'][0]['name'] = 'filtered_name'; 716 $response['data'][0]['value'] = 'filtered_value'; 717 718 return $response; 719 } 720 721 /** 722 * Filter to register a custom personal data exporter. 723 * 724 * @since 4.9.9 725 * 726 * @param array $exporters An array of personal data exporters. 727 * @return array $exporters An array of personal data exporters. 728 */ 729 public function filter_register_custom_personal_data_exporter( $exporters ) { 730 $exporters[ self::$exporter_key ] = array( 731 'exporter_friendly_name' => self::$exporter_friendly_name, 732 'callback' => array( $this, 'callback_custom_personal_data_exporter' ), 733 ); 734 return $exporters; 735 } 736 737 /** 738 * Callback for a custom personal data exporter. 739 * 740 * @since 4.9.9 741 * 742 * @param string $email_address The requester's email address. 743 * @param int $page Page number. 744 * 745 * @return array $response Export data response. 746 */ 747 public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) { 748 $data_to_export = array(); 749 750 if ( 1 === $page ) { 751 $data_to_export = array( 752 'group_id' => self::$exporter_key . '-group-id', 753 'group_label' => self::$exporter_key . '-group-label', 754 'item_id' => self::$exporter_key . '-item-id', 755 'data' => array( 756 array( 757 'name' => 'Email', 758 'value' => $email_address, 759 ), 760 ), 761 ); 762 } 763 764 return array( 765 'data' => $data_to_export, 766 'done' => true, 767 ); 768 } 769 770 /** 771 * Helper function for ajax handler. 772 * 773 * @since 4.9.9 774 * 775 * @param array $args Ajax request arguments. 776 */ 777 protected function _make_ajax_call( $args = array() ) { 778 $this->_last_response_parsed = null; 779 $this->_last_response = ''; 780 781 $defaults = array( 782 'action' => self::$action, 783 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ), 784 'exporter' => self::$exporter, 785 'page' => self::$page, 786 'sendAsEmail' => self::$send_as_email, 787 'id' => self::$request_id, 788 ); 789 790 $_POST = wp_parse_args( $args, $defaults ); 791 792 try { 793 $this->_handleAjax( self::$action ); 794 } catch ( WPAjaxDieContinueException $e ) { 795 unset( $e ); 796 } 797 798 if ( $this->_last_response ) { 799 $this->_last_response_parsed = json_decode( $this->_last_response, true ); 800 } 801 } 802 }