Ticket #43438: 43438.14.diff
File 43438.14.diff, 51.4 KB (added by , 7 years ago) |
---|
-
src/wp-admin/includes/ajax-actions.php
4406 4406 } 4407 4407 4408 4408 if ( $exporter_index > count( $exporters ) ) { 4409 wp_send_json_error( __( 'Exporter index out of range.' ) );4409 wp_send_json_error( __( 'Exporter index is out of range.' ) ); 4410 4410 } 4411 4411 4412 4412 if ( $page < 1 ) { … … 4429 4429 sprintf( __( 'Exporter array at index %s does not include a friendly name.' ), $exporter_key ) 4430 4430 ); 4431 4431 } 4432 4433 $exporter_friendly_name = $exporter['exporter_friendly_name']; 4434 4432 4435 if ( ! array_key_exists( 'callback', $exporter ) ) { 4433 4436 wp_send_json_error( 4434 4437 /* translators: %s: exporter friendly name */ 4435 sprintf( __( 'Exporter does not include a callback: %s.' ), esc_html( $exporter ['exporter_friendly_name']) )4438 sprintf( __( 'Exporter does not include a callback: %s.' ), esc_html( $exporter_friendly_name ) ) 4436 4439 ); 4437 4440 } 4438 4441 if ( ! is_callable( $exporter['callback'] ) ) { 4439 4442 wp_send_json_error( 4440 4443 /* translators: %s: exporter friendly name */ 4441 sprintf( __( 'Exporter callback is not a valid callback: %s.' ), esc_html( $exporter ['exporter_friendly_name']) )4444 sprintf( __( 'Exporter callback is not a valid callback: %s.' ), esc_html( $exporter_friendly_name ) ) 4442 4445 ); 4443 4446 } 4444 4447 4445 $callback 4446 $ exporter_friendly_name = $exporter['exporter_friendly_name'];4448 $callback = $exporter['callback']; 4449 $response = call_user_func( $callback, $email_address, $page ); 4447 4450 4448 $response = call_user_func( $callback, $email_address, $page );4449 4451 if ( is_wp_error( $response ) ) { 4450 4452 wp_send_json_error( $response ); 4451 4453 } … … 4536 4538 $request = wp_get_user_request_data( $request_id ); 4537 4539 4538 4540 if ( ! $request || 'remove_personal_data' !== $request->action_name ) { 4539 wp_send_json_error( __( 'Invalid request ID.' ) );4541 wp_send_json_error( __( 'Invalid request type.' ) ); 4540 4542 } 4541 4543 4542 4544 $email_address = $request->email; … … 4603 4605 wp_send_json_error( sprintf( __( 'Expected an array describing the eraser at index %d.' ), $eraser_index ) ); 4604 4606 } 4605 4607 4606 if ( ! array_key_exists( ' callback', $eraser ) ) {4608 if ( ! array_key_exists( 'eraser_friendly_name', $eraser ) ) { 4607 4609 /* translators: %d: array index */ 4608 wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a callback.' ), $eraser_index ) );4610 wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a friendly name.' ), $eraser_index ) ); 4609 4611 } 4610 4612 4611 if ( ! is_callable( $eraser['callback'] ) ) { 4612 /* translators: %d: array index */ 4613 wp_send_json_error( sprintf( __( 'Eraser callback at index %d is not a valid callback.' ), $eraser_index ) ); 4613 $eraser_friendly_name = $eraser['eraser_friendly_name']; 4614 4615 if ( ! array_key_exists( 'callback', $eraser ) ) { 4616 wp_send_json_error( 4617 sprintf( 4618 /* translators: %d: array index */ 4619 __( 'Eraser does not include a callback: %s.' ), 4620 esc_html( $eraser_friendly_name ) 4621 ) 4622 ); 4614 4623 } 4615 4624 4616 if ( ! array_key_exists( 'eraser_friendly_name', $eraser ) ) { 4617 /* translators: %d: array index */ 4618 wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a friendly name.' ), $eraser_index ) ); 4625 if ( ! is_callable( $eraser['callback'] ) ) { 4626 wp_send_json_error( 4627 sprintf( 4628 /* translators: %d: array index */ 4629 __( 'Eraser callback is not valid: %s.' ), 4630 esc_html( $eraser_friendly_name ) 4631 ) 4632 ); 4619 4633 } 4620 4634 4621 $callback = $eraser['callback']; 4622 $eraser_friendly_name = $eraser['eraser_friendly_name']; 4623 4635 $callback = $eraser['callback']; 4624 4636 $response = call_user_func( $callback, $email_address, $page ); 4625 4637 4626 4638 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 26 */ 27 protected static $request_id; 28 29 /** 30 * User Request Email. 31 * 32 * @since 4.9.9 33 * 34 * @var string 35 */ 36 protected static $request_email; 37 38 /** 39 * Ajax Action. 40 * 41 * @since 4.9.9 42 * 43 * @var string 44 */ 45 protected static $action; 46 47 /** 48 * Eraser Index. 49 * 50 * @since 4.9.9 51 * 52 * @var int 53 */ 54 protected static $eraser; 55 56 /** 57 * Eraser Key. 58 * 59 * @since 4.9.9 60 * 61 * @var string 62 */ 63 protected static $eraser_key; 64 65 /** 66 * Eraser Friendly Name. 67 * 68 * @since 4.9.9 69 * 70 * @var string 71 */ 72 protected static $eraser_friendly_name; 73 74 /** 75 * Page Index. 76 * 77 * @since 4.9.9 78 * 79 * @var int 80 */ 81 protected static $page; 82 83 /** 84 * Last response parsed. 85 * 86 * @since 4.9.9 87 * 88 * @var array|null 89 */ 90 protected $_last_response_parsed; 91 92 /** 93 * A response index to unset. 94 * 95 * @since 4.9.9 96 * 97 * @var string 98 */ 99 protected $index_to_unset; 100 101 /** 102 * Create user erase request fixtures. 103 * 104 * @param WP_UnitTest_Factory $factory Factory. 105 */ 106 public static function wpSetUpBeforeClass( $factory ) { 107 self::$request_email = 'requester@example.com'; 108 self::$request_id = wp_create_user_request( self::$request_email, 'remove_personal_data' ); 109 self::$action = 'wp-privacy-erase-personal-data'; 110 self::$eraser = 1; 111 self::$eraser_key = 'custom-eraser'; 112 self::$eraser_friendly_name = 'Custom Eraser'; 113 self::$page = 1; 114 } 115 116 /** 117 * Register a custom personal data eraser. 118 */ 119 public function setUp() { 120 parent::setUp(); 121 122 $this->index_to_unset = ''; 123 124 // Make sure the erasers response is not modified and avoid e.g. sending emails. 125 remove_all_filters( 'wp_privacy_personal_data_erasure_page' ); 126 remove_all_actions( 'wp_privacy_personal_data_erased' ); 127 128 // Only use our custom privacy personal data eraser. 129 remove_all_filters( 'wp_privacy_personal_data_erasers' ); 130 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) ); 131 132 $this->_setRole( 'administrator' ); 133 } 134 135 /** 136 * Clean up after each test method. 137 */ 138 public function tearDown() { 139 parent::tearDown(); 140 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) ); 141 $_POST = array(); 142 } 143 144 /** 145 * The function should successfully send erasers response data when the current user has the required 146 * capabilities. 147 * 148 * @since 4.9.9 149 * 150 * @ticket 43438 151 */ 152 public function test_wp_ajax_wp_privacy_erase_personal_data_should_success_when_current_user_has_required_capabilities() { 153 $this->assertTrue( current_user_can( 'erase_others_personal_data' ) ); 154 $this->assertTrue( current_user_can( 'delete_users' ) ); 155 156 $this->_make_ajax_call(); 157 158 $this->assertSame( 'A message regarding retained data for requester@example.com.', $this->_last_response_parsed['data']['messages'][0] ); 159 $this->assertTrue( $this->_last_response_parsed['success'] ); 160 $this->assertTrue( $this->_last_response_parsed['data']['items_removed'] ); 161 $this->assertTrue( $this->_last_response_parsed['data']['items_retained'] ); 162 $this->assertTrue( $this->_last_response_parsed['data']['done'] ); 163 } 164 165 /** 166 * The function should send error when the request ID is missing. 167 * 168 * @since 4.9.9 169 * 170 * @ticket 43438 171 */ 172 public function test_wp_ajax_wp_privacy_erase_personal_data_should_error_when_missing_request_id() { 173 $this->assertNotWPError( self::$request_id ); 174 175 // Set up a request. 176 $this->_make_ajax_call( 177 array( 178 'id' => null, 179 ) 180 ); 181 182 $this->assertFalse( $this->_last_response_parsed['success'] ); 183 $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] ); 184 } 185 186 /** 187 * The function should send error when the request ID is less than 1. 188 * 189 * @since 4.9.9 190 * 191 * @ticket 43438 192 */ 193 public function test_wp_ajax_wp_privacy_erase_personal_data_should_error_when_request_id_invalid() { 194 $this->assertNotWPError( self::$request_id ); 195 196 // Set up a request. 197 $this->_make_ajax_call( 198 array( 199 'id' => -1, 200 ) 201 ); 202 203 $this->assertFalse( $this->_last_response_parsed['success'] ); 204 $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] ); 205 } 206 207 /** 208 * The function should send an error when the current user is missing required capabilities. 209 * 210 * @since 4.9.9 211 * 212 * @ticket 43438 213 */ 214 public function test_wp_ajax_wp_privacy_erase_personal_data_should_error_when_current_user_missing_required_capabilities() { 215 $this->_setRole( 'author' ); 216 217 $this->assertFalse( current_user_can( 'erase_others_personal_data' ) ); 218 $this->assertFalse( current_user_can( 'delete_users' ) ); 219 220 $this->_make_ajax_call(); 221 222 $this->assertFalse( $this->_last_response_parsed['success'] ); 223 $this->assertSame( 'Invalid request.', $this->_last_response_parsed['data'] ); 224 } 225 226 /** 227 * The function should send an error when the request type is incorrect. 228 * 229 * @since 4.9.9 230 */ 231 public function test_function_should_error_when_incorrect_request_type() { 232 $request_id = wp_create_user_request( 'export-request@example.com', 'export_personal_data' ); 233 234 $this->_make_ajax_call( 235 array( 236 'security' => wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ), 237 'id' => $request_id, 238 ) 239 ); 240 241 $this->assertFalse( $this->_last_response_parsed['success'] ); 242 $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] ); 243 } 244 245 /** 246 * The function should send an error when the request email is invalid. 247 * 248 * @since 4.9.9 249 */ 250 public function test_function_should_error_when_invalid_email() { 251 wp_update_post( 252 array( 253 'ID' => self::$request_id, 254 'post_title' => '', 255 ) 256 ); 257 258 $this->_make_ajax_call(); 259 260 $this->assertFalse( $this->_last_response_parsed['success'] ); 261 $this->assertSame( 'Invalid email address in request.', $this->_last_response_parsed['data'] ); 262 } 263 264 /** 265 * The function should send an error when the eraser index is missing. 266 * 267 * @since 4.9.9 268 */ 269 public function test_function_should_error_when_missing_eraser_index() { 270 $this->_make_ajax_call( 271 array( 272 'eraser' => null, 273 ) 274 ); 275 276 $this->assertFalse( $this->_last_response_parsed['success'] ); 277 $this->assertSame( 'Missing eraser index.', $this->_last_response_parsed['data'] ); 278 } 279 280 /** 281 * The function should send an error when the page index is missing. 282 * 283 * @since 4.9.9 284 */ 285 public function test_function_should_error_when_missing_page_index() { 286 $this->_make_ajax_call( 287 array( 288 'page' => null, 289 ) 290 ); 291 292 $this->assertFalse( $this->_last_response_parsed['success'] ); 293 $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] ); 294 } 295 296 /** 297 * The function should send an error when the eraser index is negative. 298 * 299 * @since 4.9.9 300 */ 301 public function test_function_should_error_when_negative_eraser_index() { 302 $this->_make_ajax_call( 303 array( 304 'eraser' => -1, // Negative eraser index. 305 ) 306 ); 307 308 $this->assertFalse( $this->_last_response_parsed['success'] ); 309 $this->assertSame( 'Eraser index cannot be less than one.', $this->_last_response_parsed['data'] ); 310 } 311 312 /** 313 * The function should send an error when the eraser index is out of range. 314 * 315 * @since 4.9.9 316 */ 317 public function test_function_should_error_when_eraser_index_out_of_range() { 318 $this->_make_ajax_call( 319 array( 320 'eraser' => PHP_INT_MAX, // Out of range eraser index. 321 ) 322 ); 323 324 $this->assertFalse( $this->_last_response_parsed['success'] ); 325 $this->assertSame( 'Eraser index is out of range.', $this->_last_response_parsed['data'] ); 326 } 327 328 /** 329 * The function should send an error when the page index is less than one. 330 * 331 * @since 4.9.9 332 */ 333 public function test_function_should_error_when_page_index_less_than_one() { 334 $this->_make_ajax_call( 335 array( 336 'page' => 0, // Page index less than one. 337 ) 338 ); 339 340 $this->assertFalse( $this->_last_response_parsed['success'] ); 341 $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] ); 342 } 343 344 /** 345 * The function should send an error when an eraser is not an array. 346 * 347 * @since 4.9.9 348 */ 349 public function test_function_should_error_when_eraser_not_array() { 350 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_not_array' ), 20 ); 351 $this->_make_ajax_call(); 352 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_not_array' ), 20 ); 353 354 $this->assertFalse( $this->_last_response_parsed['success'] ); 355 $this->assertSame( 356 sprintf( 357 'Expected an array describing the eraser at index %s.', 358 self::$eraser 359 ), 360 $this->_last_response_parsed['data'] 361 ); 362 } 363 364 /** 365 * Change the eraser information to an invalid value. 366 * 367 * @since 4.9.9 368 * 369 * @param array $erasers Erasers. 370 * @return array $erasers Erasers. 371 */ 372 public function filter_eraser_not_array( $erasers ) { 373 $erasers[ self::$eraser_key ] = false; 374 375 return $erasers; 376 } 377 378 /** 379 * Unsets an array index in the test eraser. 380 * 381 * @since 4.9.9 382 * 383 * @param array $erasers Erasers. 384 * @return array $erasers Erasers. 385 */ 386 public function filter_unset_eraser_index( $erasers ) { 387 if ( ! empty( $this->index_to_unset ) ) { 388 unset( $erasers[ self::$eraser_key ][ $this->index_to_unset ] ); 389 } 390 391 return $erasers; 392 } 393 394 /** 395 * The function should send an error when an eraser is missing a friendly name. 396 * 397 * @since 4.9.9 398 */ 399 public function test_function_should_error_when_eraser_missing_friendly_name() { 400 $this->index_to_unset = 'eraser_friendly_name'; 401 402 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 403 $this->_make_ajax_call(); 404 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 405 406 $this->assertFalse( $this->_last_response_parsed['success'] ); 407 $this->assertSame( 408 sprintf( 409 'Eraser array at index %s does not include a friendly name.', 410 self::$eraser 411 ), 412 $this->_last_response_parsed['data'] 413 ); 414 } 415 416 /** 417 * The function should send an error when an eraser is missing a callback. 418 * 419 * @since 4.9.9 420 */ 421 public function test_function_should_error_when_eraser_missing_callback() { 422 $this->index_to_unset = 'callback'; 423 424 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 425 $this->_make_ajax_call(); 426 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 ); 427 428 $this->assertFalse( $this->_last_response_parsed['success'] ); 429 $this->assertSame( 430 sprintf( 431 'Eraser does not include a callback: %s.', 432 self::$eraser_friendly_name 433 ), 434 $this->_last_response_parsed['data'] 435 ); 436 } 437 438 /** 439 * The function should send an error when an eraser, at a given index, has an invalid callback. 440 * 441 * @since 4.9.9 442 */ 443 public function test_function_should_error_when_eraser_index_invalid_callback() { 444 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_index_invalid_callback' ), 20 ); 445 $this->_make_ajax_call(); 446 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_index_invalid_callback' ), 20 ); 447 448 $this->assertFalse( $this->_last_response_parsed['success'] ); 449 $this->assertSame( 450 sprintf( 451 'Eraser callback is not valid: %s.', 452 self::$eraser_friendly_name 453 ), 454 $this->_last_response_parsed['data'] 455 ); 456 } 457 458 /** 459 * Change the eraser callback to be invalid. 460 * 461 * @since 4.9.9 462 * 463 * @param array $erasers Erasers. 464 * @return array $erasers Erasers. 465 */ 466 public function filter_eraser_index_invalid_callback( $erasers ) { 467 $erasers[ self::$eraser_key ]['callback'] = false; 468 469 return $erasers; 470 } 471 472 /** 473 * The function should send an error when an eraser, at a given index, is missing an array response. 474 * 475 * @since 4.9.9 476 */ 477 public function test_function_should_error_when_eraser_index_invalid_response() { 478 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_index_invalid_response' ), 20 ); 479 $this->_make_ajax_call(); 480 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_index_invalid_response' ), 20 ); 481 482 $this->assertFalse( $this->_last_response_parsed['success'] ); 483 $this->assertSame( 484 sprintf( 485 'Did not receive array from %1$s eraser (index %2$d).', 486 self::$eraser_friendly_name, 487 self::$eraser 488 ), 489 $this->_last_response_parsed['data'] 490 ); 491 } 492 493 /** 494 * Change the eraser callback response to an invalid value. 495 * 496 * @since 4.9.9 497 * 498 * @param array $erasers Erasers. 499 * @return array $erasers Erasers. 500 */ 501 public function filter_eraser_index_invalid_response( $erasers ) { 502 $erasers[ self::$eraser_key ]['callback'] = '__return_null'; 503 504 return $erasers; 505 } 506 507 /** 508 * Unsets an array index in a response. 509 * 510 * @since 4.9.9 511 * 512 * @param string $email_address The requester's email address. 513 * @param int $page Page number. 514 * @return array $return Export data. 515 */ 516 public function filter_unset_response_index( $email_address, $page = 1 ) { 517 $response = $this->callback_personal_data_eraser( $email_address, $page ); 518 519 if ( ! empty( $this->index_to_unset ) ) { 520 unset( $response[ $this->index_to_unset ] ); 521 } 522 523 return $response; 524 } 525 526 /** 527 * Change the test eraser's callback to one that unsets an index in the response. 528 * 529 * @since 4.9.9 530 * 531 * @param array $erasers Erasers. 532 * @return array $erasers Erasers. 533 */ 534 public function filter_callback_unset_index( $erasers ) { 535 $erasers[ self::$eraser_key ]['callback'] = array( $this, 'filter_unset_response_index' ); 536 537 return $erasers; 538 } 539 540 /** 541 * The function should send an error when missing an items_removed index. 542 * 543 * @since 4.9.9 544 */ 545 public function test_function_should_error_when_eraser_items_removed_missing() { 546 $this->index_to_unset = 'items_removed'; 547 548 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 549 $this->_make_ajax_call(); 550 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 551 552 $this->assertFalse( $this->_last_response_parsed['success'] ); 553 $this->assertSame( 554 sprintf( 555 'Expected items_removed key in response array from %1$s eraser (index %2$d).', 556 self::$eraser_friendly_name, 557 self::$eraser 558 ), 559 $this->_last_response_parsed['data'] 560 ); 561 } 562 563 /** 564 * The function should send an error when missing an items_retained index. 565 * 566 * @since 4.9.9 567 */ 568 public function test_function_should_error_when_eraser_items_retained_missing() { 569 $this->index_to_unset = 'items_retained'; 570 571 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 572 $this->_make_ajax_call(); 573 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 574 575 $this->assertFalse( $this->_last_response_parsed['success'] ); 576 $this->assertSame( 577 sprintf( 578 'Expected items_retained key in response array from %1$s eraser (index %2$d).', 579 self::$eraser_friendly_name, 580 self::$eraser 581 ), 582 $this->_last_response_parsed['data'] 583 ); 584 } 585 586 /** 587 * The function should send an error when missing a messages index. 588 * 589 * @since 4.9.9 590 */ 591 public function test_function_should_error_when_eraser_messages_missing() { 592 $this->index_to_unset = 'messages'; 593 594 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 595 $this->_make_ajax_call(); 596 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 597 598 $this->assertFalse( $this->_last_response_parsed['success'] ); 599 $this->assertSame( 600 sprintf( 601 'Expected messages key in response array from %1$s eraser (index %2$d).', 602 self::$eraser_friendly_name, 603 self::$eraser 604 ), 605 $this->_last_response_parsed['data'] 606 ); 607 } 608 609 /** 610 * The function should send an error when the messages index is not an array. 611 * 612 * @since 4.9.9 613 */ 614 public function test_function_should_error_when_eraser_messages_not_array() { 615 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_invalid_messages_index' ), 20 ); 616 $this->_make_ajax_call(); 617 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_invalid_messages_index' ), 20 ); 618 619 $this->assertFalse( $this->_last_response_parsed['success'] ); 620 $this->assertSame( 621 sprintf( 622 'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).', 623 self::$eraser_friendly_name, 624 self::$eraser 625 ), 626 $this->_last_response_parsed['data'] 627 ); 628 } 629 630 /** 631 * Change the test eraser's callback to one that returns an invalid value at the message index. 632 * 633 * @since 4.9.9 634 * 635 * @param array $erasers Erasers. 636 * @return array $erasers Erasers. 637 */ 638 public function filter_callback_invalid_messages_index( $erasers ) { 639 $erasers[ self::$eraser_key ]['callback'] = array( $this, 'filter_response_messages_invalid' ); 640 641 return $erasers; 642 } 643 644 /** 645 * Change the messages index to an invalid value (not an array). 646 * 647 * @since 4.9.9 648 * 649 * @param string $email_address The requester's email address. 650 * @param int $page Page number. 651 * @return array $return Export data. 652 */ 653 public function filter_response_messages_invalid( $email_address, $page = 1 ) { 654 $response = $this->callback_personal_data_eraser( $email_address, $page ); 655 $response['messages'] = true; 656 657 return $response; 658 } 659 660 /** 661 * The function should send an error when an eraser is missing 'done' in array response. 662 * 663 * @since 4.9.9 664 */ 665 public function test_function_should_error_when_eraser_missing_done_response() { 666 $this->index_to_unset = 'done'; 667 668 add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 669 $this->_make_ajax_call(); 670 remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_callback_unset_index' ), 20 ); 671 672 $this->assertFalse( $this->_last_response_parsed['success'] ); 673 $this->assertSame( 674 sprintf( 675 'Expected done flag in response array from %1$s eraser (index %2$d).', 676 self::$eraser_friendly_name, 677 self::$eraser 678 ), 679 $this->_last_response_parsed['data'] 680 ); 681 } 682 683 /** 684 * Test that the function's output should be filterable with the `wp_privacy_personal_data_erasure_page` filter. 685 * 686 * @since 4.9.9 687 */ 688 public function test_function_output_should_be_filterable() { 689 add_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_exporter_data_response' ), 20, 6 ); 690 $this->_make_ajax_call(); 691 remove_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_exporter_data_response' ), 20 ); 692 693 $expected_new_index = self::$request_email . '-' . self::$request_id . '-' . self::$eraser_key; 694 695 $this->assertTrue( $this->_last_response_parsed['success'] ); 696 $this->assertSame( 'filtered removed', $this->_last_response_parsed['data']['items_removed'] ); 697 $this->assertSame( 'filtered retained', $this->_last_response_parsed['data']['items_retained'] ); 698 $this->assertSame( array( 'filtered messages' ), $this->_last_response_parsed['data']['messages'] ); 699 $this->assertSame( 'filtered done', $this->_last_response_parsed['data']['done'] ); 700 $this->assertSame( $expected_new_index, $this->_last_response_parsed['data']['new_index'] ); 701 } 702 703 /** 704 * Filters the exporter response. 705 * 706 * @since 4.9.9 707 * 708 * @param array $response The personal data for the given exporter and page. 709 * @param int $eraser_index The index of the eraser that provided this data. 710 * @param string $email_address The email address associated with this personal data. 711 * @param int $page The page for this response. 712 * @param int $request_id The privacy request post ID associated with this request. 713 * @param string $eraser_key The key (slug) of the eraser that provided this data. 714 * @return array Filtered export response. 715 */ 716 public function filter_exporter_data_response( $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ) { 717 $response['items_removed'] = 'filtered removed'; 718 $response['items_retained'] = 'filtered retained'; 719 $response['messages'] = array( 'filtered messages' ); 720 $response['done'] = 'filtered done'; 721 $response['new_index'] = $email_address . '-' . $request_id . '-' . $eraser_key; 722 723 return $response; 724 } 725 726 /** 727 * Register handler for a custom personal data eraser. 728 * 729 * @since 4.9.9 730 * 731 * @param array $erasers An array of personal data erasers. 732 * @return array $erasers An array of personal data erasers. 733 */ 734 public function register_custom_personal_data_eraser( $erasers ) { 735 $erasers[ self::$eraser_key ] = array( 736 'eraser_friendly_name' => self::$eraser_friendly_name, 737 'callback' => array( $this, 'callback_personal_data_eraser' ), 738 ); 739 return $erasers; 740 } 741 742 /** 743 * Custom Personal Data Eraser. 744 * 745 * @since 4.9.9 746 * 747 * @param string $email_address The comment author email address. 748 * @param int $page Page number. 749 * @return array $return Erase data. 750 */ 751 public function callback_personal_data_eraser( $email_address, $page = 1 ) { 752 if ( 1 === $page ) { 753 return array( 754 'items_removed' => true, 755 'items_retained' => true, 756 'messages' => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ), 757 'done' => true, 758 ); 759 } 760 761 return array( 762 'items_removed' => false, 763 'items_retained' => false, 764 'messages' => array(), 765 'done' => true, 766 ); 767 } 768 769 /** 770 * Helper function for ajax handler. 771 * 772 * @since 4.9.9 773 * 774 * @param array $args Ajax request arguments. 775 */ 776 protected function _make_ajax_call( $args = array() ) { 777 $this->_last_response_parsed = null; 778 $this->_last_response = ''; 779 780 $defaults = array( 781 'action' => self::$action, 782 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ), 783 'page' => self::$page, 784 'id' => self::$request_id, 785 'eraser' => self::$eraser, 786 ); 787 788 $_POST = wp_parse_args( $args, $defaults ); 789 790 try { 791 $this->_handleAjax( self::$action ); 792 } catch ( WPAjaxDieContinueException $e ) { 793 unset( $e ); 794 } 795 796 if ( $this->_last_response ) { 797 $this->_last_response_parsed = json_decode( $this->_last_response, true ); 798 } 799 } 800 } -
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 26 */ 27 protected static $request_id; 28 29 /** 30 * User Request Email. 31 * 32 * @since 4.9.9 33 * 34 * @var string 35 */ 36 protected static $request_email; 37 38 /** 39 * Ajax Action. 40 * 41 * @since 4.9.9 42 * 43 * @var string 44 */ 45 protected static $action; 46 47 /** 48 * Exporter Index. 49 * 50 * @since 4.9.9 51 * 52 * @var int 53 */ 54 protected static $exporter; 55 56 /** 57 * Exporter Key. 58 * 59 * @since 4.9.9 60 * 61 * @var string 62 */ 63 protected static $exporter_key; 64 65 /** 66 * Exporter Friendly Name. 67 * 68 * @since 4.9.9 69 * 70 * @var string 71 */ 72 protected static $exporter_friendly_name; 73 74 /** 75 * Page Index. 76 * 77 * @since 4.9.9 78 * 79 * @var int 80 */ 81 protected static $page; 82 83 /** 84 * Send As Email. 85 * 86 * @since 4.9.9 87 * 88 * @var bool 89 */ 90 protected static $send_as_email; 91 92 /** 93 * Last response parsed. 94 * 95 * @since 4.9.9 96 * 97 * @var array|null 98 */ 99 protected $_last_response_parsed; 100 101 /** 102 * Create user export request fixtures. 103 * 104 * @since 4.9.9 105 * 106 * @param WP_UnitTest_Factory $factory Factory. 107 */ 108 public static function wpSetUpBeforeClass( $factory ) { 109 self::$request_email = 'requester@example.com'; 110 self::$request_id = wp_create_user_request( self::$request_email, 'export_personal_data' ); 111 self::$action = 'wp-privacy-export-personal-data'; 112 self::$exporter = 1; 113 self::$exporter_key = 'custom-exporter'; 114 self::$exporter_friendly_name = 'Custom Exporter'; 115 self::$page = 1; 116 self::$send_as_email = false; 117 } 118 119 /** 120 * Setup before each test method. 121 * 122 * @since 4.9.9 123 */ 124 public function setUp() { 125 parent::setUp(); 126 127 // Make sure the exporter response is not modified and avoid e.g. writing export file to disk. 128 remove_all_filters( 'wp_privacy_personal_data_export_page' ); 129 130 // Only use our custom privacy personal data exporter. 131 remove_all_filters( 'wp_privacy_personal_data_exporters' ); 132 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) ); 133 134 $this->_setRole( 'administrator' ); 135 } 136 137 /** 138 * Clean up after each test method. 139 */ 140 public function tearDown() { 141 parent::tearDown(); 142 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) ); 143 $_POST = array(); 144 } 145 146 /** 147 * The function should successfully send exporter data response when the current user has the required capability. 148 * 149 * @since 4.9.9 150 */ 151 public function test_function_should_succeed_when_current_user_has_required_capability() { 152 $this->_make_ajax_call(); 153 154 $this->assertTrue( $this->_last_response_parsed['success'] ); 155 $this->assertTrue( current_user_can( 'export_others_personal_data' ) ); 156 $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] ); 157 $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] ); 158 $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] ); 159 } 160 161 /** 162 * The function should error when the current user is missing the required capability. 163 * 164 * @since 4.9.9 165 */ 166 public function test_function_should_error_when_current_user_missing_required_capability() { 167 $this->_setRole( 'author' ); 168 169 $this->_make_ajax_call(); 170 171 $this->assertFalse( $this->_last_response_parsed['success'] ); 172 $this->assertFalse( current_user_can( 'export_others_personal_data' ) ); 173 $this->assertSame( 'Invalid request.', $this->_last_response_parsed['data'] ); 174 } 175 176 /** 177 * The function should send an error when the request ID is missing. 178 * 179 * @since 4.9.9 180 */ 181 public function test_function_should_error_when_missing_request_id() { 182 $this->_make_ajax_call( 183 array( 184 'id' => null, // Missing request ID. 185 ) 186 ); 187 188 $this->assertFalse( $this->_last_response_parsed['success'] ); 189 $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] ); 190 } 191 192 /** 193 * The function should send an error when the request ID is invalid. 194 * 195 * @since 4.9.9 196 */ 197 public function test_function_should_error_when_invalid_id() { 198 $this->_make_ajax_call( 199 array( 200 'id' => -1, // Invalid request ID. 201 ) 202 ); 203 204 $this->assertFalse( $this->_last_response_parsed['success'] ); 205 $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] ); 206 } 207 208 /** 209 * The function should send an error when the request type is incorrect. 210 * 211 * @since 4.9.9 212 */ 213 public function test_function_should_error_when_incorrect_request_type() { 214 $request_id = wp_create_user_request( 'erase-request@example.com', 'erase_personal_data' ); 215 216 $this->_make_ajax_call( 217 array( 218 'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ), 219 'id' => $request_id, 220 ) 221 ); 222 223 $this->assertFalse( $this->_last_response_parsed['success'] ); 224 $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] ); 225 } 226 227 /** 228 * The function should send an error when the requester's email address is invalid. 229 * 230 * @since 4.9.9 231 */ 232 public function test_function_should_error_when_invalid_email_address() { 233 wp_update_post( 234 array( 235 'ID' => self::$request_id, 236 'post_title' => '', // Invalid requester's email address. 237 ) 238 ); 239 240 $this->_make_ajax_call(); 241 242 $this->assertFalse( $this->_last_response_parsed['success'] ); 243 $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] ); 244 } 245 246 /** 247 * The function should send an error when the exporter index is missing. 248 * 249 * @since 4.9.9 250 */ 251 public function test_function_should_error_when_missing_exporter_index() { 252 $this->_make_ajax_call( 253 array( 254 'exporter' => null, // Missing exporter index. 255 ) 256 ); 257 258 $this->assertFalse( $this->_last_response_parsed['success'] ); 259 $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] ); 260 } 261 262 /** 263 * The function should send an error when the page index is missing. 264 * 265 * @since 4.9.9 266 */ 267 public function test_function_should_error_when_missing_page_index() { 268 $this->_make_ajax_call( 269 array( 270 'page' => null, // Missing page index. 271 ) 272 ); 273 274 $this->assertFalse( $this->_last_response_parsed['success'] ); 275 $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] ); 276 } 277 278 /** 279 * The function should send an error when an exporter has improperly used the `wp_privacy_personal_data_exporters` filter. 280 * 281 * @since 4.9.9 282 */ 283 public function test_function_should_error_when_exporter_has_improperly_used_exporters_filter() { 284 // Improper filter usage: returns false instead of an expected array. 285 add_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 ); 286 $this->_make_ajax_call(); 287 remove_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 ); 288 289 $this->assertFalse( $this->_last_response_parsed['success'] ); 290 $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] ); 291 } 292 293 /** 294 * The function should send an error when the exporter index is negative. 295 * 296 * @since 4.9.9 297 */ 298 public function test_function_should_error_when_negative_exporter_index() { 299 $this->_make_ajax_call( 300 array( 301 'exporter' => -1, // Negative exporter index. 302 ) 303 ); 304 305 $this->assertFalse( $this->_last_response_parsed['success'] ); 306 $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] ); 307 } 308 309 /** 310 * The function should send an error when the exporter index is out of range. 311 * 312 * @since 4.9.9 313 */ 314 public function test_function_should_error_when_exporter_index_out_of_range() { 315 $this->_make_ajax_call( 316 array( 317 'exporter' => PHP_INT_MAX, // Out of range exporter index. 318 ) 319 ); 320 321 $this->assertFalse( $this->_last_response_parsed['success'] ); 322 $this->assertSame( 'Exporter index is out of range.', $this->_last_response_parsed['data'] ); 323 } 324 325 /** 326 * The function should send an error when the page index is less than one. 327 * 328 * @since 4.9.9 329 */ 330 public function test_function_should_error_when_page_index_less_than_one() { 331 $this->_make_ajax_call( 332 array( 333 'page' => 0, // Page index less than one. 334 ) 335 ); 336 337 $this->assertFalse( $this->_last_response_parsed['success'] ); 338 $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] ); 339 } 340 341 /** 342 * The function should send an error when an exporter is not an array. 343 * 344 * @since 4.9.9 345 */ 346 public function test_function_should_error_when_exporter_not_array() { 347 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_not_array' ), 20 ); 348 $this->_make_ajax_call(); 349 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_not_array' ), 20 ); 350 351 $this->assertFalse( $this->_last_response_parsed['success'] ); 352 $this->assertSame( 353 sprintf( 354 'Expected an array describing the exporter at index %s.', 355 self::$exporter_key 356 ), 357 $this->_last_response_parsed['data'] 358 ); 359 } 360 361 /** 362 * Change the exporter information to an invalid value. 363 * 364 * @since 4.9.9 365 * 366 * @param array $exporters Exporters. 367 * @return array $exporters Exporters. 368 */ 369 public function filter_exporter_not_array( $exporters ) { 370 $exporters[ self::$exporter_key ] = false; 371 372 return $exporters; 373 } 374 375 /** 376 * The function should send an error when an exporter is missing a friendly name. 377 * 378 * @since 4.9.9 379 */ 380 public function test_function_should_error_when_exporter_missing_friendly_name() { 381 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_friendly_name' ), 20 ); 382 $this->_make_ajax_call(); 383 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_friendly_name' ), 20 ); 384 385 $this->assertFalse( $this->_last_response_parsed['success'] ); 386 $this->assertSame( 387 sprintf( 388 'Exporter array at index %s does not include a friendly name.', 389 self::$exporter_key 390 ), 391 $this->_last_response_parsed['data'] 392 ); 393 } 394 395 396 /** 397 * Remove the custom exporter's friendly name. 398 * 399 * @since 4.9.9 400 * 401 * @param array $exporters Exporters. 402 * @return array $exporters Exporters. 403 */ 404 public function filter_exporter_missing_friendly_name( $exporters ) { 405 unset( $exporters[ self::$exporter_key ]['exporter_friendly_name'] ); 406 407 return $exporters; 408 } 409 410 /** 411 * The function should send an error when an exporter is missing a callback. 412 * 413 * @since 4.9.9 414 */ 415 public function test_function_should_error_when_exporter_missing_callback() { 416 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_callback' ), 20 ); 417 $this->_make_ajax_call(); 418 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_callback' ), 20 ); 419 420 $this->assertFalse( $this->_last_response_parsed['success'] ); 421 $this->assertSame( 422 sprintf( 423 'Exporter does not include a callback: %s.', 424 self::$exporter_friendly_name 425 ), 426 $this->_last_response_parsed['data'] 427 ); 428 } 429 430 /** 431 * Remove the custom exporter's callback function. 432 * 433 * @since 4.9.9 434 * 435 * @param array $exporters Exporters. 436 * @return array $exporters Exporters. 437 */ 438 public function filter_exporter_missing_callback( $exporters ) { 439 unset( $exporters[ self::$exporter_key ]['callback'] ); 440 441 return $exporters; 442 } 443 444 /** 445 * The function should send an error when an exporter, at a given index, has an invalid callback. 446 * 447 * @since 4.9.9 448 */ 449 public function test_function_should_error_when_exporter_index_invalid_callback() { 450 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_index_invalid_callback' ), 20 ); 451 $this->_make_ajax_call(); 452 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_index_invalid_callback' ), 20 ); 453 454 $this->assertFalse( $this->_last_response_parsed['success'] ); 455 $this->assertSame( 456 sprintf( 457 'Exporter callback is not a valid callback: %s.', 458 self::$exporter_friendly_name 459 ), 460 $this->_last_response_parsed['data'] 461 ); 462 } 463 464 /** 465 * Change the exporter callback to be invalid. 466 * 467 * @since 4.9.9 468 * 469 * @param array $exporters Exporters. 470 * @return array $exporters Exporters. 471 */ 472 public function filter_exporter_index_invalid_callback( $exporters ) { 473 $exporters[ self::$exporter_key ]['callback'] = false; 474 475 return $exporters; 476 } 477 478 /** 479 * The function should send an error when an exporter, at a given index, is missing an array response. 480 * 481 * @since 4.9.9 482 */ 483 public function test_function_should_error_when_exporter_index_invalid_response() { 484 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_index_invalid_response' ), 20 ); 485 $this->_make_ajax_call(); 486 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_index_invalid_response' ), 20 ); 487 488 $this->assertFalse( $this->_last_response_parsed['success'] ); 489 $this->assertSame( 490 sprintf( 491 'Expected response as an array from exporter: %s.', 492 self::$exporter_friendly_name 493 ), 494 $this->_last_response_parsed['data'] 495 ); 496 } 497 498 /** 499 * Change the exporter callback response to an invalid value. 500 * 501 * @since 4.9.9 502 * 503 * @param array $exporters Exporters. 504 * @return array $exporters Exporters. 505 */ 506 public function filter_exporter_index_invalid_response( $exporters ) { 507 $exporters[ self::$exporter_key ]['callback'] = '__return_null'; 508 509 return $exporters; 510 } 511 512 /** 513 * The function should send an error when an exporter is missing data in array response. 514 * 515 * @since 4.9.9 516 */ 517 public function test_function_should_error_when_exporter_missing_data_response() { 518 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_data_response' ), 20 ); 519 $this->_make_ajax_call(); 520 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_data_response' ), 20 ); 521 522 $this->assertFalse( $this->_last_response_parsed['success'] ); 523 $this->assertSame( 524 sprintf( 525 'Expected data in response array from exporter: %s.', 526 self::$exporter_friendly_name 527 ), 528 $this->_last_response_parsed['data'] 529 ); 530 } 531 532 /** 533 * Change the exporter response callback to one that returns an invalid data response value. 534 * 535 * @since 4.9.9 536 * 537 * @param array $exporters Exporters. 538 * @return array $exporters Exporters. 539 */ 540 public function filter_exporter_missing_data_response( $exporters ) { 541 $exporters[ self::$exporter_key ]['callback'] = array( $this, 'callback_missing_data_response' ); 542 return $exporters; 543 } 544 545 /** 546 * Callback for exporter's response. 547 * 548 * @since 4.9.9 549 * 550 * @param string $email_address The requester's email address. 551 * @param int $page Page number. 552 * @return array $return Export data. 553 */ 554 public function callback_missing_data_response( $email_address, $page = 1 ) { 555 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 556 unset( $response['data'] ); // Missing data part of response. 557 return $response; 558 } 559 560 /** 561 * The function should send an error when an exporter is missing 'done' in array response. 562 * 563 * @since 4.9.9 564 */ 565 public function test_function_should_error_when_exporter_missing_done_response() { 566 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_done_response' ), 20 ); 567 $this->_make_ajax_call(); 568 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_done_response' ), 20 ); 569 570 $this->assertFalse( $this->_last_response_parsed['success'] ); 571 $this->assertSame( 572 sprintf( 573 'Expected done (boolean) in response array from exporter: %s.', 574 self::$exporter_friendly_name 575 ), 576 $this->_last_response_parsed['data'] 577 ); 578 } 579 580 /** 581 * Filter the array of exporters. 582 * 583 * @since 4.9.9 584 * 585 * @param array $exporters Exporters. 586 * @return array $exporters Exporters. 587 */ 588 public function filter_exporter_missing_done_response( $exporters ) { 589 $exporters[ self::$exporter_key ]['callback'] = array( $this, 'callback_missing_done_response' ); 590 591 return $exporters; 592 } 593 594 /** 595 * Remove the response's done flag. 596 * 597 * @since 4.9.9 598 * 599 * @param string $email_address The requester's email address. 600 * @param int $page Page number. 601 * @return array $return Export data. 602 */ 603 public function callback_missing_done_response( $email_address, $page = 1 ) { 604 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 605 unset( $response['done'] ); 606 607 return $response; 608 } 609 610 /** 611 * The function should send an error when an exporter is missing 'data' array in array response. 612 * 613 * @since 4.9.9 614 */ 615 public function test_function_should_error_when_exporter_missing_data_array_response() { 616 add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_data_array_response' ), 20 ); 617 $this->_make_ajax_call(); 618 remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_missing_data_array_response' ), 20 ); 619 620 $this->assertFalse( $this->_last_response_parsed['success'] ); 621 $this->assertSame( 622 sprintf( 623 'Expected data array in response array from exporter: %s.', 624 self::$exporter_friendly_name 625 ), 626 $this->_last_response_parsed['data'] 627 ); 628 } 629 630 /** 631 * Filter the array of exporters. 632 * 633 * @since 4.9.9 634 * 635 * @param array $exporters Exporters. 636 * 637 * @return array $exporters Exporters. 638 */ 639 public function filter_exporter_missing_data_array_response( $exporters ) { 640 $exporters[ self::$exporter_key ]['callback'] = array( $this, 'callback_missing_data_array_response' ); 641 return $exporters; 642 } 643 644 /** 645 * Callback for exporter's response. 646 * 647 * @since 4.9.9 648 * 649 * @param string $email_address The requester's email address. 650 * @param int $page Page number. 651 * 652 * @return array $return Export data. 653 */ 654 public function callback_missing_data_array_response( $email_address, $page = 1 ) { 655 $response = $this->callback_custom_personal_data_exporter( $email_address, $page ); 656 $response['data'] = false; // Not an array. 657 return $response; 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_function_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 remove_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20 ); 669 670 $expected_group_label = sprintf( 671 '%s-%s-%s-%s-%s-%s', 672 self::$exporter, 673 self::$page, 674 self::$request_email, 675 self::$request_id, 676 self::$send_as_email, 677 self::$exporter_key 678 ); 679 680 $this->assertTrue( $this->_last_response_parsed['success'] ); 681 $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] ); 682 $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] ); 683 $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] ); 684 $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] ); 685 $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] ); 686 } 687 688 /** 689 * Filter exporter's data response. 690 * 691 * @since 4.9.9 692 * 693 * @param array $response The personal data for the given exporter and page. 694 * @param int $exporter_index The index of the exporter that provided this data. 695 * @param string $email_address The email address associated with this personal data. 696 * @param int $page The page for this response. 697 * @param int $request_id The privacy request post ID associated with this request. 698 * @param bool $send_as_email Whether the final results of the export should be emailed to the user. 699 * @param string $exporter_key The key (slug) of the exporter that provided this data. 700 * 701 * @return array $response The personal data for the given exporter and page. 702 */ 703 public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) { 704 $group_label = sprintf( 705 '%s-%s-%s-%s-%s-%s', 706 $exporter_index, 707 $page, 708 $email_address, 709 $request_id, 710 $send_as_email, 711 $exporter_key 712 ); 713 $response['group_label'] = $group_label; 714 $response['group_id'] = 'filtered_group_id'; 715 $response['item_id'] = 'filtered_item_id'; 716 $response['data'][0]['name'] = 'filtered_name'; 717 $response['data'][0]['value'] = 'filtered_value'; 718 719 return $response; 720 } 721 722 /** 723 * Filter to register a custom personal data exporter. 724 * 725 * @since 4.9.9 726 * 727 * @param array $exporters An array of personal data exporters. 728 * @return array $exporters An array of personal data exporters. 729 */ 730 public function filter_register_custom_personal_data_exporter( $exporters ) { 731 $exporters[ self::$exporter_key ] = array( 732 'exporter_friendly_name' => self::$exporter_friendly_name, 733 'callback' => array( $this, 'callback_custom_personal_data_exporter' ), 734 ); 735 return $exporters; 736 } 737 738 /** 739 * Callback for a custom personal data exporter. 740 * 741 * @since 4.9.9 742 * 743 * @param string $email_address The requester's email address. 744 * @param int $page Page number. 745 * 746 * @return array $response Export data response. 747 */ 748 public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) { 749 $data_to_export = array(); 750 751 if ( 1 === $page ) { 752 $data_to_export = array( 753 'group_id' => self::$exporter_key . '-group-id', 754 'group_label' => self::$exporter_key . '-group-label', 755 'item_id' => self::$exporter_key . '-item-id', 756 'data' => array( 757 array( 758 'name' => 'Email', 759 'value' => $email_address, 760 ), 761 ), 762 ); 763 } 764 765 return array( 766 'data' => $data_to_export, 767 'done' => true, 768 ); 769 } 770 771 /** 772 * Helper function for ajax handler. 773 * 774 * @since 4.9.9 775 * 776 * @param array $args Ajax request arguments. 777 */ 778 protected function _make_ajax_call( $args = array() ) { 779 $this->_last_response_parsed = null; 780 $this->_last_response = ''; 781 782 $defaults = array( 783 'action' => self::$action, 784 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ), 785 'exporter' => self::$exporter, 786 'page' => self::$page, 787 'sendAsEmail' => self::$send_as_email, 788 'id' => self::$request_id, 789 ); 790 791 $_POST = wp_parse_args( $args, $defaults ); 792 793 try { 794 $this->_handleAjax( self::$action ); 795 } catch ( WPAjaxDieContinueException $e ) { 796 unset( $e ); 797 } 798 799 if ( $this->_last_response ) { 800 $this->_last_response_parsed = json_decode( $this->_last_response, true ); 801 } 802 } 803 }