diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53e8d73..439e081 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,51 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.1.0] - 2026-01-31
+
+### Added
+
+- Custom Post Type: Buildings (`bnb_building`)
+ - Address fields (street, city, state, ZIP, country)
+ - Contact information (phone, email, website)
+ - Building details (total rooms, floors, year built)
+ - Check-in/check-out time configuration
+ - Featured image support
+ - Custom admin columns (city, country, room count)
+ - Sortable columns
+- Custom Post Type: Rooms (`bnb_room`)
+ - Building relationship (parent building selection)
+ - Room details (number, floor, size, capacity)
+ - Guest capacity (total, max adults, max children)
+ - Beds description and bathroom count
+ - Room status (available, occupied, maintenance, blocked)
+ - Image gallery with drag-and-drop sorting
+ - Featured image support
+ - Custom admin columns (building, room number, type, capacity, status)
+ - Building filter dropdown in admin list
+- Custom Taxonomy: Room Types (`bnb_room_type`)
+ - Hierarchical (category-like) structure
+ - Default types: Standard, Superior, Suite, Family, Accessible, Apartment
+ - Subtypes: Single, Double, Twin, Junior Suite, Executive Suite
+ - Base capacity meta field
+ - Sort order meta field
+- Custom Taxonomy: Amenities (`bnb_amenity`)
+ - Non-hierarchical (tag-like) structure
+ - Default amenities: WiFi, Parking, Breakfast, TV, A/C, Pet Friendly, etc.
+ - Dashicon selection for visual display
+ - Custom column showing icon
+- Admin enhancements
+ - Gallery meta box with media library integration
+ - Status badges with color coding
+ - Custom title placeholders for each post type
+ - Post type edit screens with proper asset loading
+
+### Changed
+
+- Updated admin assets to handle post type edit screens
+- Enhanced asset enqueuing to include jQuery UI Sortable for galleries
+- Improved localization with additional i18n strings
+
## [0.0.1] - 2026-01-31
### Added
@@ -35,4 +80,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Input sanitization and output escaping
- Server secret masking in license settings
+[0.1.0]: https://src.bundespruefstelle.ch/magdev/wp-bnb/releases/tag/v0.1.0
[0.0.1]: https://src.bundespruefstelle.ch/magdev/wp-bnb/releases/tag/v0.0.1
diff --git a/CLAUDE.md b/CLAUDE.md
index d10b06d..352bb0a 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -215,8 +215,14 @@ wp-bnb/
│ └── release.yml # CI/CD release pipeline
├── src/ # PHP source code (PSR-4: Magdev\WpBnb)
│ ├── Plugin.php # Main plugin singleton
-│ └── License/
-│ └── Manager.php # License management
+│ ├── License/
+│ │ └── Manager.php # License management
+│ ├── PostTypes/ # Custom post types
+│ │ ├── Building.php # Building post type
+│ │ └── Room.php # Room post type
+│ └── Taxonomies/ # Custom taxonomies
+│ ├── Amenity.php # Amenity taxonomy (tags)
+│ └── RoomType.php # Room type taxonomy (categories)
├── lib/ # Git submodules
│ └── wc-licensed-product-client/ # License client library
├── vendor/ # Composer dependencies (auto-generated)
@@ -303,3 +309,44 @@ Admin features always work; frontend requires valid license.
- Settings page uses tabs with nonce-protected form submission
- AJAX handlers require `check_ajax_referer()` and `current_user_can()` checks
- CI/CD workflow excludes `lib/` directory but includes `vendor/` in releases
+
+### 2026-01-31 - Version 0.1.0 (Core Data Structures)
+
+**Completed:**
+
+- Created Custom Post Type: Buildings (`bnb_building`)
+ - Address meta box with full address fields
+ - Contact meta box with phone, email, website
+ - Details meta box with rooms count, floors, year built, check-in/out times
+ - Custom admin columns (city, country, room count)
+ - Sortable columns and country dropdown
+- Created Custom Post Type: Rooms (`bnb_room`)
+ - Building relationship via meta field
+ - Room details: number, floor, size, capacity, beds, bathrooms
+ - Room status with color-coded badges
+ - Image gallery with media library and drag-and-drop sorting
+ - Building filter dropdown in admin list
+ - Custom admin columns with building link
+- Created Custom Taxonomy: Room Types (`bnb_room_type`)
+ - Hierarchical structure with parent/child support
+ - Base capacity and sort order meta fields
+ - Default terms with subtypes (Standard > Single/Double/Twin, etc.)
+- Created Custom Taxonomy: Amenities (`bnb_amenity`)
+ - Non-hierarchical (tag-like) structure
+ - Dashicon selection for visual display
+ - Icon column in taxonomy list
+ - Default amenities: WiFi, Parking, Breakfast, etc.
+- Updated Plugin class to register post types and taxonomies
+- Enhanced admin assets for post type edit screens
+- Added gallery JavaScript with media library integration
+- Updated activation hook to register CPTs before flushing rewrites
+- Updated version to 0.1.0
+
+**Learnings:**
+
+- Taxonomies must be registered before post types that use them
+- `show_in_menu => 'wp-bnb'` adds post types under the plugin's main menu
+- Room-building relationship uses post meta, not hierarchical post types
+- Gallery implementation uses `wp.media` frame with multiple selection
+- Admin assets need conditional loading based on both hook suffix and post type
+- Status badges use inline styles for color coding (avoiding extra CSS complexity)
diff --git a/PLAN.md b/PLAN.md
index c0a28d6..2e63a1f 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -17,24 +17,24 @@ This document outlines the implementation plan for the WP BnB Management plugin.
- [x] Basic CSS and JS assets
- [x] Documentation (README, PLAN, CLAUDE)
-### v0.1.0 - Core Data Structures
+### v0.1.0 - Core Data Structures (Current)
-- [ ] Custom Post Type: Buildings
+- [x] Custom Post Type: Buildings
- Meta fields: address, contact, description, images
- Admin columns and filtering
- - Gutenberg block for display
+ - Gutenberg block for display (planned for Phase 6)
-- [ ] Custom Post Type: Rooms
+- [x] Custom Post Type: Rooms
- Meta fields: building reference, capacity, amenities, images
- Relationship to Buildings (parent)
- Admin columns with building filter
- - Gutenberg block for display
+ - Gutenberg block for display (planned for Phase 6)
-- [ ] Custom Taxonomy: Room Types
+- [x] Custom Taxonomy: Room Types
- Standard, Suite, Family, Accessible, etc.
- Hierarchical structure
-- [ ] Custom Taxonomy: Amenities
+- [x] Custom Taxonomy: Amenities
- WiFi, Parking, Breakfast, etc.
- Non-hierarchical (tags)
@@ -285,15 +285,15 @@ The plugin will provide extensive hooks for customization:
## Version Milestones
-| Version | Focus | Target |
-|---------|-------|--------|
-| 0.0.1 | Initial setup | Complete |
-| 0.1.0 | Data structures | TBD |
-| 0.2.0 | Pricing | TBD |
-| 0.3.0 | Bookings | TBD |
-| 0.4.0 | Guests | TBD |
-| 0.5.0 | Services | TBD |
-| 0.6.0 | Frontend | TBD |
-| 0.7.0 | CF7 Integration | TBD |
-| 0.8.0 | Dashboard | TBD |
-| 1.0.0 | Stable Release | TBD |
+| Version | Focus | Target |
+| ------- | --------------- | -------- |
+| 0.0.1 | Initial setup | Complete |
+| 0.1.0 | Data structures | Complete |
+| 0.2.0 | Pricing | TBD |
+| 0.3.0 | Bookings | TBD |
+| 0.4.0 | Guests | TBD |
+| 0.5.0 | Services | TBD |
+| 0.6.0 | Frontend | TBD |
+| 0.7.0 | CF7 Integration | TBD |
+| 0.8.0 | Dashboard | TBD |
+| 1.0.0 | Stable Release | TBD |
diff --git a/assets/css/admin.css b/assets/css/admin.css
index 4161b17..e2647ba 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -79,3 +79,115 @@
.submit .button {
margin: 0;
}
+
+/* Room Gallery */
+.bnb-gallery-container {
+ padding: 10px 0;
+}
+
+.bnb-gallery-images {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-bottom: 15px;
+}
+
+.bnb-gallery-image {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ border: 1px solid #c3c4c7;
+ border-radius: 4px;
+ overflow: hidden;
+ cursor: move;
+}
+
+.bnb-gallery-image img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.bnb-gallery-image .bnb-remove-image {
+ position: absolute;
+ top: 2px;
+ right: 2px;
+ width: 20px;
+ height: 20px;
+ padding: 0;
+ border: none;
+ border-radius: 50%;
+ background: rgba(0, 0, 0, 0.7);
+ color: #fff;
+ font-size: 14px;
+ line-height: 18px;
+ text-align: center;
+ cursor: pointer;
+ opacity: 0;
+ transition: opacity 0.2s;
+}
+
+.bnb-gallery-image:hover .bnb-remove-image {
+ opacity: 1;
+}
+
+.bnb-gallery-image .bnb-remove-image:hover {
+ background: #d63638;
+}
+
+/* Status Badge */
+.bnb-status-badge {
+ display: inline-block;
+ padding: 3px 8px;
+ border-radius: 3px;
+ color: #fff;
+ font-size: 12px;
+ font-weight: 600;
+ text-transform: uppercase;
+}
+
+/* Room Details Meta Box */
+#bnb_room_details .form-table td label {
+ display: inline-block;
+ min-width: 100px;
+}
+
+/* Building Details Meta Box */
+#bnb_building_details p {
+ margin: 10px 0;
+}
+
+#bnb_building_details label {
+ display: block;
+ margin-bottom: 5px;
+ font-weight: 600;
+}
+
+#bnb_building_details input {
+ width: 100%;
+}
+
+/* Admin Columns */
+.column-city,
+.column-country,
+.column-room_number,
+.column-capacity,
+.column-status {
+ width: 100px;
+}
+
+.column-building {
+ width: 150px;
+}
+
+.column-rooms {
+ width: 80px;
+}
+
+/* Dashicons in columns */
+.column-capacity .dashicons {
+ color: #646970;
+ font-size: 16px;
+ vertical-align: middle;
+ margin-right: 3px;
+}
diff --git a/assets/js/admin.js b/assets/js/admin.js
index 4d97d53..17c565e 100644
--- a/assets/js/admin.js
+++ b/assets/js/admin.js
@@ -91,9 +91,102 @@
}
}
+ /**
+ * Initialize room gallery functionality.
+ */
+ function initRoomGallery() {
+ var $container = $('#bnb-room-gallery');
+ var $addButton = $('#bnb-add-gallery-images');
+ var $input = $('#bnb_room_gallery');
+ var $imagesContainer = $container.find('.bnb-gallery-images');
+
+ if (!$addButton.length) {
+ return;
+ }
+
+ // Media frame for selecting images.
+ var mediaFrame;
+
+ // Add images button click.
+ $addButton.on('click', function(e) {
+ e.preventDefault();
+
+ // If frame exists, reopen it.
+ if (mediaFrame) {
+ mediaFrame.open();
+ return;
+ }
+
+ // Create media frame.
+ mediaFrame = wp.media({
+ title: wpBnbAdmin.i18n.selectImages,
+ button: {
+ text: wpBnbAdmin.i18n.addToGallery
+ },
+ multiple: true,
+ library: {
+ type: 'image'
+ }
+ });
+
+ // Handle selection.
+ mediaFrame.on('select', function() {
+ var selection = mediaFrame.state().get('selection');
+ selection.each(function(attachment) {
+ var data = attachment.toJSON();
+ var thumbnail = data.sizes.thumbnail ? data.sizes.thumbnail.url : data.url;
+
+ // Check if already in gallery.
+ if ($imagesContainer.find('[data-id="' + data.id + '"]').length) {
+ return;
+ }
+
+ // Add image to gallery.
+ var $image = $('
' +
+ '

' +
+ '
' +
+ '
');
+ $imagesContainer.append($image);
+ });
+
+ updateGalleryInput();
+ });
+
+ mediaFrame.open();
+ });
+
+ // Remove image button click.
+ $imagesContainer.on('click', '.bnb-remove-image', function(e) {
+ e.preventDefault();
+ $(this).closest('.bnb-gallery-image').remove();
+ updateGalleryInput();
+ });
+
+ // Make gallery sortable.
+ $imagesContainer.sortable({
+ items: '.bnb-gallery-image',
+ cursor: 'move',
+ update: function() {
+ updateGalleryInput();
+ }
+ });
+
+ /**
+ * Update the hidden input with gallery image IDs.
+ */
+ function updateGalleryInput() {
+ var ids = [];
+ $imagesContainer.find('.bnb-gallery-image').each(function() {
+ ids.push($(this).data('id'));
+ });
+ $input.val(ids.join(','));
+ }
+ }
+
// Initialize on document ready.
$(document).ready(function() {
initLicenseManagement();
+ initRoomGallery();
});
})(jQuery);
diff --git a/src/Plugin.php b/src/Plugin.php
index 4c33f28..e83773c 100644
--- a/src/Plugin.php
+++ b/src/Plugin.php
@@ -10,6 +10,10 @@ declare( strict_types=1 );
namespace Magdev\WpBnb;
use Magdev\WpBnb\License\Manager as LicenseManager;
+use Magdev\WpBnb\PostTypes\Building;
+use Magdev\WpBnb\PostTypes\Room;
+use Magdev\WpBnb\Taxonomies\Amenity;
+use Magdev\WpBnb\Taxonomies\RoomType;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
@@ -67,6 +71,31 @@ final class Plugin {
// 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();
+ }
+
+ /**
+ * Register custom taxonomies.
+ *
+ * @return void
+ */
+ private function register_taxonomies(): void {
+ // Taxonomies must be registered before post types that use them.
+ Amenity::init();
+ RoomType::init();
}
/**
@@ -129,8 +158,14 @@ final class Plugin {
* @return void
*/
public function enqueue_admin_assets( string $hook_suffix ): void {
- // Only load on plugin pages.
- if ( strpos( $hook_suffix, 'wp-bnb' ) === false ) {
+ 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 ), true );
+ $is_edit_screen = in_array( $hook_suffix, array( 'post.php', 'post-new.php' ), true );
+
+ if ( ! $is_plugin_page && ! ( $is_our_post_type && $is_edit_screen ) ) {
return;
}
@@ -141,10 +176,18 @@ final class Plugin {
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';
+ }
+
wp_enqueue_script(
'wp-bnb-admin',
WP_BNB_URL . 'assets/js/admin.js',
- array( 'jquery' ),
+ $script_deps,
WP_BNB_VERSION,
true
);
@@ -153,12 +196,16 @@ final class Plugin {
'wp-bnb-admin',
'wpBnbAdmin',
array(
- 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
- 'nonce' => wp_create_nonce( 'wp_bnb_admin_nonce' ),
- 'i18n' => array(
- 'validating' => __( 'Validating...', 'wp-bnb' ),
- 'activating' => __( 'Activating...', 'wp-bnb' ),
- 'error' => __( 'An error occurred. Please try again.', 'wp-bnb' ),
+ 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
+ 'nonce' => wp_create_nonce( 'wp_bnb_admin_nonce' ),
+ 'postType' => $post_type,
+ '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' ),
),
)
);
diff --git a/src/PostTypes/Building.php b/src/PostTypes/Building.php
new file mode 100644
index 0000000..2e542a5
--- /dev/null
+++ b/src/PostTypes/Building.php
@@ -0,0 +1,562 @@
+ _x( 'Buildings', 'post type general name', 'wp-bnb' ),
+ 'singular_name' => _x( 'Building', 'post type singular name', 'wp-bnb' ),
+ 'menu_name' => _x( 'Buildings', 'admin menu', 'wp-bnb' ),
+ 'name_admin_bar' => _x( 'Building', 'add new on admin bar', 'wp-bnb' ),
+ 'add_new' => _x( 'Add New', 'building', 'wp-bnb' ),
+ 'add_new_item' => __( 'Add New Building', 'wp-bnb' ),
+ 'new_item' => __( 'New Building', 'wp-bnb' ),
+ 'edit_item' => __( 'Edit Building', 'wp-bnb' ),
+ 'view_item' => __( 'View Building', 'wp-bnb' ),
+ 'all_items' => __( 'Buildings', 'wp-bnb' ),
+ 'search_items' => __( 'Search Buildings', 'wp-bnb' ),
+ 'parent_item_colon' => __( 'Parent Buildings:', 'wp-bnb' ),
+ 'not_found' => __( 'No buildings found.', 'wp-bnb' ),
+ 'not_found_in_trash' => __( 'No buildings found in Trash.', 'wp-bnb' ),
+ 'featured_image' => __( 'Building Image', 'wp-bnb' ),
+ 'set_featured_image' => __( 'Set building image', 'wp-bnb' ),
+ 'remove_featured_image' => __( 'Remove building image', 'wp-bnb' ),
+ 'use_featured_image' => __( 'Use as building image', 'wp-bnb' ),
+ 'archives' => __( 'Building archives', 'wp-bnb' ),
+ 'insert_into_item' => __( 'Insert into building', 'wp-bnb' ),
+ 'uploaded_to_this_item' => __( 'Uploaded to this building', 'wp-bnb' ),
+ 'filter_items_list' => __( 'Filter buildings list', 'wp-bnb' ),
+ 'items_list_navigation' => __( 'Buildings list navigation', 'wp-bnb' ),
+ 'items_list' => __( 'Buildings list', 'wp-bnb' ),
+ );
+
+ $args = array(
+ 'labels' => $labels,
+ 'public' => true,
+ 'publicly_queryable' => true,
+ 'show_ui' => true,
+ 'show_in_menu' => 'wp-bnb',
+ 'query_var' => true,
+ 'rewrite' => array(
+ 'slug' => 'building',
+ 'with_front' => false,
+ ),
+ 'capability_type' => 'post',
+ 'has_archive' => true,
+ 'hierarchical' => false,
+ 'menu_position' => null,
+ 'menu_icon' => 'dashicons-building',
+ 'supports' => array(
+ 'title',
+ 'editor',
+ 'thumbnail',
+ 'excerpt',
+ 'revisions',
+ ),
+ 'show_in_rest' => true,
+ 'rest_base' => 'buildings',
+ 'rest_controller_class' => 'WP_REST_Posts_Controller',
+ );
+
+ register_post_type( self::POST_TYPE, $args );
+ }
+
+ /**
+ * Add meta boxes.
+ *
+ * @return void
+ */
+ public static function add_meta_boxes(): void {
+ add_meta_box(
+ 'bnb_building_address',
+ __( 'Address', 'wp-bnb' ),
+ array( self::class, 'render_address_meta_box' ),
+ self::POST_TYPE,
+ 'normal',
+ 'high'
+ );
+
+ add_meta_box(
+ 'bnb_building_contact',
+ __( 'Contact Information', 'wp-bnb' ),
+ array( self::class, 'render_contact_meta_box' ),
+ self::POST_TYPE,
+ 'normal',
+ 'high'
+ );
+
+ add_meta_box(
+ 'bnb_building_details',
+ __( 'Building Details', 'wp-bnb' ),
+ array( self::class, 'render_details_meta_box' ),
+ self::POST_TYPE,
+ 'side',
+ 'default'
+ );
+ }
+
+ /**
+ * Render address meta box.
+ *
+ * @param \WP_Post $post Current post object.
+ * @return void
+ */
+ public static function render_address_meta_box( \WP_Post $post ): void {
+ wp_nonce_field( 'bnb_building_meta', 'bnb_building_meta_nonce' );
+
+ $street = get_post_meta( $post->ID, self::META_PREFIX . 'street', true );
+ $street2 = get_post_meta( $post->ID, self::META_PREFIX . 'street2', true );
+ $city = get_post_meta( $post->ID, self::META_PREFIX . 'city', true );
+ $state = get_post_meta( $post->ID, self::META_PREFIX . 'state', true );
+ $zip = get_post_meta( $post->ID, self::META_PREFIX . 'zip', true );
+ $country = get_post_meta( $post->ID, self::META_PREFIX . 'country', true );
+ ?>
+
+ ID, self::META_PREFIX . 'phone', true );
+ $email = get_post_meta( $post->ID, self::META_PREFIX . 'email', true );
+ $website = get_post_meta( $post->ID, self::META_PREFIX . 'website', true );
+ ?>
+
+ ID, self::META_PREFIX . 'total_rooms', true );
+ $floors = get_post_meta( $post->ID, self::META_PREFIX . 'floors', true );
+ $year_built = get_post_meta( $post->ID, self::META_PREFIX . 'year_built', true );
+ $check_in = get_post_meta( $post->ID, self::META_PREFIX . 'check_in_time', true );
+ $check_out = get_post_meta( $post->ID, self::META_PREFIX . 'check_out_time', true );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $value ) {
+ $new_columns[ $key ] = $value;
+ if ( 'title' === $key ) {
+ $new_columns['city'] = __( 'City', 'wp-bnb' );
+ $new_columns['country'] = __( 'Country', 'wp-bnb' );
+ $new_columns['rooms'] = __( 'Rooms', 'wp-bnb' );
+ }
+ }
+ return $new_columns;
+ }
+
+ /**
+ * Render custom column content.
+ *
+ * @param string $column Column name.
+ * @param int $post_id Post ID.
+ * @return void
+ */
+ public static function render_column( string $column, int $post_id ): void {
+ switch ( $column ) {
+ case 'city':
+ $city = get_post_meta( $post_id, self::META_PREFIX . 'city', true );
+ echo esc_html( $city ?: '—' );
+ break;
+
+ case 'country':
+ $country = get_post_meta( $post_id, self::META_PREFIX . 'country', true );
+ if ( $country ) {
+ $countries = self::get_countries();
+ echo esc_html( $countries[ $country ] ?? $country );
+ } else {
+ echo '—';
+ }
+ break;
+
+ case 'rooms':
+ $rooms = get_posts(
+ array(
+ 'post_type' => 'bnb_room',
+ 'post_status' => 'publish',
+ 'posts_per_page' => -1,
+ 'meta_query' => array(
+ array(
+ 'key' => '_bnb_room_building_id',
+ 'value' => $post_id,
+ ),
+ ),
+ )
+ );
+ $count = count( $rooms );
+ if ( $count > 0 ) {
+ printf(
+ '%s',
+ esc_url(
+ admin_url(
+ 'edit.php?post_type=bnb_room&building_id=' . $post_id
+ )
+ ),
+ esc_html(
+ sprintf(
+ /* translators: %d: Number of rooms */
+ _n( '%d room', '%d rooms', $count, 'wp-bnb' ),
+ $count
+ )
+ )
+ );
+ } else {
+ echo '—';
+ }
+ break;
+ }
+ }
+
+ /**
+ * Add sortable columns.
+ *
+ * @param array $columns Existing sortable columns.
+ * @return array
+ */
+ public static function sortable_columns( array $columns ): array {
+ $columns['city'] = 'city';
+ $columns['country'] = 'country';
+ return $columns;
+ }
+
+ /**
+ * Change title placeholder.
+ *
+ * @param string $placeholder Default placeholder.
+ * @param \WP_Post $post Current post.
+ * @return string
+ */
+ public static function change_title_placeholder( string $placeholder, \WP_Post $post ): string {
+ if ( self::POST_TYPE === $post->post_type ) {
+ return __( 'Enter building name', 'wp-bnb' );
+ }
+ return $placeholder;
+ }
+
+ /**
+ * Get list of countries.
+ *
+ * @return array
+ */
+ public static function get_countries(): array {
+ return array(
+ 'CH' => __( 'Switzerland', 'wp-bnb' ),
+ 'DE' => __( 'Germany', 'wp-bnb' ),
+ 'AT' => __( 'Austria', 'wp-bnb' ),
+ 'FR' => __( 'France', 'wp-bnb' ),
+ 'IT' => __( 'Italy', 'wp-bnb' ),
+ 'LI' => __( 'Liechtenstein', 'wp-bnb' ),
+ 'NL' => __( 'Netherlands', 'wp-bnb' ),
+ 'BE' => __( 'Belgium', 'wp-bnb' ),
+ 'LU' => __( 'Luxembourg', 'wp-bnb' ),
+ 'GB' => __( 'United Kingdom', 'wp-bnb' ),
+ 'US' => __( 'United States', 'wp-bnb' ),
+ 'CA' => __( 'Canada', 'wp-bnb' ),
+ 'ES' => __( 'Spain', 'wp-bnb' ),
+ 'PT' => __( 'Portugal', 'wp-bnb' ),
+ );
+ }
+
+ /**
+ * Get formatted address.
+ *
+ * @param int $post_id Post ID.
+ * @return string
+ */
+ public static function get_formatted_address( int $post_id ): string {
+ $street = get_post_meta( $post_id, self::META_PREFIX . 'street', true );
+ $street2 = get_post_meta( $post_id, self::META_PREFIX . 'street2', true );
+ $city = get_post_meta( $post_id, self::META_PREFIX . 'city', true );
+ $state = get_post_meta( $post_id, self::META_PREFIX . 'state', true );
+ $zip = get_post_meta( $post_id, self::META_PREFIX . 'zip', true );
+ $country = get_post_meta( $post_id, self::META_PREFIX . 'country', true );
+
+ $parts = array();
+
+ if ( $street ) {
+ $parts[] = $street;
+ }
+ if ( $street2 ) {
+ $parts[] = $street2;
+ }
+ if ( $zip || $city ) {
+ $parts[] = trim( $zip . ' ' . $city );
+ }
+ if ( $state ) {
+ $parts[] = $state;
+ }
+ if ( $country ) {
+ $countries = self::get_countries();
+ $parts[] = $countries[ $country ] ?? $country;
+ }
+
+ return implode( "\n", $parts );
+ }
+}
diff --git a/src/PostTypes/Room.php b/src/PostTypes/Room.php
new file mode 100644
index 0000000..6617163
--- /dev/null
+++ b/src/PostTypes/Room.php
@@ -0,0 +1,660 @@
+ _x( 'Rooms', 'post type general name', 'wp-bnb' ),
+ 'singular_name' => _x( 'Room', 'post type singular name', 'wp-bnb' ),
+ 'menu_name' => _x( 'Rooms', 'admin menu', 'wp-bnb' ),
+ 'name_admin_bar' => _x( 'Room', 'add new on admin bar', 'wp-bnb' ),
+ 'add_new' => _x( 'Add New', 'room', 'wp-bnb' ),
+ 'add_new_item' => __( 'Add New Room', 'wp-bnb' ),
+ 'new_item' => __( 'New Room', 'wp-bnb' ),
+ 'edit_item' => __( 'Edit Room', 'wp-bnb' ),
+ 'view_item' => __( 'View Room', 'wp-bnb' ),
+ 'all_items' => __( 'Rooms', 'wp-bnb' ),
+ 'search_items' => __( 'Search Rooms', 'wp-bnb' ),
+ 'parent_item_colon' => __( 'Parent Rooms:', 'wp-bnb' ),
+ 'not_found' => __( 'No rooms found.', 'wp-bnb' ),
+ 'not_found_in_trash' => __( 'No rooms found in Trash.', 'wp-bnb' ),
+ 'featured_image' => __( 'Room Image', 'wp-bnb' ),
+ 'set_featured_image' => __( 'Set room image', 'wp-bnb' ),
+ 'remove_featured_image' => __( 'Remove room image', 'wp-bnb' ),
+ 'use_featured_image' => __( 'Use as room image', 'wp-bnb' ),
+ 'archives' => __( 'Room archives', 'wp-bnb' ),
+ 'insert_into_item' => __( 'Insert into room', 'wp-bnb' ),
+ 'uploaded_to_this_item' => __( 'Uploaded to this room', 'wp-bnb' ),
+ 'filter_items_list' => __( 'Filter rooms list', 'wp-bnb' ),
+ 'items_list_navigation' => __( 'Rooms list navigation', 'wp-bnb' ),
+ 'items_list' => __( 'Rooms list', 'wp-bnb' ),
+ );
+
+ $args = array(
+ 'labels' => $labels,
+ 'public' => true,
+ 'publicly_queryable' => true,
+ 'show_ui' => true,
+ 'show_in_menu' => 'wp-bnb',
+ 'query_var' => true,
+ 'rewrite' => array(
+ 'slug' => 'room',
+ 'with_front' => false,
+ ),
+ 'capability_type' => 'post',
+ 'has_archive' => true,
+ 'hierarchical' => false,
+ 'menu_position' => null,
+ 'menu_icon' => 'dashicons-admin-home',
+ 'supports' => array(
+ 'title',
+ 'editor',
+ 'thumbnail',
+ 'excerpt',
+ 'revisions',
+ ),
+ 'show_in_rest' => true,
+ 'rest_base' => 'rooms',
+ 'rest_controller_class' => 'WP_REST_Posts_Controller',
+ 'taxonomies' => array( RoomType::TAXONOMY, Amenity::TAXONOMY ),
+ );
+
+ register_post_type( self::POST_TYPE, $args );
+ }
+
+ /**
+ * Add meta boxes.
+ *
+ * @return void
+ */
+ public static function add_meta_boxes(): void {
+ add_meta_box(
+ 'bnb_room_building',
+ __( 'Building', 'wp-bnb' ),
+ array( self::class, 'render_building_meta_box' ),
+ self::POST_TYPE,
+ 'side',
+ 'high'
+ );
+
+ add_meta_box(
+ 'bnb_room_details',
+ __( 'Room Details', 'wp-bnb' ),
+ array( self::class, 'render_details_meta_box' ),
+ self::POST_TYPE,
+ 'normal',
+ 'high'
+ );
+
+ add_meta_box(
+ 'bnb_room_gallery',
+ __( 'Room Gallery', 'wp-bnb' ),
+ array( self::class, 'render_gallery_meta_box' ),
+ self::POST_TYPE,
+ 'normal',
+ 'default'
+ );
+ }
+
+ /**
+ * Render building selection meta box.
+ *
+ * @param \WP_Post $post Current post object.
+ * @return void
+ */
+ public static function render_building_meta_box( \WP_Post $post ): void {
+ wp_nonce_field( 'bnb_room_meta', 'bnb_room_meta_nonce' );
+
+ $building_id = get_post_meta( $post->ID, self::META_PREFIX . 'building_id', true );
+ $buildings = get_posts(
+ array(
+ 'post_type' => Building::POST_TYPE,
+ 'post_status' => 'publish',
+ 'posts_per_page' => -1,
+ 'orderby' => 'title',
+ 'order' => 'ASC',
+ )
+ );
+ ?>
+
+
+
+
+
+
+ ' . esc_html__( 'Add a building', 'wp-bnb' ) . ''
+ );
+ ?>
+
+
+ ID, self::META_PREFIX . 'room_number', true );
+ $floor = get_post_meta( $post->ID, self::META_PREFIX . 'floor', true );
+ $capacity = get_post_meta( $post->ID, self::META_PREFIX . 'capacity', true );
+ $max_adults = get_post_meta( $post->ID, self::META_PREFIX . 'max_adults', true );
+ $max_children = get_post_meta( $post->ID, self::META_PREFIX . 'max_children', true );
+ $size = get_post_meta( $post->ID, self::META_PREFIX . 'size', true );
+ $beds = get_post_meta( $post->ID, self::META_PREFIX . 'beds', true );
+ $bathrooms = get_post_meta( $post->ID, self::META_PREFIX . 'bathrooms', true );
+ $status = get_post_meta( $post->ID, self::META_PREFIX . 'status', true );
+ ?>
+
+ ID, self::META_PREFIX . 'gallery', true );
+ $gallery_ids = $gallery_ids ? explode( ',', $gallery_ids ) : array();
+ ?>
+
+
+
+
+
+
+
; ?>)
+
+
+
+
+
+
+
+
+
+ $value ) {
+ if ( 'taxonomy-bnb_room_type' === $key ) {
+ continue; // Will add after title.
+ }
+ if ( 'taxonomy-bnb_amenity' === $key ) {
+ continue; // Will add after room type.
+ }
+ $new_columns[ $key ] = $value;
+ if ( 'title' === $key ) {
+ $new_columns['building'] = __( 'Building', 'wp-bnb' );
+ $new_columns['room_number'] = __( 'Room #', 'wp-bnb' );
+ $new_columns['taxonomy-bnb_room_type'] = __( 'Room Type', 'wp-bnb' );
+ $new_columns['capacity'] = __( 'Capacity', 'wp-bnb' );
+ $new_columns['status'] = __( 'Status', 'wp-bnb' );
+ }
+ }
+ return $new_columns;
+ }
+
+ /**
+ * Render custom column content.
+ *
+ * @param string $column Column name.
+ * @param int $post_id Post ID.
+ * @return void
+ */
+ public static function render_column( string $column, int $post_id ): void {
+ switch ( $column ) {
+ case 'building':
+ $building_id = get_post_meta( $post_id, self::META_PREFIX . 'building_id', true );
+ if ( $building_id ) {
+ $building = get_post( $building_id );
+ if ( $building ) {
+ printf(
+ '%s',
+ esc_url( get_edit_post_link( $building_id ) ),
+ esc_html( $building->post_title )
+ );
+ } else {
+ echo '—';
+ }
+ } else {
+ echo '—';
+ }
+ break;
+
+ case 'room_number':
+ $room_number = get_post_meta( $post_id, self::META_PREFIX . 'room_number', true );
+ echo esc_html( $room_number ?: '—' );
+ break;
+
+ case 'capacity':
+ $capacity = get_post_meta( $post_id, self::META_PREFIX . 'capacity', true );
+ if ( $capacity ) {
+ printf(
+ ' %s',
+ esc_html( $capacity )
+ );
+ } else {
+ echo '—';
+ }
+ break;
+
+ case 'status':
+ $status = get_post_meta( $post_id, self::META_PREFIX . 'status', true ) ?: 'available';
+ $statuses = self::get_room_statuses();
+ $colors = self::get_status_colors();
+ ?>
+
+
+
+ Building::POST_TYPE,
+ 'post_status' => 'publish',
+ 'posts_per_page' => -1,
+ 'orderby' => 'title',
+ 'order' => 'ASC',
+ )
+ );
+
+ if ( empty( $buildings ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter display only.
+ $selected = isset( $_GET['building_id'] ) ? absint( $_GET['building_id'] ) : 0;
+ ?>
+
+ is_main_query() ) {
+ return;
+ }
+
+ if ( self::POST_TYPE !== $query->get( 'post_type' ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter query only.
+ if ( ! empty( $_GET['building_id'] ) ) {
+ $query->set(
+ 'meta_query',
+ array(
+ array(
+ 'key' => self::META_PREFIX . 'building_id',
+ 'value' => absint( $_GET['building_id'] ),
+ ),
+ )
+ );
+ }
+ }
+
+ /**
+ * Change title placeholder.
+ *
+ * @param string $placeholder Default placeholder.
+ * @param \WP_Post $post Current post.
+ * @return string
+ */
+ public static function change_title_placeholder( string $placeholder, \WP_Post $post ): string {
+ if ( self::POST_TYPE === $post->post_type ) {
+ return __( 'Enter room name', 'wp-bnb' );
+ }
+ return $placeholder;
+ }
+
+ /**
+ * Get room status options.
+ *
+ * @return array
+ */
+ public static function get_room_statuses(): array {
+ return array(
+ 'available' => __( 'Available', 'wp-bnb' ),
+ 'occupied' => __( 'Occupied', 'wp-bnb' ),
+ 'maintenance' => __( 'Maintenance', 'wp-bnb' ),
+ 'blocked' => __( 'Blocked', 'wp-bnb' ),
+ );
+ }
+
+ /**
+ * Get status color codes.
+ *
+ * @return array
+ */
+ public static function get_status_colors(): array {
+ return array(
+ 'available' => '#00a32a',
+ 'occupied' => '#72aee6',
+ 'maintenance' => '#dba617',
+ 'blocked' => '#d63638',
+ );
+ }
+
+ /**
+ * Get building for a room.
+ *
+ * @param int $room_id Room post ID.
+ * @return \WP_Post|null
+ */
+ public static function get_building( int $room_id ): ?\WP_Post {
+ $building_id = get_post_meta( $room_id, self::META_PREFIX . 'building_id', true );
+ if ( ! $building_id ) {
+ return null;
+ }
+ return get_post( $building_id );
+ }
+
+ /**
+ * Get rooms for a building.
+ *
+ * @param int $building_id Building post ID.
+ * @return array<\WP_Post>
+ */
+ public static function get_rooms_for_building( int $building_id ): array {
+ return get_posts(
+ array(
+ 'post_type' => self::POST_TYPE,
+ 'post_status' => 'publish',
+ 'posts_per_page' => -1,
+ 'meta_query' => array(
+ array(
+ 'key' => self::META_PREFIX . 'building_id',
+ 'value' => $building_id,
+ ),
+ ),
+ 'orderby' => 'meta_value',
+ 'meta_key' => self::META_PREFIX . 'room_number',
+ 'order' => 'ASC',
+ )
+ );
+ }
+}
diff --git a/src/Taxonomies/Amenity.php b/src/Taxonomies/Amenity.php
new file mode 100644
index 0000000..bf5b0f1
--- /dev/null
+++ b/src/Taxonomies/Amenity.php
@@ -0,0 +1,242 @@
+ _x( 'Amenities', 'taxonomy general name', 'wp-bnb' ),
+ 'singular_name' => _x( 'Amenity', 'taxonomy singular name', 'wp-bnb' ),
+ 'search_items' => __( 'Search Amenities', 'wp-bnb' ),
+ 'popular_items' => __( 'Popular Amenities', 'wp-bnb' ),
+ 'all_items' => __( 'All Amenities', 'wp-bnb' ),
+ 'parent_item' => null,
+ 'parent_item_colon' => null,
+ 'edit_item' => __( 'Edit Amenity', 'wp-bnb' ),
+ 'update_item' => __( 'Update Amenity', 'wp-bnb' ),
+ 'add_new_item' => __( 'Add New Amenity', 'wp-bnb' ),
+ 'new_item_name' => __( 'New Amenity Name', 'wp-bnb' ),
+ 'separate_items_with_commas' => __( 'Separate amenities with commas', 'wp-bnb' ),
+ 'add_or_remove_items' => __( 'Add or remove amenities', 'wp-bnb' ),
+ 'choose_from_most_used' => __( 'Choose from the most used amenities', 'wp-bnb' ),
+ 'not_found' => __( 'No amenities found.', 'wp-bnb' ),
+ 'menu_name' => __( 'Amenities', 'wp-bnb' ),
+ 'back_to_items' => __( '← Back to Amenities', 'wp-bnb' ),
+ );
+
+ $args = array(
+ 'labels' => $labels,
+ 'hierarchical' => false, // Non-hierarchical (like tags).
+ 'public' => true,
+ 'publicly_queryable' => true,
+ 'show_ui' => true,
+ 'show_in_menu' => true,
+ 'show_in_nav_menus' => true,
+ 'show_in_rest' => true,
+ 'show_tagcloud' => true,
+ 'show_in_quick_edit' => true,
+ 'show_admin_column' => true,
+ 'rewrite' => array(
+ 'slug' => 'amenity',
+ 'with_front' => false,
+ ),
+ 'query_var' => true,
+ 'capabilities' => array(
+ 'manage_terms' => 'manage_options',
+ 'edit_terms' => 'manage_options',
+ 'delete_terms' => 'manage_options',
+ 'assign_terms' => 'edit_posts',
+ ),
+ );
+
+ register_taxonomy( self::TAXONOMY, array( 'bnb_room' ), $args );
+ }
+
+ /**
+ * Add custom fields to the add term form.
+ *
+ * @return void
+ */
+ public static function add_form_fields(): void {
+ ?>
+
+ term_id, 'amenity_icon', true );
+ ?>
+
+ |
+
+ |
+
+
+
+ |
+
+ $value ) {
+ $new_columns[ $key ] = $value;
+ if ( 'name' === $key ) {
+ $new_columns['icon'] = __( 'Icon', 'wp-bnb' );
+ }
+ }
+ return $new_columns;
+ }
+
+ /**
+ * Render custom column content.
+ *
+ * @param string $content Column content.
+ * @param string $column_name Column name.
+ * @param int $term_id Term ID.
+ * @return string
+ */
+ public static function render_column( string $content, string $column_name, int $term_id ): string {
+ if ( 'icon' === $column_name ) {
+ $icon = get_term_meta( $term_id, 'amenity_icon', true );
+ if ( $icon ) {
+ return '';
+ }
+ return '—';
+ }
+ return $content;
+ }
+
+ /**
+ * Get available icon options.
+ *
+ * @return array
+ */
+ public static function get_icon_options(): array {
+ return array(
+ '' => __( '— Select Icon —', 'wp-bnb' ),
+ 'wifi' => __( 'WiFi', 'wp-bnb' ),
+ 'car' => __( 'Parking', 'wp-bnb' ),
+ 'food' => __( 'Breakfast', 'wp-bnb' ),
+ 'palmtree' => __( 'Garden/Pool', 'wp-bnb' ),
+ 'pets' => __( 'Pet Friendly', 'wp-bnb' ),
+ 'universal-access' => __( 'Accessibility', 'wp-bnb' ),
+ 'tv' => __( 'Television', 'wp-bnb' ),
+ 'superhero-alt' => __( 'Air Conditioning', 'wp-bnb' ),
+ 'coffee' => __( 'Coffee/Tea', 'wp-bnb' ),
+ 'admin-home' => __( 'Kitchen', 'wp-bnb' ),
+ 'businessman' => __( 'Business Center', 'wp-bnb' ),
+ 'heart' => __( 'Spa/Wellness', 'wp-bnb' ),
+ 'groups' => __( 'Family Friendly', 'wp-bnb' ),
+ 'location-alt' => __( 'Central Location', 'wp-bnb' ),
+ 'building' => __( 'Elevator', 'wp-bnb' ),
+ 'store' => __( 'Minibar', 'wp-bnb' ),
+ 'admin-appearance' => __( 'Room Service', 'wp-bnb' ),
+ 'shield' => __( 'Safe', 'wp-bnb' ),
+ 'privacy' => __( 'Non-Smoking', 'wp-bnb' ),
+ );
+ }
+
+ /**
+ * Get default amenities to seed on activation.
+ *
+ * @return array
+ */
+ public static function get_default_terms(): array {
+ return array(
+ __( 'WiFi', 'wp-bnb' ) => array( 'icon' => 'wifi' ),
+ __( 'Parking', 'wp-bnb' ) => array( 'icon' => 'car' ),
+ __( 'Breakfast Included', 'wp-bnb' ) => array( 'icon' => 'food' ),
+ __( 'Air Conditioning', 'wp-bnb' ) => array( 'icon' => 'superhero-alt' ),
+ __( 'Television', 'wp-bnb' ) => array( 'icon' => 'tv' ),
+ __( 'Pet Friendly', 'wp-bnb' ) => array( 'icon' => 'pets' ),
+ __( 'Wheelchair Accessible', 'wp-bnb' ) => array( 'icon' => 'universal-access' ),
+ __( 'Non-Smoking', 'wp-bnb' ) => array( 'icon' => 'privacy' ),
+ );
+ }
+}
diff --git a/src/Taxonomies/RoomType.php b/src/Taxonomies/RoomType.php
new file mode 100644
index 0000000..4e57f18
--- /dev/null
+++ b/src/Taxonomies/RoomType.php
@@ -0,0 +1,224 @@
+ _x( 'Room Types', 'taxonomy general name', 'wp-bnb' ),
+ 'singular_name' => _x( 'Room Type', 'taxonomy singular name', 'wp-bnb' ),
+ 'search_items' => __( 'Search Room Types', 'wp-bnb' ),
+ 'all_items' => __( 'All Room Types', 'wp-bnb' ),
+ 'parent_item' => __( 'Parent Room Type', 'wp-bnb' ),
+ 'parent_item_colon' => __( 'Parent Room Type:', 'wp-bnb' ),
+ 'edit_item' => __( 'Edit Room Type', 'wp-bnb' ),
+ 'update_item' => __( 'Update Room Type', 'wp-bnb' ),
+ 'add_new_item' => __( 'Add New Room Type', 'wp-bnb' ),
+ 'new_item_name' => __( 'New Room Type Name', 'wp-bnb' ),
+ 'menu_name' => __( 'Room Types', 'wp-bnb' ),
+ 'back_to_items' => __( '← Back to Room Types', 'wp-bnb' ),
+ 'not_found' => __( 'No room types found.', 'wp-bnb' ),
+ );
+
+ $args = array(
+ 'labels' => $labels,
+ 'hierarchical' => true, // Hierarchical (like categories).
+ 'public' => true,
+ 'publicly_queryable' => true,
+ 'show_ui' => true,
+ 'show_in_menu' => true,
+ 'show_in_nav_menus' => true,
+ 'show_in_rest' => true,
+ 'show_in_quick_edit' => true,
+ 'show_admin_column' => true,
+ 'rewrite' => array(
+ 'slug' => 'room-type',
+ 'with_front' => false,
+ 'hierarchical' => true,
+ ),
+ 'query_var' => true,
+ 'capabilities' => array(
+ 'manage_terms' => 'manage_options',
+ 'edit_terms' => 'manage_options',
+ 'delete_terms' => 'manage_options',
+ 'assign_terms' => 'edit_posts',
+ ),
+ );
+
+ register_taxonomy( self::TAXONOMY, array( 'bnb_room' ), $args );
+ }
+
+ /**
+ * Add custom fields to the add term form.
+ *
+ * @return void
+ */
+ public static function add_form_fields(): void {
+ ?>
+
+
+ term_id, 'room_type_base_capacity', true );
+ $sort_order = get_term_meta( $term->term_id, 'room_type_sort_order', true );
+ ?>
+
+ |
+
+ |
+
+
+
+ |
+
+
+ |
+
+ |
+
+
+
+ |
+
+ }>
+ */
+ public static function get_default_terms(): array {
+ return array(
+ __( 'Standard', 'wp-bnb' ) => array(
+ 'capacity' => 2,
+ 'order' => 10,
+ 'children' => array(
+ __( 'Single', 'wp-bnb' ) => array(
+ 'capacity' => 1,
+ 'order' => 11,
+ ),
+ __( 'Double', 'wp-bnb' ) => array(
+ 'capacity' => 2,
+ 'order' => 12,
+ ),
+ __( 'Twin', 'wp-bnb' ) => array(
+ 'capacity' => 2,
+ 'order' => 13,
+ ),
+ ),
+ ),
+ __( 'Superior', 'wp-bnb' ) => array(
+ 'capacity' => 2,
+ 'order' => 20,
+ ),
+ __( 'Suite', 'wp-bnb' ) => array(
+ 'capacity' => 2,
+ 'order' => 30,
+ 'children' => array(
+ __( 'Junior Suite', 'wp-bnb' ) => array(
+ 'capacity' => 2,
+ 'order' => 31,
+ ),
+ __( 'Executive Suite', 'wp-bnb' ) => array(
+ 'capacity' => 2,
+ 'order' => 32,
+ ),
+ ),
+ ),
+ __( 'Family', 'wp-bnb' ) => array(
+ 'capacity' => 4,
+ 'order' => 40,
+ ),
+ __( 'Accessible', 'wp-bnb' ) => array(
+ 'capacity' => 2,
+ 'order' => 50,
+ ),
+ __( 'Apartment', 'wp-bnb' ) => array(
+ 'capacity' => 4,
+ 'order' => 60,
+ ),
+ );
+ }
+}
diff --git a/wp-bnb.php b/wp-bnb.php
index 3e5dfdf..46d335c 100644
--- a/wp-bnb.php
+++ b/wp-bnb.php
@@ -3,7 +3,7 @@
* Plugin Name: WP BnB Management
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wp-bnb
* Description: A comprehensive Bed & Breakfast management system for WordPress. Manage buildings, rooms, bookings, and guests.
- * Version: 0.0.1
+ * Version: 0.1.0
* Requires at least: 6.0
* Requires PHP: 8.3
* Author: Marco Graetsch
@@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) {
}
// Plugin version constant - MUST match Version in header above.
-define( 'WP_BNB_VERSION', '0.0.1' );
+define( 'WP_BNB_VERSION', '0.1.0' );
// Plugin path constants.
define( 'WP_BNB_PATH', plugin_dir_path( __FILE__ ) );
@@ -155,6 +155,18 @@ function wp_bnb_activate(): void {
);
}
+ // Load Composer autoloader for activation.
+ $autoloader = WP_BNB_PATH . 'vendor/autoload.php';
+ if ( file_exists( $autoloader ) ) {
+ require_once $autoloader;
+
+ // Register post types and taxonomies before flushing rewrite rules.
+ \Magdev\WpBnb\Taxonomies\Amenity::register();
+ \Magdev\WpBnb\Taxonomies\RoomType::register();
+ \Magdev\WpBnb\PostTypes\Building::register();
+ \Magdev\WpBnb\PostTypes\Room::register();
+ }
+
// Set default options.
add_option( 'wp_bnb_version', WP_BNB_VERSION );