Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for WP personal data exporter & eraser #1355 #1356

Merged
merged 15 commits into from
Oct 21, 2023
Merged
19 changes: 18 additions & 1 deletion src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -538,10 +538,24 @@ public static function registerUserDataExporters( $exporters ) {
'exporter_friendly_name' => __( 'CommonsBooking Bookings', 'commonsbooking' ),
'callback' => array( \CommonsBooking\Wordpress\CustomPostType\Booking::class, 'exportUserBookingsByEmail' ),
);

return $exporters;
}

/**
* Registers all user data erasers ({@link https://developer.wordpress.org/plugins/privacy/adding-the-personal-data-eraser-to-your-plugin/}).
*
* @param $erasers
*
* @return mixed
*/
public static function registerUserDataErasers( $erasers ) {
$erasers[COMMONSBOOKING_PLUGIN_SLUG] = array(
'eraser_friendly_name' => __( 'CommonsBooking Bookings', 'commonsbooking' ),
'callback' => array( \CommonsBooking\Wordpress\CustomPostType\Booking::class, 'removeUserBookingsByEmail'),
);
return $erasers;
}

/**
* Gets location position for locations without coordinates.
*/
Expand Down Expand Up @@ -627,6 +641,9 @@ function ( $plugin_data ) {
//hook into WordPress personal data exporter
add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'registerUserDataExporters' ) );

//hook into WordPress personal data eraser
add_filter( 'wp_privacy_personal_data_erasers', array( $this, 'registerUserDataErasers' ) );

// iCal rewrite
iCalendar::initRewrite();

Expand Down
118 changes: 76 additions & 42 deletions src/Repository/Booking.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,26 +77,7 @@ public static function getEndingBookingsByDate( int $timestamp, array $customArg
// Overwrite args with passed custom args
$args = array_merge( $args, $customArgs );

$query = new WP_Query( $args );
if ( $query->have_posts() ) {
$posts = $query->get_posts();

// Filter by post_status, query seems not to work reliable
$posts = array_filter(
$posts,
function ( $post ) use ( $args ) {
return in_array( $post->post_status, $args['post_status'] );
}
);

foreach ( $posts as &$post ) {
$post = new \CommonsBooking\Model\Booking( $post );
}

return $posts;
}

return [];
return self::getModelsFromQuery( $args );
}

/**
Expand Down Expand Up @@ -293,27 +274,7 @@ public static function getByTimerange(
$args = array_merge( $args, $customArgs );



$query = new WP_Query( $args );
if ( $query->have_posts() ) {
$posts = $query->get_posts();

// Filter by post_status, query seems not to work reliable
$posts = array_filter(
$posts,
function ( $post ) use ( $args ) {
return in_array( $post->post_status, $args['post_status'] );
}
);

foreach ( $posts as &$post ) {
$post = new \CommonsBooking\Model\Booking( $post );
}

return $posts;
}

return [];
return self::getModelsFromQuery( $args );
}

/**
Expand Down Expand Up @@ -380,7 +341,8 @@ public static function getForCurrentUser( bool $asModel = false, $startDate = nu
}

/**
* Returns bookings.
* Returns bookings. This uses the CommonsBooking\Repository\Timeframe::get() method which
* is not based on the WP_Query class but will perform its own SQL query.
*
* @param array $locations
* @param array $items
Expand Down Expand Up @@ -411,6 +373,47 @@ public static function get(
);
}

/**
* We use this function instead of the getForUser() function when we need to paginate the results.
* This is to prevent timeouts for bigger queries such as data exports. As opposed to the getForUser() function,
* this function will use the WP_Query class to perform the query allowing us to use the pagination features of WP_Query.
*
* @param \WP_User $user The user for which to get the bookings.
* @param int $page The current page that is processed.
* @param int $perPage The number of bookings per page. A lower number will result in faster queries.
* @param array $customArgs Valid WP_Query args array.
*
* @return array
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hier fehlt noch Model/Booking type annotation

*/
public static function getForUserPaginated(
\WP_User $user,
int $page = 1,
int $perPage = 10,
$customArgs = [],
$postStatus = [ 'confirmed', 'unconfirmed', 'canceled' , 'publish', 'inherit' ]
): array {
$args = array(
'author' => $user->ID,
'post_type' => \CommonsBooking\Wordpress\CustomPostType\Booking::$postType,
'meta_query' => array(
array(
'key' => 'type',
'value' => Timeframe::BOOKING_ID,
'compare' => '=',
),
),
'post_status' => $postStatus,
'posts_per_page' => $perPage,
'paged' => $page,
'orderby' => 'ID',
'order' => 'ASC',
);
// Overwrite args with passed custom args
$args = array_merge( $args, $customArgs );

return self::getModelsFromQuery( $args );
}

/**
* Gets all bookings that are affected by the given restriction.
*
Expand Down Expand Up @@ -467,4 +470,35 @@ public static function getExistingBookings( $itemId, $locationId, $startDate, $e

}

/**
* Will take a valid WP_Query args array and return an array of Booking models.
*
* @param array $args
*
* @return \CommonsBooking\Model\Booking[]
* @throws Exception
*/
private static function getModelsFromQuery( array $args ): array {
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
$posts = $query->get_posts();

// Filter by post_status, query seems not to work reliable
$posts = array_filter(
$posts,
function ( $post ) use ( $args ) {
return in_array( $post->post_status, $args['post_status'] );
}
);

foreach ( $posts as &$post ) {
$post = new \CommonsBooking\Model\Booking( $post );
}

return $posts;
}

return [];
}

}
1 change: 1 addition & 0 deletions src/Repository/Timeframe.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public static function getBookableForCurrentUser(
* Why? We have different types of timeframes and in some cases we need multiple of them.
* In this case we need this function.
* Other functions use this one as base function for more specialized searches.
* This function is not based on the WP_Query class, probably because of performance reasons.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vielleicht eher ein TODO Investigate 😆 würde mich aber auch mal interessieren.

*
* @param array $locations
* @param array $items
Expand Down
102 changes: 79 additions & 23 deletions src/Wordpress/CustomPostType/Booking.php
Original file line number Diff line number Diff line change
Expand Up @@ -942,33 +942,34 @@ public function displayOverlappingBookingNotice( $post ) {

/**
* Export user bookings using the supplied email. This is for integration with the WordPress personal data exporter.
* TODO: FIX: We can't add pagination because of the way the @see \CommonsBooking\Repository\Booking::getForUser() works.
*
* @param string $emailAddress
* @param $page
*
* @return array
*/
public static function exportUserBookingsByEmail( string $emailAddress ): array {
$exportItems = array();
//The internal group ID used by WordPress to group the data exported by this exporter.
$groupID = 'bookings';
$groupLabel = __( 'CommonsBooking Bookings', 'commonsbooking' );

$user = get_user_by( 'email', $emailAddress );
if ( ! $user ) {
return array(
'data' => $exportItems,
'done' => true,
);
}
$bookings = \CommonsBooking\Repository\Booking::getForUser( $user, true );
if ( ! $bookings ) {
return array(
'data' => $exportItems,
'done' => true,
);
}
/** @var \CommonsBooking\Model\Booking $booking */
public static function exportUserBookingsByEmail( string $emailAddress, $page = 1 ): array {
$page = intval( $page );
$itemsPerPage = 10;
$exportItems = array();
//The internal group ID used by WordPress to group the data exported by this exporter.
$groupID = 'bookings';
$groupLabel = __( 'CommonsBooking Bookings', 'commonsbooking' );

$user = get_user_by( 'email', $emailAddress );
if ( ! $user ) {
return array(
'data' => $exportItems,
'done' => true,
);
}
$bookings = \CommonsBooking\Repository\Booking::getForUserPaginated( $user, $page, $itemsPerPage );
if ( ! $bookings ) {
return array(
'data' => $exportItems,
'done' => true,
);
}
foreach ($bookings as $booking) {
$bookingID = $booking->ID;
//exclude bookings that the user is eligible to see but are not their own
Expand Down Expand Up @@ -1026,9 +1027,64 @@ public static function exportUserBookingsByEmail( string $emailAddress ): array
'data' => $bookingData,
];
}
$done = count( $bookings ) < $itemsPerPage;
return array(
'data' => $exportItems,
'done' => true,
'done' => $done,
);
}

/**
* Remove user bookings using the supplied email. This is for integration with the WordPress personal data eraser.
* @param string $emailAddress The email address
* @param $page This parameter has no real use in this function, we just use it to stick to WordPress expected parameters.
*
* @return array
*/
public static function removeUserBookingsByEmail( string $emailAddress, $page = 1 ): array {
//we reset the page to 1, because we are deleting our results as we go. Therefore, increasing the page number would skip some results.
$page = 1;
$itemsPerPage = 10;
$removedItems = false;

$user = get_user_by( 'email', $emailAddress );
if ( ! $user ) {
return array(
'items_removed' => $removedItems,
'items_retained' => false,
'messages' => array(),
'done' => true,
);
}
$bookings = \CommonsBooking\Repository\Booking::getForUserPaginated( $user, $page, $itemsPerPage );
if ( ! $bookings ) {
return array(
'items_removed' => $removedItems,
'items_retained' => false,
'messages' => array(),
'done' => true,
);
}
foreach ($bookings as $booking) {
$bookingID = $booking->ID;
//exclude bookings that the user is eligible to see but are not their own
// we are only concerned about one user's personal data
if ( $booking->getUserData()->user_email !== $emailAddress ) {
continue;
}
//Cancel the booking before deletion so that status change emails are sent
$booking->cancel();
//Delete the booking
wp_delete_post( $bookingID, true );
$removedItems = true;
}

$done = count( $bookings ) < $itemsPerPage;
return array(
'items_removed' => $removedItems,
'items_retained' => false, // always false, we don't retain any data
'messages' => array(),
'done' => $done,
);
}

Expand Down
Loading