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