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