=== Bodholdt Tickets - Help Desk & Support Ticket System ===
Contributors: bodholdtlabs
Tags: help desk, helpdesk, support ticket, support, customer support
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 8.0
Stable tag: 1.6.20
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

A fast, friendly help desk for WordPress: front-end ticket form, customer portal, custom fields, canned replies, CSAT, and an optional AI co-pilot.

== Description ==

Bodholdt Tickets is a complete, self-hosted support desk for WordPress. Unlimited tickets, up to three people working the queue, and no monthly fee.

* **Front-end Agent Support Desk.** Work the queue from a clean, fast interface, with no wp-admin clutter.
* **Front-end ticket form + Gutenberg block.** Drop a support form on any page with a block or the `[bodholdt_support_form]` shortcode.
* **Customer portal.** Customers track and reply to their tickets through a secure magic link, with no account required (`[bodholdt_portal]`).
* **Custom fields.** Add your own fields to the ticket form (text, select, checkbox, and more).
* **Canned / saved replies.** Insert your common answers in one click.
* **Customer satisfaction (CSAT).** Collect a rating when a ticket is resolved.
* **File attachments.** Customers and agents can attach files, stored in a protected directory.
* **Gamification.** XP, levels, streaks, and a leaderboard make clearing the queue feel good.
* **AI co-pilot (optional, bring your own key).** Drafts a reply for your agent to review and edit. It never sends on its own.

Unlimited tickets, no ticket caps, and no monthly fee. Up to 3 people can be given agent access on the free edition; everything listed above is included.

Translated into your language: the plugin ships a translation template and loads translations from its own `languages` folder, and every customer-facing and admin surface is right-to-left ready.

== External services ==

This plugin can connect to the following third-party services. Both are **optional** and are only contacted when you enable them.

**1. Anthropic API (AI co-pilot)**
When you enable the AI co-pilot and paste your own Anthropic API key, the plugin sends the ticket's conversation text and your configured brand-voice instructions to Anthropic's API to generate a draft reply for your agent. Data is sent only when an agent or the configured automation requests a draft. No data is sent if the AI co-pilot is disabled or no key is set.
- Terms: https://www.anthropic.com/legal/commercial-terms
- Privacy: https://www.anthropic.com/legal/privacy

**2. Browser Web Push services (agent notifications)**
If an agent opts in to background notifications, the plugin sends notification payloads to the push service operated by the agent's browser vendor (for example Mozilla, Google, or Microsoft). The endpoint is provided by the browser at subscription time. Data sent is limited to the notification content (for example "New reply on ticket #123"). No data is sent unless an agent subscribes.
- These endpoints are operated by the respective browser vendors under their own privacy policies.

== Installation ==

1. Upload the plugin to `/wp-content/plugins/` or install it from the Plugins screen.
2. Activate it.
3. Visit **Bodholdt Tickets** in wp-admin to configure agents, categories, and the ticket form.
4. Add the form to a page with the Bodholdt Tickets block or the `[bodholdt_support_form]` shortcode, and the portal with `[bodholdt_portal]`.

== Frequently Asked Questions ==

= How many tickets and agents do I get? =
Tickets are unlimited, with no monthly cap. Up to 3 people can be given agent access on the free edition. If your team is bigger than that, the separate Bodholdt Tickets Pro lifts the seat limit.

= Do I need an API key for the AI co-pilot? =
Only if you want to use it. Paste your own Anthropic API key under the AI settings. The co-pilot only ever drafts a reply; your agent reviews and sends.

= Does email-to-ticket work? =
The free plugin handles outbound replies and a front-end form, portal, and embeddable widget. Inbound email piping (IMAP/Gmail/Exchange), automation and SLA targets, department routing, and advanced reporting are part of the separate Bodholdt Tickets Pro.

= Can I run the free edition and Bodholdt Tickets Pro at the same time? =
No, and you do not need to. They are the same product, so only one should be active. If both are active, the second one to load stops and tells you which folder to deactivate. Your tickets, settings, and configuration live in the database, so switching between them loses nothing.

= Is it available in my language? =
The plugin loads translations from its own `languages` folder and ships an up-to-date translation template, so a `.mo` file you drop in (or one supplied by translate.wordpress.org) is picked up. Right-to-left languages such as Arabic, Hebrew, and Farsi are supported without a separate stylesheet.

== Screenshots ==

1. The unified Support Desk: one queue for every ticket with Open / Mine / All / Resolved filters, color-coded priority bars, category tags, and a full conversation thread in the reading pane.
2. The built-in AI co-pilot drafts a reply from the conversation using your own Anthropic key and brand voice, and you always review and edit before sending.
3. A clean, white-label front-end support form (also available as a Gutenberg block) so customers can open a ticket from any page, with optional file attachments.
4. The passwordless customer portal: visitors sign in by magic link to track "My tickets," reply, and start a new request, with no account required.
5. Configure the AI co-pilot in Settings: bring-your-own Anthropic key, choose the Claude model, and set your brand voice and knowledge base (plus channels, categories, and custom fields).
6. Built-in reports: ticket volume over time, by channel and category, first-response and resolution speed, customer satisfaction (CSAT), and what people ask about most.
7. Agent gamification: XP, levels, daily streaks, and a team leaderboard to keep support fun and responsive.

== Security ==

Found a security problem in this plugin? Please tell us before you publish it, and we
will tell you what we found and when a fix will ship.

Email support@bodholdtlabs.com with "Security" in the subject line. We acknowledge
within five business days. Full policy, including what is in scope and what we ask of
you, is at https://bodholdtlabs.com/security/

We do not run a bug bounty and cannot pay for reports. We would rather say so up front
than have you find out afterwards.

== Changelog ==

= 1.6.20 (2026-09-05) =
* The Manage account link in the license screen now points at the address that answers directly, instead of one that redirects first.

= 1.6.19 (2026-09-05) =
* The customer portal page now tells browsers and proxies not to store it. It carried no caching instruction at all, while the account pages beside it did, so a signed-in customer's ticket list and their session token could be kept by a shared browser and shown to whoever opened the page next. A page that merely quotes the shortcode in documentation is left alone, because it is not an account page.

= 1.6.18 (2026-09-05) =
* You can now use any Claude model your key has access to, instead of only the three this plugin was built with. The Model setting was a fixed dropdown, and the save handler refused anything that was not one of those three, so when Anthropic released something newer there was no way to select it: the field quietly fell back to the default and the plugin treated its own age as your mistake. It is now a text field with the same three offered as suggestions, and it accepts any model id. Use Test connection after changing it.
* The model id also used to be written down in seven places across three files, which is how a list like that ends up half updated. It is written down once now, and the shipped suggestions are unchanged in this release.

= 1.6.17 (2026-09-05) =
* An email with no plain-text part became a blank ticket. Inbound email-to-ticket read only the plain-text body a message carries, and mail sent as HTML alone has none, so the ticket was created with nothing in it while the endpoint reported success. The agent alert then said "(no message body)" and the customer receipt quoted nothing back, which reads at both ends like the desk lost the message. The HTML part is now used when there is no text part, converted to readable text so a message looks the same however it was sent.

= 1.6.16 (2026-09-04) =
* **The customer portal says what your support actually is.** The sign-in line and the support card used to mention support credits whether or not you had switched the paid support model on, which on a fresh install is off. They now describe opening and replying to a ticket, and the payment-return banner no longer announces a credit.

= 1.6.15 =
* **Tested with WordPress 7.1.**
* **The arcade panel now filters the markup it draws through an allowlist** instead of printing it as given. Nothing about how it looks or behaves changes, and the progress bar keeps the labels a screen reader reads out. The small script behind the milestone popups was tightened the same way.

= 1.6.14 =
* **Ticket attachments are now encrypted on disk.** Attachments live in your uploads folder, and whether that folder can be read directly over the web is decided by your web server, not by this plugin: Apache and LiteSpeed honour the rule the plugin writes, nginx and Caddy do not. Encrypting the files means a direct request returns unreadable data, so the support desk, which checks whether you are an agent or the customer who owns the ticket, is the only way to the file again. Existing attachments are converted quietly in the background, and files from before this release keep working either way. A new entry under Tools, Site Health tells you whether your server exposes the folder.

= 1.6.13 =
* **Attachment filenames now come from a cryptographic random source.** They were generated with a WordPress helper that other plugins are allowed to filter, which meant another plugin could decide how unguessable your attachment names were. Stored files are also written with tighter permissions.

= 1.6.12 =
* **Plainer writing.** A few messages used long dashes where a full stop or a separator reads more easily. Nothing functional changed.

= 1.6.11 =
* **The free contact widget no longer offers the categories that cost a support credit.** If you have turned the paid support model on, a question about one of your products was a tracked ticket on the Support page and a free message one click away on the same page. The widget now asks Tickets which categories are credited and simply leaves those out, so each door offers what it is for. Nothing changes if you have not turned the paid model on, because then nothing is credited.

= 1.6.10 =
* **There is now a plain list of every ticket inside WordPress admin.** The Support Desk is an app, and when an app fails it takes the whole queue with it. That is exactly what happened when the desk stopped loading on sites using Plain permalinks: for as long as it lasted there was no other way to see a single ticket. Under Tickets you will now find All Tickets, a straightforward table of every ticket with its status, the customer and their email address, which filters, searches and pages. WordPress builds it directly, with no JavaScript of its own, so it keeps working when the desk does not and it works with JavaScript switched off. It is read only on purpose. It is there so you can see the queue and answer people from your own mail, not to become a second desk. Your agents can open it too, not just administrators.
* **The desk no longer names the wrong plan beside a locked report.** Per-agent and per-department reporting unlocks on the first paid plan, but the reports screen said Studio: in the badge, on the button, and in three separate sentences. Anyone who saw those sections was being pointed at a plan more expensive than the feature actually needs. The plan name now comes from the same place as the check that unlocks the feature, so the two cannot drift apart again.

= 1.6.9 =
* **Everything the desk and the forms say can now be translated.** The plugin's JavaScript asks WordPress for translations exactly as its PHP does, but the translation catalog was built by scanning PHP only, so not one of those strings was ever offered to a translator. A fully translated site still showed the Support Desk, the customer portal, the support form and the widget in English, with no way to tell why. The catalog now includes them: 331 more strings, the desk among them. Nothing changed in the code that shows them, so an untranslated site looks exactly as it did.

= 1.6.8 =
* **The support paywall no longer throws away what you typed.** A customer who filled in the form, attached a file, and pressed Send watched the whole message vanish and be replaced by a request for payment. The form now stays where it was, the buy panel appears alongside it, and "Back to your message" returns everything untouched. The Send button is restored too, instead of staying stuck on "Sending...".
* **The paywall stopped claiming something that was not true.** It said an active subscription or an in-window license includes support. Under the credits model the in-window allowance is deliberately not used, so half that sentence was dead on arrival for the stores most likely to show it. It now states only what is true wherever it appears.
* **"New request" in the customer portal goes to the support form again.** The portal took whichever published page the database returned first for the shortcode, which could be a documentation page that merely quotes it. The plugin now records the pages it creates and prefers them, and the fallback lookup is ordered instead of arbitrary.

= 1.6.7 =
* **The arcade is fully translatable now, and its toasts can be dismissed.** Several labels the arcade shows (the dismiss button, the "Nice" message on a plain points toast, and the level badge) stayed in English no matter what language your site runs in, because the shared arcade code never passed them through for translation. They now translate like everything else, the level number is formatted for your locale, and every celebration has a close button. Toasts also pause while you hover or focus them, and skip their timer entirely if you have asked your system to reduce motion.
* **A site that cannot create the arcade tables now keeps trying.** On a host where the database user is not allowed to create tables, the plugin recorded the arcade as installed anyway and never tried again, so the arcade stayed silently broken forever. It now confirms the tables really exist before recording success, and retries later if they do not.

= 1.6.6 =
* **The arcade now has an off switch, and it covers the Support Desk too.** Since 1.6.5 the arcade has been off by default, but there was no control for it anywhere, so there was no way to turn it back on. There is now one under Appearance, alongside confetti and sound. It also does more than it used to: the Support Desk kept showing agents the XP chip, the leaderboard, and a points toast on every reply no matter what, because only the WordPress admin scoreboard honored the switch. One control now covers both. If you want the arcade on your desk, turn it on under Appearance.

= 1.6.5 =
* The arcade (XP, ranks, HUD, confetti) is now off by default. Turn it on any time under the plugin's Arcade settings. Nothing else changes.

= 1.6.4 =
* **A new ticket now emails somebody.** Agent email alerts are on by default, so the team hears about a new ticket or a customer reply even when nobody has the Support Desk open. Fresh installs used to ship with alerts off, which meant a complaint could sit unseen for days. Sites with saved Tickets settings keep them exactly as they are; the new default applies where the settings were never saved.
* **Customers get a receipt.** When a customer opens a ticket through the form, the widget, or the portal, they now receive one confirmation email with their ticket reference and a copy of their message, so they have a written record and something to reply to. You can turn this off under Tickets, General, "Send customers a receipt."
* **Receipts cannot start mail loops.** The receipt declares itself machine-sent (RFC 3834), is never sent to bounce or no-reply addresses, skips inbound mail that identifies itself as automatic, and is capped at two per address per hour. Bounce handlers and other helpdesks cannot ping-pong with it.
* **Alerts reach a one-person shop.** On a site where the support address is also the owner's own address, alerts used to be silently skipped by a safeguard this edition never needed. The owner now actually gets the alert.

= 1.6.3 =
* **The Support Desk now works on default permalinks.** On a fresh WordPress site set to Plain permalinks, the agent Support Desk could not load its ticket queue: it showed an empty inbox with a "No route was found" error while real tickets waited unseen. The desk now builds its addresses correctly under every permalink setting, so the queue loads and agents can answer tickets straight away.
* **A clear heads-up when permalinks need changing.** If your site is on Plain permalinks, administrators now see one dismissible notice explaining that setting Permalinks to Post name makes the Support Desk and the support pages work. This is the same setting that lets the /support/ and /my-tickets/ pages open instead of your blog.

= 1.6.2 =
* **American spellings throughout.** The plugin and this changelog used British spellings in places (licence, colour, behaviour, cancelled). They now match the rest of Bodholdt Labs. Wording only, nothing behaves differently.

= 1.6.1 =
* **Privacy requests now hand over the whole record.** A personal-data export includes every field on a ticket, the customer snapshot stored with it, private notes, activity history, tags, attachment details, and any web-push registrations, without ever exposing the secret that authenticates a push subscription. An erase request removes message content, activity, attachments, tags, stored customer snapshots, and push registrations. If you answer a request under GDPR, CCPA, or a similar law, what you produce is now complete.
* The other two changes in this release only affect the paid edition, so nothing else here is different.

= 1.6.0 =
The worldwide-readiness release. It also folds in every fix that shipped as 1.5.4 in the paid edition, because the free edition went from 1.5.3 straight to 1.6.0.
* **Translations finally load.** Earlier free builds shipped a correct translation template and correct headers but never told WordPress to read the plugin's `languages` folder, so no translation could ever apply. Fixed, and the build now fails if that call ever goes missing again.
* **Right-to-left support.** Arabic, Hebrew, Farsi, and other RTL languages lay out correctly across the admin screens, the support form, the portal, and the widget. No separate stylesheet is needed.
* **Times are correct outside UTC.** Dates and times in the Support Desk, the customer portal, and email now read correctly on sites that are not set to UTC. They previously drifted by your timezone offset.
* **Accessibility (WCAG 2.1 AA).** Fixed low-contrast headings, buttons, and focus rings; every focus indicator is now clearly visible; status and error messages are announced to screen readers; failed actions say what went wrong instead of silently reverting; animation honors the reduced-motion setting.
* **Free and paid can no longer collide.** Activating Bodholdt Tickets Pro while this edition is active used to end in a raw PHP fatal error. Now the second copy stops cleanly and an admin notice names the folder to deactivate.
* **Privacy.** The erase tool under Tools, Erase Personal Data now clears everything it should in the free edition, including data it previously left behind. Uninstall now also removes web-push subscriptions and per-user settings, on every site of a multisite network.
* **Security and reliability (from 1.5.4).** The erase tool fully drains customers with large ticket histories instead of stopping after the first batch. Internal agent notes and their attachments can no longer be reached from the customer portal. Portal actions carry a per-session token. Rate limiting keys on the real client address. Web-push endpoints must be HTTPS and are validated before use, and the stored push key is encrypted at rest. Legacy Office document uploads are accepted again, and a queue-search edge case no longer errors.
* **Attribution.** The bundled canvas-confetti library now ships with its ISC copyright and permission notice and a full license file, as its license requires.
* **Honest limits.** This readme previously said the free edition had no agent-seat limit. It does: 3 seats. The copy now says so plainly, and the FAQ explains it.

= 1.5.3 =
* **Privacy tools (GDPR export and erase).** The plugin answers data-subject requests through the built-in WordPress privacy tools. Export returns a customer's tickets, messages, and attachments; erase deletes the messages and attachments and anonymizes the ticket history. Suggested privacy-policy text is added to Settings, Privacy. No change to how the support desk works.

= 1.5.2 =
* **Arcade engine polish.** XP counts correctly when several events land at the same moment, the on-screen HUD labels are fully translatable, and celebration cards always stay within the screen. No change to how the support desk works.

= 1.5.1 =
* **The Support Desk now names your rank.** The desk's top-bar scoreboard shows your Response Ops rank (Rookie through Desk Legend) next to your level and XP, matching the wp-admin HUD, so your progress reads the same on both surfaces. Your stats panel shows it too.

= 1.5.0 =
* **Response Ops.** A HUD in wp-admin tracks your level, rank, and XP as you work tickets, and agents get the same on the Support Desk itself, with a celebration when a customer rates a reply or you reach a new rank. Trophies unlock as you go. All optional, sound stays off by default, and the desk works exactly as before.

= 1.1.0 =
* Settings redesign. The one long settings page is now a set of tabs (General, Channels, Notifications, Team, AI Co-pilot, Appearance), each with its own Save, so you configure one thing at a time. Every setting is exactly where it was, just grouped.
* A new setup status card at the top tells you at a glance whether your desk is ready, and links straight to the step that still needs doing.
* Custom fields now have a friendly row editor instead of a type-the-syntax text box. The plain text editor is still there under "Edit as text" if you prefer it.
* The widget embed snippet hides its secret token until you click Reveal, and has a one-click Copy button.
* The database table list collapses to a single "all tables ready" line unless something needs attention.
* Support Desk: every emoji is replaced with original brand icons, including the achievement badges. No functional change.

= 1.0.0 =
* **Out of beta.** Bodholdt Tickets graduates to 1.0 after 30 or more shipped releases. No breaking changes; this marks the product as production-stable.
* Added an "External services" section to this readme (Anthropic, Web Push) and self-hosted the admin font, so no admin request is sent to Google Fonts.

= 0.33.4 =
First public release of the free edition. A complete, self-hosted help desk with unlimited tickets, up to 3 agent seats, and no monthly fee:
* Unified front-end Support Desk with Open / Mine / All / Resolved filters, priority and category, and a full conversation thread.
* Front-end support form as a Gutenberg block or the `[bodholdt_support_form]` shortcode, with optional file attachments.
* Passwordless customer portal (`[bodholdt_portal]`). Visitors sign in by magic link to track, reply to, and open tickets, with no account required.
* Custom fields, canned/saved replies, and customer satisfaction (CSAT) ratings on resolved tickets.
* Built-in reports: ticket volume over time, by channel and category, response and resolution speed, CSAT, and top topics.
* Agent gamification: XP, levels, daily streaks, and a team leaderboard.
* Optional AI co-pilot. Bring your own Anthropic API key; it only ever drafts a reply for an agent to review and edit, and never sends on its own.

== Upgrade Notice ==

= 1.6.0 =
Recommended for everyone. Translations now load at all, times are correct outside UTC, right-to-left languages are supported, accessibility failures are fixed, privacy erase is complete, and installing the paid edition alongside this one no longer causes a fatal error. Carries the security fixes released as 1.5.4 in the paid edition.

= 0.33.4 =
First public release of the free edition.
