All checks were successful
Create Release Package / build-release (push) Successful in 59s
- Register tag generators via wpcf7_admin_init hook - Add BnB Building select tag generator with first_as_label option - Add BnB Room select tag generator with building_field and include_price options - Add BnB Check-in date tag generator with min/max advance options - Add BnB Check-out date tag generator with checkin_field and min/max nights options - Add BnB Guests count tag generator with room_field and min/max/default options - All generators support id and class attribute configuration - Remove bug from Known Bugs section in CLAUDE.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1655 lines
54 KiB
PHP
1655 lines
54 KiB
PHP
<?php
|
|
/**
|
|
* Contact Form 7 Integration.
|
|
*
|
|
* Provides custom form tags for booking and inquiry forms.
|
|
*
|
|
* @package Magdev\WpBnb\Integration
|
|
*/
|
|
|
|
declare( strict_types=1 );
|
|
|
|
namespace Magdev\WpBnb\Integration;
|
|
|
|
use Magdev\WpBnb\Booking\Availability;
|
|
use Magdev\WpBnb\PostTypes\Booking;
|
|
use Magdev\WpBnb\PostTypes\Building;
|
|
use Magdev\WpBnb\PostTypes\Guest;
|
|
use Magdev\WpBnb\PostTypes\Room;
|
|
use Magdev\WpBnb\Pricing\Calculator;
|
|
|
|
/**
|
|
* CF7 Integration class.
|
|
*/
|
|
final class CF7 {
|
|
|
|
/**
|
|
* CSS class to identify booking forms.
|
|
*
|
|
* @var string
|
|
*/
|
|
public const BOOKING_FORM_CLASS = 'wp-bnb-booking-form';
|
|
|
|
/**
|
|
* Meta key prefix for booking meta.
|
|
*
|
|
* @var string
|
|
*/
|
|
private const META_PREFIX = '_bnb_booking_';
|
|
|
|
/**
|
|
* Initialize the CF7 integration.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function init(): void {
|
|
// Register custom form tags.
|
|
add_action( 'wpcf7_init', array( self::class, 'register_form_tags' ) );
|
|
|
|
// Register tag generators for admin.
|
|
add_action( 'wpcf7_admin_init', array( self::class, 'register_tag_generators' ), 60 );
|
|
|
|
// Register validation filters.
|
|
add_filter( 'wpcf7_validate_bnb_room_select', array( self::class, 'validate_room_select' ), 10, 2 );
|
|
add_filter( 'wpcf7_validate_bnb_room_select*', array( self::class, 'validate_room_select' ), 10, 2 );
|
|
add_filter( 'wpcf7_validate_bnb_date_checkin', array( self::class, 'validate_date_checkin' ), 10, 2 );
|
|
add_filter( 'wpcf7_validate_bnb_date_checkin*', array( self::class, 'validate_date_checkin' ), 10, 2 );
|
|
add_filter( 'wpcf7_validate_bnb_date_checkout', array( self::class, 'validate_date_checkout' ), 10, 2 );
|
|
add_filter( 'wpcf7_validate_bnb_date_checkout*', array( self::class, 'validate_date_checkout' ), 10, 2 );
|
|
add_filter( 'wpcf7_validate_bnb_guests', array( self::class, 'validate_guests' ), 10, 2 );
|
|
add_filter( 'wpcf7_validate_bnb_guests*', array( self::class, 'validate_guests' ), 10, 2 );
|
|
|
|
// Availability validation and booking creation.
|
|
add_action( 'wpcf7_before_send_mail', array( self::class, 'validate_availability_before_mail' ), 10, 3 );
|
|
add_action( 'wpcf7_mail_sent', array( self::class, 'on_mail_sent' ) );
|
|
|
|
// Add custom mail tags.
|
|
add_filter( 'wpcf7_special_mail_tags', array( self::class, 'custom_mail_tags' ), 10, 4 );
|
|
}
|
|
|
|
/**
|
|
* Check if CF7 is active.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function is_cf7_active(): bool {
|
|
return class_exists( 'WPCF7' );
|
|
}
|
|
|
|
/**
|
|
* Register custom CF7 form tags.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function register_form_tags(): void {
|
|
if ( ! function_exists( 'wpcf7_add_form_tag' ) ) {
|
|
return;
|
|
}
|
|
|
|
// Building selector (optional filter).
|
|
wpcf7_add_form_tag(
|
|
array( 'bnb_building_select', 'bnb_building_select*' ),
|
|
array( self::class, 'render_building_select_tag' ),
|
|
array( 'name-attr' => true )
|
|
);
|
|
|
|
// Room selector.
|
|
wpcf7_add_form_tag(
|
|
array( 'bnb_room_select', 'bnb_room_select*' ),
|
|
array( self::class, 'render_room_select_tag' ),
|
|
array( 'name-attr' => true )
|
|
);
|
|
|
|
// Check-in date.
|
|
wpcf7_add_form_tag(
|
|
array( 'bnb_date_checkin', 'bnb_date_checkin*' ),
|
|
array( self::class, 'render_date_checkin_tag' ),
|
|
array( 'name-attr' => true )
|
|
);
|
|
|
|
// Check-out date.
|
|
wpcf7_add_form_tag(
|
|
array( 'bnb_date_checkout', 'bnb_date_checkout*' ),
|
|
array( self::class, 'render_date_checkout_tag' ),
|
|
array( 'name-attr' => true )
|
|
);
|
|
|
|
// Guests count.
|
|
wpcf7_add_form_tag(
|
|
array( 'bnb_guests', 'bnb_guests*' ),
|
|
array( self::class, 'render_guests_tag' ),
|
|
array( 'name-attr' => true )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Register tag generators for CF7 admin.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function register_tag_generators(): void {
|
|
if ( ! class_exists( 'WPCF7_TagGenerator' ) ) {
|
|
return;
|
|
}
|
|
|
|
$tag_generator = \WPCF7_TagGenerator::get_instance();
|
|
|
|
// Building select tag generator.
|
|
$tag_generator->add(
|
|
'bnb_building_select',
|
|
__( 'BnB building', 'wp-bnb' ),
|
|
array( self::class, 'tag_generator_building_select' ),
|
|
array( 'version' => '2' )
|
|
);
|
|
|
|
// Room select tag generator.
|
|
$tag_generator->add(
|
|
'bnb_room_select',
|
|
__( 'BnB room', 'wp-bnb' ),
|
|
array( self::class, 'tag_generator_room_select' ),
|
|
array( 'version' => '2' )
|
|
);
|
|
|
|
// Check-in date tag generator.
|
|
$tag_generator->add(
|
|
'bnb_date_checkin',
|
|
__( 'BnB check-in', 'wp-bnb' ),
|
|
array( self::class, 'tag_generator_date_checkin' ),
|
|
array( 'version' => '2' )
|
|
);
|
|
|
|
// Check-out date tag generator.
|
|
$tag_generator->add(
|
|
'bnb_date_checkout',
|
|
__( 'BnB check-out', 'wp-bnb' ),
|
|
array( self::class, 'tag_generator_date_checkout' ),
|
|
array( 'version' => '2' )
|
|
);
|
|
|
|
// Guests count tag generator.
|
|
$tag_generator->add(
|
|
'bnb_guests',
|
|
__( 'BnB guests', 'wp-bnb' ),
|
|
array( self::class, 'tag_generator_guests' ),
|
|
array( 'version' => '2' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Tag generator callback for building select.
|
|
*
|
|
* @param \WPCF7_ContactForm $contact_form Contact form object.
|
|
* @param array $options Tag generator options.
|
|
* @return void
|
|
*/
|
|
public static function tag_generator_building_select( $contact_form, $options = array() ): void {
|
|
$field_id = $options['content'] ?? 'wpcf7-tg-pane-bnb_building_select';
|
|
$field_type = 'bnb_building_select';
|
|
?>
|
|
<header class="description-box">
|
|
<h3><?php esc_html_e( 'BnB Building Select', 'wp-bnb' ); ?></h3>
|
|
<p><?php esc_html_e( 'Generates a dropdown to select a building. Use this to filter rooms by building.', 'wp-bnb' ); ?></p>
|
|
</header>
|
|
|
|
<div class="control-box">
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-required-legend">
|
|
<?php esc_html_e( 'Field type', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<label>
|
|
<input type="checkbox" name="required" aria-describedby="<?php echo esc_attr( $field_id ); ?>-required-legend" />
|
|
<?php esc_html_e( 'Required field', 'wp-bnb' ); ?>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-name-legend">
|
|
<?php esc_html_e( 'Name', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr( $field_id ); ?>-name"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-name-legend"
|
|
value="building" pattern="[A-Za-z][A-Za-z0-9_\-]*" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-first-as-label-legend">
|
|
<?php esc_html_e( 'First option label', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="first_as_label" class="option oneline"
|
|
id="<?php echo esc_attr( $field_id ); ?>-first-as-label"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-first-as-label-legend"
|
|
placeholder="<?php esc_attr_e( '-- Select Building --', 'wp-bnb' ); ?>" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-id-legend">
|
|
<?php esc_html_e( 'Id attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="id" class="idvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-id"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-id-legend" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-class-legend">
|
|
<?php esc_html_e( 'Class attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="class" class="classvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-class"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-class-legend" />
|
|
</fieldset>
|
|
</div>
|
|
|
|
<footer class="insert-box">
|
|
<div class="flex-container">
|
|
<input type="text" name="<?php echo esc_attr( $field_type ); ?>" class="tag code" readonly onfocus="this.select()" />
|
|
<button type="button" class="button button-primary tag-generator-insert-button">
|
|
<?php esc_html_e( 'Insert Tag', 'wp-bnb' ); ?>
|
|
</button>
|
|
</div>
|
|
<p class="mail-tag-tip">
|
|
<?php
|
|
printf(
|
|
/* translators: %s: mail tag */
|
|
esc_html__( 'Use this tag in the Mail tab: %s', 'wp-bnb' ),
|
|
'<strong><span class="mail-tag"></span></strong>'
|
|
);
|
|
?>
|
|
</p>
|
|
</footer>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Tag generator callback for room select.
|
|
*
|
|
* @param \WPCF7_ContactForm $contact_form Contact form object.
|
|
* @param array $options Tag generator options.
|
|
* @return void
|
|
*/
|
|
public static function tag_generator_room_select( $contact_form, $options = array() ): void {
|
|
$field_id = $options['content'] ?? 'wpcf7-tg-pane-bnb_room_select';
|
|
$field_type = 'bnb_room_select';
|
|
?>
|
|
<header class="description-box">
|
|
<h3><?php esc_html_e( 'BnB Room Select', 'wp-bnb' ); ?></h3>
|
|
<p><?php esc_html_e( 'Generates a dropdown to select a room. Rooms are grouped by building and include capacity information.', 'wp-bnb' ); ?></p>
|
|
</header>
|
|
|
|
<div class="control-box">
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-required-legend">
|
|
<?php esc_html_e( 'Field type', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<label>
|
|
<input type="checkbox" name="required" aria-describedby="<?php echo esc_attr( $field_id ); ?>-required-legend" checked />
|
|
<?php esc_html_e( 'Required field', 'wp-bnb' ); ?>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-name-legend">
|
|
<?php esc_html_e( 'Name', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr( $field_id ); ?>-name"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-name-legend"
|
|
value="room" pattern="[A-Za-z][A-Za-z0-9_\-]*" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-building-field-legend">
|
|
<?php esc_html_e( 'Building field name', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="building_field" class="option oneline"
|
|
id="<?php echo esc_attr( $field_id ); ?>-building-field"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-building-field-legend"
|
|
placeholder="building" />
|
|
<p class="description">
|
|
<?php esc_html_e( 'Enter the name of a building select field to filter rooms by selected building.', 'wp-bnb' ); ?>
|
|
</p>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-include-price-legend">
|
|
<?php esc_html_e( 'Display options', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<label>
|
|
<input type="checkbox" name="include_price" class="option" value="true"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-include-price-legend" />
|
|
<?php esc_html_e( 'Include price in room options', 'wp-bnb' ); ?>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-id-legend">
|
|
<?php esc_html_e( 'Id attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="id" class="idvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-id"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-id-legend" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-class-legend">
|
|
<?php esc_html_e( 'Class attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="class" class="classvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-class"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-class-legend" />
|
|
</fieldset>
|
|
</div>
|
|
|
|
<footer class="insert-box">
|
|
<div class="flex-container">
|
|
<input type="text" name="<?php echo esc_attr( $field_type ); ?>" class="tag code" readonly onfocus="this.select()" />
|
|
<button type="button" class="button button-primary tag-generator-insert-button">
|
|
<?php esc_html_e( 'Insert Tag', 'wp-bnb' ); ?>
|
|
</button>
|
|
</div>
|
|
<p class="mail-tag-tip">
|
|
<?php
|
|
printf(
|
|
/* translators: %s: mail tag */
|
|
esc_html__( 'Use this tag in the Mail tab: %s', 'wp-bnb' ),
|
|
'<strong><span class="mail-tag"></span></strong>'
|
|
);
|
|
?>
|
|
</p>
|
|
</footer>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Tag generator callback for check-in date.
|
|
*
|
|
* @param \WPCF7_ContactForm $contact_form Contact form object.
|
|
* @param array $options Tag generator options.
|
|
* @return void
|
|
*/
|
|
public static function tag_generator_date_checkin( $contact_form, $options = array() ): void {
|
|
$field_id = $options['content'] ?? 'wpcf7-tg-pane-bnb_date_checkin';
|
|
$field_type = 'bnb_date_checkin';
|
|
?>
|
|
<header class="description-box">
|
|
<h3><?php esc_html_e( 'BnB Check-in Date', 'wp-bnb' ); ?></h3>
|
|
<p><?php esc_html_e( 'Generates a date picker for check-in date selection with automatic validation.', 'wp-bnb' ); ?></p>
|
|
</header>
|
|
|
|
<div class="control-box">
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-required-legend">
|
|
<?php esc_html_e( 'Field type', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<label>
|
|
<input type="checkbox" name="required" aria-describedby="<?php echo esc_attr( $field_id ); ?>-required-legend" checked />
|
|
<?php esc_html_e( 'Required field', 'wp-bnb' ); ?>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-name-legend">
|
|
<?php esc_html_e( 'Name', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr( $field_id ); ?>-name"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-name-legend"
|
|
value="check_in" pattern="[A-Za-z][A-Za-z0-9_\-]*" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-min-advance-legend">
|
|
<?php esc_html_e( 'Minimum advance booking (days)', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="number" name="min_advance" class="option oneline" min="0" max="365"
|
|
id="<?php echo esc_attr( $field_id ); ?>-min-advance"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-min-advance-legend"
|
|
placeholder="0" />
|
|
<p class="description">
|
|
<?php esc_html_e( 'Minimum days in advance required for booking (0 = today).', 'wp-bnb' ); ?>
|
|
</p>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-max-advance-legend">
|
|
<?php esc_html_e( 'Maximum advance booking (days)', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="number" name="max_advance" class="option oneline" min="1" max="730"
|
|
id="<?php echo esc_attr( $field_id ); ?>-max-advance"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-max-advance-legend"
|
|
placeholder="365" />
|
|
<p class="description">
|
|
<?php esc_html_e( 'Maximum days in advance allowed for booking.', 'wp-bnb' ); ?>
|
|
</p>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-id-legend">
|
|
<?php esc_html_e( 'Id attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="id" class="idvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-id"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-id-legend" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-class-legend">
|
|
<?php esc_html_e( 'Class attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="class" class="classvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-class"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-class-legend" />
|
|
</fieldset>
|
|
</div>
|
|
|
|
<footer class="insert-box">
|
|
<div class="flex-container">
|
|
<input type="text" name="<?php echo esc_attr( $field_type ); ?>" class="tag code" readonly onfocus="this.select()" />
|
|
<button type="button" class="button button-primary tag-generator-insert-button">
|
|
<?php esc_html_e( 'Insert Tag', 'wp-bnb' ); ?>
|
|
</button>
|
|
</div>
|
|
<p class="mail-tag-tip">
|
|
<?php
|
|
printf(
|
|
/* translators: %s: mail tag */
|
|
esc_html__( 'Use this tag in the Mail tab: %s', 'wp-bnb' ),
|
|
'<strong><span class="mail-tag"></span></strong>'
|
|
);
|
|
?>
|
|
</p>
|
|
</footer>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Tag generator callback for check-out date.
|
|
*
|
|
* @param \WPCF7_ContactForm $contact_form Contact form object.
|
|
* @param array $options Tag generator options.
|
|
* @return void
|
|
*/
|
|
public static function tag_generator_date_checkout( $contact_form, $options = array() ): void {
|
|
$field_id = $options['content'] ?? 'wpcf7-tg-pane-bnb_date_checkout';
|
|
$field_type = 'bnb_date_checkout';
|
|
?>
|
|
<header class="description-box">
|
|
<h3><?php esc_html_e( 'BnB Check-out Date', 'wp-bnb' ); ?></h3>
|
|
<p><?php esc_html_e( 'Generates a date picker for check-out date selection with automatic validation against check-in.', 'wp-bnb' ); ?></p>
|
|
</header>
|
|
|
|
<div class="control-box">
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-required-legend">
|
|
<?php esc_html_e( 'Field type', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<label>
|
|
<input type="checkbox" name="required" aria-describedby="<?php echo esc_attr( $field_id ); ?>-required-legend" checked />
|
|
<?php esc_html_e( 'Required field', 'wp-bnb' ); ?>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-name-legend">
|
|
<?php esc_html_e( 'Name', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr( $field_id ); ?>-name"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-name-legend"
|
|
value="check_out" pattern="[A-Za-z][A-Za-z0-9_\-]*" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-checkin-field-legend">
|
|
<?php esc_html_e( 'Check-in field name', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="checkin_field" class="option oneline"
|
|
id="<?php echo esc_attr( $field_id ); ?>-checkin-field"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-checkin-field-legend"
|
|
placeholder="check_in" />
|
|
<p class="description">
|
|
<?php esc_html_e( 'Enter the name of the check-in date field for date validation.', 'wp-bnb' ); ?>
|
|
</p>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-min-nights-legend">
|
|
<?php esc_html_e( 'Minimum nights', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="number" name="min_nights" class="option oneline" min="1" max="365"
|
|
id="<?php echo esc_attr( $field_id ); ?>-min-nights"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-min-nights-legend"
|
|
placeholder="1" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-max-nights-legend">
|
|
<?php esc_html_e( 'Maximum nights', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="number" name="max_nights" class="option oneline" min="1" max="365"
|
|
id="<?php echo esc_attr( $field_id ); ?>-max-nights"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-max-nights-legend"
|
|
placeholder="365" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-id-legend">
|
|
<?php esc_html_e( 'Id attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="id" class="idvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-id"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-id-legend" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-class-legend">
|
|
<?php esc_html_e( 'Class attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="class" class="classvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-class"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-class-legend" />
|
|
</fieldset>
|
|
</div>
|
|
|
|
<footer class="insert-box">
|
|
<div class="flex-container">
|
|
<input type="text" name="<?php echo esc_attr( $field_type ); ?>" class="tag code" readonly onfocus="this.select()" />
|
|
<button type="button" class="button button-primary tag-generator-insert-button">
|
|
<?php esc_html_e( 'Insert Tag', 'wp-bnb' ); ?>
|
|
</button>
|
|
</div>
|
|
<p class="mail-tag-tip">
|
|
<?php
|
|
printf(
|
|
/* translators: %s: mail tag */
|
|
esc_html__( 'Use this tag in the Mail tab: %s', 'wp-bnb' ),
|
|
'<strong><span class="mail-tag"></span></strong>'
|
|
);
|
|
?>
|
|
</p>
|
|
</footer>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Tag generator callback for guests count.
|
|
*
|
|
* @param \WPCF7_ContactForm $contact_form Contact form object.
|
|
* @param array $options Tag generator options.
|
|
* @return void
|
|
*/
|
|
public static function tag_generator_guests( $contact_form, $options = array() ): void {
|
|
$field_id = $options['content'] ?? 'wpcf7-tg-pane-bnb_guests';
|
|
$field_type = 'bnb_guests';
|
|
?>
|
|
<header class="description-box">
|
|
<h3><?php esc_html_e( 'BnB Guests Count', 'wp-bnb' ); ?></h3>
|
|
<p><?php esc_html_e( 'Generates a number input for guest count with validation against room capacity.', 'wp-bnb' ); ?></p>
|
|
</header>
|
|
|
|
<div class="control-box">
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-required-legend">
|
|
<?php esc_html_e( 'Field type', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<label>
|
|
<input type="checkbox" name="required" aria-describedby="<?php echo esc_attr( $field_id ); ?>-required-legend" checked />
|
|
<?php esc_html_e( 'Required field', 'wp-bnb' ); ?>
|
|
</label>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-name-legend">
|
|
<?php esc_html_e( 'Name', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr( $field_id ); ?>-name"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-name-legend"
|
|
value="guests" pattern="[A-Za-z][A-Za-z0-9_\-]*" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-room-field-legend">
|
|
<?php esc_html_e( 'Room field name', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="room_field" class="option oneline"
|
|
id="<?php echo esc_attr( $field_id ); ?>-room-field"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-room-field-legend"
|
|
placeholder="room" />
|
|
<p class="description">
|
|
<?php esc_html_e( 'Enter the name of the room select field to validate against room capacity.', 'wp-bnb' ); ?>
|
|
</p>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-min-legend">
|
|
<?php esc_html_e( 'Minimum guests', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="number" name="min" class="option oneline" min="1" max="50"
|
|
id="<?php echo esc_attr( $field_id ); ?>-min"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-min-legend"
|
|
placeholder="1" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-max-legend">
|
|
<?php esc_html_e( 'Maximum guests', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="number" name="max" class="option oneline" min="1" max="50"
|
|
id="<?php echo esc_attr( $field_id ); ?>-max"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-max-legend"
|
|
placeholder="10" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-default-legend">
|
|
<?php esc_html_e( 'Default value', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="number" name="default" class="option oneline" min="1" max="50"
|
|
id="<?php echo esc_attr( $field_id ); ?>-default"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-default-legend"
|
|
placeholder="1" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-id-legend">
|
|
<?php esc_html_e( 'Id attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="id" class="idvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-id"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-id-legend" />
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend id="<?php echo esc_attr( $field_id ); ?>-class-legend">
|
|
<?php esc_html_e( 'Class attribute', 'wp-bnb' ); ?>
|
|
</legend>
|
|
<input type="text" name="class" class="classvalue oneline option"
|
|
id="<?php echo esc_attr( $field_id ); ?>-class"
|
|
aria-describedby="<?php echo esc_attr( $field_id ); ?>-class-legend" />
|
|
</fieldset>
|
|
</div>
|
|
|
|
<footer class="insert-box">
|
|
<div class="flex-container">
|
|
<input type="text" name="<?php echo esc_attr( $field_type ); ?>" class="tag code" readonly onfocus="this.select()" />
|
|
<button type="button" class="button button-primary tag-generator-insert-button">
|
|
<?php esc_html_e( 'Insert Tag', 'wp-bnb' ); ?>
|
|
</button>
|
|
</div>
|
|
<p class="mail-tag-tip">
|
|
<?php
|
|
printf(
|
|
/* translators: %s: mail tag */
|
|
esc_html__( 'Use this tag in the Mail tab: %s', 'wp-bnb' ),
|
|
'<strong><span class="mail-tag"></span></strong>'
|
|
);
|
|
?>
|
|
</p>
|
|
</footer>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render building select tag.
|
|
*
|
|
* @param \WPCF7_FormTag $tag Form tag object.
|
|
* @return string HTML output.
|
|
*/
|
|
public static function render_building_select_tag( $tag ): string {
|
|
if ( empty( $tag->name ) ) {
|
|
return '';
|
|
}
|
|
|
|
$validation_error = wpcf7_get_validation_error( $tag->name );
|
|
$class = wpcf7_form_controls_class( $tag->type );
|
|
|
|
if ( $validation_error ) {
|
|
$class .= ' wpcf7-not-valid';
|
|
}
|
|
|
|
$atts = array(
|
|
'class' => trim( $class . ' wp-bnb-building-select' ),
|
|
'id' => $tag->get_id_option(),
|
|
'name' => $tag->name,
|
|
'aria-required' => $tag->is_required() ? 'true' : 'false',
|
|
'aria-invalid' => $validation_error ? 'true' : 'false',
|
|
'data-bnb-building-select' => 'true',
|
|
);
|
|
|
|
// Get first_as_label option.
|
|
$first_label = __( '-- Select Building --', 'wp-bnb' );
|
|
foreach ( $tag->options as $option ) {
|
|
if ( strpos( $option, 'first_as_label:' ) === 0 ) {
|
|
$first_label = str_replace( array( 'first_as_label:', '"', "'" ), '', $option );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get buildings that have rooms.
|
|
$buildings = get_posts(
|
|
array(
|
|
'post_type' => Building::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => -1,
|
|
'orderby' => 'title',
|
|
'order' => 'ASC',
|
|
)
|
|
);
|
|
|
|
// Build options.
|
|
$options = sprintf( '<option value="">%s</option>', esc_html( $first_label ) );
|
|
|
|
foreach ( $buildings as $building ) {
|
|
$room_count = count( Room::get_rooms_for_building( $building->ID ) );
|
|
if ( $room_count > 0 ) {
|
|
$options .= sprintf(
|
|
'<option value="%d">%s</option>',
|
|
esc_attr( $building->ID ),
|
|
esc_html( $building->post_title )
|
|
);
|
|
}
|
|
}
|
|
|
|
$atts_html = wpcf7_format_atts( $atts );
|
|
|
|
$html = sprintf( '<span class="wpcf7-form-control-wrap" data-name="%s">', esc_attr( $tag->name ) );
|
|
$html .= sprintf( '<select %s>%s</select>', $atts_html, $options );
|
|
$html .= $validation_error;
|
|
$html .= '</span>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Render room select tag.
|
|
*
|
|
* @param \WPCF7_FormTag $tag Form tag object.
|
|
* @return string HTML output.
|
|
*/
|
|
public static function render_room_select_tag( $tag ): string {
|
|
if ( empty( $tag->name ) ) {
|
|
return '';
|
|
}
|
|
|
|
$validation_error = wpcf7_get_validation_error( $tag->name );
|
|
$class = wpcf7_form_controls_class( $tag->type );
|
|
|
|
if ( $validation_error ) {
|
|
$class .= ' wpcf7-not-valid';
|
|
}
|
|
|
|
// Parse options.
|
|
$building_field = '';
|
|
$include_price = false;
|
|
|
|
foreach ( $tag->options as $option ) {
|
|
if ( strpos( $option, 'building_field:' ) === 0 ) {
|
|
$building_field = str_replace( array( 'building_field:', '"', "'" ), '', $option );
|
|
}
|
|
if ( 'include_price:true' === $option || 'include_price' === $option ) {
|
|
$include_price = true;
|
|
}
|
|
}
|
|
|
|
$atts = array(
|
|
'class' => trim( $class . ' wp-bnb-room-select' ),
|
|
'id' => $tag->get_id_option(),
|
|
'name' => $tag->name,
|
|
'aria-required' => $tag->is_required() ? 'true' : 'false',
|
|
'aria-invalid' => $validation_error ? 'true' : 'false',
|
|
'data-bnb-room-select' => 'true',
|
|
);
|
|
|
|
if ( $building_field ) {
|
|
$atts['data-building-field'] = $building_field;
|
|
}
|
|
|
|
if ( $tag->is_required() ) {
|
|
$atts['required'] = 'required';
|
|
}
|
|
|
|
// Get all rooms grouped by building.
|
|
$rooms = get_posts(
|
|
array(
|
|
'post_type' => Room::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => -1,
|
|
'orderby' => 'title',
|
|
'order' => 'ASC',
|
|
)
|
|
);
|
|
|
|
$rooms_by_building = array();
|
|
foreach ( $rooms as $room ) {
|
|
$building = Room::get_building( $room->ID );
|
|
$building_id = $building ? $building->ID : 0;
|
|
|
|
if ( ! isset( $rooms_by_building[ $building_id ] ) ) {
|
|
$rooms_by_building[ $building_id ] = array(
|
|
'name' => $building ? $building->post_title : __( 'No Building', 'wp-bnb' ),
|
|
'rooms' => array(),
|
|
);
|
|
}
|
|
|
|
$rooms_by_building[ $building_id ]['rooms'][] = $room;
|
|
}
|
|
|
|
// Build options.
|
|
$options = sprintf( '<option value="">%s</option>', esc_html__( '-- Select Room --', 'wp-bnb' ) );
|
|
$currency = get_option( 'wp_bnb_currency', 'CHF' );
|
|
|
|
foreach ( $rooms_by_building as $building_id => $data ) {
|
|
$options .= sprintf( '<optgroup label="%s">', esc_attr( $data['name'] ) );
|
|
|
|
foreach ( $data['rooms'] as $room ) {
|
|
$capacity = (int) get_post_meta( $room->ID, '_bnb_room_capacity', true ) ?: 2;
|
|
$nightly_rate = (float) get_post_meta( $room->ID, '_bnb_room_price_short_term', true );
|
|
$room_status = get_post_meta( $room->ID, '_bnb_room_status', true ) ?: 'available';
|
|
|
|
// Skip rooms that are not available.
|
|
if ( 'available' !== $room_status ) {
|
|
continue;
|
|
}
|
|
|
|
$label = $room->post_title;
|
|
$label .= sprintf( ' (%d %s)', $capacity, _n( 'guest', 'guests', $capacity, 'wp-bnb' ) );
|
|
|
|
if ( $include_price && $nightly_rate > 0 ) {
|
|
$label .= sprintf( ' - %s %s/%s', $currency, number_format( $nightly_rate, 2 ), __( 'night', 'wp-bnb' ) );
|
|
}
|
|
|
|
$options .= sprintf(
|
|
'<option value="%d" data-capacity="%d" data-building="%d" data-price="%s">%s</option>',
|
|
esc_attr( $room->ID ),
|
|
esc_attr( $capacity ),
|
|
esc_attr( $building_id ),
|
|
esc_attr( $nightly_rate ),
|
|
esc_html( $label )
|
|
);
|
|
}
|
|
|
|
$options .= '</optgroup>';
|
|
}
|
|
|
|
$atts_html = wpcf7_format_atts( $atts );
|
|
|
|
$html = sprintf( '<span class="wpcf7-form-control-wrap" data-name="%s">', esc_attr( $tag->name ) );
|
|
$html .= sprintf( '<select %s>%s</select>', $atts_html, $options );
|
|
$html .= $validation_error;
|
|
$html .= '</span>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Render check-in date tag.
|
|
*
|
|
* @param \WPCF7_FormTag $tag Form tag object.
|
|
* @return string HTML output.
|
|
*/
|
|
public static function render_date_checkin_tag( $tag ): string {
|
|
if ( empty( $tag->name ) ) {
|
|
return '';
|
|
}
|
|
|
|
$validation_error = wpcf7_get_validation_error( $tag->name );
|
|
$class = wpcf7_form_controls_class( $tag->type, 'wpcf7-date' );
|
|
|
|
if ( $validation_error ) {
|
|
$class .= ' wpcf7-not-valid';
|
|
}
|
|
|
|
// Parse options.
|
|
$min_advance = 0;
|
|
$max_advance = 365;
|
|
|
|
foreach ( $tag->options as $option ) {
|
|
if ( strpos( $option, 'min_advance:' ) === 0 ) {
|
|
$min_advance = (int) str_replace( 'min_advance:', '', $option );
|
|
}
|
|
if ( strpos( $option, 'max_advance:' ) === 0 ) {
|
|
$max_advance = (int) str_replace( 'max_advance:', '', $option );
|
|
}
|
|
}
|
|
|
|
$min_date = gmdate( 'Y-m-d', strtotime( "+{$min_advance} days" ) );
|
|
$max_date = gmdate( 'Y-m-d', strtotime( "+{$max_advance} days" ) );
|
|
|
|
$atts = array(
|
|
'type' => 'date',
|
|
'class' => trim( $class . ' wp-bnb-date-checkin' ),
|
|
'id' => $tag->get_id_option(),
|
|
'name' => $tag->name,
|
|
'min' => $min_date,
|
|
'max' => $max_date,
|
|
'aria-required' => $tag->is_required() ? 'true' : 'false',
|
|
'aria-invalid' => $validation_error ? 'true' : 'false',
|
|
'data-bnb-checkin' => 'true',
|
|
);
|
|
|
|
if ( $tag->is_required() ) {
|
|
$atts['required'] = 'required';
|
|
}
|
|
|
|
// Handle default value from POST.
|
|
if ( isset( $_POST[ $tag->name ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
$atts['value'] = sanitize_text_field( wp_unslash( $_POST[ $tag->name ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
}
|
|
|
|
$atts_html = wpcf7_format_atts( $atts );
|
|
|
|
$html = sprintf( '<span class="wpcf7-form-control-wrap" data-name="%s">', esc_attr( $tag->name ) );
|
|
$html .= sprintf( '<input %s />', $atts_html );
|
|
$html .= $validation_error;
|
|
$html .= '</span>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Render check-out date tag.
|
|
*
|
|
* @param \WPCF7_FormTag $tag Form tag object.
|
|
* @return string HTML output.
|
|
*/
|
|
public static function render_date_checkout_tag( $tag ): string {
|
|
if ( empty( $tag->name ) ) {
|
|
return '';
|
|
}
|
|
|
|
$validation_error = wpcf7_get_validation_error( $tag->name );
|
|
$class = wpcf7_form_controls_class( $tag->type, 'wpcf7-date' );
|
|
|
|
if ( $validation_error ) {
|
|
$class .= ' wpcf7-not-valid';
|
|
}
|
|
|
|
// Parse options.
|
|
$checkin_field = 'check_in';
|
|
$min_nights = 1;
|
|
$max_nights = 365;
|
|
|
|
foreach ( $tag->options as $option ) {
|
|
if ( strpos( $option, 'checkin_field:' ) === 0 ) {
|
|
$checkin_field = str_replace( array( 'checkin_field:', '"', "'" ), '', $option );
|
|
}
|
|
if ( strpos( $option, 'min_nights:' ) === 0 ) {
|
|
$min_nights = (int) str_replace( 'min_nights:', '', $option );
|
|
}
|
|
if ( strpos( $option, 'max_nights:' ) === 0 ) {
|
|
$max_nights = (int) str_replace( 'max_nights:', '', $option );
|
|
}
|
|
}
|
|
|
|
// Default min is tomorrow.
|
|
$min_date = gmdate( 'Y-m-d', strtotime( '+1 day' ) );
|
|
$max_date = gmdate( 'Y-m-d', strtotime( '+366 days' ) );
|
|
|
|
$atts = array(
|
|
'type' => 'date',
|
|
'class' => trim( $class . ' wp-bnb-date-checkout' ),
|
|
'id' => $tag->get_id_option(),
|
|
'name' => $tag->name,
|
|
'min' => $min_date,
|
|
'max' => $max_date,
|
|
'aria-required' => $tag->is_required() ? 'true' : 'false',
|
|
'aria-invalid' => $validation_error ? 'true' : 'false',
|
|
'data-bnb-checkout' => 'true',
|
|
'data-checkin-field' => $checkin_field,
|
|
'data-min-nights' => $min_nights,
|
|
'data-max-nights' => $max_nights,
|
|
);
|
|
|
|
if ( $tag->is_required() ) {
|
|
$atts['required'] = 'required';
|
|
}
|
|
|
|
// Handle default value from POST.
|
|
if ( isset( $_POST[ $tag->name ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
$atts['value'] = sanitize_text_field( wp_unslash( $_POST[ $tag->name ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
}
|
|
|
|
$atts_html = wpcf7_format_atts( $atts );
|
|
|
|
$html = sprintf( '<span class="wpcf7-form-control-wrap" data-name="%s">', esc_attr( $tag->name ) );
|
|
$html .= sprintf( '<input %s />', $atts_html );
|
|
$html .= $validation_error;
|
|
$html .= '</span>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Render guests count tag.
|
|
*
|
|
* @param \WPCF7_FormTag $tag Form tag object.
|
|
* @return string HTML output.
|
|
*/
|
|
public static function render_guests_tag( $tag ): string {
|
|
if ( empty( $tag->name ) ) {
|
|
return '';
|
|
}
|
|
|
|
$validation_error = wpcf7_get_validation_error( $tag->name );
|
|
$class = wpcf7_form_controls_class( $tag->type, 'wpcf7-number' );
|
|
|
|
if ( $validation_error ) {
|
|
$class .= ' wpcf7-not-valid';
|
|
}
|
|
|
|
// Parse options.
|
|
$min = 1;
|
|
$max = 10;
|
|
$default = 1;
|
|
$room_field = '';
|
|
|
|
foreach ( $tag->options as $option ) {
|
|
if ( strpos( $option, 'min:' ) === 0 ) {
|
|
$min = (int) str_replace( 'min:', '', $option );
|
|
}
|
|
if ( strpos( $option, 'max:' ) === 0 ) {
|
|
$max = (int) str_replace( 'max:', '', $option );
|
|
}
|
|
if ( strpos( $option, 'default:' ) === 0 ) {
|
|
$default = (int) str_replace( 'default:', '', $option );
|
|
}
|
|
if ( strpos( $option, 'room_field:' ) === 0 ) {
|
|
$room_field = str_replace( array( 'room_field:', '"', "'" ), '', $option );
|
|
}
|
|
}
|
|
|
|
$atts = array(
|
|
'type' => 'number',
|
|
'class' => trim( $class . ' wp-bnb-guests' ),
|
|
'id' => $tag->get_id_option(),
|
|
'name' => $tag->name,
|
|
'min' => $min,
|
|
'max' => $max,
|
|
'value' => $default,
|
|
'aria-required' => $tag->is_required() ? 'true' : 'false',
|
|
'aria-invalid' => $validation_error ? 'true' : 'false',
|
|
'data-bnb-guests' => 'true',
|
|
);
|
|
|
|
if ( $room_field ) {
|
|
$atts['data-room-field'] = $room_field;
|
|
}
|
|
|
|
if ( $tag->is_required() ) {
|
|
$atts['required'] = 'required';
|
|
}
|
|
|
|
// Handle default value from POST.
|
|
if ( isset( $_POST[ $tag->name ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
$atts['value'] = absint( $_POST[ $tag->name ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
}
|
|
|
|
$atts_html = wpcf7_format_atts( $atts );
|
|
|
|
$html = sprintf( '<span class="wpcf7-form-control-wrap" data-name="%s">', esc_attr( $tag->name ) );
|
|
$html .= sprintf( '<input %s />', $atts_html );
|
|
$html .= $validation_error;
|
|
$html .= '</span>';
|
|
|
|
return $html;
|
|
}
|
|
|
|
/**
|
|
* Validate room select field.
|
|
*
|
|
* @param \WPCF7_Validation $result Validation result.
|
|
* @param \WPCF7_FormTag $tag Form tag.
|
|
* @return \WPCF7_Validation
|
|
*/
|
|
public static function validate_room_select( $result, $tag ) {
|
|
$name = $tag->name;
|
|
$room_id = isset( $_POST[ $name ] ) ? absint( $_POST[ $name ] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
|
|
if ( $tag->is_required() && ! $room_id ) {
|
|
$result->invalidate( $tag, __( 'Please select a room.', 'wp-bnb' ) );
|
|
return $result;
|
|
}
|
|
|
|
if ( $room_id ) {
|
|
$room = get_post( $room_id );
|
|
if ( ! $room || Room::POST_TYPE !== $room->post_type ) {
|
|
$result->invalidate( $tag, __( 'Invalid room selected.', 'wp-bnb' ) );
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Validate check-in date field.
|
|
*
|
|
* @param \WPCF7_Validation $result Validation result.
|
|
* @param \WPCF7_FormTag $tag Form tag.
|
|
* @return \WPCF7_Validation
|
|
*/
|
|
public static function validate_date_checkin( $result, $tag ) {
|
|
$name = $tag->name;
|
|
$date = isset( $_POST[ $name ] ) ? sanitize_text_field( wp_unslash( $_POST[ $name ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
|
|
if ( $tag->is_required() && empty( $date ) ) {
|
|
$result->invalidate( $tag, __( 'Please select a check-in date.', 'wp-bnb' ) );
|
|
return $result;
|
|
}
|
|
|
|
if ( $date ) {
|
|
// Validate format.
|
|
if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) ) {
|
|
$result->invalidate( $tag, __( 'Invalid date format.', 'wp-bnb' ) );
|
|
return $result;
|
|
}
|
|
|
|
// Validate not in past.
|
|
$check_in = strtotime( $date );
|
|
$today = strtotime( 'today' );
|
|
|
|
if ( $check_in < $today ) {
|
|
$result->invalidate( $tag, __( 'Check-in date cannot be in the past.', 'wp-bnb' ) );
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Validate check-out date field.
|
|
*
|
|
* @param \WPCF7_Validation $result Validation result.
|
|
* @param \WPCF7_FormTag $tag Form tag.
|
|
* @return \WPCF7_Validation
|
|
*/
|
|
public static function validate_date_checkout( $result, $tag ) {
|
|
$name = $tag->name;
|
|
$check_out = isset( $_POST[ $name ] ) ? sanitize_text_field( wp_unslash( $_POST[ $name ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
|
|
// Try common check-in field names.
|
|
$check_in = '';
|
|
$checkin_fields = array( 'check_in', 'checkin', 'check-in', 'arrival' );
|
|
foreach ( $checkin_fields as $field ) {
|
|
if ( isset( $_POST[ $field ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
$check_in = sanitize_text_field( wp_unslash( $_POST[ $field ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( $tag->is_required() && empty( $check_out ) ) {
|
|
$result->invalidate( $tag, __( 'Please select a check-out date.', 'wp-bnb' ) );
|
|
return $result;
|
|
}
|
|
|
|
if ( $check_out ) {
|
|
// Validate format.
|
|
if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $check_out ) ) {
|
|
$result->invalidate( $tag, __( 'Invalid date format.', 'wp-bnb' ) );
|
|
return $result;
|
|
}
|
|
|
|
// Validate check-out is after check-in.
|
|
if ( $check_in && strtotime( $check_out ) <= strtotime( $check_in ) ) {
|
|
$result->invalidate( $tag, __( 'Check-out must be after check-in.', 'wp-bnb' ) );
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Validate guests field.
|
|
*
|
|
* @param \WPCF7_Validation $result Validation result.
|
|
* @param \WPCF7_FormTag $tag Form tag.
|
|
* @return \WPCF7_Validation
|
|
*/
|
|
public static function validate_guests( $result, $tag ) {
|
|
$name = $tag->name;
|
|
$guests = isset( $_POST[ $name ] ) ? absint( $_POST[ $name ] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
|
|
if ( $tag->is_required() && $guests < 1 ) {
|
|
$result->invalidate( $tag, __( 'Please enter number of guests.', 'wp-bnb' ) );
|
|
return $result;
|
|
}
|
|
|
|
// Try to get room_id for capacity validation.
|
|
$room_id = 0;
|
|
$room_fields = array( 'room', 'room_id', 'room-id' );
|
|
foreach ( $room_fields as $field ) {
|
|
if ( isset( $_POST[ $field ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
$room_id = absint( $_POST[ $field ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Validate against room capacity if room is selected.
|
|
if ( $room_id && $guests > 0 ) {
|
|
$capacity = (int) get_post_meta( $room_id, '_bnb_room_capacity', true );
|
|
if ( $capacity > 0 && $guests > $capacity ) {
|
|
$result->invalidate(
|
|
$tag,
|
|
sprintf(
|
|
/* translators: %d: Maximum guest capacity */
|
|
__( 'This room has a maximum capacity of %d guests.', 'wp-bnb' ),
|
|
$capacity
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Validate availability before sending mail.
|
|
*
|
|
* @param \WPCF7_ContactForm $contact_form Contact form object.
|
|
* @param bool $abort Whether to abort.
|
|
* @param \WPCF7_Submission $submission Submission object.
|
|
* @return void
|
|
*/
|
|
public static function validate_availability_before_mail( $contact_form, &$abort, $submission ): void {
|
|
if ( ! self::is_booking_form( $contact_form ) ) {
|
|
return;
|
|
}
|
|
|
|
$posted_data = $submission->get_posted_data();
|
|
|
|
// Get room and dates.
|
|
$room_id = self::get_field_value( $posted_data, array( 'room', 'room_id', 'room-id' ), 'int' );
|
|
$check_in = self::get_field_value( $posted_data, array( 'check_in', 'checkin', 'check-in', 'arrival' ) );
|
|
$check_out = self::get_field_value( $posted_data, array( 'check_out', 'checkout', 'check-out', 'departure' ) );
|
|
|
|
if ( ! $room_id || ! $check_in || ! $check_out ) {
|
|
return;
|
|
}
|
|
|
|
// Check availability.
|
|
if ( ! Availability::is_available( $room_id, $check_in, $check_out ) ) {
|
|
$abort = true;
|
|
$submission->set_status( 'validation_failed' );
|
|
$submission->set_response(
|
|
__( 'Sorry, this room is not available for the selected dates. Please choose different dates.', 'wp-bnb' )
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle mail sent event - create booking.
|
|
*
|
|
* @param \WPCF7_ContactForm $contact_form Contact form object.
|
|
* @return void
|
|
*/
|
|
public static function on_mail_sent( $contact_form ): void {
|
|
if ( ! self::is_booking_form( $contact_form ) ) {
|
|
return;
|
|
}
|
|
|
|
$submission = \WPCF7_Submission::get_instance();
|
|
if ( ! $submission ) {
|
|
return;
|
|
}
|
|
|
|
$posted_data = $submission->get_posted_data();
|
|
|
|
// Extract booking data.
|
|
$room_id = self::get_field_value( $posted_data, array( 'room', 'room_id', 'room-id' ), 'int' );
|
|
$check_in = self::get_field_value( $posted_data, array( 'check_in', 'checkin', 'check-in', 'arrival' ) );
|
|
$check_out = self::get_field_value( $posted_data, array( 'check_out', 'checkout', 'check-out', 'departure' ) );
|
|
|
|
if ( ! $room_id || ! $check_in || ! $check_out ) {
|
|
return;
|
|
}
|
|
|
|
// Guest data.
|
|
$guest_name = self::get_field_value( $posted_data, array( 'your-name', 'name', 'guest_name', 'guest-name' ) );
|
|
$guest_email = self::get_field_value( $posted_data, array( 'your-email', 'email', 'guest_email', 'guest-email' ) );
|
|
$guest_phone = self::get_field_value( $posted_data, array( 'your-phone', 'phone', 'tel', 'guest_phone', 'guest-phone' ) );
|
|
|
|
// Guest counts.
|
|
$guests = self::get_field_value( $posted_data, array( 'guests', 'guest_count', 'guest-count' ), 'int' );
|
|
$adults = self::get_field_value( $posted_data, array( 'adults', 'adult_count' ), 'int' );
|
|
$children = self::get_field_value( $posted_data, array( 'children', 'child_count' ), 'int' );
|
|
|
|
// If single guests field, use it as adults.
|
|
if ( $guests && ! $adults ) {
|
|
$adults = $guests;
|
|
}
|
|
if ( ! $adults ) {
|
|
$adults = 1;
|
|
}
|
|
if ( ! $children ) {
|
|
$children = 0;
|
|
}
|
|
|
|
// Notes from message field.
|
|
$notes = self::get_field_value( $posted_data, array( 'your-message', 'message', 'notes', 'special_requests', 'special-requests' ) );
|
|
|
|
// Create the booking.
|
|
$booking_id = self::create_booking(
|
|
array(
|
|
'room_id' => $room_id,
|
|
'check_in' => $check_in,
|
|
'check_out' => $check_out,
|
|
'guest_name' => $guest_name,
|
|
'guest_email' => $guest_email,
|
|
'guest_phone' => $guest_phone,
|
|
'adults' => $adults,
|
|
'children' => $children,
|
|
'notes' => $notes,
|
|
'source' => 'cf7_form_' . $contact_form->id(),
|
|
)
|
|
);
|
|
|
|
if ( $booking_id ) {
|
|
// Store booking ID for potential use in mail tags.
|
|
$submission->add_extra_var( 'bnb_booking_id', (string) $booking_id );
|
|
$submission->add_extra_var( 'bnb_booking_reference', get_the_title( $booking_id ) );
|
|
|
|
/**
|
|
* Fires after a booking is created from a CF7 form.
|
|
*
|
|
* @param int $booking_id Created booking post ID.
|
|
* @param \WPCF7_ContactForm $contact_form CF7 form object.
|
|
* @param array $posted_data Form submission data.
|
|
*/
|
|
do_action( 'wp_bnb_cf7_booking_created', $booking_id, $contact_form, $posted_data );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a booking from form data.
|
|
*
|
|
* @param array $data Booking data.
|
|
* @return int|false Booking post ID or false on failure.
|
|
*/
|
|
private static function create_booking( array $data ) {
|
|
// Find or create guest.
|
|
$guest_id = null;
|
|
if ( ! empty( $data['guest_name'] ) ) {
|
|
$guest_id = self::find_or_create_guest(
|
|
$data['guest_name'],
|
|
$data['guest_email'] ?? '',
|
|
$data['guest_phone'] ?? ''
|
|
);
|
|
}
|
|
|
|
// Calculate price.
|
|
$price = 0;
|
|
$breakdown = array();
|
|
if ( $data['room_id'] && $data['check_in'] && $data['check_out'] ) {
|
|
try {
|
|
$calculator = new Calculator( $data['room_id'], $data['check_in'], $data['check_out'] );
|
|
$price = $calculator->calculate();
|
|
$breakdown = $calculator->getBreakdown();
|
|
} catch ( \Exception $e ) {
|
|
// Price calculation failed, continue without price.
|
|
}
|
|
}
|
|
|
|
// Generate booking reference.
|
|
$reference = Booking::generate_reference();
|
|
|
|
// Create booking post.
|
|
$booking_id = wp_insert_post(
|
|
array(
|
|
'post_type' => Booking::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'post_title' => $reference,
|
|
)
|
|
);
|
|
|
|
if ( is_wp_error( $booking_id ) || ! $booking_id ) {
|
|
return false;
|
|
}
|
|
|
|
// Save booking meta.
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'room_id', $data['room_id'] );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'check_in', $data['check_in'] );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'check_out', $data['check_out'] );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'status', 'pending' );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'guest_name', $data['guest_name'] ?? '' );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'guest_email', $data['guest_email'] ?? '' );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'guest_phone', $data['guest_phone'] ?? '' );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'adults', $data['adults'] ?? 1 );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'children', $data['children'] ?? 0 );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'guest_notes', $data['notes'] ?? '' );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'calculated_price', $price );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'price_breakdown', $breakdown );
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'source', $data['source'] ?? 'cf7' );
|
|
|
|
if ( $guest_id ) {
|
|
update_post_meta( $booking_id, self::META_PREFIX . 'guest_id', $guest_id );
|
|
}
|
|
|
|
// Generate comprehensive title (Guest Name (dates)).
|
|
self::update_booking_title( $booking_id, $data );
|
|
|
|
// Trigger the booking status changed action (for email notifications).
|
|
do_action( 'wp_bnb_booking_status_changed', $booking_id, '', 'pending' );
|
|
|
|
return $booking_id;
|
|
}
|
|
|
|
/**
|
|
* Update booking title with guest name and dates.
|
|
*
|
|
* @param int $booking_id Booking post ID.
|
|
* @param array $data Booking data.
|
|
* @return void
|
|
*/
|
|
private static function update_booking_title( int $booking_id, array $data ): void {
|
|
$guest_name = $data['guest_name'] ?? __( 'Unknown Guest', 'wp-bnb' );
|
|
|
|
// Format dates.
|
|
$date_part = '';
|
|
if ( ! empty( $data['check_in'] ) && ! empty( $data['check_out'] ) ) {
|
|
$check_in_date = \DateTime::createFromFormat( 'Y-m-d', $data['check_in'] );
|
|
$check_out_date = \DateTime::createFromFormat( 'Y-m-d', $data['check_out'] );
|
|
|
|
if ( $check_in_date && $check_out_date ) {
|
|
if ( $check_in_date->format( 'Y' ) === $check_out_date->format( 'Y' ) ) {
|
|
$date_part = sprintf(
|
|
'%s - %s',
|
|
$check_in_date->format( 'd.m' ),
|
|
$check_out_date->format( 'd.m.Y' )
|
|
);
|
|
} else {
|
|
$date_part = sprintf(
|
|
'%s - %s',
|
|
$check_in_date->format( 'd.m.Y' ),
|
|
$check_out_date->format( 'd.m.Y' )
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$title = $guest_name;
|
|
if ( $date_part ) {
|
|
$title .= sprintf( ' (%s)', $date_part );
|
|
}
|
|
|
|
// Update the post title directly.
|
|
global $wpdb;
|
|
$wpdb->update(
|
|
$wpdb->posts,
|
|
array( 'post_title' => $title ),
|
|
array( 'ID' => $booking_id ),
|
|
array( '%s' ),
|
|
array( '%d' )
|
|
);
|
|
|
|
clean_post_cache( $booking_id );
|
|
}
|
|
|
|
/**
|
|
* Find an existing guest by email or create a new one.
|
|
*
|
|
* @param string $name Guest full name.
|
|
* @param string $email Guest email.
|
|
* @param string $phone Guest phone (optional).
|
|
* @return int|null Guest post ID or null on failure.
|
|
*/
|
|
private static function find_or_create_guest( string $name, string $email, string $phone = '' ): ?int {
|
|
if ( empty( $name ) ) {
|
|
return null;
|
|
}
|
|
|
|
// Try to find existing guest by email.
|
|
if ( ! empty( $email ) ) {
|
|
$existing_guest = Guest::get_by_email( $email );
|
|
if ( $existing_guest ) {
|
|
return $existing_guest->ID;
|
|
}
|
|
}
|
|
|
|
// Parse name into first/last name.
|
|
$name_parts = explode( ' ', trim( $name ), 2 );
|
|
$first_name = $name_parts[0] ?? '';
|
|
$last_name = $name_parts[1] ?? '';
|
|
|
|
// Create new guest post.
|
|
$guest_id = wp_insert_post(
|
|
array(
|
|
'post_type' => Guest::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'post_title' => $name,
|
|
)
|
|
);
|
|
|
|
if ( is_wp_error( $guest_id ) || ! $guest_id ) {
|
|
return null;
|
|
}
|
|
|
|
// Save guest meta.
|
|
update_post_meta( $guest_id, '_bnb_guest_first_name', $first_name );
|
|
update_post_meta( $guest_id, '_bnb_guest_last_name', $last_name );
|
|
|
|
if ( ! empty( $email ) ) {
|
|
update_post_meta( $guest_id, '_bnb_guest_email', $email );
|
|
}
|
|
|
|
if ( ! empty( $phone ) ) {
|
|
update_post_meta( $guest_id, '_bnb_guest_phone', $phone );
|
|
}
|
|
|
|
// Set default status.
|
|
update_post_meta( $guest_id, '_bnb_guest_status', 'active' );
|
|
|
|
return $guest_id;
|
|
}
|
|
|
|
/**
|
|
* Check if a contact form is a booking form.
|
|
*
|
|
* @param \WPCF7_ContactForm $contact_form Contact form object.
|
|
* @return bool
|
|
*/
|
|
private static function is_booking_form( $contact_form ): bool {
|
|
// Check for CSS class.
|
|
$additional_settings = $contact_form->additional_setting( 'class', false );
|
|
if ( is_array( $additional_settings ) ) {
|
|
foreach ( $additional_settings as $setting ) {
|
|
if ( strpos( $setting, self::BOOKING_FORM_CLASS ) !== false ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Auto-detect by checking for required booking fields.
|
|
$form_content = $contact_form->prop( 'form' );
|
|
$has_room = strpos( $form_content, '[bnb_room_select' ) !== false;
|
|
$has_checkin = strpos( $form_content, '[bnb_date_checkin' ) !== false;
|
|
$has_checkout = strpos( $form_content, '[bnb_date_checkout' ) !== false;
|
|
|
|
return $has_room && $has_checkin && $has_checkout;
|
|
}
|
|
|
|
/**
|
|
* Get field value from posted data.
|
|
*
|
|
* @param array $posted_data Posted form data.
|
|
* @param array $field_names Possible field names.
|
|
* @param string $type Type cast ('int', 'string').
|
|
* @return mixed
|
|
*/
|
|
private static function get_field_value( array $posted_data, array $field_names, string $type = 'string' ) {
|
|
foreach ( $field_names as $field ) {
|
|
if ( isset( $posted_data[ $field ] ) && '' !== $posted_data[ $field ] ) {
|
|
$value = is_array( $posted_data[ $field ] )
|
|
? $posted_data[ $field ][0]
|
|
: $posted_data[ $field ];
|
|
|
|
if ( 'int' === $type ) {
|
|
return absint( $value );
|
|
}
|
|
|
|
return sanitize_text_field( $value );
|
|
}
|
|
}
|
|
|
|
return 'int' === $type ? 0 : '';
|
|
}
|
|
|
|
/**
|
|
* Handle custom mail tags.
|
|
*
|
|
* @param string|null $output Output value.
|
|
* @param string $name Tag name.
|
|
* @param bool $html Whether HTML is allowed.
|
|
* @param array $mail_tag Mail tag data.
|
|
* @return string|null
|
|
*/
|
|
public static function custom_mail_tags( $output, $name, $html, $mail_tag ) {
|
|
$submission = \WPCF7_Submission::get_instance();
|
|
if ( ! $submission ) {
|
|
return $output;
|
|
}
|
|
|
|
switch ( $name ) {
|
|
case '_bnb_booking_reference':
|
|
return $submission->get_extra_var( 'bnb_booking_reference' ) ?: '';
|
|
|
|
case '_bnb_booking_id':
|
|
return $submission->get_extra_var( 'bnb_booking_id' ) ?: '';
|
|
|
|
case '_bnb_room_name':
|
|
$posted_data = $submission->get_posted_data();
|
|
$room_id = self::get_field_value( $posted_data, array( 'room', 'room_id', 'room-id' ), 'int' );
|
|
if ( $room_id ) {
|
|
$room = get_post( $room_id );
|
|
return $room ? $room->post_title : '';
|
|
}
|
|
return '';
|
|
|
|
case '_bnb_calculated_price':
|
|
$posted_data = $submission->get_posted_data();
|
|
$room_id = self::get_field_value( $posted_data, array( 'room', 'room_id', 'room-id' ), 'int' );
|
|
$check_in = self::get_field_value( $posted_data, array( 'check_in', 'checkin', 'check-in', 'arrival' ) );
|
|
$check_out = self::get_field_value( $posted_data, array( 'check_out', 'checkout', 'check-out', 'departure' ) );
|
|
|
|
if ( $room_id && $check_in && $check_out ) {
|
|
try {
|
|
$calculator = new Calculator( $room_id, $check_in, $check_out );
|
|
$price = $calculator->calculate();
|
|
return Calculator::formatPrice( $price );
|
|
} catch ( \Exception $e ) {
|
|
return '';
|
|
}
|
|
}
|
|
return '';
|
|
|
|
case '_bnb_nights':
|
|
$posted_data = $submission->get_posted_data();
|
|
$check_in = self::get_field_value( $posted_data, array( 'check_in', 'checkin', 'check-in', 'arrival' ) );
|
|
$check_out = self::get_field_value( $posted_data, array( 'check_out', 'checkout', 'check-out', 'departure' ) );
|
|
|
|
if ( $check_in && $check_out ) {
|
|
$nights = Booking::calculate_nights( $check_in, $check_out );
|
|
return (string) $nights;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
}
|