summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJonathan Desrosiers <desrosj@git.wordpress.org>2019-03-15 18:07:09 +0000
committerJonathan Desrosiers <desrosj@git.wordpress.org>2019-03-15 18:07:09 +0000
commit152e9d2b8fd52d86f3f61b9c3d60162a993b079b (patch)
tree4e68b7774e4688bcbaf77dc5a8f4c3111dff49b9
parent40ff06c08335f3b0972ed4422493e1f268086dc9 (diff)
downloadwordpress-152e9d2b8fd52d86f3f61b9c3d60162a993b079b.tar.gz
wordpress-152e9d2b8fd52d86f3f61b9c3d60162a993b079b.zip
Privacy: Add unit tests for exporting and erasing personal data.
Props birgire, garrett-eclipse, desrosj. Fixes #43438. git-svn-id: https://develop.svn.wordpress.org/trunk@44909 602fd350-edb4-49c9-b593-d223f7449a82
-rw-r--r--tests/phpunit/includes/testcase-ajax.php2
-rwxr-xr-xtests/phpunit/tests/ajax/PrivacyErasePersonalData.php814
-rwxr-xr-xtests/phpunit/tests/ajax/PrivacyExportPersonalData.php818
3 files changed, 1634 insertions, 0 deletions
diff --git a/tests/phpunit/includes/testcase-ajax.php b/tests/phpunit/includes/testcase-ajax.php
index bf5a5bce91..736d362f76 100644
--- a/tests/phpunit/includes/testcase-ajax.php
+++ b/tests/phpunit/includes/testcase-ajax.php
@@ -119,6 +119,8 @@ abstract class WP_Ajax_UnitTestCase extends WP_UnitTestCase {
'delete-theme',
'install-theme',
'get-post-thumbnail-html',
+ 'wp-privacy-export-personal-data',
+ 'wp-privacy-erase-personal-data',
);
public static function setUpBeforeClass() {
diff --git a/tests/phpunit/tests/ajax/PrivacyErasePersonalData.php b/tests/phpunit/tests/ajax/PrivacyErasePersonalData.php
new file mode 100755
index 0000000000..4d4126a677
--- /dev/null
+++ b/tests/phpunit/tests/ajax/PrivacyErasePersonalData.php
@@ -0,0 +1,814 @@
+<?php
+/**
+ * Testing Ajax handler for erasing personal data.
+ *
+ * @package WordPress\UnitTests
+ * @since 5.2.0
+ */
+
+/**
+ * Tests_Ajax_PrivacyExportPersonalData class.
+ *
+ * @since 5.2.0
+ *
+ * @group ajax
+ * @group privacy
+ *
+ * @covers ::wp_ajax_wp_privacy_erase_personal_data
+ */
+class Tests_Ajax_PrivacyErasePersonalData extends WP_Ajax_UnitTestCase {
+
+ /**
+ * User Request ID.
+ *
+ * @since 5.2.0
+ *
+ * @var int $request_id
+ */
+ protected static $request_id;
+
+ /**
+ * User Request Email.
+ *
+ * @since 5.2.0
+ *
+ * @var string $request_email
+ */
+ protected static $request_email;
+
+ /**
+ * Ajax Action.
+ *
+ * @since 5.2.0
+ *
+ * @var string $action
+ */
+ protected static $action;
+
+ /**
+ * Eraser Index.
+ *
+ * @since 5.2.0
+ *
+ * @var int $eraser
+ */
+ protected static $eraser;
+
+ /**
+ * Eraser Key.
+ *
+ * @since 5.2.0
+ *
+ * @var string $eraser_key
+ */
+ protected static $eraser_key;
+
+ /**
+ * Eraser Friendly Name.
+ *
+ * @since 5.2.0
+ *
+ * @var string $eraser_friendly_name
+ */
+ protected static $eraser_friendly_name;
+
+ /**
+ * Page Index.
+ *
+ * @since 5.2.0
+ *
+ * @var int $page
+ */
+ protected static $page;
+
+ /**
+ * Last response parsed.
+ *
+ * @since 5.2.0
+ *
+ * @var array $_last_response_parsed
+ */
+ protected $_last_response_parsed;
+
+ /**
+ * An array key in the test eraser to unset.
+ *
+ * @since 5.2.0
+ *
+ * @var string $key_to_unset
+ */
+ protected $key_to_unset;
+
+ /**
+ * A value to change the test eraser callback to.
+ *
+ * @since 5.2.0
+ *
+ * @var string $new_callback_value
+ */
+ protected $new_callback_value;
+
+ /**
+ * Create user erase request fixtures.
+ *
+ * @param WP_UnitTest_Factory $factory Factory.
+ */
+ public static function wpSetUpBeforeClass( $factory ) {
+ self::$request_email = 'requester@example.com';
+ self::$request_id = wp_create_user_request( self::$request_email, 'remove_personal_data' );
+ self::$action = 'wp-privacy-erase-personal-data';
+ self::$eraser = 1;
+ self::$eraser_key = 'custom-eraser';
+ self::$eraser_friendly_name = 'Custom Eraser';
+ self::$page = 1;
+ }
+
+ /**
+ * Register a custom personal data eraser.
+ */
+ public function setUp() {
+ parent::setUp();
+
+ $this->key_to_unset = '';
+
+ // Make sure the erasers response is not modified and avoid sending emails.
+ remove_all_filters( 'wp_privacy_personal_data_erasure_page' );
+ remove_all_actions( 'wp_privacy_personal_data_erased' );
+
+ // Only use our custom privacy personal data eraser.
+ remove_all_filters( 'wp_privacy_personal_data_erasers' );
+ add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
+
+ $this->_setRole( 'administrator' );
+ }
+
+ /**
+ * Clean up after each test method.
+ */
+ public function tearDown() {
+ remove_filter( 'wp_privacy_personal_data_erasers', array( $this, 'register_custom_personal_data_eraser' ) );
+ $this->new_callback_value = '';
+
+ parent::tearDown();
+ }
+
+ /**
+ * Helper method for changing the test eraser's callback function.
+ *
+ * @param string|array $callback New test eraser callback index value.
+ */
+ protected function _set_eraser_callback( $callback ) {
+ $this->new_callback_value = $callback;
+ add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_eraser_callback_value' ), 20 );
+ }
+
+ /**
+ * Change the test eraser callback to a specified value.
+ *
+ * @since 5.2.0
+ *
+ * @param array $erasers List of data erasers.
+ *
+ * @return array $erasersList of data erasers.
+ */
+ public function filter_eraser_callback_value( $erasers ) {
+ $erasers[ self::$eraser_key ]['callback'] = $this->new_callback_value;
+
+ return $erasers;
+ }
+
+ /**
+ * Helper method for unsetting an array index in the test eraser.
+ *
+ * @param string|bool $key Test eraser key to unset.
+ */
+ protected function _unset_eraser_key( $key ) {
+ $this->key_to_unset = $key;
+ add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'filter_unset_eraser_index' ), 20 );
+ }
+
+ /**
+ * Unsets an array key in the test eraser.
+ *
+ * If the key is false, the eraser is set to false.
+ *
+ * @since 5.2.0
+ *
+ * @param array $erasers Erasers.
+ *
+ * @return array $erasers Erasers.
+ */
+ public function filter_unset_eraser_index( $erasers ) {
+ if ( false === $this->key_to_unset ) {
+ $erasers[ self::$eraser_key ] = false;
+ } elseif ( ! empty( $this->key_to_unset ) ) {
+ unset( $erasers[ self::$eraser_key ][ $this->key_to_unset ] );
+ }
+
+ return $erasers;
+ }
+
+ /**
+ * Helper method for erasing a key from the eraser response.
+ *
+ * @since 5.2.0
+ *
+ * @param array $key Response key to unset.
+ */
+ protected function _unset_response_key( $key ) {
+ $this->key_to_unset = $key;
+ $this->_set_eraser_callback( array( $this, 'filter_unset_response_index' ) );
+ }
+
+ /**
+ * Unsets an array index in a response.
+ *
+ * @since 5.2.0
+ *
+ * @param string $email_address The requester's email address.
+ * @param int $page Page number.
+ *
+ * @return array $return Export data.
+ */
+ public function filter_unset_response_index( $email_address, $page = 1 ) {
+ $response = $this->callback_personal_data_eraser( $email_address, $page );
+
+ if ( ! empty( $this->key_to_unset ) ) {
+ unset( $response[ $this->key_to_unset ] );
+ }
+
+ return $response;
+ }
+
+ /**
+ * The function should send an error when the request ID is missing.
+ *
+ * @since 5.2.0
+ *
+ * @ticket 43438
+ */
+ public function test_error_when_missing_request_id() {
+ $this->assertNotWPError( self::$request_id );
+
+ // Set up a request.
+ $this->_make_ajax_call(
+ array(
+ 'id' => null, // Missing request ID.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the request ID is less than 1.
+ *
+ * @since 5.2.0
+ *
+ * @ticket 43438
+ */
+ public function test_error_when_request_id_invalid() {
+ $this->assertNotWPError( self::$request_id );
+
+ // Set up a request.
+ $this->_make_ajax_call(
+ array(
+ 'id' => -1, // Invalid request ID.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the current user is missing required capabilities.
+ *
+ * @since 5.2.0
+ *
+ * @ticket 43438
+ */
+ public function test_error_when_current_user_missing_required_capabilities() {
+ $this->_setRole( 'author' );
+
+ $this->assertFalse( current_user_can( 'erase_others_personal_data' ) );
+ $this->assertFalse( current_user_can( 'delete_users' ) );
+
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the nonce does not validate.
+ *
+ * @since 5.2.0
+ */
+ public function test_failure_with_invalid_nonce() {
+ $this->setExpectedException( 'WPAjaxDieStopException', '-1' );
+
+ $this->_make_ajax_call(
+ array(
+ 'security' => 'invalid-nonce',
+ )
+ );
+ }
+
+ /**
+ * The function should send an error when the request type is incorrect.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_incorrect_request_type() {
+ $request_id = wp_create_user_request(
+ 'export-request@example.com',
+ 'export_personal_data' // Incorrect request type, expects 'remove_personal_data'.
+ );
+
+ $this->_make_ajax_call(
+ array(
+ 'security' => wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ),
+ 'id' => $request_id,
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the request email is invalid.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_invalid_email() {
+ wp_update_post(
+ array(
+ 'ID' => self::$request_id,
+ 'post_title' => '', // Invalid requester's email address.
+ )
+ );
+
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Invalid email address in request.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the eraser index is missing.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_missing_eraser_index() {
+ $this->_make_ajax_call(
+ array(
+ 'eraser' => null, // Missing eraser index.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Missing eraser index.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the page index is missing.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_missing_page_index() {
+ $this->_make_ajax_call(
+ array(
+ 'page' => null, // Missing page index.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the eraser index is negative.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_negative_eraser_index() {
+ $this->_make_ajax_call(
+ array(
+ 'eraser' => -1, // Negative eraser index.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Eraser index cannot be less than one.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the eraser index is out of range.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_index_out_of_range() {
+ $this->_make_ajax_call(
+ array(
+ 'eraser' => PHP_INT_MAX, // Out of range eraser index.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Eraser index is out of range.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the page index is less than one.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_page_index_less_than_one() {
+ $this->_make_ajax_call(
+ array(
+ 'page' => 0, // Page index less than one.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when an eraser is not an array.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_not_array() {
+ $this->_unset_eraser_key( false );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected an array describing the eraser at index %s.',
+ self::$eraser
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when an eraser is missing a friendly name.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_missing_friendly_name() {
+ $this->_unset_eraser_key( 'eraser_friendly_name' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Eraser array at index %s does not include a friendly name.',
+ self::$eraser
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when an eraser is missing a callback.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_missing_callback() {
+ $this->_unset_eraser_key( 'callback' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Eraser does not include a callback: %s.',
+ self::$eraser_friendly_name
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when an eraser, at a given index, has an invalid callback.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_index_invalid_callback() {
+ $this->_set_eraser_callback( false );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Eraser callback is not valid: %s.',
+ self::$eraser_friendly_name
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when an eraser, at a given index, is missing an array response.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_index_invalid_response() {
+ $this->_set_eraser_callback( '__return_null' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Did not receive array from %1$s eraser (index %2$d).',
+ self::$eraser_friendly_name,
+ self::$eraser
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when missing an items_removed index.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_items_removed_missing() {
+ $this->_unset_response_key( 'items_removed' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected items_removed key in response array from %1$s eraser (index %2$d).',
+ self::$eraser_friendly_name,
+ self::$eraser
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when missing an items_retained index.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_items_retained_missing() {
+ $this->_unset_response_key( 'items_retained' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected items_retained key in response array from %1$s eraser (index %2$d).',
+ self::$eraser_friendly_name,
+ self::$eraser
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when missing a messages index.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_messages_missing() {
+ $this->_unset_response_key( 'messages' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected messages key in response array from %1$s eraser (index %2$d).',
+ self::$eraser_friendly_name,
+ self::$eraser
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when the messages index is not an array.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_messages_not_array() {
+ $this->_set_eraser_callback( array( $this, 'filter_response_messages_invalid' ) );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).',
+ self::$eraser_friendly_name,
+ self::$eraser
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * Change the messages index to an invalid value (not an array).
+ *
+ * @since 5.2.0
+ *
+ * @param string $email_address The requester's email address.
+ * @param int $page Page number.
+ *
+ * @return array $return Export data.
+ */
+ public function filter_response_messages_invalid( $email_address, $page = 1 ) {
+ $response = $this->callback_personal_data_eraser( $email_address, $page );
+ $response['messages'] = true;
+
+ return $response;
+ }
+
+ /**
+ * The function should send an error when an eraser is missing 'done' in array response.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_eraser_missing_done_response() {
+ $this->_unset_response_key( 'done' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected done flag in response array from %1$s eraser (index %2$d).',
+ self::$eraser_friendly_name,
+ self::$eraser
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should successfully send erasers response data when the current user has the required
+ * capabilities.
+ *
+ * @since 5.2.0
+ *
+ * @ticket 43438
+ */
+ public function test_success_when_current_user_has_required_capabilities() {
+ $this->assertTrue( current_user_can( 'erase_others_personal_data' ) );
+ $this->assertTrue( current_user_can( 'delete_users' ) );
+
+ $this->_make_ajax_call();
+
+ $this->assertSame(
+ sprintf( 'A message regarding retained data for %s.', self::$request_email ),
+ $this->_last_response_parsed['data']['messages'][0]
+ );
+ $this->assertTrue( $this->_last_response_parsed['success'] );
+ $this->assertTrue( $this->_last_response_parsed['data']['items_removed'] );
+ $this->assertTrue( $this->_last_response_parsed['data']['items_retained'] );
+ $this->assertTrue( $this->_last_response_parsed['data']['done'] );
+ }
+
+ /**
+ * The function should successfully send erasers response data when no items to erase.
+ *
+ * @since 5.2.0
+ *
+ * @ticket 43438
+ */
+ public function test_success_when_no_items_to_erase() {
+
+ $this->_make_ajax_call( array( 'page' => 2 ) );
+
+ $this->assertTrue( $this->_last_response_parsed['success'] );
+ $this->assertFalse( $this->_last_response_parsed['data']['items_removed'] );
+ $this->assertFalse( $this->_last_response_parsed['data']['items_retained'] );
+ $this->assertEmpty( $this->_last_response_parsed['data']['messages'] );
+ $this->assertTrue( $this->_last_response_parsed['data']['done'] );
+ }
+
+ /**
+ * Test that the function's output should be filterable with the `wp_privacy_personal_data_erasure_page` filter.
+ *
+ * @since 5.2.0
+ */
+ public function test_output_should_be_filterable() {
+ add_filter( 'wp_privacy_personal_data_erasure_page', array( $this, 'filter_eraser_data_response' ), 20, 6 );
+ $this->_make_ajax_call();
+
+ $expected_new_index = self::$request_email . '-' . self::$request_id . '-' . self::$eraser_key;
+
+ $this->assertTrue( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'filtered removed', $this->_last_response_parsed['data']['items_removed'] );
+ $this->assertSame( 'filtered retained', $this->_last_response_parsed['data']['items_retained'] );
+ $this->assertSame( array( 'filtered messages' ), $this->_last_response_parsed['data']['messages'] );
+ $this->assertSame( 'filtered done', $this->_last_response_parsed['data']['done'] );
+ $this->assertSame( $expected_new_index, $this->_last_response_parsed['data']['new_index'] );
+ }
+
+ /**
+ * Filters the eraser response.
+ *
+ * @since 5.2.0
+ *
+ * @param array $response The personal data for the given eraser and page.
+ * @param int $eraser_index The index of the eraser that provided this data.
+ * @param string $email_address The email address associated with this personal data.
+ * @param int $page The page for this response.
+ * @param int $request_id The privacy request post ID associated with this request.
+ * @param string $eraser_key The key (slug) of the eraser that provided this data.
+ *
+ * @return array Filtered erase response.
+ */
+ public function filter_eraser_data_response( $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ) {
+ $response['items_removed'] = 'filtered removed';
+ $response['items_retained'] = 'filtered retained';
+ $response['messages'] = array( 'filtered messages' );
+ $response['done'] = 'filtered done';
+ $response['new_index'] = $email_address . '-' . $request_id . '-' . $eraser_key;
+
+ return $response;
+ }
+
+ /**
+ * Register handler for a custom personal data eraser.
+ *
+ * @since 5.2.0
+ *
+ * @param array $erasers An array of personal data erasers.
+ *
+ * @return array $erasers An array of personal data erasers.
+ */
+ public function register_custom_personal_data_eraser( $erasers ) {
+ $erasers[ self::$eraser_key ] = array(
+ 'eraser_friendly_name' => self::$eraser_friendly_name,
+ 'callback' => array( $this, 'callback_personal_data_eraser' ),
+ );
+ return $erasers;
+ }
+
+ /**
+ * Custom Personal Data Eraser.
+ *
+ * @since 5.2.0
+ *
+ * @param string $email_address The comment author email address.
+ * @param int $page Page number.
+ *
+ * @return array $return Erase data.
+ */
+ public function callback_personal_data_eraser( $email_address, $page = 1 ) {
+ if ( 1 === $page ) {
+ return array(
+ 'items_removed' => true,
+ 'items_retained' => true,
+ 'messages' => array( sprintf( 'A message regarding retained data for %s.', $email_address ) ),
+ 'done' => true,
+ );
+ }
+
+ return array(
+ 'items_removed' => false,
+ 'items_retained' => false,
+ 'messages' => array(),
+ 'done' => true,
+ );
+ }
+
+ /**
+ * Helper function for ajax handler.
+ *
+ * @since 5.2.0
+ *
+ * @param array $args Ajax request arguments.
+ */
+ protected function _make_ajax_call( $args = array() ) {
+ $this->_last_response_parsed = null;
+ $this->_last_response = '';
+
+ $defaults = array(
+ 'action' => self::$action,
+ 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ),
+ 'page' => self::$page,
+ 'id' => self::$request_id,
+ 'eraser' => self::$eraser,
+ );
+
+ $_POST = wp_parse_args( $args, $defaults );
+
+ try {
+ $this->_handleAjax( self::$action );
+ } catch ( WPAjaxDieContinueException $e ) {
+ unset( $e );
+ }
+
+ if ( $this->_last_response ) {
+ $this->_last_response_parsed = json_decode( $this->_last_response, true );
+ }
+ }
+}
diff --git a/tests/phpunit/tests/ajax/PrivacyExportPersonalData.php b/tests/phpunit/tests/ajax/PrivacyExportPersonalData.php
new file mode 100755
index 0000000000..7473a4957c
--- /dev/null
+++ b/tests/phpunit/tests/ajax/PrivacyExportPersonalData.php
@@ -0,0 +1,818 @@
+<?php
+/**
+ * Testing Ajax handler for exporting personal data.
+ *
+ * @package WordPress\UnitTests
+ * @since 5.2.0
+ */
+
+/**
+ * Tests_Ajax_PrivacyExportPersonalData class.
+ *
+ * @since 5.2.0
+ *
+ * @group ajax
+ * @group privacy
+ *
+ * @covers ::wp_ajax_wp_privacy_export_personal_data
+ */
+class Tests_Ajax_PrivacyExportPersonalData extends WP_Ajax_UnitTestCase {
+
+ /**
+ * User Request ID.
+ *
+ * @since 5.2.0
+ *
+ * @var int $request_id
+ */
+ protected static $request_id;
+
+ /**
+ * User Request Email.
+ *
+ * @since 5.2.0
+ *
+ * @var string $request_email
+ */
+ protected static $request_email;
+
+ /**
+ * Ajax Action.
+ *
+ * @since 5.2.0
+ *
+ * @var string $action
+ */
+ protected static $action;
+
+ /**
+ * Exporter Index.
+ *
+ * @since 5.2.0
+ *
+ * @var int $exporter
+ */
+ protected static $exporter;
+
+ /**
+ * Exporter Key.
+ *
+ * @since 5.2.0
+ *
+ * @var string $exporter_key
+ */
+ protected static $exporter_key;
+
+ /**
+ * Exporter Friendly Name.
+ *
+ * @since 5.2.0
+ *
+ * @var string $exporter_friendly_name
+ */
+ protected static $exporter_friendly_name;
+
+ /**
+ * Page Index.
+ *
+ * @since 5.2.0
+ *
+ * @var int $page
+ */
+ protected static $page;
+
+ /**
+ * Send As Email.
+ *
+ * @since 5.2.0
+ *
+ * @var bool $send_as_email
+ */
+ protected static $send_as_email;
+
+ /**
+ * Last response parsed.
+ *
+ * @since 5.2.0
+ *
+ * @var array $_last_response_parsed
+ */
+ protected $_last_response_parsed;
+
+ /**
+ * An array key in the test exporter to unset.
+ *
+ * @since 5.2.0
+ *
+ * @var string $key_to_unset
+ */
+ protected $key_to_unset;
+
+ /**
+ * A value to change the test exporter callback to.
+ *
+ * @since 5.2.0
+ *
+ * @var string $new_callback_value
+ */
+ protected $new_callback_value;
+
+ /**
+ * Create user export request fixtures.
+ *
+ * @since 5.2.0
+ *
+ * @param WP_UnitTest_Factory $factory Factory.
+ */
+ public static function wpSetUpBeforeClass( $factory ) {
+ self::$request_email = 'requester@example.com';
+ self::$request_id = wp_create_user_request( self::$request_email, 'export_personal_data' );
+ self::$action = 'wp-privacy-export-personal-data';
+ self::$exporter = 1;
+ self::$exporter_key = 'custom-exporter';
+ self::$exporter_friendly_name = 'Custom Exporter';
+ self::$page = 1;
+ self::$send_as_email = false;
+ }
+
+ /**
+ * Setup before each test method.
+ *
+ * @since 5.2.0
+ */
+ public function setUp() {
+ parent::setUp();
+
+ $this->key_to_unset = '';
+ $this->new_callback_value = '';
+
+ // Make sure the exporter response is not modified and avoid e.g. writing export file to disk.
+ remove_all_filters( 'wp_privacy_personal_data_export_page' );
+
+ // Only use our custom privacy personal data exporter.
+ remove_all_filters( 'wp_privacy_personal_data_exporters' );
+ add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
+
+ $this->_setRole( 'administrator' );
+ }
+
+ /**
+ * Clean up after each test method.
+ */
+ public function tearDown() {
+ remove_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_register_custom_personal_data_exporter' ) );
+
+ parent::tearDown();
+ }
+
+ /**
+ * Helper method for changing the test exporter's callback function.
+ *
+ * @param string|array $callback New test exporter callback function.
+ */
+ protected function _set_exporter_callback( $callback ) {
+ $this->new_callback_value = $callback;
+ add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_exporter_callback_value' ), 20 );
+ }
+
+ /**
+ * Change the test exporter callback to a specified value.
+ *
+ * @since 5.2.0
+ *
+ * @param array $exporters List of data exporters.
+ * @return array $exporters List of data exporters.
+ */
+ public function filter_exporter_callback_value( $exporters ) {
+ $exporters[ self::$exporter_key ]['callback'] = $this->new_callback_value;
+
+ return $exporters;
+ }
+
+ /**
+ * Helper method for unsetting an array index in the test exporter.
+ *
+ * @param string $key Test exporter key to unset.
+ */
+ protected function _unset_exporter_key( $key ) {
+ $this->key_to_unset = $key;
+ add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_unset_exporter_key' ), 20 );
+ }
+
+ /**
+ * Unset a specified key in the test exporter array.
+ *
+ * @param array $exporters List of data exporters.
+ *
+ * @return array $exporters List of data exporters.
+ */
+ public function filter_unset_exporter_key( $exporters ) {
+ if ( false === $this->key_to_unset ) {
+ $exporters[ self::$exporter_key ] = false;
+ } elseif ( ! empty( $this->key_to_unset ) ) {
+ unset( $exporters[ self::$exporter_key ][ $this->key_to_unset ] );
+ }
+
+ return $exporters;
+ }
+
+ /**
+ * The function should send an error when the request ID is missing.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_missing_request_id() {
+ $this->_make_ajax_call(
+ array(
+ 'id' => null, // Missing request ID.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Missing request ID.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the request ID is less than 1.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_invalid_id() {
+ $this->_make_ajax_call(
+ array(
+ 'id' => -1, // Invalid request ID, less than 1.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Invalid request ID.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the current user is missing the required capability.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_current_user_missing_required_capability() {
+ $this->_setRole( 'author' );
+
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertFalse( current_user_can( 'export_others_personal_data' ) );
+ $this->assertSame( 'Sorry, you are not allowed to perform this action.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the nonce does not validate.
+ *
+ * @since 5.2.0
+ */
+ public function test_failure_with_invalid_nonce() {
+ $this->setExpectedException( 'WPAjaxDieStopException', '-1' );
+
+ $this->_make_ajax_call(
+ array(
+ 'security' => 'invalid-nonce',
+ )
+ );
+ }
+
+ /**
+ * The function should send an error when the request type is incorrect.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_incorrect_request_type() {
+ $request_id = wp_create_user_request(
+ 'erase-request@example.com',
+ 'remove_personal_data' // Incorrect request type, expects 'export_personal_data'.
+ );
+
+ $this->_make_ajax_call(
+ array(
+ 'security' => wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ),
+ 'id' => $request_id,
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Invalid request type.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the requester's email address is invalid.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_invalid_email_address() {
+ wp_update_post(
+ array(
+ 'ID' => self::$request_id,
+ 'post_title' => '', // Invalid requester's email address.
+ )
+ );
+
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'A valid email address must be given.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the exporter index is missing.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_missing_exporter_index() {
+ $this->_make_ajax_call(
+ array(
+ 'exporter' => null, // Missing exporter index.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Missing exporter index.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the page index is missing.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_missing_page_index() {
+ $this->_make_ajax_call(
+ array(
+ 'page' => null, // Missing page index.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Missing page index.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when an exporter has improperly used the `wp_privacy_personal_data_exporters` filter.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_has_improperly_used_exporters_filter() {
+ // Improper filter usage: returns false instead of an expected array.
+ add_filter( 'wp_privacy_personal_data_exporters', '__return_false', 999 );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'An exporter has improperly used the registration filter.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the exporter index is negative.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_negative_exporter_index() {
+ $this->_make_ajax_call(
+ array(
+ 'exporter' => -1, // Negative exporter index.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Exporter index cannot be negative.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the exporter index is out of range.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_index_out_of_range() {
+ $this->_make_ajax_call(
+ array(
+ 'exporter' => PHP_INT_MAX, // Out of range exporter index.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Exporter index is out of range.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when the page index is less than one.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_page_index_less_than_one() {
+ $this->_make_ajax_call(
+ array(
+ 'page' => 0, // Page index less than one.
+ )
+ );
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'Page index cannot be less than one.', $this->_last_response_parsed['data'] );
+ }
+
+ /**
+ * The function should send an error when an exporter is not an array.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_not_array() {
+ $this->_unset_exporter_key( false );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected an array describing the exporter at index %s.',
+ self::$exporter_key
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when an exporter is missing a friendly name.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_missing_friendly_name() {
+ $this->_unset_exporter_key( 'exporter_friendly_name' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Exporter array at index %s does not include a friendly name.',
+ self::$exporter_key
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when an exporter is missing a callback.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_missing_callback() {
+ $this->_unset_exporter_key( 'callback' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Exporter does not include a callback: %s.',
+ self::$exporter_friendly_name
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when an exporter, at a given index, has an invalid callback.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_index_invalid_callback() {
+ $this->_set_exporter_callback( false );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Exporter callback is not a valid callback: %s.',
+ self::$exporter_friendly_name
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * When an exporter callback returns a WP_Error, it should be passed as the error.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_callback_returns_wp_error() {
+ $this->_set_exporter_callback( array( $this, 'callback_return_wp_error' ) );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'passed_message', $this->_last_response_parsed['data'][0]['code'] );
+ $this->assertSame( 'This is a WP_Error message.', $this->_last_response_parsed['data'][0]['message'] );
+ }
+
+ /**
+ * Callback for exporter's response.
+ *
+ * @since 5.2.0
+ *
+ * @param string $email_address The requester's email address.
+ * @param int $page Page number.
+ * @return WP_Error WP_Error instance.
+ */
+ public function callback_return_wp_error( $email_address, $page = 1 ) {
+ return new WP_Error( 'passed_message', 'This is a WP_Error message.' );
+ }
+
+ /**
+ * The function should send an error when an exporter, at a given index, is missing an array response.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_index_invalid_response() {
+ $this->_set_exporter_callback( '__return_null' );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected response as an array from exporter: %s.',
+ self::$exporter_friendly_name
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * The function should send an error when an exporter is missing data in array response.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_missing_data_response() {
+ $this->_set_exporter_callback( array( $this, 'callback_missing_data_response' ) );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected data in response array from exporter: %s.',
+ self::$exporter_friendly_name
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * Callback for exporter's response.
+ *
+ * @since 5.2.0
+ *
+ * @param string $email_address The requester's email address.
+ * @param int $page Page number.
+ *
+ * @return array $return Export data.
+ */
+ public function callback_missing_data_response( $email_address, $page = 1 ) {
+ $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
+ unset( $response['data'] ); // Missing data part of response.
+
+ return $response;
+ }
+
+ /**
+ * The function should send an error when an exporter is missing 'data' array in array response.
+ *
+ * @since 5.2.0
+ */
+ public function test_function_should_error_when_exporter_missing_data_array_response() {
+ $this->_set_exporter_callback( array( $this, 'callback_missing_data_array_response' ) );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected data array in response array from exporter: %s.',
+ self::$exporter_friendly_name
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * Callback for exporter's response.
+ *
+ * @since 5.2.0
+ *
+ * @param string $email_address The requester's email address.
+ * @param int $page Page number.
+ *
+ * @return array $return Export data.
+ */
+ public function callback_missing_data_array_response( $email_address, $page = 1 ) {
+ $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
+ $response['data'] = false; // Not an array.
+ return $response;
+ }
+
+ /**
+ * The function should send an error when an exporter is missing 'done' in array response.
+ *
+ * @since 5.2.0
+ */
+ public function test_error_when_exporter_missing_done_response() {
+ $this->_set_exporter_callback( array( $this, 'callback_missing_done_response' ) );
+ $this->_make_ajax_call();
+
+ $this->assertFalse( $this->_last_response_parsed['success'] );
+ $this->assertSame(
+ sprintf(
+ 'Expected done (boolean) in response array from exporter: %s.',
+ self::$exporter_friendly_name
+ ),
+ $this->_last_response_parsed['data']
+ );
+ }
+
+ /**
+ * Remove the response's done flag.
+ *
+ * @since 5.2.0
+ *
+ * @param string $email_address The requester's email address.
+ * @param int $page Page number.
+ *
+ * @return array $return Export data.
+ */
+ public function callback_missing_done_response( $email_address, $page = 1 ) {
+ $response = $this->callback_custom_personal_data_exporter( $email_address, $page );
+ unset( $response['done'] );
+
+ return $response;
+ }
+
+ /**
+ * The function should successfully send exporter data response when the current user has the required capability.
+ *
+ * @since 5.2.0
+ */
+ public function test_succeeds_when_current_user_has_required_capability() {
+ $this->assertTrue( current_user_can( 'export_others_personal_data' ) );
+
+ $this->_make_ajax_call();
+
+ $this->assertTrue( $this->_last_response_parsed['success'] );
+ $this->assertSame( 'custom-exporter-item-id', $this->_last_response_parsed['data']['data']['item_id'] );
+ $this->assertSame( 'Email', $this->_last_response_parsed['data']['data']['data'][0]['name'] );
+ $this->assertSame( self::$request_email, $this->_last_response_parsed['data']['data']['data'][0]['value'] );
+ }
+
+ /**
+ * The function should successfully send exporter data response when no items to export.
+ *
+ * @since 5.2.0
+ */
+ public function test_success_when_no_items_to_export() {
+
+ $this->_make_ajax_call( array( 'page' => 2 ) );
+
+ $this->assertTrue( $this->_last_response_parsed['success'] );
+ $this->assertEmpty( $this->_last_response_parsed['data']['data'] );
+ $this->assertTrue( $this->_last_response_parsed['data']['done'] );
+ }
+
+ /**
+ * The function's output should be filterable with the `wp_privacy_personal_data_export_page` filter.
+ *
+ * @since 5.2.0
+ */
+ public function test_output_should_be_filterable() {
+ add_filter( 'wp_privacy_personal_data_export_page', array( $this, 'filter_exporter_data_response' ), 20, 7 );
+ $this->_make_ajax_call();
+
+ $expected_group_label = sprintf(
+ '%s-%s-%s-%s-%s-%s',
+ self::$exporter,
+ self::$page,
+ self::$request_email,
+ self::$request_id,
+ self::$send_as_email,
+ self::$exporter_key
+ );
+
+ $this->assertTrue( $this->_last_response_parsed['success'] );
+ $this->assertSame( $expected_group_label, $this->_last_response_parsed['data']['group_label'] );
+ $this->assertSame( 'filtered_group_id', $this->_last_response_parsed['data']['group_id'] );
+ $this->assertSame( 'filtered_item_id', $this->_last_response_parsed['data']['item_id'] );
+ $this->assertSame( 'filtered_name', $this->_last_response_parsed['data']['data'][0]['name'] );
+ $this->assertSame( 'filtered_value', $this->_last_response_parsed['data']['data'][0]['value'] );
+ }
+
+ /**
+ * Filter exporter's data response.
+ *
+ * @since 5.2.0
+ *
+ * @param array $response The personal data for the given exporter and page.
+ * @param int $exporter_index The index of the exporter that provided this data.
+ * @param string $email_address The email address associated with this personal data.
+ * @param int $page The page for this response.
+ * @param int $request_id The privacy request post ID associated with this request.
+ * @param bool $send_as_email Whether the final results of the export should be emailed to the user.
+ * @param string $exporter_key The key (slug) of the exporter that provided this data.
+ *
+ * @return array $response The personal data for the given exporter and page.
+ */
+ public function filter_exporter_data_response( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) {
+ $group_label = sprintf(
+ '%s-%s-%s-%s-%s-%s',
+ $exporter_index,
+ $page,
+ $email_address,
+ $request_id,
+ $send_as_email,
+ $exporter_key
+ );
+ $response['group_label'] = $group_label;
+ $response['group_id'] = 'filtered_group_id';
+ $response['item_id'] = 'filtered_item_id';
+ $response['data'][0]['name'] = 'filtered_name';
+ $response['data'][0]['value'] = 'filtered_value';
+
+ return $response;
+ }
+
+ /**
+ * Filter to register a custom personal data exporter.
+ *
+ * @since 5.2.0
+ *
+ * @param array $exporters An array of personal data exporters.
+ *
+ * @return array $exporters An array of personal data exporters.
+ */
+ public function filter_register_custom_personal_data_exporter( $exporters ) {
+ $exporters[ self::$exporter_key ] = array(
+ 'exporter_friendly_name' => self::$exporter_friendly_name,
+ 'callback' => array( $this, 'callback_custom_personal_data_exporter' ),
+ );
+ return $exporters;
+ }
+
+ /**
+ * Callback for a custom personal data exporter.
+ *
+ * @since 5.2.0
+ *
+ * @param string $email_address The requester's email address.
+ * @param int $page Page number.
+ *
+ * @return array $response Export data response.
+ */
+ public function callback_custom_personal_data_exporter( $email_address, $page = 1 ) {
+ $data_to_export = array();
+
+ if ( 1 === $page ) {
+ $data_to_export = array(
+ 'group_id' => self::$exporter_key . '-group-id',
+ 'group_label' => self::$exporter_key . '-group-label',
+ 'item_id' => self::$exporter_key . '-item-id',
+ 'data' => array(
+ array(
+ 'name' => 'Email',
+ 'value' => $email_address,
+ ),
+ ),
+ );
+ }
+
+ return array(
+ 'data' => $data_to_export,
+ 'done' => true,
+ );
+ }
+
+ /**
+ * Helper function for ajax handler.
+ *
+ * @since 5.2.0
+ *
+ * @param array $args Ajax request arguments.
+ */
+ protected function _make_ajax_call( $args = array() ) {
+ $this->_last_response_parsed = null;
+ $this->_last_response = '';
+
+ $defaults = array(
+ 'action' => self::$action,
+ 'security' => wp_create_nonce( self::$action . '-' . self::$request_id ),
+ 'exporter' => self::$exporter,
+ 'page' => self::$page,
+ 'sendAsEmail' => self::$send_as_email,
+ 'id' => self::$request_id,
+ );
+
+ $_POST = wp_parse_args( $args, $defaults );
+
+ try {
+ $this->_handleAjax( self::$action );
+ } catch ( WPAjaxDieContinueException $e ) {
+ unset( $e );
+ }
+
+ if ( $this->_last_response ) {
+ $this->_last_response_parsed = json_decode( $this->_last_response, true );
+ }
+ }
+}