{"id":286933,"date":"2026-09-25T11:17:48","date_gmt":"2026-09-25T11:17:48","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/music-instructor-booking\/"},"modified":"2026-09-25T11:17:37","modified_gmt":"2026-09-25T11:17:37","slug":"slotwise","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/slotwise\/","author":23461108,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.10.3","stable_tag":"1.10.3","tested":"7.0.6","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"Slotwise - Multi-Purpose Booking & Appointment Plugin","header_author":"Mytsys Software Solutions","header_description":"SlotWise \u2014 The intelligent appointment booking plugin that auto-configures itself for your industry. Music, medical, beauty, fitness and 8 more. Payments, packages, discounts, drag-drop forms and theme-matching built in.","assets_banners_color":"d1a09f","last_updated":"2026-09-25 11:17:37","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wordpress.org\/plugins\/slotwise\/","header_author_uri":"https:\/\/www.mytsys.com\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":54,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.10.3":{"tag":"1.10.3","author":"mytsyssoftwaresolutions","date":"2026-09-25 11:17:37","revision":3712938}},"upgrade_notice":{"1.9.0":"<p>Privacy update \u2014 the plugin no longer loads fonts from Google, so visitor IP addresses are never shared with third parties.<\/p>","1.8.5":"<p>Important update \u2014 fixes the package booking form rendering without its fields, and resolves all remaining code-standards findings.<\/p>","1.8.4":"<p>Important update \u2014 fixes a bug that prevented bookings and settings from saving. All users should update.<\/p>","1.8.3":"<p>Compatibility update \u2014 now tested and confirmed working with WordPress 7.0.<\/p>","1.8.1":"<p>Recommended update \u2014 improves output escaping throughout the admin dashboard and ensures booking timestamps always use your WordPress timezone setting.<\/p>","1.7.1":"<p>Safe to upgrade \u2014 your data is preserved. Fixes white-on-white theme banner, hardcoded hero title, and brings the plugin into full WordPress.org compliance.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3712938,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3712938,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3712938,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3712938,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.10.3"],"block_files":[],"assets_screenshots":[],"screenshots":{"1":"Industry Setup Wizard \u2014 choose your business type to auto-configure everything","2":"Welcome dashboard showing booking stats, theme match, and industry configuration","3":"Front-end booking form with service selector, calendar, time slots, promo code, and payment","4":"Promo\/discount alert banner showing active deals to customers","5":"Price breakdown with applied discount and total","6":"Packages pricing grid with booking button","7":"Admin bookings dashboard with search, status tabs, and one-click cancel","8":"Discount code management \u2014 create percentage or fixed codes with expiry and usage limits","9":"Visual drag-drop form builder \u2014 reorder and configure booking form fields","10":"Appearance settings with live preview"}},"plugin_section":[],"plugin_tags":[8132,269,416,4062,4959],"plugin_category":[40],"plugin_contributors":[282704],"plugin_business_model":[],"class_list":["post-286933","plugin","type-plugin","status-publish","hentry","plugin_tags-appointments","plugin_tags-booking","plugin_tags-calendar","plugin_tags-schedule","plugin_tags-services","plugin_category-calendar-and-events","plugin_contributors-mytsyssoftwaresolutions","plugin_committers-mytsyssoftwaresolutions"],"banners":{"banner":"https:\/\/ps.w.org\/slotwise\/assets\/banner-772x250.png?rev=3712938","banner_2x":"https:\/\/ps.w.org\/slotwise\/assets\/banner-1544x500.png?rev=3712938","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/slotwise\/assets\/icon-128x128.png?rev=3712938","icon_2x":"https:\/\/ps.w.org\/slotwise\/assets\/icon-256x256.png?rev=3712938","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p><strong>SlotWise<\/strong> by Mytsys Software Solutions is a full-featured appointment booking plugin that auto-configures itself for your industry. Choose from 12 business categories (music studio, medical clinic, hair salon, beauty, car wash, fitness, dental, tutoring, massage, pet grooming, photography, cleaning) and the plugin pre-loads appropriate services, staff, time slots, and form fields \u2014 ready in minutes.<\/p>\n\n<p>It adds a beautiful, mobile-responsive booking form and packages grid to any page via shortcode, with a complete admin dashboard to manage bookings, staff, services, packages, discount codes, and payments.<\/p>\n\n<h4>Key Features<\/h4>\n\n<ul>\n<li><strong>Industry Setup Wizard<\/strong> \u2014 One-time wizard auto-configures services, staff, forms, and packages for 12 business types<\/li>\n<li><strong>Dynamic terminology<\/strong> \u2014 Admin labels, page titles, and menus update to match your industry (e.g. \"Consultations\" for medical, \"Treatments\" for beauty)<\/li>\n<li><strong>Two booking modes<\/strong> \u2014 Standard booking form and simplified Package Booking form<\/li>\n<li><strong>Multi-session packages<\/strong> \u2014 Create packages with name, description, price, session count, and featured badge<\/li>\n<li><strong>Service listings grid<\/strong> \u2014 Display all your services in a stylish card layout<\/li>\n<li><strong>Four payment methods<\/strong> \u2014 Card (Stripe Elements), PayPal Standard, Stripe, and Cash App<\/li>\n<li><strong>Discount &amp; promo codes<\/strong> \u2014 Percentage or fixed amount, date ranges, usage limits, service restrictions, live front-end validation<\/li>\n<li><strong>Visual drag-drop form builder<\/strong> \u2014 Reorder, show\/hide, and relabel booking form fields from admin<\/li>\n<li><strong>Admin dashboard<\/strong> \u2014 View, search, and manage all bookings; update status; filter by date\/status; one-click cancellation with customer email<\/li>\n<li><strong>iCal attachments<\/strong> \u2014 Confirmation emails include a .ics calendar file for Google\/Apple\/Outlook<\/li>\n<li><strong>Self-service cancellation<\/strong> \u2014 Customers can cancel via a secure token link in their confirmation email<\/li>\n<li><strong>Automatic theme matching<\/strong> \u2014 Detects your active WordPress theme colours on activation and applies them to the booking form (14 themes supported)<\/li>\n<li><strong>Slot blocking<\/strong> \u2014 Admin can block specific date + time combinations from the calendar<\/li>\n<li><strong>Weekend closure<\/strong> \u2014 Toggle Saturday\/Sunday availability independently<\/li>\n<li><strong>Email notifications<\/strong> \u2014 Automatic confirmation to customer + admin new-booking alert with HTML template<\/li>\n<li><strong>Data safety on update<\/strong> \u2014 Plugin upgrades never delete your data; <code>dbDelta()<\/code> + schema migration only adds columns, never drops them<\/li>\n<li><strong>Fully translatable<\/strong> \u2014 Uses standard WordPress i18n with <code>.pot<\/code> file (text domain: <code>slotwise<\/code>)<\/li>\n<li><strong>Theme-safe<\/strong> \u2014 All CSS is scoped to plugin wrappers; no global resets or overrides<\/li>\n<\/ul>\n\n<h4>Shortcodes<\/h4>\n\n<ul>\n<li><code>[slotwise_booking_form]<\/code> \u2014 Full appointment booking form with service\/level selection and payment<\/li>\n<li><code>[slotwise_package_booking]<\/code> \u2014 Simplified booking form for packages \u2014 no service or level needed<\/li>\n<li><code>[slotwise_packages]<\/code> \u2014 Displays the packages pricing grid<\/li>\n<li><code>[slotwise_classes]<\/code> \u2014 Displays the services\/classes listings grid<\/li>\n<\/ul>\n\n<h4>Privacy<\/h4>\n\n<p>Customer data (name, email, phone, booking details) is stored in custom database tables on your WordPress installation. No data is sent to third parties except as required to process payments \u2014 see the External Services section below for full details.<\/p>\n\n<p>No data is collected or sent to Mytsys Software Solutions.<\/p>\n\n<h3>External Services<\/h3>\n\n<p>This plugin connects to the following third-party services to process payments. These services are only contacted when a customer submits a booking with the corresponding payment method selected. You must enable each payment method explicitly in Settings \u2014 none are active by default.<\/p>\n\n<h4>Stripe<\/h4>\n\n<p>Used for card payments and Stripe-powered payment processing.<\/p>\n\n<p><strong>What is sent:<\/strong> When a customer submits a booking using card or Stripe payment, the customer's card details are tokenised entirely in their browser by the Stripe.js library (loaded from <code>https:\/\/js.stripe.com\/v3\/<\/code>) and a secure payment token is sent to your Stripe account via the Stripe API (<code>https:\/\/api.stripe.com<\/code>). Raw card numbers never pass through your server. The customer's name and booking amount are also sent to Stripe to complete the charge.<\/p>\n\n<p><strong>When it is sent:<\/strong> Only when a customer completes a booking using Card or Stripe payment methods.<\/p>\n\n<p><strong>Stripe Terms of Service:<\/strong> https:\/\/stripe.com\/legal\/ssa\n<strong>Stripe Privacy Policy:<\/strong> https:\/\/stripe.com\/privacy<\/p>\n\n<h4>PayPal<\/h4>\n\n<p>Used for PayPal Standard payment processing.<\/p>\n\n<p><strong>What is sent:<\/strong> When a customer selects PayPal as their payment method, they are redirected to PayPal's website where they complete payment. The booking amount, your PayPal email address, and a booking reference are passed to PayPal. PayPal then sends an Instant Payment Notification (IPN) callback to your site to confirm payment.<\/p>\n\n<p><strong>When it is sent:<\/strong> Only when a customer clicks to pay via PayPal at checkout.<\/p>\n\n<p><strong>PayPal Terms of Service:<\/strong> https:\/\/www.paypal.com\/us\/legalhub\/useragreement-full\n<strong>PayPal Privacy Policy:<\/strong> https:\/\/www.paypal.com\/us\/legalhub\/privacy-full<\/p>\n\n<h4>Cash App<\/h4>\n\n<p>Used for Cash App Pay payment processing (US market only).<\/p>\n\n<p><strong>What is sent:<\/strong> When a customer selects Cash App as their payment method, they are directed to send payment to your configured $Cashtag. The booking amount and reference are displayed to the customer. No customer data is automatically sent to Cash App's servers by this plugin \u2014 the customer initiates the transfer themselves through their Cash App account.<\/p>\n\n<p><strong>When it is sent:<\/strong> Only when a customer selects Cash App as their payment method.<\/p>\n\n<p><strong>Cash App Terms of Service:<\/strong> https:\/\/cash.app\/legal\/us\/en-us\/tos\n<strong>Cash App Privacy Policy:<\/strong> https:\/\/cash.app\/legal\/us\/en-us\/privacy<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>slotwise<\/code> folder to <code>\/wp-content\/plugins\/<\/code><\/li>\n<li>Activate the plugin through the <strong>Plugins<\/strong> menu in WordPress<\/li>\n<li>The Setup Wizard will launch automatically \u2014 choose your industry to auto-configure services, staff, and forms<\/li>\n<li>Go to <strong>SlotWise &gt; Settings<\/strong> to enter your business name, contact details, and payment credentials<\/li>\n<li>Add <code>[slotwise_booking_form]<\/code> or <code>[slotwise_package_booking]<\/code> to any page<\/li>\n<\/ol>\n\n<p><strong>For Stripe\/Card:<\/strong> Enter your Stripe public and secret keys in Settings \u2192 Payment. Stripe.js tokenises card numbers client-side for PCI compliance.<\/p>\n\n<p><strong>For PayPal:<\/strong> Enter your PayPal email under Settings \u2192 Payment. Enable Sandbox mode for testing.<\/p>\n\n<p><strong>For Cash App:<\/strong> Enter your $Cashtag in Settings \u2192 Payment and enable Cash App.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20upgrading%20the%20plugin%20delete%20my%20data%3F\"><h3>Does upgrading the plugin delete my data?<\/h3><\/dt>\n<dd><p>No. The upgrade system uses <code>dbDelta()<\/code> which only adds missing tables and columns \u2014 it never drops or modifies existing data.<\/p><\/dd>\n<dt id=\"what%20industries%20are%20supported%20by%20the%20setup%20wizard%3F\"><h3>What industries are supported by the Setup Wizard?<\/h3><\/dt>\n<dd><p>Music Studio, Medical \/ Doctor Clinic, Hair Salon, Beauty &amp; Aesthetics, Car Wash \/ Auto Detailing, Fitness \/ Personal Training, Dental Practice, Private Tutoring, Massage Therapy, Pet Grooming, Photography Studio, and Cleaning Services. You can switch industry at any time from Settings \u2014 it reloads seed data but never touches your real bookings.<\/p><\/dd>\n<dt id=\"what%20is%20the%20difference%20between%20the%20two%20booking%20forms%3F\"><h3>What is the difference between the two booking forms?<\/h3><\/dt>\n<dd><p>[slotwise_booking_form] is the full form: the customer selects a service, level, duration, date, time, and pays.<\/p>\n\n<pre><code>[slotwise_package_booking] is the simplified form: the customer picks a package (which includes a fixed number of sessions at a fixed price), selects a date and time, and pays. No service or level dropdown needed.\n<\/code><\/pre><\/dd>\n<dt id=\"is%20card%20payment%20pci%20compliant%3F\"><h3>Is card payment PCI compliant?<\/h3><\/dt>\n<dd><p>Yes. The card form uses Stripe Elements, which tokenises card data in the browser. Raw card numbers never pass through your server.<\/p><\/dd>\n<dt id=\"can%20i%20customise%20the%20appearance%3F\"><h3>Can I customise the appearance?<\/h3><\/dt>\n<dd><p>Yes \u2014 go to <strong>SlotWise &gt; Appearance<\/strong> to change the accent colour, background colour, fonts, and font size. Changes apply instantly on all pages that show the plugin shortcodes. The plugin also auto-detects your active WordPress theme colours on activation.<\/p><\/dd>\n<dt id=\"are%20discount%20codes%20validated%20server-side%3F\"><h3>Are discount codes validated server-side?<\/h3><\/dt>\n<dd><p>Yes. Discount amounts are always recalculated on the server before any charge is applied. The discount code and amount from the browser POST are never trusted \u2014 the code is re-validated and the saving recalculated server-side before being applied to the payment.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.10.3<\/h4>\n\n<ul>\n<li><strong>CRITICAL FIX:<\/strong> The Appearance screen failed with a fatal error and could not be opened. <code>page_appearance()<\/code> instantiated <code>SLOTWISE_Theme_Detector<\/code> and called <code>get_active_theme_name()<\/code> on it \u2014 but that class exposes only static methods and has neither a constructor nor a method by that name. The call was introduced in 1.8.0 when page logic was moved out of the view files; it was never needed, because the Appearance view already prepares its own theme, colour and font data. The redundant block has been removed and the screen loads normally.<\/li>\n<li>The removed lines also read three option names that do not exist (<code>slotwise_theme_matched_time<\/code>, <code>slotwise_theme_matched_src<\/code>, <code>slotwise_active_theme_slug<\/code>); the view reads the correct ones.<\/li>\n<\/ul>\n\n<h4>1.10.2<\/h4>\n\n<ul>\n<li><strong>Fix:<\/strong> Booking reference numbers were prefixed <code>VG-<\/code>, a leftover from the plugin's original single-client origins. References now use the neutral <code>SW-<\/code> prefix, so a clinic's patients no longer receive a booking reference branded for an unrelated business.<\/li>\n<li>Removed remaining legacy branding from code comments, the frontend script header, and the Cash App placeholder example.<\/li>\n<li>Documented the two intentional empty catch blocks (CSPRNG fallback and theme.json detection fallback) so static analysers no longer flag them as swallowed errors.<\/li>\n<\/ul>\n\n<h4>1.10.1<\/h4>\n\n<ul>\n<li><strong>Industry terminology completed across the whole plugin.<\/strong> 1.10.0 covered the menu and the Services and Staff screens; this release finishes the job. Customer emails, booking confirmations, cancellation pages, validation messages, payment errors, the Bookings table, Discounts, Packages, Settings, Appearance and every front-end template now use the vocabulary of the configured industry. A clinic's patients no longer receive an email whose first row is labelled \"Class\".<\/li>\n<li>Front-end headings such as \"Studio Hours\", \"Lesson Packages\" and \"Select a class\" now read \"Clinic Hours\", \"Appointment Packages\" and \"Select a consultation\" for a medical practice, and adapt for all 12 industries.<\/li>\n<li>Product copy on the Welcome dashboard rewritten to be industry-neutral rather than music-specific.<\/li>\n<li>All dynamic terms pass through <code>sprintf()<\/code> into fixed translatable sentences, each with a <code>translators:<\/code> comment placed where WPCS requires it. Translation template regenerated.<\/li>\n<\/ul>\n\n<h4>1.10.0<\/h4>\n\n<ul>\n<li><strong>Industry terminology now drives the interface.<\/strong> Each of the 12 industry profiles has always carried its own vocabulary (Class\/Instructor for music, Consultation\/Doctor for medical, Service\/Stylist for salons, and so on), but nothing in the plugin consumed it \u2014 the admin screens stayed hardcoded to music wording even after another industry was applied. A medical clinic saw its own consultations listed under a heading that read \"Music Classes\".<\/li>\n<li>Added <code>SLOTWISE_Industry_Profiles::term()<\/code>, <code>current_terminology()<\/code>, <code>default_terminology()<\/code> and <code>example()<\/code>. Terminology now resolves from the configured industry with a neutral fallback (Service \/ Team Member \/ Appointment \/ Client) when no industry has been chosen.<\/li>\n<li>Admin menu items, page headings, section titles, button labels, admin notices and input placeholders on the Services and Staff screens now follow the active industry. Placeholders draw a real example from the industry profile, so a clinic sees \"e.g. General Consultation\" rather than \"e.g. Guitar Lessons\".<\/li>\n<li>Every dynamic term is passed as a <code>sprintf()<\/code> argument into a fixed translatable sentence with a <code>translators:<\/code> comment, so translations remain correct and no string is ever built by concatenation.<\/li>\n<li>Removed the hardcoded \"Price \/ Hour\" label, which was wrong for any industry not billing hourly.<\/li>\n<\/ul>\n\n<h4>1.9.3<\/h4>\n\n<ul>\n<li><strong>CRITICAL FIX:<\/strong> The Welcome dashboard and Setup Wizard rendered white text on a white background, making them unreadable. <code>admin\/css\/smb-welcome.css<\/code> and <code>admin\/css\/smb-setup-wizard.css<\/code> still contained raw <code>&lt;?php ... ?&gt;<\/code> blocks left over from when they were inline <code>&lt;style&gt;<\/code> tags. A <code>.css<\/code> file is served statically and never runs through PHP, so <code>--w-accent<\/code> was never defined; every <code>background: var(--w-accent)<\/code> was dropped as invalid while the accompanying <code>color: #fff<\/code> stayed valid \u2014 producing white on white.<\/li>\n<li>Both stylesheets are now pure CSS with safe default custom properties, and the site's configured accent colour is applied at runtime through <code>wp_add_inline_style()<\/code>. Because the defaults are baked into the stylesheets, these screens can no longer render unreadable even if the inline style fails to load.<\/li>\n<\/ul>\n\n<h4>1.9.2<\/h4>\n\n<ul>\n<li><strong>CRITICAL FIX:<\/strong> The Industry Setup Wizard was completely non-functional \u2014 clicking a service card did nothing. <code>admin\/js\/smb-setup-wizard.js<\/code> contained four raw <code>&lt;?php ... ?&gt;<\/code> blocks left behind when the script was extracted from an inline block. A <code>.js<\/code> file is served statically and never passes through PHP, so the browser received literal PHP source on line 3 \u2014 a JavaScript syntax error that aborted the whole file before a single event listener was attached. All four values (selected industry, nonce, AJAX URL, redirect URL) are now passed correctly via <code>wp_localize_script()<\/code> as <code>slotwiseWizard<\/code>, and the wizard's user-facing strings are now translatable.<\/li>\n<\/ul>\n\n<h4>1.9.1<\/h4>\n\n<ul>\n<li><strong>Slug change:<\/strong> The plugin slug is now <code>slotwise<\/code>. Main file renamed to <code>slotwise.php<\/code>, translation template to <code>languages\/slotwise.pot<\/code>, and the text domain to <code>slotwise<\/code> across all 1,080 translatable strings.<\/li>\n<li><strong>Prefixing:<\/strong> All constants renamed <code>SLTWSE_<\/code> to <code>SLOTWISE_<\/code>; all option keys, AJAX actions, nonce actions, script handles and localized JS objects renamed <code>sltwse_<\/code> to <code>slotwise_<\/code>. Custom database table names are intentionally unchanged.<\/li>\n<li><strong>Fix:<\/strong> Removed an orphaned duplicate copy of SortableJS at <code>admin\/js\/sortable.min.js<\/code>.<\/li>\n<li><strong>Fix:<\/strong> Prefixed the SortableJS script handle so it cannot collide with another plugin loading the same library.<\/li>\n<\/ul>\n\n<h4>1.9.0<\/h4>\n\n<ul>\n<li><strong>PRIVACY \/ GUIDELINE FIX:<\/strong> Removed all Google Fonts CDN loading. The frontend stylesheet carried an unconditional <code>@import<\/code> from fonts.googleapis.com that transmitted every visitor's IP address to Google on every page view, with no way for the site owner to disable it and no disclosure in the readme. Two further remote font enqueues (frontend and admin) were also removed. Typography now uses named families with full system font stack fallbacks \u2014 no request leaves the visitor's browser.<\/li>\n<li><strong>Fix:<\/strong> Added the direct-access guard to the six <code>index.php<\/code> directory stubs \u2014 every PHP file in the plugin is now guarded.<\/li>\n<li><strong>Fix:<\/strong> Prefixed the Stripe.js script handle (<code>stripe-js<\/code> to <code>slotwise-stripe-js<\/code>). Stripe.js itself remains remote because Stripe's PCI-DSS SAQ-A terms prohibit self-hosting it; it loads only when Stripe or card payments are enabled with keys configured, and is disclosed under External Services.<\/li>\n<li><strong>Docs:<\/strong> Documented the <code>base64_encode()<\/code> call as RFC 7617 HTTP Basic authentication required by the Stripe REST API, not obfuscation.<\/li>\n<\/ul>\n\n<h4>1.8.5<\/h4>\n\n<ul>\n<li><strong>CRITICAL FIX:<\/strong> Restored six variables the v1.8.0 controller refactor left undefined \u2014 <code>$currency<\/code> in the Bookings and Classes screens, <code>$symbol<\/code> in the classes grid, and <code>$symbol<\/code> plus all six field groups (<code>$name_fields<\/code>, <code>$contact_fields<\/code>, <code>$date_field<\/code>, <code>$time_field<\/code>, <code>$notes_field<\/code>, <code>$custom_fields<\/code>) in the package booking form. These produced Undefined-variable notices under WP_DEBUG and rendered the package booking form without its input fields.<\/li>\n<li><strong>Fix:<\/strong> Renamed <code>$tabs<\/code> and <code>$type<\/code> in admin views \u2014 both collide with WordPress core globals and could be overwritten mid-render.<\/li>\n<li><strong>Performance:<\/strong> Discount lookups now use the WordPress object cache with a generation-counter invalidation scheme, so a single write invalidates every cached read with no missable path.<\/li>\n<li><strong>Security:<\/strong> <code>.ics<\/code> calendar attachment writing converted from direct <code>file_put_contents()<\/code> to the <code>WP_Filesystem<\/code> API; removes all error-suppression operators.<\/li>\n<li><strong>Security:<\/strong> PayPal IPN nonce exemption narrowed to the exact block that reads the callback payload, with the notify-validate handshake documented inline.<\/li>\n<li><strong>Audit:<\/strong> Verified with PHP_CodeSniffer 3.13 + WordPress Coding Standards 3.1 \u2014 zero errors and zero warnings across every WordPress.Security, WordPress.DB, WordPress.NamingConventions, WordPress.WP and WordPress.PHP sniff. All 37 files pass <code>php -l<\/code>.<\/li>\n<\/ul>\n\n<h4>1.8.4<\/h4>\n\n<ul>\n<li><strong>CRITICAL FIX:<\/strong> Repaired 44 broken database table references. A previous prefix rename had changed <code>$wpdb-&gt;prefix . 'smb_TABLE'<\/code> to <code>'slotwise_TABLE'<\/code> in insert\/update\/delete calls, while the tables are actually created as <code>smb_*<\/code>. This silently broke every booking save, package save, instructor save, time slot save, form field save and discount save. All 44 references corrected.<\/li>\n<li><strong>Compliance:<\/strong> Restored <code>phpcs:disable PrefixAllGlobals<\/code> headers to all 15 view\/template files (lost during the v1.8.0 controller refactor) \u2014 resolves ~180 NonPrefixedVariable warnings<\/li>\n<li><strong>Security:<\/strong> PayPal IPN field extraction now applies <code>sanitize_text_field()<\/code> to each named key; handler carries a scoped nonce-sniff exemption documenting the notify-validate handshake<\/li>\n<li><strong>Compliance:<\/strong> <code>get_all_bookings()<\/code> and all custom-table reads\/writes now carry complete <code>DirectQuery<\/code> + <code>NoCaching<\/code> exemptions with justification<\/li>\n<\/ul>\n\n<h4>1.8.3<\/h4>\n\n<ul>\n<li><strong>Compatibility:<\/strong> Updated \"Tested up to\" to WordPress 7.0 (released May 2026) \u2014 required by the WordPress.org automated scanner for plugin visibility in search results<\/li>\n<\/ul>\n\n<h4>1.8.2<\/h4>\n\n<ul>\n<li><strong>Fix PHP Syntax (CRITICAL):<\/strong> All 13 view and template files now pass <code>php -l<\/code> with zero errors. Root cause: files beginning with <code>&lt;?php ... ?&gt;<\/code> followed by HTML fail PHP's strict syntax linter because it sees HTML after the closing tag as unexpected content in a PHP file. Fixed by restructuring all view files to open with <code>&lt;?php if (!defined('ABSPATH')) { exit; } ?&gt;<\/code> immediately before the HTML output on the same line, eliminating the parse error completely.<\/li>\n<li><strong>Fix escaped backslash:<\/strong> Removed erroneous <code>\\$<\/code> escaping in <code>classes.php<\/code> and <code>packages.php<\/code> (introduced by a Python string-escaping bug in a previous fix) which caused a secondary parse error on line 129.<\/li>\n<\/ul>\n\n<h4>1.8.1<\/h4>\n\n<ul>\n<li><strong>Security (Escaping):<\/strong> All boolean\/ternary echoes in attribute context now wrapped with esc_attr() across settings.php, bookings.php, form-builder.php, pkg-form-builder.php, and setup-wizard.php<\/li>\n<li><strong>Data Integrity (Timezone):<\/strong> created_at timestamp in insert_booking() now written via current_time('mysql') (WordPress-timezone-aware) rather than relying on MySQL server CURRENT_TIMESTAMP which may differ from the WordPress site timezone<\/li>\n<li><strong>Audit:<\/strong> Confirmed zero PHP session usage, zero raw mail() calls, zero unprefixed functions, zero SQL injection vectors<\/li>\n<\/ul>\n\n<h4>1.8.0<\/h4>\n\n<ul>\n<li><strong>Fix PHP Syntax:<\/strong> ALL PHP business logic removed from view and template files. Every admin view now starts with only <code>&lt;?php if (!defined('ABSPATH')) exit; ?&gt;<\/code> then HTML. All data preparation (DB queries, GET\/POST handling, variable setup) moved into class methods (<code>page_packages()<\/code>, <code>page_settings()<\/code>, <code>page_welcome()<\/code>, <code>page_discounts()<\/code>, <code>page_appearance()<\/code>, <code>page_setup_wizard()<\/code>, <code>page_form_builder()<\/code>, <code>page_pkg_form_builder()<\/code>) and shortcode methods (<code>booking_form()<\/code>, <code>packages_grid()<\/code>).<\/li>\n<li><strong>Fix Processing whole input:<\/strong> Completely removed <code>file_get_contents('php:\/\/input')<\/code>. PayPal IPN reads from <code>$_POST<\/code> directly. Verification replay string built from a named whitelist of exactly 13 specific IPN keys \u2014 the whole <code>$_POST<\/code> array is never passed to any function.<\/li>\n<li><strong>Fix Sanitizing:<\/strong> Every <code>$_POST<\/code>\/<code>$_GET<\/code> read across all 37 PHP files uses <code>sanitize_*(wp_unslash())<\/code>. Color fields now use <code>sanitize_text_field(wp_unslash())<\/code> before <code>sanitize_hex_color()<\/code>. Zero unsanitized inputs.<\/li>\n<\/ul>\n\n<h4>1.7.9<\/h4>\n\n<ul>\n<li><strong>Fix Processing whole input:<\/strong> Completely removed file_get_contents('php:\/\/input'). PayPal IPN now reads from $_POST (PHP auto-populates from the form-encoded body) and rebuilds the verification replay using http_build_query(wp_unslash($_POST)). Zero raw stream reading.<\/li>\n<li><strong>Fix Sanitizing:<\/strong> All remaining $_POST\/$_GET reads audited line by line across all 37 files. Every field now has sanitize_text_field(wp_unslash()), sanitize_email(wp_unslash()), sanitize_key(wp_unslash()), or absint(wp_unslash()) as appropriate. Checkbox isset() returns are annotated with phpcs:ignore explaining they return literal 1 or 0, never the POST value.<\/li>\n<li><strong>Fix PHP Syntax:<\/strong> Confirmed zero actual parse errors. All files pass brace balance check.<\/li>\n<\/ul>\n\n<h4>1.7.8<\/h4>\n\n<ul>\n<li><strong>Fix PHP Syntax:<\/strong> Moved nonce verification from staff-slots.php into page_staff_slots() class method; all 13 view\/template files now use inline <code>?&gt;&lt;html&gt;<\/code> (no newline gap) eliminating \"unexpected token &lt;\" from PHP linters<\/li>\n<li><strong>Fix Sanitizing:<\/strong> Added wp_unslash() to absint() on $_GET['slotwise_pkg'] in shortcodes.php<\/li>\n<li><strong>Fix Processing whole input:<\/strong> Added explicit phpcs:ignore with justification on file_get_contents('php:\/\/input') \u2014 PayPal IPN service integration requires raw body replay; all actual data extracted field-by-field with individual sanitize functions<\/li>\n<\/ul>\n\n<p><h4>1.7.7<\/h4><\/p>\n\n<ul>\n<li><strong>Security:<\/strong> Added wp_unslash() to every absint() call on $_POST and $_GET throughout the plugin \u2014 package_id, instructor_id, slot_id, class_id, field_id, id, sessions, sort_order, usage_limit, edit params<\/li>\n<li><strong>Security:<\/strong> Added wp_unslash() to sanitize_key() on $_GET['msg'] reads in staff-slots.php and packages.php<\/li>\n<li><strong>Security:<\/strong> PayPal IPN raw body now validated against URL-encoded character whitelist (regex) before any use; oversized payloads (&gt;4KB) return HTTP 413<\/li>\n<li><strong>Security:<\/strong> array_map('absint') on $_POST['ids'] now wraps each element through wp_unslash() first<\/li>\n<\/ul>\n\n<p><h4>1.7.6<\/h4><\/p>\n\n<ul>\n<li><strong>Fix:<\/strong> PHP syntax error in <code>staff-slots.php<\/code> \u2014 confirmed resolved: <code>?&gt;<\/code> closing tag correctly inserted before HTML output on line 8<\/li>\n<li><strong>Fix:<\/strong> PayPal IPN <code>invoice<\/code> field was read from <code>$ipn<\/code> but was missing from the sanitized extraction array \u2014 added with <code>sanitize_text_field()<\/code><\/li>\n<li><strong>Fix:<\/strong> Removed double-wrapped <code>wp_nonce_url()<\/code> on instructor and slot edit links in <code>staff-slots.php<\/code><\/li>\n<li><strong>Security:<\/strong> Added <code>wp_verify_nonce()<\/code> + <code>wp_nonce_url()<\/code> to <code>classes.php<\/code> edit GET param (<code>slotwise_edit_class<\/code>)<\/li>\n<li><strong>Security:<\/strong> Added <code>wp_verify_nonce()<\/code> to <code>admin\/class-smb-admin.php<\/code> <code>page_classes()<\/code> edit GET read<\/li>\n<li><strong>Security:<\/strong> <code>slotwise_admin_confirm<\/code> and <code>slotwise_confirm<\/code> GET params now wrapped with <code>sanitize_text_field( wp_unslash() )<\/code><\/li>\n<\/ul>\n\n<p><h4>1.7.5<\/h4><\/p>\n\n<ul>\n<li><strong>Fix:<\/strong> PHP syntax error in <code>staff-slots.php<\/code> \u2014 missing <code>?&gt;<\/code> closing tag before HTML output<\/li>\n<li><strong>Security:<\/strong> Added nonce verification to <code>packages.php<\/code> edit GET param via <code>wp_nonce_url()<\/code> + <code>wp_verify_nonce()<\/code><\/li>\n<li><strong>Security:<\/strong> Package pre-select GET params in <code>shortcodes.php<\/code> now verified with <code>slotwise_pkg_preselect<\/code> nonce generated by <code>wp_nonce_url()<\/code> in the \"Book This Package\" button<\/li>\n<li><strong>Security:<\/strong> Added <code>phpcs:ignore<\/code> annotations with full justification on token-authenticated handlers (<code>admin-cancel<\/code>, <code>cancel<\/code>) \u2014 64-char cryptographic tokens serve as nonce equivalents for email link auth<\/li>\n<li><strong>Security:<\/strong> PayPal IPN handler: added 4KB size limit on raw POST body; all fields extracted with individual <code>sanitize_*()<\/code> functions instead of using the raw array<\/li>\n<li><strong>Compliance:<\/strong> <code>sltData<\/code> \u2192 <code>slotwiseData<\/code>, <code>sltAdmin<\/code> \u2192 <code>slotwiseAdmin<\/code> \u2014 JS localized object names now use the full plugin prefix (updated in 7 PHP and JS files)<\/li>\n<li><strong>Compliance:<\/strong> <code>database.php<\/code> <code>get_all_bookings()<\/code> now uses <code>%i<\/code> identifier placeholder in <code>wpdb-&gt;prepare()<\/code> for <code>ORDER BY<\/code> column name instead of string concatenation<\/li>\n<\/ul>\n\n<p><h4>1.7.4<\/h4><\/p>\n\n<ul>\n<li><strong>Compliance:<\/strong> All global functions renamed to <code>slotwise_<\/code> prefix (<code>slotwise_run<\/code>, <code>slotwise_add_cron_intervals<\/code>, <code>slotwise_expire_pending_bookings<\/code>, <code>slotwise_plugin_action_links<\/code>, <code>slotwise_status_badge_class<\/code>, <code>slotwise_pay_pill_class<\/code>)<\/li>\n<li><strong>Compliance:<\/strong> Added <code>phpcs:disable WordPress.NamingConventions.PrefixAllGlobals<\/code> to all 16 view\/template files \u2014 local variables in included views are not in global PHP scope<\/li>\n<li><strong>Security:<\/strong> All remaining <code>$_POST<\/code> numeric fields now wrapped with <code>sanitize_text_field( wp_unslash(...) )<\/code> before casting: <code>price<\/code>, <code>value<\/code>, <code>min_amount<\/code>, <code>slotwise_package_price<\/code><\/li>\n<li><strong>Security:<\/strong> Canvas form field AJAX handlers now use <code>sanitize_key()<\/code> for <code>field_key<\/code>\/<code>type<\/code> and <code>sanitize_text_field()<\/code> for <code>label<\/code>\/<code>placeholder<\/code>; <code>sort_order<\/code> uses <code>absint( wp_unslash(...) )<\/code><\/li>\n<li><strong>Compliance:<\/strong> <code>database.php<\/code> upgrade() method wrapped in <code>phpcs:disable\/enable<\/code> block covering SchemaChange, DirectQuery, and NoCaching for all ALTER TABLE operations<\/li>\n<\/ul>\n\n<p><h4>1.7.3<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Backward-compatible shortcode aliases \u2014 old <code>[smb_booking_form]<\/code>, <code>[smb_classes]<\/code>, <code>[smb_packages]<\/code>, <code>[smb_package_booking]<\/code> continue working on existing pages after upgrade<\/li>\n<li><strong>New:<\/strong> Automatic one-time shortcode migration \u2014 on activation\/upgrade, all pages and posts are scanned and old <code>smb_*<\/code> shortcode tags are silently replaced with <code>slotwise_*<\/code> equivalents in the database; migration runs once and never repeats<\/li>\n<\/ul>\n\n<p><h4>1.7.2<\/h4><\/p>\n\n<ul>\n<li><strong>Compliance:<\/strong> Prefix renamed from <code>smb_<\/code>\/<code>SMB_<\/code> to <code>slotwise_<\/code>\/<code>SLOTWISE_<\/code> (6-char unique prefix) across all PHP classes, constants, options, AJAX hooks, nonce actions, and shortcodes<\/li>\n<li><strong>Compliance:<\/strong> Removed CDN fallback for SortableJS \u2014 now bundled locally in <code>admin\/js\/vendor\/Sortable.min.js<\/code><\/li>\n<li><strong>Compliance:<\/strong> Removed <code>ini_set('display_errors')<\/code> from security class \u2014 error display is WordPress core's responsibility<\/li>\n<li><strong>Security:<\/strong> Added nonce verification to all admin GET reads (<code>staff-slots.php<\/code> edit forms, <code>discounts.php<\/code> edit links, <code>bookings.php<\/code> filter\/search form)<\/li>\n<li><strong>Security:<\/strong> All <code>$_POST<\/code> fields now explicitly extracted and sanitized with appropriate <code>sanitize_*()<\/code> functions before use; whole <code>$_POST<\/code> array no longer passed to any function<\/li>\n<li><strong>Security:<\/strong> <code>esc_sql()<\/code> added to whitelisted <code>$orderby<\/code> and <code>$order<\/code> values in <code>get_all_bookings()<\/code> query<\/li>\n<li><strong>Escaping:<\/strong> <code>wp_kses_post()<\/code> used for all HTML variable output in email templates; <code>wp_kses()<\/code> used for anchor link output in settings page<\/li>\n<li><strong>Shortcodes renamed:<\/strong> <code>[slotwise_booking_form]<\/code>, <code>[slotwise_package_booking]<\/code>, <code>[slotwise_packages]<\/code>, <code>[slotwise_classes]<\/code><\/li>\n<\/ul>\n\n<p><h4>1.7.1<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Active promo alert banner \u2014 when discount codes are running, an animated amber banner appears on the booking form showing the code and saving, with a scroll-to-field button<\/li>\n<li><strong>Fix:<\/strong> Promo code field was hidden in some configurations; now always shown when active discounts exist<\/li>\n<li><strong>Improvement:<\/strong> Promo code input shows green\/red visual state on success\/error; Apply button hides and Remove appears after successful application<\/li>\n<li><strong>Improvement:<\/strong> Price summary redesigned with clearer breakdown rows, divider, and accent-coloured total<\/li>\n<li><strong>Fix:<\/strong> Hero title on Welcome dashboard was hardcoded to \"Music Studio\" \u2014 now dynamically reflects active industry<\/li>\n<li><strong>Fix:<\/strong> Theme match banner was appearing as white-on-white on some themes \u2014 replaced with solid visible styling<\/li>\n<li><strong>Compliance:<\/strong> All inline <code>&lt;style&gt;<\/code> and <code>&lt;script&gt;<\/code> tags extracted to enqueued CSS\/JS files (<code>wp_enqueue_style<\/code>, <code>wp_enqueue_script<\/code>) in accordance with WordPress plugin guidelines<\/li>\n<li><strong>Compliance:<\/strong> Text domain corrected to match plugin slug (<code>slotwise<\/code>)<\/li>\n<li><strong>Compliance:<\/strong> External services (Stripe, PayPal, Cash App) documented in readme<\/li>\n<\/ul>\n\n<p><h4>1.7.0<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Industry Setup Wizard \u2014 first-run wizard lets you choose from 12 business categories<\/li>\n<li><strong>New:<\/strong> SLOTWISE_Industry_Profiles class \u2014 12 industry presets each with services, staff, time slots, form fields, packages, and terminology<\/li>\n<li><strong>New:<\/strong> Dynamic admin menus, page titles, and labels that update to match your selected industry<\/li>\n<li><strong>New:<\/strong> Plugin rebranded to SlotWise<\/li>\n<li><strong>New:<\/strong> Automatic theme colour detection expanded to 14 WordPress themes<\/li>\n<li><strong>New:<\/strong> Welcome dashboard with animated stats, theme-match banner, and industry configuration panel<\/li>\n<\/ul>\n\n<p><h4>1.5.2<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Discount and promo code system \u2014 percentage or fixed amount, date ranges, usage limits, service restrictions<\/li>\n<li><strong>New:<\/strong> Live AJAX discount validation on booking form with real-time price update<\/li>\n<li><strong>New:<\/strong> Discounts admin page with full CRUD and usage tracking<\/li>\n<li><strong>Improvement:<\/strong> Mobile UI improvements throughout<\/li>\n<li><strong>Compliance:<\/strong> Plugin Checker compliance pass \u2014 all warnings resolved<\/li>\n<\/ul>\n\n<p><h4>1.5.1<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> iCal (.ics) attachment on confirmation emails<\/li>\n<li><strong>Improvement:<\/strong> Admin cancel flow improved with reason field and updated email template<\/li>\n<\/ul>\n\n<p><h4>1.5.0<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Visual drag-drop form builder with AJAX-powered reorder and inline field editing<\/li>\n<\/ul>\n\n<p><h4>1.4.9<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Admin can cancel any booking from dashboard with optional reason sent to customer<\/li>\n<li><strong>New:<\/strong> Booking status tabs: All \/ Confirmed \/ Pending \/ Cancelled (with live counts)<\/li>\n<\/ul>\n\n<p><h4>1.4.8<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Self-service cancellation \u2014 secure token link in customer emails<\/li>\n<li><strong>Fix:<\/strong> Apostrophe in customer name causing DB write error<\/li>\n<\/ul>\n\n<p><h4>1.4.7<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> HTML confirmation email to customer with booking reference<\/li>\n<li><strong>New:<\/strong> Admin notification email on each new booking<\/li>\n<\/ul>\n\n<p><h4>1.4.0<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Cash App as a fourth payment method<\/li>\n<li><strong>New:<\/strong> Payment method visibility respects enable\/disable toggles<\/li>\n<li><strong>Security:<\/strong> Full audit pass; all output escaped; all queries use <code>$wpdb-&gt;prepare()<\/code><\/li>\n<\/ul>\n\n<p><h4>1.3.0<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> Custom calendar date picker replaces native input \u2014 weekends in red, booked dates in amber<\/li>\n<li><strong>New:<\/strong> Slot blocking \u2014 admin can block specific date + time combinations<\/li>\n<li><strong>New:<\/strong> Visual drag-drop form builder (initial version)<\/li>\n<li><strong>New:<\/strong> Package redirect: \"Get Started\" on packages page scrolls to booking form<\/li>\n<\/ul>\n\n<p><h4>1.2.0<\/h4><\/p>\n\n<ul>\n<li><strong>New:<\/strong> <code>[slotwise_package_booking]<\/code> shortcode \u2014 simplified booking form for packages<\/li>\n<li><strong>New:<\/strong> Multi-session lesson packages with pricing grid shortcode<\/li>\n<li><strong>New:<\/strong> Stripe Elements for PCI-compliant card processing<\/li>\n<li><strong>Security:<\/strong> All nonces, sanitization, and escaping hardened<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Calendar with AJAX-powered booked-date greying<\/li>\n<li>Rate limiting, nonce checks, IP proxy detection<\/li>\n<li>PayPal IPN callback for automatic payment confirmation<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release: booking form, class listings, PayPal, Stripe, admin dashboard, email notifications<\/li>\n<\/ul>","raw_excerpt":"Appointment booking for any service business. Auto-configures for your industry with payments, packages, discount codes, and booking forms.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/286933","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=286933"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/mytsyssoftwaresolutions"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=286933"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=286933"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=286933"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=286933"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=286933"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=286933"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}