Files
wp-bnb/src/Frontend/Shortcodes.php

868 lines
30 KiB
PHP
Raw Normal View History

<?php
/**
* Frontend shortcodes.
*
* Handles all shortcode registration and rendering.
*
* @package Magdev\WpBnb\Frontend
*/
declare( strict_types=1 );
namespace Magdev\WpBnb\Frontend;
use Magdev\WpBnb\Booking\Availability;
use Magdev\WpBnb\PostTypes\Building;
use Magdev\WpBnb\PostTypes\Room;
use Magdev\WpBnb\Pricing\Calculator;
use Magdev\WpBnb\Pricing\PricingTier;
use Magdev\WpBnb\Taxonomies\Amenity;
use Magdev\WpBnb\Taxonomies\RoomType;
/**
* Shortcodes class.
*/
final class Shortcodes {
/**
* Initialize shortcodes.
*
* @return void
*/
public static function init(): void {
add_shortcode( 'bnb_buildings', array( self::class, 'render_buildings' ) );
add_shortcode( 'bnb_rooms', array( self::class, 'render_rooms' ) );
add_shortcode( 'bnb_room_search', array( self::class, 'render_room_search' ) );
add_shortcode( 'bnb_building', array( self::class, 'render_single_building' ) );
add_shortcode( 'bnb_room', array( self::class, 'render_single_room' ) );
}
/**
* Render buildings list/grid shortcode.
*
* @param array $atts Shortcode attributes.
* @return string HTML output.
*/
public static function render_buildings( $atts ): string {
$atts = shortcode_atts(
array(
'layout' => 'grid',
'columns' => 3,
'limit' => -1,
'orderby' => 'title',
'order' => 'ASC',
'show_image' => 'yes',
'show_address' => 'yes',
'show_rooms_count' => 'yes',
),
$atts,
'bnb_buildings'
);
// Query buildings.
$query_args = array(
'post_type' => Building::POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => (int) $atts['limit'],
'orderby' => sanitize_text_field( $atts['orderby'] ),
'order' => strtoupper( $atts['order'] ) === 'DESC' ? 'DESC' : 'ASC',
);
$buildings = get_posts( $query_args );
if ( empty( $buildings ) ) {
return '<p class="wp-bnb-no-results">' . esc_html__( 'No buildings found.', 'wp-bnb' ) . '</p>';
}
$layout = sanitize_text_field( $atts['layout'] );
$columns = max( 1, min( 4, (int) $atts['columns'] ) );
$classes = array(
'wp-bnb-buildings',
'wp-bnb-buildings-' . $layout,
);
if ( 'grid' === $layout ) {
$classes[] = 'wp-bnb-columns-' . $columns;
}
ob_start();
?>
<div class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>">
<?php foreach ( $buildings as $building ) : ?>
<?php echo self::render_building_card( $building, $atts ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endforeach; ?>
</div>
<?php
return ob_get_clean();
}
/**
* Render a single building card.
*
* @param \WP_Post $building Building post.
* @param array $atts Display attributes.
* @return string HTML output.
*/
private static function render_building_card( \WP_Post $building, array $atts ): string {
$show_image = 'yes' === $atts['show_image'];
$show_address = 'yes' === $atts['show_address'];
$show_rooms_count = 'yes' === $atts['show_rooms_count'];
// Get room count.
$rooms = Room::get_rooms_for_building( $building->ID );
$room_count = count( $rooms );
ob_start();
?>
<div class="wp-bnb-building-card">
<?php if ( $show_image && has_post_thumbnail( $building->ID ) ) : ?>
<div class="wp-bnb-building-image">
<a href="<?php echo esc_url( get_permalink( $building->ID ) ); ?>">
<?php echo get_the_post_thumbnail( $building->ID, 'medium_large' ); ?>
</a>
</div>
<?php endif; ?>
<div class="wp-bnb-building-content">
<h3 class="wp-bnb-building-title">
<a href="<?php echo esc_url( get_permalink( $building->ID ) ); ?>">
<?php echo esc_html( $building->post_title ); ?>
</a>
</h3>
<?php if ( $show_address ) : ?>
<?php
$city = get_post_meta( $building->ID, '_bnb_building_city', true );
$country = get_post_meta( $building->ID, '_bnb_building_country', true );
if ( $city || $country ) :
$countries = Building::get_countries();
$country_name = $countries[ $country ] ?? $country;
?>
<p class="wp-bnb-building-address">
<span class="dashicons dashicons-location"></span>
<?php echo esc_html( implode( ', ', array_filter( array( $city, $country_name ) ) ) ); ?>
</p>
<?php endif; ?>
<?php endif; ?>
<?php if ( $show_rooms_count && $room_count > 0 ) : ?>
<p class="wp-bnb-building-rooms">
<span class="dashicons dashicons-admin-home"></span>
<?php
printf(
/* translators: %d: Number of rooms */
esc_html( _n( '%d room', '%d rooms', $room_count, 'wp-bnb' ) ),
(int) $room_count
);
?>
</p>
<?php endif; ?>
<?php if ( has_excerpt( $building->ID ) ) : ?>
<div class="wp-bnb-building-excerpt">
<?php echo wp_kses_post( get_the_excerpt( $building->ID ) ); ?>
</div>
<?php endif; ?>
<a href="<?php echo esc_url( get_permalink( $building->ID ) ); ?>" class="wp-bnb-button">
<?php esc_html_e( 'View Details', 'wp-bnb' ); ?>
</a>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Render rooms list/grid shortcode.
*
* @param array $atts Shortcode attributes.
* @return string HTML output.
*/
public static function render_rooms( $atts ): string {
$atts = shortcode_atts(
array(
'layout' => 'grid',
'columns' => 3,
'limit' => 12,
'building_id' => 0,
'room_type' => '',
'amenities' => '',
'orderby' => 'title',
'order' => 'ASC',
'show_image' => 'yes',
'show_price' => 'yes',
'show_capacity' => 'yes',
'show_amenities' => 'yes',
'show_building' => 'yes',
),
$atts,
'bnb_rooms'
);
// Use search function for filtering.
$search_args = array(
'building_id' => (int) $atts['building_id'],
'room_type' => sanitize_text_field( $atts['room_type'] ),
'amenities' => $atts['amenities'] ? explode( ',', $atts['amenities'] ) : array(),
'orderby' => sanitize_text_field( $atts['orderby'] ),
'order' => $atts['order'],
'limit' => (int) $atts['limit'],
);
$rooms = Search::search( $search_args );
if ( empty( $rooms ) ) {
return '<p class="wp-bnb-no-results">' . esc_html__( 'No rooms found.', 'wp-bnb' ) . '</p>';
}
$layout = sanitize_text_field( $atts['layout'] );
$columns = max( 1, min( 4, (int) $atts['columns'] ) );
$classes = array(
'wp-bnb-rooms',
'wp-bnb-rooms-' . $layout,
);
if ( 'grid' === $layout ) {
$classes[] = 'wp-bnb-columns-' . $columns;
}
ob_start();
?>
<div class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>">
<?php foreach ( $rooms as $room_data ) : ?>
<?php echo self::render_room_card( $room_data, $atts ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endforeach; ?>
</div>
<?php
return ob_get_clean();
}
/**
* Render a single room card.
*
* @param array $room Room data array.
* @param array $atts Display attributes.
* @return string HTML output.
*/
private static function render_room_card( array $room, array $atts ): string {
$show_image = 'yes' === $atts['show_image'];
$show_price = 'yes' === $atts['show_price'];
$show_capacity = 'yes' === $atts['show_capacity'];
$show_amenities = 'yes' === $atts['show_amenities'];
$show_building = 'yes' === $atts['show_building'];
ob_start();
?>
<div class="wp-bnb-room-card" data-room-id="<?php echo esc_attr( $room['id'] ); ?>">
<?php if ( $show_image && ! empty( $room['featured_image'] ) ) : ?>
<div class="wp-bnb-room-image">
<a href="<?php echo esc_url( $room['permalink'] ); ?>">
<img src="<?php echo esc_url( $room['featured_image'] ); ?>" alt="<?php echo esc_attr( $room['title'] ); ?>">
</a>
<?php if ( ! empty( $room['room_types'] ) ) : ?>
<span class="wp-bnb-room-type-badge"><?php echo esc_html( $room['room_types'][0] ); ?></span>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="wp-bnb-room-content">
<h3 class="wp-bnb-room-title">
<a href="<?php echo esc_url( $room['permalink'] ); ?>">
<?php echo esc_html( $room['title'] ); ?>
</a>
</h3>
<?php if ( $show_building && ! empty( $room['building'] ) ) : ?>
<p class="wp-bnb-room-building">
<span class="dashicons dashicons-building"></span>
<a href="<?php echo esc_url( $room['building']['permalink'] ); ?>">
<?php echo esc_html( $room['building']['title'] ); ?>
</a>
<?php if ( ! empty( $room['building']['city'] ) ) : ?>
<span class="wp-bnb-room-city">, <?php echo esc_html( $room['building']['city'] ); ?></span>
<?php endif; ?>
</p>
<?php endif; ?>
<div class="wp-bnb-room-meta">
<?php if ( $show_capacity && ! empty( $room['capacity'] ) ) : ?>
<span class="wp-bnb-room-capacity">
<span class="dashicons dashicons-groups"></span>
<?php
printf(
/* translators: %d: Number of guests */
esc_html( _n( '%d guest', '%d guests', $room['capacity'], 'wp-bnb' ) ),
(int) $room['capacity']
);
?>
</span>
<?php endif; ?>
<?php if ( ! empty( $room['size'] ) ) : ?>
<span class="wp-bnb-room-size">
<span class="dashicons dashicons-editor-expand"></span>
<?php echo esc_html( $room['size'] ); ?>
</span>
<?php endif; ?>
<?php if ( ! empty( $room['beds'] ) ) : ?>
<span class="wp-bnb-room-beds">
<span class="dashicons dashicons-admin-home"></span>
<?php echo esc_html( $room['beds'] ); ?>
</span>
<?php endif; ?>
</div>
<?php if ( $show_amenities && ! empty( $room['amenities'] ) ) : ?>
<div class="wp-bnb-room-amenities">
<?php foreach ( array_slice( $room['amenities'], 0, 4 ) as $amenity ) : ?>
<span class="wp-bnb-amenity" title="<?php echo esc_attr( $amenity['name'] ); ?>">
<?php if ( ! empty( $amenity['icon'] ) ) : ?>
<span class="dashicons dashicons-<?php echo esc_attr( $amenity['icon'] ); ?>"></span>
<?php endif; ?>
<span class="wp-bnb-amenity-name"><?php echo esc_html( $amenity['name'] ); ?></span>
</span>
<?php endforeach; ?>
<?php if ( count( $room['amenities'] ) > 4 ) : ?>
<span class="wp-bnb-amenity-more">
+<?php echo (int) ( count( $room['amenities'] ) - 4 ); ?>
</span>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="wp-bnb-room-footer">
<?php if ( $show_price && ! empty( $room['price_formatted'] ) ) : ?>
<span class="wp-bnb-room-price">
<span class="wp-bnb-price-amount"><?php echo esc_html( $room['price_formatted'] ); ?></span>
<span class="wp-bnb-price-unit"><?php esc_html_e( '/night', 'wp-bnb' ); ?></span>
</span>
<?php endif; ?>
<a href="<?php echo esc_url( $room['permalink'] ); ?>" class="wp-bnb-button">
<?php esc_html_e( 'View Details', 'wp-bnb' ); ?>
</a>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Render room search form with results.
*
* @param array $atts Shortcode attributes.
* @return string HTML output.
*/
public static function render_room_search( $atts ): string {
$atts = shortcode_atts(
array(
'layout' => 'grid',
'columns' => 3,
'show_dates' => 'yes',
'show_guests' => 'yes',
'show_room_type' => 'yes',
'show_amenities' => 'yes',
'show_price_range' => 'yes',
'show_building' => 'yes',
'results_per_page' => 12,
),
$atts,
'bnb_room_search'
);
// Get search form data.
$form_data = Search::get_search_form_data();
$layout = sanitize_text_field( $atts['layout'] );
$columns = max( 1, min( 4, (int) $atts['columns'] ) );
ob_start();
?>
<div class="wp-bnb-room-search" data-layout="<?php echo esc_attr( $layout ); ?>" data-columns="<?php echo esc_attr( $columns ); ?>" data-per-page="<?php echo esc_attr( $atts['results_per_page'] ); ?>">
<form class="wp-bnb-search-form" id="wp-bnb-search-form">
<div class="wp-bnb-search-fields">
<?php if ( 'yes' === $atts['show_dates'] ) : ?>
<div class="wp-bnb-field wp-bnb-field-dates">
<div class="wp-bnb-field-group">
<label for="wp-bnb-check-in"><?php esc_html_e( 'Check-in', 'wp-bnb' ); ?></label>
<input type="date" id="wp-bnb-check-in" name="check_in" min="<?php echo esc_attr( gmdate( 'Y-m-d' ) ); ?>">
</div>
<div class="wp-bnb-field-group">
<label for="wp-bnb-check-out"><?php esc_html_e( 'Check-out', 'wp-bnb' ); ?></label>
<input type="date" id="wp-bnb-check-out" name="check_out" min="<?php echo esc_attr( gmdate( 'Y-m-d', strtotime( '+1 day' ) ) ); ?>">
</div>
</div>
<?php endif; ?>
<?php if ( 'yes' === $atts['show_guests'] ) : ?>
<div class="wp-bnb-field wp-bnb-field-guests">
<label for="wp-bnb-guests"><?php esc_html_e( 'Guests', 'wp-bnb' ); ?></label>
<select id="wp-bnb-guests" name="guests">
<option value=""><?php esc_html_e( 'Any', 'wp-bnb' ); ?></option>
<?php for ( $i = 1; $i <= $form_data['capacity_range']['max']; $i++ ) : ?>
<option value="<?php echo esc_attr( $i ); ?>">
<?php echo esc_html( $i ); ?>
</option>
<?php endfor; ?>
</select>
</div>
<?php endif; ?>
<?php if ( 'yes' === $atts['show_room_type'] && ! empty( $form_data['room_types'] ) ) : ?>
<div class="wp-bnb-field wp-bnb-field-room-type">
<label for="wp-bnb-room-type"><?php esc_html_e( 'Room Type', 'wp-bnb' ); ?></label>
<select id="wp-bnb-room-type" name="room_type">
<option value=""><?php esc_html_e( 'All Types', 'wp-bnb' ); ?></option>
<?php foreach ( $form_data['room_types'] as $type ) : ?>
<option value="<?php echo esc_attr( $type['slug'] ); ?>">
<?php echo esc_html( $type['name'] ); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if ( 'yes' === $atts['show_building'] && ! empty( $form_data['buildings'] ) ) : ?>
<div class="wp-bnb-field wp-bnb-field-building">
<label for="wp-bnb-building"><?php esc_html_e( 'Building', 'wp-bnb' ); ?></label>
<select id="wp-bnb-building" name="building_id">
<option value=""><?php esc_html_e( 'All Buildings', 'wp-bnb' ); ?></option>
<?php foreach ( $form_data['buildings'] as $building ) : ?>
<option value="<?php echo esc_attr( $building['id'] ); ?>">
<?php echo esc_html( $building['title'] ); ?>
<?php if ( ! empty( $building['city'] ) ) : ?>
(<?php echo esc_html( $building['city'] ); ?>)
<?php endif; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if ( 'yes' === $atts['show_price_range'] && $form_data['price_range']['max'] > 0 ) : ?>
<div class="wp-bnb-field wp-bnb-field-price-range">
<label><?php esc_html_e( 'Price Range', 'wp-bnb' ); ?></label>
<div class="wp-bnb-price-range-inputs">
<input type="number" id="wp-bnb-price-min" name="price_min" placeholder="<?php esc_attr_e( 'Min', 'wp-bnb' ); ?>" min="0" step="10">
<span class="wp-bnb-price-separator">-</span>
<input type="number" id="wp-bnb-price-max" name="price_max" placeholder="<?php esc_attr_e( 'Max', 'wp-bnb' ); ?>" min="0" step="10">
<span class="wp-bnb-currency"><?php echo esc_html( $form_data['currency'] ); ?></span>
</div>
</div>
<?php endif; ?>
</div>
<?php if ( 'yes' === $atts['show_amenities'] && ! empty( $form_data['amenities'] ) ) : ?>
<div class="wp-bnb-search-amenities">
<label><?php esc_html_e( 'Amenities', 'wp-bnb' ); ?></label>
<div class="wp-bnb-amenities-list">
<?php foreach ( $form_data['amenities'] as $amenity ) : ?>
<label class="wp-bnb-amenity-checkbox">
<input type="checkbox" name="amenities[]" value="<?php echo esc_attr( $amenity['slug'] ); ?>">
<?php if ( ! empty( $amenity['icon'] ) ) : ?>
<span class="dashicons dashicons-<?php echo esc_attr( $amenity['icon'] ); ?>"></span>
<?php endif; ?>
<span><?php echo esc_html( $amenity['name'] ); ?></span>
</label>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div class="wp-bnb-search-actions">
<button type="submit" class="wp-bnb-button wp-bnb-button-primary">
<span class="dashicons dashicons-search"></span>
<?php esc_html_e( 'Search Rooms', 'wp-bnb' ); ?>
</button>
<button type="reset" class="wp-bnb-button wp-bnb-button-secondary">
<?php esc_html_e( 'Clear', 'wp-bnb' ); ?>
</button>
</div>
</form>
<div class="wp-bnb-search-results-container">
<div class="wp-bnb-search-status">
<span class="wp-bnb-results-count"></span>
<div class="wp-bnb-sort-options">
<label for="wp-bnb-sort"><?php esc_html_e( 'Sort by:', 'wp-bnb' ); ?></label>
<select id="wp-bnb-sort" name="orderby">
<option value="title"><?php esc_html_e( 'Name', 'wp-bnb' ); ?></option>
<option value="price"><?php esc_html_e( 'Price', 'wp-bnb' ); ?></option>
<option value="capacity"><?php esc_html_e( 'Capacity', 'wp-bnb' ); ?></option>
</select>
</div>
</div>
<div class="wp-bnb-search-results wp-bnb-rooms wp-bnb-rooms-<?php echo esc_attr( $layout ); ?> wp-bnb-columns-<?php echo esc_attr( $columns ); ?>">
<div class="wp-bnb-loading">
<span class="wp-bnb-spinner"></span>
<span><?php esc_html_e( 'Loading rooms...', 'wp-bnb' ); ?></span>
</div>
</div>
<div class="wp-bnb-search-pagination">
<button type="button" class="wp-bnb-button wp-bnb-load-more" style="display:none;">
<?php esc_html_e( 'Load More', 'wp-bnb' ); ?>
</button>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Render single building shortcode.
*
* @param array $atts Shortcode attributes.
* @return string HTML output.
*/
public static function render_single_building( $atts ): string {
$atts = shortcode_atts(
array(
'id' => 0,
'show_rooms' => 'yes',
'show_address' => 'yes',
'show_contact' => 'yes',
),
$atts,
'bnb_building'
);
$building_id = (int) $atts['id'];
if ( ! $building_id ) {
return '<p class="wp-bnb-error">' . esc_html__( 'Building ID is required.', 'wp-bnb' ) . '</p>';
}
$building = get_post( $building_id );
if ( ! $building || Building::POST_TYPE !== $building->post_type ) {
return '<p class="wp-bnb-error">' . esc_html__( 'Building not found.', 'wp-bnb' ) . '</p>';
}
$show_rooms = 'yes' === $atts['show_rooms'];
$show_address = 'yes' === $atts['show_address'];
$show_contact = 'yes' === $atts['show_contact'];
ob_start();
?>
<div class="wp-bnb-building-single">
<?php if ( has_post_thumbnail( $building->ID ) ) : ?>
<div class="wp-bnb-building-featured-image">
<?php echo get_the_post_thumbnail( $building->ID, 'large' ); ?>
</div>
<?php endif; ?>
<div class="wp-bnb-building-header">
<h2 class="wp-bnb-building-title"><?php echo esc_html( $building->post_title ); ?></h2>
</div>
<div class="wp-bnb-building-details">
<?php if ( $show_address ) : ?>
<?php $address = Building::get_formatted_address( $building->ID ); ?>
<?php if ( ! empty( $address ) ) : ?>
<div class="wp-bnb-building-address">
<h4><?php esc_html_e( 'Address', 'wp-bnb' ); ?></h4>
<address><?php echo nl2br( esc_html( $address ) ); ?></address>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if ( $show_contact ) : ?>
<?php
$phone = get_post_meta( $building->ID, '_bnb_building_phone', true );
$email = get_post_meta( $building->ID, '_bnb_building_email', true );
$website = get_post_meta( $building->ID, '_bnb_building_website', true );
?>
<?php if ( $phone || $email || $website ) : ?>
<div class="wp-bnb-building-contact">
<h4><?php esc_html_e( 'Contact', 'wp-bnb' ); ?></h4>
<?php if ( $phone ) : ?>
<p class="wp-bnb-contact-phone">
<span class="dashicons dashicons-phone"></span>
<a href="tel:<?php echo esc_attr( $phone ); ?>"><?php echo esc_html( $phone ); ?></a>
</p>
<?php endif; ?>
<?php if ( $email ) : ?>
<p class="wp-bnb-contact-email">
<span class="dashicons dashicons-email"></span>
<a href="mailto:<?php echo esc_attr( $email ); ?>"><?php echo esc_html( $email ); ?></a>
</p>
<?php endif; ?>
<?php if ( $website ) : ?>
<p class="wp-bnb-contact-website">
<span class="dashicons dashicons-admin-site"></span>
<a href="<?php echo esc_url( $website ); ?>" target="_blank" rel="noopener"><?php echo esc_html( $website ); ?></a>
</p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
<?php
$check_in_time = get_post_meta( $building->ID, '_bnb_building_check_in_time', true );
$check_out_time = get_post_meta( $building->ID, '_bnb_building_check_out_time', true );
if ( $check_in_time || $check_out_time ) :
?>
<div class="wp-bnb-building-times">
<h4><?php esc_html_e( 'Check-in / Check-out', 'wp-bnb' ); ?></h4>
<?php if ( $check_in_time ) : ?>
<p><strong><?php esc_html_e( 'Check-in:', 'wp-bnb' ); ?></strong> <?php echo esc_html( $check_in_time ); ?></p>
<?php endif; ?>
<?php if ( $check_out_time ) : ?>
<p><strong><?php esc_html_e( 'Check-out:', 'wp-bnb' ); ?></strong> <?php echo esc_html( $check_out_time ); ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php if ( ! empty( $building->post_content ) ) : ?>
<div class="wp-bnb-building-description">
<?php echo wp_kses_post( apply_filters( 'the_content', $building->post_content ) ); ?>
</div>
<?php endif; ?>
<?php if ( $show_rooms ) : ?>
<?php $rooms = Room::get_rooms_for_building( $building->ID ); ?>
<?php if ( ! empty( $rooms ) ) : ?>
<div class="wp-bnb-building-rooms">
<h3><?php esc_html_e( 'Available Rooms', 'wp-bnb' ); ?></h3>
<?php
echo self::render_rooms(
array(
'building_id' => $building->ID,
'show_building' => 'no',
'limit' => -1,
)
);
?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
/**
* Render single room shortcode.
*
* @param array $atts Shortcode attributes.
* @return string HTML output.
*/
public static function render_single_room( $atts ): string {
$atts = shortcode_atts(
array(
'id' => 0,
'show_gallery' => 'yes',
'show_pricing' => 'yes',
'show_amenities' => 'yes',
'show_availability' => 'yes',
),
$atts,
'bnb_room'
);
$room_id = (int) $atts['id'];
if ( ! $room_id ) {
return '<p class="wp-bnb-error">' . esc_html__( 'Room ID is required.', 'wp-bnb' ) . '</p>';
}
$room = get_post( $room_id );
if ( ! $room || Room::POST_TYPE !== $room->post_type ) {
return '<p class="wp-bnb-error">' . esc_html__( 'Room not found.', 'wp-bnb' ) . '</p>';
}
$show_gallery = 'yes' === $atts['show_gallery'];
$show_pricing = 'yes' === $atts['show_pricing'];
$show_amenities = 'yes' === $atts['show_amenities'];
$show_availability = 'yes' === $atts['show_availability'];
// Get room data.
$room_data = Search::get_room_data( $room );
ob_start();
?>
<div class="wp-bnb-room-single" data-room-id="<?php echo esc_attr( $room->ID ); ?>">
<?php if ( $show_gallery && ( has_post_thumbnail( $room->ID ) || ! empty( $room_data['gallery'] ) ) ) : ?>
<div class="wp-bnb-room-gallery">
<?php if ( has_post_thumbnail( $room->ID ) ) : ?>
<div class="wp-bnb-room-featured-image">
<?php echo get_the_post_thumbnail( $room->ID, 'large' ); ?>
</div>
<?php endif; ?>
<?php if ( ! empty( $room_data['gallery'] ) ) : ?>
<div class="wp-bnb-room-gallery-thumbnails">
<?php foreach ( $room_data['gallery'] as $image ) : ?>
<a href="<?php echo esc_url( $image['url'] ); ?>" class="wp-bnb-gallery-thumb" data-gallery>
<img src="<?php echo esc_url( $image['thumb'] ); ?>" alt="">
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="wp-bnb-room-header">
<div class="wp-bnb-room-header-content">
<h2 class="wp-bnb-room-title"><?php echo esc_html( $room->post_title ); ?></h2>
<?php if ( ! empty( $room_data['building'] ) ) : ?>
<p class="wp-bnb-room-building">
<span class="dashicons dashicons-building"></span>
<a href="<?php echo esc_url( $room_data['building']['permalink'] ); ?>">
<?php echo esc_html( $room_data['building']['title'] ); ?>
</a>
<?php if ( ! empty( $room_data['building']['city'] ) ) : ?>
<span>, <?php echo esc_html( $room_data['building']['city'] ); ?></span>
<?php endif; ?>
</p>
<?php endif; ?>
<?php if ( ! empty( $room_data['room_types'] ) ) : ?>
<span class="wp-bnb-room-type"><?php echo esc_html( implode( ', ', $room_data['room_types'] ) ); ?></span>
<?php endif; ?>
</div>
<?php if ( $show_pricing && ! empty( $room_data['price_formatted'] ) ) : ?>
<div class="wp-bnb-room-header-price">
<span class="wp-bnb-price-label"><?php esc_html_e( 'From', 'wp-bnb' ); ?></span>
<span class="wp-bnb-price-amount"><?php echo esc_html( $room_data['price_formatted'] ); ?></span>
<span class="wp-bnb-price-unit"><?php esc_html_e( '/night', 'wp-bnb' ); ?></span>
</div>
<?php endif; ?>
</div>
<div class="wp-bnb-room-info">
<div class="wp-bnb-room-specs">
<?php if ( ! empty( $room_data['capacity'] ) ) : ?>
<div class="wp-bnb-spec">
<span class="dashicons dashicons-groups"></span>
<span class="wp-bnb-spec-label"><?php esc_html_e( 'Capacity', 'wp-bnb' ); ?></span>
<span class="wp-bnb-spec-value">
<?php
printf(
/* translators: %d: Number of guests */
esc_html( _n( '%d guest', '%d guests', $room_data['capacity'], 'wp-bnb' ) ),
(int) $room_data['capacity']
);
?>
</span>
</div>
<?php endif; ?>
<?php if ( ! empty( $room_data['size'] ) ) : ?>
<div class="wp-bnb-spec">
<span class="dashicons dashicons-editor-expand"></span>
<span class="wp-bnb-spec-label"><?php esc_html_e( 'Size', 'wp-bnb' ); ?></span>
<span class="wp-bnb-spec-value"><?php echo esc_html( $room_data['size'] ); ?> m²</span>
</div>
<?php endif; ?>
<?php if ( ! empty( $room_data['beds'] ) ) : ?>
<div class="wp-bnb-spec">
<span class="dashicons dashicons-admin-home"></span>
<span class="wp-bnb-spec-label"><?php esc_html_e( 'Beds', 'wp-bnb' ); ?></span>
<span class="wp-bnb-spec-value"><?php echo esc_html( $room_data['beds'] ); ?></span>
</div>
<?php endif; ?>
<?php if ( ! empty( $room_data['bathrooms'] ) ) : ?>
<div class="wp-bnb-spec">
<span class="dashicons dashicons-admin-page"></span>
<span class="wp-bnb-spec-label"><?php esc_html_e( 'Bathrooms', 'wp-bnb' ); ?></span>
<span class="wp-bnb-spec-value"><?php echo esc_html( $room_data['bathrooms'] ); ?></span>
</div>
<?php endif; ?>
<?php if ( ! empty( $room_data['floor'] ) ) : ?>
<div class="wp-bnb-spec">
<span class="dashicons dashicons-building"></span>
<span class="wp-bnb-spec-label"><?php esc_html_e( 'Floor', 'wp-bnb' ); ?></span>
<span class="wp-bnb-spec-value"><?php echo esc_html( $room_data['floor'] ); ?></span>
</div>
<?php endif; ?>
</div>
<?php if ( $show_amenities && ! empty( $room_data['amenities'] ) ) : ?>
<div class="wp-bnb-room-amenities-full">
<h4><?php esc_html_e( 'Amenities', 'wp-bnb' ); ?></h4>
<ul class="wp-bnb-amenities-list">
<?php foreach ( $room_data['amenities'] as $amenity ) : ?>
<li class="wp-bnb-amenity">
<?php if ( ! empty( $amenity['icon'] ) ) : ?>
<span class="dashicons dashicons-<?php echo esc_attr( $amenity['icon'] ); ?>"></span>
<?php endif; ?>
<span><?php echo esc_html( $amenity['name'] ); ?></span>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
</div>
<?php if ( ! empty( $room->post_content ) ) : ?>
<div class="wp-bnb-room-description">
<?php echo wp_kses_post( apply_filters( 'the_content', $room->post_content ) ); ?>
</div>
<?php endif; ?>
<?php if ( $show_pricing ) : ?>
<?php $pricing = Calculator::getRoomPricing( $room->ID ); ?>
<div class="wp-bnb-room-pricing-details">
<h4><?php esc_html_e( 'Pricing', 'wp-bnb' ); ?></h4>
<table class="wp-bnb-pricing-table">
<tbody>
<?php foreach ( PricingTier::cases() as $tier ) : ?>
<?php $price = $pricing[ $tier->value ]['price'] ?? null; ?>
<?php if ( $price ) : ?>
<tr>
<td class="wp-bnb-tier-label"><?php echo esc_html( $tier->label() ); ?></td>
<td class="wp-bnb-tier-price">
<?php echo esc_html( Calculator::formatPrice( $price ) ); ?>
<span class="wp-bnb-tier-unit"><?php echo esc_html( $tier->unit() ); ?></span>
</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if ( $show_availability ) : ?>
<div class="wp-bnb-room-availability">
<h4><?php esc_html_e( 'Check Availability', 'wp-bnb' ); ?></h4>
<form class="wp-bnb-availability-form" data-room-id="<?php echo esc_attr( $room->ID ); ?>">
<div class="wp-bnb-availability-fields">
<div class="wp-bnb-field-group">
<label for="wp-bnb-avail-check-in"><?php esc_html_e( 'Check-in', 'wp-bnb' ); ?></label>
<input type="date" id="wp-bnb-avail-check-in" name="check_in" min="<?php echo esc_attr( gmdate( 'Y-m-d' ) ); ?>" required>
</div>
<div class="wp-bnb-field-group">
<label for="wp-bnb-avail-check-out"><?php esc_html_e( 'Check-out', 'wp-bnb' ); ?></label>
<input type="date" id="wp-bnb-avail-check-out" name="check_out" min="<?php echo esc_attr( gmdate( 'Y-m-d', strtotime( '+1 day' ) ) ); ?>" required>
</div>
<button type="submit" class="wp-bnb-button wp-bnb-button-primary">
<?php esc_html_e( 'Check', 'wp-bnb' ); ?>
</button>
</div>
<div class="wp-bnb-availability-result" style="display:none;"></div>
</form>
</div>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
}