All checks were successful
Create Release Package / build-release (push) Successful in 1m1s
New Features: - Auto-update system with configurable check frequency - Updates tab in settings with manual check button - Localhost development mode bypasses license validation - Extended general settings (address, contact, social media) - Pricing settings split into subtabs - Guest ID/passport encryption using AES-256-CBC - Guest auto-creation from booking form Bug Fixes: - Fixed Booking admin issues with auto-draft status - Fixed guest dropdown loading in booking form - Fixed booking history display on Guest edit page - Fixed service pricing meta box (Gutenberg hiding meta boxes) Changes: - Admin submenu reordered for better organization - Booking title shows guest name and dates (room removed) - Service, Guest, Booking use classic editor (not Gutenberg) - Settings tabs flush with content (no gap) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1193 lines
36 KiB
PHP
1193 lines
36 KiB
PHP
<?php
|
|
/**
|
|
* Guest post type.
|
|
*
|
|
* Custom post type for BnB guests.
|
|
*
|
|
* @package Magdev\WpBnb\PostTypes
|
|
*/
|
|
|
|
declare( strict_types=1 );
|
|
|
|
namespace Magdev\WpBnb\PostTypes;
|
|
|
|
/**
|
|
* Guest post type class.
|
|
*/
|
|
final class Guest {
|
|
|
|
/**
|
|
* Post type slug.
|
|
*
|
|
* @var string
|
|
*/
|
|
public const POST_TYPE = 'bnb_guest';
|
|
|
|
/**
|
|
* Meta key prefix.
|
|
*
|
|
* @var string
|
|
*/
|
|
private const META_PREFIX = '_bnb_guest_';
|
|
|
|
/**
|
|
* Encryption method.
|
|
*
|
|
* @var string
|
|
*/
|
|
private const ENCRYPTION_METHOD = 'aes-256-cbc';
|
|
|
|
/**
|
|
* Encrypt sensitive data.
|
|
*
|
|
* Uses WordPress AUTH_KEY for encryption key derivation.
|
|
*
|
|
* @param string $data Plain text data to encrypt.
|
|
* @return string Encrypted data (base64 encoded).
|
|
*/
|
|
private static function encrypt( string $data ): string {
|
|
if ( empty( $data ) ) {
|
|
return '';
|
|
}
|
|
|
|
$key = hash( 'sha256', AUTH_KEY . 'wp_bnb_guest_encryption', true );
|
|
$iv = openssl_random_pseudo_bytes( openssl_cipher_iv_length( self::ENCRYPTION_METHOD ) );
|
|
|
|
$encrypted = openssl_encrypt( $data, self::ENCRYPTION_METHOD, $key, 0, $iv );
|
|
|
|
if ( false === $encrypted ) {
|
|
return '';
|
|
}
|
|
|
|
// Store IV with encrypted data (IV is not secret, just needs to be unique).
|
|
return base64_encode( $iv . '::' . $encrypted );
|
|
}
|
|
|
|
/**
|
|
* Decrypt sensitive data.
|
|
*
|
|
* @param string $data Encrypted data (base64 encoded).
|
|
* @return string Decrypted plain text.
|
|
*/
|
|
private static function decrypt( string $data ): string {
|
|
if ( empty( $data ) ) {
|
|
return '';
|
|
}
|
|
|
|
$decoded = base64_decode( $data, true );
|
|
if ( false === $decoded ) {
|
|
// Data might be stored unencrypted (legacy), return as-is.
|
|
return $data;
|
|
}
|
|
|
|
$parts = explode( '::', $decoded, 2 );
|
|
if ( count( $parts ) !== 2 ) {
|
|
// Data might be stored unencrypted (legacy), return as-is.
|
|
return $data;
|
|
}
|
|
|
|
list( $iv, $encrypted ) = $parts;
|
|
$key = hash( 'sha256', AUTH_KEY . 'wp_bnb_guest_encryption', true );
|
|
|
|
$decrypted = openssl_decrypt( $encrypted, self::ENCRYPTION_METHOD, $key, 0, $iv );
|
|
|
|
if ( false === $decrypted ) {
|
|
// Decryption failed, might be legacy unencrypted data.
|
|
return $data;
|
|
}
|
|
|
|
return $decrypted;
|
|
}
|
|
|
|
/**
|
|
* Initialize the post type.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function init(): void {
|
|
add_action( 'init', array( self::class, 'register' ) );
|
|
add_action( 'add_meta_boxes', array( self::class, 'add_meta_boxes' ) );
|
|
add_action( 'save_post_' . self::POST_TYPE, array( self::class, 'save_meta' ), 10, 2 );
|
|
add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( self::class, 'add_columns' ) );
|
|
add_action( 'manage_' . self::POST_TYPE . '_posts_custom_column', array( self::class, 'render_column' ), 10, 2 );
|
|
add_filter( 'manage_edit-' . self::POST_TYPE . '_sortable_columns', array( self::class, 'sortable_columns' ) );
|
|
add_action( 'restrict_manage_posts', array( self::class, 'add_filters' ) );
|
|
add_action( 'pre_get_posts', array( self::class, 'filter_query' ) );
|
|
add_filter( 'enter_title_here', array( self::class, 'change_title_placeholder' ), 10, 2 );
|
|
|
|
// Disable Gutenberg block editor for Guests - use classic editor for simpler UI.
|
|
add_filter( 'use_block_editor_for_post_type', array( self::class, 'disable_block_editor' ), 10, 2 );
|
|
}
|
|
|
|
/**
|
|
* Disable block editor for Guests post type.
|
|
*
|
|
* @param bool $use_block_editor Whether to use block editor.
|
|
* @param string $post_type Post type.
|
|
* @return bool
|
|
*/
|
|
public static function disable_block_editor( bool $use_block_editor, string $post_type ): bool {
|
|
if ( self::POST_TYPE === $post_type ) {
|
|
return false;
|
|
}
|
|
return $use_block_editor;
|
|
}
|
|
|
|
/**
|
|
* Register the post type.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function register(): void {
|
|
$labels = array(
|
|
'name' => _x( 'Guests', 'post type general name', 'wp-bnb' ),
|
|
'singular_name' => _x( 'Guest', 'post type singular name', 'wp-bnb' ),
|
|
'menu_name' => _x( 'Guests', 'admin menu', 'wp-bnb' ),
|
|
'name_admin_bar' => _x( 'Guest', 'add new on admin bar', 'wp-bnb' ),
|
|
'add_new' => _x( 'Add New', 'guest', 'wp-bnb' ),
|
|
'add_new_item' => __( 'Add New Guest', 'wp-bnb' ),
|
|
'new_item' => __( 'New Guest', 'wp-bnb' ),
|
|
'edit_item' => __( 'Edit Guest', 'wp-bnb' ),
|
|
'view_item' => __( 'View Guest', 'wp-bnb' ),
|
|
'all_items' => __( 'Guests', 'wp-bnb' ),
|
|
'search_items' => __( 'Search Guests', 'wp-bnb' ),
|
|
'parent_item_colon' => __( 'Parent Guests:', 'wp-bnb' ),
|
|
'not_found' => __( 'No guests found.', 'wp-bnb' ),
|
|
'not_found_in_trash' => __( 'No guests found in Trash.', 'wp-bnb' ),
|
|
'archives' => __( 'Guest archives', 'wp-bnb' ),
|
|
'insert_into_item' => __( 'Insert into guest', 'wp-bnb' ),
|
|
'uploaded_to_this_item' => __( 'Uploaded to this guest', 'wp-bnb' ),
|
|
'filter_items_list' => __( 'Filter guests list', 'wp-bnb' ),
|
|
'items_list_navigation' => __( 'Guests list navigation', 'wp-bnb' ),
|
|
'items_list' => __( 'Guests list', 'wp-bnb' ),
|
|
);
|
|
|
|
$args = array(
|
|
'labels' => $labels,
|
|
'public' => false,
|
|
'publicly_queryable' => false,
|
|
'show_ui' => true,
|
|
'show_in_menu' => 'wp-bnb',
|
|
'query_var' => false,
|
|
'rewrite' => false,
|
|
'capability_type' => 'post',
|
|
'has_archive' => false,
|
|
'hierarchical' => false,
|
|
'menu_position' => null,
|
|
'menu_icon' => 'dashicons-groups',
|
|
'supports' => array(
|
|
'title',
|
|
'revisions',
|
|
),
|
|
'show_in_rest' => true,
|
|
'rest_base' => 'guests',
|
|
'rest_controller_class' => 'WP_REST_Posts_Controller',
|
|
);
|
|
|
|
register_post_type( self::POST_TYPE, $args );
|
|
}
|
|
|
|
/**
|
|
* Add meta boxes.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function add_meta_boxes(): void {
|
|
add_meta_box(
|
|
'bnb_guest_personal',
|
|
__( 'Personal Information', 'wp-bnb' ),
|
|
array( self::class, 'render_personal_meta_box' ),
|
|
self::POST_TYPE,
|
|
'normal',
|
|
'high'
|
|
);
|
|
|
|
add_meta_box(
|
|
'bnb_guest_address',
|
|
__( 'Address', 'wp-bnb' ),
|
|
array( self::class, 'render_address_meta_box' ),
|
|
self::POST_TYPE,
|
|
'normal',
|
|
'high'
|
|
);
|
|
|
|
add_meta_box(
|
|
'bnb_guest_identification',
|
|
__( 'Identification', 'wp-bnb' ),
|
|
array( self::class, 'render_identification_meta_box' ),
|
|
self::POST_TYPE,
|
|
'normal',
|
|
'default'
|
|
);
|
|
|
|
add_meta_box(
|
|
'bnb_guest_consent',
|
|
__( 'Consent & Privacy', 'wp-bnb' ),
|
|
array( self::class, 'render_consent_meta_box' ),
|
|
self::POST_TYPE,
|
|
'side',
|
|
'high'
|
|
);
|
|
|
|
add_meta_box(
|
|
'bnb_guest_bookings',
|
|
__( 'Booking History', 'wp-bnb' ),
|
|
array( self::class, 'render_bookings_meta_box' ),
|
|
self::POST_TYPE,
|
|
'normal',
|
|
'default'
|
|
);
|
|
|
|
add_meta_box(
|
|
'bnb_guest_status',
|
|
__( 'Status & Notes', 'wp-bnb' ),
|
|
array( self::class, 'render_status_meta_box' ),
|
|
self::POST_TYPE,
|
|
'side',
|
|
'default'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Render personal information meta box.
|
|
*
|
|
* @param \WP_Post $post Current post object.
|
|
* @return void
|
|
*/
|
|
public static function render_personal_meta_box( \WP_Post $post ): void {
|
|
wp_nonce_field( 'bnb_guest_meta', 'bnb_guest_meta_nonce' );
|
|
|
|
$first_name = get_post_meta( $post->ID, self::META_PREFIX . 'first_name', true );
|
|
$last_name = get_post_meta( $post->ID, self::META_PREFIX . 'last_name', true );
|
|
$email = get_post_meta( $post->ID, self::META_PREFIX . 'email', true );
|
|
$phone = get_post_meta( $post->ID, self::META_PREFIX . 'phone', true );
|
|
$date_of_birth = get_post_meta( $post->ID, self::META_PREFIX . 'date_of_birth', true );
|
|
$nationality = get_post_meta( $post->ID, self::META_PREFIX . 'nationality', true );
|
|
?>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_first_name"><?php esc_html_e( 'First Name', 'wp-bnb' ); ?> <span class="required">*</span></label>
|
|
</th>
|
|
<td>
|
|
<input type="text" id="bnb_guest_first_name" name="bnb_guest_first_name"
|
|
value="<?php echo esc_attr( $first_name ); ?>" class="regular-text" required>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_last_name"><?php esc_html_e( 'Last Name', 'wp-bnb' ); ?> <span class="required">*</span></label>
|
|
</th>
|
|
<td>
|
|
<input type="text" id="bnb_guest_last_name" name="bnb_guest_last_name"
|
|
value="<?php echo esc_attr( $last_name ); ?>" class="regular-text" required>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_email"><?php esc_html_e( 'Email', 'wp-bnb' ); ?> <span class="required">*</span></label>
|
|
</th>
|
|
<td>
|
|
<input type="email" id="bnb_guest_email" name="bnb_guest_email"
|
|
value="<?php echo esc_attr( $email ); ?>" class="regular-text" required>
|
|
<p class="description"><?php esc_html_e( 'Used as unique identifier for the guest.', 'wp-bnb' ); ?></p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_phone"><?php esc_html_e( 'Phone', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<input type="tel" id="bnb_guest_phone" name="bnb_guest_phone"
|
|
value="<?php echo esc_attr( $phone ); ?>" class="regular-text">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_date_of_birth"><?php esc_html_e( 'Date of Birth', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<input type="date" id="bnb_guest_date_of_birth" name="bnb_guest_date_of_birth"
|
|
value="<?php echo esc_attr( $date_of_birth ); ?>">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_nationality"><?php esc_html_e( 'Nationality', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<select id="bnb_guest_nationality" name="bnb_guest_nationality">
|
|
<option value=""><?php esc_html_e( '— Select Nationality —', 'wp-bnb' ); ?></option>
|
|
<?php foreach ( Building::get_countries() as $code => $name ) : ?>
|
|
<option value="<?php echo esc_attr( $code ); ?>" <?php selected( $nationality, $code ); ?>>
|
|
<?php echo esc_html( $name ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render address meta box.
|
|
*
|
|
* @param \WP_Post $post Current post object.
|
|
* @return void
|
|
*/
|
|
public static function render_address_meta_box( \WP_Post $post ): void {
|
|
$street = get_post_meta( $post->ID, self::META_PREFIX . 'street', true );
|
|
$city = get_post_meta( $post->ID, self::META_PREFIX . 'city', true );
|
|
$postal_code = get_post_meta( $post->ID, self::META_PREFIX . 'postal_code', true );
|
|
$country = get_post_meta( $post->ID, self::META_PREFIX . 'country', true );
|
|
?>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_street"><?php esc_html_e( 'Street Address', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<input type="text" id="bnb_guest_street" name="bnb_guest_street"
|
|
value="<?php echo esc_attr( $street ); ?>" class="large-text">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_city"><?php esc_html_e( 'City', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<input type="text" id="bnb_guest_city" name="bnb_guest_city"
|
|
value="<?php echo esc_attr( $city ); ?>" class="regular-text">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_postal_code"><?php esc_html_e( 'ZIP / Postal Code', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<input type="text" id="bnb_guest_postal_code" name="bnb_guest_postal_code"
|
|
value="<?php echo esc_attr( $postal_code ); ?>" class="regular-text">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_country"><?php esc_html_e( 'Country', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<select id="bnb_guest_country" name="bnb_guest_country">
|
|
<option value=""><?php esc_html_e( '— Select Country —', 'wp-bnb' ); ?></option>
|
|
<?php foreach ( Building::get_countries() as $code => $name ) : ?>
|
|
<option value="<?php echo esc_attr( $code ); ?>" <?php selected( $country, $code ); ?>>
|
|
<?php echo esc_html( $name ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render identification meta box.
|
|
*
|
|
* @param \WP_Post $post Current post object.
|
|
* @return void
|
|
*/
|
|
public static function render_identification_meta_box( \WP_Post $post ): void {
|
|
$id_type = get_post_meta( $post->ID, self::META_PREFIX . 'id_type', true );
|
|
$id_number = self::decrypt( get_post_meta( $post->ID, self::META_PREFIX . 'id_number', true ) );
|
|
$id_expiry = get_post_meta( $post->ID, self::META_PREFIX . 'id_expiry', true );
|
|
?>
|
|
<p class="description" style="margin-bottom: 15px;">
|
|
<span class="dashicons dashicons-shield" style="color: #00a32a;"></span>
|
|
<?php esc_html_e( 'This information is encrypted and stored securely.', 'wp-bnb' ); ?>
|
|
</p>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_id_type"><?php esc_html_e( 'ID Type', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<select id="bnb_guest_id_type" name="bnb_guest_id_type">
|
|
<option value=""><?php esc_html_e( '— Select ID Type —', 'wp-bnb' ); ?></option>
|
|
<?php foreach ( self::get_id_types() as $type => $label ) : ?>
|
|
<option value="<?php echo esc_attr( $type ); ?>" <?php selected( $id_type, $type ); ?>>
|
|
<?php echo esc_html( $label ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_id_number"><?php esc_html_e( 'ID Number', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<input type="text" id="bnb_guest_id_number" name="bnb_guest_id_number"
|
|
value="<?php echo esc_attr( $id_number ); ?>" class="regular-text">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row">
|
|
<label for="bnb_guest_id_expiry"><?php esc_html_e( 'Expiry Date', 'wp-bnb' ); ?></label>
|
|
</th>
|
|
<td>
|
|
<input type="date" id="bnb_guest_id_expiry" name="bnb_guest_id_expiry"
|
|
value="<?php echo esc_attr( $id_expiry ); ?>">
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render consent & privacy meta box.
|
|
*
|
|
* @param \WP_Post $post Current post object.
|
|
* @return void
|
|
*/
|
|
public static function render_consent_meta_box( \WP_Post $post ): void {
|
|
$consent_marketing = get_post_meta( $post->ID, self::META_PREFIX . 'consent_marketing', true );
|
|
$consent_data = get_post_meta( $post->ID, self::META_PREFIX . 'consent_data', true );
|
|
$consent_date = get_post_meta( $post->ID, self::META_PREFIX . 'consent_date', true );
|
|
?>
|
|
<p>
|
|
<label>
|
|
<input type="checkbox" name="bnb_guest_consent_data" value="1"
|
|
<?php checked( $consent_data, '1' ); ?>>
|
|
<?php esc_html_e( 'Data Processing Consent', 'wp-bnb' ); ?>
|
|
</label>
|
|
</p>
|
|
<p class="description" style="margin-left: 24px;">
|
|
<?php esc_html_e( 'Guest consents to processing personal data for booking purposes.', 'wp-bnb' ); ?>
|
|
</p>
|
|
|
|
<p style="margin-top: 15px;">
|
|
<label>
|
|
<input type="checkbox" name="bnb_guest_consent_marketing" value="1"
|
|
<?php checked( $consent_marketing, '1' ); ?>>
|
|
<?php esc_html_e( 'Marketing Consent', 'wp-bnb' ); ?>
|
|
</label>
|
|
</p>
|
|
<p class="description" style="margin-left: 24px;">
|
|
<?php esc_html_e( 'Guest consents to receive marketing communications.', 'wp-bnb' ); ?>
|
|
</p>
|
|
|
|
<?php if ( $consent_date ) : ?>
|
|
<p style="margin-top: 15px; padding-top: 10px; border-top: 1px solid #ddd;">
|
|
<strong><?php esc_html_e( 'Consent recorded:', 'wp-bnb' ); ?></strong><br>
|
|
<?php echo esc_html( wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $consent_date ) ) ); ?>
|
|
</p>
|
|
<?php endif; ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render booking history meta box.
|
|
*
|
|
* @param \WP_Post $post Current post object.
|
|
* @return void
|
|
*/
|
|
public static function render_bookings_meta_box( \WP_Post $post ): void {
|
|
$bookings = self::get_bookings( $post->ID );
|
|
$total_spent = self::get_total_spent( $post->ID );
|
|
$booking_count = count( $bookings );
|
|
|
|
if ( 0 === $booking_count ) {
|
|
echo '<p>' . esc_html__( 'No bookings found for this guest.', 'wp-bnb' ) . '</p>';
|
|
return;
|
|
}
|
|
?>
|
|
<div class="bnb-guest-stats" style="display: flex; gap: 20px; margin-bottom: 15px; padding: 10px; background: #f9f9f9; border-radius: 4px;">
|
|
<div>
|
|
<strong><?php esc_html_e( 'Total Bookings:', 'wp-bnb' ); ?></strong>
|
|
<?php echo esc_html( $booking_count ); ?>
|
|
</div>
|
|
<div>
|
|
<strong><?php esc_html_e( 'Total Spent:', 'wp-bnb' ); ?></strong>
|
|
<?php echo esc_html( number_format( $total_spent, 2 ) ); ?> <?php echo esc_html( get_option( 'wp_bnb_currency', 'CHF' ) ); ?>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="widefat striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e( 'Reference', 'wp-bnb' ); ?></th>
|
|
<th><?php esc_html_e( 'Room', 'wp-bnb' ); ?></th>
|
|
<th><?php esc_html_e( 'Dates', 'wp-bnb' ); ?></th>
|
|
<th><?php esc_html_e( 'Price', 'wp-bnb' ); ?></th>
|
|
<th><?php esc_html_e( 'Status', 'wp-bnb' ); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ( array_slice( $bookings, 0, 10 ) as $booking ) : ?>
|
|
<?php
|
|
$reference = get_post_meta( $booking->ID, '_bnb_booking_reference', true );
|
|
$room_id = get_post_meta( $booking->ID, '_bnb_booking_room_id', true );
|
|
$room = get_post( $room_id );
|
|
$check_in = get_post_meta( $booking->ID, '_bnb_booking_check_in', true );
|
|
$check_out = get_post_meta( $booking->ID, '_bnb_booking_check_out', true );
|
|
$price = get_post_meta( $booking->ID, '_bnb_booking_calculated_price', true );
|
|
$status = get_post_meta( $booking->ID, '_bnb_booking_status', true );
|
|
$statuses = Booking::get_booking_statuses();
|
|
$colors = Booking::get_status_colors();
|
|
?>
|
|
<tr>
|
|
<td>
|
|
<a href="<?php echo esc_url( get_edit_post_link( $booking->ID ) ); ?>">
|
|
<?php echo esc_html( $reference ?: '#' . $booking->ID ); ?>
|
|
</a>
|
|
</td>
|
|
<td><?php echo esc_html( $room ? $room->post_title : '—' ); ?></td>
|
|
<td>
|
|
<?php
|
|
if ( $check_in && $check_out ) {
|
|
echo esc_html( wp_date( 'M j', strtotime( $check_in ) ) );
|
|
echo ' - ';
|
|
echo esc_html( wp_date( 'M j, Y', strtotime( $check_out ) ) );
|
|
} else {
|
|
echo '—';
|
|
}
|
|
?>
|
|
</td>
|
|
<td>
|
|
<?php
|
|
if ( $price ) {
|
|
echo esc_html( number_format( (float) $price, 2 ) );
|
|
} else {
|
|
echo '—';
|
|
}
|
|
?>
|
|
</td>
|
|
<td>
|
|
<span class="bnb-status-badge" style="background-color: <?php echo esc_attr( $colors[ $status ] ?? '#999' ); ?>; color: #fff; padding: 2px 8px; border-radius: 3px; font-size: 11px;">
|
|
<?php echo esc_html( $statuses[ $status ] ?? $status ); ?>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
|
|
<?php if ( $booking_count > 10 ) : ?>
|
|
<p style="margin-top: 10px;">
|
|
<a href="<?php echo esc_url( admin_url( 'edit.php?post_type=' . Booking::POST_TYPE . '&guest_id=' . $post->ID ) ); ?>">
|
|
<?php
|
|
printf(
|
|
/* translators: %d: Number of additional bookings */
|
|
esc_html__( 'View all %d bookings', 'wp-bnb' ),
|
|
$booking_count
|
|
);
|
|
?>
|
|
</a>
|
|
</p>
|
|
<?php endif; ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render status & notes meta box.
|
|
*
|
|
* @param \WP_Post $post Current post object.
|
|
* @return void
|
|
*/
|
|
public static function render_status_meta_box( \WP_Post $post ): void {
|
|
$status = get_post_meta( $post->ID, self::META_PREFIX . 'status', true ) ?: 'active';
|
|
$notes = get_post_meta( $post->ID, self::META_PREFIX . 'notes', true );
|
|
$preferences = get_post_meta( $post->ID, self::META_PREFIX . 'preferences', true );
|
|
?>
|
|
<p>
|
|
<label for="bnb_guest_status"><strong><?php esc_html_e( 'Status', 'wp-bnb' ); ?></strong></label>
|
|
<select id="bnb_guest_status" name="bnb_guest_status" class="widefat" style="margin-top: 5px;">
|
|
<?php foreach ( self::get_statuses() as $key => $label ) : ?>
|
|
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $status, $key ); ?>>
|
|
<?php echo esc_html( $label ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</p>
|
|
|
|
<p style="margin-top: 15px;">
|
|
<label for="bnb_guest_preferences"><strong><?php esc_html_e( 'Guest Preferences', 'wp-bnb' ); ?></strong></label>
|
|
<textarea id="bnb_guest_preferences" name="bnb_guest_preferences" class="widefat" rows="3"
|
|
style="margin-top: 5px;"><?php echo esc_textarea( $preferences ); ?></textarea>
|
|
<span class="description"><?php esc_html_e( 'Room preferences, dietary requirements, etc.', 'wp-bnb' ); ?></span>
|
|
</p>
|
|
|
|
<p style="margin-top: 15px;">
|
|
<label for="bnb_guest_notes"><strong><?php esc_html_e( 'Internal Notes', 'wp-bnb' ); ?></strong></label>
|
|
<textarea id="bnb_guest_notes" name="bnb_guest_notes" class="widefat" rows="3"
|
|
style="margin-top: 5px;"><?php echo esc_textarea( $notes ); ?></textarea>
|
|
<span class="description"><?php esc_html_e( 'Private notes (not shared with guest).', 'wp-bnb' ); ?></span>
|
|
</p>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Save post meta.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param \WP_Post $post Post object.
|
|
* @return void
|
|
*/
|
|
public static function save_meta( int $post_id, \WP_Post $post ): void {
|
|
// Verify nonce.
|
|
if ( ! isset( $_POST['bnb_guest_meta_nonce'] ) ||
|
|
! wp_verify_nonce( sanitize_key( $_POST['bnb_guest_meta_nonce'] ), 'bnb_guest_meta' ) ) {
|
|
return;
|
|
}
|
|
|
|
// Check autosave.
|
|
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
|
return;
|
|
}
|
|
|
|
// Check permissions.
|
|
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
|
return;
|
|
}
|
|
|
|
// Text fields (non-sensitive).
|
|
$text_fields = array(
|
|
'first_name',
|
|
'last_name',
|
|
'phone',
|
|
'street',
|
|
'city',
|
|
'postal_code',
|
|
'country',
|
|
'nationality',
|
|
'id_type',
|
|
'status',
|
|
);
|
|
|
|
foreach ( $text_fields as $field ) {
|
|
$key = 'bnb_guest_' . $field;
|
|
if ( isset( $_POST[ $key ] ) ) {
|
|
update_post_meta(
|
|
$post_id,
|
|
self::META_PREFIX . $field,
|
|
sanitize_text_field( wp_unslash( $_POST[ $key ] ) )
|
|
);
|
|
}
|
|
}
|
|
|
|
// Sensitive field: ID number (encrypted).
|
|
if ( isset( $_POST['bnb_guest_id_number'] ) ) {
|
|
$id_number = sanitize_text_field( wp_unslash( $_POST['bnb_guest_id_number'] ) );
|
|
update_post_meta(
|
|
$post_id,
|
|
self::META_PREFIX . 'id_number',
|
|
self::encrypt( $id_number )
|
|
);
|
|
}
|
|
|
|
// Email field (special sanitization).
|
|
if ( isset( $_POST['bnb_guest_email'] ) ) {
|
|
update_post_meta(
|
|
$post_id,
|
|
self::META_PREFIX . 'email',
|
|
sanitize_email( wp_unslash( $_POST['bnb_guest_email'] ) )
|
|
);
|
|
}
|
|
|
|
// Date fields.
|
|
$date_fields = array( 'date_of_birth', 'id_expiry' );
|
|
foreach ( $date_fields as $field ) {
|
|
$key = 'bnb_guest_' . $field;
|
|
if ( isset( $_POST[ $key ] ) ) {
|
|
$date = sanitize_text_field( wp_unslash( $_POST[ $key ] ) );
|
|
if ( empty( $date ) || preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) ) {
|
|
update_post_meta( $post_id, self::META_PREFIX . $field, $date );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Textarea fields.
|
|
$textarea_fields = array( 'notes', 'preferences' );
|
|
foreach ( $textarea_fields as $field ) {
|
|
$key = 'bnb_guest_' . $field;
|
|
if ( isset( $_POST[ $key ] ) ) {
|
|
update_post_meta(
|
|
$post_id,
|
|
self::META_PREFIX . $field,
|
|
sanitize_textarea_field( wp_unslash( $_POST[ $key ] ) )
|
|
);
|
|
}
|
|
}
|
|
|
|
// Consent checkboxes.
|
|
$consent_fields = array( 'consent_marketing', 'consent_data' );
|
|
$consent_changed = false;
|
|
|
|
foreach ( $consent_fields as $field ) {
|
|
$key = 'bnb_guest_' . $field;
|
|
$old_value = get_post_meta( $post_id, self::META_PREFIX . $field, true );
|
|
$new_value = isset( $_POST[ $key ] ) ? '1' : '';
|
|
|
|
if ( $old_value !== $new_value ) {
|
|
$consent_changed = true;
|
|
}
|
|
|
|
update_post_meta( $post_id, self::META_PREFIX . $field, $new_value );
|
|
}
|
|
|
|
// Update consent date if consent changed.
|
|
if ( $consent_changed ) {
|
|
update_post_meta( $post_id, self::META_PREFIX . 'consent_date', current_time( 'mysql' ) );
|
|
}
|
|
|
|
// Auto-update post title from name fields.
|
|
$first_name = get_post_meta( $post_id, self::META_PREFIX . 'first_name', true );
|
|
$last_name = get_post_meta( $post_id, self::META_PREFIX . 'last_name', true );
|
|
|
|
if ( $first_name || $last_name ) {
|
|
$full_name = trim( $first_name . ' ' . $last_name );
|
|
if ( $post->post_title !== $full_name ) {
|
|
remove_action( 'save_post_' . self::POST_TYPE, array( self::class, 'save_meta' ), 10 );
|
|
wp_update_post(
|
|
array(
|
|
'ID' => $post_id,
|
|
'post_title' => $full_name,
|
|
)
|
|
);
|
|
add_action( 'save_post_' . self::POST_TYPE, array( self::class, 'save_meta' ), 10, 2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add custom columns to the post list.
|
|
*
|
|
* @param array $columns Existing columns.
|
|
* @return array
|
|
*/
|
|
public static function add_columns( array $columns ): array {
|
|
$new_columns = array();
|
|
foreach ( $columns as $key => $value ) {
|
|
$new_columns[ $key ] = $value;
|
|
if ( 'title' === $key ) {
|
|
$new_columns['email'] = __( 'Email', 'wp-bnb' );
|
|
$new_columns['phone'] = __( 'Phone', 'wp-bnb' );
|
|
$new_columns['country'] = __( 'Country', 'wp-bnb' );
|
|
$new_columns['bookings'] = __( 'Bookings', 'wp-bnb' );
|
|
$new_columns['status'] = __( 'Status', 'wp-bnb' );
|
|
}
|
|
}
|
|
// Remove default date column.
|
|
unset( $new_columns['date'] );
|
|
return $new_columns;
|
|
}
|
|
|
|
/**
|
|
* Render custom column content.
|
|
*
|
|
* @param string $column Column name.
|
|
* @param int $post_id Post ID.
|
|
* @return void
|
|
*/
|
|
public static function render_column( string $column, int $post_id ): void {
|
|
switch ( $column ) {
|
|
case 'email':
|
|
$email = get_post_meta( $post_id, self::META_PREFIX . 'email', true );
|
|
if ( $email ) {
|
|
printf(
|
|
'<a href="mailto:%s">%s</a>',
|
|
esc_attr( $email ),
|
|
esc_html( $email )
|
|
);
|
|
} else {
|
|
echo '—';
|
|
}
|
|
break;
|
|
|
|
case 'phone':
|
|
$phone = get_post_meta( $post_id, self::META_PREFIX . 'phone', true );
|
|
echo esc_html( $phone ?: '—' );
|
|
break;
|
|
|
|
case 'country':
|
|
$country = get_post_meta( $post_id, self::META_PREFIX . 'country', true );
|
|
if ( $country ) {
|
|
$countries = Building::get_countries();
|
|
echo esc_html( $countries[ $country ] ?? $country );
|
|
} else {
|
|
echo '—';
|
|
}
|
|
break;
|
|
|
|
case 'bookings':
|
|
$count = self::get_booking_count( $post_id );
|
|
if ( $count > 0 ) {
|
|
printf(
|
|
'<a href="%s">%s</a>',
|
|
esc_url( admin_url( 'edit.php?post_type=' . Booking::POST_TYPE . '&guest_id=' . $post_id ) ),
|
|
esc_html(
|
|
sprintf(
|
|
/* translators: %d: Number of bookings */
|
|
_n( '%d booking', '%d bookings', $count, 'wp-bnb' ),
|
|
$count
|
|
)
|
|
)
|
|
);
|
|
} else {
|
|
echo '—';
|
|
}
|
|
break;
|
|
|
|
case 'status':
|
|
$status = get_post_meta( $post_id, self::META_PREFIX . 'status', true ) ?: 'active';
|
|
$colors = self::get_status_colors();
|
|
$statuses = self::get_statuses();
|
|
printf(
|
|
'<span class="bnb-status-badge" style="background-color: %s; color: #fff; padding: 2px 8px; border-radius: 3px; font-size: 11px;">%s</span>',
|
|
esc_attr( $colors[ $status ] ?? '#999' ),
|
|
esc_html( $statuses[ $status ] ?? $status )
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add sortable columns.
|
|
*
|
|
* @param array $columns Existing sortable columns.
|
|
* @return array
|
|
*/
|
|
public static function sortable_columns( array $columns ): array {
|
|
$columns['email'] = 'email';
|
|
$columns['country'] = 'country';
|
|
$columns['status'] = 'status';
|
|
return $columns;
|
|
}
|
|
|
|
/**
|
|
* Add filter dropdowns to admin list.
|
|
*
|
|
* @param string $post_type Current post type.
|
|
* @return void
|
|
*/
|
|
public static function add_filters( string $post_type ): void {
|
|
if ( self::POST_TYPE !== $post_type ) {
|
|
return;
|
|
}
|
|
|
|
// Status filter.
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter display only.
|
|
$selected_status = isset( $_GET['guest_status'] ) ? sanitize_text_field( wp_unslash( $_GET['guest_status'] ) ) : '';
|
|
?>
|
|
<select name="guest_status">
|
|
<option value=""><?php esc_html_e( 'All Statuses', 'wp-bnb' ); ?></option>
|
|
<?php foreach ( self::get_statuses() as $key => $label ) : ?>
|
|
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $selected_status, $key ); ?>>
|
|
<?php echo esc_html( $label ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php
|
|
|
|
// Country filter.
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter display only.
|
|
$selected_country = isset( $_GET['guest_country'] ) ? sanitize_text_field( wp_unslash( $_GET['guest_country'] ) ) : '';
|
|
?>
|
|
<select name="guest_country">
|
|
<option value=""><?php esc_html_e( 'All Countries', 'wp-bnb' ); ?></option>
|
|
<?php foreach ( Building::get_countries() as $code => $name ) : ?>
|
|
<option value="<?php echo esc_attr( $code ); ?>" <?php selected( $selected_country, $code ); ?>>
|
|
<?php echo esc_html( $name ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Filter query based on filters.
|
|
*
|
|
* @param \WP_Query $query The query object.
|
|
* @return void
|
|
*/
|
|
public static function filter_query( \WP_Query $query ): void {
|
|
if ( ! is_admin() || ! $query->is_main_query() ) {
|
|
return;
|
|
}
|
|
|
|
if ( self::POST_TYPE !== $query->get( 'post_type' ) ) {
|
|
return;
|
|
}
|
|
|
|
$meta_query = array();
|
|
|
|
// Status filter.
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter query only.
|
|
if ( ! empty( $_GET['guest_status'] ) ) {
|
|
$meta_query[] = array(
|
|
'key' => self::META_PREFIX . 'status',
|
|
'value' => sanitize_text_field( wp_unslash( $_GET['guest_status'] ) ),
|
|
);
|
|
}
|
|
|
|
// Country filter.
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter query only.
|
|
if ( ! empty( $_GET['guest_country'] ) ) {
|
|
$meta_query[] = array(
|
|
'key' => self::META_PREFIX . 'country',
|
|
'value' => sanitize_text_field( wp_unslash( $_GET['guest_country'] ) ),
|
|
);
|
|
}
|
|
|
|
if ( ! empty( $meta_query ) ) {
|
|
$meta_query['relation'] = 'AND';
|
|
$query->set( 'meta_query', $meta_query );
|
|
}
|
|
|
|
// Handle orderby for custom columns.
|
|
$orderby = $query->get( 'orderby' );
|
|
if ( 'email' === $orderby ) {
|
|
$query->set( 'meta_key', self::META_PREFIX . 'email' );
|
|
$query->set( 'orderby', 'meta_value' );
|
|
} elseif ( 'country' === $orderby ) {
|
|
$query->set( 'meta_key', self::META_PREFIX . 'country' );
|
|
$query->set( 'orderby', 'meta_value' );
|
|
} elseif ( 'status' === $orderby ) {
|
|
$query->set( 'meta_key', self::META_PREFIX . 'status' );
|
|
$query->set( 'orderby', 'meta_value' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Change title placeholder.
|
|
*
|
|
* @param string $placeholder Default placeholder.
|
|
* @param \WP_Post $post Current post.
|
|
* @return string
|
|
*/
|
|
public static function change_title_placeholder( string $placeholder, \WP_Post $post ): string {
|
|
if ( self::POST_TYPE === $post->post_type ) {
|
|
return __( 'Guest name (auto-generated from first/last name)', 'wp-bnb' );
|
|
}
|
|
return $placeholder;
|
|
}
|
|
|
|
/**
|
|
* Get guest statuses.
|
|
*
|
|
* @return array<string, string>
|
|
*/
|
|
public static function get_statuses(): array {
|
|
return array(
|
|
'active' => __( 'Active', 'wp-bnb' ),
|
|
'inactive' => __( 'Inactive', 'wp-bnb' ),
|
|
'blocked' => __( 'Blocked', 'wp-bnb' ),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get status colors.
|
|
*
|
|
* @return array<string, string>
|
|
*/
|
|
public static function get_status_colors(): array {
|
|
return array(
|
|
'active' => '#00a32a',
|
|
'inactive' => '#72aee6',
|
|
'blocked' => '#d63638',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get ID types.
|
|
*
|
|
* @return array<string, string>
|
|
*/
|
|
public static function get_id_types(): array {
|
|
return array(
|
|
'passport' => __( 'Passport', 'wp-bnb' ),
|
|
'id_card' => __( 'ID Card', 'wp-bnb' ),
|
|
'drivers_license' => __( "Driver's License", 'wp-bnb' ),
|
|
'other' => __( 'Other', 'wp-bnb' ),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get guest by email.
|
|
*
|
|
* @param string $email Email address to search for.
|
|
* @return \WP_Post|null Guest post or null if not found.
|
|
*/
|
|
public static function get_by_email( string $email ): ?\WP_Post {
|
|
if ( empty( $email ) ) {
|
|
return null;
|
|
}
|
|
|
|
$guests = get_posts(
|
|
array(
|
|
'post_type' => self::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => 1,
|
|
'meta_query' => array(
|
|
array(
|
|
'key' => self::META_PREFIX . 'email',
|
|
'value' => $email,
|
|
),
|
|
),
|
|
)
|
|
);
|
|
|
|
return ! empty( $guests ) ? $guests[0] : null;
|
|
}
|
|
|
|
/**
|
|
* Get all bookings for a guest.
|
|
*
|
|
* @param int $guest_id Guest post ID.
|
|
* @return array Array of booking posts.
|
|
*/
|
|
public static function get_bookings( int $guest_id ): array {
|
|
$email = get_post_meta( $guest_id, self::META_PREFIX . 'email', true );
|
|
if ( empty( $email ) ) {
|
|
return array();
|
|
}
|
|
|
|
// First try to find bookings by guest_id (new way).
|
|
$bookings_by_id = get_posts(
|
|
array(
|
|
'post_type' => Booking::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => -1,
|
|
'meta_query' => array(
|
|
array(
|
|
'key' => '_bnb_booking_guest_id',
|
|
'value' => $guest_id,
|
|
),
|
|
),
|
|
)
|
|
);
|
|
|
|
// Also find bookings by email (legacy way).
|
|
$bookings_by_email = get_posts(
|
|
array(
|
|
'post_type' => Booking::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => -1,
|
|
'meta_query' => array(
|
|
array(
|
|
'key' => '_bnb_booking_guest_email',
|
|
'value' => $email,
|
|
),
|
|
),
|
|
)
|
|
);
|
|
|
|
// Merge and deduplicate.
|
|
$all_bookings = array_merge( $bookings_by_id, $bookings_by_email );
|
|
$unique_ids = array();
|
|
$result = array();
|
|
|
|
foreach ( $all_bookings as $booking ) {
|
|
if ( ! in_array( $booking->ID, $unique_ids, true ) ) {
|
|
$unique_ids[] = $booking->ID;
|
|
$result[] = $booking;
|
|
}
|
|
}
|
|
|
|
// Sort by check-in date descending.
|
|
usort(
|
|
$result,
|
|
function ( $a, $b ) {
|
|
$date_a = get_post_meta( $a->ID, '_bnb_booking_check_in', true );
|
|
$date_b = get_post_meta( $b->ID, '_bnb_booking_check_in', true );
|
|
return strcmp( $date_b, $date_a );
|
|
}
|
|
);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get booking count for a guest.
|
|
*
|
|
* @param int $guest_id Guest post ID.
|
|
* @return int Number of bookings.
|
|
*/
|
|
public static function get_booking_count( int $guest_id ): int {
|
|
return count( self::get_bookings( $guest_id ) );
|
|
}
|
|
|
|
/**
|
|
* Get total spent by a guest.
|
|
*
|
|
* @param int $guest_id Guest post ID.
|
|
* @return float Total amount spent.
|
|
*/
|
|
public static function get_total_spent( int $guest_id ): float {
|
|
$bookings = self::get_bookings( $guest_id );
|
|
$total = 0.0;
|
|
|
|
foreach ( $bookings as $booking ) {
|
|
$status = get_post_meta( $booking->ID, '_bnb_booking_status', true );
|
|
// Only count completed bookings (checked_out) or confirmed ones.
|
|
if ( in_array( $status, array( 'confirmed', 'checked_in', 'checked_out' ), true ) ) {
|
|
$price = get_post_meta( $booking->ID, '_bnb_booking_calculated_price', true );
|
|
$total += floatval( $price );
|
|
}
|
|
}
|
|
|
|
return $total;
|
|
}
|
|
|
|
/**
|
|
* Get formatted address for a guest.
|
|
*
|
|
* @param int $guest_id Guest post ID.
|
|
* @return string Formatted address.
|
|
*/
|
|
public static function get_formatted_address( int $guest_id ): string {
|
|
$street = get_post_meta( $guest_id, self::META_PREFIX . 'street', true );
|
|
$city = get_post_meta( $guest_id, self::META_PREFIX . 'city', true );
|
|
$postal_code = get_post_meta( $guest_id, self::META_PREFIX . 'postal_code', true );
|
|
$country = get_post_meta( $guest_id, self::META_PREFIX . 'country', true );
|
|
|
|
$parts = array();
|
|
|
|
if ( $street ) {
|
|
$parts[] = $street;
|
|
}
|
|
if ( $postal_code || $city ) {
|
|
$parts[] = trim( $postal_code . ' ' . $city );
|
|
}
|
|
if ( $country ) {
|
|
$countries = Building::get_countries();
|
|
$parts[] = $countries[ $country ] ?? $country;
|
|
}
|
|
|
|
return implode( "\n", $parts );
|
|
}
|
|
|
|
/**
|
|
* Get guest's full name.
|
|
*
|
|
* @param int $guest_id Guest post ID.
|
|
* @return string Full name.
|
|
*/
|
|
public static function get_full_name( int $guest_id ): string {
|
|
$first_name = get_post_meta( $guest_id, self::META_PREFIX . 'first_name', true );
|
|
$last_name = get_post_meta( $guest_id, self::META_PREFIX . 'last_name', true );
|
|
|
|
return trim( $first_name . ' ' . $last_name );
|
|
}
|
|
|
|
/**
|
|
* Get guest's ID number (decrypted).
|
|
*
|
|
* @param int $guest_id Guest post ID.
|
|
* @return string Decrypted ID number.
|
|
*/
|
|
public static function get_id_number( int $guest_id ): string {
|
|
$encrypted = get_post_meta( $guest_id, self::META_PREFIX . 'id_number', true );
|
|
return self::decrypt( $encrypted );
|
|
}
|
|
}
|