Install
Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/ledorofficial/booking-engine/releases/download/v1.15.1/booking-engine.zipA WordPress plugin for karunasiargao.com — a branded availability search that replaces the Smoobu embed. A compact one-month calendar (live nightly prices, sold-out nights greyed out) that sends the visitor to:
https://bookings.karunasiargao.com/?arrival=YYYY-MM-DD&departure=YYYY-MM-DD&guests=N#rooms
Install
- Download the latest zip from Releases (or Code → Download
ZIP and rename the folder to
booking-engine). - WP Admin → Plugins → Add New → Upload Plugin → the zip → Install → Activate.
- Place it:
- Search shortcode:
[karuna_booking_search]— in Elementor use a Shortcode element (never a Text / Heading / HTML widget) - Per-room calendar shortcode:
[karuna_room_calendar room_id="123"]— same rule, use a Shortcode element - Widget: Appearance → Widgets → Booking Engine
- Search shortcode:
Updates
The plugin checks this repo's Releases. When a release has a higher version than what's installed, WordPress shows the normal update available notice in Plugins and updates with one click — no re-upload. (WordPress checks automatically every ~12h; force it from Dashboard → Updates → Check again.)
Shipping an update = bump the Version: header in booking-engine.php
and push to main. A GitHub Actions workflow
(.github/workflows/release.yml) then tags and publishes the matching
vX.Y.Z release with a zip automatically — no manual release step needed.
Uses Plugin Update Checker
(bundled in plugin-update-checker/). Works token-free while this repo is public.
Using it on another property's website
One install of the booking engine serves several brands, each on its own
domain. To use this plugin on, say, the El Nido glamping WordPress site, install
it there and point base at that property's booking domain:
[karuna_booking_search base="https://bookings.glampingatkarunaelnido.com/"]
[karuna_room_calendar base="https://bookings.glampingatkarunaelnido.com/" room_id="123"]
The booking site only returns that property's own rooms, prices and availability,
and only allows requests from its own WordPress domain (CORS) — so the WordPress
site's address must be listed in that property's CORS origins on the booking
engine. The paste-in HTML snippet takes the same change via its action and
data-kbs-api attributes.
Shortcode attributes
| Attribute | Default | Notes |
|---|---|---|
base |
https://bookings.karunasiargao.com/ |
The booking site: where the search submits and where prices / sold-out nights are read from (<base>api/calendar). |
target |
_self |
_blank opens results in a new tab. |
max_guests |
16 |
Ceiling for Adults + Children in the Guests picker. |
prices |
on |
off hides the per-night prices. |
layout |
stacked |
stacked = teal card; inline = light wide row. |
button |
Search |
Submit button label. |
room |
(none) | Smoobu room id, for a room's own page: the calendar shows that room's prices, guests cap at its capacity, and the search lands on that room. |
[karuna_booking_search max_guests="4"] Skygazer (sleeps 4)
[karuna_booking_search prices="off"] calendar without prices
[karuna_booking_search layout="inline" button="Check availability"] homepage row
Layouts: stacked is the compact teal card for a sidebar (like the Smoobu
widget). inline is a full-width white row — Arrival · Departure · Guests ·
button — for a homepage hero on a light background; it wraps to two columns
below 640px.
Room calendar shortcode
[karuna_room_calendar] — a read-only, branded replacement for Smoobu's
embeddable per-apartment calendar iframe (no date picking, no form; just
shows sold-out nights). Place one per room page.
| Attribute | Default | Notes |
|---|---|---|
room_id |
(required) | The room's Smoobu apartment ID. |
months |
6 |
Total months of data fetched; 2 shown at a time, paged with the ›/‹ arrows. |
base |
https://bookings.karunasiargao.com/ |
Where the calendar JSON is fetched from. |
[karuna_room_calendar room_id="3492081"]
Find a room's Smoobu apartment ID in the Smoobu dashboard, or in this plugin's existing search results (the booking engine's admin/rooms list uses the same IDs).
Languages (Polylang)
The plugin follows the site's current language — Polylang (recommended), WPML or TranslatePress — for the same six languages the booking engine speaks: English, 简体中文, 繁體中文, 日本語, 한국어, Русский.
- Handoff: the search form sends
lang=xx, and every link on the page to the booking engine (Book Now buttons, menus, room links) gets?lang=xxadded, so a guest reading the Japanese site lands on the Japanese booking page. - Widget text (Arrival, Guests, calendar months/weekdays, the default button labels "Search" / "Check availability") is translated. Any other custom button label is shown exactly as written — translate it in the page itself.
- Switcher:
[karuna_language_switcher]renders the round-flag dropdown, linking each language to the translation of the page being viewed. Attributes:align="right|left",theme="dark|light"(dark for a teal header),code="on|off"(show e.g. JA beside the flag). - Polylang language locales to use:
en_US,zh_CN,zh_TW,ja,ko_KR,ru_RU. - Filters:
kbs_engine_lang(override the detected language),kbs_booking_hosts(extra hosts whose links get?lang=; anybookings.*host is already covered).
Making the rest of a Polylang site follow the language (v1.11)
Free Polylang translates posts and pages but not menus' targets, Elementor header/footer templates' text, or hard-coded links. When Polylang is active and the visitor is on a non-default language, the plugin also:
- points menu items at the translated pages (and uses their titles);
- rewrites internal links in the page (buttons, header, footer) to the
translated page, resolved by slug — links with
hreflang(the switcher) and external links are left alone; - swaps dictionary text: option
kbs_i18n_dictionary={ "ja": { "Book Now": "今すぐ予約", … }, "zh-CN": { … } }(engine language code → English text node → translation). Use it for text that lives outside Polylang's reach: shared header/footer labels, a Formidable form's labels, a chat widget. Filter:kbs_i18n_dictionary.
Free Polylang cannot share a slug between languages, so translations are stored
with a language suffix (faq → faq-ja) — but since v1.12 (includes/clean-slugs.php)
visitors never see it: translated permalinks are the default-language path under the
language prefix (/ja/faq/), clean URLs resolve to the translation, and the old
suffixed URL 301-redirects. Create translations with the suffixed slug. The theme's header/footer builder only
renders a template whose language matches the page — after enabling Polylang,
assign English to the existing header/footer templates and create translated
copies, or the site loses its header and footer.
Backend dependency
The search widget's price calendar reads GET /api/calendar on the booking
engine — the lowest-nightly-rate map, as JSON, with an
Access-Control-Allow-Origin header for karunasiargao.com. If that endpoint
or flatpickr's CDN is unreachable the widget falls back to plain native date
fields, so it always works.
The room calendar shortcode reads GET /api/calendar/room/{apartmentId} —
that room's own sold-out nights, same CORS treatment, no flatpickr dependency
(a plain read-only grid).
Both endpoints live in the booking-engine repo (bookings.karunasiargao.com),
origins controlled by CORS_ORIGINS.
No-plugin alternative
elementor-html-widget.html is the whole widget as one paste-in block (markup +
`
Releases
20 releases. Each count is every asset in that release; expand a row for the breakdown.