Add guest management and GDPR privacy compliance (v0.4.0)
All checks were successful
Create Release Package / build-release (push) Successful in 1m26s

- Create Guest CPT with personal info, address, ID/passport tracking
- Add guest-booking integration with AJAX search and linking
- Implement GDPR compliance via WordPress Privacy API (export/erasure)
- Update EmailNotifier to use Guest CPT data with new placeholders
- Add CSS styles for guest search, linked display, and privacy UI

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 14:59:43 +01:00
parent c66af8e299
commit aab3a4d1aa
9 changed files with 2858 additions and 71 deletions

View File

@@ -12,6 +12,7 @@ declare( strict_types=1 );
namespace Magdev\WpBnb\Booking;
use Magdev\WpBnb\PostTypes\Booking;
use Magdev\WpBnb\PostTypes\Guest;
use Magdev\WpBnb\PostTypes\Room;
use Magdev\WpBnb\Pricing\Calculator;
@@ -203,8 +204,8 @@ final class EmailNotifier {
* @return array Booking data.
*/
private static function get_booking_data( int $booking_id ): array {
$booking = get_post( $booking_id );
$room = Booking::get_room( $booking_id );
$booking = get_post( $booking_id );
$room = Booking::get_room( $booking_id );
$building = Booking::get_building( $booking_id );
$check_in = get_post_meta( $booking_id, '_bnb_booking_check_in', true );
@@ -221,31 +222,84 @@ final class EmailNotifier {
$statuses = Booking::get_booking_statuses();
// Get guest data - prefer Guest CPT if linked, fallback to booking meta.
$guest_data = self::get_guest_data( $booking_id );
return array(
'booking_id' => $booking_id,
'booking_reference' => $booking ? $booking->post_title : '',
'guest_name' => get_post_meta( $booking_id, '_bnb_booking_guest_name', true ),
'guest_email' => get_post_meta( $booking_id, '_bnb_booking_guest_email', true ),
'guest_phone' => get_post_meta( $booking_id, '_bnb_booking_guest_phone', true ),
'guest_notes' => get_post_meta( $booking_id, '_bnb_booking_guest_notes', true ),
'adults' => $adults ?: 1,
'children' => $children ?: 0,
'room_name' => $room ? $room->post_title : '',
'room_id' => $room ? $room->ID : 0,
'building_name' => $building ? $building->post_title : '',
'building_id' => $building ? $building->ID : 0,
'check_in_date' => $check_in ? wp_date( get_option( 'date_format' ), strtotime( $check_in ) ) : '',
'check_out_date' => $check_out ? wp_date( get_option( 'date_format' ), strtotime( $check_out ) ) : '',
'check_in_raw' => $check_in,
'check_out_raw' => $check_out,
'nights' => $nights,
'total_price' => $price ? Calculator::formatPrice( (float) $price ) : '',
'status' => $statuses[ $status ] ?? $status,
'status_raw' => $status,
'site_name' => get_bloginfo( 'name' ),
'site_url' => home_url(),
'admin_email' => get_option( 'admin_email' ),
'booking_url' => admin_url( 'post.php?post=' . $booking_id . '&action=edit' ),
'booking_id' => $booking_id,
'booking_reference' => $booking ? $booking->post_title : '',
'guest_name' => $guest_data['name'],
'guest_first_name' => $guest_data['first_name'],
'guest_last_name' => $guest_data['last_name'],
'guest_email' => $guest_data['email'],
'guest_phone' => $guest_data['phone'],
'guest_notes' => $guest_data['notes'],
'guest_full_address' => $guest_data['full_address'],
'adults' => $adults ?: 1,
'children' => $children ?: 0,
'room_name' => $room ? $room->post_title : '',
'room_id' => $room ? $room->ID : 0,
'building_name' => $building ? $building->post_title : '',
'building_id' => $building ? $building->ID : 0,
'check_in_date' => $check_in ? wp_date( get_option( 'date_format' ), strtotime( $check_in ) ) : '',
'check_out_date' => $check_out ? wp_date( get_option( 'date_format' ), strtotime( $check_out ) ) : '',
'check_in_raw' => $check_in,
'check_out_raw' => $check_out,
'nights' => $nights,
'total_price' => $price ? Calculator::formatPrice( (float) $price ) : '',
'status' => $statuses[ $status ] ?? $status,
'status_raw' => $status,
'site_name' => get_bloginfo( 'name' ),
'site_url' => home_url(),
'admin_email' => get_option( 'admin_email' ),
'booking_url' => admin_url( 'post.php?post=' . $booking_id . '&action=edit' ),
);
}
/**
* Get guest data from Guest CPT or booking meta.
*
* @param int $booking_id Booking post ID.
* @return array Guest data with keys: name, first_name, last_name, email, phone, notes, full_address.
*/
private static function get_guest_data( int $booking_id ): array {
$guest_id = get_post_meta( $booking_id, '_bnb_booking_guest_id', true );
// Try to get data from Guest CPT.
if ( $guest_id ) {
$guest = get_post( $guest_id );
if ( $guest && Guest::POST_TYPE === $guest->post_type ) {
$first_name = get_post_meta( $guest_id, '_bnb_guest_first_name', true );
$last_name = get_post_meta( $guest_id, '_bnb_guest_last_name', true );
return array(
'name' => Guest::get_full_name( $guest_id ),
'first_name' => $first_name,
'last_name' => $last_name,
'email' => get_post_meta( $guest_id, '_bnb_guest_email', true ),
'phone' => get_post_meta( $guest_id, '_bnb_guest_phone', true ),
'notes' => get_post_meta( $guest_id, '_bnb_guest_notes', true ),
'full_address' => Guest::get_formatted_address( $guest_id ),
);
}
}
// Fallback to booking meta (legacy bookings).
$guest_name = get_post_meta( $booking_id, '_bnb_booking_guest_name', true );
// Try to split name into first/last for legacy data.
$name_parts = explode( ' ', $guest_name, 2 );
$first_name = $name_parts[0] ?? '';
$last_name = $name_parts[1] ?? '';
return array(
'name' => $guest_name,
'first_name' => $first_name,
'last_name' => $last_name,
'email' => get_post_meta( $booking_id, '_bnb_booking_guest_email', true ),
'phone' => get_post_meta( $booking_id, '_bnb_booking_guest_phone', true ),
'notes' => get_post_meta( $booking_id, '_bnb_booking_guest_notes', true ),
'full_address' => '', // Legacy bookings don't have full address.
);
}