init_hooks(); $this->init_components(); } /** * Initialize WordPress hooks. * * @return void */ private function init_hooks(): void { // Load text domain. add_action( 'init', array( $this, 'load_textdomain' ) ); // Register assets. add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ) ); // Add plugin action links. add_filter( 'plugin_action_links_' . WP_BNB_BASENAME, array( $this, 'add_action_links' ) ); // Register custom post types and taxonomies. $this->register_post_types(); $this->register_taxonomies(); } /** * Register custom post types. * * @return void */ private function register_post_types(): void { Building::init(); Room::init(); Booking::init(); Guest::init(); Service::init(); } /** * Register custom taxonomies. * * @return void */ private function register_taxonomies(): void { // Taxonomies must be registered before post types that use them. Amenity::init(); RoomType::init(); ServiceCategory::init(); } /** * Initialize plugin components. * * @return void */ private function init_components(): void { // Initialize License Manager (always active for admin). LicenseManager::get_instance(); // Initialize auto-updater (requires license configuration). $this->init_updater(); // Initialize Contact Form 7 integration if CF7 is active. // This runs in both admin (for tag generators) and frontend (for form rendering). if ( class_exists( 'WPCF7' ) ) { CF7::init(); } // Initialize Prometheus metrics integration. Prometheus::init(); // Initialize admin components. if ( is_admin() ) { $this->init_admin(); } // Initialize frontend components only if licensed. if ( ! is_admin() && LicenseManager::is_license_valid() ) { $this->init_frontend(); } } /** * Initialize the plugin auto-updater. * * @return void */ private function init_updater(): void { $updater = new LicenseUpdater( plugin_file: WP_BNB_PATH . 'wp-bnb.php', current_version: WP_BNB_VERSION, ); $updater->init(); } /** * Initialize admin components. * * @return void */ private function init_admin(): void { // Admin menu and settings will be added here. add_action( 'admin_menu', array( $this, 'register_admin_menu' ) ); add_action( 'admin_menu', array( $this, 'reorder_admin_menu' ), 99 ); add_action( 'admin_init', array( $this, 'register_settings' ) ); // Initialize seasons admin page. SeasonsAdmin::init(); // Initialize calendar admin page. CalendarAdmin::init(); // Initialize email notifier. EmailNotifier::init(); // Initialize privacy manager for GDPR compliance. PrivacyManager::init(); // Register AJAX handlers. add_action( 'wp_ajax_wp_bnb_check_availability', array( $this, 'ajax_check_availability' ) ); add_action( 'wp_ajax_wp_bnb_search_guest', array( $this, 'ajax_search_guest' ) ); } /** * Initialize frontend components. * * @return void */ private function init_frontend(): void { // Initialize search (registers AJAX handlers). Search::init(); // Initialize shortcodes. Shortcodes::init(); // Initialize Gutenberg blocks. BlockRegistrar::init(); // Register widgets. add_action( 'widgets_init', array( $this, 'register_widgets' ) ); } /** * Register frontend widgets. * * @return void */ public function register_widgets(): void { register_widget( SimilarRooms::class ); register_widget( BuildingRooms::class ); register_widget( AvailabilityCalendar::class ); } /** * Load plugin text domain. * * @return void */ public function load_textdomain(): void { load_plugin_textdomain( 'wp-bnb', false, dirname( WP_BNB_BASENAME ) . '/languages' ); } /** * Enqueue admin assets. * * @param string $hook_suffix Current admin page hook. * @return void */ public function enqueue_admin_assets( string $hook_suffix ): void { global $post_type; // Check if we're on plugin pages or editing our custom post types. $is_plugin_page = strpos( $hook_suffix, 'wp-bnb' ) !== false; $is_our_post_type = in_array( $post_type, array( Building::POST_TYPE, Room::POST_TYPE, Booking::POST_TYPE, Guest::POST_TYPE, Service::POST_TYPE ), true ); $is_edit_screen = in_array( $hook_suffix, array( 'post.php', 'post-new.php' ), true ); $is_dashboard = 'toplevel_page_wp-bnb' === $hook_suffix; if ( ! $is_plugin_page && ! ( $is_our_post_type && $is_edit_screen ) ) { return; } wp_enqueue_style( 'wp-bnb-admin', WP_BNB_URL . 'assets/css/admin.css', array(), WP_BNB_VERSION ); $script_deps = array( 'jquery' ); // Add media dependencies for room gallery. if ( Room::POST_TYPE === $post_type && $is_edit_screen ) { wp_enqueue_media(); $script_deps[] = 'jquery-ui-sortable'; } // Add Chart.js for dashboard. if ( $is_dashboard ) { wp_enqueue_script( 'chartjs', 'https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js', array(), '4.4.1', true ); $script_deps[] = 'chartjs'; } wp_enqueue_script( 'wp-bnb-admin', WP_BNB_URL . 'assets/js/admin.js', $script_deps, WP_BNB_VERSION, true ); // Build localize data. $localize_data = array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'wp_bnb_admin_nonce' ), 'postType' => $post_type, 'isDashboard' => $is_dashboard, 'i18n' => array( 'validating' => __( 'Validating...', 'wp-bnb' ), 'activating' => __( 'Activating...', 'wp-bnb' ), 'error' => __( 'An error occurred. Please try again.', 'wp-bnb' ), 'selectImages' => __( 'Select Images', 'wp-bnb' ), 'addToGallery' => __( 'Add to Gallery', 'wp-bnb' ), 'confirmRemove' => __( 'Are you sure you want to remove this image?', 'wp-bnb' ), 'increase' => __( 'increase', 'wp-bnb' ), 'discount' => __( 'discount', 'wp-bnb' ), 'normalPrice' => __( 'Normal price', 'wp-bnb' ), 'checking' => __( 'Checking availability...', 'wp-bnb' ), 'available' => __( 'Available', 'wp-bnb' ), 'notAvailable' => __( 'Not available - conflicts with existing booking', 'wp-bnb' ), 'selectRoomAndDates' => __( 'Select room and dates to check availability', 'wp-bnb' ), 'nights' => __( 'nights', 'wp-bnb' ), 'night' => __( 'night', 'wp-bnb' ), 'calculating' => __( 'Calculating price...', 'wp-bnb' ), 'searchingGuests' => __( 'Searching...', 'wp-bnb' ), 'noGuestsFound' => __( 'No guests found', 'wp-bnb' ), 'selectGuest' => __( 'Select', 'wp-bnb' ), 'guestBlocked' => __( 'Blocked', 'wp-bnb' ), 'perNightDescription' => __( 'This price will be charged per night of the stay.', 'wp-bnb' ), 'perBookingDescription' => __( 'This price will be charged once for the booking.', 'wp-bnb' ), 'justNow' => __( 'Just now', 'wp-bnb' ), 'updateAvailable' => __( 'Update available!', 'wp-bnb' ), 'upToDate' => __( '(You are up to date)', 'wp-bnb' ), 'checkingUpdates' => __( 'Checking for updates...', 'wp-bnb' ), 'occupancy' => __( 'Occupancy %', 'wp-bnb' ), 'revenue' => __( 'Revenue', 'wp-bnb' ), ), ); // Add chart data for dashboard. if ( $is_dashboard ) { $localize_data['chartData'] = array( 'occupancy' => DashboardAdmin::get_occupancy_trend_data( 30 ), 'revenue' => DashboardAdmin::get_revenue_trend_data( 6 ), ); } wp_localize_script( 'wp-bnb-admin', 'wpBnbAdmin', $localize_data ); } /** * Enqueue frontend assets. * * @return void */ public function enqueue_frontend_assets(): void { // Only load if licensed. if ( ! LicenseManager::is_license_valid() ) { return; } wp_enqueue_style( 'wp-bnb-frontend', WP_BNB_URL . 'assets/css/frontend.css', array(), WP_BNB_VERSION ); wp_enqueue_script( 'wp-bnb-frontend', WP_BNB_URL . 'assets/js/frontend.js', array(), WP_BNB_VERSION, true ); wp_localize_script( 'wp-bnb-frontend', 'wpBnbFrontend', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'wp_bnb_frontend_nonce' ), 'i18n' => array( 'searching' => __( 'Searching...', 'wp-bnb' ), 'noResults' => __( 'No rooms found matching your criteria.', 'wp-bnb' ), 'resultsFound' => __( '%d rooms found', 'wp-bnb' ), 'loadMore' => __( 'Load More', 'wp-bnb' ), 'viewDetails' => __( 'View Details', 'wp-bnb' ), 'perNight' => __( 'night', 'wp-bnb' ), 'guests' => __( 'guests', 'wp-bnb' ), 'invalidDateRange' => __( 'Check-out must be after check-in', 'wp-bnb' ), 'selectDates' => __( 'Please select check-in and check-out dates.', 'wp-bnb' ), 'available' => __( 'Room is available!', 'wp-bnb' ), 'notAvailable' => __( 'Sorry, the room is not available for these dates.', 'wp-bnb' ), 'totalPrice' => __( 'Total', 'wp-bnb' ), 'bookNow' => __( 'Book Now', 'wp-bnb' ), 'total' => __( 'Total', 'wp-bnb' ), 'nights' => __( 'nights', 'wp-bnb' ), 'basePrice' => __( 'Base', 'wp-bnb' ), 'weekendSurcharge' => __( 'Weekend surcharge', 'wp-bnb' ), 'season' => __( 'Season', 'wp-bnb' ), ), ) ); // Load CF7 integration assets if CF7 is active. if ( class_exists( 'WPCF7' ) ) { wp_enqueue_style( 'wp-bnb-cf7', WP_BNB_URL . 'assets/css/cf7-integration.css', array( 'contact-form-7' ), WP_BNB_VERSION ); wp_enqueue_script( 'wp-bnb-cf7', WP_BNB_URL . 'assets/js/cf7-integration.js', array( 'contact-form-7' ), WP_BNB_VERSION, true ); wp_localize_script( 'wp-bnb-cf7', 'wpBnbCF7', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'wp_bnb_frontend_nonce' ), 'i18n' => array( 'selectRoom' => __( '-- Select Room --', 'wp-bnb' ), 'checking' => __( 'Checking availability...', 'wp-bnb' ), 'available' => __( 'Room is available!', 'wp-bnb' ), 'unavailable' => __( 'Room is not available for these dates', 'wp-bnb' ), 'invalidDateRange' => __( 'Check-out must be after check-in', 'wp-bnb' ), 'capacityExceeded' => __( 'Maximum %d guests for this room', 'wp-bnb' ), 'estimatedTotal' => __( 'Estimated Total', 'wp-bnb' ), 'nights' => __( 'nights', 'wp-bnb' ), ), ) ); } } /** * Add plugin action links. * * @param array $links Existing plugin links. * @return array */ public function add_action_links( array $links ): array { $plugin_links = array( '' . esc_html__( 'Settings', 'wp-bnb' ) . '', ); return array_merge( $plugin_links, $links ); } /** * Register admin menu. * * @return void */ public function register_admin_menu(): void { // Main menu. add_menu_page( __( 'WP BnB', 'wp-bnb' ), __( 'WP BnB', 'wp-bnb' ), 'manage_options', 'wp-bnb', array( $this, 'render_dashboard_page' ), 'dashicons-building', 30 ); // Dashboard submenu. add_submenu_page( 'wp-bnb', __( 'Dashboard', 'wp-bnb' ), __( 'Dashboard', 'wp-bnb' ), 'manage_options', 'wp-bnb', array( $this, 'render_dashboard_page' ) ); // Reports submenu. add_submenu_page( 'wp-bnb', __( 'Reports', 'wp-bnb' ), __( 'Reports', 'wp-bnb' ), 'manage_options', 'wp-bnb-reports', array( $this, 'render_reports_page' ) ); // Settings submenu. add_submenu_page( 'wp-bnb', __( 'Settings', 'wp-bnb' ), __( 'Settings', 'wp-bnb' ), 'manage_options', 'wp-bnb-settings', array( $this, 'render_settings_page' ) ); } /** * Reorder the admin submenu items. * * Places Dashboard at top, Settings at bottom, and organizes * the remaining items in logical order. * * @return void */ public function reorder_admin_menu(): void { global $submenu; if ( ! isset( $submenu['wp-bnb'] ) ) { return; } // Define the desired order of menu slugs. $desired_order = array( 'wp-bnb', // Dashboard. 'edit.php?post_type=bnb_building', // Buildings. 'edit.php?post_type=bnb_room', // Rooms. 'edit.php?post_type=bnb_booking', // Bookings. 'edit.php?post_type=bnb_guest', // Guests. 'edit.php?post_type=bnb_service', // Services. 'wp-bnb-calendar', // Calendar. 'wp-bnb-reports', // Reports. 'wp-bnb-seasons', // Seasons. 'wp-bnb-settings', // Settings (always last). ); $current_menu = $submenu['wp-bnb']; $ordered_menu = array(); $index = 0; // Add items in the desired order. foreach ( $desired_order as $slug ) { foreach ( $current_menu as $key => $item ) { if ( $item[2] === $slug ) { $ordered_menu[ $index ] = $item; unset( $current_menu[ $key ] ); ++$index; break; } } } // Append any remaining items not in the desired order. foreach ( $current_menu as $item ) { $ordered_menu[ $index ] = $item; ++$index; } $submenu['wp-bnb'] = $ordered_menu; } /** * Register plugin settings. * * @return void */ public function register_settings(): void { // License settings are handled by LicenseManager. // Additional settings will be added here. } /** * Render dashboard page. * * @return void */ public function render_dashboard_page(): void { DashboardAdmin::render(); } /** * Render reports page. * * @return void */ public function render_reports_page(): void { ReportsAdmin::render(); } /** * Render settings page. * * @return void */ public function render_settings_page(): void { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Tab switching only. $active_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'general'; // Handle form submission. if ( isset( $_POST['wp_bnb_settings_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['wp_bnb_settings_nonce'] ), 'wp_bnb_save_settings' ) ) { $this->save_settings( $active_tab ); } ?>

render_pricing_settings(); break; case 'license': $this->render_license_settings(); break; case 'updates': $this->render_updates_settings(); break; case 'metrics': $this->render_metrics_settings(); break; default: $this->render_general_settings(); break; } ?>

__( 'Monday', 'wp-bnb' ), 2 => __( 'Tuesday', 'wp-bnb' ), 3 => __( 'Wednesday', 'wp-bnb' ), 4 => __( 'Thursday', 'wp-bnb' ), 5 => __( 'Friday', 'wp-bnb' ), 6 => __( 'Saturday', 'wp-bnb' ), 7 => __( 'Sunday', 'wp-bnb' ), ); $selected_days = array_map( 'intval', explode( ',', $weekend_days ) ); $base_url = admin_url( 'admin.php?page=wp-bnb-settings&tab=pricing' ); ?>

name ); ?> start_date . ' - ' . $season->end_date ); ?> getModifierLabel() ); ?> priority ); ?> active ) : ?>

get_current_version(); $last_check = LicenseUpdater::get_last_check(); $update_info = $updater->get_cached_update_info(); $update_available = false; $latest_version = $current_version; $notifications_enabled = LicenseUpdater::is_notifications_enabled(); $auto_install_enabled = LicenseUpdater::is_auto_install_enabled(); $check_frequency = LicenseUpdater::get_check_frequency(); $license_valid = LicenseManager::is_license_valid(); if ( $update_info instanceof UpdateInfo && $update_info->updateAvailable ) { $latest_version = $update_info->version ?? $current_version; $update_available = version_compare( $current_version, $latest_version, '<' ); } ?>


wp-prometheus' ); ?>

wp_bnb_buildings_total
wp_bnb_rooms_total
wp_bnb_bookings_total
wp_bnb_occupancy_rate_current
wp_bnb_occupancy_rate_this_month
wp_bnb_checkins_today
wp_bnb_checkouts_today
wp_bnb_revenue_this_month
wp_bnb_revenue_ytd
wp_bnb_booking_avg_value
wp_bnb_guests_total
wp_bnb_guests_repeat

assets/grafana/wp-bnb-dashboard.json

array( 'class' => 'dashicons-yes-alt', 'color' => '#00a32a', 'label' => __( 'Valid', 'wp-bnb' ), ), 'invalid' => array( 'class' => 'dashicons-dismiss', 'color' => '#d63638', 'label' => __( 'Invalid', 'wp-bnb' ), ), 'expired' => array( 'class' => 'dashicons-warning', 'color' => '#dba617', 'label' => __( 'Expired', 'wp-bnb' ), ), 'revoked' => array( 'class' => 'dashicons-dismiss', 'color' => '#d63638', 'label' => __( 'Revoked', 'wp-bnb' ), ), 'inactive' => array( 'class' => 'dashicons-marker', 'color' => '#72aee6', 'label' => __( 'Inactive', 'wp-bnb' ), ), 'unchecked' => array( 'class' => 'dashicons-info', 'color' => '#72aee6', 'label' => __( 'Not checked', 'wp-bnb' ), ), 'unconfigured' => array( 'class' => 'dashicons-admin-generic', 'color' => '#646970', 'label' => __( 'Not configured', 'wp-bnb' ), ), ); $badge = $badges[ $status ] ?? $badges['unconfigured']; ?> save_pricing_settings(); break; case 'license': $this->save_license_settings(); break; case 'updates': $this->save_updates_settings(); break; case 'metrics': $this->save_metrics_settings(); break; default: $this->save_general_settings(); break; } } /** * Save general settings. * * @return void */ private function save_general_settings(): void { // Business Information. if ( isset( $_POST['wp_bnb_business_name'] ) ) { update_option( 'wp_bnb_business_name', sanitize_text_field( wp_unslash( $_POST['wp_bnb_business_name'] ) ) ); } if ( isset( $_POST['wp_bnb_currency'] ) ) { update_option( 'wp_bnb_currency', sanitize_text_field( wp_unslash( $_POST['wp_bnb_currency'] ) ) ); } // Address fields. $address_fields = array( 'street', 'city', 'postal', 'country' ); foreach ( $address_fields as $field ) { $key = 'wp_bnb_address_' . $field; if ( isset( $_POST[ $key ] ) ) { update_option( $key, sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) ); } } // Contact fields. if ( isset( $_POST['wp_bnb_contact_email'] ) ) { update_option( 'wp_bnb_contact_email', sanitize_email( wp_unslash( $_POST['wp_bnb_contact_email'] ) ) ); } if ( isset( $_POST['wp_bnb_contact_phone'] ) ) { update_option( 'wp_bnb_contact_phone', sanitize_text_field( wp_unslash( $_POST['wp_bnb_contact_phone'] ) ) ); } if ( isset( $_POST['wp_bnb_contact_website'] ) ) { update_option( 'wp_bnb_contact_website', esc_url_raw( wp_unslash( $_POST['wp_bnb_contact_website'] ) ) ); } // Social media fields. $social_fields = array( 'facebook', 'instagram', 'x', 'linkedin', 'tripadvisor' ); foreach ( $social_fields as $field ) { $key = 'wp_bnb_social_' . $field; if ( isset( $_POST[ $key ] ) ) { update_option( $key, esc_url_raw( wp_unslash( $_POST[ $key ] ) ) ); } } add_settings_error( 'wp_bnb_settings', 'settings_saved', __( 'Settings saved.', 'wp-bnb' ), 'success' ); settings_errors( 'wp_bnb_settings' ); } /** * Save pricing settings. * * @return void */ private function save_pricing_settings(): void { if ( isset( $_POST['wp_bnb_short_term_max_nights'] ) ) { $value = absint( $_POST['wp_bnb_short_term_max_nights'] ); $value = max( 1, min( 30, $value ) ); update_option( 'wp_bnb_short_term_max_nights', $value ); } if ( isset( $_POST['wp_bnb_mid_term_max_nights'] ) ) { $value = absint( $_POST['wp_bnb_mid_term_max_nights'] ); $value = max( 7, min( 90, $value ) ); update_option( 'wp_bnb_mid_term_max_nights', $value ); } if ( isset( $_POST['wp_bnb_weekend_days'] ) && is_array( $_POST['wp_bnb_weekend_days'] ) ) { $days = array_map( 'absint', $_POST['wp_bnb_weekend_days'] ); $days = array_filter( $days, fn( $d ) => $d >= 1 && $d <= 7 ); update_option( 'wp_bnb_weekend_days', implode( ',', $days ) ); } else { update_option( 'wp_bnb_weekend_days', '' ); } add_settings_error( 'wp_bnb_settings', 'settings_saved', __( 'Pricing settings saved.', 'wp-bnb' ), 'success' ); settings_errors( 'wp_bnb_settings' ); } /** * Save license settings. * * @return void */ private function save_license_settings(): void { $data = array( 'license_key' => isset( $_POST['wp_bnb_license_key'] ) ? sanitize_text_field( wp_unslash( $_POST['wp_bnb_license_key'] ) ) : '', 'server_url' => isset( $_POST['wp_bnb_license_server_url'] ) ? esc_url_raw( wp_unslash( $_POST['wp_bnb_license_server_url'] ) ) : '', 'server_secret' => isset( $_POST['wp_bnb_license_server_secret'] ) ? sanitize_text_field( wp_unslash( $_POST['wp_bnb_license_server_secret'] ) ) : '', ); LicenseManager::save_settings( $data ); add_settings_error( 'wp_bnb_settings', 'settings_saved', __( 'License settings saved.', 'wp-bnb' ), 'success' ); settings_errors( 'wp_bnb_settings' ); } /** * Save updates settings. * * @return void */ private function save_updates_settings(): void { $notifications_enabled = isset( $_POST['wp_bnb_update_notifications_enabled'] ) ? 'yes' : 'no'; update_option( LicenseUpdater::OPTION_NOTIFICATIONS_ENABLED, $notifications_enabled ); $auto_install_enabled = isset( $_POST['wp_bnb_auto_install_enabled'] ) ? 'yes' : 'no'; update_option( LicenseUpdater::OPTION_AUTO_INSTALL_ENABLED, $auto_install_enabled ); if ( isset( $_POST['wp_bnb_update_check_frequency'] ) ) { $frequency = absint( $_POST['wp_bnb_update_check_frequency'] ); $frequency = max( 1, min( 168, $frequency ) ); // Clamp between 1-168 hours. update_option( LicenseUpdater::OPTION_CHECK_FREQUENCY, $frequency ); // Clear update cache when frequency changes so new frequency takes effect. $updater = LicenseUpdater::get_instance(); if ( null !== $updater ) { $updater->clear_cache(); } } add_settings_error( 'wp_bnb_settings', 'settings_saved', __( 'Update settings saved.', 'wp-bnb' ), 'success' ); settings_errors( 'wp_bnb_settings' ); } /** * Save metrics settings. * * @return void */ private function save_metrics_settings(): void { $metrics_enabled = isset( $_POST['wp_bnb_metrics_enabled'] ) ? 'yes' : 'no'; update_option( Prometheus::OPTION_ENABLED, $metrics_enabled ); add_settings_error( 'wp_bnb_settings', 'settings_saved', __( 'Metrics settings saved.', 'wp-bnb' ), 'success' ); settings_errors( 'wp_bnb_settings' ); } /** * AJAX handler for checking room availability. * * @return void */ public function ajax_check_availability(): void { check_ajax_referer( 'wp_bnb_admin_nonce', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'wp-bnb' ) ) ); } $room_id = isset( $_POST['room_id'] ) ? absint( $_POST['room_id'] ) : 0; $check_in = isset( $_POST['check_in'] ) ? sanitize_text_field( wp_unslash( $_POST['check_in'] ) ) : ''; $check_out = isset( $_POST['check_out'] ) ? sanitize_text_field( wp_unslash( $_POST['check_out'] ) ) : ''; $exclude = isset( $_POST['exclude_booking'] ) ? absint( $_POST['exclude_booking'] ) : null; if ( ! $room_id || ! $check_in || ! $check_out ) { wp_send_json_error( array( 'message' => __( 'Missing required parameters.', 'wp-bnb' ) ) ); } // Validate dates. if ( strtotime( $check_out ) <= strtotime( $check_in ) ) { wp_send_json_error( array( 'message' => __( 'Check-out date must be after check-in date.', 'wp-bnb' ) ) ); } $result = Availability::check_availability_with_price( $room_id, $check_in, $check_out, $exclude ); wp_send_json_success( $result ); } /** * AJAX handler for searching guests by email. * * @return void */ public function ajax_search_guest(): void { check_ajax_referer( 'wp_bnb_admin_nonce', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'wp-bnb' ) ) ); } $search = isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : ''; if ( strlen( $search ) < 2 ) { wp_send_json_success( array( 'guests' => array() ) ); } // Search by email or name. $guests = get_posts( array( 'post_type' => Guest::POST_TYPE, 'post_status' => 'publish', 'posts_per_page' => 10, 'meta_query' => array( 'relation' => 'OR', array( 'key' => '_bnb_guest_email', 'value' => $search, 'compare' => 'LIKE', ), array( 'key' => '_bnb_guest_first_name', 'value' => $search, 'compare' => 'LIKE', ), array( 'key' => '_bnb_guest_last_name', 'value' => $search, 'compare' => 'LIKE', ), ), ) ); $results = array(); foreach ( $guests as $guest ) { $status = get_post_meta( $guest->ID, '_bnb_guest_status', true ) ?: 'active'; $results[] = array( 'id' => $guest->ID, 'name' => Guest::get_full_name( $guest->ID ), 'email' => get_post_meta( $guest->ID, '_bnb_guest_email', true ), 'phone' => get_post_meta( $guest->ID, '_bnb_guest_phone', true ), 'status' => $status, ); } wp_send_json_success( array( 'guests' => $results ) ); } /** * Get Twig environment. * * @return Environment */ public function get_twig(): Environment { if ( null === $this->twig ) { $loader = new FilesystemLoader( WP_BNB_PATH . 'templates' ); $this->twig = new Environment( $loader, array( 'cache' => WP_DEBUG ? false : WP_BNB_PATH . 'cache/twig', 'debug' => WP_DEBUG, ) ); } return $this->twig; } /** * Render a Twig template. * * @param string $template Template name. * @param array $context Template context. * @return string */ public function render( string $template, array $context = array() ): string { return $this->get_twig()->render( $template, $context ); } }