{"id":364319,"date":"2026-09-17T16:25:48","date_gmt":"2026-09-17T16:25:48","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/seat-table-booking-for-woocommerce\/"},"modified":"2026-09-17T16:25:17","modified_gmt":"2026-09-17T16:25:17","slug":"seat-table-booking-for-woocommerce","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/seat-table-booking-for-woocommerce\/","author":23547000,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.6.2","stable_tag":"1.6.2","tested":"7.1.1","requires":"6.8","requires_php":"8.1","requires_plugins":null,"header_name":"Seat & Table Booking for WooCommerce","header_author":"Magnus V.","header_description":"Draw your floor plan, price every table or seat, and sell reservations as WooCommerce products.","assets_banners_color":"bd7cb4","last_updated":"2026-09-17 16:25:17","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":30,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.6.2":{"tag":"1.6.2","author":"macvej","date":"2026-09-17 16:25:17","revision":3700601}},"upgrade_notice":{"1.6.2":"<p>Hardening from the wordpress.org review: the General settings option now stores only the keys\nit knows. No visible change. Recommended for everyone.<\/p>","1.6.1":"<p>Security and performance. Closes a case where an outside caller could reach check-in with the\nAPI switched off, requires an Application Password over HTTPS for third-party scanner apps, and\nmakes the check-in block in order emails far lighter on a busy day. Recommended for everyone.<\/p>","1.6.0":"<p>A month calendar for Booking times, a new Settings &gt; Appearance tab so the booking interface\nfollows your theme, and a round of security hardening from the wordpress.org review.\nRecommended for everyone.<\/p>","1.5.1":"<p>Security hardening for every request handler and the cart, plus a round of fixes to the floor\nplan editor and order lifecycle from the wordpress.org review. Recommended for everyone.<\/p>","1.5.0":"<p>Fixes check-in times being shown shifted by the site&#039;s UTC offset on the Bookings screen, and\nmakes that screen substantially faster on venues with many units.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3700601,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3700601,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3700601,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3700601,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3700601,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.6.2"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[164435,127709,126872,34016,51225],"plugin_category":[],"plugin_contributors":[277115],"plugin_business_model":[],"class_list":["post-364319","plugin","type-plugin","status-publish","hentry","plugin_tags-floor-plan","plugin_tags-restaurant-reservations","plugin_tags-seating-chart","plugin_tags-table-booking","plugin_tags-woocommerce-booking","plugin_contributors-macvej","plugin_committers-macvej"],"banners":{"banner":"https:\/\/ps.w.org\/seat-table-booking-for-woocommerce\/assets\/banner-772x250.png?rev=3700601","banner_2x":"https:\/\/ps.w.org\/seat-table-booking-for-woocommerce\/assets\/banner-1544x500.png?rev=3700601","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/seat-table-booking-for-woocommerce\/assets\/icon.svg?rev=3700601","icon":"https:\/\/ps.w.org\/seat-table-booking-for-woocommerce\/assets\/icon.svg?rev=3700601","icon_2x":false,"generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p><strong>Seat &amp; Table Booking for WooCommerce<\/strong> is a table reservation and seat booking plugin that\nturns any WooCommerce shop into a booking system for a room with a layout: a restaurant's\ntables, a cinema's seats, a theatre's rows, a co-working space's desks, a wedding hall's\nseating plan. It adds a <strong>Booking venue<\/strong> product type to WooCommerce, and one product is one\nfloor plan.<\/p>\n\n<p>Upload a photo or a drawing of the room, drag the tables to where they really are, give each\none a price and a capacity, and set the timeslots it can be booked in. Customers then pick a\ndate, a timeslot and a table straight off the seating chart, and the table goes into the cart\nlike any other WooCommerce product. WooCommerce handles the payment, the order and the emails -\nthere is no second checkout, no second dashboard, no subscription and no external service.<\/p>\n\n<p>Every paid reservation gets a QR check-in code, and door staff scan it from a full-screen\nscanner page built into the plugin.<\/p>\n\n<h4>Key features<\/h4>\n\n<p><strong>For the shop owner<\/strong><\/p>\n\n<ul>\n<li>A visual floor plan editor: draw tables and seats on an uploaded image and drag them into\nplace, with a grid, undo, rotation and a row tool for cinema-style seating.<\/li>\n<li>Per-unit price, label, capacity and an internal note.<\/li>\n<li>Recurring timeslot rules per weekday, with optional first and last dates.<\/li>\n<li>Blackout dates, for the whole day or for a single timeslot.<\/li>\n<li>A configurable hold duration, per site or per venue, so an abandoned cart frees the table.<\/li>\n<li>A Bookings screen: every table against every sitting for a day, with guests, arrivals, a\nneeds-attention bar, and a staff block per sitting for walk-ins.<\/li>\n<li>A Reservation column on the WooCommerce orders list.<\/li>\n<li>QR check-in codes, a built-in scanner page, a \"Scanner\" role for door staff, and an\noptional REST API for third-party scanner apps.<\/li>\n<li>Per-venue QR appearance: colours, an optional logo and a caption.<\/li>\n<li>An Appearance tab with fifteen optional colour overrides, a live preview and a contrast\nwarning. Left alone the booking controls follow your theme's own colours, so the card fits a\ndark or a branded theme with nothing to configure.<\/li>\n<\/ul>\n\n<p><strong>For the customer<\/strong><\/p>\n\n<ul>\n<li>Pick a date, then a timeslot, then a table or seats from the floor plan.<\/li>\n<li>Only dates and times that can actually be booked are offered.<\/li>\n<li>Booked tables are visibly unavailable, tables too small for the party are not offered, and\na \"find N seats together\" button does the searching on a seating chart.<\/li>\n<li>The whole picker works with a keyboard and a screen reader.<\/li>\n<li>The table, date and time appear on the cart, the checkout, the order and the order emails,\nwith an optional arrival note.<\/li>\n<\/ul>\n\n<h4>Who it is for<\/h4>\n\n<ul>\n<li><strong>Restaurants, caf\u00e9s and bars<\/strong> - tables of different sizes and prices, sittings per evening,\na table held back for walk-ins.<\/li>\n<li><strong>Cinemas, theatres and concert venues<\/strong> - seat-by-seat booking from a seating chart.<\/li>\n<li><strong>Events with a fixed layout<\/strong> - a wedding, a gala dinner or a conference is a timeslot rule\nwhose first and last day are the same.<\/li>\n<li><strong>Co-working spaces and studios<\/strong> - desks and rooms booked by the hour or by the day.<\/li>\n<\/ul>\n\n<h4>How it works<\/h4>\n\n<ol>\n<li>Create a product and set its type to <strong>Booking venue<\/strong>.<\/li>\n<li>On the <strong>Floor plan<\/strong> tab, upload a picture of the room and draw the tables or seats on it.<\/li>\n<li>On the <strong>Booking times<\/strong> tab, set the weekly timeslots and any closed dates.<\/li>\n<li>Publish. Customers book from the product page, pay through the normal WooCommerce checkout,\nand receive a QR check-in code with their order confirmation.<\/li>\n<\/ol>\n\n<h4>How double bookings are prevented<\/h4>\n\n<p>Adding a table to the cart creates a short-lived hold on that exact unit, date and timeslot.\nThe hold is enforced by a database uniqueness constraint, so two customers racing for the last\ntable cannot both win - the second is told the table has just gone. Holds that are never paid\nfor expire on their own and the table returns to the floor plan.<\/p>\n\n<h4>Check-in at the door<\/h4>\n\n<p>Every paid reservation gets a QR check-in code, shown on the confirmation email, the thank-you\npage and My Account. Staff scan it at <code>\/table-check-in\/<\/code>, a full-screen scanner page built into\nthe plugin, restricted to a \"Scanner\" role that can check guests in and nothing else. The\nscanner has a flashlight toggle and a type-in field for a desk without a camera.<\/p>\n\n<p>The same check-in also works over a REST API, so a third-party scanner app - a phone app, a\nhandheld reader, whatever the door already uses - can call it too. The API is off by default;\na shop owner turns it on under Seats &amp; Tables &gt; Settings &gt; Scanner \/ API, and a scanner app\nauthenticates with a WordPress Application Password over HTTPS, the same mechanism WordPress\nships for any external app that needs to act as a user.<\/p>\n\n<h4>Compatibility<\/h4>\n\n<ul>\n<li>WooCommerce High-Performance Order Storage (HPOS) and the classic posts table.<\/li>\n<li>The Cart and Checkout blocks and the classic shortcode cart and checkout.<\/li>\n<li>Any theme: the booking form replaces the add-to-cart form on the product page and uses\nWooCommerce's own notices and buttons.<\/li>\n<li>Fully translatable; the plugin ships with a <code>.pot<\/code> file.<\/li>\n<\/ul>\n\n<h4>Third-party code<\/h4>\n\n<p>Bundles Fabric.js 7.4.0 (MIT) for the admin floor plan editor, in\n    inc\/product-type\/js\/lib\/, as published on npm (<code>fabric\/dist\/index.min.js<\/code>,\nhttps:\/\/www.npmjs.com\/package\/fabric\/v\/7.4.0). Unminified source (the project tags this\nrelease <code>v740<\/code>): https:\/\/github.com\/fabricjs\/fabric.js\/tree\/v740<\/p>\n\n<p>Also bundles endroid\/qr-code 5.1.0 (MIT) for generating check-in QR codes, together with its\ndependencies bacon\/bacon-qr-code 3.0.1 (https:\/\/github.com\/Bacon\/BaconQrCode) and dasprid\/enum\n1.0.6 (https:\/\/github.com\/DASPRiD\/Enum), both BSD-2-Clause, installed with Composer,\nunmodified, in <code>inc\/qr\/lib\/qrcodegen\/<\/code>. The MIT licence applies to endroid\/qr-code itself only.\nSource: https:\/\/github.com\/endroid\/qr-code<\/p>\n\n<p>And jsQR 1.4.0 (Apache-2.0) for decoding QR codes in the check-in scanner, in\n    inc\/scanner\/js\/jsqr.js. Source: https:\/\/github.com\/cozmo\/jsQR<\/p>\n\n<p>No other minified or compiled code is shipped, and no assets are loaded from a CDN.<\/p>\n\n<h4>External services<\/h4>\n\n<p>None. This plugin makes no outbound requests, sends no analytics or telemetry, and needs no\naccount anywhere. QR codes are generated on your own server by the bundled library, and every\nscript and stylesheet is served from your own site.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate WooCommerce.<\/li>\n<li>Upload the plugin to <code>\/wp-content\/plugins\/<\/code> and activate it, or install it from\nPlugins &gt; Add New.<\/li>\n<li>Create a product, set its type to \"Booking venue\", then use the \"Floor plan\" and\n\"Booking times\" tabs to lay out the venue.<\/li>\n<li>Optional: set the hold duration, unpaid-order release window, booking notice and check-in\nwindow under Seats &amp; Tables &gt; Settings.<\/li>\n<\/ol>\n\n<p>Deleting the plugin removes its settings and the Scanner role. Bookings, floor plans and QR\ncheck-in data are kept, so reinstalling the plugin picks up exactly where you left off -\nunless <code>STBK_REMOVE_ALL_DATA<\/code> is defined as <code>true<\/code> in <code>wp-config.php<\/code> before deleting, which\nalso drops the database tables and the generated QR images. Deactivating it removes nothing.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20it%20work%20with%20high-performance%20order%20storage%20%28hpos%29%3F\"><h3>Does it work with High-Performance Order Storage (HPOS)?<\/h3><\/dt>\n<dd><p>Yes. The plugin declares HPOS compatibility and reads and writes orders only through the\nWooCommerce order API, so it works with both order storage modes.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20the%20cart%20and%20checkout%20blocks%3F\"><h3>Does it work with the Cart and Checkout blocks?<\/h3><\/dt>\n<dd><p>Yes, and with the classic shortcode cart and checkout.<\/p><\/dd>\n<dt id=\"can%20i%20run%20more%20than%20one%20venue%3F\"><h3>Can I run more than one venue?<\/h3><\/dt>\n<dd><p>Yes. Every Booking venue product is its own floor plan with its own timeslots, prices and\nsettings, and one order can hold tables at several venues.<\/p><\/dd>\n<dt id=\"which%20timezone%20are%20bookings%20in%3F\"><h3>Which timezone are bookings in?<\/h3><\/dt>\n<dd><p>The site's timezone, as set under Settings &gt; General. Timeslots, booking dates, the \"booking\nnotice\" cut-off and the check-in window all use it.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20a%20customer%20abandons%20the%20cart%3F\"><h3>What happens if a customer abandons the cart?<\/h3><\/dt>\n<dd><p>The hold expires after the configured number of minutes (15 by default) and the table becomes\nbookable again. If the customer returns to a cart with an expired hold, the line is removed and\nthey are told why. Removing a line and pressing \"Undo\" takes the hold again if the table is\nstill free.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20an%20order%20is%20cancelled%2C%20refunded%20or%20deleted%3F\"><h3>What happens if an order is cancelled, refunded or deleted?<\/h3><\/dt>\n<dd><p>The booking is released, its QR code is cancelled and the table is immediately bookable again.\nTrashing or deleting an order does the same.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20an%20order%20that%20is%20never%20paid%20for%3F\"><h3>What happens to an order that is never paid for?<\/h3><\/dt>\n<dd><p>Its tables are released after the window set in Seats &amp; Tables &gt; Settings (60 minutes by\ndefault), the order is cancelled and a note is added to it. Orders on hold awaiting a bank\ntransfer are never released automatically.<\/p><\/dd>\n<dt id=\"can%20one%20table%20be%20booked%20twice%20in%20the%20same%20timeslot%3F\"><h3>Can one table be booked twice in the same timeslot?<\/h3><\/dt>\n<dd><p>No. A unique index on unit, date and timeslot makes that impossible at the database level.<\/p><\/dd>\n<dt id=\"does%20it%20depend%20on%20wp-cron%3F\"><h3>Does it depend on WP-Cron?<\/h3><\/dt>\n<dd><p>No. Expired holds are treated as free the moment they expire; a five-minute WP-Cron event only\ntidies the rows up. The unpaid-order release also runs on that event, so on a site where\nWP-Cron is disabled it runs whenever the system cron calls <code>wp-cron.php<\/code>.<\/p><\/dd>\n<dt id=\"can%20staff%20hold%20a%20table%20back%20for%20a%20walk-in%3F\"><h3>Can staff hold a table back for a walk-in?<\/h3><\/dt>\n<dd><p>Yes. Seats &amp; Tables &gt; Bookings, pick the venue and the date, then press \"+ Block\" in the empty\ncell for that sitting. The table disappears from the floor plan for that sitting until the block\nis released.<\/p><\/dd>\n<dt id=\"how%20do%20staff%20scan%20tickets%20at%20the%20door%3F\"><h3>How do staff scan tickets at the door?<\/h3><\/dt>\n<dd><p>Open <code>\/table-check-in\/<\/code> on a phone or tablet logged in as a user with the \"Scanner\" role and\npoint the camera at the guest's QR code. A valid, unused code within its check-in window is\nadmitted and marked used; a cancelled, already-used or out-of-window code is refused, with the\nreason shown on screen.<\/p><\/dd>\n<dt id=\"what%20can%20the%20scanner%20role%20do%3F\"><h3>What can the Scanner role do?<\/h3><\/dt>\n<dd><p>Check guests in and nothing else. A Scanner user cannot see orders, products or the dashboard.\nShop managers and administrators can scan as well.<\/p><\/dd>\n<dt id=\"can%20a%20third-party%20scanner%20app%20call%20the%20api%3F\"><h3>Can a third-party scanner app call the API?<\/h3><\/dt>\n<dd><p>Yes, once a shop owner turns on \"Enable API\" under Seats &amp; Tables &gt; Settings &gt; Scanner \/ API -\nit is off by default. The app authenticates with a WordPress Application Password and calls\n    stbk\/v1\/scanner\/checkin\/{id} the same way the built-in scanner page does. The settings page\ndocuments every response.<\/p>\n\n<p>An Application Password is required; a real account password is not accepted, and the site must\nbe on HTTPS, because that is what WordPress itself requires before it will accept one. The\nbuilt-in scanner page is unaffected either way - it uses the staff member's own login session.<\/p><\/dd>\n<dt id=\"what%20if%20a%20guest%20loses%20the%20email%20with%20the%20qr%20code%3F\"><h3>What if a guest loses the email with the QR code?<\/h3><\/dt>\n<dd><p>Staff can resend it from the Bookings screen. The code is also shown on the WooCommerce\nthank-you page and in the order details under My Account, so a signed-in customer can pull it\nup there without waiting on an email at all.<\/p><\/dd>\n<dt id=\"what%20is%20removed%20when%20i%20delete%20the%20plugin%3F\"><h3>What is removed when I delete the plugin?<\/h3><\/dt>\n<dd><p>Its settings, the Scanner role and its scheduled event. Bookings, floor plans and QR data stay\nunless <code>STBK_REMOVE_ALL_DATA<\/code> is defined as <code>true<\/code> in <code>wp-config.php<\/code> first.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.6.2<\/h4>\n\n<ul>\n<li>Hardened: the General settings option's sanitize callback now returns only the keys it knows.\nIt used to hand back the four minute settings on top of a copy of the raw input, so any other\nkey reaching the option through the Settings API was stored as it arrived. The four legacy\nScanner \/ API keys the option can still carry from before 1.2.0 are kept for the one-time\nmigration, folded to 0\/1 and checked with <code>sanitize_hex_color()<\/code>; everything else is dropped.\nThe tab's own save path stores through the same callback.<\/li>\n<\/ul>\n\n<h4>1.6.1<\/h4>\n\n<ul>\n<li>Security: on hosts that hand PHP the Basic credentials as PHP_AUTH_USER\/PHP_AUTH_PW without\nexposing an Authorization header, an outside caller could reach check-in with \"Enable API\"\nswitched off. WordPress authenticates such a caller before this plugin runs, so the request\narrived looking like the built-in scanner's own session - the one path that deliberately does\nnot consult that setting. The credentials are rebuilt into the header they arrived in, so the\ntoggle is now checked on every outside request.<\/li>\n<li>Hardened: every numeric value read from a request is now checked by shape before it is cast,\nthrough one shared guard rather than field by field. <code>absint()<\/code> and <code>(int)<\/code> answer 1 for an\narray instead of refusing it, so a crafted request could turn a product id, a guest count, an\nattachment id or a hold duration into a valid 1. The previous round fixed this inside the floor\nplan payload; it now covers every request handler in the plugin.<\/li>\n<li>Changed: outside callers are authenticated by WordPress's own application-password validator\ninstead of by a credential check inside the plugin. A real account password is no longer\naccepted: an Application Password is required, and WordPress only accepts those over HTTPS.\nA scanner currently configured with an account password needs an Application Password instead\n\n<ul>\n<li>generate one under Users and paste it in place of the password. The built-in scanner page is\nunaffected, as it authenticates with the staff member's own login session.<\/li>\n<\/ul><\/li>\n<li>Changed: a non-numeric value in the hold, notice and check-in window fields, or in the QR\nlead-time field, now falls back to the setting's default rather than being read as zero.<\/li>\n<li>Changed: the check-in link in plain-text order emails is escaped with <code>esc_url()<\/code> rather than\n  esc_url_raw(), which is a sanitizer for storage and redirects rather than an escaper for\noutput. The link that reaches the inbox is byte for byte the same.<\/li>\n<li>Changed: the add-to-cart confirmation closes its output buffer in its own statement instead of\ninside the call that consumes it, so the buffer is never held open across another function call.<\/li>\n<li>Changed: the generated Appearance CSS is escaped where it is handed to WordPress rather than\ntrusted from the builder four files away. It emits only fixed selectors and validated colours,\nso nothing renders differently.<\/li>\n<li>Performance: the check-in block in order emails, on the thank-you page and in My Account no\nlonger reads the venue's whole day of bookings once per check-in code. An order carrying\nseveral codes ran one venue-wide query per code and discarded almost every row it fetched; the\nlookup is now scoped to that order and its sitting, so the work no longer grows with how busy\nthe restaurant is on the day.<\/li>\n<li>Fixed: losing the race for the last table during checkout with the Cart and Checkout blocks now\nanswers with a conflict instead of a server error. The step that promotes a hold to a booking is\nshared by both checkout paths; the classic one has always shown the explanation as a notice, but\nthe blocks turned it into \"unknown server error\" with HTTP 500 - which makes an ordinary booking\ncollision look like an outage to uptime monitoring and to any client that retries on 5xx. The\nsentence the customer reads is unchanged.<\/li>\n<li>Fixed: on the Bookings screen, opening a booking's details now moves the keyboard to the panel\nthat just opened, and closing it puts the keyboard back on the booking it came from. The panel\nsits after the whole day's table, so reaching it previously meant tabbing past every remaining\nbooking, and closing it left the keyboard nowhere at all. Nothing changes for mouse users.<\/li>\n<li>Changed: when a settings screen cannot save, it now says so in a normal WordPress notice at the\ntop of the screen instead of a browser alert box. The message is usually about one field - which\ncolour was not read, for example - so it belongs next to the fields rather than in a dialog you\nhave to dismiss before you can look at them.<\/li>\n<\/ul>\n\n<h4>1.6.0<\/h4>\n\n<p><strong>Booking times<\/strong><\/p>\n\n<ul>\n<li>The Booking times tab is now a month calendar of what customers will actually be offered,\nrather than two tables of bare inputs. Pick a day to see its sittings, close it, or add one;\nevery rule is reachable from any day it lands on, and the calendar redraws as you edit rather\nthan only telling the truth after an update.<\/li>\n<li>Sittings are no longer added by saving the product to get a blank row - and deleting the last\none now saves, which it previously did not.<\/li>\n<li>Weekdays are toggles instead of checkboxes, date bounds are picked as \"always \/ from a date \/\nbetween two dates \/ one date only\" instead of two raw fields, and the tab warns before the save\nhandler quietly drops a zero-length sitting or swaps date bounds that are the wrong way round.<\/li>\n<li>The calendar shows existing bookings per day for a year ahead and a month back.<\/li>\n<\/ul>\n\n<p><strong>Appearance<\/strong><\/p>\n\n<ul>\n<li>The booking controls now take their colours from your theme instead of bringing their own.\nThe card, the fields, the captions and the five message tones are all derived from whatever\ntext colour your theme sets, so they follow a dark palette or a branded one with nothing to\nconfigure - and the confirmation line no longer forces its own serif over your theme's font.<\/li>\n<li>New Settings &gt; Appearance tab: fifteen colour overrides in three folding groups, all shut by\ndefault. Five for the floor plan - selected unit, plan background, plan labels, unit outline\nand taken fill; eight for the booking card - background, text, captions, borders, field fill,\nfocus ring, and the background and border of the field being used; and two for the Add to cart\nbutton. Every field is empty by default and empty means inherit, so a shop that never opens the\nscreen keeps the shipped plan and keeps the card following its theme.<\/li>\n<li>On the card, setting the text colour is usually all you need: the captions, borders and field\nfills are mixed from it, so they move with it. The rest are there to override that.<\/li>\n<li>The Add to cart button is the one setting that overrides your theme outright, including any\ngradient it paints the button with, because that button otherwise belongs to your theme by\ndesign. Left empty it still does.<\/li>\n<li>Setting one colour carries the tokens that belong with it: a brand-coloured selected table\ngets a matching rim and halo rather than the shipped terracotta, and darkening the plan\nbackground carries the free-table fill, the wall and the hatching with it.<\/li>\n<li>The tab measures the three contrast pairs that carry text and warns when one drops below\n4.5:1. It warns rather than blocks - matching a brand colour exactly is often worth one\nborderline ratio, but it should not be a surprise.<\/li>\n<\/ul>\n\n<p><strong>Storefront<\/strong><\/p>\n\n<ul>\n<li>The date dropdown is grouped by month. With a 90 day horizon a venue open six days a week\noffers around eighty dates, which as one flat list is a long scroll with nothing to navigate\nby - especially on a phone. The month headings come from the site's own locale.<\/li>\n<li>Fixed: on a narrow screen a unit's tooltip was drawn outside the floor plan and clipped away,\nso its capacity, price and note could not be read on a phone at all.<\/li>\n<li>Fixed: the booking form and floor plan no longer inherit a theme's button styling - uppercase\ntransforms, minimum widths, gradients and hover transforms were moving tables off their own\ncoordinates on Divi, Flatsome, Woodmart and several block themes.<\/li>\n<li>Fixed: fields no longer overflow the booking card on themes that do not set border-box.<\/li>\n<li>Added: the floor plan gets a usable minimum size in the full-screen view on phones, tooltips\nopen on tap, and fields no longer trigger Safari's zoom-on-focus.<\/li>\n<\/ul>\n\n<p><strong>Performance and isolation<\/strong><\/p>\n\n<ul>\n<li>Performance: the floor plan editor, Fabric.js and the three product data panels no longer\nload on product edit screens that are not booking venues - roughly 370 KB of script, styles\nand markup off every simple, variable or grouped product screen in the shop.<\/li>\n<li>Changed: the Scanner \/ API screen's \".hide\" helper and the QR tab's colour tokens are scoped\nto this plugin, so neither reaches another plugin's markup on a shared admin screen.<\/li>\n<\/ul>\n\n<p><strong>Review round<\/strong><\/p>\n\n<ul>\n<li>Removed the \"Tested up to\" line from the main plugin file. It is not a plugin header, and\ncarrying it in two places is how a plugin ends up advertising a compatibility version nobody\nmeant; the readme is the only place it belongs.<\/li>\n<li>Hardened: every field of a posted floor plan payload is now checked by shape before it is cast.\nCrafted JSON could hand a label, a note or an id an array instead of a value, and <code>absint()<\/code>\nanswers 1 for one of those rather than refusing it - which on the id field pointed a row at\nwhichever unit happened to be id 1.<\/li>\n<li>Hardened: the same check on the posted unit id list behind add-to-cart.<\/li>\n<li>Changed: escaping moved to the point of output everywhere markup was built into a variable\nfirst - the confirmation email's QR block and plain-text body, the cart's floor plan thumbnail,\nthe floor plan viewer's background image and tooltip hints, the Floor Plan tab and the\nAppearance tab's swatches. Nothing renders differently; the escaping is now visible where the\nvalue is written rather than argued for in a comment above it.<\/li>\n<\/ul>\n\n<h4>1.5.1<\/h4>\n\n<ul>\n<li>Security: every admin-ajax endpoint, including the public read-only ones and the cart\nthumbnail, now requires a nonce; the product-screen saves verify the plugin's own nonce.<\/li>\n<li>Security: a venue can no longer be added to the cart without a reservation via\n  ?add-to-cart=, a draft or private venue is bookable only by users who can edit it, and a\ncrafted request can no longer hold several tables at once under one party size.<\/li>\n<li>Changed: admin page slugs, script handles and the JavaScript settings objects are all\nprefixed <code>stbk<\/code> literally, as the wordpress.org prefix scanner requires.<\/li>\n<li>Fixed: a check-in window that crosses midnight is honoured on both sides of it.<\/li>\n<li>Fixed: an order released for non-payment is now cancelled (and its QR codes with it) rather\nthan left pending with no tables.<\/li>\n<li>Fixed: trashing or deleting an order releases its tables, and \"Undo\" after removing a cart\nline takes the hold again.<\/li>\n<li>Fixed: on a seating chart, changing the timeslot keeps the seats that are still free rather\nthan dropping the whole pick; a failed slot request now says so with a retry instead of\nreading as a closed day.<\/li>\n<li>Fixed: a venue with no floor plan yet shows a short notice instead of an empty booking form.<\/li>\n<li>Fixed: the floor plan editor - Enter in a field no longer submits the product, a unit can no\nlonger be scaled to nothing, a circle stays a circle when resized, a unit at the top-left\ncorner is no longer moved, two quick background picks no longer race, and the grid preference\nsurvives a browser that blocks local storage.<\/li>\n<li>Fixed: timeslot rules with the same start and end, duplicate weekdays or reversed date\nbounds are cleaned up on save, and a blackout can only point at one of the venue's own rules.<\/li>\n<li>Fixed: the floor plan, timeslot and QR tabs only save for a Booking venue, so a product\nchanged to another type stops writing venue data.<\/li>\n<li>Performance: a venue's units and timeslot rules are read once per request rather than once per\ncaller, and the floor plan save updates rows in place instead of deleting and re-inserting.<\/li>\n<li>Changed: the check-in scanner idles while its tab is in the background and recovers when the\ncamera is unplugged; the check-in REST route accepts GET as well as POST.<\/li>\n<li>Changed: readme rewritten with a fuller feature list, compatibility notes and FAQ; every\nfunction and class carries a docblock.<\/li>\n<\/ul>\n\n<h4>1.5.0<\/h4>\n\n<ul>\n<li>Fixed: the Bookings screen showed check-in times shifted by the site's UTC offset - a guest\nadmitted at 18:04 read as 20:04 on a UTC+2 site.<\/li>\n<li>Fixed: the Bookings, Settings and Scanner \/ API screens each closed one <code>&lt;div&gt;<\/code> more than they\nopened, which could push the admin footer out of place.<\/li>\n<li>Fixed: a venue whose QR colours were entered in three-digit form (<code>#abc<\/code>) silently produced no\nQR image at all.<\/li>\n<li>Fixed: the Scanner role is now created on sites upgraded by copying files over, not only on a\nfresh activation, and the Scanner \/ API options are removed when the plugin is deleted.<\/li>\n<li>Changed: on a seating chart, a party larger than one booking can hold is told so at the guests\nbox instead of after picking every seat.<\/li>\n<li>Performance: the Bookings day view reads its check-in codes and its orders in one batch each\nrather than once per booking, so the query count no longer grows with the size of the room.<\/li>\n<li>Fixed: the Reservation column, the order emails and the check-in codes matched to them read the\ntimeslot from the booking rule rather than from a translated meta key, so an order placed while\nthe site ran in one language still shows its time when read in another.<\/li>\n<li>Fixed: deleting the plugin now removes every product setting it wrote - hold duration, arrival\nnote and QR appearance - not only the floor plan background.<\/li>\n<li>Fixed: the \"still has bookings\" warning in the floor plan editor counts from the site's own\ncalendar day rather than UTC's.<\/li>\n<li>Changed: the floor plan editor runs on Fabric.js 7.4.0, up from 5.1.0.<\/li>\n<li>New: the check-in scanner gained a flashlight toggle (shown when the camera has one), a\ntype-or-paste code field in its menu for a desk without a camera, and refusals now stay on\nscreen for six seconds or until tapped, so staff can read the reason.<\/li>\n<li>Changed: the check-in API only accepts the Basic authentication scheme; a Bearer or Digest\nheader is ignored rather than decoded as if it were Basic credentials.<\/li>\n<li>Security: the generated-QR folder in uploads gets an index.php and an .htaccess that turns\ndirectory listing off (the images themselves stay reachable, as the emails need them to be), so\na server with directory listing enabled cannot hand out every live check-in code at once. Sites\nupgrading get the two files on the next QR render.<\/li>\n<\/ul>\n\n<h4>1.4.0<\/h4>\n\n<ul>\n<li>New: the Bookings screen is a day matrix - every table against every sitting, with a KPI\nstrip (guests, tables booked, arrived %, next-sitting countdown), a needs-attention bar for\nexpiring cart holds and late parties, and a details drawer with all check-in actions.<\/li>\n<li>New: filter the matrix by search, status, hide-empty and density - built for venues with\nmany tables; seat venues start with empty seats hidden.<\/li>\n<li>New: block a unit straight from its empty cell instead of a separate dropdown form.<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>New: the date field on a venue page is a dropdown of only the dates that can actually be\nbooked - closed and fully booked days are simply not offered.<\/li>\n<li>New: a \"View larger\" button opens the floor plan full screen, on any device.<\/li>\n<li>Changed: guests are asked for before the date, the plan stays dimmed until date and timeslot\nare chosen, and adding to the cart shows a standard WooCommerce notice while the button stays\nan ordinary add-to-cart button.<\/li>\n<li>Changed: the admin menu is named \"Seats &amp; Tables\"; General is the first settings tab, the\nBookings screen left the tab bar (it lives in the submenu), and the Scanner \/ API page is\nreached from the settings tabs instead of its own submenu row.<\/li>\n<li>Fixed: the floor plan editor no longer closes on a stray backdrop click, no longer shifts its\nview when focus lands low in the tool rail, and hides the seats box for seats (a seat is\nalways one person). Chairs drawn around a stretched table keep their proportions. Raw _stbk\nmeta keys are hidden on the admin order screen.<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>New: every paid reservation gets a QR check-in code, shown on the confirmation email, the\nthank-you page and My Account.<\/li>\n<li>New: a full-screen <code>\/table-check-in\/<\/code> scanner page and a \"Scanner\" role for door staff.<\/li>\n<li>New: a REST check-in API for third-party scanner apps, off by default, plus check-in\nmanagement from the Bookings screen (manual check-in, resend, reset).<\/li>\n<li>Changed: deleting the plugin no longer drops its database tables by default - bookings, floor\nplans and QR data are kept unless <code>STBK_REMOVE_ALL_DATA<\/code> is defined as <code>true<\/code> before deleting.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>New: a Bookings screen in wp-admin showing every reservation for a venue on a date.<\/li>\n<li>New: staff can block a table for a sitting, and release a block or a lapsed hold.<\/li>\n<li>New: customers say how many guests are coming, and tables too small are not offered.<\/li>\n<li>New: per-venue hold duration, a required booking notice, and an unpaid-order release window.<\/li>\n<li>New: a Reservation column on the orders list, and a reservation summary in customer emails.<\/li>\n<li>Fix: a booking line can no longer have its quantity raised in the cart.<\/li>\n<li>Fix: a failed payment no longer releases the table before the customer can retry.<\/li>\n<li>Fix: orders that are never paid for release their tables instead of holding them forever.<\/li>\n<li>Fix: timeslots that have already started are no longer offered for today.<\/li>\n<li>Fix: deleting a venue product now removes its floor plan, rules and bookings.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<\/ul>","raw_excerpt":"Table booking and seat reservations for WooCommerce. Draw your floor plan, price every table, and take bookings with QR check-in.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/364319","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=364319"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/macvej"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=364319"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=364319"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=364319"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=364319"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=364319"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=364319"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}