Release v0.6.1 - Bug fixes and enhancements
All checks were successful
Create Release Package / build-release (push) Successful in 1m1s
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>
This commit is contained in:
@@ -30,6 +30,75 @@ final class Guest {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -45,6 +114,23 @@ final class Guest {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -311,12 +397,12 @@ final class Guest {
|
||||
*/
|
||||
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 = get_post_meta( $post->ID, self::META_PREFIX . 'id_number', 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: #d63638;"></span>
|
||||
<?php esc_html_e( 'This information is sensitive. Handle with care according to privacy regulations.', 'wp-bnb' ); ?>
|
||||
<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>
|
||||
@@ -443,9 +529,9 @@ final class Guest {
|
||||
$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_total_price', 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_statuses();
|
||||
$statuses = Booking::get_booking_statuses();
|
||||
$colors = Booking::get_status_colors();
|
||||
?>
|
||||
<tr>
|
||||
@@ -563,7 +649,7 @@ final class Guest {
|
||||
return;
|
||||
}
|
||||
|
||||
// Text fields.
|
||||
// Text fields (non-sensitive).
|
||||
$text_fields = array(
|
||||
'first_name',
|
||||
'last_name',
|
||||
@@ -574,7 +660,6 @@ final class Guest {
|
||||
'country',
|
||||
'nationality',
|
||||
'id_type',
|
||||
'id_number',
|
||||
'status',
|
||||
);
|
||||
|
||||
@@ -589,6 +674,16 @@ final class Guest {
|
||||
}
|
||||
}
|
||||
|
||||
// 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(
|
||||
@@ -1035,7 +1130,7 @@ final class Guest {
|
||||
$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_total_price', true );
|
||||
$price = get_post_meta( $booking->ID, '_bnb_booking_calculated_price', true );
|
||||
$total += floatval( $price );
|
||||
}
|
||||
}
|
||||
@@ -1083,4 +1178,15 @@ final class Guest {
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user