The Event System
The complete setup guide: every screen, every feature, in the order you will meet them.
On this page
Getting Started
This section takes you from a plugin zip to a working install: requirements, installation, the license key, the built-in setup wizard, and what the plugin creates for you automatically. Budget about an hour for the full first pass.
Requirements
The Event System builds its booking flow on top of WooCommerce, so both WooCommerce and WooCommerce Bookings must be installed and active before customers can book rooms. That applies to Pro: Lite runs the calendar and event features with no WooCommerce dependency.
| Requirement | Minimum | Why |
|---|---|---|
| WordPress | 6.0 or newer | Declared minimum for the plugin. |
| PHP | 7.4 or newer | Declared minimum for the plugin. |
| WooCommerce | Active (Pro) | Required for Pro booking features. Carts, checkout, orders, and payments all run through WooCommerce. Lite does not need it. |
| WooCommerce Bookings | Active (Pro) | Required for the booking modal and room booking. Lite does not need it. |
Lite vs Pro
The Event System comes in two builds. Lite is free and runs a public events calendar. Pro adds the entire venue operations stack. Both are the same plugin: enter a Pro license key on a Lite install under Events > Settings > License and the next update delivers the full plugin in place, keeping all of your events and settings.
| Included in Lite (free) | Pro adds |
|---|---|
| Display calendar shortcode, all six styles, plus saved calendar presets | The complete booking wizard: date and time, resources, breakout rooms, room setup, review |
| Single event pages with add-to-calendar buttons and search engine event markup | Rooms, buildings, room type pricing, floor plan designer, automatic room assignment |
| Events Dashboard and the staff Event Calendar | Staff review before payment, room holds, double-booking protection |
| One iCal feed import, events only | Unlimited feeds, room availability blocks, class schedule intake for student systems |
| Automatic image and tag rules for imported events | Add-on services with inventory, organization discounts, discount approvals |
| Taxonomy bulk importer and MEC compatibility | Eight lifecycle emails, reminders, customer self-service edits, facilities work orders |
| Free updates from the phpDoesntExist update server, no key needed | Checkout and account styling, the availability REST API, priority support |
Installing the plugin
- In WordPress admin, go to Plugins > Add New > Upload Plugin.
- Choose the plugin zip you downloaded from phpdoesntexist.com and click Install Now.
- Click Activate.
After activation you get a new top-level Events menu in the admin sidebar, and a welcome notice at the top of admin screens offering to start the setup wizard. The notice has a Start setup button and a Dismiss link. If you leave the wizard partway through, the notice changes to Resume setup and remembers which step you were on.
Entering your license key
Your license key is delivered at purchase: it appears on the order confirmation page and in your account at phpdoesntexist.com. The plugin is fully functional without a key. The key only controls automatic updates.
- Go to Events > Settings and open the License tab (it sits in the Configuration group of the Settings sidebar).
- Paste your key into the License key field. Keys look like
PDNE-XXXX-XXXX-XXXX-XXXX. - Click Save and verify. The plugin contacts the phpDoesntExist update server and shows the result immediately.
The Status row on the same tab reports one of these states, along with a Last checked timestamp:
| Status | Meaning |
|---|---|
| Active | The key verified. Automatic updates are on, and the message shows the latest available version. |
| Not valid | The update server rejected the key. Check for typos and confirm the subscription behind it is still active. Updates stay off until a valid key is entered. |
| Could not verify | The server could not be reached. The key is saved and verification is retried on the next update check. |
| No license key saved | The plugin works, but it will not receive updates. |
With a valid key, updates appear on the normal Plugins screen with the standard Update Now flow. The plugin checks for updates roughly every 12 hours, and also re-checks whenever you open the Plugins or Updates screen (throttled to once every 2 minutes).
The six-step setup wizard
Open the wizard from the welcome notice, or any time later via the Setup wizard link at the bottom of the Events > Settings sidebar. A left rail lists all six steps with green checkmarks on completed ones, so you can jump around and come back later. On Lite installs the wizard has five steps: the Rooms and Plans step is part of Pro and is skipped automatically, with the remaining steps renumbered. Every configurable step has both a Skip for now button and a Save and continue button. Skipping records the step as visited without saving anything.
| Step | Name | What it configures |
|---|---|---|
| 1 | Welcome | Nothing to save. Runs the live system check: PHP version, WordPress version, WooCommerce, WooCommerce Bookings. |
| 2 | Auto-Updates | Points you to the License tab with an Open the License tab button. If a key is already saved, the step confirms that and you can continue. |
| 3 | Features | A module on/off grid, grouped by category. Modules you turn off do not register hooks, add menu items, or run cron jobs. Modules marked CORE cannot be disabled. Every module is configurable in detail later under Events > Settings. |
| 4 | Rooms & Plans | Per-room table caps shown to customers in the Room Setup tab: Max round tables per room (default 15, range 1 to 50) and Max chairs per table (default 10, range 1 to 20). Floor plans themselves are built later at Events > Floor Plans. |
| 5 | Branding | Checkout primary colour (a hex color picker used for primary buttons and accents through checkout), plus the email sender identity: Site / sender name and Admin / reply-to address. |
| 6 | Done | A recap of visited and skipped steps, with jump-off links to Floor Plans, Room Types, Settings, and the Plugins screen. Finish setup closes the wizard and returns you to the Plugins screen. |
What gets created automatically
On activation, the plugin creates a published page titled Book an Event at the slug book-event, containing the [tes_booking_modal] shortcode. This is the customer-facing booking page. You can rename the page, change its slug, or move the shortcode to any other page. The page is created once: if a page with the slug book-event already exists, the plugin leaves it alone and never overwrites its content.
Activation also schedules the plugin's background tasks (feed sync and booking reminder crons) and creates its database tables.
Separately, on the first admin page load by an administrator, the plugin creates three legal pages if they do not already exist:
- Privacy Policy at
/privacy-policy/ - Terms & Conditions at
/terms-and-conditions/ - Refund & Cancellation Policy at
/refund-and-cancellation-policy/
If a page already exists at one of those slugs (WordPress ships a Privacy Policy draft on fresh installs), the plugin adopts it: a draft gets published, and existing content is never overwritten. The plugin also points WordPress's privacy page setting and WooCommerce's terms page setting at these pages, but only when those settings are empty. A small Privacy / Terms / Refund links strip is added to the site footer.
[tes_calendar] is not placed anywhere automatically. Add it to any page when you are ready to show a public event calendar (see the calendar section).Your first hour, in order
- Confirm WooCommerce and WooCommerce Bookings are active. The wizard's Welcome step verifies this for you.
- Enter and verify your license key on Events > Settings > License (see above).
- Run the wizard end to end: pick your modules, set the table caps, and set your checkout colour.
- Set up your rooms and map them to bookable products under Events > Room Types (see Rooms and Room Types).
- Draw a floor plan for at least one room at Events > Floor Plans (see Floor Plans).
- Open your Book an Event page on the front end and run a test booking from date pick through checkout (see the booking flow section).
- Read the three auto-created legal pages and replace the template language with your own policies.
- Optionally drop
[tes_calendar]on a page to publish your event calendar (see the calendar section).
Rooms and Facilities
Rooms are internal inventory records: customers never browse them directly. A customer books a Room Type through the booking modal (the page holding [tes_booking_modal]), and the plugin assigns specific rooms behind the scenes after checkout. This section covers how rooms enter the system, how you configure and price them, and how the automatic assigner picks rooms.
Where rooms come from
You do not type rooms in by hand. Rooms are created two ways:
- Pushed from your class schedule or SIS. When your student information system (or any script) pushes room data to the plugin, each room is created or updated with its building, room number, floor, capacity, chairs, computers, tables, square footage, and features. Rooms are grouped under a Building automatically.
- Adopted from iCal feed locations. When an imported calendar event names a location such as "TCC 430 Conference Room", the importer first tries to match it to an existing room: exact title, then the title with a descriptor suffix stripped (Conference Room, Computer Lab, Classroom, and similar), then a building-plus-number lookup. If nothing matches, a new room is created only when the setting Auto-create rooms from iCal location strings is enabled (it is off by default, under the iCal feed import module in Settings). Rooms created this way carry a blue iCal badge in the room console.
Every room defaults to bookable. You opt a room out with the Force Non-Bookable checkbox described below; non-bookable rooms still track blocked times internally.
The room console (Events > iCal Feeds)
The room-to-product mapping console lives on Events > iCal Feeds, in the Rooms section. Rooms are listed by building, with read-only columns for Room, Capacity, Computers, Type, and Features, plus these editable columns:
| Column | What it does |
|---|---|
| Mapped To | The WooCommerce bookable product this room belongs to. Options include -- Not Mapped -- and -- Non-Bookable (track only) --. Shown only when plugin pricing is off; with plugin pricing on, this mapping is maintained automatically from each room's Pricing Type. |
| Pricing Type | The Room Type that prices this room (see Room Types). Shown only when plugin pricing is on. |
| Force Non-Bookable | Admin override that permanently marks the room non-bookable. It wins over imports and every heuristic. Non-bookable rows are tinted red and show a NOT BOOKABLE badge. |
| Breakout | When checked, this specific room may be auto-assigned as a breakout room. Defaults to off, so you can keep an oversized lecture hall out of breakout duty even when its Room Type is otherwise eligible. |
| Lock | Locked rooms are protected from all auto-matching: imports will not change the product mapping or the pricing type. Locked rows are tinted yellow. |
Below the tables: Save Room Mappings saves the form, Auto-Match All Rooms smart-matches every room to a product by building, type, and name keywords (auditorium, lecture, conference, computer, classroom, and so on), and Re-map Pricing Types (visible only with plugin pricing on) auto-fills each bookable room's Pricing Type. Auto-matching never touches locked rooms and never overwrites an existing assignment unless run with force.
Room Types pricing (Events > Room Types)
Go to Events > Room Types, click + Add Type, fill in the row, then click Save All Changes. Each Room Type silently creates and maintains a hidden WooCommerce bookable product (the Booking Product column). Do not edit those products by hand: their fields are overwritten on every save.
| Field | Default | What it does |
|---|---|---|
| Label | New Type | Name of the tier, for example Classroom or Conference Center. |
| Billing Mode | per hour | per hour or per day. |
| Rate | 0 | Base price for one room, per hour or per day. |
| Room Counts | empty | Comma-separated counts a customer may book in one request. Empty means single-room only. Use count:price for tier pricing: 1, 2:475, 3:700 means 1 room at the base rate, a flat 475 total for 2 rooms, a flat 700 total for 3. |
| Customer Edit | off | When on, customers can change their Room Setup preset from My Account > My Bookings after ordering. |
| Lock (hrs before) | 24 | Hours before the event start when customer edits lock. |
| Customer Room Setup tab | off | Shows the table-layout designer in the booking modal for this type. Enable for venues, leave off for classrooms, lots, and greenspace. |
| Standard tables (per room) | 10 | Tables included in the room price, per room. |
| Std. chairs / table | 8 | Chairs included per standard table. |
| Max tables / room | 13 | Cap on tables a customer can place per room. |
| Max chairs / table | 10 | Cap on the chair slider per table. |
| Standard type | Round | Round or Banquet, the included table shape. |
| Extra table $/each (overage) | 5.00 | Charge per table above the standard quota times room count. 0 disables it. |
| Default chair kind | Padded ($2) | Drives the per-chair surcharge rate: Padded ($2) or Plastic ($1). Individual tables can override this in the Floor Plans editor. |
| Facilities buffer (hours) | 2 | Dead time held before and after an event so facilities can flip the room. Applies only on a side where the customer did not book their own setup or teardown window. Range 0 to 12; use 0 for classroom-style types. |
| Partial-day hourly rate ($) | 0.00 | When a setup or teardown day shares a calendar day with another event, bill only the hours the customer actually owns at this rate, capped at the per-day price. 0 charges the full day. |
| Min booking duration (minutes) | 60 | Shortest event the calendar will accept. 0 removes the floor. |
Room Plans global caps (Events > Room Plans)
Events > Room Plans holds two site-wide defaults for the booking-modal Room Setup tab: Max round tables per room (default 15, range 1 to 50) and Max chairs per table (default 10, range 1 to 20, applies to round and banquet tables). Click Save Changes to apply.
Floor Plans designer (Events > Floor Plans)
Events > Floor Plans stores per-room visual layouts. Each plan holds the room structure (shapes and doors) plus named table presets (banquet, classroom, theater by default, custom names allowed). Staff see the matching plan on the Booking Review page, and customers with the Room Setup tab arrange tables on top of it.
- Under Add a floor plan, enter a Name and pick a Plan Type: Single Room, 2 Connected Rooms, or 3 Connected Rooms.
- Choose the mapped room in each slot (Left, Middle, Right, ordered left to right), then click Create Plan + Open Editor.
- In the editor, draw the space with shapes (room, hallway, screen, stairs, wall, men's and women's bathrooms), place single or double doors, and drop round or banquet tables with per-table chair counts. Items support rotation, labels, locking, and a per-table chair kind override.
- Click Save Floor Plan.
Multi-room combo plans matter for multi-room bookings: when a booking covers several rooms, the plugin looks for a plan mapped to exactly that room set, then any plan with the same room count, and only then falls back to the first room's single plan. The Generate plans for unmapped rooms button bulk-creates a starter plan, with default banquet and classroom presets, for every room that has none. It is safe to re-run after new rooms import.
How rooms are assigned at checkout
When an order is placed, the assigner runs once per booking and picks from rooms mapped to the booked product that are free for the whole window, including setup and teardown days and the facilities buffer:
- Smallest room that fits wins. Rooms are scored so a 30-seat room beats a 200-seat room for a small group. The attendee-count filter only applies when Enforce room max occupancy at booking time is enabled in Settings (off by default).
- Preferred building is a soft preference. If the customer picked a building, its rooms get a large scoring bonus, but the assigner still falls back to other buildings rather than fail.
- Multi-room bookings take the top N available rooms and block all of them.
- Breakout rooms run as a second pass: only rooms with the Breakout checkbox are eligible, and the assigner picks the tightest cluster, same building first, then the closest run of room numbers, so a request tends to land as 431, 432, 433 rather than rooms scattered across campus.
The result is stamped on the order as Assigned Room (or Assigned Rooms, and Assigned Breakout Rooms), which the customer sees on the order confirmation. If staff have already set a room on a booking, the assigner leaves it alone.
The Booking Page
Customers book rooms through a single wizard that lives on your booking page, steps them through dates, extras, and layout, and ends with an add-to-cart into the normal WooCommerce checkout. This section walks the wizard tab by tab from the customer's point of view, then explains the hold system that protects their room while they check out, and finishes with the admin screen where you define the add-on services the wizard offers.
The booking page and shortcode
Place the shortcode [tes_booking_modal] on any page. The plugin's other flows assume this page lives at /book-event, which is where customers are sent when a cart item has to be removed, so create a page with that slug unless you have a reason not to. The separate [tes_calendar] shortcode renders the public availability calendar and is a different feature.
The shortcode renders the booking wizard inline on the page. It loads its own styles and scripts and deliberately disables the stock WooCommerce Bookings booking form on that page, so do not combine it with a raw Bookings product form on the same page.
The wizard shows up to five tabs. Default labels: Date & Time, Add-ons, Breakout Rooms, Room Setup, and Review. The Add-ons tab is fed by the admin screen named Resources Needed, and many sites rename the tab to match, using the "Tab" fields in the same settings section.
Date & Time
The first tab collects what, when, and how big:
- Select an event type. A dropdown (placeholder ", Choose an option , ") listing your bookable room types. With the "Use plugin Room Types for pricing" module on, each entry shows its rate, for example "Conference Center ($475/day)". Only types with a published product and at least one bookable room appear.
- Number of rooms. Shown when the product offers multi-room counts (see the note below). Each count renders as a radio, with the per-day price under it if price display is on.
- Number of attendees. Optional. If the Booking Modal setting "Enforce room max occupancy at booking time" is on (it is off by default), too-high headcounts trigger a warning and block the tab; when off, staff sort out capacity at review time.
- Preferred building. A "No preference" dropdown, shown only when rooms of the chosen type span more than one building.
- Dates and times. A calendar with start and end dates, a "Multi-day booking" option, and a "Time window (optional)" picker with start and end times. The hint reads "Times are recorded with your booking. Rates still apply per day."
Setup and teardown days
When the Setup & Teardown Time module is enabled (it is by default), the wizard adds a "Setup time" section asking "When do you need setup?" with the choices "Not needed", "Same day as event", and "1 day before event" (or more days), plus "Setup starts at" and "Setup ends at" times. A mirrored "Teardown time" section asks "When do you need teardown?" with "Same day as event end" and "1 day after event". Setup must end at or before the event start time, and teardown must start at or after the event end time.
Choosing a day before or after reserves that whole extra day, and the room is held continuously from setup start through teardown end so nothing can be booked in between. The customer sees the notice "Reserves 1 extra day. Shorter setup windows may bill hourly instead of the full daily rate." The day-before and day-after choices are controlled by the module's "Allow setup to start the day before the event" and "Allow teardown to end the day after the event" settings, both on by default.
1, 2:475, 3:700: a plain count uses the base price per room, while count:price sets a flat per-day total for that tier (useful for volume discounts). The checkbox "Display tier prices on the room count selector" is on by default.Resources Needed (the Add-ons tab)
This tab lists the add-on services you defined on the admin Resources Needed screen, grouped by category. Each item shows its label, description, and, when its Show Price box is checked, its price and billing unit. The customer ticks the items they want and sets a quantity up to the item's Max Qty.
Items billed per hour get a From and To time picker so the customer can book the service for a sub-window of the event, for example IT support from 2:00 PM to 3:30 PM. The picker defaults to the full event window. Its increment (every 15 minutes, 30 minutes, or hour, default 30) and clock format (12-hour or 24-hour, default 12-hour) come from the Booking Modal settings "Resources Needed time-picker increment" and "Resources Needed time format".
Items with a limited inventory show how many are available, and the plugin blocks over-booking across overlapping bookings at add-to-cart time. Items marked as required for the chosen room arrive pre-checked and locked, though quantity and hour window stay editable. If nothing applies to the chosen room, the tab reads "No resources available."
Breakout Rooms
Shown only when the Breakout Rooms module is enabled and at least one allowed room type has bookable rooms. The tab asks "Do you need breakout rooms for your event?" with two choices, "Yes, add breakout rooms" and "No, just the main room". Choosing yes reveals:
- "How many breakout rooms?", capped by the module's maximum per booking (default 6).
- "Room type for breakout rooms", a dropdown of the allowed types with their rates.
- "Preferred building (optional)", listing only buildings that actually contain rooms of the chosen type. It is skipped when there is nothing to choose between.
- "Use the same time as the main booking", or a separate time window for the breakout rooms.
Breakout rooms are reserved for the same dates as the main booking and appear as their own line on the Review tab.
Room Setup
This tab appears only for room types that have the room setup designer enabled in their Room Type configuration. The customer arranges tables and chairs on the room's floor plan, or picks a preset layout. For a multi-room booking, the wizard shows one floor plan per booked room, side by side.
Each room type defines a standard allotment of tables and chairs. Anything the customer adds beyond it is priced as an overage using the room type's extra table and extra chair rates, and the Review total updates live as they drag. Hard caps on tables per room and chairs per table are enforced in the wizard.
Review and add to cart
The Review tab summarizes the booking with line items: "Base booking", "Add-ons", "Breakout rooms", "Setup time" and "Teardown time", "Setup (extra days)" and "Teardown (extra days)" when applicable, then "Subtotal" and "Total". The customer finishes with the Confirm & add to cart button, which places the booking in the WooCommerce cart and re-verifies availability on the server.
If the chosen window can no longer be satisfied, the wizard says so instead of submitting: "No rooms available for this time" when everything is taken, or a partial message when fewer rooms are free than requested, along with a list of where the conflicts are and a "Change date or time" prompt.
Room holds: how the customer is protected
The moment a booking is added to the cart, the plugin writes a room hold that reserves a specific physical room for that customer's window, including any setup and teardown days. Other customers attempting the same room and time are turned away at add-to-cart with a notice explaining the slot was just held by another customer and that cart holds release automatically after 60 minutes.
| Stage | Hold state | Expires |
|---|---|---|
| In cart | Pending cart | 60 minutes after add-to-cart |
| Order placed | Pending review | Never, staff review has no deadline |
| Booking approved, awaiting payment | Awaiting payment | After the admin-set payment window (hours) |
| Paid | Paid | Never |
A background task runs every 5 minutes to release any hold past its expiry, so an abandoned cart cannot camp on a room. Removing the item from the cart, or emptying the cart, releases the hold immediately. Approved orders that go unpaid past the payment window are cancelled automatically, which frees their rooms.
/book-event/. The customer is never allowed to pay for a room that can no longer be assigned. The same check also stops one customer from adding two overlapping bookings of the same room product to their own cart.Sign in before checkout
The Booking Modal setting "Require login to complete a booking" is on by default. When a guest with a booking in the cart reaches checkout, they see the notice "Please sign in or create an account to complete your booking." and are redirected to the My Account login page. After logging in or registering they are returned straight to checkout, and the order is tied to their account so it appears in My Bookings later. Non-booking products in a mixed cart do not trigger the gate. Turn the setting off only if you genuinely need guest bookings.
The branded empty-cart page
When the cart drains, for example after an expired hold was removed, WooCommerce would normally show a bare "cart is empty" notice or a blank checkout. The Empty Cart Checkout Screen module (on by default) replaces that with a branded page: an animated empty-cart graphic, a heading ("Your cart is empty"), body copy ("Looks like there's nothing waiting for you here. Pick a date and a room to get started."), and a button ("Browse events") that links to /book-event/. Heading, body, button text, URL, colors, and sizes are all editable under Events > Settings in the Empty Cart Checkout Screen module.
Admin: defining resources on the Resources Needed screen
Go to Events > Resources Needed. This screen defines the optional resources customers can add to a booking, such as staff, equipment, furniture, or food. Click + Add Category to create a group (Category Label, Order, and an optional Description shown under the category title), then + Add Item to this Category for each service. Save with Save All Changes.
| Column | What it does | Default |
|---|---|---|
| Item Label | Name the customer sees | New Item |
| Price | Dollar amount per unit | 0 |
| Unit | Billing unit: each, hour, day, or flat | each |
| Max Qty | Highest quantity one customer can pick | 100 |
| Min hrs | Minimum hours for hour-billed items, 0 means no minimum. The maximum is always the event duration itself | 0 |
| Avail | Inventory cap, how many exist across all customers at the same time, 0 means unlimited | 0 |
| Show Price | Whether the price is displayed to the customer | Checked |
| Description | Short helper text under the item | Empty |
Under every item sits a per-room grid with one row per bookable room product and two checkboxes: Show (the customer sees this item as an option for that room) and Require (the item is pre-checked and locked when that room is picked, while quantity and hour window stay editable). Two master toggles, "Show in all rooms" and "Require in all rooms", fill or clear a whole column at once. Leaving every Show box unchecked with "Show in all rooms" on means the item appears for every room.
Reviewing Booking Requests
The Event System does not let customers pay at checkout. Every booking, from every room and every product, is routed to your staff first. You review the request, adjust anything that needs adjusting, and only then decide whether the customer gets a payment link. This section walks through that workflow end to end.
How review-before-payment works
When a customer submits a request through the booking form (the [tes_booking_modal] shortcode), the plugin forces the resulting WooCommerce order into a plugin-owned order status called Pending Review. This happens regardless of whether "Requires Confirmation" is checked on the product: the plugin intercepts the order at several points so no booking can slip through to a payment gateway. Orders in Pending Review are deliberately not payable, so no money moves until you act.
- The customer sees checkout copy saying their booking is "being reviewed" instead of a payment prompt.
- The Pending Review status appears alongside the normal statuses in WooCommerce > Orders, and orders in it stay editable.
- While a customer has a booking in their cart but has not submitted it, the slot is held for 60 minutes. If they never submit, the hold releases and the slot opens up again.
The Booking Requests screen
Go to Events > Booking Requests. The list shows up to 100 orders with columns for Order, Requester, Product, Booking window, Room, Status, and Submitted. Four filter tabs sit above the table:
| Tab | What it shows |
|---|---|
| Pending review | Requests still waiting on a decision (Pending Review, plus WooCommerce Bookings' own pending-confirmation status). |
| Approved | Requests you approved: Pending payment (invoice sent, unpaid), Processing, On hold, and Completed. Non-booking orders are filtered out. |
| Declined / cancelled | Cancelled, Failed, and Refunded orders, including bookings auto-cancelled for non-payment. |
| All | Every order status. |
Each row has a Review button that opens the detail page and a Delete link. The toolbar also offers Delete All in this view and a Repair corrupt bookings utility that heals bookings whose stored times were damaged by earlier plugin builds (safe to run any time).
Reviewing and editing a request
Click Review to open the detail page. Everything the customer submitted is editable before you decide:
- Requester panel. Name, Email, Phone, Department / company, Address, and City / state are click-to-edit: click a value, type, then press Enter or click away to save. Esc cancels. An Organization & insurance panel shows the customer's organization type, the discount percent applied at checkout (click the number to override it), and their proof-of-insurance PDF, with a "Not on file" warning when it is missing.
- Event details. Adjust Start and End with date-time pickers and set Expected attendees, which is used to check whether the assigned room has enough capacity. These fields save on change, no Save button needed. Changing the times also updates the room calendar and conflict checks.
- Room assignment. A "Currently assigned" line shows the room or rooms on the booking, and a dropdown lets you reassign. Rooms are grouped under Available (with building, capacity, and a suitability score) and Conflicts (override with care), which lists rooms already taken for that window along with what they conflict with. The selection saves on change.
- Facilities specifics. A set of Y / N / NA radio choices that carry over to the facilities work-order ticket if you use that integration.
The itemized bill and fees
The Customer-selected services table is the single itemized bill: the room base price, every add-on the customer picked, chair and table surcharges, and any fees you add, each with Description, Detail, and Amount columns. Below it, an Order total banner shows exactly what the customer will pay.
To bill something extra, use Add a fee or service:
- Pick a service from the dropdown. It lists your Extra Services catalogue (the same one managed under Events > Resources Needed) with each item's price and unit, or choose the custom option to type a free-text description and unit price.
- Set a Quantity (defaults to 1). A live Total preview updates as you type.
- Click Add fee. The fee joins the itemized list with a remove link.
Approve, Approve with changes, Decline
The Decision panel has one Message to requester box and three decision tabs. Your message drops into the {message} slot of whichever email template the decision uses. A live Email preview below the tabs shows the exact subject line and body that will send, updating on every keystroke; the preview and the real email come from the same renderer, so what you see is what sends.
| Decision | What happens | Email sent |
|---|---|---|
| Approve | Bookings are confirmed, the order moves to Pending payment, and the approval time is stamped for the payment window. Message is optional. | Subject "Your Event Reservation Has Been Approved" plus the product name. Includes room, attendees, order number, total, and the secure payment link. |
| Approve with changes | Same as Approve, but your message is required and must describe the changes you made. | Subject "Your Event Reservation Has Been Approved with Recommended Changes" plus the product name. Leads with your change summary, then the updated details, updated total, and the payment link. |
| Decline | Bookings are cancelled, the order moves to Cancelled, and the rooms are released. A reason is required, and a confirmation prompt appears before sending. | Subject "Update on Your Event Reservation Request" plus the product name. Includes your reason and invites the requester to reply about alternate dates or rooms. |
The send button label changes to match the selected decision, for example "Approve & send email" or "Decline & send email". If a required message is empty, the page warns "A message is required for this decision." A separate Save changes button stores your notes and facilities picks without deciding or emailing, so you can come back later. Approving also fires the work-order push to your facilities system when that integration is configured.
Payment links, the payment window, and gateways
Approval emails carry a secure payment link: WooCommerce's pay-for-order URL, unique to that order and keyed to it, which opens a payment form for the exact approved total. The customer can also log in and pay from My Account > Orders. Before approval neither route works, because orders under review are excluded from payable statuses.
Two settings in Events > Settings (Booking category) govern what happens next:
| Setting | Module | Default | What it does |
|---|---|---|---|
| Payment window after approval (hours) | Room Hold Ledger | 168 (seven days; range 1 to 720) | How long an approved booking stays held while awaiting payment. A sweep runs every 5 minutes; once the window expires, the order is auto-cancelled with an order note, its bookings are cancelled, the rooms return to availability, and the event leaves the site calendar. The order then appears under the Declined / cancelled tab. |
| Allowed gateways for booking orders | Booking Payment Gateways | None checked (all enabled gateways show) | A checklist of payment methods, pulled live from WooCommerce > Settings > Payments, that customers see when paying for a booking order. Use it to hide defaults like bank transfer, check, or cash on delivery from booking payments. It only affects orders containing a booking. |
Emails and Reminders
The Event System sends its own set of booking emails so your customers and your events office see clear, venue-specific messages instead of generic store copy. Every one of these emails is editable from a single admin page, and three of them are automated pre-event reminders driven by a scheduled job.
The eight email templates
The plugin ships eight templates. Each has a default subject and body that work out of the box, so you can launch without touching them and reword later.
| Template (as titled on the page) | When it sends | Who receives it |
|---|---|---|
| New booking request (staff alert) | The moment a customer submits a reservation request at checkout | The staff addresses in its Send to field, never the customer |
| Submitted (immediate acknowledgment) | The same moment, right alongside the staff alert | The customer's billing email |
| Approved | When staff approve a request as submitted on the Booking Review screen | The customer |
| Approved with changes | When staff approve a request after modifying it (fees, times, room, attendees) | The customer |
| Declined | When staff decline a request | The customer |
| Payment reminder (14 days out) | By the reminder job, when the order still has an unpaid balance 14 days before the event | The customer |
| Event reminder (7 days out) | By the reminder job, for confirmed bookings 7 days before the event | The customer |
| Event reminder (3 days out) | By the reminder job, for confirmed bookings 3 days before the event | The customer |
Editing templates: the Booking Emails tab
All eight templates live on one page: Events > Settings > Booking Emails (the Booking Emails tab sits in the Configuration group of the Settings sidebar). Administrators and users with the Events Manager role can edit here.
- Open Events > Settings > Booking Emails.
- Each template shows a Subject field and a Body box. Line breaks are preserved and basic HTML is allowed in the body.
- Click into the field you want to change, then click any merge tag chip at the top of the page to insert it at your cursor. If no field is focused, clicking a chip copies the tag to your clipboard instead.
- Click Save email templates at the bottom. A "Email templates saved" notice confirms the save.
Merge tags
Merge tags are placeholders in curly braces that the plugin swaps for real order data at send time. They work in both subjects and bodies of every template.
| Tag | Renders as |
|---|---|
{first_name_billing}, {last_name_billing}, {full_name_billing} | The customer's billing name |
{email_billing}, {phone_billing}, {company_billing} | Billing email, phone, and company or department |
{order_number}, {order_total} | Order number and formatted total |
{product_name} | The booked product name (first item on the order) |
{booking_date}, {booking_time} | The event date (for example Jul 24, 2026) and the start-to-end time range, in your site's timezone |
{room_name}, {building_name} | The assigned room name(s) and building(s). Multi-room bookings list every assigned room |
{attendee_count} | Expected attendee count |
{pay_link} | The secure payment link for the order. On a $0 order it renders a sentence saying no payment is required instead of a link |
{edit_setup_link}, {edit_deadline_date}, {edit_setup_section} | The customer's self-service room setup editing link, its deadline, and a full ready-made paragraph combining both. All three render empty once editing is disabled or locked |
{message} | The message the reviewer types on the Booking Review screen (approval notes, change summary, or decline reason) |
{changes_summary}, {decline_reason}, {admin_notes} | Aliases for {message}, kept so older saved templates keep working |
{site_name}, {support_email} | Your site name and the WordPress admin email |
{review_link}, {booking_details} | For the staff alert: a direct admin link to the request, and a full booking summary block (setup deadline, setup, event and teardown windows, room layout, resources, assigned rooms, and a Review button) |
{message} or one of its aliases more than once, only the first occurrence renders. Later duplicates are stripped automatically so the reviewer's note never repeats.Who receives what
The staff alert is the only template that goes to staff. Its Send to field takes a comma-separated list of addresses (semicolons and new lines also work, and duplicates are removed). Leave it empty and no one is alerted; the customer never receives this email either way. Replying to a staff alert goes straight to the requester, because its reply address is the customer's billing email.
Every other template goes only to the customer's billing email. There are no staff copies or BCCs on customer emails. Replies to customer emails go to your WordPress admin email address.
All booking emails send from your site name and the WordPress admin email address, except reminders, which can use their own from-address (see reminder scheduling).
How the plugin replaces WooCommerce's emails
When an order contains at least one booking, the plugin suppresses WooCommerce's stock "Processing order" customer email and sends the Submitted acknowledgment instead, so the requester gets exactly one confirmation written for the reservation-request flow. This happens once per order, even if checkout hooks re-fire. Orders with no bookings (merchandise, other products) keep the normal WooCommerce emails.
Approval and decline emails
The three decision emails are triggered by the Approve, Approve with changes, and Decline actions on the Booking Review screen. Whatever message the reviewer types there fills the {message} slot, and the review screen shows a live preview that is identical to what actually mails, built from the same template and data.
- Both approve variants include
{pay_link}, the secure link where the customer completes payment for the order. - On a fully discounted $0 order, the payment paragraph is swapped for a sentence stating that no payment is required, since the pay page would reject a zero-total order.
- Both approve variants automatically carry the self-service setup editing section while customer editing is open, even if your saved template predates the
{edit_setup_section}tag. The section disappears on its own once editing is locked. - The decline email carries the reason the reviewer entered in
{message}.
Reminder scheduling and the from-address
Reminders are controlled by the Pre-Event Email Reminders module in Events > Settings (Booking category). It is on by default; switch the module off to stop all three reminders.
A scheduled job is set for 8:00 AM the following day in your site's timezone and then repeats hourly. Each hourly run checks upcoming bookings and sends:
- Payment reminder (14 days out): only to orders that still need payment.
- Event reminders (7 and 3 days out): to confirmed bookings whose order is not cancelled, refunded, failed, or trashed.
Each window covers the full calendar day that many days ahead, and each reminder fires at most once per booking; a sent flag on the order keeps the hourly job from repeating itself. A run sends at most 10 reminder emails, with the soonest events first; anything over the cap goes out on the next hourly run.
The module has two optional fields: Reminder "From" email address and Reminder "From" name. These apply only to the three reminder emails, useful for routing them through a dedicated noreply@ address. Left blank, reminders use the WordPress admin email and site name like every other booking email. No other plugin or WordPress email is affected by these fields.
Delivery logging
Every send, and every failure, is recorded as a note on the WooCommerce order, prefixed with [Booking emails]. The note names the template, the recipient, and the subject, or the failure reason. If a customer says they never got an email, check the order's notes first. A note saying wp_mail returned false usually means the server has no SMTP mailer configured; install and configure an SMTP plugin, then re-test.
Customer Self-Service
Once a booking is submitted, customers do not have to call your events office for every layout tweak. The Event System adds a My Bookings tab to the WooCommerce My Account page where customers can see their bookings and, if you allow it, rearrange their room setup themselves up to a cutoff you control. Anything that changes the price goes back through your staff for approval.
The My Bookings tab
Logged-in customers see a My Bookings tab in My Account, inserted directly after the standard Orders tab. It lists one row per booked room line, with these columns: Order, Date / Time, Room Type, Current Setup, and Status.
The Status column shows a colored badge:
| Badge | Meaning | Action shown |
|---|---|---|
| Editable (green) | Editing is enabled for this Room Type and the cutoff has not passed. | An Edit setup button. |
| Locked (red) | Editing was allowed, but the booking is now inside the lock window. | The text Cutoff passed. |
| Not editable (gray) | Self-service editing is off for this Room Type. | None. |
The list also picks up guest checkouts: if a customer booked without an account and later signs in with the same billing email, those bookings appear in their My Bookings tab automatically.
/my-account/bookings/ to the new My Bookings tab, so old links in order emails still land in the right place.Turning editing on and setting the cutoff
There are two controls, and it matters which one is in charge.
Global deadline (active by default)
Under Events > Settings, in the Booking Modal module, the field Customer setup-edit deadline (days before event) sets one sitewide cutoff. The default is 14 days. While this value is greater than 0, it wins over the per-type settings and it force-enables self-service editing for every Room Type that has the Room Setup designer turned on (the Customer Room Setup tab checkbox in that type's Setup config). Set it to 0 to hand control back to the per-type settings below.
Per-Room-Type settings (used when the global deadline is 0)
On Events > Room Types, each type row has two columns:
| Column | Default | What it does |
|---|---|---|
| Customer Edit (Allow checkbox) | Off | When on, customers can change their room setup for bookings of this type from My Account after submitting the order. |
| Lock (hrs before) | 24 | How many hours before the event starts to lock customer edits. After the cutoff the customer sees the Locked badge instead of an edit link. Ignored when Customer Edit is off. |
The cutoff is measured against the event's real start time, including the start time of day the customer chose. A 24-hour lock before a 2:00 pm event locks at 2:00 pm the previous day, not at midnight.
What customers can edit
The scope is deliberately narrow: customers can change the room setup only, meaning the table and chair arrangement and the layout preset. Dates, times, attendee counts, add-on resources, and the assigned room stay staff-only, because those affect pricing and room assignment.
Clicking Edit setup opens an Edit Room Setup page showing the order number and event date. The same interactive setup designer customers used at booking time (the one embedded by the [tes_booking_modal] shortcode) mounts inline in edit-only mode: it opens straight to the setup canvas, pre-loaded with the customer's current arrangement, with all other booking steps hidden. Saving writes the new layout to the order immediately, and the printed room layout line on the order page and in WooCommerce emails is refreshed to match.
After saving, the customer is returned to My Bookings with the notice Your room setup has been updated. If they try to save after the cutoff they see That booking is past its edit cutoff and can no longer be changed.
When an edit changes the price
Extra tables and chairs beyond the Room Type's standard quota carry overage charges, so a customer edit can move the booking total. When that happens, the new layout still saves right away, but the order is flagged for staff re-review. The price does not change until you act.
On Events > Booking Requests, the affected booking shows a PENDING RE-REVIEW banner at the top of its detail view, reading The customer edited their room setup. The banner includes a before-and-after table (Tables, Chairs total, Setup overage) and the Price delta, plus two buttons:
- Approve change: applies the delta to the order line total and recalculates the order. If the order is already paid (status Processing or Completed) and the customer owes more, the button reads Approve & create top-up order instead. Approving then auto-creates a separate WooCommerce order in Pending status with a single fee line named Setup change top-up for order #N, copying the customer's billing details. Send the customer the payment link for that order; the two orders are cross-linked in their order notes.
- Reject & revert: restores the customer's tables to the exact pre-edit arrangement and leaves the price untouched.
If the customer edits several times before you decide, the banner always compares the latest layout against the original baseline, so the delta reflects the cumulative change since your last decision.
The no-JavaScript fallback
The Edit Room Setup page also renders a plain HTML form beneath the interactive designer. When the designer loads successfully, the form is hidden. If JavaScript fails or is disabled, the customer still sees a simple Room Setup preset dropdown listing the named layout presets from the room's floor plan (for example Banquet, Classroom, Theater), with Save changes and Cancel controls.
The dropdown offers presets from the assigned room's floor plan. If staff have not assigned a room yet, it falls back to a plan attached to any room of the same type. If no plan exists at all, the dropdown shows a single placeholder saying no layouts are available and staff will choose.
Calendars & Events
The Event System gives you two kinds of calendars: public calendars you place on pages with the [tes_calendar] shortcode, and staff calendars inside wp-admin for planning and room scheduling. This section covers both, plus single event pages, the Events Dashboard, the Create Event portal, and the automation that tags and illustrates imported events.
The [tes_calendar] shortcode
Add [tes_calendar] to any page or post to show a display-only calendar of your events. Visitors can page between months with the built-in previous and next arrows; the calendar swaps in place without a full page reload. There is no booking on this surface, it is purely for browsing.
Six visual styles are available through the style attribute:
| Style | What it looks like |
|---|---|
modern-grid | Full month grid. Up to three event pills per day; a +N more button opens a popover listing the rest of that day. This is the default. |
classic-list | Chronological rows with a date block, title, time, location, and excerpt. Shows today through the next 90 days. |
card-grid | Responsive cards with the featured image on top and date, title, time, location, and excerpt below. Today through 90 days. |
timeline | Vertical timeline grouped by date along a colored spine. Today through 90 days. |
agenda-week | A single seven-day strip. Shows the current week when viewing the current month. |
card-modern | Full month grid with a live search box, Tags and Categories filter dropdowns, removable filter chips, a Clear filters button, and a Today jump button. Event pills are color-coded by category and show a hover popover with description, duration, location, and category and tag badges. Filtering happens instantly in the browser. |
Supported attributes:
| Attribute | Purpose | Default |
|---|---|---|
style | One of the six style names above. | modern-grid, or the default style set in Display Calendar settings |
id | Loads a Saved Calendar configuration (see Saved Calendars). | none |
tags | Comma-separated tag slugs; only events with those tags appear. | all events |
categories | Comma-separated event category slugs. | all events |
month | Starting month in YYYY-MM form. | current month |
limit | Caps how many events render, mainly useful for the list styles. | no cap |
accent | Hex color for highlights, for example #4f46e5. | Display Calendar accent setting |
Example: [tes_calendar style="card-modern" tags="athletics,recruitment" accent="#0f766e"]
Saved Calendars
Instead of repeating attributes on every page, save a calendar configuration once under Settings > Booking > Display Calendar, in the Saved Calendars panel.
- Click + Add Calendar.
- Enter a Calendar name. A slug is derived automatically and the shortcode preview updates as you type.
- Set the Visual style, Tag filter (Ctrl or Cmd-click for multiple; empty means all; only tags actually attached to events are listed), the MEC category filter when categories exist, an Accent colour, and a Limit (0 = no limit).
- Click Save Changes, then click the shortcode line on the card, for example
[tes_calendar id="upcoming-public-events"], to copy it to your clipboard.
Attributes you add to the shortcode override the saved values, so one saved filter can power several pages with different styles.
card-modern with a saved calendar, add it as an attribute: [tes_calendar id="your-slug" style="card-modern"].Single event pages
Clicking any calendar event opens its own page. The plugin replaces the theme's generic post layout with an event template, configured under Settings > Booking > Event Page. Three layouts are available: standard (hero band with breadcrumb, title, date, time, and location meta, registration button, and the image alongside), hero (full-width banner image with the title overlaid), and sidebar (content on the left, details column on the right, stacking on mobile). Toggles let you hide the image, time, location, tags, or back link, and you can set the breadcrumb labels and the event URL slug (default event).
Every event page with a valid start date gets an Add to calendar control offering Google Calendar, Outlook, and Apple Calendar / .ics file. If the event has a registration URL, a registration button appears (label configurable, default Register). The page also prints Schema.org Event markup in the page head, so search engines can show the event with dates, location, and image in event-rich results.
Events Dashboard
Events > Events Dashboard is the staff list of every event, titled All Events. You can search by name and filter by status (Upcoming, This month, Past, All events, each with a live count), by location (rooms and locations grouped in one dropdown), by tag, and by source (Created in Events Dashboard, Created via Event Creator, Manually / legacy (no source), or a specific iCal feed). The table shows Event, When, Where, Tags, Source, and Status, with Edit and View buttons per row.
The built-in editor (via Add New or Edit) covers the title, start and end dates, an All-day event checkbox, start and end times, description, tags, registration URL, and featured image. The Where card accepts a bookable Room, a Location post, or a free-text fallback; if more than one is set, Room wins, then Location post, then free text, and every surface (calendar, dashboard, event page) uses that same precedence.
Create Event portal
Events > Create Event is a guided form for scheduling an event that also blocks the rooms it uses. It walks through four steps:
- Basics: event title (required), category, description, tags, and an optional registration URL.
- Visibility: Public (shows on the public events calendar, rooms blocked for setup, event, and teardown windows) or Internal (hidden from the public calendar, rooms still blocked and visible to staff in availability views).
- Primary Room: filter by Building and Min capacity, then pick a room. The dropdown checks live availability against your entered times; rooms with a conflict are marked and disabled, and a badge on the card reads Available or Conflict with details of the clashing booking.
- Breakout Rooms (optional): add as many as needed with + Add breakout room. Each breakout has its own setup, event, and teardown windows, independent of the primary room.
Each room card has three time rows, Setup, Event, and Teardown, each with start and end pickers. The event window is required for the primary room; setup and teardown are optional but need both a start and an end when used. If the optional simple mode is turned on in settings, the setup and teardown rows are replaced by Setup duration and Teardown duration dropdowns, and the windows are calculated to run immediately before and after the event. The Create Event button disables while any room shows a conflict, and the server re-checks all windows on save, so double-booking a room is blocked even if two people submit at once.
Access is controlled by the tp_create_event capability. Administrators always have it. To grant it to other staff, go to Events > Event Permissions (the Event Creator Permissions page), tick the users who should be able to create events, and click Save Permissions.
Staff calendars: Event Calendar and Room Calendar
Events > Event Calendar is a printable month view of everything happening at the venue: paid room bookings and calendar events together. Day chips are color-coded by phase, and the legend reads Setup, Event (internal), Event (external), Teardown, with a dashed border meaning pending review. Setup and teardown days appear on the days they actually occupy, so multi-day prep is visible even when the event itself falls in another month. Toolbar controls give you Prev, Today, Next, filters (All, Internal only, External only), a Grid or List view toggle, and a Print this view button that strips the admin chrome for a clean printout. The List view is a day-by-day prep sheet showing rooms, the full setup-to-teardown span, attendee count, and contact details, omitting any field that is empty.
Internal versus external is classified automatically: bookings by the customer's billing email domain (a domain matching your site counts as internal; developers can adjust the list with the tes_internal_domains filter), events created through Create Event as internal, and iCal-imported events as external.
Events > Room Calendar answers a different question: what is on one specific room. Pick a Building, then a Room, and the calendar shows every block on that room in month, week, day, or list view. The Show Sources toggles let you switch each color-coded source on or off: Classes (push), iCal events, Admin-created, and Bookings. A sidebar panel shows the room's capacity and type plus counts of blocks this week and today, and clicking any block opens a detail dialog with when, room, and source. Multiple class sections meeting in the same room at the same time collapse into a single block labeled with the section count, so the room reads as occupied once rather than several times.
Automatic tagging and image rules for imported events
Two automations tidy up events as they arrive from iCal feeds. Both run at import time only; they do not retroactively change events already on the site.
Keyword tagging. Each label term (the mec_label taxonomy) gains two fields on its add and edit screens: Match keywords (comma-separated; an imported event whose title contains any keyword, case-insensitive, gets the label automatically) and Photo. When a label is auto-assigned, the first matching label's photo becomes the event's featured image, but only if the event does not already have one. The label list table shows Keywords and Photo columns so you can review your rules at a glance.
Event Image Rules. On the plugin's Settings screen, the Event Image Rules tab holds a simple table of Keyword and Image URL pairs. When a feed imports an event, the title is checked against each keyword in order; the first match wins, its image is downloaded into your Media Library, and it is set as the featured image. Use + Add rule and Save rules to manage the list.
Imports & Integrations
The Event System can pull schedules in from outside systems and push booking details out to a facilities team. This section covers iCal feeds, the class-schedule endpoint for student information systems, the Support Candy work-order push, the bulk taxonomy importer, and the public REST endpoints.
iCal feeds
Go to Events > iCal Feeds (the page is titled iCal Feed Importer). Each feed runs on its own schedule via WP-Cron.
- Under Add New Feed, enter a Feed Name (for example, Campus Events) and the Feed URL, the URL to the .ics iCal feed.
- Choose an Import Interval: Every 15 Minutes, Every 30 Minutes, Hourly, Twice Daily, Daily (the default), or Weekly.
- Choose an Import Mode (see Import modes). The default is Both.
- Leave Auto-Import ("Enable automatic re-importing on schedule") checked, then click Add Feed.
The Configured Feeds table lists each feed with its interval, an Active or Inactive status, and Last Import counts (new, updated, blocks, plus skipped and failed when present). Use Import Now on a row to run one feed immediately, or Import All Now to run every enabled feed. To inspect what came in, open Events > iCal Feed Data: it has an All Feeds tab plus one tab per feed, showing each event's name, start, end, duration, room, building, and description.
When an event's location names several rooms (for example "TCC 430 & TCC 431"), the importer records a separate time block for each room and matches each name against your existing room list. Events whose start date is more than 4 days in the past are skipped, and events that disappear from the feed upstream are removed on the next import.
Import modes and what switching cleans up
The Import Mode controls what each feed produces. The exact options are:
| Mode | What it creates | What it removes |
|---|---|---|
| Blocks Only (room availability, no public events) | Time blocks that mark rooms busy | Deletes any calendar events this feed created earlier |
| Events Only (MEC calendar, no room blocking) | Public calendar events | Deletes this feed's stored time blocks |
| Both (MEC events + room blocking) | Calendar events and time blocks | Nothing |
| Off (import disabled) | Nothing, the feed is skipped | Deletes both the feed's events and its blocks |
MEC compatibility
Imported events are stored in Modern Events Calendar's format: an mec-events post with the standard mec_* date and time fields, locations as mec_location entries, and categories in the mec_category taxonomy. MEC itself is not required. When it is absent, the plugin registers the same post types and taxonomies itself and the bundled calendar reads the data directly, so events appear on any page using [tes_calendar]. If MEC is installed, it sees the same events.
Deduplication uses the iCal UID, so re-imports update existing events instead of duplicating them. iCal CATEGORIES values become regular WordPress tags, and a feed-supplied event type (when present) becomes the event's mec_category term.
Class schedule intake (SIS push)
A student information system can push term schedules directly to the site. The Class Schedule REST Endpoint panel at the bottom of Events > iCal Feeds shows everything the sender needs: the Endpoint (POST) URL, a Ping (GET, auth test) URL, the API Key, and the Header Name (X-TestPlugin-Key). The sender includes the key in that header, or as Authorization: Bearer <key>, on every request.
A push updates several things at once:
- Rooms are created or updated with capacity, floor, and feature metadata, and auto-mapped to your existing bookable products. A push never creates WooCommerce products.
- Buildings, courses, and sections are stored as reference data.
- Class meetings become time blocks that make rooms unavailable for booking. With the default replace scope ("all"), blocks missing from the latest push are pruned automatically.
The panel also shows the Last Received Payload (time, content type, byte count, a raw preview, the push metadata, and the exact response returned), which is the first place to look when a push does not land. Use Rotate Key to issue a new key; the old one stops working immediately, so update the sending system at the same time.
Work-order push (Support Candy)
When staff approve a booking, the plugin can create a ticket on a separate facilities site running Support Candy. Configure it in Events > Settings > Booking Modal under the heading Work order integration (Support Candy).
| Setting | Notes | Default |
|---|---|---|
| Push approved bookings to the work-order site | Master toggle. Failures are logged and noted on the order but never block the approval. | Off |
| Endpoint URL | Support Candy's ticket REST endpoint (commonly /wp-json/wpsc/v2/tickets) for App Password mode, or the companion receiver's intake URL for HMAC mode. | Empty |
| Authentication method | WordPress Application Password (recommended) or HMAC shared secret (companion MU-plugin). | App Password |
| Application username / Application Password | Credentials from Users > Profile > Application Passwords on the work-order site. Include the spaces in the password. | Empty |
| HMAC shared secret (only when using HMAC mode) | A long random string both sites know. The shipped receiver plugin on the work-order site verifies an HMAC-SHA256 signature before creating the ticket. | Empty |
| Support Candy category / priority / initial status ID | Optional routing so tickets land in the right queue. | 0 (Support Candy default) |
| Setup-ready buffer (minutes before event) | Drives the room-ready deadline printed on the ticket. | 30 |
The ticket contains the booking end to end: a "Booked by (event contact)" block with the customer's name, email, and phone, a "Please have the room ready by" deadline, the customer setup, event, and teardown timeline, event information with a link back to the booking, the Facilities Y/N support-services rows, the room layout summary, the floor plan image (a screenshot of the review canvas when captured, otherwise a server-rendered vector version, hosted on the events site and embedded in the ticket), admin notes, and the paid addons list. One booking gets one ticket: approving the same booking again posts an update into the existing ticket rather than opening a new one.
Use the Test work-order connection panel on the same tab before going live. Run connection test sends two probes with your saved credentials: an authentication check against the work-order site, then a real test ticket with the subject prefix [PLUGIN TEST]. The panel prints the raw responses so you can see exactly what Support Candy said. Delete the test ticket afterward.
Taxonomy Importer
To load category and label lists in bulk, open Events > Settings > Taxonomy Importer (under Configuration in the settings sidebar). Paste one JSON document and click Run import. It supports three taxonomies: mec_category, post_tag, and mec_label.
- Each term entry needs a
name. Optional keys:slug,description, andparent(the slug of the parent term). mec_labelentries can also carrykeywordsfor the event auto-tagger and eitherthumbnail_idorthumbnail_url(the URL is downloaded into your media library).- Terms with an existing slug are updated, not duplicated, so re-running an import is safe.
- An optional
cleararray wipes the listed taxonomies before importing. You must also check the confirmation checkbox, and the deletion cannot be undone.
A schema reference with a working sample is printed on the page, and the results notice lists created and updated counts per taxonomy plus any errors.
REST API overview
The booking form rendered by [tes_booking_modal] talks to a set of public, read-only availability endpoints under /wp-json/testplugin/v1/. They need no key, and they never write bookings; actual booking writes go through the WooCommerce cart.
| Endpoint (GET) | Purpose | Key parameters |
|---|---|---|
/availability/check | Which rooms under a product are free for a specific window | product_id, start, end; optional min_capacity, needs_computers, needs_accessible, units, preferred_building |
/availability/slots | Open time slots on one date | product_id, date, duration (15 to 1440 minutes); day_start default 08:00, day_end default 22:00 |
/availability/products | Bookable products with their room counts | None |
/availability/hold-windows | Precomputed blocked ranges for a calendar view | product_id, from, to |
/availability/occupancy | Every booked, held, or blocked interval in a date range | product_id, from, to |
/availability/blocked-days | Days that are fully booked or unavailable, cached 5 minutes | product_id; months default 6 |
/availability/extra-service | Remaining inventory for an Extra Services item in a window | item_id, date, time_start, time_end |
The only inbound write endpoint is the class-schedule POST described above, and it always requires the API key.
Styling and Branding
The Event System ships with a neutral look you can rebrand in two layers. A single accent color drives the defaults for every plugin surface, and dedicated settings tabs let you fine-tune the checkout, the thank-you page, and the empty-cart landing page without writing CSS. The plugin also creates a set of starter legal pages and links them in a small footer strip.
The accent color system
Out of the box, every plugin surface uses a neutral indigo: #4f46e5, with a darker hover shade #4338ca. This pair flows into the booking form rendered by [tes_booking_modal], the calendar rendered by [tes_calendar], the event page, admin email buttons, and the default colors on several settings pages. The plugin prints the pair as the CSS variables --tes-accent and --tes-accent-hover on every front-end and admin page.
To rebrand everything at once, a developer can hook two filters, typically in a child theme's functions.php or a small mu-plugin:
tes_accent_color, for exampleadd_filter('tes_accent_color', function () { return '#7a0019'; });tes_accent_hover_colorfor the hover shade, for exampleadd_filter('tes_accent_hover_color', function () { return '#5c0013'; });
Checkout Styling tab
Go to Events > Settings > Checkout Styling (in the Configuration group). These settings control the look of the checkout page. Values are applied as inline CSS variables, so changes show up on the next page load with no cache purge. The same stylesheet also loads on the order-pay page (see the order-pay page).
The Colors section:
| Field | Default |
|---|---|
| Primary color (buttons, links, focus ring) | #1d4ed8 |
| Primary hover color | #1e40af |
| Accent color (subtle highlights) | #eef2ff |
| Body text color | #0f172a |
| Muted / secondary text color | #64748b |
| Card background color | #ffffff |
| Page background color | #f1f5f9 |
| Border color | #e2e8f0 |
| Error / required color | #dc2626 |
| Success / valid color | #059669 |
The Sizing & typography section:
| Field | Default |
|---|---|
| Card corner radius | 14px |
| Input corner radius | 10px |
| Button corner radius | 10px |
| Checkout max width | 1180px |
| Section heading size | 18px |
| Field label size | 13px |
| Input / body text size | 14px |
| Font family | inherit |
- Type a hex value or use the color swatch next to each field. The two stay in sync.
- Leave a field blank to fall back to its default.
- Click Save checkout styling. A Reset to defaults button restores everything, and the Open checkout in new tab button under "Preview the checkout" lets you check the result.
Order Received and My Account styling
Go to Events > Settings > Order Received Styling. These settings control the order-received (thank-you) page customers see after placing a booking. The same ruleset also styles the View order page and the rest of the My Account area (Orders, Downloads, Addresses, Account details), so one theme covers everywhere a customer reviews an order.
Colors: Primary color (links, totals, buttons) #1d4ed8, Primary hover color #1e40af, Accent color (booking cards, address box) #eef2ff, Body text color #0f172a, Muted / secondary text color #64748b, Card background color #ffffff, Page background color #f1f5f9, Border color #e2e8f0, Success color (thank-you banner accent) #059669, Pending status text color #b45309, Pending status background color #fef3c7, Danger / cancel color #dc2626.
Sizing & typography: Card corner radius 14px, Page max width 1080px, Thank-you banner size 26px, Section heading size 20px, Body text size 14px, Font family inherit. Click Save order received styling to apply.
The order-pay page
When you approve a reservation, the customer receives a secure payment link that opens WooCommerce's order-pay screen. The plugin restyles that raw screen into two cards, an order summary and a payment card, using the exact same color and size values you saved on the Order Received Styling tab. There is no separate settings page for it: retheme the thank-you page and the pay page follows.
Empty cart landing page
If a visitor reaches the checkout with an empty cart, WooCommerce normally shows a bare notice or redirects to the cart. The plugin replaces that with a branded landing page: an animated cart illustration, a heading, body copy, and a call-to-action button. The module is called Empty Cart Checkout Screen, lives in the Booking group at Events > Settings, and is enabled by default.
| Setting | Default |
|---|---|
| Heading | Your cart is empty |
| Body text | Looks like there's nothing waiting for you here. Pick a date and a room to get started. |
| Button text | Browse events (leave blank to hide the button) |
| Button URL | /book-event/ |
| Heading color | the accent color |
| Body text color | #333333 |
| Button background | the accent color |
| Button text color | #ffffff |
| Heading size (px) | 36 |
| Body size (px) | 16 |
| Space above banner (px) | 32 |
The button's hover state is computed automatically as a darker shade of the button background, so you only pick one color. Body text accepts basic HTML such as links and line breaks. The illustration inherits the heading color, and the heading is rendered with a small ":(" emoticon after your text.
Legal pages and footer links
The first time an administrator visits the dashboard after activation, the plugin publishes three legal pages if they do not already exist:
- Privacy Policy at
/privacy-policy/ - Terms & Conditions at
/terms-and-conditions/ - Refund & Cancellation Policy at
/refund-and-cancellation-policy/
If a page already exists at one of those slugs (WordPress ships a privacy policy draft on fresh installs), the plugin adopts it: it publishes the draft but never touches its content. It also wires WordPress's privacy page setting and WooCommerce's terms page setting to these pages, but only if you have not already chosen pages there. Your explicit choice always wins.
A small footer strip with links to the three pages, separated by dots, is added near the bottom of every front-end page automatically. It appears as long as the pages exist and there is no setting to disable it. The page titles are used as the link labels, so renaming a page updates its footer link.
Settings & Troubleshooting
How the Settings page works
Everything togglable lives at Events > Settings. The page (titled Plugin Settings) has a sidebar on the left listing every feature module under its category header, and the right pane shows one module at a time. Click a module in the sidebar, adjust its toggle and options, then click Save Changes. Each form saves only that section, so you never lose settings in other modules.
The rule for every module is simple: off means fully disabled. As the page itself puts it, disabled modules do not register hooks, do not add menu items, and do not run cron jobs. A small dot next to each sidebar entry shows its state: a filled dot means enabled, an open circle means disabled, and an accent-colored dot marked "Always on" means the module is Core and its toggle is locked on.
Below the module categories the sidebar also lists a Diagnostics group (Booking Diagnostics, Booking Debug, Room Holds (on Lite, only Booking Debug appears in this group), covered below) and a System group with Setup wizard and About. The About tab shows your plugin version, module counts, and a button to re-run the setup wizard at any time.
Module reference
The table below lists every settings module in the order and grouping the plugin registers them. "Default" is the state on a fresh install. Core modules cannot be turned off.
| Module | Default | What turning it on does |
|---|---|---|
| Imports | ||
| iCal Feed Importer | On | Pulls events from your configured iCal URLs into Modern Events Calendar on a cron schedule. Sub-option (off by default): auto-create a room when a feed mentions a location the plugin does not recognize. |
| Display | ||
| Event Image Rules | On | Auto-assigns featured images to imported events based on title-keyword rules. |
| Event Auto-Tagger | On | Auto-tags imported events based on title-keyword matching. |
| Booking | ||
| Per-Chair Surcharge | Off | Adds a per-chair line item to the booking total, with separate plastic (default 1) and padded (default 2) rates. |
| Booking Modal | On | The public booking flow behind [tes_booking_modal]. Also holds the work order integration settings, occupancy enforcement, login requirement, and all customer-facing copy overrides. |
| Auto Room Assigner | On | Assigns the best-fit room to every new booking at checkout based on capacity, building preference, and proximity. |
| Setup & Teardown Time | On | Lets customers reserve setup time before and teardown time after their event; the room is blocked across the whole window. |
| Use plugin Room Types for pricing | Off | Prices bookings from your Room Types (cost/hour or cost/day) instead of WooCommerce product prices. |
| Pre-Event Email Reminders | On | Sends a payment reminder 14 days before the event if unpaid, plus event reminders 7 and 3 days before. Each fires at most once per booking. |
| Empty Cart Checkout Screen | Always on (Core) | Replaces the blank empty-cart checkout page with a configurable banner, heading, body text, and button. |
| Event Creator Defaults | On | Smart defaults on the Create Event screen: auto-filled start/end times and an optional simple setup/teardown duration mode. |
| Breakout Rooms | Off | Lets customers add breakout rooms from the Room Types you allowlist, priced at each type's rate times duration. |
| Room Hold Ledger | Always on (Core) | Soft-locks a room slot the moment a customer adds it to a cart and rejects overlapping bookings until the hold releases. Sub-option: Payment window after approval (hours), default 168 (seven days). |
| Event Page | On | The single-event page customers land on from a calendar: layout style, section toggles, breadcrumbs, and the event URL slug. |
| Display Calendar | On | Read-only event calendars via [tes_calendar] shortcodes, with site-wide default style and colors plus saved named calendars. |
| Organization Discounts | On | Lets customers self-identify as a discount-eligible organization type, applying per-type discounts and insurance requirements at checkout. |
| Booking Review | On | Options for the staff Booking Review page, currently the fee discount preset percentages (default "25, 50, 75, 100"). |
| Discount Approval Workflow | On | Routes per-line discount requests through an approver email before the discount is written to the order. |
| Checkout Fields | On | Plugin-owned checkout fields: phone, email confirmation, event purpose, and point of contact, each settable to required, optional, or disabled. |
| Booking Payment Gateways | On | Restricts which payment gateways customers see when paying for a booking order. Leave every box unchecked to show all enabled gateways. |
| Admin Tools | ||
| Create Event Portal | On | Admin form for facilities staff to create events with setup/event/teardown windows and breakout rooms. |
| Events Dashboard | On | Adds an Events Dashboard sub-page listing every event with filters and a streamlined editor. |
| Room Calendar | Always on (Core) | Visual calendar of every block on a room: classes, iCal events, and admin events together. |
| MEC Data Inspector | On | Admin-only debugging endpoint that dumps calendar database rows for a given event. |
| Taxonomy Importer | On | Bulk-imports categories, tags, and labels via JSON. |
Booking Diagnostics
Open Events > Settings > Booking Diagnostics (in the Diagnostics group of the sidebar). This page exercises every time-blocking and availability code path in read-only mode and flags places where two parts of the system disagree about the same time window. It creates no bookings and makes no external calls.
- Choose Products to test: "Auto (first 3 bookable products)", "All bookable products (slower)", or a single product to drill into.
- Set the Focal date (defaults to today plus 14 days). Optionally tick "Show full payloads in details rows".
- Click Run Tests.
The checks verify, per product: the duration and range-picker configuration, how many rooms are mapped to it, availability for a future window, a full-year-out window, the capacity filter, the computer-requirement filter, free-slot scans at 30, 60, and full-day granularity, and cross-validation that the occupancy data and the slot list agree with the availability engine. It also flags global problems: no bookable products at all, bookable rooms with no parent product mapping, and (when plugin pricing is on) rooms with no Room Type assigned.
To reproduce a specific problem booking, use the Custom scenario (optional) block: tick "Run a custom scenario in addition to the auto sweep", set the start and end date/time, and optionally tick Multi-day or All-day event (all-day forces the window to 00:00 through 23:59). The report shows how many rooms are available for exactly that window, names the first conflict if none are, and cross-checks the calendar slot list against the result.
Results appear as a tally of OK, WARN, FAIL, SKIP, and INFO findings with an "Issues to investigate" callout at the top. Use Copy All Results or Copy Issues Only to paste a plain-text report into a support conversation.
Booking Debug
Events > Settings > Booking Debug shows the Booking Debug Log: a capture of what the booking modal submits from the browser and what WooCommerce and WooCommerce Bookings do with it on the server.
While capture is on, the log records up to the latest 100 events (older entries roll off): the client-side modal submission, the raw server POST, the WooCommerce validation verdict with any error notices, the WooCommerce Bookings validation verdict, and any error notices raised during add-to-cart. Each row shows time, type, and IP address; expand a row to see the full JSON payload plus the originating URL and browser. The browser-side reporter is rate limited to 20 events per 5 minutes per IP address so one visitor cannot flood the log. Use Clear log to empty it.
Room Holds diagnostics
Events > Settings > Room Holds is a live view of the room-hold ledger. Each row is a soft lock on a room slot that moves from pending_cart through pending_review and awaiting_payment to paid or released. Stat cards at the top count rows in each state plus any holds expiring within 5 minutes.
A cron sweep runs every 5 minutes and releases expired holds automatically: cart holds expire after 60 minutes, and approved-but-unpaid holds expire after the payment window (the Payment window after approval setting on the Room Hold Ledger module). The panel shows the sweep's next run time; Run sweep now triggers it immediately.
If a slot seems stuck, filter by state ("Active (blocking states)" is the default) or product, then click Release on the offending row. Releasing a hold frees the slot immediately and also clears the matching item out of the customer's cart. Release all (current filter) does the same for every blocking hold matching the current filter.
Work-order connection test
If you push approved bookings to a Support Candy site, the Test work-order connection panel sits at the bottom of the Events > Settings > Booking Modal page, below the work order settings. Click Run connection test and it runs two probes with your saved credentials: first it verifies the Application Password works at all, then it creates a real test ticket on the work-order site. The panel prints the exact HTTP status and response body, with plain-language hints for the common failures (revoked password, stripped Authorization header, user not assigned as a Support Candy agent, agent missing category access, or an invalid category/priority/status ID).
[PLUGIN TEST] on the work-order site. Delete it there after the test.Common issues and fixes
Bookings are not appearing and the modal has nothing to book
The booking flow reserves rooms through WooCommerce Bookings. Confirm both WooCommerce and WooCommerce Bookings are active under Plugins, then run Booking Diagnostics. A FAIL reading "No bookable products were found" means the plugin has nothing to offer customers until the bookable products exist.
Imported events are missing
Check three things on Events > iCal Feeds: the iCal Feed Importer module is enabled in Settings, the feed's Import Mode is not "Off (import disabled)" or "Blocks Only" (blocks-only imports room availability without creating public events), and the Configured Feeds table's Last Import column, which reports how many items failed on the last run. Use Import All Now to re-run immediately and read the result notice.
A customer cannot pay for an approved booking
Two usual causes. First, the payment window may have lapsed: after Payment window after approval (hours) (Room Hold Ledger module, default 168) the order is auto-cancelled and its rooms released, which you can confirm on the Room Holds tab. Second, check Events > Settings > Booking Payment Gateways: if the Allowed gateways for booking orders list only has boxes checked for gateways that are flagged "disabled in WC", customers see no way to pay. Leave every box unchecked to fall back to all gateways enabled in WooCommerce.
Calendar event links return 404
Go to Settings > Permalinks and click Save Changes to flush rewrite rules. If it persists, open the Event Page module and check the Event URL slug for a collision with another page or post type on your site; changing that slug flushes rewrite rules automatically.
A booking never gets a room assigned
Confirm the Auto Room Assigner module is enabled, then run Booking Diagnostics. Two warnings point at the fix: "rooms without parent product" means a room is not mapped to any bookable product, and "product has no rooms mapped" means the product will always report empty availability. Map each room to its product and make sure the room is marked bookable, then re-run the tests.