Add guest management and GDPR privacy compliance (v0.4.0)
All checks were successful
Create Release Package / build-release (push) Successful in 1m26s
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:
@@ -280,41 +280,97 @@ final class Booking {
|
||||
* @return void
|
||||
*/
|
||||
public static function render_guest_meta_box( \WP_Post $post ): void {
|
||||
$guest_id = get_post_meta( $post->ID, self::META_PREFIX . 'guest_id', true );
|
||||
$guest_name = get_post_meta( $post->ID, self::META_PREFIX . 'guest_name', true );
|
||||
$guest_email = get_post_meta( $post->ID, self::META_PREFIX . 'guest_email', true );
|
||||
$guest_phone = get_post_meta( $post->ID, self::META_PREFIX . 'guest_phone', true );
|
||||
$adults = get_post_meta( $post->ID, self::META_PREFIX . 'adults', true );
|
||||
$children = get_post_meta( $post->ID, self::META_PREFIX . 'children', true );
|
||||
$guest_notes = get_post_meta( $post->ID, self::META_PREFIX . 'guest_notes', true );
|
||||
|
||||
// If guest_id exists, get guest data from Guest CPT.
|
||||
$linked_guest = null;
|
||||
if ( $guest_id ) {
|
||||
$linked_guest = get_post( $guest_id );
|
||||
if ( $linked_guest && Guest::POST_TYPE === $linked_guest->post_type ) {
|
||||
$guest_name = Guest::get_full_name( $guest_id );
|
||||
$guest_email = get_post_meta( $guest_id, '_bnb_guest_email', true );
|
||||
$guest_phone = get_post_meta( $guest_id, '_bnb_guest_phone', true );
|
||||
} else {
|
||||
$linked_guest = null;
|
||||
$guest_id = '';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<input type="hidden" id="bnb_booking_guest_id" name="bnb_booking_guest_id" value="<?php echo esc_attr( $guest_id ); ?>">
|
||||
|
||||
<?php if ( $linked_guest ) : ?>
|
||||
<div id="bnb-linked-guest-info" class="bnb-linked-guest">
|
||||
<p>
|
||||
<span class="dashicons dashicons-admin-users"></span>
|
||||
<strong><?php echo esc_html( $linked_guest->post_title ); ?></strong>
|
||||
<a href="<?php echo esc_url( get_edit_post_link( $guest_id ) ); ?>" target="_blank" class="button button-small">
|
||||
<?php esc_html_e( 'View Guest Profile', 'wp-bnb' ); ?>
|
||||
</a>
|
||||
<button type="button" id="bnb-unlink-guest" class="button button-small button-link-delete">
|
||||
<?php esc_html_e( 'Unlink', 'wp-bnb' ); ?>
|
||||
</button>
|
||||
</p>
|
||||
<?php if ( $guest_email ) : ?>
|
||||
<p><small><?php echo esc_html( $guest_email ); ?></small></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="bnb-guest-search-container" class="bnb-guest-search" <?php echo $linked_guest ? 'style="display:none;"' : ''; ?>>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="bnb_booking_guest_search"><?php esc_html_e( 'Search Guest', 'wp-bnb' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="bnb_booking_guest_search" class="regular-text"
|
||||
placeholder="<?php esc_attr_e( 'Search by email...', 'wp-bnb' ); ?>">
|
||||
<p class="description"><?php esc_html_e( 'Search for existing guest or enter details below.', 'wp-bnb' ); ?></p>
|
||||
<div id="bnb-guest-search-results" class="bnb-guest-search-results" style="display:none;"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="bnb-guest-fields-container" <?php echo $linked_guest ? 'style="display:none;"' : ''; ?>>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="bnb_booking_guest_name"><?php esc_html_e( 'Guest Name', 'wp-bnb' ); ?> <span class="required">*</span></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="bnb_booking_guest_name" name="bnb_booking_guest_name"
|
||||
value="<?php echo esc_attr( $guest_name ); ?>" class="regular-text" <?php echo $linked_guest ? '' : 'required'; ?>>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="bnb_booking_guest_email"><?php esc_html_e( 'Email', 'wp-bnb' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="email" id="bnb_booking_guest_email" name="bnb_booking_guest_email"
|
||||
value="<?php echo esc_attr( $guest_email ); ?>" class="regular-text">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="bnb_booking_guest_phone"><?php esc_html_e( 'Phone', 'wp-bnb' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="tel" id="bnb_booking_guest_phone" name="bnb_booking_guest_phone"
|
||||
value="<?php echo esc_attr( $guest_phone ); ?>" class="regular-text">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="bnb_booking_guest_name"><?php esc_html_e( 'Guest Name', 'wp-bnb' ); ?> <span class="required">*</span></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="bnb_booking_guest_name" name="bnb_booking_guest_name"
|
||||
value="<?php echo esc_attr( $guest_name ); ?>" class="regular-text" required>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="bnb_booking_guest_email"><?php esc_html_e( 'Email', 'wp-bnb' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="email" id="bnb_booking_guest_email" name="bnb_booking_guest_email"
|
||||
value="<?php echo esc_attr( $guest_email ); ?>" class="regular-text">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="bnb_booking_guest_phone"><?php esc_html_e( 'Phone', 'wp-bnb' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="tel" id="bnb_booking_guest_phone" name="bnb_booking_guest_phone"
|
||||
value="<?php echo esc_attr( $guest_phone ); ?>" class="regular-text">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<?php esc_html_e( 'Guests', 'wp-bnb' ); ?>
|
||||
@@ -535,21 +591,48 @@ final class Booking {
|
||||
}
|
||||
}
|
||||
|
||||
// Guest text fields.
|
||||
$guest_fields = array( 'guest_name', 'guest_email', 'guest_phone', 'guest_notes' );
|
||||
foreach ( $guest_fields as $field ) {
|
||||
$key = 'bnb_booking_' . $field;
|
||||
if ( isset( $_POST[ $key ] ) ) {
|
||||
$value = wp_unslash( $_POST[ $key ] );
|
||||
if ( 'guest_email' === $field ) {
|
||||
$value = sanitize_email( $value );
|
||||
} elseif ( 'guest_notes' === $field ) {
|
||||
$value = sanitize_textarea_field( $value );
|
||||
} else {
|
||||
$value = sanitize_text_field( $value );
|
||||
}
|
||||
update_post_meta( $post_id, self::META_PREFIX . $field, $value );
|
||||
// Guest ID (linked guest record).
|
||||
$guest_id = isset( $_POST['bnb_booking_guest_id'] ) ? absint( $_POST['bnb_booking_guest_id'] ) : 0;
|
||||
if ( $guest_id ) {
|
||||
// Verify guest exists.
|
||||
$guest = get_post( $guest_id );
|
||||
if ( $guest && Guest::POST_TYPE === $guest->post_type ) {
|
||||
update_post_meta( $post_id, self::META_PREFIX . 'guest_id', $guest_id );
|
||||
// Sync guest data from Guest CPT for searching/display purposes.
|
||||
update_post_meta( $post_id, self::META_PREFIX . 'guest_name', Guest::get_full_name( $guest_id ) );
|
||||
update_post_meta( $post_id, self::META_PREFIX . 'guest_email', get_post_meta( $guest_id, '_bnb_guest_email', true ) );
|
||||
update_post_meta( $post_id, self::META_PREFIX . 'guest_phone', get_post_meta( $guest_id, '_bnb_guest_phone', true ) );
|
||||
} else {
|
||||
delete_post_meta( $post_id, self::META_PREFIX . 'guest_id' );
|
||||
}
|
||||
} else {
|
||||
delete_post_meta( $post_id, self::META_PREFIX . 'guest_id' );
|
||||
|
||||
// Guest text fields (only save if no guest_id).
|
||||
$guest_fields = array( 'guest_name', 'guest_email', 'guest_phone', 'guest_notes' );
|
||||
foreach ( $guest_fields as $field ) {
|
||||
$key = 'bnb_booking_' . $field;
|
||||
if ( isset( $_POST[ $key ] ) ) {
|
||||
$value = wp_unslash( $_POST[ $key ] );
|
||||
if ( 'guest_email' === $field ) {
|
||||
$value = sanitize_email( $value );
|
||||
} elseif ( 'guest_notes' === $field ) {
|
||||
$value = sanitize_textarea_field( $value );
|
||||
} else {
|
||||
$value = sanitize_text_field( $value );
|
||||
}
|
||||
update_post_meta( $post_id, self::META_PREFIX . $field, $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Guest notes are always saved (per-booking notes).
|
||||
if ( isset( $_POST['bnb_booking_guest_notes'] ) ) {
|
||||
update_post_meta(
|
||||
$post_id,
|
||||
self::META_PREFIX . 'guest_notes',
|
||||
sanitize_textarea_field( wp_unslash( $_POST['bnb_booking_guest_notes'] ) )
|
||||
);
|
||||
}
|
||||
|
||||
// Guest counts.
|
||||
@@ -664,10 +747,21 @@ final class Booking {
|
||||
break;
|
||||
|
||||
case 'guest':
|
||||
$guest_id = get_post_meta( $post_id, self::META_PREFIX . 'guest_id', true );
|
||||
$guest_name = get_post_meta( $post_id, self::META_PREFIX . 'guest_name', true );
|
||||
$guest_email = get_post_meta( $post_id, self::META_PREFIX . 'guest_email', true );
|
||||
if ( $guest_name ) {
|
||||
echo esc_html( $guest_name );
|
||||
if ( $guest_id ) {
|
||||
// Linked guest - show link to guest profile.
|
||||
printf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url( get_edit_post_link( $guest_id ) ),
|
||||
esc_html( $guest_name )
|
||||
);
|
||||
echo ' <span class="dashicons dashicons-admin-users" style="font-size: 14px; vertical-align: middle;" title="' . esc_attr__( 'Linked Guest', 'wp-bnb' ) . '"></span>';
|
||||
} else {
|
||||
echo esc_html( $guest_name );
|
||||
}
|
||||
if ( $guest_email ) {
|
||||
echo '<br><small><a href="mailto:' . esc_attr( $guest_email ) . '">' . esc_html( $guest_email ) . '</a></small>';
|
||||
}
|
||||
@@ -1071,6 +1165,47 @@ final class Booking {
|
||||
return Room::get_building( $room->ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get guest for a booking.
|
||||
*
|
||||
* Returns the linked Guest post if guest_id exists, or a stdClass object
|
||||
* with guest data from booking meta for backward compatibility.
|
||||
*
|
||||
* @param int $booking_id Booking post ID.
|
||||
* @return \WP_Post|\stdClass|null Guest post, virtual guest object, or null.
|
||||
*/
|
||||
public static function get_guest( int $booking_id ) {
|
||||
$guest_id = get_post_meta( $booking_id, self::META_PREFIX . 'guest_id', true );
|
||||
|
||||
// If linked to Guest CPT, return the guest post.
|
||||
if ( $guest_id ) {
|
||||
$guest = get_post( $guest_id );
|
||||
if ( $guest && Guest::POST_TYPE === $guest->post_type ) {
|
||||
return $guest;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, create a virtual guest object from booking meta.
|
||||
$guest_name = get_post_meta( $booking_id, self::META_PREFIX . 'guest_name', true );
|
||||
$guest_email = get_post_meta( $booking_id, self::META_PREFIX . 'guest_email', true );
|
||||
$guest_phone = get_post_meta( $booking_id, self::META_PREFIX . 'guest_phone', true );
|
||||
|
||||
if ( ! $guest_name && ! $guest_email ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return virtual guest object for backward compatibility.
|
||||
$virtual_guest = new \stdClass();
|
||||
$virtual_guest->ID = 0;
|
||||
$virtual_guest->post_type = 'virtual_guest';
|
||||
$virtual_guest->post_title = $guest_name ?: '';
|
||||
$virtual_guest->name = $guest_name ?: '';
|
||||
$virtual_guest->email = $guest_email ?: '';
|
||||
$virtual_guest->phone = $guest_phone ?: '';
|
||||
|
||||
return $virtual_guest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bookings for a room.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user